Home | History | Annotate | Download | only in manifest_handlers
      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::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 
    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
    363