Home | History | Annotate | Download | only in extensions
      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 
     23 using base::DictionaryValue;
     24 namespace keys = extension_manifest_keys;
     25 namespace values = extension_manifest_values;
     26 namespace errors = extension_manifest_errors;
     27 
     28 namespace extensions {
     29 
     30 namespace {
     31 
     32 const char kBackground[] = "background";
     33 
     34 static base::LazyInstance<BackgroundInfo> g_empty_background_info =
     35     LAZY_INSTANCE_INITIALIZER;
     36 
     37 const 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 
     47 BackgroundInfo::BackgroundInfo()
     48     : is_persistent_(true),
     49       allow_js_access_(true) {
     50 }
     51 
     52 BackgroundInfo::~BackgroundInfo() {
     53 }
     54 
     55 // static
     56 GURL 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
     64 const std::vector<std::string>& BackgroundInfo::GetBackgroundScripts(
     65     const Extension* extension) {
     66   return GetBackgroundInfo(extension).background_scripts_;
     67 }
     68 
     69 // static
     70 bool BackgroundInfo::HasBackgroundPage(const Extension* extension) {
     71   return GetBackgroundInfo(extension).has_background_page();
     72 }
     73 
     74 // static
     75 bool BackgroundInfo::AllowJSAccess(const Extension* extension) {
     76   return GetBackgroundInfo(extension).allow_js_access_;
     77 }
     78 
     79 // static
     80 bool BackgroundInfo::HasPersistentBackgroundPage(const Extension* extension)  {
     81   return GetBackgroundInfo(extension).has_persistent_background_page();
     82 }
     83 
     84 // static
     85 bool BackgroundInfo::HasLazyBackgroundPage(const Extension* extension) {
     86   return GetBackgroundInfo(extension).has_lazy_background_page();
     87 }
     88 
     89 bool 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 
    101 bool 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 
    129 bool 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 
    175 bool 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 
    189 bool 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 
    214 bool 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 
    230 BackgroundManifestHandler::BackgroundManifestHandler() {
    231 }
    232 
    233 BackgroundManifestHandler::~BackgroundManifestHandler() {
    234 }
    235 
    236 bool 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 
    258 bool 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 
    295 bool BackgroundManifestHandler::AlwaysParseForType(Manifest::Type type) const {
    296   return type == Manifest::TYPE_PLATFORM_APP;
    297 }
    298 
    299 const 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