Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(315)

Side by Side Diff: chrome/browser/ui/webui/chrome_url_data_manager_backend.cc

Issue 12049052: Move core url data manager classes to content. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: review comments Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/webui/chrome_url_data_manager_backend.h"
6
7 #include <set>
8
9 #include "base/basictypes.h"
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/compiler_specific.h"
13 #include "base/debug/trace_event.h"
14 #include "base/file_util.h"
15 #include "base/lazy_instance.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/memory/ref_counted_memory.h"
18 #include "base/memory/weak_ptr.h"
19 #include "base/message_loop.h"
20 #include "base/string_util.h"
21 #include "chrome/browser/ui/webui/shared_resources_data_source.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/common/url_constants.h"
24 #include "googleurl/src/url_util.h"
25 #include "grit/platform_locale_settings.h"
26 #include "net/base/io_buffer.h"
27 #include "net/base/net_errors.h"
28 #include "net/http/http_response_headers.h"
29 #include "net/url_request/url_request.h"
30 #include "net/url_request/url_request_context.h"
31 #include "net/url_request/url_request_job.h"
32 #include "net/url_request/url_request_job_factory.h"
33
34 using content::BrowserThread;
35
36 namespace {
37
38 // TODO(tsepez) remove unsafe-eval when bidichecker_packaged.js fixed.
39 const char kChromeURLContentSecurityPolicyHeaderBase[] =
40 "Content-Security-Policy: script-src chrome://resources "
41 "'self' 'unsafe-eval'; ";
42
43 const char kChromeURLXFrameOptionsHeader[] = "X-Frame-Options: DENY";
44
45 // Parse a URL into the components used to resolve its request. |source_name|
46 // is the hostname and |path| is the remaining portion of the URL.
47 void URLToRequest(const GURL& url, std::string* source_name,
48 std::string* path) {
49 DCHECK(url.SchemeIs(chrome::kChromeDevToolsScheme) ||
50 url.SchemeIs(chrome::kChromeUIScheme));
51
52 if (!url.is_valid()) {
53 NOTREACHED();
54 return;
55 }
56
57 // Our input looks like: chrome://source_name/extra_bits?foo .
58 // So the url's "host" is our source, and everything after the host is
59 // the path.
60 source_name->assign(url.host());
61
62 const std::string& spec = url.possibly_invalid_spec();
63 const url_parse::Parsed& parsed = url.parsed_for_possibly_invalid_spec();
64 // + 1 to skip the slash at the beginning of the path.
65 int offset = parsed.CountCharactersBefore(url_parse::Parsed::PATH, false) + 1;
66
67 if (offset < static_cast<int>(spec.size()))
68 path->assign(spec.substr(offset));
69 }
70
71 } // namespace
72
73 // URLRequestChromeJob is a net::URLRequestJob that manages running
74 // chrome-internal resource requests asynchronously.
75 // It hands off URL requests to ChromeURLDataManager, which asynchronously
76 // calls back once the data is available.
77 class URLRequestChromeJob : public net::URLRequestJob,
78 public base::SupportsWeakPtr<URLRequestChromeJob> {
79 public:
80 // |is_incognito| set when job is generated from an incognito profile.
81 URLRequestChromeJob(net::URLRequest* request,
82 net::NetworkDelegate* network_delegate,
83 ChromeURLDataManagerBackend* backend,
84 bool is_incognito);
85
86 // net::URLRequestJob implementation.
87 virtual void Start() OVERRIDE;
88 virtual void Kill() OVERRIDE;
89 virtual bool ReadRawData(net::IOBuffer* buf,
90 int buf_size,
91 int* bytes_read) OVERRIDE;
92 virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
93 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE;
94
95 // Used to notify that the requested data's |mime_type| is ready.
96 void MimeTypeAvailable(const std::string& mime_type);
97
98 // Called by ChromeURLDataManager to notify us that the data blob is ready
99 // for us.
100 void DataAvailable(base::RefCountedMemory* bytes);
101
102 void set_mime_type(const std::string& mime_type) {
103 mime_type_ = mime_type;
104 }
105
106 void set_allow_caching(bool allow_caching) {
107 allow_caching_ = allow_caching;
108 }
109
110 void set_add_content_security_policy(bool add_content_security_policy) {
111 add_content_security_policy_ = add_content_security_policy;
112 }
113
114 void set_content_security_policy_object_source(
115 const std::string& data) {
116 content_security_policy_object_source_ = data;
117 }
118
119 void set_content_security_policy_frame_source(
120 const std::string& data) {
121 content_security_policy_frame_source_ = data;
122 }
123
124 void set_deny_xframe_options(bool deny_xframe_options) {
125 deny_xframe_options_ = deny_xframe_options;
126 }
127
128 // Returns true when job was generated from an incognito profile.
129 bool is_incognito() const {
130 return is_incognito_;
131 }
132
133 private:
134 virtual ~URLRequestChromeJob();
135
136 // Helper for Start(), to let us start asynchronously.
137 // (This pattern is shared by most net::URLRequestJob implementations.)
138 void StartAsync();
139
140 // Do the actual copy from data_ (the data we're serving) into |buf|.
141 // Separate from ReadRawData so we can handle async I/O.
142 void CompleteRead(net::IOBuffer* buf, int buf_size, int* bytes_read);
143
144 // The actual data we're serving. NULL until it's been fetched.
145 scoped_refptr<base::RefCountedMemory> data_;
146 // The current offset into the data that we're handing off to our
147 // callers via the Read interfaces.
148 int data_offset_;
149
150 // For async reads, we keep around a pointer to the buffer that
151 // we're reading into.
152 scoped_refptr<net::IOBuffer> pending_buf_;
153 int pending_buf_size_;
154 std::string mime_type_;
155
156 // If true, set a header in the response to prevent it from being cached.
157 bool allow_caching_;
158
159 // If true, set the Content Security Policy (CSP) header.
160 bool add_content_security_policy_;
161
162 // These are used with the CSP.
163 std::string content_security_policy_object_source_;
164 std::string content_security_policy_frame_source_;
165
166 // If true, sets the "X-Frame-Options: DENY" header.
167 bool deny_xframe_options_;
168
169 // True when job is generated from an incognito profile.
170 const bool is_incognito_;
171
172 // The backend is owned by ChromeURLRequestContext and always outlives us.
173 ChromeURLDataManagerBackend* backend_;
174
175 base::WeakPtrFactory<URLRequestChromeJob> weak_factory_;
176
177 DISALLOW_COPY_AND_ASSIGN(URLRequestChromeJob);
178 };
179
180 URLRequestChromeJob::URLRequestChromeJob(net::URLRequest* request,
181 net::NetworkDelegate* network_delegate,
182 ChromeURLDataManagerBackend* backend,
183 bool is_incognito)
184 : net::URLRequestJob(request, network_delegate),
185 data_offset_(0),
186 pending_buf_size_(0),
187 allow_caching_(true),
188 add_content_security_policy_(true),
189 content_security_policy_object_source_("object-src 'none';"),
190 content_security_policy_frame_source_("frame-src 'none';"),
191 deny_xframe_options_(true),
192 is_incognito_(is_incognito),
193 backend_(backend),
194 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
195 DCHECK(backend);
196 }
197
198 URLRequestChromeJob::~URLRequestChromeJob() {
199 CHECK(!backend_->HasPendingJob(this));
200 }
201
202 void URLRequestChromeJob::Start() {
203 // Start reading asynchronously so that all error reporting and data
204 // callbacks happen as they would for network requests.
205 MessageLoop::current()->PostTask(
206 FROM_HERE,
207 base::Bind(&URLRequestChromeJob::StartAsync,
208 weak_factory_.GetWeakPtr()));
209
210 TRACE_EVENT_ASYNC_BEGIN1("browser", "DataManager:Request", this, "URL",
211 request_->url().possibly_invalid_spec());
212 }
213
214 void URLRequestChromeJob::Kill() {
215 backend_->RemoveRequest(this);
216 }
217
218 bool URLRequestChromeJob::GetMimeType(std::string* mime_type) const {
219 *mime_type = mime_type_;
220 return !mime_type_.empty();
221 }
222
223 void URLRequestChromeJob::GetResponseInfo(net::HttpResponseInfo* info) {
224 DCHECK(!info->headers);
225 // Set the headers so that requests serviced by ChromeURLDataManager return a
226 // status code of 200. Without this they return a 0, which makes the status
227 // indistiguishable from other error types. Instant relies on getting a 200.
228 info->headers = new net::HttpResponseHeaders("HTTP/1.1 200 OK");
229
230 // Determine the least-privileged content security policy header, if any,
231 // that is compatible with a given WebUI URL, and append it to the existing
232 // response headers.
233 if (add_content_security_policy_) {
234 std::string base = kChromeURLContentSecurityPolicyHeaderBase;
235 base.append(content_security_policy_object_source_);
236 base.append(content_security_policy_frame_source_);
237 info->headers->AddHeader(base);
238 }
239
240 if (deny_xframe_options_)
241 info->headers->AddHeader(kChromeURLXFrameOptionsHeader);
242
243 if (!allow_caching_)
244 info->headers->AddHeader("Cache-Control: no-cache");
245 }
246
247 void URLRequestChromeJob::MimeTypeAvailable(const std::string& mime_type) {
248 set_mime_type(mime_type);
249 NotifyHeadersComplete();
250 }
251
252 void URLRequestChromeJob::DataAvailable(base::RefCountedMemory* bytes) {
253 TRACE_EVENT_ASYNC_END0("browser", "DataManager:Request", this);
254 if (bytes) {
255 // The request completed, and we have all the data.
256 // Clear any IO pending status.
257 SetStatus(net::URLRequestStatus());
258
259 data_ = bytes;
260 int bytes_read;
261 if (pending_buf_.get()) {
262 CHECK(pending_buf_->data());
263 CompleteRead(pending_buf_, pending_buf_size_, &bytes_read);
264 pending_buf_ = NULL;
265 NotifyReadComplete(bytes_read);
266 }
267 } else {
268 // The request failed.
269 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
270 net::ERR_FAILED));
271 }
272 }
273
274 bool URLRequestChromeJob::ReadRawData(net::IOBuffer* buf, int buf_size,
275 int* bytes_read) {
276 if (!data_.get()) {
277 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
278 DCHECK(!pending_buf_.get());
279 CHECK(buf->data());
280 pending_buf_ = buf;
281 pending_buf_size_ = buf_size;
282 return false; // Tell the caller we're still waiting for data.
283 }
284
285 // Otherwise, the data is available.
286 CompleteRead(buf, buf_size, bytes_read);
287 return true;
288 }
289
290 void URLRequestChromeJob::CompleteRead(net::IOBuffer* buf, int buf_size,
291 int* bytes_read) {
292 int remaining = static_cast<int>(data_->size()) - data_offset_;
293 if (buf_size > remaining)
294 buf_size = remaining;
295 if (buf_size > 0) {
296 memcpy(buf->data(), data_->front() + data_offset_, buf_size);
297 data_offset_ += buf_size;
298 }
299 *bytes_read = buf_size;
300 }
301
302 void URLRequestChromeJob::StartAsync() {
303 if (!request_)
304 return;
305
306 if (!backend_->StartRequest(request_->url(), this)) {
307 NotifyStartError(net::URLRequestStatus(net::URLRequestStatus::FAILED,
308 net::ERR_INVALID_URL));
309 }
310 }
311
312 namespace {
313
314 // Gets mime type for data that is available from |source| by |path|.
315 // After that, notifies |job| that mime type is available. This method
316 // should be called on the UI thread, but notification is performed on
317 // the IO thread.
318 void GetMimeTypeOnUI(URLDataSourceImpl* source,
319 const std::string& path,
320 const base::WeakPtr<URLRequestChromeJob>& job) {
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
322 std::string mime_type = source->source()->GetMimeType(path);
323 BrowserThread::PostTask(
324 BrowserThread::IO, FROM_HERE,
325 base::Bind(&URLRequestChromeJob::MimeTypeAvailable, job, mime_type));
326 }
327
328 } // namespace
329
330 namespace {
331
332 class ChromeProtocolHandler
333 : public net::URLRequestJobFactory::ProtocolHandler {
334 public:
335 // |is_incognito| should be set for incognito profiles.
336 explicit ChromeProtocolHandler(ChromeURLDataManagerBackend* backend,
337 bool is_incognito);
338 ~ChromeProtocolHandler();
339
340 virtual net::URLRequestJob* MaybeCreateJob(
341 net::URLRequest* request,
342 net::NetworkDelegate* network_delegate) const OVERRIDE;
343
344 private:
345 // These members are owned by ProfileIOData, which owns this ProtocolHandler.
346 ChromeURLDataManagerBackend* const backend_;
347
348 // True when generated from an incognito profile.
349 const bool is_incognito_;
350
351 DISALLOW_COPY_AND_ASSIGN(ChromeProtocolHandler);
352 };
353
354 ChromeProtocolHandler::ChromeProtocolHandler(
355 ChromeURLDataManagerBackend* backend, bool is_incognito)
356 : backend_(backend), is_incognito_(is_incognito) {}
357
358 ChromeProtocolHandler::~ChromeProtocolHandler() {}
359
360 net::URLRequestJob* ChromeProtocolHandler::MaybeCreateJob(
361 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
362 DCHECK(request);
363
364 // Fall back to using a custom handler
365 return new URLRequestChromeJob(request, network_delegate, backend_,
366 is_incognito_);
367 }
368
369 } // namespace
370
371 ChromeURLDataManagerBackend::ChromeURLDataManagerBackend()
372 : next_request_id_(0) {
373 content::URLDataSource* shared_source = new SharedResourcesDataSource();
374 URLDataSourceImpl* source_impl =
375 new URLDataSourceImpl(shared_source->GetSource(), shared_source);
376 AddDataSource(source_impl);
377 }
378
379 ChromeURLDataManagerBackend::~ChromeURLDataManagerBackend() {
380 for (DataSourceMap::iterator i = data_sources_.begin();
381 i != data_sources_.end(); ++i) {
382 i->second->backend_ = NULL;
383 }
384 data_sources_.clear();
385 }
386
387 // static
388 net::URLRequestJobFactory::ProtocolHandler*
389 ChromeURLDataManagerBackend::CreateProtocolHandler(
390 ChromeURLDataManagerBackend* backend, bool is_incognito) {
391 DCHECK(backend);
392 return new ChromeProtocolHandler(backend, is_incognito);
393 }
394
395 void ChromeURLDataManagerBackend::AddDataSource(
396 URLDataSourceImpl* source) {
397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
398 DataSourceMap::iterator i = data_sources_.find(source->source_name());
399 if (i != data_sources_.end()) {
400 if (!source->source()->ShouldReplaceExistingSource())
401 return;
402 i->second->backend_ = NULL;
403 }
404 data_sources_[source->source_name()] = source;
405 source->backend_ = this;
406 }
407
408 bool ChromeURLDataManagerBackend::HasPendingJob(
409 URLRequestChromeJob* job) const {
410 for (PendingRequestMap::const_iterator i = pending_requests_.begin();
411 i != pending_requests_.end(); ++i) {
412 if (i->second == job)
413 return true;
414 }
415 return false;
416 }
417
418 bool ChromeURLDataManagerBackend::StartRequest(const GURL& url,
419 URLRequestChromeJob* job) {
420 // Parse the URL into a request for a source and path.
421 std::string source_name;
422 std::string path;
423 URLToRequest(url, &source_name, &path);
424
425 // Look up the data source for the request.
426 DataSourceMap::iterator i = data_sources_.find(source_name);
427 if (i == data_sources_.end())
428 return false;
429
430 URLDataSourceImpl* source = i->second;
431
432 // Save this request so we know where to send the data.
433 RequestID request_id = next_request_id_++;
434 pending_requests_.insert(std::make_pair(request_id, job));
435
436 job->set_allow_caching(source->source()->AllowCaching());
437 job->set_add_content_security_policy(
438 source->source()->ShouldAddContentSecurityPolicy());
439 job->set_content_security_policy_object_source(
440 source->source()->GetContentSecurityPolicyObjectSrc());
441 job->set_content_security_policy_frame_source(
442 source->source()->GetContentSecurityPolicyFrameSrc());
443 job->set_deny_xframe_options(
444 source->source()->ShouldDenyXFrameOptions());
445
446 // Forward along the request to the data source.
447 MessageLoop* target_message_loop =
448 source->source()->MessageLoopForRequestPath(path);
449 if (!target_message_loop) {
450 bool is_incognito = job->is_incognito();
451 job->MimeTypeAvailable(source->source()->GetMimeType(path));
452 // Eliminate potentially dangling pointer to avoid future use.
453 job = NULL;
454
455 // The DataSource is agnostic to which thread StartDataRequest is called
456 // on for this path. Call directly into it from this thread, the IO
457 // thread.
458 source->source()->StartDataRequest(
459 path, is_incognito,
460 base::Bind(&URLDataSourceImpl::SendResponse, source, request_id));
461 } else {
462 // URLRequestChromeJob should receive mime type before data. This
463 // is guaranteed because request for mime type is placed in the
464 // message loop before request for data. And correspondingly their
465 // replies are put on the IO thread in the same order.
466 target_message_loop->PostTask(
467 FROM_HERE,
468 base::Bind(&GetMimeTypeOnUI,
469 scoped_refptr<URLDataSourceImpl>(source),
470 path, job->AsWeakPtr()));
471
472 // The DataSource wants StartDataRequest to be called on a specific thread,
473 // usually the UI thread, for this path.
474 target_message_loop->PostTask(
475 FROM_HERE,
476 base::Bind(&ChromeURLDataManagerBackend::CallStartRequest,
477 make_scoped_refptr(source), path, job->is_incognito(),
478 request_id));
479 }
480 return true;
481 }
482
483 void ChromeURLDataManagerBackend::CallStartRequest(
484 scoped_refptr<URLDataSourceImpl> source,
485 const std::string& path,
486 bool is_incognito,
487 int request_id) {
488 source->source()->StartDataRequest(
489 path,
490 is_incognito,
491 base::Bind(&URLDataSourceImpl::SendResponse, source, request_id));
492 }
493
494 void ChromeURLDataManagerBackend::RemoveRequest(URLRequestChromeJob* job) {
495 // Remove the request from our list of pending requests.
496 // If/when the source sends the data that was requested, the data will just
497 // be thrown away.
498 for (PendingRequestMap::iterator i = pending_requests_.begin();
499 i != pending_requests_.end(); ++i) {
500 if (i->second == job) {
501 pending_requests_.erase(i);
502 return;
503 }
504 }
505 }
506
507 void ChromeURLDataManagerBackend::DataAvailable(RequestID request_id,
508 base::RefCountedMemory* bytes) {
509 // Forward this data on to the pending net::URLRequest, if it exists.
510 PendingRequestMap::iterator i = pending_requests_.find(request_id);
511 if (i != pending_requests_.end()) {
512 URLRequestChromeJob* job(i->second);
513 pending_requests_.erase(i);
514 job->DataAvailable(bytes);
515 }
516 }
517
518 namespace {
519
520 class DevToolsJobFactory
521 : public net::URLRequestJobFactory::ProtocolHandler {
522 public:
523 // |is_incognito| should be set for incognito profiles.
524 DevToolsJobFactory(ChromeURLDataManagerBackend* backend,
525 net::NetworkDelegate* network_delegate,
526 bool is_incognito);
527 virtual ~DevToolsJobFactory();
528
529 virtual net::URLRequestJob* MaybeCreateJob(
530 net::URLRequest* request,
531 net::NetworkDelegate* network_delegate) const OVERRIDE;
532
533 private:
534 // |backend_| and |network_delegate_| are owned by ProfileIOData, which owns
535 // this ProtocolHandler.
536 ChromeURLDataManagerBackend* const backend_;
537 net::NetworkDelegate* network_delegate_;
538
539 // True when generated from an incognito profile.
540 const bool is_incognito_;
541
542 DISALLOW_COPY_AND_ASSIGN(DevToolsJobFactory);
543 };
544
545 DevToolsJobFactory::DevToolsJobFactory(ChromeURLDataManagerBackend* backend,
546 net::NetworkDelegate* network_delegate,
547 bool is_incognito)
548 : backend_(backend),
549 network_delegate_(network_delegate),
550 is_incognito_(is_incognito) {
551 DCHECK(backend_);
552 }
553
554 DevToolsJobFactory::~DevToolsJobFactory() {}
555
556 net::URLRequestJob*
557 DevToolsJobFactory::MaybeCreateJob(
558 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
559 return new URLRequestChromeJob(request, network_delegate, backend_,
560 is_incognito_);
561 }
562
563 } // namespace
564
565 net::URLRequestJobFactory::ProtocolHandler*
566 CreateDevToolsProtocolHandler(ChromeURLDataManagerBackend* backend,
567 net::NetworkDelegate* network_delegate,
568 bool is_incognito) {
569 return new DevToolsJobFactory(backend, network_delegate, is_incognito);
570 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/webui/chrome_url_data_manager_backend.h ('k') | chrome/browser/ui/webui/chrome_url_data_manager_factory.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698