OLD | NEW |
(Empty) | |
| 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 "chrome/browser/extensions/url_request_util.h" |
| 6 |
| 7 #include <string> |
| 8 |
| 9 #include "base/files/file_path.h" |
| 10 #include "base/memory/weak_ptr.h" |
| 11 #include "base/path_service.h" |
| 12 #include "base/strings/string_number_conversions.h" |
| 13 #include "base/strings/string_util.h" |
| 14 #include "base/strings/stringprintf.h" |
| 15 #include "base/task_runner_util.h" |
| 16 #include "chrome/browser/extensions/extension_renderer_state.h" |
| 17 #include "chrome/browser/extensions/image_loader.h" |
| 18 #include "chrome/common/chrome_paths.h" |
| 19 #include "chrome/common/extensions/manifest_url_handler.h" |
| 20 #include "content/public/browser/browser_thread.h" |
| 21 #include "content/public/browser/resource_request_info.h" |
| 22 #include "extensions/browser/extension_protocols.h" |
| 23 #include "extensions/browser/info_map.h" |
| 24 #include "extensions/common/file_util.h" |
| 25 #include "extensions/common/manifest_handlers/icons_handler.h" |
| 26 #include "extensions/common/manifest_handlers/web_accessible_resources_info.h" |
| 27 #include "extensions/common/manifest_handlers/webview_info.h" |
| 28 #include "net/base/mime_util.h" |
| 29 #include "net/base/net_errors.h" |
| 30 #include "net/http/http_request_headers.h" |
| 31 #include "net/http/http_response_headers.h" |
| 32 #include "net/http/http_response_info.h" |
| 33 #include "net/url_request/url_request.h" |
| 34 #include "net/url_request/url_request_simple_job.h" |
| 35 #include "ui/base/resource/resource_bundle.h" |
| 36 |
| 37 using content::BrowserThread; |
| 38 |
| 39 namespace { |
| 40 |
| 41 // A request for an extension resource in a Chrome .pak file. These are used |
| 42 // by component extensions. |
| 43 class URLRequestResourceBundleJob : public net::URLRequestSimpleJob { |
| 44 public: |
| 45 URLRequestResourceBundleJob(net::URLRequest* request, |
| 46 net::NetworkDelegate* network_delegate, |
| 47 const base::FilePath& filename, |
| 48 int resource_id, |
| 49 const std::string& content_security_policy, |
| 50 bool send_cors_header) |
| 51 : net::URLRequestSimpleJob(request, network_delegate), |
| 52 filename_(filename), |
| 53 resource_id_(resource_id), |
| 54 weak_factory_(this) { |
| 55 // Leave cache headers out of resource bundle requests. |
| 56 response_info_.headers = extensions::BuildHttpHeaders( |
| 57 content_security_policy, send_cors_header, base::Time()); |
| 58 } |
| 59 |
| 60 // Overridden from URLRequestSimpleJob: |
| 61 virtual int GetData(std::string* mime_type, |
| 62 std::string* charset, |
| 63 std::string* data, |
| 64 const net::CompletionCallback& callback) const OVERRIDE { |
| 65 const ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 66 *data = rb.GetRawDataResource(resource_id_).as_string(); |
| 67 |
| 68 // Add the Content-Length header now that we know the resource length. |
| 69 response_info_.headers->AddHeader( |
| 70 base::StringPrintf("%s: %s", |
| 71 net::HttpRequestHeaders::kContentLength, |
| 72 base::UintToString(data->size()).c_str())); |
| 73 |
| 74 std::string* read_mime_type = new std::string; |
| 75 bool posted = base::PostTaskAndReplyWithResult( |
| 76 BrowserThread::GetBlockingPool(), |
| 77 FROM_HERE, |
| 78 base::Bind(&net::GetMimeTypeFromFile, |
| 79 filename_, |
| 80 base::Unretained(read_mime_type)), |
| 81 base::Bind(&URLRequestResourceBundleJob::OnMimeTypeRead, |
| 82 weak_factory_.GetWeakPtr(), |
| 83 mime_type, |
| 84 charset, |
| 85 data, |
| 86 base::Owned(read_mime_type), |
| 87 callback)); |
| 88 DCHECK(posted); |
| 89 |
| 90 return net::ERR_IO_PENDING; |
| 91 } |
| 92 |
| 93 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE { |
| 94 *info = response_info_; |
| 95 } |
| 96 |
| 97 private: |
| 98 virtual ~URLRequestResourceBundleJob() {} |
| 99 |
| 100 void OnMimeTypeRead(std::string* out_mime_type, |
| 101 std::string* charset, |
| 102 std::string* data, |
| 103 std::string* read_mime_type, |
| 104 const net::CompletionCallback& callback, |
| 105 bool read_result) { |
| 106 *out_mime_type = *read_mime_type; |
| 107 if (StartsWithASCII(*read_mime_type, "text/", false)) { |
| 108 // All of our HTML files should be UTF-8 and for other resource types |
| 109 // (like images), charset doesn't matter. |
| 110 DCHECK(IsStringUTF8(*data)); |
| 111 *charset = "utf-8"; |
| 112 } |
| 113 int result = read_result ? net::OK : net::ERR_INVALID_URL; |
| 114 callback.Run(result); |
| 115 } |
| 116 |
| 117 // We need the filename of the resource to determine the mime type. |
| 118 base::FilePath filename_; |
| 119 |
| 120 // The resource bundle id to load. |
| 121 int resource_id_; |
| 122 |
| 123 net::HttpResponseInfo response_info_; |
| 124 |
| 125 mutable base::WeakPtrFactory<URLRequestResourceBundleJob> weak_factory_; |
| 126 }; |
| 127 |
| 128 } // namespace |
| 129 |
| 130 namespace extensions { |
| 131 namespace url_request_util { |
| 132 |
| 133 bool AllowCrossRendererResourceLoad(net::URLRequest* request, |
| 134 bool is_incognito, |
| 135 const Extension* extension, |
| 136 InfoMap* extension_info_map) { |
| 137 const content::ResourceRequestInfo* info = |
| 138 content::ResourceRequestInfo::ForRequest(request); |
| 139 |
| 140 // Check workers so that importScripts works from extension workers. |
| 141 if (extension_info_map->worker_process_map().Contains(request->url().host(), |
| 142 info->GetChildID())) { |
| 143 return true; |
| 144 } |
| 145 |
| 146 // Extensions with webview: allow loading certain resources by guest renderers |
| 147 // with privileged partition IDs as specified in the manifest file. |
| 148 ExtensionRendererState* renderer_state = |
| 149 ExtensionRendererState::GetInstance(); |
| 150 ExtensionRendererState::WebViewInfo webview_info; |
| 151 bool is_guest = renderer_state->GetWebViewInfo( |
| 152 info->GetChildID(), info->GetRouteID(), &webview_info); |
| 153 std::string resource_path = request->url().path(); |
| 154 if (is_guest && WebviewInfo::IsResourceWebviewAccessible( |
| 155 extension, webview_info.partition_id, resource_path)) { |
| 156 return true; |
| 157 } |
| 158 |
| 159 // If the request is for navigations outside of webviews, then it should be |
| 160 // allowed. The navigation logic in CrossSiteResourceHandler will properly |
| 161 // transfer the navigation to a privileged process before it commits. |
| 162 if (ResourceType::IsFrame(info->GetResourceType()) && !is_guest) |
| 163 return true; |
| 164 |
| 165 if (!content::PageTransitionIsWebTriggerable(info->GetPageTransition())) |
| 166 return false; |
| 167 |
| 168 // The following checks require that we have an actual extension object. If we |
| 169 // don't have it, allow the request handling to continue with the rest of the |
| 170 // checks. |
| 171 if (!extension) |
| 172 return true; |
| 173 |
| 174 // Disallow loading of packaged resources for hosted apps. We don't allow |
| 175 // hybrid hosted/packaged apps. The one exception is access to icons, since |
| 176 // some extensions want to be able to do things like create their own |
| 177 // launchers. |
| 178 std::string resource_root_relative_path = |
| 179 request->url().path().empty() ? std::string() |
| 180 : request->url().path().substr(1); |
| 181 if (extension->is_hosted_app() && |
| 182 !IconsInfo::GetIcons(extension) |
| 183 .ContainsPath(resource_root_relative_path)) { |
| 184 LOG(ERROR) << "Denying load of " << request->url().spec() << " from " |
| 185 << "hosted app."; |
| 186 return false; |
| 187 } |
| 188 |
| 189 // Extensions with web_accessible_resources: allow loading by regular |
| 190 // renderers. Since not all subresources are required to be listed in a v2 |
| 191 // manifest, we must allow all loads if there are any web accessible |
| 192 // resources. See http://crbug.com/179127. |
| 193 if (extension->manifest_version() < 2 || |
| 194 WebAccessibleResourcesInfo::HasWebAccessibleResources(extension)) { |
| 195 return true; |
| 196 } |
| 197 |
| 198 // If there aren't any explicitly marked web accessible resources, the |
| 199 // load should be allowed only if it is by DevTools. A close approximation is |
| 200 // checking if the extension contains a DevTools page. |
| 201 if (!ManifestURL::GetDevToolsPage(extension).is_empty()) |
| 202 return true; |
| 203 |
| 204 // No special exception. Block the load. |
| 205 return false; |
| 206 } |
| 207 |
| 208 net::URLRequestJob* MaybeCreateURLRequestResourceBundleJob( |
| 209 net::URLRequest* request, |
| 210 net::NetworkDelegate* network_delegate, |
| 211 const base::FilePath& directory_path, |
| 212 const std::string& content_security_policy, |
| 213 bool send_cors_header) { |
| 214 base::FilePath resources_path; |
| 215 base::FilePath relative_path; |
| 216 // Try to load extension resources from chrome resource file if |
| 217 // directory_path is a descendant of resources_path. resources_path |
| 218 // corresponds to src/chrome/browser/resources in source tree. |
| 219 if (PathService::Get(chrome::DIR_RESOURCES, &resources_path) && |
| 220 // Since component extension resources are included in |
| 221 // component_extension_resources.pak file in resources_path, calculate |
| 222 // extension relative path against resources_path. |
| 223 resources_path.AppendRelativePath(directory_path, &relative_path)) { |
| 224 base::FilePath request_path = |
| 225 extensions::file_util::ExtensionURLToRelativeFilePath(request->url()); |
| 226 int resource_id = 0; |
| 227 if (extensions::ImageLoader::IsComponentExtensionResource( |
| 228 directory_path, request_path, &resource_id)) { |
| 229 relative_path = relative_path.Append(request_path); |
| 230 relative_path = relative_path.NormalizePathSeparators(); |
| 231 return new URLRequestResourceBundleJob(request, |
| 232 network_delegate, |
| 233 relative_path, |
| 234 resource_id, |
| 235 content_security_policy, |
| 236 send_cors_header); |
| 237 } |
| 238 } |
| 239 return NULL; |
| 240 } |
| 241 |
| 242 } // namespace url_request_util |
| 243 } // namespace extensions |
OLD | NEW |