1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "extensions/common/manifest_handlers/background_info.h"
6
7#include "base/command_line.h"
8#include "base/file_util.h"
9#include "base/lazy_instance.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/utf_string_conversions.h"
13#include "content/public/common/content_switches.h"
14#include "extensions/common/constants.h"
15#include "extensions/common/error_utils.h"
16#include "extensions/common/file_util.h"
17#include "extensions/common/manifest_constants.h"
18#include "extensions/common/manifest_handlers/permissions_parser.h"
19#include "extensions/common/permissions/api_permission_set.h"
20#include "extensions/common/switches.h"
21#include "grit/extensions_strings.h"
22#include "ui/base/l10n/l10n_util.h"
23
24using base::ASCIIToUTF16;
25using base::DictionaryValue;
26
27namespace extensions {
28
29namespace keys = manifest_keys;
30namespace values = manifest_values;
31namespace errors = manifest_errors;
32
33namespace {
34
35const char kBackground[] = "background";
36
37static base::LazyInstance<BackgroundInfo> g_empty_background_info =
38    LAZY_INSTANCE_INITIALIZER;
39
40const BackgroundInfo& GetBackgroundInfo(const Extension* extension) {
41  BackgroundInfo* info = static_cast<BackgroundInfo*>(
42      extension->GetManifestData(kBackground));
43  if (!info)
44    return g_empty_background_info.Get();
45  return *info;
46}
47
48}  // namespace
49
50BackgroundInfo::BackgroundInfo()
51    : is_persistent_(true),
52      allow_js_access_(true) {
53}
54
55BackgroundInfo::~BackgroundInfo() {
56}
57
58// static
59GURL BackgroundInfo::GetBackgroundURL(const Extension* extension) {
60  const BackgroundInfo& info = GetBackgroundInfo(extension);
61  if (info.background_scripts_.empty())
62    return info.background_url_;
63  return extension->GetResourceURL(kGeneratedBackgroundPageFilename);
64}
65
66// static
67const std::vector<std::string>& BackgroundInfo::GetBackgroundScripts(
68    const Extension* extension) {
69  return GetBackgroundInfo(extension).background_scripts_;
70}
71
72// static
73const std::string& BackgroundInfo::GetServiceWorkerScript(
74    const Extension* extension) {
75  return GetBackgroundInfo(extension).service_worker_script_;
76}
77
78// static
79bool BackgroundInfo::HasBackgroundPage(const Extension* extension) {
80  return GetBackgroundInfo(extension).has_background_page();
81}
82
83// static
84bool BackgroundInfo::HasPersistentBackgroundPage(const Extension* extension)  {
85  return GetBackgroundInfo(extension).has_persistent_background_page();
86}
87
88// static
89bool BackgroundInfo::HasLazyBackgroundPage(const Extension* extension) {
90  return GetBackgroundInfo(extension).has_lazy_background_page();
91}
92
93// static
94bool BackgroundInfo::HasGeneratedBackgroundPage(const Extension* extension) {
95  const BackgroundInfo& info = GetBackgroundInfo(extension);
96  return !info.background_scripts_.empty();
97}
98
99// static
100bool BackgroundInfo::HasServiceWorker(const Extension* extension) {
101  return GetBackgroundInfo(extension).has_service_worker();
102}
103
104// static
105bool BackgroundInfo::AllowJSAccess(const Extension* extension) {
106  return GetBackgroundInfo(extension).allow_js_access_;
107}
108
109bool BackgroundInfo::Parse(const Extension* extension, base::string16* error) {
110  const std::string& bg_scripts_key = extension->is_platform_app() ?
111      keys::kPlatformAppBackgroundScripts : keys::kBackgroundScripts;
112  const std::string& sw_scripts_key =
113      extension->is_platform_app()
114          ? keys::kPlatformAppServiceWorkerScript
115          : "";  // TODO(scheib): Support extensions crbug.com/346885
116  if (!LoadServiceWorkerScript(extension, sw_scripts_key, error) ||
117      !LoadBackgroundScripts(extension, bg_scripts_key, error) ||
118      !LoadBackgroundPage(extension, error) ||
119      !LoadBackgroundPersistent(extension, error) ||
120      !LoadAllowJSAccess(extension, error)) {
121    return false;
122  }
123
124  int background_solution_sum = (background_url_.is_valid() ? 1 : 0) +
125                                (!background_scripts_.empty() ? 1 : 0) +
126                                (has_service_worker() ? 1 : 0);
127  if (background_solution_sum > 1) {
128    *error = ASCIIToUTF16(errors::kInvalidBackgroundCombination);
129    return false;
130  }
131
132  return true;
133}
134
135bool BackgroundInfo::LoadServiceWorkerScript(const Extension* extension,
136                                             const std::string& key,
137                                             base::string16* error) {
138  const base::Value* service_worker_script_value = NULL;
139  if (!extension->manifest()->Get(key, &service_worker_script_value))
140    return true;
141
142  if (!CommandLine::ForCurrentProcess()->HasSwitch(
143          ::switches::kEnableServiceWorker)) {
144    *error = ASCIIToUTF16(errors::kServiceWorkerRequiresFlag);
145    return false;
146  }
147
148  CHECK(service_worker_script_value);
149  if (!service_worker_script_value->GetAsString(&service_worker_script_)) {
150    *error = ASCIIToUTF16(errors::kInvalidServiceWorkerScript);
151    return false;
152  }
153  return true;
154}
155
156bool BackgroundInfo::LoadBackgroundScripts(const Extension* extension,
157                                           const std::string& key,
158                                           base::string16* error) {
159  const base::Value* background_scripts_value = NULL;
160  if (!extension->manifest()->Get(key, &background_scripts_value))
161    return true;
162
163  CHECK(background_scripts_value);
164  if (background_scripts_value->GetType() != base::Value::TYPE_LIST) {
165    *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts);
166    return false;
167  }
168
169  const base::ListValue* background_scripts = NULL;
170  background_scripts_value->GetAsList(&background_scripts);
171  for (size_t i = 0; i < background_scripts->GetSize(); ++i) {
172    std::string script;
173    if (!background_scripts->GetString(i, &script)) {
174      *error = ErrorUtils::FormatErrorMessageUTF16(
175          errors::kInvalidBackgroundScript, base::IntToString(i));
176      return false;
177    }
178    background_scripts_.push_back(script);
179  }
180
181  return true;
182}
183
184bool BackgroundInfo::LoadBackgroundPage(const Extension* extension,
185                                        const std::string& key,
186                                        base::string16* error) {
187  const base::Value* background_page_value = NULL;
188  if (!extension->manifest()->Get(key, &background_page_value))
189    return true;
190
191  std::string background_str;
192  if (!background_page_value->GetAsString(&background_str)) {
193    *error = ASCIIToUTF16(errors::kInvalidBackground);
194    return false;
195  }
196
197  if (extension->is_hosted_app()) {
198    background_url_ = GURL(background_str);
199
200    if (!PermissionsParser::HasAPIPermission(extension,
201                                             APIPermission::kBackground)) {
202      *error = ASCIIToUTF16(errors::kBackgroundPermissionNeeded);
203      return false;
204    }
205    // Hosted apps require an absolute URL.
206    if (!background_url_.is_valid()) {
207      *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp);
208      return false;
209    }
210
211    if (!(background_url_.SchemeIs("https") ||
212          (CommandLine::ForCurrentProcess()->HasSwitch(
213              switches::kAllowHTTPBackgroundPage) &&
214           background_url_.SchemeIs("http")))) {
215      *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp);
216      return false;
217    }
218  } else {
219    background_url_ = extension->GetResourceURL(background_str);
220  }
221
222  return true;
223}
224
225bool BackgroundInfo::LoadBackgroundPage(const Extension* extension,
226                                        base::string16* error) {
227  if (extension->is_platform_app()) {
228    return LoadBackgroundPage(
229        extension, keys::kPlatformAppBackgroundPage, error);
230  }
231
232  if (!LoadBackgroundPage(extension, keys::kBackgroundPage, error))
233    return false;
234  if (background_url_.is_empty())
235    return LoadBackgroundPage(extension, keys::kBackgroundPageLegacy, error);
236  return true;
237}
238
239bool BackgroundInfo::LoadBackgroundPersistent(const Extension* extension,
240                                              base::string16* error) {
241  if (extension->is_platform_app()) {
242    is_persistent_ = false;
243    return true;
244  }
245
246  const base::Value* background_persistent = NULL;
247  if (!extension->manifest()->Get(keys::kBackgroundPersistent,
248                                  &background_persistent))
249    return true;
250
251  if (!background_persistent->GetAsBoolean(&is_persistent_)) {
252    *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistent);
253    return false;
254  }
255
256  if (!has_background_page()) {
257    *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage);
258    return false;
259  }
260
261  return true;
262}
263
264bool BackgroundInfo::LoadAllowJSAccess(const Extension* extension,
265                                       base::string16* error) {
266  const base::Value* allow_js_access = NULL;
267  if (!extension->manifest()->Get(keys::kBackgroundAllowJsAccess,
268                                  &allow_js_access))
269    return true;
270
271  if (!allow_js_access->IsType(base::Value::TYPE_BOOLEAN) ||
272      !allow_js_access->GetAsBoolean(&allow_js_access_)) {
273    *error = ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccess);
274    return false;
275  }
276
277  return true;
278}
279
280BackgroundManifestHandler::BackgroundManifestHandler() {
281}
282
283BackgroundManifestHandler::~BackgroundManifestHandler() {
284}
285
286bool BackgroundManifestHandler::Parse(Extension* extension,
287                                      base::string16* error) {
288  scoped_ptr<BackgroundInfo> info(new BackgroundInfo);
289  if (!info->Parse(extension, error))
290    return false;
291
292  // Platform apps must have background pages or service workers.
293  if (extension->is_platform_app() && !info->has_background_page() &&
294      !info->has_service_worker()) {
295    *error = ASCIIToUTF16(errors::kBackgroundRequiredForPlatformApps);
296    return false;
297  }
298  // Lazy background pages are incompatible with the webRequest API.
299  if (info->has_lazy_background_page() &&
300      PermissionsParser::HasAPIPermission(extension,
301                                          APIPermission::kWebRequest)) {
302    *error = ASCIIToUTF16(errors::kWebRequestConflictsWithLazyBackground);
303    return false;
304  }
305
306  extension->SetManifestData(kBackground, info.release());
307  return true;
308}
309
310bool BackgroundManifestHandler::Validate(
311    const Extension* extension,
312    std::string* error,
313    std::vector<InstallWarning>* warnings) const {
314  // Validate that background scripts exist.
315  const std::vector<std::string>& background_scripts =
316      BackgroundInfo::GetBackgroundScripts(extension);
317  for (size_t i = 0; i < background_scripts.size(); ++i) {
318    if (!base::PathExists(
319            extension->GetResource(background_scripts[i]).GetFilePath())) {
320      *error = l10n_util::GetStringFUTF8(
321          IDS_EXTENSION_LOAD_BACKGROUND_SCRIPT_FAILED,
322          base::UTF8ToUTF16(background_scripts[i]));
323      return false;
324    }
325  }
326
327  // Validate background page location, except for hosted apps, which should use
328  // an external URL. Background page for hosted apps are verified when the
329  // extension is created (in Extension::InitFromValue)
330  if (BackgroundInfo::HasBackgroundPage(extension) &&
331      !extension->is_hosted_app() && background_scripts.empty()) {
332    base::FilePath page_path = file_util::ExtensionURLToRelativeFilePath(
333        BackgroundInfo::GetBackgroundURL(extension));
334    const base::FilePath path = extension->GetResource(page_path).GetFilePath();
335    if (path.empty() || !base::PathExists(path)) {
336      *error =
337          l10n_util::GetStringFUTF8(
338              IDS_EXTENSION_LOAD_BACKGROUND_PAGE_FAILED,
339              page_path.LossyDisplayName());
340      return false;
341    }
342  }
343  return true;
344}
345
346bool BackgroundManifestHandler::AlwaysParseForType(Manifest::Type type) const {
347  return type == Manifest::TYPE_PLATFORM_APP;
348}
349
350const std::vector<std::string> BackgroundManifestHandler::Keys() const {
351  static const char* keys[] = {keys::kBackgroundAllowJsAccess,
352                               keys::kBackgroundPage,
353                               keys::kBackgroundPageLegacy,
354                               keys::kBackgroundPersistent,
355                               keys::kBackgroundScripts,
356                               keys::kPlatformAppBackgroundPage,
357                               keys::kPlatformAppBackgroundScripts,
358                               keys::kPlatformAppServiceWorkerScript};
359  return std::vector<std::string>(keys, keys + arraysize(keys));
360}
361
362}  // namespace extensions
363