Home | History | Annotate | Download | only in manifest_handlers
      1 // Copyright 2014 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/options_page_info.h"
      6 
      7 #include "base/files/file_util.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/strings/utf_string_conversions.h"
     10 #include "extensions/common/api/extensions_manifest_types.h"
     11 #include "extensions/common/error_utils.h"
     12 #include "extensions/common/feature_switch.h"
     13 #include "extensions/common/file_util.h"
     14 #include "extensions/common/manifest_constants.h"
     15 #include "extensions/strings/grit/extensions_strings.h"
     16 #include "ui/base/l10n/l10n_util.h"
     17 
     18 using base::ASCIIToUTF16;
     19 using base::DictionaryValue;
     20 
     21 namespace extensions {
     22 
     23 namespace keys = manifest_keys;
     24 namespace errors = manifest_errors;
     25 
     26 using core_api::extensions_manifest_types::OptionsUI;
     27 
     28 namespace {
     29 
     30 OptionsPageInfo* GetOptionsPageInfo(const Extension* extension) {
     31   return static_cast<OptionsPageInfo*>(
     32       extension->GetManifestData(keys::kOptionsUI));
     33 }
     34 
     35 // Parses |url_string| into a GURL |result| if it is a valid options page for
     36 // this app/extension. If not, it returns the reason in |error|. Because this
     37 // handles URLs for both "options_page" and "options_ui.page", the name of the
     38 // manifest field must be provided in |manifest_field_name|.
     39 bool ParseOptionsUrl(Extension* extension,
     40                      const std::string& url_string,
     41                      const std::string& manifest_field_name,
     42                      base::string16* error,
     43                      GURL* result) {
     44   if (extension->is_hosted_app()) {
     45     // Hosted apps require an absolute URL.
     46     GURL options_url(url_string);
     47     if (!options_url.is_valid() || !options_url.SchemeIsHTTPOrHTTPS()) {
     48       *error = base::ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp);
     49       return false;
     50     }
     51     *result = options_url;
     52     return true;
     53   }
     54 
     55   // Otherwise the options URL should be inside the extension.
     56   if (GURL(url_string).is_valid()) {
     57     *error = base::ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage);
     58     return false;
     59   }
     60 
     61   GURL resource_url = extension->GetResourceURL(url_string);
     62   if (!resource_url.is_valid()) {
     63     *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidOptionsPage,
     64                                                  manifest_field_name);
     65     return false;
     66   }
     67   *result = resource_url;
     68   return true;
     69 }
     70 
     71 }  // namespace
     72 
     73 OptionsPageInfo::OptionsPageInfo(const GURL& options_page,
     74                                  bool chrome_styles,
     75                                  bool open_in_tab)
     76     : options_page_(options_page),
     77       chrome_styles_(chrome_styles),
     78       open_in_tab_(open_in_tab) {
     79 }
     80 
     81 OptionsPageInfo::~OptionsPageInfo() {
     82 }
     83 
     84 // static
     85 const GURL& OptionsPageInfo::GetOptionsPage(const Extension* extension) {
     86   OptionsPageInfo* info = GetOptionsPageInfo(extension);
     87   return info ? info->options_page_ : GURL::EmptyGURL();
     88 }
     89 
     90 // static
     91 bool OptionsPageInfo::HasOptionsPage(const Extension* extension) {
     92   return !OptionsPageInfo::GetOptionsPage(extension).is_empty();
     93 }
     94 
     95 // static
     96 bool OptionsPageInfo::ShouldUseChromeStyle(const Extension* extension) {
     97   OptionsPageInfo* info = GetOptionsPageInfo(extension);
     98   return info && info->chrome_styles_;
     99 }
    100 
    101 // static
    102 bool OptionsPageInfo::ShouldOpenInTab(const Extension* extension) {
    103   OptionsPageInfo* info = GetOptionsPageInfo(extension);
    104   return info && info->open_in_tab_;
    105 }
    106 
    107 scoped_ptr<OptionsPageInfo> OptionsPageInfo::Create(
    108     Extension* extension,
    109     const base::Value* options_ui_value,
    110     const std::string& options_page_string,
    111     std::vector<InstallWarning>* install_warnings,
    112     base::string16* error) {
    113   GURL options_page;
    114   bool chrome_style = false;
    115   bool open_in_tab = !FeatureSwitch::embedded_extension_options()->IsEnabled();
    116 
    117   // Parse the options_ui object.
    118   if (options_ui_value &&
    119       FeatureSwitch::embedded_extension_options()->IsEnabled()) {
    120     base::string16 options_ui_error;
    121 
    122     scoped_ptr<OptionsUI> options_ui =
    123         OptionsUI::FromValue(*options_ui_value, &options_ui_error);
    124     if (!options_ui_error.empty()) {
    125       // OptionsUI::FromValue populates |error| both when there are
    126       // errors (in which case |options_ui| will be NULL) and warnings
    127       // (in which case |options_ui| will be valid). Either way, show it
    128       // as an install warning.
    129       install_warnings->push_back(
    130           InstallWarning(base::UTF16ToASCII(options_ui_error)));
    131     }
    132 
    133     if (options_ui) {
    134       base::string16 options_parse_error;
    135       if (!ParseOptionsUrl(extension,
    136                            options_ui->page,
    137                            keys::kOptionsUI,
    138                            &options_parse_error,
    139                            &options_page)) {
    140         install_warnings->push_back(
    141             InstallWarning(base::UTF16ToASCII(options_parse_error)));
    142       }
    143       chrome_style =
    144           options_ui->chrome_style.get() && *options_ui->chrome_style;
    145       open_in_tab = options_ui->open_in_tab.get() && *options_ui->open_in_tab;
    146     }
    147   }
    148 
    149   // Parse the legacy options_page entry if there was no entry for
    150   // options_ui.page.
    151   if (!options_page_string.empty() && !options_page.is_valid()) {
    152     if (!ParseOptionsUrl(extension,
    153                          options_page_string,
    154                          keys::kOptionsPage,
    155                          error,
    156                          &options_page)) {
    157       return scoped_ptr<OptionsPageInfo>();
    158     }
    159   }
    160 
    161   return make_scoped_ptr(
    162       new OptionsPageInfo(options_page, chrome_style, open_in_tab));
    163 }
    164 
    165 OptionsPageManifestHandler::OptionsPageManifestHandler() {
    166 }
    167 
    168 OptionsPageManifestHandler::~OptionsPageManifestHandler() {
    169 }
    170 
    171 bool OptionsPageManifestHandler::Parse(Extension* extension,
    172                                        base::string16* error) {
    173   std::vector<InstallWarning> install_warnings;
    174   const Manifest* manifest = extension->manifest();
    175 
    176   std::string options_page_string;
    177   if (manifest->HasPath(keys::kOptionsPage) &&
    178       !manifest->GetString(keys::kOptionsPage, &options_page_string)) {
    179     *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidOptionsPage,
    180                                                  keys::kOptionsPage);
    181     return false;
    182   }
    183 
    184   const base::Value* options_ui_value = NULL;
    185   ignore_result(manifest->Get(keys::kOptionsUI, &options_ui_value));
    186 
    187   scoped_ptr<OptionsPageInfo> info =
    188       OptionsPageInfo::Create(extension,
    189                               options_ui_value,
    190                               options_page_string,
    191                               &install_warnings,
    192                               error);
    193   if (!info)
    194     return false;
    195 
    196   extension->AddInstallWarnings(install_warnings);
    197   extension->SetManifestData(keys::kOptionsUI, info.release());
    198   return true;
    199 }
    200 
    201 bool OptionsPageManifestHandler::Validate(
    202     const Extension* extension,
    203     std::string* error,
    204     std::vector<InstallWarning>* warnings) const {
    205   // Validate path to the options page.  Don't check the URL for hosted apps,
    206   // because they are expected to refer to an external URL.
    207   if (!OptionsPageInfo::HasOptionsPage(extension) || extension->is_hosted_app())
    208     return true;
    209 
    210   base::FilePath options_path = file_util::ExtensionURLToRelativeFilePath(
    211       OptionsPageInfo::GetOptionsPage(extension));
    212   base::FilePath path = extension->GetResource(options_path).GetFilePath();
    213   if (path.empty() || !base::PathExists(path)) {
    214     *error = l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_OPTIONS_PAGE_FAILED,
    215                                        options_path.LossyDisplayName());
    216     return false;
    217   }
    218   return true;
    219 }
    220 
    221 const std::vector<std::string> OptionsPageManifestHandler::Keys() const {
    222   static const char* keys[] = {keys::kOptionsPage, keys::kOptionsUI};
    223   return std::vector<std::string>(keys, keys + arraysize(keys));
    224 }
    225 
    226 }  // namespace extensions
    227