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