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 "chrome/common/extensions/chrome_extensions_client.h" 6 7 #include "base/command_line.h" 8 #include "base/strings/string_util.h" 9 #include "chrome/common/chrome_switches.h" 10 #include "chrome/common/chrome_version_info.h" 11 #include "chrome/common/extensions/chrome_manifest_handlers.h" 12 #include "chrome/common/extensions/extension_constants.h" 13 #include "chrome/common/extensions/features/chrome_channel_feature_filter.h" 14 #include "chrome/common/extensions/features/feature_channel.h" 15 #include "chrome/common/url_constants.h" 16 #include "chrome/grit/chromium_strings.h" 17 #include "chrome/grit/common_resources.h" 18 #include "chrome/grit/generated_resources.h" 19 #include "content/public/common/url_constants.h" 20 #include "extensions/common/common_manifest_handlers.h" 21 #include "extensions/common/extension.h" 22 #include "extensions/common/extension_api.h" 23 #include "extensions/common/extension_urls.h" 24 #include "extensions/common/features/api_feature.h" 25 #include "extensions/common/features/base_feature_provider.h" 26 #include "extensions/common/features/feature_provider.h" 27 #include "extensions/common/features/json_feature_provider_source.h" 28 #include "extensions/common/features/manifest_feature.h" 29 #include "extensions/common/features/permission_feature.h" 30 #include "extensions/common/features/simple_feature.h" 31 #include "extensions/common/manifest_constants.h" 32 #include "extensions/common/manifest_handler.h" 33 #include "extensions/common/permissions/api_permission_set.h" 34 #include "extensions/common/permissions/permission_message.h" 35 #include "extensions/common/permissions/permissions_info.h" 36 #include "extensions/common/url_pattern.h" 37 #include "extensions/common/url_pattern_set.h" 38 #include "extensions/grit/extensions_resources.h" 39 #include "ui/base/l10n/l10n_util.h" 40 #include "url/gurl.h" 41 42 // TODO(thestig): Remove these #defines. This file should not be built when 43 // extensions are disabled. 44 #if defined(ENABLE_EXTENSIONS) 45 #include "chrome/common/extensions/api/generated_schemas.h" 46 #include "chrome/grit/extensions_api_resources.h" 47 #include "extensions/common/api/generated_schemas.h" 48 #endif 49 50 namespace extensions { 51 52 namespace { 53 54 // TODO(battre): Delete the HTTP URL once the blacklist is downloaded via HTTPS. 55 const char kExtensionBlocklistUrlPrefix[] = 56 "http://www.gstatic.com/chrome/extensions/blacklist"; 57 const char kExtensionBlocklistHttpsUrlPrefix[] = 58 "https://www.gstatic.com/chrome/extensions/blacklist"; 59 60 const char kThumbsWhiteListedExtension[] = "khopmbdjffemhegeeobelklnbglcdgfh"; 61 62 template <class FeatureClass> 63 SimpleFeature* CreateFeature() { 64 SimpleFeature* feature = new FeatureClass; 65 feature->AddFilter( 66 scoped_ptr<SimpleFeatureFilter>(new ChromeChannelFeatureFilter(feature))); 67 return feature; 68 } 69 70 } // namespace 71 72 static base::LazyInstance<ChromeExtensionsClient> g_client = 73 LAZY_INSTANCE_INITIALIZER; 74 75 ChromeExtensionsClient::ChromeExtensionsClient() 76 : chrome_api_permissions_(ChromeAPIPermissions()), 77 extensions_api_permissions_(ExtensionsAPIPermissions()) { 78 } 79 80 ChromeExtensionsClient::~ChromeExtensionsClient() { 81 } 82 83 void ChromeExtensionsClient::Initialize() { 84 // Registration could already be finalized in unit tests, where the utility 85 // thread runs in-process. 86 if (!ManifestHandler::IsRegistrationFinalized()) { 87 RegisterCommonManifestHandlers(); 88 #if defined(ENABLE_EXTENSIONS) 89 RegisterChromeManifestHandlers(); 90 #endif 91 ManifestHandler::FinalizeRegistration(); 92 } 93 94 // Set up permissions. 95 PermissionsInfo::GetInstance()->AddProvider(chrome_api_permissions_); 96 PermissionsInfo::GetInstance()->AddProvider(extensions_api_permissions_); 97 98 // Set up the scripting whitelist. 99 // Whitelist ChromeVox, an accessibility extension from Google that needs 100 // the ability to script webui pages. This is temporary and is not 101 // meant to be a general solution. 102 // TODO(dmazzoni): remove this once we have an extension API that 103 // allows any extension to request read-only access to webui pages. 104 scripting_whitelist_.push_back(extension_misc::kChromeVoxExtensionId); 105 106 // Whitelist "Discover DevTools Companion" extension from Google that 107 // needs the ability to script DevTools pages. Companion will assist 108 // online courses and will be needed while the online educational programs 109 // are in place. 110 scripting_whitelist_.push_back("angkfkebojeancgemegoedelbnjgcgme"); 111 } 112 113 const PermissionMessageProvider& 114 ChromeExtensionsClient::GetPermissionMessageProvider() const { 115 return permission_message_provider_; 116 } 117 118 const std::string ChromeExtensionsClient::GetProductName() { 119 return l10n_util::GetStringUTF8(IDS_PRODUCT_NAME); 120 } 121 122 scoped_ptr<FeatureProvider> ChromeExtensionsClient::CreateFeatureProvider( 123 const std::string& name) const { 124 scoped_ptr<FeatureProvider> provider; 125 scoped_ptr<JSONFeatureProviderSource> source( 126 CreateFeatureProviderSource(name)); 127 if (name == "api") { 128 provider.reset(new BaseFeatureProvider(source->dictionary(), 129 CreateFeature<APIFeature>)); 130 } else if (name == "manifest") { 131 provider.reset(new BaseFeatureProvider(source->dictionary(), 132 CreateFeature<ManifestFeature>)); 133 } else if (name == "permission") { 134 provider.reset(new BaseFeatureProvider(source->dictionary(), 135 CreateFeature<PermissionFeature>)); 136 } else { 137 NOTREACHED(); 138 } 139 return provider.Pass(); 140 } 141 142 scoped_ptr<JSONFeatureProviderSource> 143 ChromeExtensionsClient::CreateFeatureProviderSource( 144 const std::string& name) const { 145 scoped_ptr<JSONFeatureProviderSource> source( 146 new JSONFeatureProviderSource(name)); 147 if (name == "api") { 148 source->LoadJSON(IDR_EXTENSION_API_FEATURES); 149 source->LoadJSON(IDR_CHROME_EXTENSION_API_FEATURES); 150 } else if (name == "manifest") { 151 source->LoadJSON(IDR_EXTENSION_MANIFEST_FEATURES); 152 source->LoadJSON(IDR_CHROME_EXTENSION_MANIFEST_FEATURES); 153 } else if (name == "permission") { 154 source->LoadJSON(IDR_EXTENSION_PERMISSION_FEATURES); 155 source->LoadJSON(IDR_CHROME_EXTENSION_PERMISSION_FEATURES); 156 } else { 157 NOTREACHED(); 158 source.reset(); 159 } 160 return source.Pass(); 161 } 162 163 void ChromeExtensionsClient::FilterHostPermissions( 164 const URLPatternSet& hosts, 165 URLPatternSet* new_hosts, 166 std::set<PermissionMessage>* messages) const { 167 for (URLPatternSet::const_iterator i = hosts.begin(); 168 i != hosts.end(); ++i) { 169 // Filters out every URL pattern that matches chrome:// scheme. 170 if (i->scheme() == content::kChromeUIScheme) { 171 // chrome://favicon is the only URL for chrome:// scheme that we 172 // want to support. We want to deprecate the "chrome" scheme. 173 // We should not add any additional "host" here. 174 if (GURL(chrome::kChromeUIFaviconURL).host() != i->host()) 175 continue; 176 messages->insert(PermissionMessage( 177 PermissionMessage::kFavicon, 178 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FAVICON))); 179 } else { 180 new_hosts->AddPattern(*i); 181 } 182 } 183 } 184 185 void ChromeExtensionsClient::SetScriptingWhitelist( 186 const ExtensionsClient::ScriptingWhitelist& whitelist) { 187 scripting_whitelist_ = whitelist; 188 } 189 190 const ExtensionsClient::ScriptingWhitelist& 191 ChromeExtensionsClient::GetScriptingWhitelist() const { 192 return scripting_whitelist_; 193 } 194 195 URLPatternSet ChromeExtensionsClient::GetPermittedChromeSchemeHosts( 196 const Extension* extension, 197 const APIPermissionSet& api_permissions) const { 198 URLPatternSet hosts; 199 // Regular extensions are only allowed access to chrome://favicon. 200 hosts.AddPattern(URLPattern(URLPattern::SCHEME_CHROMEUI, 201 chrome::kChromeUIFaviconURL)); 202 203 // Experimental extensions are also allowed chrome://thumb. 204 // 205 // TODO: A public API should be created for retrieving thumbnails. 206 // See http://crbug.com/222856. A temporary hack is implemented here to 207 // make chrome://thumbs available to NTP Russia extension as 208 // non-experimental. 209 if ((api_permissions.find(APIPermission::kExperimental) != 210 api_permissions.end()) || 211 (extension->id() == kThumbsWhiteListedExtension && 212 extension->from_webstore())) { 213 hosts.AddPattern(URLPattern(URLPattern::SCHEME_CHROMEUI, 214 chrome::kChromeUIThumbnailURL)); 215 } 216 return hosts; 217 } 218 219 bool ChromeExtensionsClient::IsScriptableURL( 220 const GURL& url, std::string* error) const { 221 // The gallery is special-cased as a restricted URL for scripting to prevent 222 // access to special JS bindings we expose to the gallery (and avoid things 223 // like extensions removing the "report abuse" link). 224 // TODO(erikkay): This seems like the wrong test. Shouldn't we we testing 225 // against the store app extent? 226 GURL store_url(extension_urls::GetWebstoreLaunchURL()); 227 if (url.host() == store_url.host()) { 228 if (error) 229 *error = manifest_errors::kCannotScriptGallery; 230 return false; 231 } 232 return true; 233 } 234 235 bool ChromeExtensionsClient::IsAPISchemaGenerated( 236 const std::string& name) const { 237 #if defined(ENABLE_EXTENSIONS) 238 // Test from most common to least common. 239 return api::GeneratedSchemas::IsGenerated(name) || 240 core_api::GeneratedSchemas::IsGenerated(name); 241 #else 242 return false; 243 #endif 244 } 245 246 base::StringPiece ChromeExtensionsClient::GetAPISchema( 247 const std::string& name) const { 248 #if defined(ENABLE_EXTENSIONS) 249 // Test from most common to least common. 250 if (api::GeneratedSchemas::IsGenerated(name)) 251 return api::GeneratedSchemas::Get(name); 252 253 return core_api::GeneratedSchemas::Get(name); 254 #else 255 return base::StringPiece(); 256 #endif 257 } 258 259 void ChromeExtensionsClient::RegisterAPISchemaResources( 260 ExtensionAPI* api) const { 261 #if defined(ENABLE_EXTENSIONS) 262 api->RegisterSchemaResource("accessibilityPrivate", 263 IDR_EXTENSION_API_JSON_ACCESSIBILITYPRIVATE); 264 api->RegisterSchemaResource("app", IDR_EXTENSION_API_JSON_APP); 265 api->RegisterSchemaResource("browserAction", 266 IDR_EXTENSION_API_JSON_BROWSERACTION); 267 api->RegisterSchemaResource("commands", IDR_EXTENSION_API_JSON_COMMANDS); 268 api->RegisterSchemaResource("declarativeContent", 269 IDR_EXTENSION_API_JSON_DECLARATIVE_CONTENT); 270 api->RegisterSchemaResource("declarativeWebRequest", 271 IDR_EXTENSION_API_JSON_DECLARATIVE_WEBREQUEST); 272 api->RegisterSchemaResource("fileBrowserHandler", 273 IDR_EXTENSION_API_JSON_FILEBROWSERHANDLER); 274 api->RegisterSchemaResource("inputMethodPrivate", 275 IDR_EXTENSION_API_JSON_INPUTMETHODPRIVATE); 276 api->RegisterSchemaResource("pageAction", IDR_EXTENSION_API_JSON_PAGEACTION); 277 api->RegisterSchemaResource("privacy", IDR_EXTENSION_API_JSON_PRIVACY); 278 api->RegisterSchemaResource("processes", IDR_EXTENSION_API_JSON_PROCESSES); 279 api->RegisterSchemaResource("proxy", IDR_EXTENSION_API_JSON_PROXY); 280 api->RegisterSchemaResource("scriptBadge", 281 IDR_EXTENSION_API_JSON_SCRIPTBADGE); 282 api->RegisterSchemaResource("ttsEngine", IDR_EXTENSION_API_JSON_TTSENGINE); 283 api->RegisterSchemaResource("tts", IDR_EXTENSION_API_JSON_TTS); 284 api->RegisterSchemaResource("types", IDR_EXTENSION_API_JSON_TYPES); 285 api->RegisterSchemaResource("types.private", 286 IDR_EXTENSION_API_JSON_TYPES_PRIVATE); 287 api->RegisterSchemaResource("webstore", IDR_EXTENSION_API_JSON_WEBSTORE); 288 api->RegisterSchemaResource("webViewRequest", 289 IDR_EXTENSION_API_JSON_WEB_VIEW_REQUEST); 290 #endif // defined(ENABLE_EXTENSIONS) 291 } 292 293 bool ChromeExtensionsClient::ShouldSuppressFatalErrors() const { 294 // Suppress fatal errors only on beta and stable channels. 295 return GetCurrentChannel() > chrome::VersionInfo::CHANNEL_DEV; 296 } 297 298 std::string ChromeExtensionsClient::GetWebstoreBaseURL() const { 299 std::string gallery_prefix = extension_urls::kChromeWebstoreBaseURL; 300 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAppsGalleryURL)) 301 gallery_prefix = CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 302 switches::kAppsGalleryURL); 303 if (EndsWith(gallery_prefix, "/", true)) 304 gallery_prefix = gallery_prefix.substr(0, gallery_prefix.length() - 1); 305 return gallery_prefix; 306 } 307 308 std::string ChromeExtensionsClient::GetWebstoreUpdateURL() const { 309 CommandLine* cmdline = CommandLine::ForCurrentProcess(); 310 if (cmdline->HasSwitch(switches::kAppsGalleryUpdateURL)) 311 return cmdline->GetSwitchValueASCII(switches::kAppsGalleryUpdateURL); 312 else 313 return extension_urls::GetDefaultWebstoreUpdateUrl().spec(); 314 } 315 316 bool ChromeExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const { 317 // The extension blacklist URL is returned from the update service and 318 // therefore not determined by Chromium. If the location of the blacklist file 319 // ever changes, we need to update this function. A DCHECK in the 320 // ExtensionUpdater ensures that we notice a change. This is the full URL 321 // of a blacklist: 322 // http://www.gstatic.com/chrome/extensions/blacklist/l_0_0_0_7.txt 323 return StartsWithASCII(url.spec(), kExtensionBlocklistUrlPrefix, true) || 324 StartsWithASCII(url.spec(), kExtensionBlocklistHttpsUrlPrefix, true); 325 } 326 327 // static 328 ChromeExtensionsClient* ChromeExtensionsClient::GetInstance() { 329 return g_client.Pointer(); 330 } 331 332 } // namespace extensions 333