1// Copyright (c) 2011 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/browser/extensions/extension_protocols.h" 6 7#include <algorithm> 8 9#include "base/file_path.h" 10#include "base/logging.h" 11#include "base/message_loop.h" 12#include "base/path_service.h" 13#include "base/string_util.h" 14#include "base/threading/thread_restrictions.h" 15#include "build/build_config.h" 16#include "chrome/browser/net/chrome_url_request_context.h" 17#include "chrome/common/chrome_paths.h" 18#include "chrome/common/extensions/extension.h" 19#include "chrome/common/extensions/extension_file_util.h" 20#include "chrome/common/extensions/extension_resource.h" 21#include "chrome/common/url_constants.h" 22#include "content/browser/renderer_host/resource_dispatcher_host.h" 23#include "content/browser/renderer_host/resource_dispatcher_host_request_info.h" 24#include "googleurl/src/url_util.h" 25#include "grit/component_extension_resources_map.h" 26#include "net/base/mime_util.h" 27#include "net/base/net_errors.h" 28#include "net/url_request/url_request_error_job.h" 29#include "net/url_request/url_request_file_job.h" 30#include "net/url_request/url_request_simple_job.h" 31#include "ui/base/resource/resource_bundle.h" 32 33namespace { 34 35class URLRequestResourceBundleJob : public net::URLRequestSimpleJob { 36 public: 37 explicit URLRequestResourceBundleJob(net::URLRequest* request, 38 const FilePath& filename, int resource_id) 39 : net::URLRequestSimpleJob(request), 40 filename_(filename), 41 resource_id_(resource_id) { } 42 43 // Overridden from URLRequestSimpleJob: 44 virtual bool GetData(std::string* mime_type, 45 std::string* charset, 46 std::string* data) const { 47 const ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 48 *data = rb.GetRawDataResource(resource_id_).as_string(); 49 50 // Requests should not block on the disk! On Windows this goes to the 51 // registry. 52 // http://code.google.com/p/chromium/issues/detail?id=59849 53 bool result; 54 { 55 base::ThreadRestrictions::ScopedAllowIO allow_io; 56 result = net::GetMimeTypeFromFile(filename_, mime_type); 57 } 58 59 if (StartsWithASCII(*mime_type, "text/", false)) { 60 // All of our HTML files should be UTF-8 and for other resource types 61 // (like images), charset doesn't matter. 62 DCHECK(IsStringUTF8(*data)); 63 *charset = "utf-8"; 64 } 65 return result; 66 } 67 68 private: 69 virtual ~URLRequestResourceBundleJob() { } 70 71 // We need the filename of the resource to determine the mime type. 72 FilePath filename_; 73 74 // The resource bundle id to load. 75 int resource_id_; 76}; 77 78// Returns true if an chrome-extension:// resource should be allowed to load. 79// TODO(aa): This should be moved into ExtensionResourceRequestPolicy, but we 80// first need to find a way to get CanLoadInIncognito state into the renderers. 81bool AllowExtensionResourceLoad(net::URLRequest* request, 82 ChromeURLRequestContext* context, 83 const std::string& scheme) { 84 const ResourceDispatcherHostRequestInfo* info = 85 ResourceDispatcherHost::InfoForRequest(request); 86 87 // We have seen crashes where info is NULL: crbug.com/52374. 88 if (!info) { 89 LOG(ERROR) << "Allowing load of " << request->url().spec() 90 << "from unknown origin. Could not find user data for " 91 << "request."; 92 return true; 93 } 94 95 // Don't allow toplevel navigations to extension resources in incognito mode. 96 // This is because an extension must run in a single process, and an 97 // incognito tab prevents that. 98 if (context->is_incognito() && 99 info->resource_type() == ResourceType::MAIN_FRAME && 100 !context->extension_info_map()-> 101 ExtensionCanLoadInIncognito(request->url().host())) { 102 LOG(ERROR) << "Denying load of " << request->url().spec() << " from " 103 << "incognito tab."; 104 return false; 105 } 106 107 return true; 108} 109 110} // namespace 111 112// Factory registered with net::URLRequest to create URLRequestJobs for 113// extension:// URLs. 114static net::URLRequestJob* CreateExtensionURLRequestJob( 115 net::URLRequest* request, 116 const std::string& scheme) { 117 ChromeURLRequestContext* context = 118 static_cast<ChromeURLRequestContext*>(request->context()); 119 120 // TODO(mpcomplete): better error code. 121 if (!AllowExtensionResourceLoad(request, context, scheme)) { 122 LOG(ERROR) << "disallowed in extension protocols"; 123 return new net::URLRequestErrorJob(request, net::ERR_ADDRESS_UNREACHABLE); 124 } 125 126 // chrome-extension://extension-id/resource/path.js 127 const std::string& extension_id = request->url().host(); 128 FilePath directory_path = context->extension_info_map()-> 129 GetPathForExtension(extension_id); 130 if (directory_path.value().empty()) { 131 if (context->extension_info_map()->URLIsForExtensionIcon(request->url())) 132 directory_path = context->extension_info_map()-> 133 GetPathForDisabledExtension(extension_id); 134 if (directory_path.value().empty()) { 135 LOG(WARNING) << "Failed to GetPathForExtension: " << extension_id; 136 return NULL; 137 } 138 } 139 140 FilePath resources_path; 141 if (PathService::Get(chrome::DIR_RESOURCES, &resources_path) && 142 directory_path.DirName() == resources_path) { 143 FilePath relative_path = directory_path.BaseName().Append( 144 extension_file_util::ExtensionURLToRelativeFilePath(request->url())); 145#if defined(OS_WIN) 146 relative_path = relative_path.NormalizeWindowsPathSeparators(); 147#endif 148 149 // TODO(tc): Make a map of FilePath -> resource ids so we don't have to 150 // covert to FilePaths all the time. This will be more useful as we add 151 // more resources. 152 for (size_t i = 0; i < kComponentExtensionResourcesSize; ++i) { 153 FilePath bm_resource_path = 154 FilePath().AppendASCII(kComponentExtensionResources[i].name); 155#if defined(OS_WIN) 156 bm_resource_path = bm_resource_path.NormalizeWindowsPathSeparators(); 157#endif 158 if (relative_path == bm_resource_path) { 159 return new URLRequestResourceBundleJob(request, relative_path, 160 kComponentExtensionResources[i].value); 161 } 162 } 163 } 164 // TODO(tc): Move all of these files into resources.pak so we don't break 165 // when updating on Linux. 166 ExtensionResource resource(extension_id, directory_path, 167 extension_file_util::ExtensionURLToRelativeFilePath(request->url())); 168 169 FilePath resource_file_path; 170 { 171 // Getting the file path will touch the file system. Fixing 172 // crbug.com/59849 would also fix this. Suppress the error for now. 173 base::ThreadRestrictions::ScopedAllowIO allow_io; 174 resource_file_path = resource.GetFilePath(); 175 } 176 return new net::URLRequestFileJob(request, resource_file_path); 177} 178 179// Factory registered with net::URLRequest to create URLRequestJobs for 180// chrome-user-script:/ URLs. 181static net::URLRequestJob* CreateUserScriptURLRequestJob( 182 net::URLRequest* request, 183 const std::string& scheme) { 184 ChromeURLRequestContext* context = 185 static_cast<ChromeURLRequestContext*>(request->context()); 186 187 // chrome-user-script:/user-script-name.user.js 188 FilePath directory_path = context->user_script_dir_path(); 189 190 ExtensionResource resource(request->url().host(), directory_path, 191 extension_file_util::ExtensionURLToRelativeFilePath(request->url())); 192 193 return new net::URLRequestFileJob(request, resource.GetFilePath()); 194} 195 196void RegisterExtensionProtocols() { 197 net::URLRequest::RegisterProtocolFactory(chrome::kExtensionScheme, 198 &CreateExtensionURLRequestJob); 199 net::URLRequest::RegisterProtocolFactory(chrome::kUserScriptScheme, 200 &CreateUserScriptURLRequestJob); 201} 202