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