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

Side by Side Diff: content/browser/webui/web_ui_url_loader_factory.cc

Issue 2867083002: Move the new WebUI's code to read the data off the IO thread as it's generally from the memory-mapp… (Closed)
Patch Set: Created 3 years, 7 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
« no previous file with comments | « content/browser/webui/url_data_manager_backend.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 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/web_ui_url_loader_factory.h" 5 #include "content/browser/webui/web_ui_url_loader_factory.h"
6 6
7 #include <map> 7 #include <map>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/lazy_instance.h" 10 #include "base/lazy_instance.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/memory/ref_counted_memory.h" 12 #include "base/memory/ref_counted_memory.h"
13 #include "base/strings/string_piece.h" 13 #include "base/strings/string_piece.h"
14 #include "base/sys_byteorder.h"
15 #include "content/browser/frame_host/frame_tree_node.h" 14 #include "content/browser/frame_host/frame_tree_node.h"
16 #include "content/browser/frame_host/render_frame_host_impl.h" 15 #include "content/browser/frame_host/render_frame_host_impl.h"
17 #include "content/browser/resource_context_impl.h" 16 #include "content/browser/resource_context_impl.h"
18 #include "content/browser/webui/url_data_manager_backend.h" 17 #include "content/browser/webui/url_data_manager_backend.h"
19 #include "content/browser/webui/url_data_source_impl.h" 18 #include "content/browser/webui/url_data_source_impl.h"
20 #include "content/public/browser/browser_context.h" 19 #include "content/public/browser/browser_context.h"
21 #include "content/public/browser/browser_thread.h" 20 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/render_process_host.h" 21 #include "content/public/browser/render_process_host.h"
23 #include "content/public/browser/web_contents.h" 22 #include "content/public/browser/web_contents.h"
24 #include "mojo/public/cpp/bindings/binding_set.h" 23 #include "mojo/public/cpp/bindings/binding_set.h"
25 #include "third_party/zlib/google/compression_utils.h" 24 #include "third_party/zlib/google/compression_utils.h"
26 #include "ui/base/template_expressions.h" 25 #include "ui/base/template_expressions.h"
27 26
28 namespace content { 27 namespace content {
29 28
30 namespace { 29 namespace {
31 30
32 class WebUIURLLoaderFactory; 31 class WebUIURLLoaderFactory;
33 base::LazyInstance<std::map<int, std::unique_ptr<WebUIURLLoaderFactory>>>::Leaky 32 base::LazyInstance<std::map<int, std::unique_ptr<WebUIURLLoaderFactory>>>::Leaky
34 g_factories = LAZY_INSTANCE_INITIALIZER; 33 g_factories = LAZY_INSTANCE_INITIALIZER;
35 34
36 class URLLoaderImpl : public mojom::URLLoader { 35 void CallOnError(mojo::InterfacePtrInfo<mojom::URLLoaderClient> client_info,
37 public: 36 int error_code) {
38 static void Create(mojom::URLLoaderAssociatedRequest loader, 37 mojom::URLLoaderClientPtr client;
39 const ResourceRequest& request, 38 client.Bind(std::move(client_info));
40 int frame_tree_node_id, 39
41 mojo::InterfacePtrInfo<mojom::URLLoaderClient> client_info, 40 ResourceRequestCompletionStatus status;
42 ResourceContext* resource_context) { 41 status.error_code = error_code;
43 mojom::URLLoaderClientPtr client; 42 client->OnComplete(status);
44 client.Bind(std::move(client_info)); 43 }
45 new URLLoaderImpl(std::move(loader), request, frame_tree_node_id, 44
46 std::move(client), resource_context); 45 void ReadData(scoped_refptr<ResourceResponse> headers,
46 const ui::TemplateReplacements* replacements,
47 bool gzipped,
48 scoped_refptr<URLDataSourceImpl> data_source,
49 mojo::InterfacePtrInfo<mojom::URLLoaderClient> client_info,
yzshen1 2017/05/08 18:07:04 fyi: you could use the alias mojom::URLLoaderClien
jam 2017/05/08 18:18:53 Done.
50 scoped_refptr<base::RefCountedMemory> bytes) {
51 if (!bytes) {
52 CallOnError(std::move(client_info), net::ERR_FAILED);
53 return;
47 } 54 }
48 55
49 private: 56 mojom::URLLoaderClientPtr client;
50 URLLoaderImpl(mojom::URLLoaderAssociatedRequest loader, 57 client.Bind(std::move(client_info));
51 const ResourceRequest& request, 58 client->OnReceiveResponse(headers->head, base::nullopt, nullptr);
52 int frame_tree_node_id, 59
53 mojom::URLLoaderClientPtr client, 60 base::StringPiece input(reinterpret_cast<const char*>(bytes->front()),
54 ResourceContext* resource_context) 61 bytes->size());
55 : binding_(this, std::move(loader)), 62
56 client_(std::move(client)), 63 if (replacements) {
57 replacements_(nullptr), 64 std::string temp_string;
58 weak_factory_(this) { 65 // We won't know the the final output size ahead of time, so we have to
59 // NOTE: this duplicates code in URLDataManagerBackend::StartRequest. 66 // use an intermediate string.
60 if (!URLDataManagerBackend::CheckURLIsValid(request.url)) { 67 base::StringPiece source;
61 OnError(net::ERR_INVALID_URL); 68 std::string temp_str;
62 return; 69 if (gzipped) {
70 temp_str.resize(compression::GetUncompressedSize(input));
71 source.set(temp_str.c_str(), temp_str.size());
72 CHECK(compression::GzipUncompress(input, source));
73 gzipped = false;
74 } else {
75 source = input;
63 } 76 }
64 77 temp_str = ui::ReplaceTemplateExpressions(source, *replacements);
65 URLDataSourceImpl* source = 78 bytes = base::RefCountedString::TakeString(&temp_str);
66 GetURLDataManagerForResourceContext(resource_context) 79 input.set(reinterpret_cast<const char*>(bytes->front()), bytes->size());
67 ->GetDataSourceFromURL(request.url);
68 if (!source) {
69 OnError(net::ERR_INVALID_URL);
70 return;
71 }
72
73 if (!source->source()->ShouldServiceRequest(request.url, resource_context,
74 -1)) {
75 OnError(net::ERR_INVALID_URL);
76 return;
77 }
78
79 std::string path;
80 URLDataManagerBackend::URLToRequestPath(request.url, &path);
81 gzipped_ = source->source()->IsGzipped(path);
82 if (source->source()->GetMimeType(path) == "text/html")
83 replacements_ = source->GetReplacements();
84
85 net::HttpRequestHeaders request_headers;
86 request_headers.AddHeadersFromString(request.headers);
87 std::string origin_header;
88 request_headers.GetHeader(net::HttpRequestHeaders::kOrigin, &origin_header);
89
90 scoped_refptr<net::HttpResponseHeaders> headers =
91 URLDataManagerBackend::GetHeaders(source, path, origin_header);
92
93 ResourceResponseHead head;
94 head.headers = headers;
95 head.mime_type = source->source()->GetMimeType(path);
96 // TODO: fill all the time related field i.e. request_time response_time
97 // request_start response_start
98 client_->OnReceiveResponse(head, base::nullopt, nullptr);
99
100 ResourceRequestInfo::WebContentsGetter wc_getter =
101 base::Bind(WebContents::FromFrameTreeNodeId, frame_tree_node_id);
102
103 // Forward along the request to the data source.
104 // TODO(jam): once we only have this code path for WebUI, and not the
105 // URLLRequestJob one, then we should switch data sources to run on the UI
106 // thread by default.
107 scoped_refptr<base::SingleThreadTaskRunner> target_runner =
108 source->source()->TaskRunnerForRequestPath(path);
109 if (!target_runner) {
110 source->source()->StartDataRequest(
111 path, wc_getter,
112 base::Bind(&URLLoaderImpl::DataAvailable,
113 weak_factory_.GetWeakPtr()));
114 } else {
115 // The DataSource wants StartDataRequest to be called on a specific
116 // thread, usually the UI thread, for this path.
117 target_runner->PostTask(
118 FROM_HERE, base::Bind(&URLLoaderImpl::CallStartDataRequest,
119 base::RetainedRef(source), path, wc_getter,
120 weak_factory_.GetWeakPtr()));
121 }
122 } 80 }
123 81
124 void FollowRedirect() override { NOTREACHED(); } 82 uint32_t output_size =
125 void SetPriority(net::RequestPriority priority, 83 gzipped ? compression::GetUncompressedSize(input) : bytes->size();
126 int32_t intra_priority_value) override { 84
127 NOTREACHED(); 85 MojoCreateDataPipeOptions options;
86 options.struct_size = sizeof(MojoCreateDataPipeOptions);
87 options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
88 options.element_num_bytes = 1;
89 options.capacity_num_bytes = output_size;
90 mojo::DataPipe data_pipe(options);
91
92 DCHECK(data_pipe.producer_handle.is_valid());
93 DCHECK(data_pipe.consumer_handle.is_valid());
94
95 void* buffer = nullptr;
96 uint32_t num_bytes = output_size;
97 MojoResult result =
98 BeginWriteDataRaw(data_pipe.producer_handle.get(), &buffer, &num_bytes,
99 MOJO_WRITE_DATA_FLAG_NONE);
100 CHECK_EQ(result, MOJO_RESULT_OK);
101 CHECK_EQ(num_bytes, output_size);
102
103 if (gzipped) {
104 base::StringPiece output(static_cast<char*>(buffer), num_bytes);
105 CHECK(compression::GzipUncompress(input, output));
106 } else {
107 memcpy(buffer, bytes->front(), output_size);
108 }
109 result = EndWriteDataRaw(data_pipe.producer_handle.get(), num_bytes);
110 CHECK_EQ(result, MOJO_RESULT_OK);
111
112 client->OnStartLoadingResponseBody(std::move(data_pipe.consumer_handle));
113
114 ResourceRequestCompletionStatus request_complete_data;
115 request_complete_data.error_code = net::OK;
116 request_complete_data.exists_in_cache = false;
117 request_complete_data.completion_time = base::TimeTicks::Now();
118 request_complete_data.encoded_data_length = output_size;
119 request_complete_data.encoded_body_length = output_size;
120 client->OnComplete(request_complete_data);
121 }
122
123 void DataAvailable(scoped_refptr<ResourceResponse> headers,
124 const ui::TemplateReplacements* replacements,
125 bool gzipped,
126 scoped_refptr<URLDataSourceImpl> source,
127 mojo::InterfacePtrInfo<mojom::URLLoaderClient> client_info,
128 scoped_refptr<base::RefCountedMemory> bytes) {
129 // Since the bytes are from the memory mapped resource file, copying the
130 // data can lead to disk access.
131 BrowserThread::PostTask(
132 BrowserThread::FILE_USER_BLOCKING, FROM_HERE,
133 base::BindOnce(ReadData, headers, replacements, gzipped, source,
134 std::move(client_info), bytes));
135 }
136
137 void StartURLLoader(const ResourceRequest& request,
138 int frame_tree_node_id,
139 mojo::InterfacePtrInfo<mojom::URLLoaderClient> client_info,
140 ResourceContext* resource_context) {
141 // NOTE: this duplicates code in URLDataManagerBackend::StartRequest.
142 if (!URLDataManagerBackend::CheckURLIsValid(request.url)) {
143 CallOnError(std::move(client_info), net::ERR_INVALID_URL);
144 return;
128 } 145 }
129 146
130 static void CallStartDataRequest( 147 URLDataSourceImpl* source =
131 scoped_refptr<URLDataSourceImpl> source, 148 GetURLDataManagerForResourceContext(resource_context)
132 const std::string& path, 149 ->GetDataSourceFromURL(request.url);
133 const ResourceRequestInfo::WebContentsGetter& wc_getter, 150 if (!source) {
134 const base::WeakPtr<URLLoaderImpl>& weak_ptr) { 151 CallOnError(std::move(client_info), net::ERR_INVALID_URL);
135 source->source()->StartDataRequest( 152 return;
136 path, wc_getter,
137 base::Bind(URLLoaderImpl::DataAvailableOnTargetThread, weak_ptr));
138 } 153 }
139 154
140 static void DataAvailableOnTargetThread( 155 if (!source->source()->ShouldServiceRequest(request.url, resource_context,
141 const base::WeakPtr<URLLoaderImpl>& weak_ptr, 156 -1)) {
142 scoped_refptr<base::RefCountedMemory> bytes) { 157 CallOnError(std::move(client_info), net::ERR_INVALID_URL);
143 BrowserThread::PostTask( 158 return;
144 BrowserThread::IO, FROM_HERE,
145 base::Bind(&URLLoaderImpl::DataAvailable, weak_ptr, bytes));
146 } 159 }
147 160
148 void DataAvailable(scoped_refptr<base::RefCountedMemory> bytes) { 161 std::string path;
149 if (!bytes) { 162 URLDataManagerBackend::URLToRequestPath(request.url, &path);
150 OnError(net::ERR_FAILED);
151 return;
152 }
153 163
154 base::StringPiece input(reinterpret_cast<const char*>(bytes->front()), 164 net::HttpRequestHeaders request_headers;
155 bytes->size()); 165 request_headers.AddHeadersFromString(request.headers);
156 if (replacements_) { 166 std::string origin_header;
157 std::string temp_string; 167 request_headers.GetHeader(net::HttpRequestHeaders::kOrigin, &origin_header);
158 // We won't know the the final output size ahead of time, so we have to
159 // use an intermediate string.
160 base::StringPiece source;
161 std::string temp_str;
162 if (gzipped_) {
163 temp_str.resize(compression::GetUncompressedSize(input));
164 source.set(temp_str.c_str(), temp_str.size());
165 CHECK(compression::GzipUncompress(input, source));
166 gzipped_ = false;
167 } else {
168 source = input;
169 }
170 temp_str = ui::ReplaceTemplateExpressions(source, *replacements_);
171 bytes = base::RefCountedString::TakeString(&temp_str);
172 input.set(reinterpret_cast<const char*>(bytes->front()), bytes->size());
173 }
174 168
175 uint32_t output_size = 169 scoped_refptr<net::HttpResponseHeaders> headers =
176 gzipped_ ? compression::GetUncompressedSize(input) : bytes->size(); 170 URLDataManagerBackend::GetHeaders(source, path, origin_header);
177 171
178 MojoCreateDataPipeOptions options; 172 scoped_refptr<ResourceResponse> resource_response(new ResourceResponse);
179 options.struct_size = sizeof(MojoCreateDataPipeOptions); 173 resource_response->head.headers = headers;
180 options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; 174 resource_response->head.mime_type = source->source()->GetMimeType(path);
181 options.element_num_bytes = 1; 175 // TODO: fill all the time related field i.e. request_time response_time
182 options.capacity_num_bytes = output_size; 176 // request_start response_start
183 mojo::DataPipe data_pipe(options);
184 177
185 DCHECK(data_pipe.producer_handle.is_valid()); 178 ResourceRequestInfo::WebContentsGetter wc_getter =
186 DCHECK(data_pipe.consumer_handle.is_valid()); 179 base::Bind(WebContents::FromFrameTreeNodeId, frame_tree_node_id);
187 180
188 void* buffer = nullptr; 181 bool gzipped = source->source()->IsGzipped(path);
189 uint32_t num_bytes = output_size; 182 const ui::TemplateReplacements* replacements = nullptr;
190 MojoResult result = 183 if (source->source()->GetMimeType(path) == "text/html")
191 BeginWriteDataRaw(data_pipe.producer_handle.get(), &buffer, &num_bytes, 184 replacements = source->GetReplacements();
192 MOJO_WRITE_DATA_FLAG_NONE); 185 // To keep the same behavior as the old WebUI code, we call the source to get
193 CHECK_EQ(result, MOJO_RESULT_OK); 186 // the value for |gzipped| and |replacements| on the IO thread. Since
194 CHECK_EQ(num_bytes, output_size); 187 // |replacements| is owned by |source| keep a reference to it in the callback.
188 auto data_available_callback =
189 base::Bind(DataAvailable, resource_response, replacements, gzipped,
190 base::RetainedRef(source), base::Passed(&client_info));
195 191
196 if (gzipped_) { 192 // TODO(jam): once we only have this code path for WebUI, and not the
197 base::StringPiece output(static_cast<char*>(buffer), num_bytes); 193 // URLLRequestJob one, then we should switch data sources to run on the UI
198 CHECK(compression::GzipUncompress(input, output)); 194 // thread by default.
199 } else { 195 scoped_refptr<base::SingleThreadTaskRunner> target_runner =
200 memcpy(buffer, bytes->front(), output_size); 196 source->source()->TaskRunnerForRequestPath(path);
201 } 197 if (!target_runner) {
202 result = EndWriteDataRaw(data_pipe.producer_handle.get(), num_bytes); 198 source->source()->StartDataRequest(path, wc_getter,
203 CHECK_EQ(result, MOJO_RESULT_OK); 199 data_available_callback);
204 200 return;
205 client_->OnStartLoadingResponseBody(std::move(data_pipe.consumer_handle));
206
207 ResourceRequestCompletionStatus request_complete_data;
208 request_complete_data.error_code = net::OK;
209 request_complete_data.exists_in_cache = false;
210 request_complete_data.completion_time = base::TimeTicks::Now();
211 request_complete_data.encoded_data_length = output_size;
212 request_complete_data.encoded_body_length = output_size;
213 client_->OnComplete(request_complete_data);
214 delete this;
215 } 201 }
216 202
217 void OnError(int error_code) { 203 // The DataSource wants StartDataRequest to be called on a specific
218 ResourceRequestCompletionStatus status; 204 // thread, usually the UI thread, for this path.
219 status.error_code = error_code; 205 target_runner->PostTask(
220 client_->OnComplete(status); 206 FROM_HERE, base::BindOnce(&URLDataSource::StartDataRequest,
221 delete this; 207 base::Unretained(source->source()), path,
222 } 208 wc_getter, data_available_callback));
223 209 }
224 mojo::AssociatedBinding<mojom::URLLoader> binding_;
225 mojom::URLLoaderClientPtr client_;
226 bool gzipped_;
227 // Replacement dictionary for i18n.
228 const ui::TemplateReplacements* replacements_;
229 base::WeakPtrFactory<URLLoaderImpl> weak_factory_;
230
231 DISALLOW_COPY_AND_ASSIGN(URLLoaderImpl);
232 };
233 210
234 class WebUIURLLoaderFactory : public mojom::URLLoaderFactory, 211 class WebUIURLLoaderFactory : public mojom::URLLoaderFactory,
235 public FrameTreeNode::Observer { 212 public FrameTreeNode::Observer {
236 public: 213 public:
237 WebUIURLLoaderFactory(FrameTreeNode* ftn) 214 WebUIURLLoaderFactory(FrameTreeNode* ftn)
238 : frame_tree_node_id_(ftn->frame_tree_node_id()), 215 : frame_tree_node_id_(ftn->frame_tree_node_id()),
239 resource_context_(ftn->current_frame_host() 216 resource_context_(ftn->current_frame_host()
240 ->GetProcess() 217 ->GetProcess()
241 ->GetBrowserContext() 218 ->GetBrowserContext()
242 ->GetResourceContext()) { 219 ->GetResourceContext()) {
243 ftn->AddObserver(this); 220 ftn->AddObserver(this);
244 } 221 }
245 222
246 ~WebUIURLLoaderFactory() override {} 223 ~WebUIURLLoaderFactory() override {}
247 224
248 mojom::URLLoaderFactoryPtr CreateBinding() { 225 mojom::URLLoaderFactoryPtr CreateBinding() {
249 return loader_factory_bindings_.CreateInterfacePtrAndBind(this); 226 return loader_factory_bindings_.CreateInterfacePtrAndBind(this);
250 } 227 }
251 228
252 // mojom::URLLoaderFactory implementation: 229 // mojom::URLLoaderFactory implementation:
253 void CreateLoaderAndStart(mojom::URLLoaderAssociatedRequest loader, 230 void CreateLoaderAndStart(mojom::URLLoaderAssociatedRequest loader,
254 int32_t routing_id, 231 int32_t routing_id,
255 int32_t request_id, 232 int32_t request_id,
256 uint32_t options, 233 uint32_t options,
257 const ResourceRequest& request, 234 const ResourceRequest& request,
258 mojom::URLLoaderClientPtr client) override { 235 mojom::URLLoaderClientPtr client) override {
259 DCHECK_CURRENTLY_ON(BrowserThread::UI); 236 DCHECK_CURRENTLY_ON(BrowserThread::UI);
260 BrowserThread::PostTask( 237 BrowserThread::PostTask(
261 BrowserThread::IO, FROM_HERE, 238 BrowserThread::IO, FROM_HERE,
262 base::BindOnce(&URLLoaderImpl::Create, std::move(loader), request, 239 base::BindOnce(&StartURLLoader, request, frame_tree_node_id_,
263 frame_tree_node_id_, client.PassInterface(), 240 client.PassInterface(), resource_context_));
264 resource_context_));
265 } 241 }
266 242
267 void SyncLoad(int32_t routing_id, 243 void SyncLoad(int32_t routing_id,
268 int32_t request_id, 244 int32_t request_id,
269 const ResourceRequest& request, 245 const ResourceRequest& request,
270 SyncLoadCallback callback) override { 246 SyncLoadCallback callback) override {
271 NOTREACHED(); 247 NOTREACHED();
272 } 248 }
273 249
274 // FrameTreeNode::Observer implementation: 250 // FrameTreeNode::Observer implementation:
(...skipping 12 matching lines...) Expand all
287 } // namespace 263 } // namespace
288 264
289 mojom::URLLoaderFactoryPtr GetWebUIURLLoader(FrameTreeNode* node) { 265 mojom::URLLoaderFactoryPtr GetWebUIURLLoader(FrameTreeNode* node) {
290 int ftn_id = node->frame_tree_node_id(); 266 int ftn_id = node->frame_tree_node_id();
291 if (g_factories.Get()[ftn_id].get() == nullptr) 267 if (g_factories.Get()[ftn_id].get() == nullptr)
292 g_factories.Get()[ftn_id] = base::MakeUnique<WebUIURLLoaderFactory>(node); 268 g_factories.Get()[ftn_id] = base::MakeUnique<WebUIURLLoaderFactory>(node);
293 return g_factories.Get()[ftn_id]->CreateBinding(); 269 return g_factories.Get()[ftn_id]->CreateBinding();
294 } 270 }
295 271
296 } // namespace content 272 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/webui/url_data_manager_backend.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698