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