Do not update the url text while in batch edit mode.
[chromium-blink-merge.git] / extensions / browser / extension_protocols.cc
blob8ad21c1d4e0f45668b83ff50eb63ef0912b94fb7
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.
5 #include "extensions/browser/extension_protocols.h"
7 #include <algorithm>
8 #include <string>
9 #include <vector>
11 #include "base/base64.h"
12 #include "base/compiler_specific.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/format_macros.h"
16 #include "base/logging.h"
17 #include "base/memory/weak_ptr.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/metrics/field_trial.h"
20 #include "base/metrics/histogram.h"
21 #include "base/metrics/sparse_histogram.h"
22 #include "base/sha1.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/threading/sequenced_worker_pool.h"
28 #include "base/threading/thread_restrictions.h"
29 #include "base/timer/elapsed_timer.h"
30 #include "build/build_config.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/resource_request_info.h"
33 #include "crypto/secure_hash.h"
34 #include "crypto/sha2.h"
35 #include "extensions/browser/content_verifier.h"
36 #include "extensions/browser/content_verify_job.h"
37 #include "extensions/browser/extensions_browser_client.h"
38 #include "extensions/browser/info_map.h"
39 #include "extensions/browser/url_request_util.h"
40 #include "extensions/common/constants.h"
41 #include "extensions/common/extension.h"
42 #include "extensions/common/extension_resource.h"
43 #include "extensions/common/file_util.h"
44 #include "extensions/common/manifest_handlers/background_info.h"
45 #include "extensions/common/manifest_handlers/csp_info.h"
46 #include "extensions/common/manifest_handlers/icons_handler.h"
47 #include "extensions/common/manifest_handlers/incognito_info.h"
48 #include "extensions/common/manifest_handlers/shared_module_info.h"
49 #include "extensions/common/manifest_handlers/web_accessible_resources_info.h"
50 #include "net/base/io_buffer.h"
51 #include "net/base/net_errors.h"
52 #include "net/http/http_request_headers.h"
53 #include "net/http/http_response_headers.h"
54 #include "net/http/http_response_info.h"
55 #include "net/url_request/url_request_error_job.h"
56 #include "net/url_request/url_request_file_job.h"
57 #include "net/url_request/url_request_simple_job.h"
58 #include "url/url_util.h"
60 using content::BrowserThread;
61 using content::ResourceRequestInfo;
62 using content::ResourceType;
63 using extensions::Extension;
64 using extensions::SharedModuleInfo;
66 namespace extensions {
67 namespace {
69 class GeneratedBackgroundPageJob : public net::URLRequestSimpleJob {
70 public:
71 GeneratedBackgroundPageJob(net::URLRequest* request,
72 net::NetworkDelegate* network_delegate,
73 const scoped_refptr<const Extension> extension,
74 const std::string& content_security_policy)
75 : net::URLRequestSimpleJob(request, network_delegate),
76 extension_(extension) {
77 const bool send_cors_headers = false;
78 // Leave cache headers out of generated background page jobs.
79 response_info_.headers = BuildHttpHeaders(content_security_policy,
80 send_cors_headers,
81 base::Time());
84 // Overridden from URLRequestSimpleJob:
85 int GetData(std::string* mime_type,
86 std::string* charset,
87 std::string* data,
88 const net::CompletionCallback& callback) const override {
89 *mime_type = "text/html";
90 *charset = "utf-8";
92 *data = "<!DOCTYPE html>\n<body>\n";
93 const std::vector<std::string>& background_scripts =
94 extensions::BackgroundInfo::GetBackgroundScripts(extension_.get());
95 for (size_t i = 0; i < background_scripts.size(); ++i) {
96 *data += "<script src=\"";
97 *data += background_scripts[i];
98 *data += "\"></script>\n";
101 return net::OK;
104 void GetResponseInfo(net::HttpResponseInfo* info) override {
105 *info = response_info_;
108 private:
109 ~GeneratedBackgroundPageJob() override {}
111 scoped_refptr<const Extension> extension_;
112 net::HttpResponseInfo response_info_;
115 base::Time GetFileLastModifiedTime(const base::FilePath& filename) {
116 if (base::PathExists(filename)) {
117 base::File::Info info;
118 if (base::GetFileInfo(filename, &info))
119 return info.last_modified;
121 return base::Time();
124 base::Time GetFileCreationTime(const base::FilePath& filename) {
125 if (base::PathExists(filename)) {
126 base::File::Info info;
127 if (base::GetFileInfo(filename, &info))
128 return info.creation_time;
130 return base::Time();
133 void ReadResourceFilePathAndLastModifiedTime(
134 const extensions::ExtensionResource& resource,
135 const base::FilePath& directory,
136 base::FilePath* file_path,
137 base::Time* last_modified_time) {
138 *file_path = resource.GetFilePath();
139 *last_modified_time = GetFileLastModifiedTime(*file_path);
140 // While we're here, log the delta between extension directory
141 // creation time and the resource's last modification time.
142 base::ElapsedTimer query_timer;
143 base::Time dir_creation_time = GetFileCreationTime(directory);
144 UMA_HISTOGRAM_TIMES("Extensions.ResourceDirectoryTimestampQueryLatency",
145 query_timer.Elapsed());
146 int64 delta_seconds = (*last_modified_time - dir_creation_time).InSeconds();
147 if (delta_seconds >= 0) {
148 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.ResourceLastModifiedDelta",
149 delta_seconds,
151 base::TimeDelta::FromDays(30).InSeconds(),
152 50);
153 } else {
154 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.ResourceLastModifiedNegativeDelta",
155 -delta_seconds,
157 base::TimeDelta::FromDays(30).InSeconds(),
158 50);
162 class URLRequestExtensionJob : public net::URLRequestFileJob {
163 public:
164 URLRequestExtensionJob(net::URLRequest* request,
165 net::NetworkDelegate* network_delegate,
166 const std::string& extension_id,
167 const base::FilePath& directory_path,
168 const base::FilePath& relative_path,
169 const std::string& content_security_policy,
170 bool send_cors_header,
171 bool follow_symlinks_anywhere,
172 ContentVerifyJob* verify_job)
173 : net::URLRequestFileJob(
174 request,
175 network_delegate,
176 base::FilePath(),
177 BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
178 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
179 verify_job_(verify_job),
180 seek_position_(0),
181 bytes_read_(0),
182 directory_path_(directory_path),
183 // TODO(tc): Move all of these files into resources.pak so we don't
184 // break when updating on Linux.
185 resource_(extension_id, directory_path, relative_path),
186 content_security_policy_(content_security_policy),
187 send_cors_header_(send_cors_header),
188 weak_factory_(this) {
189 if (follow_symlinks_anywhere) {
190 resource_.set_follow_symlinks_anywhere();
194 void GetResponseInfo(net::HttpResponseInfo* info) override {
195 *info = response_info_;
198 void Start() override {
199 request_timer_.reset(new base::ElapsedTimer());
200 base::FilePath* read_file_path = new base::FilePath;
201 base::Time* last_modified_time = new base::Time();
202 bool posted = BrowserThread::PostBlockingPoolTaskAndReply(
203 FROM_HERE,
204 base::Bind(&ReadResourceFilePathAndLastModifiedTime,
205 resource_,
206 directory_path_,
207 base::Unretained(read_file_path),
208 base::Unretained(last_modified_time)),
209 base::Bind(&URLRequestExtensionJob::OnFilePathAndLastModifiedTimeRead,
210 weak_factory_.GetWeakPtr(),
211 base::Owned(read_file_path),
212 base::Owned(last_modified_time)));
213 DCHECK(posted);
216 bool IsRedirectResponse(GURL* location, int* http_status_code) override {
217 return false;
220 void SetExtraRequestHeaders(const net::HttpRequestHeaders& headers) override {
221 // TODO(asargent) - we'll need to add proper support for range headers.
222 // crbug.com/369895.
223 std::string range_header;
224 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
225 if (verify_job_.get())
226 verify_job_ = NULL;
228 URLRequestFileJob::SetExtraRequestHeaders(headers);
231 void OnSeekComplete(int64 result) override {
232 DCHECK_EQ(seek_position_, 0);
233 seek_position_ = result;
234 // TODO(asargent) - we'll need to add proper support for range headers.
235 // crbug.com/369895.
236 if (result > 0 && verify_job_.get())
237 verify_job_ = NULL;
240 void OnReadComplete(net::IOBuffer* buffer, int result) override {
241 if (result >= 0)
242 UMA_HISTOGRAM_COUNTS("ExtensionUrlRequest.OnReadCompleteResult", result);
243 else
244 UMA_HISTOGRAM_SPARSE_SLOWLY("ExtensionUrlRequest.OnReadCompleteError",
245 -result);
246 if (result > 0) {
247 bytes_read_ += result;
248 if (verify_job_.get()) {
249 verify_job_->BytesRead(result, buffer->data());
250 if (!remaining_bytes())
251 verify_job_->DoneReading();
256 private:
257 ~URLRequestExtensionJob() override {
258 UMA_HISTOGRAM_COUNTS("ExtensionUrlRequest.TotalKbRead", bytes_read_ / 1024);
259 UMA_HISTOGRAM_COUNTS("ExtensionUrlRequest.SeekPosition", seek_position_);
260 if (request_timer_.get())
261 UMA_HISTOGRAM_TIMES("ExtensionUrlRequest.Latency",
262 request_timer_->Elapsed());
265 void OnFilePathAndLastModifiedTimeRead(base::FilePath* read_file_path,
266 base::Time* last_modified_time) {
267 file_path_ = *read_file_path;
268 response_info_.headers = BuildHttpHeaders(
269 content_security_policy_,
270 send_cors_header_,
271 *last_modified_time);
272 URLRequestFileJob::Start();
275 scoped_refptr<ContentVerifyJob> verify_job_;
277 scoped_ptr<base::ElapsedTimer> request_timer_;
279 // The position we seeked to in the file.
280 int64 seek_position_;
282 // The number of bytes of content we read from the file.
283 int bytes_read_;
285 net::HttpResponseInfo response_info_;
286 base::FilePath directory_path_;
287 extensions::ExtensionResource resource_;
288 std::string content_security_policy_;
289 bool send_cors_header_;
290 base::WeakPtrFactory<URLRequestExtensionJob> weak_factory_;
293 bool ExtensionCanLoadInIncognito(const ResourceRequestInfo* info,
294 const std::string& extension_id,
295 extensions::InfoMap* extension_info_map) {
296 if (!extension_info_map->IsIncognitoEnabled(extension_id))
297 return false;
299 // Only allow incognito toplevel navigations to extension resources in
300 // split mode. In spanning mode, the extension must run in a single process,
301 // and an incognito tab prevents that.
302 if (info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
303 const Extension* extension =
304 extension_info_map->extensions().GetByID(extension_id);
305 return extension && extensions::IncognitoInfo::IsSplitMode(extension);
308 return true;
311 // Returns true if an chrome-extension:// resource should be allowed to load.
312 // Pass true for |is_incognito| only for incognito profiles and not Chrome OS
313 // guest mode profiles.
314 // TODO(aa): This should be moved into ExtensionResourceRequestPolicy, but we
315 // first need to find a way to get CanLoadInIncognito state into the renderers.
316 bool AllowExtensionResourceLoad(net::URLRequest* request,
317 bool is_incognito,
318 const Extension* extension,
319 extensions::InfoMap* extension_info_map) {
320 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
322 // We have seen crashes where info is NULL: crbug.com/52374.
323 if (!info) {
324 LOG(ERROR) << "Allowing load of " << request->url().spec()
325 << "from unknown origin. Could not find user data for "
326 << "request.";
327 return true;
330 if (is_incognito && !ExtensionCanLoadInIncognito(
331 info, request->url().host(), extension_info_map)) {
332 return false;
335 // The following checks are meant to replicate similar set of checks in the
336 // renderer process, performed by ResourceRequestPolicy::CanRequestResource.
337 // These are not exactly equivalent, because we don't have the same bits of
338 // information. The two checks need to be kept in sync as much as possible, as
339 // an exploited renderer can bypass the checks in ResourceRequestPolicy.
341 // Check if the extension for which this request is made is indeed loaded in
342 // the process sending the request. If not, we need to explicitly check if
343 // the resource is explicitly accessible or fits in a set of exception cases.
344 // Note: This allows a case where two extensions execute in the same renderer
345 // process to request each other's resources. We can't do a more precise
346 // check, since the renderer can lie about which extension has made the
347 // request.
348 if (extension_info_map->process_map().Contains(
349 request->url().host(), info->GetChildID())) {
350 return true;
353 // Allow the extension module embedder to grant permission for loads.
354 if (ExtensionsBrowserClient::Get()->AllowCrossRendererResourceLoad(
355 request, is_incognito, extension, extension_info_map)) {
356 return true;
359 // No special exceptions for cross-process loading. Block the load.
360 return false;
363 // Returns true if the given URL references an icon in the given extension.
364 bool URLIsForExtensionIcon(const GURL& url, const Extension* extension) {
365 DCHECK(url.SchemeIs(extensions::kExtensionScheme));
367 if (!extension)
368 return false;
370 std::string path = url.path();
371 DCHECK_EQ(url.host(), extension->id());
372 DCHECK(path.length() > 0 && path[0] == '/');
373 path = path.substr(1);
374 return extensions::IconsInfo::GetIcons(extension).ContainsPath(path);
377 class ExtensionProtocolHandler
378 : public net::URLRequestJobFactory::ProtocolHandler {
379 public:
380 ExtensionProtocolHandler(bool is_incognito,
381 extensions::InfoMap* extension_info_map)
382 : is_incognito_(is_incognito), extension_info_map_(extension_info_map) {}
384 ~ExtensionProtocolHandler() override {}
386 net::URLRequestJob* MaybeCreateJob(
387 net::URLRequest* request,
388 net::NetworkDelegate* network_delegate) const override;
390 private:
391 const bool is_incognito_;
392 extensions::InfoMap* const extension_info_map_;
393 DISALLOW_COPY_AND_ASSIGN(ExtensionProtocolHandler);
396 // Creates URLRequestJobs for extension:// URLs.
397 net::URLRequestJob*
398 ExtensionProtocolHandler::MaybeCreateJob(
399 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
400 // chrome-extension://extension-id/resource/path.js
401 std::string extension_id = request->url().host();
402 const Extension* extension =
403 extension_info_map_->extensions().GetByID(extension_id);
405 // TODO(mpcomplete): better error code.
406 if (!AllowExtensionResourceLoad(
407 request, is_incognito_, extension, extension_info_map_)) {
408 return new net::URLRequestErrorJob(
409 request, network_delegate, net::ERR_ADDRESS_UNREACHABLE);
412 // If this is a disabled extension only allow the icon to load.
413 base::FilePath directory_path;
414 if (extension)
415 directory_path = extension->path();
416 if (directory_path.value().empty()) {
417 const Extension* disabled_extension =
418 extension_info_map_->disabled_extensions().GetByID(extension_id);
419 if (URLIsForExtensionIcon(request->url(), disabled_extension))
420 directory_path = disabled_extension->path();
421 if (directory_path.value().empty()) {
422 LOG(WARNING) << "Failed to GetPathForExtension: " << extension_id;
423 return NULL;
427 // Set up content security policy.
428 std::string content_security_policy;
429 bool send_cors_header = false;
430 bool follow_symlinks_anywhere = false;
432 if (extension) {
433 std::string resource_path = request->url().path();
435 // Use default CSP for <webview>.
436 if (!url_request_util::IsWebViewRequest(request)) {
437 content_security_policy =
438 extensions::CSPInfo::GetResourceContentSecurityPolicy(extension,
439 resource_path);
442 if ((extension->manifest_version() >= 2 ||
443 extensions::WebAccessibleResourcesInfo::HasWebAccessibleResources(
444 extension)) &&
445 extensions::WebAccessibleResourcesInfo::IsResourceWebAccessible(
446 extension, resource_path)) {
447 send_cors_header = true;
450 follow_symlinks_anywhere =
451 (extension->creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)
452 != 0;
455 // Create a job for a generated background page.
456 std::string path = request->url().path();
457 if (path.size() > 1 &&
458 path.substr(1) == extensions::kGeneratedBackgroundPageFilename) {
459 return new GeneratedBackgroundPageJob(
460 request, network_delegate, extension, content_security_policy);
463 // Component extension resources may be part of the embedder's resource files,
464 // for example component_extension_resources.pak in Chrome.
465 net::URLRequestJob* resource_bundle_job =
466 extensions::ExtensionsBrowserClient::Get()
467 ->MaybeCreateResourceBundleRequestJob(request,
468 network_delegate,
469 directory_path,
470 content_security_policy,
471 send_cors_header);
472 if (resource_bundle_job)
473 return resource_bundle_job;
475 base::FilePath relative_path =
476 extensions::file_util::ExtensionURLToRelativeFilePath(request->url());
478 // Handle shared resources (extension A loading resources out of extension B).
479 if (SharedModuleInfo::IsImportedPath(path)) {
480 std::string new_extension_id;
481 std::string new_relative_path;
482 SharedModuleInfo::ParseImportedPath(path, &new_extension_id,
483 &new_relative_path);
484 const Extension* new_extension =
485 extension_info_map_->extensions().GetByID(new_extension_id);
487 if (SharedModuleInfo::ImportsExtensionById(extension, new_extension_id) &&
488 new_extension) {
489 directory_path = new_extension->path();
490 extension_id = new_extension_id;
491 relative_path = base::FilePath::FromUTF8Unsafe(new_relative_path);
492 } else {
493 return NULL;
496 ContentVerifyJob* verify_job = NULL;
497 ContentVerifier* verifier = extension_info_map_->content_verifier();
498 if (verifier) {
499 verify_job =
500 verifier->CreateJobFor(extension_id, directory_path, relative_path);
501 if (verify_job)
502 verify_job->Start();
505 return new URLRequestExtensionJob(request,
506 network_delegate,
507 extension_id,
508 directory_path,
509 relative_path,
510 content_security_policy,
511 send_cors_header,
512 follow_symlinks_anywhere,
513 verify_job);
516 } // namespace
518 net::HttpResponseHeaders* BuildHttpHeaders(
519 const std::string& content_security_policy,
520 bool send_cors_header,
521 const base::Time& last_modified_time) {
522 std::string raw_headers;
523 raw_headers.append("HTTP/1.1 200 OK");
524 if (!content_security_policy.empty()) {
525 raw_headers.append(1, '\0');
526 raw_headers.append("Content-Security-Policy: ");
527 raw_headers.append(content_security_policy);
530 if (send_cors_header) {
531 raw_headers.append(1, '\0');
532 raw_headers.append("Access-Control-Allow-Origin: *");
535 if (!last_modified_time.is_null()) {
536 // Hash the time and make an etag to avoid exposing the exact
537 // user installation time of the extension.
538 std::string hash =
539 base::StringPrintf("%" PRId64, last_modified_time.ToInternalValue());
540 hash = base::SHA1HashString(hash);
541 std::string etag;
542 base::Base64Encode(hash, &etag);
543 raw_headers.append(1, '\0');
544 raw_headers.append("ETag: \"");
545 raw_headers.append(etag);
546 raw_headers.append("\"");
547 // Also force revalidation.
548 raw_headers.append(1, '\0');
549 raw_headers.append("cache-control: no-cache");
552 raw_headers.append(2, '\0');
553 return new net::HttpResponseHeaders(raw_headers);
556 net::URLRequestJobFactory::ProtocolHandler* CreateExtensionProtocolHandler(
557 bool is_incognito,
558 extensions::InfoMap* extension_info_map) {
559 return new ExtensionProtocolHandler(is_incognito, extension_info_map);
562 } // namespace extensions