Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / extensions / common / manifest_handlers / background_info.cc
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
24 using base::ASCIIToUTF16;
25 using base::DictionaryValue;
26
27 namespace extensions {
28
29 namespace keys = manifest_keys;
30 namespace values = manifest_values;
31 namespace errors = manifest_errors;
32
33 namespace {
34
35 const char kBackground[] = "background";
36
37 static base::LazyInstance<BackgroundInfo> g_empty_background_info =
38     LAZY_INSTANCE_INITIALIZER;
39
40 const 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
50 BackgroundInfo::BackgroundInfo()
51     : is_persistent_(true),
52       allow_js_access_(true) {
53 }
54
55 BackgroundInfo::~BackgroundInfo() {
56 }
57
58 // static
59 GURL 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
67 const std::vector<std::string>& BackgroundInfo::GetBackgroundScripts(
68     const Extension* extension) {
69   return GetBackgroundInfo(extension).background_scripts_;
70 }
71
72 // static
73 const std::string& BackgroundInfo::GetServiceWorkerScript(
74     const Extension* extension) {
75   return GetBackgroundInfo(extension).service_worker_script_;
76 }
77
78 // static
79 bool BackgroundInfo::HasBackgroundPage(const Extension* extension) {
80   return GetBackgroundInfo(extension).has_background_page();
81 }
82
83 // static
84 bool BackgroundInfo::HasPersistentBackgroundPage(const Extension* extension)  {
85   return GetBackgroundInfo(extension).has_persistent_background_page();
86 }
87
88 // static
89 bool BackgroundInfo::HasLazyBackgroundPage(const Extension* extension) {
90   return GetBackgroundInfo(extension).has_lazy_background_page();
91 }
92
93 // static
94 bool BackgroundInfo::HasGeneratedBackgroundPage(const Extension* extension) {
95   const BackgroundInfo& info = GetBackgroundInfo(extension);
96   return !info.background_scripts_.empty();
97 }
98
99 // static
100 bool BackgroundInfo::HasServiceWorker(const Extension* extension) {
101   return GetBackgroundInfo(extension).has_service_worker();
102 }
103
104 // static
105 bool BackgroundInfo::AllowJSAccess(const Extension* extension) {
106   return GetBackgroundInfo(extension).allow_js_access_;
107 }
108
109 bool 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
135 bool 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::kEnableExperimentalWebPlatformFeatures)) {
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
156 bool 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
184 bool 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
225 bool 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
239 bool 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
264 bool 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
280 BackgroundManifestHandler::BackgroundManifestHandler() {
281 }
282
283 BackgroundManifestHandler::~BackgroundManifestHandler() {
284 }
285
286 bool 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
310 bool 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
346 bool BackgroundManifestHandler::AlwaysParseForType(Manifest::Type type) const {
347   return type == Manifest::TYPE_PLATFORM_APP;
348 }
349
350 const 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