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/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 
     23 using base::ASCIIToUTF16;
     24 using base::DictionaryValue;
     25 
     26 namespace extensions {
     27 
     28 namespace keys = manifest_keys;
     29 namespace values = manifest_values;
     30 namespace errors = manifest_errors;
     31 
     32 namespace {
     33 
     34 const char kBackground[] = "background";
     35 
     36 static base::LazyInstance<BackgroundInfo> g_empty_background_info =
     37     LAZY_INSTANCE_INITIALIZER;
     38 
     39 const 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 
     49 BackgroundInfo::BackgroundInfo()
     50     : is_persistent_(true),
     51       allow_js_access_(true) {
     52 }
     53 
     54 BackgroundInfo::~BackgroundInfo() {
     55 }
     56 
     57 // static
     58 GURL 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
     66 const std::vector<std::string>& BackgroundInfo::GetBackgroundScripts(
     67     const Extension* extension) {
     68   return GetBackgroundInfo(extension).background_scripts_;
     69 }
     70 
     71 // static
     72 bool BackgroundInfo::HasBackgroundPage(const Extension* extension) {
     73   return GetBackgroundInfo(extension).has_background_page();
     74 }
     75 
     76 // static
     77 bool BackgroundInfo::HasPersistentBackgroundPage(const Extension* extension)  {
     78   return GetBackgroundInfo(extension).has_persistent_background_page();
     79 }
     80 
     81 // static
     82 bool BackgroundInfo::HasLazyBackgroundPage(const Extension* extension) {
     83   return GetBackgroundInfo(extension).has_lazy_background_page();
     84 }
     85 
     86 // static
     87 bool BackgroundInfo::HasGeneratedBackgroundPage(const Extension* extension) {
     88   const BackgroundInfo& info = GetBackgroundInfo(extension);
     89   return !info.background_scripts_.empty();
     90 }
     91 
     92 // static
     93 bool BackgroundInfo::AllowJSAccess(const Extension* extension) {
     94   return GetBackgroundInfo(extension).allow_js_access_;
     95 }
     96 
     97 bool 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 
    117 bool 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 
    145 bool 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 
    186 bool 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 
    200 bool 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 
    225 bool 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 
    241 BackgroundManifestHandler::BackgroundManifestHandler() {
    242 }
    243 
    244 BackgroundManifestHandler::~BackgroundManifestHandler() {
    245 }
    246 
    247 bool 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 
    270 bool 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 
    306 bool BackgroundManifestHandler::AlwaysParseForType(Manifest::Type type) const {
    307   return type == Manifest::TYPE_PLATFORM_APP;
    308 }
    309 
    310 const 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