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

Side by Side Diff: content/browser/fileapi/fileapi_message_filter.cc

Issue 15817013: Add Stream support to WebBlobRegistry and FileAPIMessageFilter. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Resolve Created 7 years, 5 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
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/fileapi/fileapi_message_filter.h" 5 #include "content/browser/fileapi/fileapi_message_filter.h"
6 6
7 #include <string> 7 #include <string>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/files/file_path.h" 11 #include "base/files/file_path.h"
12 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
13 #include "base/platform_file.h" 13 #include "base/platform_file.h"
14 #include "base/sequenced_task_runner.h" 14 #include "base/sequenced_task_runner.h"
15 #include "base/strings/string_util.h"
15 #include "base/threading/thread.h" 16 #include "base/threading/thread.h"
16 #include "base/time/time.h" 17 #include "base/time/time.h"
17 #include "content/browser/child_process_security_policy_impl.h" 18 #include "content/browser/child_process_security_policy_impl.h"
18 #include "content/browser/fileapi/browser_file_system_helper.h" 19 #include "content/browser/fileapi/browser_file_system_helper.h"
19 #include "content/browser/fileapi/chrome_blob_storage_context.h" 20 #include "content/browser/fileapi/chrome_blob_storage_context.h"
21 #include "content/browser/streams/stream_registry.h"
20 #include "content/common/fileapi/file_system_messages.h" 22 #include "content/common/fileapi/file_system_messages.h"
21 #include "content/common/fileapi/webblob_messages.h" 23 #include "content/common/fileapi/webblob_messages.h"
22 #include "content/public/browser/user_metrics.h" 24 #include "content/public/browser/user_metrics.h"
23 #include "ipc/ipc_platform_file.h" 25 #include "ipc/ipc_platform_file.h"
26 #include "net/base/io_buffer.h"
24 #include "net/base/mime_util.h" 27 #include "net/base/mime_util.h"
25 #include "net/url_request/url_request_context.h" 28 #include "net/url_request/url_request_context.h"
26 #include "net/url_request/url_request_context_getter.h" 29 #include "net/url_request/url_request_context_getter.h"
27 #include "url/gurl.h" 30 #include "url/gurl.h"
28 #include "webkit/browser/blob/blob_storage_controller.h" 31 #include "webkit/browser/blob/blob_storage_controller.h"
29 #include "webkit/browser/fileapi/file_observers.h" 32 #include "webkit/browser/fileapi/file_observers.h"
30 #include "webkit/browser/fileapi/file_permission_policy.h" 33 #include "webkit/browser/fileapi/file_permission_policy.h"
31 #include "webkit/browser/fileapi/file_system_context.h" 34 #include "webkit/browser/fileapi/file_system_context.h"
32 #include "webkit/browser/fileapi/file_system_task_runners.h" 35 #include "webkit/browser/fileapi/file_system_task_runners.h"
33 #include "webkit/browser/fileapi/isolated_context.h" 36 #include "webkit/browser/fileapi/isolated_context.h"
(...skipping 23 matching lines...) Expand all
57 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeAllPermissionsForFile( 60 ChildProcessSecurityPolicyImpl::GetInstance()->RevokeAllPermissionsForFile(
58 child_id, path); 61 child_id, path);
59 } 62 }
60 63
61 } // namespace 64 } // namespace
62 65
63 FileAPIMessageFilter::FileAPIMessageFilter( 66 FileAPIMessageFilter::FileAPIMessageFilter(
64 int process_id, 67 int process_id,
65 net::URLRequestContextGetter* request_context_getter, 68 net::URLRequestContextGetter* request_context_getter,
66 fileapi::FileSystemContext* file_system_context, 69 fileapi::FileSystemContext* file_system_context,
67 ChromeBlobStorageContext* blob_storage_context) 70 ChromeBlobStorageContext* blob_storage_context,
71 StreamContext* stream_context)
68 : process_id_(process_id), 72 : process_id_(process_id),
69 context_(file_system_context), 73 context_(file_system_context),
70 request_context_getter_(request_context_getter), 74 request_context_getter_(request_context_getter),
71 request_context_(NULL), 75 request_context_(NULL),
72 blob_storage_context_(blob_storage_context) { 76 blob_storage_context_(blob_storage_context),
77 stream_context_(stream_context) {
73 DCHECK(context_); 78 DCHECK(context_);
74 DCHECK(request_context_getter_.get()); 79 DCHECK(request_context_getter_.get());
75 DCHECK(blob_storage_context); 80 DCHECK(blob_storage_context);
81 DCHECK(stream_context);
76 } 82 }
77 83
78 FileAPIMessageFilter::FileAPIMessageFilter( 84 FileAPIMessageFilter::FileAPIMessageFilter(
79 int process_id, 85 int process_id,
80 net::URLRequestContext* request_context, 86 net::URLRequestContext* request_context,
81 fileapi::FileSystemContext* file_system_context, 87 fileapi::FileSystemContext* file_system_context,
82 ChromeBlobStorageContext* blob_storage_context) 88 ChromeBlobStorageContext* blob_storage_context,
89 StreamContext* stream_context)
83 : process_id_(process_id), 90 : process_id_(process_id),
84 context_(file_system_context), 91 context_(file_system_context),
85 request_context_(request_context), 92 request_context_(request_context),
86 blob_storage_context_(blob_storage_context) { 93 blob_storage_context_(blob_storage_context),
94 stream_context_(stream_context) {
87 DCHECK(context_); 95 DCHECK(context_);
88 DCHECK(request_context_); 96 DCHECK(request_context_);
89 DCHECK(blob_storage_context); 97 DCHECK(blob_storage_context);
98 DCHECK(stream_context);
90 } 99 }
91 100
92 void FileAPIMessageFilter::OnChannelConnected(int32 peer_pid) { 101 void FileAPIMessageFilter::OnChannelConnected(int32 peer_pid) {
93 BrowserMessageFilter::OnChannelConnected(peer_pid); 102 BrowserMessageFilter::OnChannelConnected(peer_pid);
94 103
95 if (request_context_getter_.get()) { 104 if (request_context_getter_.get()) {
96 DCHECK(!request_context_); 105 DCHECK(!request_context_);
97 request_context_ = request_context_getter_->GetURLRequestContext(); 106 request_context_ = request_context_getter_->GetURLRequestContext();
98 request_context_getter_ = NULL; 107 request_context_getter_ = NULL;
99 DCHECK(request_context_); 108 DCHECK(request_context_);
100 } 109 }
101 } 110 }
102 111
112 scoped_refptr<Stream> FileAPIMessageFilter::GetStreamForURL(const GURL& url) {
113 return stream_context_->registry()->GetStream(url);
114 }
kinuko 2013/07/23 13:57:23 nit: please match the method order in .cc and .h (
tyoshino (SeeGerritForStatus) 2013/07/24 12:14:20 Done.
115
103 void FileAPIMessageFilter::OnChannelClosing() { 116 void FileAPIMessageFilter::OnChannelClosing() {
104 BrowserMessageFilter::OnChannelClosing(); 117 BrowserMessageFilter::OnChannelClosing();
105 118
106 // Unregister all the blob URLs that are previously registered in this 119 // Unregister all the blob URLs that are previously registered in this
107 // process. 120 // process.
108 for (base::hash_set<std::string>::const_iterator iter = blob_urls_.begin(); 121 for (base::hash_set<std::string>::const_iterator iter = blob_urls_.begin();
109 iter != blob_urls_.end(); ++iter) { 122 iter != blob_urls_.end(); ++iter) {
110 blob_storage_context_->controller()->RemoveBlob(GURL(*iter)); 123 const GURL url(*iter);
124 // Intercept stream URLs.
125 if (GetStreamForURL(url))
126 stream_context_->registry()->UnregisterStream(url);
127 else
128 blob_storage_context_->controller()->RemoveBlob(url);
111 } 129 }
112 130
113 in_transit_snapshot_files_.clear(); 131 in_transit_snapshot_files_.clear();
114 132
115 // Close all files that are previously OpenFile()'ed in this process. 133 // Close all files that are previously OpenFile()'ed in this process.
116 if (!on_close_callbacks_.IsEmpty()) { 134 if (!on_close_callbacks_.IsEmpty()) {
117 DLOG(INFO) 135 DLOG(INFO)
118 << "File API: Renderer process shut down before NotifyCloseFile" 136 << "File API: Renderer process shut down before NotifyCloseFile"
119 << " for " << on_close_callbacks_.size() << " files opened in PPAPI"; 137 << " for " << on_close_callbacks_.size() << " files opened in PPAPI";
120 } 138 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
161 IPC_MESSAGE_HANDLER(FileSystemHostMsg_NotifyCloseFile, OnNotifyCloseFile) 179 IPC_MESSAGE_HANDLER(FileSystemHostMsg_NotifyCloseFile, OnNotifyCloseFile)
162 IPC_MESSAGE_HANDLER(FileSystemHostMsg_CreateSnapshotFile, 180 IPC_MESSAGE_HANDLER(FileSystemHostMsg_CreateSnapshotFile,
163 OnCreateSnapshotFile) 181 OnCreateSnapshotFile)
164 IPC_MESSAGE_HANDLER(FileSystemHostMsg_DidReceiveSnapshotFile, 182 IPC_MESSAGE_HANDLER(FileSystemHostMsg_DidReceiveSnapshotFile,
165 OnDidReceiveSnapshotFile) 183 OnDidReceiveSnapshotFile)
166 IPC_MESSAGE_HANDLER(FileSystemHostMsg_WillUpdate, OnWillUpdate) 184 IPC_MESSAGE_HANDLER(FileSystemHostMsg_WillUpdate, OnWillUpdate)
167 IPC_MESSAGE_HANDLER(FileSystemHostMsg_DidUpdate, OnDidUpdate) 185 IPC_MESSAGE_HANDLER(FileSystemHostMsg_DidUpdate, OnDidUpdate)
168 IPC_MESSAGE_HANDLER(FileSystemHostMsg_SyncGetPlatformPath, 186 IPC_MESSAGE_HANDLER(FileSystemHostMsg_SyncGetPlatformPath,
169 OnSyncGetPlatformPath) 187 OnSyncGetPlatformPath)
170 IPC_MESSAGE_HANDLER(BlobHostMsg_StartBuildingBlob, OnStartBuildingBlob) 188 IPC_MESSAGE_HANDLER(BlobHostMsg_StartBuildingBlob, OnStartBuildingBlob)
171 IPC_MESSAGE_HANDLER(BlobHostMsg_AppendBlobDataItem, OnAppendBlobDataItem) 189 IPC_MESSAGE_HANDLER(BlobHostMsg_StartBuildingStream, OnStartBuildingStream)
172 IPC_MESSAGE_HANDLER(BlobHostMsg_SyncAppendSharedMemory, 190 IPC_MESSAGE_HANDLER(BlobHostMsg_AppendBlobDataItemToBlobOrStream,
173 OnAppendSharedMemory) 191 OnAppendBlobDataItemToBlobOrStream)
192 IPC_MESSAGE_HANDLER(BlobHostMsg_SyncAppendSharedMemoryToBlobOrStream,
193 OnAppendSharedMemoryToBlobOrStream)
174 IPC_MESSAGE_HANDLER(BlobHostMsg_FinishBuildingBlob, OnFinishBuildingBlob) 194 IPC_MESSAGE_HANDLER(BlobHostMsg_FinishBuildingBlob, OnFinishBuildingBlob)
175 IPC_MESSAGE_HANDLER(BlobHostMsg_CloneBlob, OnCloneBlob) 195 IPC_MESSAGE_HANDLER(BlobHostMsg_FinishBuildingStream,
176 IPC_MESSAGE_HANDLER(BlobHostMsg_RemoveBlob, OnRemoveBlob) 196 OnFinishBuildingStream)
197 IPC_MESSAGE_HANDLER(BlobHostMsg_CloneBlobOrStream, OnCloneBlobOrStream)
198 IPC_MESSAGE_HANDLER(BlobHostMsg_RemoveBlobOrStream, OnRemoveBlobOrStream)
177 IPC_MESSAGE_UNHANDLED(handled = false) 199 IPC_MESSAGE_UNHANDLED(handled = false)
178 IPC_END_MESSAGE_MAP_EX() 200 IPC_END_MESSAGE_MAP_EX()
179 return handled; 201 return handled;
180 } 202 }
181 203
182 FileAPIMessageFilter::~FileAPIMessageFilter() {} 204 FileAPIMessageFilter::~FileAPIMessageFilter() {}
183 205
184 void FileAPIMessageFilter::BadMessageReceived() { 206 void FileAPIMessageFilter::BadMessageReceived() {
185 RecordAction(UserMetricsAction("BadMessageTerminate_FAMF")); 207 RecordAction(UserMetricsAction("BadMessageTerminate_FAMF"));
186 BrowserMessageFilter::BadMessageReceived(); 208 BrowserMessageFilter::BadMessageReceived();
(...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after
512 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 534 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
513 in_transit_snapshot_files_.erase(request_id); 535 in_transit_snapshot_files_.erase(request_id);
514 } 536 }
515 537
516 void FileAPIMessageFilter::OnStartBuildingBlob(const GURL& url) { 538 void FileAPIMessageFilter::OnStartBuildingBlob(const GURL& url) {
517 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 539 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
518 blob_storage_context_->controller()->StartBuildingBlob(url); 540 blob_storage_context_->controller()->StartBuildingBlob(url);
519 blob_urls_.insert(url.spec()); 541 blob_urls_.insert(url.spec());
520 } 542 }
521 543
522 void FileAPIMessageFilter::OnAppendBlobDataItem( 544 // Currently |content_type| is ignored inside Chromium.
545 //
546 // TODO(tyoshino): Set |content_type| to the stream.
547 void FileAPIMessageFilter::OnStartBuildingStream(
548 const GURL& url, const std::string& content_type) {
549 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
550 // Only an internal Blob URL is expected here. See the BlobURL of the Blink.
551 DCHECK(StartsWithASCII(
Tom Sepez 2013/07/23 18:08:25 What happens if a compromised renderer violates th
tyoshino (SeeGerritForStatus) 2013/07/24 12:14:20 Done.
552 url.path(), "blobinternal%3A///", true /* case_sensitive*/));
kinuko 2013/07/23 13:57:23 nit: space between case_sensitive and '*/'
tyoshino (SeeGerritForStatus) 2013/07/24 12:14:20 Done.
553 // Use an empty security origin for now. Stream accepts a security origin
554 // but how it's handled is not fixed yet.
555 new Stream(stream_context_->registry(),
556 NULL /* write_observer */,
557 GURL() /* security_origin */,
Tom Sepez 2013/07/23 18:08:25 Does this mean all streams share the same origin?
tyoshino (SeeGerritForStatus) 2013/07/24 12:14:20 Yes. But we have another origin checking system.
tyoshino (SeeGerritForStatus) 2013/07/29 07:44:00 Merged the fix.
558 url);
559 blob_urls_.insert(url.spec());
560 }
561
562 void FileAPIMessageFilter::OnAppendBlobDataItemToBlobOrStream(
523 const GURL& url, const BlobData::Item& item) { 563 const GURL& url, const BlobData::Item& item) {
524 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 564 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
565
566 scoped_refptr<Stream> stream(GetStreamForURL(url));
567 if (stream.get()) {
568 // Data for stream is delivered as TYPE_BYTES item.
569 if (item.type() != BlobData::Item::TYPE_BYTES) {
570 BadMessageReceived();
571 return;
572 }
573 stream->AddData(item.bytes(), item.length());
574
575 return;
576 }
577
525 if (item.type() == BlobData::Item::TYPE_FILE_FILESYSTEM) { 578 if (item.type() == BlobData::Item::TYPE_FILE_FILESYSTEM) {
526 base::PlatformFileError error; 579 base::PlatformFileError error;
527 FileSystemURL filesystem_url(context_->CrackURL(item.url())); 580 FileSystemURL filesystem_url(context_->CrackURL(item.url()));
528 if (!HasPermissionsForFile(filesystem_url, 581 if (!HasPermissionsForFile(filesystem_url,
529 fileapi::kReadFilePermissions, &error)) { 582 fileapi::kReadFilePermissions, &error)) {
530 OnRemoveBlob(url); 583 OnRemoveBlobOrStream(url);
531 return; 584 return;
532 } 585 }
533 } 586 }
534 if (item.type() == BlobData::Item::TYPE_FILE && 587 if (item.type() == BlobData::Item::TYPE_FILE &&
535 !ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile( 588 !ChildProcessSecurityPolicyImpl::GetInstance()->CanReadFile(
536 process_id_, item.path())) { 589 process_id_, item.path())) {
537 OnRemoveBlob(url); 590 OnRemoveBlobOrStream(url);
538 return; 591 return;
539 } 592 }
540 if (item.length() == 0) { 593 if (item.length() == 0) {
541 BadMessageReceived(); 594 BadMessageReceived();
542 return; 595 return;
543 } 596 }
544 blob_storage_context_->controller()->AppendBlobDataItem(url, item); 597 blob_storage_context_->controller()->AppendBlobDataItem(url, item);
545 } 598 }
546 599
547 void FileAPIMessageFilter::OnAppendSharedMemory( 600 void FileAPIMessageFilter::OnAppendSharedMemoryToBlobOrStream(
548 const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size) { 601 const GURL& url, base::SharedMemoryHandle handle, size_t buffer_size) {
549 DCHECK(base::SharedMemory::IsHandleValid(handle)); 602 DCHECK(base::SharedMemory::IsHandleValid(handle));
Tom Sepez 2013/07/23 18:08:25 What happens if a compromised renderer either lies
tyoshino (SeeGerritForStatus) 2013/07/24 12:14:20 Filed a bug and CC-ed you.
550 if (!buffer_size) { 603 if (!buffer_size) {
551 BadMessageReceived(); 604 BadMessageReceived();
552 return; 605 return;
553 } 606 }
554 #if defined(OS_WIN) 607 #if defined(OS_WIN)
555 base::SharedMemory shared_memory(handle, true, PeerHandle()); 608 base::SharedMemory shared_memory(handle, true, PeerHandle());
556 #else 609 #else
557 base::SharedMemory shared_memory(handle, true); 610 base::SharedMemory shared_memory(handle, true);
558 #endif 611 #endif
559 if (!shared_memory.Map(buffer_size)) { 612 if (!shared_memory.Map(buffer_size)) {
560 OnRemoveBlob(url); 613 OnRemoveBlobOrStream(url);
561 return; 614 return;
562 } 615 }
563 616
617 scoped_refptr<Stream> stream(GetStreamForURL(url));
618 if (stream.get()) {
619 stream->AddData(static_cast<char*>(shared_memory.memory()), buffer_size);
620 return;
621 }
622
564 BlobData::Item item; 623 BlobData::Item item;
565 item.SetToSharedBytes(static_cast<char*>(shared_memory.memory()), 624 item.SetToSharedBytes(static_cast<char*>(shared_memory.memory()),
566 buffer_size); 625 buffer_size);
567 blob_storage_context_->controller()->AppendBlobDataItem(url, item); 626 blob_storage_context_->controller()->AppendBlobDataItem(url, item);
568 } 627 }
569 628
570 void FileAPIMessageFilter::OnFinishBuildingBlob( 629 void FileAPIMessageFilter::OnFinishBuildingBlob(
571 const GURL& url, const std::string& content_type) { 630 const GURL& url, const std::string& content_type) {
572 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 631 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
573 blob_storage_context_->controller()->FinishBuildingBlob(url, content_type); 632 blob_storage_context_->controller()->FinishBuildingBlob(url, content_type);
574 } 633 }
575 634
576 void FileAPIMessageFilter::OnCloneBlob( 635 void FileAPIMessageFilter::OnFinishBuildingStream(const GURL& url) {
636 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
637 scoped_refptr<Stream> stream(GetStreamForURL(url));
638 if (stream.get())
639 stream->Finalize();
kinuko 2013/07/23 13:57:23 Should we DCHECK(stream.get()) here?
tyoshino (SeeGerritForStatus) 2013/07/24 12:14:20 Added NOTREACHED
640 }
641
642 void FileAPIMessageFilter::OnCloneBlobOrStream(
577 const GURL& url, const GURL& src_url) { 643 const GURL& url, const GURL& src_url) {
578 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 644 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
579 blob_storage_context_->controller()->CloneBlob(url, src_url); 645 if (GetStreamForURL(src_url))
646 stream_context_->registry()->CloneStream(url, src_url);
647 else
648 blob_storage_context_->controller()->CloneBlob(url, src_url);
580 blob_urls_.insert(url.spec()); 649 blob_urls_.insert(url.spec());
581 } 650 }
582 651
583 void FileAPIMessageFilter::OnRemoveBlob(const GURL& url) { 652 void FileAPIMessageFilter::OnRemoveBlobOrStream(const GURL& url) {
584 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 653 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
585 blob_storage_context_->controller()->RemoveBlob(url); 654 if (GetStreamForURL(url).get())
655 stream_context_->registry()->UnregisterStream(url);
656 else
657 blob_storage_context_->controller()->RemoveBlob(url);
586 blob_urls_.erase(url.spec()); 658 blob_urls_.erase(url.spec());
587 } 659 }
588 660
589 void FileAPIMessageFilter::DidFinish(int request_id, 661 void FileAPIMessageFilter::DidFinish(int request_id,
590 base::PlatformFileError result) { 662 base::PlatformFileError result) {
591 if (result == base::PLATFORM_FILE_OK) 663 if (result == base::PLATFORM_FILE_OK)
592 Send(new FileSystemMsg_DidSucceed(request_id)); 664 Send(new FileSystemMsg_DidSucceed(request_id));
593 else 665 else
594 Send(new FileSystemMsg_DidFail(request_id, result)); 666 Send(new FileSystemMsg_DidFail(request_id, result));
595 operations_.erase(request_id); 667 operations_.erase(request_id);
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
736 const FileSystemURL& url, int permissions, base::PlatformFileError* error) { 808 const FileSystemURL& url, int permissions, base::PlatformFileError* error) {
737 return CheckFileSystemPermissionsForProcess(context_, process_id_, url, 809 return CheckFileSystemPermissionsForProcess(context_, process_id_, url,
738 permissions, error); 810 permissions, error);
739 } 811 }
740 812
741 fileapi::FileSystemOperationRunner* FileAPIMessageFilter::operation_runner() { 813 fileapi::FileSystemOperationRunner* FileAPIMessageFilter::operation_runner() {
742 return context_->operation_runner(); 814 return context_->operation_runner();
743 } 815 }
744 816
745 } // namespace content 817 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698