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 |