| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/browser/webui/url_data_manager_backend.h" | 5 #include "content/browser/webui/url_data_manager_backend.h" |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 #include <utility> | 8 #include <utility> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 27 matching lines...) Expand all Loading... |
| 38 #include "content/public/browser/browser_context.h" | 38 #include "content/public/browser/browser_context.h" |
| 39 #include "content/public/browser/browser_thread.h" | 39 #include "content/public/browser/browser_thread.h" |
| 40 #include "content/public/browser/content_browser_client.h" | 40 #include "content/public/browser/content_browser_client.h" |
| 41 #include "content/public/browser/render_process_host.h" | 41 #include "content/public/browser/render_process_host.h" |
| 42 #include "content/public/browser/resource_request_info.h" | 42 #include "content/public/browser/resource_request_info.h" |
| 43 #include "content/public/common/url_constants.h" | 43 #include "content/public/common/url_constants.h" |
| 44 #include "net/base/io_buffer.h" | 44 #include "net/base/io_buffer.h" |
| 45 #include "net/base/net_errors.h" | 45 #include "net/base/net_errors.h" |
| 46 #include "net/filter/gzip_source_stream.h" | 46 #include "net/filter/gzip_source_stream.h" |
| 47 #include "net/filter/source_stream.h" | 47 #include "net/filter/source_stream.h" |
| 48 #include "net/http/http_response_headers.h" | |
| 49 #include "net/http/http_status_code.h" | 48 #include "net/http/http_status_code.h" |
| 50 #include "net/log/net_log_util.h" | 49 #include "net/log/net_log_util.h" |
| 51 #include "net/url_request/url_request.h" | 50 #include "net/url_request/url_request.h" |
| 52 #include "net/url_request/url_request_context.h" | 51 #include "net/url_request/url_request_context.h" |
| 53 #include "net/url_request/url_request_error_job.h" | 52 #include "net/url_request/url_request_error_job.h" |
| 54 #include "net/url_request/url_request_job.h" | 53 #include "net/url_request/url_request_job.h" |
| 55 #include "net/url_request/url_request_job_factory.h" | 54 #include "net/url_request/url_request_job_factory.h" |
| 56 #include "ui/base/template_expressions.h" | 55 #include "ui/base/template_expressions.h" |
| 57 #include "url/url_util.h" | 56 #include "url/url_util.h" |
| 58 | 57 |
| 59 namespace content { | 58 namespace content { |
| 60 | 59 |
| 61 namespace { | 60 namespace { |
| 62 | 61 |
| 63 const char kChromeURLContentSecurityPolicyHeaderBase[] = | 62 const char kChromeURLContentSecurityPolicyHeaderBase[] = |
| 64 "Content-Security-Policy: "; | 63 "Content-Security-Policy: "; |
| 65 | 64 |
| 66 const char kChromeURLXFrameOptionsHeader[] = "X-Frame-Options: DENY"; | 65 const char kChromeURLXFrameOptionsHeader[] = "X-Frame-Options: DENY"; |
| 67 const char kNetworkErrorKey[] = "netError"; | 66 const char kNetworkErrorKey[] = "netError"; |
| 68 | 67 |
| 69 bool SchemeIsInSchemes(const std::string& scheme, | 68 bool SchemeIsInSchemes(const std::string& scheme, |
| 70 const std::vector<std::string>& schemes) { | 69 const std::vector<std::string>& schemes) { |
| 71 return std::find(schemes.begin(), schemes.end(), scheme) != schemes.end(); | 70 return std::find(schemes.begin(), schemes.end(), scheme) != schemes.end(); |
| 72 } | 71 } |
| 73 | 72 |
| 74 // Returns whether |url| passes some sanity checks and is a valid GURL. | |
| 75 bool CheckURLIsValid(const GURL& url) { | |
| 76 std::vector<std::string> additional_schemes; | |
| 77 DCHECK(url.SchemeIs(kChromeDevToolsScheme) || url.SchemeIs(kChromeUIScheme) || | |
| 78 (GetContentClient()->browser()->GetAdditionalWebUISchemes( | |
| 79 &additional_schemes), | |
| 80 SchemeIsInSchemes(url.scheme(), additional_schemes))); | |
| 81 | |
| 82 if (!url.is_valid()) { | |
| 83 NOTREACHED(); | |
| 84 return false; | |
| 85 } | |
| 86 | |
| 87 return true; | |
| 88 } | |
| 89 | |
| 90 // Parse |url| to get the path which will be used to resolve the request. The | |
| 91 // path is the remaining portion after the scheme and hostname. | |
| 92 void URLToRequestPath(const GURL& url, std::string* path) { | |
| 93 const std::string& spec = url.possibly_invalid_spec(); | |
| 94 const url::Parsed& parsed = url.parsed_for_possibly_invalid_spec(); | |
| 95 // + 1 to skip the slash at the beginning of the path. | |
| 96 int offset = parsed.CountCharactersBefore(url::Parsed::PATH, false) + 1; | |
| 97 | |
| 98 if (offset < static_cast<int>(spec.size())) | |
| 99 path->assign(spec.substr(offset)); | |
| 100 } | |
| 101 | |
| 102 // Returns a value of 'Origin:' header for the |request| if the header is set. | 73 // Returns a value of 'Origin:' header for the |request| if the header is set. |
| 103 // Otherwise returns an empty string. | 74 // Otherwise returns an empty string. |
| 104 std::string GetOriginHeaderValue(const net::URLRequest* request) { | 75 std::string GetOriginHeaderValue(const net::URLRequest* request) { |
| 105 std::string result; | 76 std::string result; |
| 106 if (request->extra_request_headers().GetHeader( | 77 if (request->extra_request_headers().GetHeader( |
| 107 net::HttpRequestHeaders::kOrigin, &result)) | 78 net::HttpRequestHeaders::kOrigin, &result)) |
| 108 return result; | 79 return result; |
| 109 net::HttpRequestHeaders headers; | 80 net::HttpRequestHeaders headers; |
| 110 if (request->GetFullRequestHeaders(&headers)) | 81 if (request->GetFullRequestHeaders(&headers)) |
| 111 headers.GetHeader(net::HttpRequestHeaders::kOrigin, &result); | 82 headers.GetHeader(net::HttpRequestHeaders::kOrigin, &result); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 127 } | 98 } |
| 128 | 99 |
| 129 } // namespace | 100 } // namespace |
| 130 | 101 |
| 131 // URLRequestChromeJob is a net::URLRequestJob that manages running | 102 // URLRequestChromeJob is a net::URLRequestJob that manages running |
| 132 // chrome-internal resource requests asynchronously. | 103 // chrome-internal resource requests asynchronously. |
| 133 // It hands off URL requests to ChromeURLDataManager, which asynchronously | 104 // It hands off URL requests to ChromeURLDataManager, which asynchronously |
| 134 // calls back once the data is available. | 105 // calls back once the data is available. |
| 135 class URLRequestChromeJob : public net::URLRequestJob { | 106 class URLRequestChromeJob : public net::URLRequestJob { |
| 136 public: | 107 public: |
| 137 // |is_incognito| set when job is generated from an incognito profile. | |
| 138 URLRequestChromeJob(net::URLRequest* request, | 108 URLRequestChromeJob(net::URLRequest* request, |
| 139 net::NetworkDelegate* network_delegate, | 109 net::NetworkDelegate* network_delegate, |
| 140 URLDataManagerBackend* backend, | 110 URLDataManagerBackend* backend); |
| 141 bool is_incognito); | |
| 142 | 111 |
| 143 // net::URLRequestJob implementation. | 112 // net::URLRequestJob implementation. |
| 144 void Start() override; | 113 void Start() override; |
| 145 void Kill() override; | 114 void Kill() override; |
| 146 int ReadRawData(net::IOBuffer* buf, int buf_size) override; | 115 int ReadRawData(net::IOBuffer* buf, int buf_size) override; |
| 147 bool GetMimeType(std::string* mime_type) const override; | 116 bool GetMimeType(std::string* mime_type) const override; |
| 148 void GetResponseInfo(net::HttpResponseInfo* info) override; | 117 void GetResponseInfo(net::HttpResponseInfo* info) override; |
| 149 std::unique_ptr<net::SourceStream> SetUpSourceStream() override; | 118 std::unique_ptr<net::SourceStream> SetUpSourceStream() override; |
| 150 | 119 |
| 151 // Used to notify that the requested data's |mime_type| is ready. | 120 // Used to notify that the requested data's |mime_type| is ready. |
| 152 void MimeTypeAvailable(const std::string& mime_type); | 121 void MimeTypeAvailable(const std::string& mime_type); |
| 153 | 122 |
| 154 // Called by ChromeURLDataManager to notify us that the data blob is ready | 123 // Called by ChromeURLDataManager to notify us that the data blob is ready |
| 155 // for us. |bytes| may be null, indicating an error. | 124 // for us. |bytes| may be null, indicating an error. |
| 156 void DataAvailable(base::RefCountedMemory* bytes); | 125 void DataAvailable(base::RefCountedMemory* bytes); |
| 157 | 126 |
| 158 // Returns a weak pointer to the job. | |
| 159 base::WeakPtr<URLRequestChromeJob> AsWeakPtr(); | |
| 160 | |
| 161 void set_mime_type(const std::string& mime_type) { | |
| 162 mime_type_ = mime_type; | |
| 163 } | |
| 164 | |
| 165 void set_allow_caching(bool allow_caching) { | |
| 166 allow_caching_ = allow_caching; | |
| 167 } | |
| 168 | |
| 169 void set_add_content_security_policy(bool add_content_security_policy) { | |
| 170 add_content_security_policy_ = add_content_security_policy; | |
| 171 } | |
| 172 | |
| 173 void set_content_security_policy_object_source( | |
| 174 const std::string& data) { | |
| 175 content_security_policy_object_source_ = data; | |
| 176 } | |
| 177 | |
| 178 void set_content_security_policy_script_source( | |
| 179 const std::string& data) { | |
| 180 content_security_policy_script_source_ = data; | |
| 181 } | |
| 182 | |
| 183 void set_content_security_policy_child_source( | |
| 184 const std::string& data) { | |
| 185 content_security_policy_child_source_ = data; | |
| 186 } | |
| 187 | |
| 188 void set_content_security_policy_style_source( | |
| 189 const std::string& data) { | |
| 190 content_security_policy_style_source_ = data; | |
| 191 } | |
| 192 | |
| 193 void set_content_security_policy_image_source( | |
| 194 const std::string& data) { | |
| 195 content_security_policy_image_source_ = data; | |
| 196 } | |
| 197 | |
| 198 void set_deny_xframe_options(bool deny_xframe_options) { | |
| 199 deny_xframe_options_ = deny_xframe_options; | |
| 200 } | |
| 201 | |
| 202 void set_send_content_type_header(bool send_content_type_header) { | |
| 203 send_content_type_header_ = send_content_type_header; | |
| 204 } | |
| 205 | |
| 206 void set_access_control_allow_origin(const std::string& value) { | |
| 207 access_control_allow_origin_ = value; | |
| 208 } | |
| 209 | |
| 210 void set_is_gzipped(bool is_gzipped) { | 127 void set_is_gzipped(bool is_gzipped) { |
| 211 is_gzipped_ = is_gzipped; | 128 is_gzipped_ = is_gzipped; |
| 212 } | 129 } |
| 213 | 130 |
| 214 void SetReplacements(const ui::TemplateReplacements* replacements) { | 131 void SetReplacements(const ui::TemplateReplacements* replacements) { |
| 215 replacements_ = replacements; | 132 replacements_ = replacements; |
| 216 } | 133 } |
| 217 | 134 |
| 218 // Returns true when job was generated from an incognito profile. | |
| 219 bool is_incognito() const { | |
| 220 return is_incognito_; | |
| 221 } | |
| 222 | |
| 223 private: | 135 private: |
| 224 ~URLRequestChromeJob() override; | 136 ~URLRequestChromeJob() override; |
| 225 | 137 |
| 226 // Helper for Start(), to let us start asynchronously. | 138 // Helper for Start(), to let us start asynchronously. |
| 227 // (This pattern is shared by most net::URLRequestJob implementations.) | 139 // (This pattern is shared by most net::URLRequestJob implementations.) |
| 228 void StartAsync(); | 140 void StartAsync(); |
| 229 | 141 |
| 230 // Due to a race condition, DevTools relies on a legacy thread hop to the UI | 142 // Due to a race condition, DevTools relies on a legacy thread hop to the UI |
| 231 // thread before calling StartAsync. | 143 // thread before calling StartAsync. |
| 232 // TODO(caseq): Fix the race condition and remove this thread hop in | 144 // TODO(caseq): Fix the race condition and remove this thread hop in |
| (...skipping 15 matching lines...) Expand all Loading... |
| 248 // When DataAvailable() is called with a null argument, indicating an error, | 160 // When DataAvailable() is called with a null argument, indicating an error, |
| 249 // this is set accordingly to a code for ReadRawData() to return. | 161 // this is set accordingly to a code for ReadRawData() to return. |
| 250 net::Error data_available_status_; | 162 net::Error data_available_status_; |
| 251 | 163 |
| 252 // For async reads, we keep around a pointer to the buffer that | 164 // For async reads, we keep around a pointer to the buffer that |
| 253 // we're reading into. | 165 // we're reading into. |
| 254 scoped_refptr<net::IOBuffer> pending_buf_; | 166 scoped_refptr<net::IOBuffer> pending_buf_; |
| 255 int pending_buf_size_; | 167 int pending_buf_size_; |
| 256 std::string mime_type_; | 168 std::string mime_type_; |
| 257 | 169 |
| 258 // If true, set a header in the response to prevent it from being cached. | |
| 259 bool allow_caching_; | |
| 260 | |
| 261 // If true, set the Content Security Policy (CSP) header. | |
| 262 bool add_content_security_policy_; | |
| 263 | |
| 264 // These are used with the CSP. | |
| 265 std::string content_security_policy_script_source_; | |
| 266 std::string content_security_policy_object_source_; | |
| 267 std::string content_security_policy_child_source_; | |
| 268 std::string content_security_policy_style_source_; | |
| 269 std::string content_security_policy_image_source_; | |
| 270 | |
| 271 // If true, sets the "X-Frame-Options: DENY" header. | |
| 272 bool deny_xframe_options_; | |
| 273 | |
| 274 // If true, sets the "Content-Type: <mime-type>" header. | |
| 275 bool send_content_type_header_; | |
| 276 | |
| 277 // If not empty, "Access-Control-Allow-Origin:" is set to the value of this | |
| 278 // string. | |
| 279 std::string access_control_allow_origin_; | |
| 280 | |
| 281 // True when job is generated from an incognito profile. | |
| 282 const bool is_incognito_; | |
| 283 | |
| 284 // True when gzip encoding should be used. NOTE: this requires the original | 170 // True when gzip encoding should be used. NOTE: this requires the original |
| 285 // resources in resources.pak use compress="gzip". | 171 // resources in resources.pak use compress="gzip". |
| 286 bool is_gzipped_; | 172 bool is_gzipped_; |
| 287 | 173 |
| 288 // Replacement dictionary for i18n. | 174 // Replacement dictionary for i18n. |
| 289 const ui::TemplateReplacements* replacements_; | 175 const ui::TemplateReplacements* replacements_; |
| 290 | 176 |
| 291 // The backend is owned by net::URLRequestContext and always outlives us. | 177 // The backend is owned by net::URLRequestContext and always outlives us. |
| 292 URLDataManagerBackend* const backend_; | 178 URLDataManagerBackend* const backend_; |
| 293 | 179 |
| 294 base::WeakPtrFactory<URLRequestChromeJob> weak_factory_; | 180 base::WeakPtrFactory<URLRequestChromeJob> weak_factory_; |
| 295 | 181 |
| 296 DISALLOW_COPY_AND_ASSIGN(URLRequestChromeJob); | 182 DISALLOW_COPY_AND_ASSIGN(URLRequestChromeJob); |
| 297 }; | 183 }; |
| 298 | 184 |
| 299 URLRequestChromeJob::URLRequestChromeJob(net::URLRequest* request, | 185 URLRequestChromeJob::URLRequestChromeJob(net::URLRequest* request, |
| 300 net::NetworkDelegate* network_delegate, | 186 net::NetworkDelegate* network_delegate, |
| 301 URLDataManagerBackend* backend, | 187 URLDataManagerBackend* backend) |
| 302 bool is_incognito) | |
| 303 : net::URLRequestJob(request, network_delegate), | 188 : net::URLRequestJob(request, network_delegate), |
| 304 data_offset_(0), | 189 data_offset_(0), |
| 305 data_available_status_(net::OK), | 190 data_available_status_(net::OK), |
| 306 pending_buf_size_(0), | 191 pending_buf_size_(0), |
| 307 allow_caching_(true), | |
| 308 add_content_security_policy_(true), | |
| 309 deny_xframe_options_(true), | |
| 310 send_content_type_header_(false), | |
| 311 is_incognito_(is_incognito), | |
| 312 is_gzipped_(false), | 192 is_gzipped_(false), |
| 313 replacements_(nullptr), | 193 replacements_(nullptr), |
| 314 backend_(backend), | 194 backend_(backend), |
| 315 weak_factory_(this) { | 195 weak_factory_(this) { |
| 316 DCHECK(backend); | 196 DCHECK(backend); |
| 317 } | 197 } |
| 318 | 198 |
| 319 URLRequestChromeJob::~URLRequestChromeJob() { | 199 URLRequestChromeJob::~URLRequestChromeJob() { |
| 320 CHECK(!backend_->HasPendingJob(this)); | 200 CHECK(!backend_->HasPendingJob(this)); |
| 321 } | 201 } |
| (...skipping 30 matching lines...) Expand all Loading... |
| 352 URLRequestJob::Kill(); | 232 URLRequestJob::Kill(); |
| 353 } | 233 } |
| 354 | 234 |
| 355 bool URLRequestChromeJob::GetMimeType(std::string* mime_type) const { | 235 bool URLRequestChromeJob::GetMimeType(std::string* mime_type) const { |
| 356 *mime_type = mime_type_; | 236 *mime_type = mime_type_; |
| 357 return !mime_type_.empty(); | 237 return !mime_type_.empty(); |
| 358 } | 238 } |
| 359 | 239 |
| 360 void URLRequestChromeJob::GetResponseInfo(net::HttpResponseInfo* info) { | 240 void URLRequestChromeJob::GetResponseInfo(net::HttpResponseInfo* info) { |
| 361 DCHECK(!info->headers.get()); | 241 DCHECK(!info->headers.get()); |
| 362 // Set the headers so that requests serviced by ChromeURLDataManager return a | 242 URLDataSourceImpl* source = backend_->GetDataSourceFromURL(request()->url()); |
| 363 // status code of 200. Without this they return a 0, which makes the status | 243 std::string path; |
| 364 // indistiguishable from other error types. Instant relies on getting a 200. | 244 URLDataManagerBackend::URLToRequestPath(request()->url(), &path); |
| 365 info->headers = new net::HttpResponseHeaders("HTTP/1.1 200 OK"); | 245 info->headers = URLDataManagerBackend::GetHeaders( |
| 366 | 246 source, path, GetOriginHeaderValue(request())); |
| 367 // Determine the least-privileged content security policy header, if any, | |
| 368 // that is compatible with a given WebUI URL, and append it to the existing | |
| 369 // response headers. | |
| 370 if (add_content_security_policy_) { | |
| 371 std::string base = kChromeURLContentSecurityPolicyHeaderBase; | |
| 372 base.append(content_security_policy_script_source_); | |
| 373 base.append(content_security_policy_object_source_); | |
| 374 base.append(content_security_policy_child_source_); | |
| 375 base.append(content_security_policy_style_source_); | |
| 376 base.append(content_security_policy_image_source_); | |
| 377 info->headers->AddHeader(base); | |
| 378 } | |
| 379 | |
| 380 if (deny_xframe_options_) | |
| 381 info->headers->AddHeader(kChromeURLXFrameOptionsHeader); | |
| 382 | |
| 383 if (!allow_caching_) | |
| 384 info->headers->AddHeader("Cache-Control: no-cache"); | |
| 385 | |
| 386 if (send_content_type_header_ && !mime_type_.empty()) { | |
| 387 std::string content_type = | |
| 388 base::StringPrintf("%s:%s", net::HttpRequestHeaders::kContentType, | |
| 389 mime_type_.c_str()); | |
| 390 info->headers->AddHeader(content_type); | |
| 391 } | |
| 392 | |
| 393 if (!access_control_allow_origin_.empty()) { | |
| 394 info->headers->AddHeader("Access-Control-Allow-Origin: " + | |
| 395 access_control_allow_origin_); | |
| 396 info->headers->AddHeader("Vary: Origin"); | |
| 397 } | |
| 398 | |
| 399 if (is_gzipped_) | 247 if (is_gzipped_) |
| 400 info->headers->AddHeader("Content-Encoding: gzip"); | 248 info->headers->AddHeader("Content-Encoding: gzip"); |
| 401 } | 249 } |
| 402 | 250 |
| 403 std::unique_ptr<net::SourceStream> URLRequestChromeJob::SetUpSourceStream() { | 251 std::unique_ptr<net::SourceStream> URLRequestChromeJob::SetUpSourceStream() { |
| 404 std::unique_ptr<net::SourceStream> source_stream = | 252 std::unique_ptr<net::SourceStream> source_stream = |
| 405 net::URLRequestJob::SetUpSourceStream(); | 253 net::URLRequestJob::SetUpSourceStream(); |
| 406 | 254 |
| 407 if (is_gzipped_) { | 255 if (is_gzipped_) { |
| 408 source_stream = net::GzipSourceStream::Create(std::move(source_stream), | 256 source_stream = net::GzipSourceStream::Create(std::move(source_stream), |
| 409 net::SourceStream::TYPE_GZIP); | 257 net::SourceStream::TYPE_GZIP); |
| 410 } | 258 } |
| 411 | 259 |
| 412 if (replacements_) { | 260 if (replacements_) { |
| 413 source_stream = content::I18nSourceStream::Create( | 261 source_stream = I18nSourceStream::Create( |
| 414 std::move(source_stream), net::SourceStream::TYPE_NONE, replacements_); | 262 std::move(source_stream), net::SourceStream::TYPE_NONE, replacements_); |
| 415 } | 263 } |
| 416 | 264 |
| 417 return source_stream; | 265 return source_stream; |
| 418 } | 266 } |
| 419 | 267 |
| 420 void URLRequestChromeJob::MimeTypeAvailable(const std::string& mime_type) { | 268 void URLRequestChromeJob::MimeTypeAvailable(const std::string& mime_type) { |
| 421 set_mime_type(mime_type); | 269 mime_type_ = mime_type; |
| 422 NotifyHeadersComplete(); | 270 NotifyHeadersComplete(); |
| 423 } | 271 } |
| 424 | 272 |
| 425 void URLRequestChromeJob::DataAvailable(base::RefCountedMemory* bytes) { | 273 void URLRequestChromeJob::DataAvailable(base::RefCountedMemory* bytes) { |
| 426 TRACE_EVENT_ASYNC_END0("browser", "DataManager:Request", this); | 274 TRACE_EVENT_ASYNC_END0("browser", "DataManager:Request", this); |
| 427 DCHECK(!data_); | 275 DCHECK(!data_); |
| 428 | 276 |
| 429 // All further requests will be satisfied from the passed-in data. | 277 // All further requests will be satisfied from the passed-in data. |
| 430 data_ = bytes; | 278 data_ = bytes; |
| 431 if (!bytes) | 279 if (!bytes) |
| 432 data_available_status_ = net::ERR_FAILED; | 280 data_available_status_ = net::ERR_FAILED; |
| 433 | 281 |
| 434 if (pending_buf_) { | 282 if (pending_buf_) { |
| 435 // The request has already been marked async. | 283 // The request has already been marked async. |
| 436 int result = bytes ? PostReadTask(pending_buf_, pending_buf_size_) | 284 int result = bytes ? PostReadTask(pending_buf_, pending_buf_size_) |
| 437 : data_available_status_; | 285 : data_available_status_; |
| 438 pending_buf_ = nullptr; | 286 pending_buf_ = nullptr; |
| 439 if (result != net::ERR_IO_PENDING) | 287 if (result != net::ERR_IO_PENDING) |
| 440 ReadRawDataComplete(result); | 288 ReadRawDataComplete(result); |
| 441 } | 289 } |
| 442 } | 290 } |
| 443 | 291 |
| 444 base::WeakPtr<URLRequestChromeJob> URLRequestChromeJob::AsWeakPtr() { | |
| 445 return weak_factory_.GetWeakPtr(); | |
| 446 } | |
| 447 | |
| 448 int URLRequestChromeJob::ReadRawData(net::IOBuffer* buf, int buf_size) { | 292 int URLRequestChromeJob::ReadRawData(net::IOBuffer* buf, int buf_size) { |
| 449 DCHECK(!pending_buf_.get()); | 293 DCHECK(!pending_buf_.get()); |
| 450 | 294 |
| 451 // Handle the cases when DataAvailable() has already been called. | 295 // Handle the cases when DataAvailable() has already been called. |
| 452 if (data_available_status_ != net::OK) | 296 if (data_available_status_ != net::OK) |
| 453 return data_available_status_; | 297 return data_available_status_; |
| 454 if (data_) | 298 if (data_) |
| 455 return PostReadTask(buf, buf_size); | 299 return PostReadTask(buf, buf_size); |
| 456 | 300 |
| 457 // DataAvailable() has not been called yet. Mark the request as async. | 301 // DataAvailable() has not been called yet. Mark the request as async. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 470 if (buf_size > remaining) | 314 if (buf_size > remaining) |
| 471 buf_size = remaining; | 315 buf_size = remaining; |
| 472 | 316 |
| 473 if (buf_size == 0) | 317 if (buf_size == 0) |
| 474 return 0; | 318 return 0; |
| 475 | 319 |
| 476 base::PostTaskWithTraitsAndReply( | 320 base::PostTaskWithTraitsAndReply( |
| 477 FROM_HERE, {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, | 321 FROM_HERE, {base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| 478 base::Bind(&CopyData, base::RetainedRef(buf), buf_size, data_, | 322 base::Bind(&CopyData, base::RetainedRef(buf), buf_size, data_, |
| 479 data_offset_), | 323 data_offset_), |
| 480 base::Bind(&URLRequestChromeJob::ReadRawDataComplete, AsWeakPtr(), | 324 base::Bind(&URLRequestChromeJob::ReadRawDataComplete, |
| 481 buf_size)); | 325 weak_factory_.GetWeakPtr(), buf_size)); |
| 482 data_offset_ += buf_size; | 326 data_offset_ += buf_size; |
| 483 | 327 |
| 484 return net::ERR_IO_PENDING; | 328 return net::ERR_IO_PENDING; |
| 485 } | 329 } |
| 486 | 330 |
| 487 void URLRequestChromeJob::DelayStartForDevTools( | 331 void URLRequestChromeJob::DelayStartForDevTools( |
| 488 const base::WeakPtr<URLRequestChromeJob>& job) { | 332 const base::WeakPtr<URLRequestChromeJob>& job) { |
| 489 BrowserThread::PostTask( | 333 BrowserThread::PostTask( |
| 490 BrowserThread::IO, | 334 BrowserThread::IO, |
| 491 FROM_HERE, | 335 FROM_HERE, |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 524 if (error_code == net_error_code) | 368 if (error_code == net_error_code) |
| 525 return true; | 369 return true; |
| 526 } | 370 } |
| 527 } | 371 } |
| 528 return false; | 372 return false; |
| 529 } | 373 } |
| 530 | 374 |
| 531 class ChromeProtocolHandler | 375 class ChromeProtocolHandler |
| 532 : public net::URLRequestJobFactory::ProtocolHandler { | 376 : public net::URLRequestJobFactory::ProtocolHandler { |
| 533 public: | 377 public: |
| 534 // |is_incognito| should be set for incognito profiles. | |
| 535 ChromeProtocolHandler(ResourceContext* resource_context, | 378 ChromeProtocolHandler(ResourceContext* resource_context, |
| 536 bool is_incognito, | |
| 537 ChromeBlobStorageContext* blob_storage_context) | 379 ChromeBlobStorageContext* blob_storage_context) |
| 538 : resource_context_(resource_context), | 380 : resource_context_(resource_context), |
| 539 is_incognito_(is_incognito), | |
| 540 blob_storage_context_(blob_storage_context) {} | 381 blob_storage_context_(blob_storage_context) {} |
| 541 ~ChromeProtocolHandler() override {} | 382 ~ChromeProtocolHandler() override {} |
| 542 | 383 |
| 543 net::URLRequestJob* MaybeCreateJob( | 384 net::URLRequestJob* MaybeCreateJob( |
| 544 net::URLRequest* request, | 385 net::URLRequest* request, |
| 545 net::NetworkDelegate* network_delegate) const override { | 386 net::NetworkDelegate* network_delegate) const override { |
| 546 DCHECK(request); | 387 DCHECK(request); |
| 547 | 388 |
| 548 // Check for chrome://view-http-cache/*, which uses its own job type. | 389 // Check for chrome://view-http-cache/*, which uses its own job type. |
| 549 if (ViewHttpCacheJobFactory::IsSupportedURL(request->url())) | 390 if (ViewHttpCacheJobFactory::IsSupportedURL(request->url())) |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 583 // Check for chrome://dino which is an alias for chrome://network-error/-106 | 424 // Check for chrome://dino which is an alias for chrome://network-error/-106 |
| 584 if (request->url().SchemeIs(kChromeUIScheme) && | 425 if (request->url().SchemeIs(kChromeUIScheme) && |
| 585 request->url().host() == kChromeUIDinoHost) { | 426 request->url().host() == kChromeUIDinoHost) { |
| 586 return new net::URLRequestErrorJob(request, network_delegate, | 427 return new net::URLRequestErrorJob(request, network_delegate, |
| 587 net::Error::ERR_INTERNET_DISCONNECTED); | 428 net::Error::ERR_INTERNET_DISCONNECTED); |
| 588 } | 429 } |
| 589 | 430 |
| 590 // Fall back to using a custom handler | 431 // Fall back to using a custom handler |
| 591 return new URLRequestChromeJob( | 432 return new URLRequestChromeJob( |
| 592 request, network_delegate, | 433 request, network_delegate, |
| 593 GetURLDataManagerForResourceContext(resource_context_), is_incognito_); | 434 GetURLDataManagerForResourceContext(resource_context_)); |
| 594 } | 435 } |
| 595 | 436 |
| 596 bool IsSafeRedirectTarget(const GURL& location) const override { | 437 bool IsSafeRedirectTarget(const GURL& location) const override { |
| 597 return false; | 438 return false; |
| 598 } | 439 } |
| 599 | 440 |
| 600 private: | 441 private: |
| 601 // These members are owned by ProfileIOData, which owns this ProtocolHandler. | 442 // These members are owned by ProfileIOData, which owns this ProtocolHandler. |
| 602 ResourceContext* const resource_context_; | 443 ResourceContext* const resource_context_; |
| 603 | 444 |
| 604 // True when generated from an incognito profile. | |
| 605 const bool is_incognito_; | |
| 606 ChromeBlobStorageContext* blob_storage_context_; | 445 ChromeBlobStorageContext* blob_storage_context_; |
| 607 | 446 |
| 608 DISALLOW_COPY_AND_ASSIGN(ChromeProtocolHandler); | 447 DISALLOW_COPY_AND_ASSIGN(ChromeProtocolHandler); |
| 609 }; | 448 }; |
| 610 | 449 |
| 611 } // namespace | 450 } // namespace |
| 612 | 451 |
| 613 URLDataManagerBackend::URLDataManagerBackend() | 452 URLDataManagerBackend::URLDataManagerBackend() |
| 614 : next_request_id_(0) { | 453 : next_request_id_(0) { |
| 615 URLDataSource* shared_source = new SharedResourcesDataSource(); | 454 URLDataSource* shared_source = new SharedResourcesDataSource(); |
| 616 URLDataSourceImpl* source_impl = | 455 URLDataSourceImpl* source_impl = |
| 617 new URLDataSourceImpl(shared_source->GetSource(), shared_source); | 456 new URLDataSourceImpl(shared_source->GetSource(), shared_source); |
| 618 AddDataSource(source_impl); | 457 AddDataSource(source_impl); |
| 619 } | 458 } |
| 620 | 459 |
| 621 URLDataManagerBackend::~URLDataManagerBackend() { | 460 URLDataManagerBackend::~URLDataManagerBackend() { |
| 622 for (const auto& i : data_sources_) | 461 for (const auto& i : data_sources_) |
| 623 i.second->backend_ = nullptr; | 462 i.second->backend_ = nullptr; |
| 624 data_sources_.clear(); | 463 data_sources_.clear(); |
| 625 } | 464 } |
| 626 | 465 |
| 627 // static | 466 // static |
| 628 std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler> | 467 std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler> |
| 629 URLDataManagerBackend::CreateProtocolHandler( | 468 URLDataManagerBackend::CreateProtocolHandler( |
| 630 ResourceContext* resource_context, | 469 ResourceContext* resource_context, |
| 631 bool is_incognito, | |
| 632 ChromeBlobStorageContext* blob_storage_context) { | 470 ChromeBlobStorageContext* blob_storage_context) { |
| 633 DCHECK(resource_context); | 471 DCHECK(resource_context); |
| 634 return base::MakeUnique<ChromeProtocolHandler>(resource_context, is_incognito, | 472 return base::MakeUnique<ChromeProtocolHandler>(resource_context, |
| 635 blob_storage_context); | 473 blob_storage_context); |
| 636 } | 474 } |
| 637 | 475 |
| 638 void URLDataManagerBackend::AddDataSource( | 476 void URLDataManagerBackend::AddDataSource( |
| 639 URLDataSourceImpl* source) { | 477 URLDataSourceImpl* source) { |
| 640 DCHECK_CURRENTLY_ON(BrowserThread::IO); | 478 DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| 641 DataSourceMap::iterator i = data_sources_.find(source->source_name()); | 479 DataSourceMap::iterator i = data_sources_.find(source->source_name()); |
| 642 if (i != data_sources_.end()) { | 480 if (i != data_sources_.end()) { |
| 643 if (!source->source()->ShouldReplaceExistingSource()) | 481 if (!source->source()->ShouldReplaceExistingSource()) |
| 644 return; | 482 return; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 665 for (PendingRequestMap::const_iterator i = pending_requests_.begin(); | 503 for (PendingRequestMap::const_iterator i = pending_requests_.begin(); |
| 666 i != pending_requests_.end(); ++i) { | 504 i != pending_requests_.end(); ++i) { |
| 667 if (i->second == job) | 505 if (i->second == job) |
| 668 return true; | 506 return true; |
| 669 } | 507 } |
| 670 return false; | 508 return false; |
| 671 } | 509 } |
| 672 | 510 |
| 673 bool URLDataManagerBackend::StartRequest(const net::URLRequest* request, | 511 bool URLDataManagerBackend::StartRequest(const net::URLRequest* request, |
| 674 URLRequestChromeJob* job) { | 512 URLRequestChromeJob* job) { |
| 513 // NOTE: this duplicates code in web_ui_url_loader_factory.cc's URLLoaderImpl. |
| 675 if (!CheckURLIsValid(request->url())) | 514 if (!CheckURLIsValid(request->url())) |
| 676 return false; | 515 return false; |
| 677 | 516 |
| 678 URLDataSourceImpl* source = GetDataSourceFromURL(request->url()); | 517 URLDataSourceImpl* source = GetDataSourceFromURL(request->url()); |
| 679 if (!source) | 518 if (!source) |
| 680 return false; | 519 return false; |
| 681 | 520 |
| 682 const content::ResourceRequestInfo* info = | 521 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); |
| 683 content::ResourceRequestInfo::ForRequest(request); | |
| 684 if (!source->source()->ShouldServiceRequest( | 522 if (!source->source()->ShouldServiceRequest( |
| 685 request->url(), info ? info->GetContext() : nullptr, | 523 request->url(), info ? info->GetContext() : nullptr, |
| 686 info ? info->GetChildID() : -1)) { | 524 info ? info->GetChildID() : -1)) { |
| 687 return false; | 525 return false; |
| 688 } | 526 } |
| 689 | 527 |
| 690 std::string path; | 528 std::string path; |
| 691 URLToRequestPath(request->url(), &path); | 529 URLToRequestPath(request->url(), &path); |
| 692 | 530 |
| 693 // Save this request so we know where to send the data. | 531 // Save this request so we know where to send the data. |
| 694 RequestID request_id = next_request_id_++; | 532 RequestID request_id = next_request_id_++; |
| 695 pending_requests_.insert(std::make_pair(request_id, job)); | 533 pending_requests_.insert(std::make_pair(request_id, job)); |
| 696 | 534 |
| 697 job->set_allow_caching(source->source()->AllowCaching()); | |
| 698 job->set_add_content_security_policy( | |
| 699 source->source()->ShouldAddContentSecurityPolicy()); | |
| 700 job->set_content_security_policy_script_source( | |
| 701 source->source()->GetContentSecurityPolicyScriptSrc()); | |
| 702 job->set_content_security_policy_object_source( | |
| 703 source->source()->GetContentSecurityPolicyObjectSrc()); | |
| 704 job->set_content_security_policy_child_source( | |
| 705 source->source()->GetContentSecurityPolicyChildSrc()); | |
| 706 job->set_content_security_policy_style_source( | |
| 707 source->source()->GetContentSecurityPolicyStyleSrc()); | |
| 708 job->set_content_security_policy_image_source( | |
| 709 source->source()->GetContentSecurityPolicyImgSrc()); | |
| 710 job->set_deny_xframe_options( | |
| 711 source->source()->ShouldDenyXFrameOptions()); | |
| 712 job->set_send_content_type_header( | |
| 713 source->source()->ShouldServeMimeTypeAsContentTypeHeader()); | |
| 714 job->set_is_gzipped(source->source()->IsGzipped(path)); | 535 job->set_is_gzipped(source->source()->IsGzipped(path)); |
| 715 | 536 |
| 716 // TODO(dschuyler): improve filtering of which resource to run template | 537 // TODO(dschuyler): improve filtering of which resource to run template |
| 717 // replacements upon. | 538 // replacements upon. |
| 718 std::string mime_type = source->source()->GetMimeType(path); | 539 std::string mime_type = source->source()->GetMimeType(path); |
| 719 if (mime_type == "text/html") | 540 if (mime_type == "text/html") |
| 720 job->SetReplacements(source->GetReplacements()); | 541 job->SetReplacements(source->GetReplacements()); |
| 721 | 542 |
| 722 std::string origin = GetOriginHeaderValue(request); | |
| 723 if (!origin.empty()) { | |
| 724 std::string header = | |
| 725 source->source()->GetAccessControlAllowOriginForOrigin(origin); | |
| 726 DCHECK(header.empty() || header == origin || header == "*" || | |
| 727 header == "null"); | |
| 728 job->set_access_control_allow_origin(header); | |
| 729 } | |
| 730 | |
| 731 // Also notifies that the headers are complete. | 543 // Also notifies that the headers are complete. |
| 732 job->MimeTypeAvailable(mime_type); | 544 job->MimeTypeAvailable(mime_type); |
| 733 | 545 |
| 734 // Look up additional request info to pass down. | 546 // Look up additional request info to pass down. |
| 735 ResourceRequestInfo::WebContentsGetter wc_getter; | 547 ResourceRequestInfo::WebContentsGetter wc_getter; |
| 736 if (info) | 548 if (info) |
| 737 wc_getter = info->GetWebContentsGetterForRequest(); | 549 wc_getter = info->GetWebContentsGetterForRequest(); |
| 738 | 550 |
| 739 // Forward along the request to the data source. | 551 // Forward along the request to the data source. |
| 740 scoped_refptr<base::SingleThreadTaskRunner> target_runner = | 552 scoped_refptr<base::SingleThreadTaskRunner> target_runner = |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 803 base::RefCountedMemory* bytes) { | 615 base::RefCountedMemory* bytes) { |
| 804 // Forward this data on to the pending net::URLRequest, if it exists. | 616 // Forward this data on to the pending net::URLRequest, if it exists. |
| 805 PendingRequestMap::iterator i = pending_requests_.find(request_id); | 617 PendingRequestMap::iterator i = pending_requests_.find(request_id); |
| 806 if (i != pending_requests_.end()) { | 618 if (i != pending_requests_.end()) { |
| 807 URLRequestChromeJob* job = i->second; | 619 URLRequestChromeJob* job = i->second; |
| 808 pending_requests_.erase(i); | 620 pending_requests_.erase(i); |
| 809 job->DataAvailable(bytes); | 621 job->DataAvailable(bytes); |
| 810 } | 622 } |
| 811 } | 623 } |
| 812 | 624 |
| 625 scoped_refptr<net::HttpResponseHeaders> URLDataManagerBackend::GetHeaders( |
| 626 URLDataSourceImpl* source_impl, |
| 627 const std::string& path, |
| 628 const std::string& origin) { |
| 629 // Set the headers so that requests serviced by ChromeURLDataManager return a |
| 630 // status code of 200. Without this they return a 0, which makes the status |
| 631 // indistiguishable from other error types. Instant relies on getting a 200. |
| 632 scoped_refptr<net::HttpResponseHeaders> headers( |
| 633 new net::HttpResponseHeaders("HTTP/1.1 200 OK")); |
| 634 if (!source_impl) |
| 635 return headers; |
| 636 |
| 637 URLDataSource* source = source_impl->source(); |
| 638 // Determine the least-privileged content security policy header, if any, |
| 639 // that is compatible with a given WebUI URL, and append it to the existing |
| 640 // response headers. |
| 641 if (source->ShouldAddContentSecurityPolicy()) { |
| 642 std::string base = kChromeURLContentSecurityPolicyHeaderBase; |
| 643 base.append(source->GetContentSecurityPolicyScriptSrc()); |
| 644 base.append(source->GetContentSecurityPolicyObjectSrc()); |
| 645 base.append(source->GetContentSecurityPolicyChildSrc()); |
| 646 base.append(source->GetContentSecurityPolicyStyleSrc()); |
| 647 base.append(source->GetContentSecurityPolicyImgSrc()); |
| 648 headers->AddHeader(base); |
| 649 } |
| 650 |
| 651 if (source->ShouldDenyXFrameOptions()) |
| 652 headers->AddHeader(kChromeURLXFrameOptionsHeader); |
| 653 |
| 654 if (!source->AllowCaching()) |
| 655 headers->AddHeader("Cache-Control: no-cache"); |
| 656 |
| 657 std::string mime_type = source->GetMimeType(path); |
| 658 if (source->ShouldServeMimeTypeAsContentTypeHeader() && !mime_type.empty()) { |
| 659 std::string content_type = base::StringPrintf( |
| 660 "%s:%s", net::HttpRequestHeaders::kContentType, mime_type.c_str()); |
| 661 headers->AddHeader(content_type); |
| 662 } |
| 663 |
| 664 if (!origin.empty()) { |
| 665 std::string header = source->GetAccessControlAllowOriginForOrigin(origin); |
| 666 DCHECK(header.empty() || header == origin || header == "*" || |
| 667 header == "null"); |
| 668 if (!header.empty()) { |
| 669 headers->AddHeader("Access-Control-Allow-Origin: " + header); |
| 670 headers->AddHeader("Vary: Origin"); |
| 671 } |
| 672 } |
| 673 |
| 674 return headers; |
| 675 } |
| 676 |
| 677 bool URLDataManagerBackend::CheckURLIsValid(const GURL& url) { |
| 678 std::vector<std::string> additional_schemes; |
| 679 DCHECK(url.SchemeIs(kChromeDevToolsScheme) || url.SchemeIs(kChromeUIScheme) || |
| 680 (GetContentClient()->browser()->GetAdditionalWebUISchemes( |
| 681 &additional_schemes), |
| 682 SchemeIsInSchemes(url.scheme(), additional_schemes))); |
| 683 |
| 684 if (!url.is_valid()) { |
| 685 NOTREACHED(); |
| 686 return false; |
| 687 } |
| 688 |
| 689 return true; |
| 690 } |
| 691 |
| 692 void URLDataManagerBackend::URLToRequestPath(const GURL& url, |
| 693 std::string* path) { |
| 694 const std::string& spec = url.possibly_invalid_spec(); |
| 695 const url::Parsed& parsed = url.parsed_for_possibly_invalid_spec(); |
| 696 // + 1 to skip the slash at the beginning of the path. |
| 697 int offset = parsed.CountCharactersBefore(url::Parsed::PATH, false) + 1; |
| 698 |
| 699 if (offset < static_cast<int>(spec.size())) |
| 700 path->assign(spec.substr(offset)); |
| 701 } |
| 702 |
| 813 namespace { | 703 namespace { |
| 814 | 704 |
| 815 class DevToolsJobFactory | 705 class DevToolsJobFactory |
| 816 : public net::URLRequestJobFactory::ProtocolHandler { | 706 : public net::URLRequestJobFactory::ProtocolHandler { |
| 817 public: | 707 public: |
| 818 // |is_incognito| should be set for incognito profiles. | 708 explicit DevToolsJobFactory(ResourceContext* resource_context); |
| 819 DevToolsJobFactory(ResourceContext* resource_context, bool is_incognito); | |
| 820 ~DevToolsJobFactory() override; | 709 ~DevToolsJobFactory() override; |
| 821 | 710 |
| 822 net::URLRequestJob* MaybeCreateJob( | 711 net::URLRequestJob* MaybeCreateJob( |
| 823 net::URLRequest* request, | 712 net::URLRequest* request, |
| 824 net::NetworkDelegate* network_delegate) const override; | 713 net::NetworkDelegate* network_delegate) const override; |
| 825 | 714 |
| 826 private: | 715 private: |
| 827 // |resource_context_| and |network_delegate_| are owned by ProfileIOData, | 716 // |resource_context_| and |network_delegate_| are owned by ProfileIOData, |
| 828 // which owns this ProtocolHandler. | 717 // which owns this ProtocolHandler. |
| 829 ResourceContext* const resource_context_; | 718 ResourceContext* const resource_context_; |
| 830 | 719 |
| 831 // True when generated from an incognito profile. | |
| 832 const bool is_incognito_; | |
| 833 | |
| 834 DISALLOW_COPY_AND_ASSIGN(DevToolsJobFactory); | 720 DISALLOW_COPY_AND_ASSIGN(DevToolsJobFactory); |
| 835 }; | 721 }; |
| 836 | 722 |
| 837 DevToolsJobFactory::DevToolsJobFactory(ResourceContext* resource_context, | 723 DevToolsJobFactory::DevToolsJobFactory(ResourceContext* resource_context) |
| 838 bool is_incognito) | 724 : resource_context_(resource_context) { |
| 839 : resource_context_(resource_context), is_incognito_(is_incognito) { | |
| 840 DCHECK(resource_context_); | 725 DCHECK(resource_context_); |
| 841 } | 726 } |
| 842 | 727 |
| 843 DevToolsJobFactory::~DevToolsJobFactory() {} | 728 DevToolsJobFactory::~DevToolsJobFactory() {} |
| 844 | 729 |
| 845 net::URLRequestJob* | 730 net::URLRequestJob* |
| 846 DevToolsJobFactory::MaybeCreateJob( | 731 DevToolsJobFactory::MaybeCreateJob( |
| 847 net::URLRequest* request, net::NetworkDelegate* network_delegate) const { | 732 net::URLRequest* request, net::NetworkDelegate* network_delegate) const { |
| 848 return new URLRequestChromeJob( | 733 return new URLRequestChromeJob( |
| 849 request, network_delegate, | 734 request, network_delegate, |
| 850 GetURLDataManagerForResourceContext(resource_context_), is_incognito_); | 735 GetURLDataManagerForResourceContext(resource_context_)); |
| 851 } | 736 } |
| 852 | 737 |
| 853 } // namespace | 738 } // namespace |
| 854 | 739 |
| 855 net::URLRequestJobFactory::ProtocolHandler* CreateDevToolsProtocolHandler( | 740 net::URLRequestJobFactory::ProtocolHandler* CreateDevToolsProtocolHandler( |
| 856 ResourceContext* resource_context, | 741 ResourceContext* resource_context) { |
| 857 bool is_incognito) { | 742 return new DevToolsJobFactory(resource_context); |
| 858 return new DevToolsJobFactory(resource_context, is_incognito); | |
| 859 } | 743 } |
| 860 | 744 |
| 861 } // namespace content | 745 } // namespace content |
| OLD | NEW |