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