1ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be 3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file. 4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_protocols.h" 6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm> 8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_path.h" 10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h" 11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/message_loop.h" 12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/path_service.h" 13c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h" 143f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread_restrictions.h" 15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "build/build_config.h" 16c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/net/chrome_url_request_context.h" 17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_paths.h" 18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension.h" 19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_file_util.h" 20c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_resource.h" 21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/url_constants.h" 22dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/resource_dispatcher_host.h" 23dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h" 24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "googleurl/src/url_util.h" 253f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "grit/component_extension_resources_map.h" 26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/mime_util.h" 27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/net_errors.h" 28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/url_request/url_request_error_job.h" 29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/url_request/url_request_file_job.h" 30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/url_request/url_request_simple_job.h" 3172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h" 32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace { 34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 3521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenclass URLRequestResourceBundleJob : public net::URLRequestSimpleJob { 36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public: 3721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen explicit URLRequestResourceBundleJob(net::URLRequest* request, 38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const FilePath& filename, int resource_id) 3921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen : net::URLRequestSimpleJob(request), 40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch filename_(filename), 41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch resource_id_(resource_id) { } 42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 4321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen // Overridden from URLRequestSimpleJob: 44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch virtual bool GetData(std::string* mime_type, 45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string* charset, 46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch std::string* data) const { 47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *data = rb.GetRawDataResource(resource_id_).as_string(); 49ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 50ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // Requests should not block on the disk! On Windows this goes to the 51ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // registry. 52ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen // http://code.google.com/p/chromium/issues/detail?id=59849 53ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen bool result; 54ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen { 55ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen base::ThreadRestrictions::ScopedAllowIO allow_io; 56ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen result = net::GetMimeTypeFromFile(filename_, mime_type); 57ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 58ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen 59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (StartsWithASCII(*mime_type, "text/", false)) { 60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // All of our HTML files should be UTF-8 and for other resource types 61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // (like images), charset doesn't matter. 62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch DCHECK(IsStringUTF8(*data)); 63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch *charset = "utf-8"; 64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return result; 66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private: 69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch virtual ~URLRequestResourceBundleJob() { } 70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // We need the filename of the resource to determine the mime type. 72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath filename_; 73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // The resource bundle id to load. 75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch int resource_id_; 76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}; 77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 783345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// Returns true if an chrome-extension:// resource should be allowed to load. 7972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// TODO(aa): This should be moved into ExtensionResourceRequestPolicy, but we 8072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// first need to find a way to get CanLoadInIncognito state into the renderers. 8121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenbool AllowExtensionResourceLoad(net::URLRequest* request, 823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ChromeURLRequestContext* context, 833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const std::string& scheme) { 843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const ResourceDispatcherHostRequestInfo* info = 853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ResourceDispatcherHost::InfoForRequest(request); 863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // We have seen crashes where info is NULL: crbug.com/52374. 883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!info) { 893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick LOG(ERROR) << "Allowing load of " << request->url().spec() 903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick << "from unknown origin. Could not find user data for " 913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick << "request."; 923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return true; 933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 943345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // Don't allow toplevel navigations to extension resources in incognito mode. 963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // This is because an extension must run in a single process, and an 973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick // incognito tab prevents that. 98ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (context->is_incognito() && 993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick info->resource_type() == ResourceType::MAIN_FRAME && 100731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick !context->extension_info_map()-> 101731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick ExtensionCanLoadInIncognito(request->url().host())) { 1023345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick LOG(ERROR) << "Denying load of " << request->url().spec() << " from " 1033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick << "incognito tab."; 1043345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return false; 1053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick } 1063345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 10772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen return true; 1083345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 1093345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // namespace 111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 11221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// Factory registered with net::URLRequest to create URLRequestJobs for 11321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// extension:// URLs. 11421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenstatic net::URLRequestJob* CreateExtensionURLRequestJob( 11521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen net::URLRequest* request, 11621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen const std::string& scheme) { 117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ChromeURLRequestContext* context = 118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static_cast<ChromeURLRequestContext*>(request->context()); 119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(mpcomplete): better error code. 12172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen if (!AllowExtensionResourceLoad(request, context, scheme)) { 12272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen LOG(ERROR) << "disallowed in extension protocols"; 12321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen return new net::URLRequestErrorJob(request, net::ERR_ADDRESS_UNREACHABLE); 12472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen } 125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // chrome-extension://extension-id/resource/path.js 127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch const std::string& extension_id = request->url().host(); 128731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick FilePath directory_path = context->extension_info_map()-> 129731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick GetPathForExtension(extension_id); 130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (directory_path.value().empty()) { 131ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (context->extension_info_map()->URLIsForExtensionIcon(request->url())) 132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen directory_path = context->extension_info_map()-> 133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen GetPathForDisabledExtension(extension_id); 134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen if (directory_path.value().empty()) { 135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen LOG(WARNING) << "Failed to GetPathForExtension: " << extension_id; 136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen return NULL; 137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen } 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath resources_path; 141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (PathService::Get(chrome::DIR_RESOURCES, &resources_path) && 142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch directory_path.DirName() == resources_path) { 143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath relative_path = directory_path.BaseName().Append( 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch extension_file_util::ExtensionURLToRelativeFilePath(request->url())); 145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN) 146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch relative_path = relative_path.NormalizeWindowsPathSeparators(); 147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(tc): Make a map of FilePath -> resource ids so we don't have to 150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // covert to FilePaths all the time. This will be more useful as we add 151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // more resources. 1523f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen for (size_t i = 0; i < kComponentExtensionResourcesSize; ++i) { 153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath bm_resource_path = 1543f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen FilePath().AppendASCII(kComponentExtensionResources[i].name); 155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN) 156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch bm_resource_path = bm_resource_path.NormalizeWindowsPathSeparators(); 157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif 158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch if (relative_path == bm_resource_path) { 159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch return new URLRequestResourceBundleJob(request, relative_path, 1603f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen kComponentExtensionResources[i].value); 161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch } 164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // TODO(tc): Move all of these files into resources.pak so we don't break 165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // when updating on Linux. 166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExtensionResource resource(extension_id, directory_path, 167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch extension_file_util::ExtensionURLToRelativeFilePath(request->url())); 168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 1694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch FilePath resource_file_path; 1704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch { 1714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch // Getting the file path will touch the file system. Fixing 1724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch // crbug.com/59849 would also fix this. Suppress the error for now. 1734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch base::ThreadRestrictions::ScopedAllowIO allow_io; 1744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch resource_file_path = resource.GetFilePath(); 1754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch } 17621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen return new net::URLRequestFileJob(request, resource_file_path); 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 17921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen// Factory registered with net::URLRequest to create URLRequestJobs for 180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// chrome-user-script:/ URLs. 18121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenstatic net::URLRequestJob* CreateUserScriptURLRequestJob( 18221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen net::URLRequest* request, 18321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen const std::string& scheme) { 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ChromeURLRequestContext* context = 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch static_cast<ChromeURLRequestContext*>(request->context()); 186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch // chrome-user-script:/user-script-name.user.js 188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch FilePath directory_path = context->user_script_dir_path(); 189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch ExtensionResource resource(request->url().host(), directory_path, 191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch extension_file_util::ExtensionURLToRelativeFilePath(request->url())); 192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 19321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen return new net::URLRequestFileJob(request, resource.GetFilePath()); 194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid RegisterExtensionProtocols() { 19721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen net::URLRequest::RegisterProtocolFactory(chrome::kExtensionScheme, 19821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen &CreateExtensionURLRequestJob); 19921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen net::URLRequest::RegisterProtocolFactory(chrome::kUserScriptScheme, 20021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen &CreateUserScriptURLRequestJob); 201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} 202