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

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

Issue 2860903006: Handle webuis when using the network service. (Closed)
Patch Set: nits 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "content/browser/webui/web_ui_url_loader_factory.h"
6
7 #include <map>
8
9 #include "base/bind.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted_memory.h"
13 #include "base/strings/string_piece.h"
14 #include "base/sys_byteorder.h"
15 #include "content/browser/frame_host/frame_tree_node.h"
16 #include "content/browser/frame_host/render_frame_host_impl.h"
17 #include "content/browser/resource_context_impl.h"
18 #include "content/browser/web_contents/web_contents_impl.h"
19 #include "content/browser/webui/url_data_manager_backend.h"
20 #include "content/browser/webui/url_data_source_impl.h"
21 #include "content/public/browser/browser_context.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "mojo/public/cpp/bindings/binding_set.h"
25 #include "net/base/io_buffer.h"
mmenke 2017/05/05 19:34:15 Is this used?
jam 2017/05/05 22:19:39 nope, removed
26 #include "third_party/zlib/zlib.h"
27 #include "ui/base/template_expressions.h"
28
29 namespace content {
30
31 namespace {
32 class WebUIURLLoaderFactory;
33 base::LazyInstance<std::map<int, std::unique_ptr<WebUIURLLoaderFactory>>>::Leaky
34 g_factories = LAZY_INSTANCE_INITIALIZER;
35
36 WebContents* GetWebContentsFromFTNID(int frame_tree_node_id) {
scottmg 2017/05/05 19:27:42 Maybe you could move this function somewhere acces
jam 2017/05/05 22:19:40 sure, reused WebContents::FromFrameTreeNodeId
37 FrameTreeNode* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
38 return ftn ? WebContentsImpl::FromFrameTreeNode(ftn) : nullptr;
39 }
40
41 class URLLoaderImpl : public mojom::URLLoader {
42 public:
43 static void Create(mojom::URLLoaderAssociatedRequest loader,
44 const ResourceRequest& request,
45 int frame_tree_node_id,
46 mojo::InterfacePtrInfo<mojom::URLLoaderClient> client_info,
47 ResourceContext* resource_context) {
48 mojom::URLLoaderClientPtr client;
49 client.Bind(std::move(client_info));
50 new URLLoaderImpl(std::move(loader), request, frame_tree_node_id,
51 std::move(client), resource_context);
52 }
53
54 private:
55 URLLoaderImpl(mojom::URLLoaderAssociatedRequest loader,
56 const ResourceRequest& request,
57 int frame_tree_node_id,
58 mojom::URLLoaderClientPtr client,
59 ResourceContext* resource_context)
60 : binding_(this, std::move(loader)),
61 client_(std::move(client)),
62 weak_factory_(this) {
63 // NOTE: This duplicates code in URLDataManagerBackend::StartRequest
yzshen1 2017/05/05 19:54:07 In this case do we also want to add a comment in U
jam 2017/05/05 22:19:40 Done.
64 if (!URLDataManagerBackend::CheckURLIsValid(request.url)) {
65 OnError(net::ERR_INVALID_URL);
66 return;
67 }
68
69 URLDataSourceImpl* source =
70 GetURLDataManagerForResourceContext(resource_context)
71 ->GetDataSourceFromURL(request.url);
72 if (!source) {
73 OnError(net::ERR_INVALID_URL);
74 return;
75 }
76
77 if (!source->source()->ShouldServiceRequest(request.url, resource_context,
78 -1)) {
79 OnError(net::ERR_INVALID_URL);
80 return;
81 }
82
83 std::string path;
84 URLDataManagerBackend::URLToRequestPath(request.url, &path);
85 gzipped_ = source->source()->IsGzipped(path);
86 replacements_ = source->GetReplacements();
87
88 net::HttpRequestHeaders request_headers;
89 request_headers.AddHeadersFromString(request.headers);
90 std::string origin_header;
91 request_headers.GetHeader(net::HttpRequestHeaders::kOrigin, &origin_header);
92
93 scoped_refptr<net::HttpResponseHeaders> headers =
94 URLDataManagerBackend::GetHeaders(source, path, origin_header);
95
96 ResourceResponseHead head;
scottmg 2017/05/05 19:27:42 (This is more a general question, not specific to
jam 2017/05/05 22:19:40 I agree, it's not clear to me that for webui many
97 head.headers = headers;
98 head.mime_type = source->source()->GetMimeType(path);
99 // TODO: fill all the time related field i.e. request_time response_time
100 // request_start response_start
101 client_->OnReceiveResponse(head, base::nullopt,
102 mojom::DownloadedTempFilePtr());
yzshen1 2017/05/05 19:54:07 nit, fyi: you could use nullptr instead of mojom::
jam 2017/05/05 22:19:39 Done.
103
104 ResourceRequestInfo::WebContentsGetter wc_getter =
105 base::Bind(&GetWebContentsFromFTNID, frame_tree_node_id);
106
107 // Forward along the request to the data source.
108 // TODO(jam): once we only have this code path for WebUI, and not the
109 // URLLRequestJob one, then we should switch data sources to run on the UI
110 // thread by default.
111 scoped_refptr<base::SingleThreadTaskRunner> target_runner =
112 source->source()->TaskRunnerForRequestPath(path);
113 if (!target_runner) {
114 source->source()->StartDataRequest(
115 path, wc_getter,
116 base::Bind(&URLLoaderImpl::DataAvailable,
117 weak_factory_.GetWeakPtr()));
118 } else {
119 // The DataSource wants StartDataRequest to be called on a specific
120 // thread, usually the UI thread, for this path.
121 target_runner->PostTask(
122 FROM_HERE,
123 base::Bind(&URLDataSource::StartDataRequest,
124 base::Unretained(source->source()), path, wc_getter,
scottmg 2017/05/05 19:27:42 This was doing a RetainedRef on source before (I t
jam 2017/05/05 22:19:40 Thanks, I missed that in the other code path. Fixe
125 base::Bind(&URLLoaderImpl::DataAvailableOnTargetThread,
126 weak_factory_.GetWeakPtr())));
127 }
128 }
129
130 void FollowRedirect() override { NOTREACHED(); }
131 void SetPriority(net::RequestPriority priority,
132 int32_t intra_priority_value) override {
133 NOTREACHED();
134 }
135
136 static void DataAvailableOnTargetThread(
137 const base::WeakPtr<URLLoaderImpl>& weak_ptr,
138 scoped_refptr<base::RefCountedMemory> bytes) {
139 BrowserThread::PostTask(
140 BrowserThread::IO, FROM_HERE,
141 base::Bind(&URLLoaderImpl::DataAvailable, weak_ptr, bytes));
142 }
143
144 uint32_t GetUnGzippedSize(
scottmg 2017/05/05 19:27:42 Could you use third_party/zlib/google/compression_
jam 2017/05/05 22:19:40 Ah, didn't know about that (this came from the old
145 const scoped_refptr<base::RefCountedMemory>& bytes) {
146 CHECK(bytes->size() >= 4);
147 // Size of output comes from footer of gzip file format, found as the last 4
148 // bytes in the compressed file, which are stored little endian.
149 return base::ByteSwapToLE32(
150 *reinterpret_cast<const uint32_t*>(&bytes->front()[bytes->size() - 4]));
151 }
152
153 void UnGzip(const scoped_refptr<base::RefCountedMemory>& bytes,
154 void* output,
155 uint32_t output_size) {
156 z_stream inflateStream;
scottmg 2017/05/05 19:27:42 inflateStream -> inflate_stream
jam 2017/05/05 22:19:40 (removed this code per your other comment)
157 memset(&inflateStream, 0, sizeof(inflateStream));
158 inflateStream.avail_in = bytes->size();
159 inflateStream.next_in = const_cast<Bytef*>(bytes->front());
160 inflateStream.avail_out = output_size;
161 inflateStream.next_out = reinterpret_cast<Bytef*>(output);
162
163 CHECK(inflateInit2(&inflateStream, 16) == Z_OK);
scottmg 2017/05/05 19:27:43 Where'd the 16 come from? https://cs.chromium.org/
164 CHECK(inflate(&inflateStream, Z_FINISH) == Z_STREAM_END);
165 CHECK(inflateEnd(&inflateStream) == Z_OK);
166 }
167
168 void DataAvailable(scoped_refptr<base::RefCountedMemory> bytes) {
mmenke 2017/05/05 19:34:15 Is this a memory mapped file? I seem to recall it
jam 2017/05/05 22:19:40 This is what the other code does on the IO thread
mmenke 2017/05/05 22:59:27 I don't think it does. See URLRequestChromeJob::
jam 2017/05/05 23:25:07 ah, thanks I misread that code then. I'll take car
169 if (!bytes) {
170 OnError(net::ERR_FAILED);
171 return;
172 }
173
174 if (replacements_) {
scottmg 2017/05/05 19:27:42 Is it going to do this on all response types? Shou
jam 2017/05/05 22:19:39 Good point, the other code did this too. I didn't
175 std::string temp_string;
176 // We won't know the the final output size ahead of time, so we have to
177 // use an intermediate string.
178 base::StringPiece source;
179 std::string temp_str;
180 if (gzipped_) {
181 temp_str.resize(GetUnGzippedSize(bytes));
182 UnGzip(bytes, &temp_str[0], temp_str.size());
183 source.set(temp_str.c_str(), temp_str.size());
184 gzipped_ = false;
185 } else {
186 source.set(reinterpret_cast<const char*>(bytes->front()),
187 bytes->size());
188 }
189 temp_str = ui::ReplaceTemplateExpressions(source, *replacements_);
190 bytes = base::RefCountedString::TakeString(&temp_str);
191 }
192
193 uint32_t output_size = gzipped_ ? GetUnGzippedSize(bytes) : bytes->size();
194
195 MojoCreateDataPipeOptions options;
196 options.struct_size = sizeof(MojoCreateDataPipeOptions);
197 options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
198 options.element_num_bytes = 1;
199 options.capacity_num_bytes = output_size;
200 mojo::DataPipe data_pipe(options);
201
202 DCHECK(data_pipe.producer_handle.is_valid());
203 DCHECK(data_pipe.consumer_handle.is_valid());
204
205 void* buffer = nullptr;
206 uint32_t num_bytes = output_size;
207 MojoResult result =
208 BeginWriteDataRaw(data_pipe.producer_handle.get(), &buffer, &num_bytes,
mmenke 2017/05/05 19:34:15 Is this guaranteed to work? Is output_sized guara
jam 2017/05/05 22:19:39 Good question, I wondered about that as well, but
209 MOJO_WRITE_DATA_FLAG_NONE);
210 CHECK_EQ(result, MOJO_RESULT_OK);
211 CHECK_EQ(num_bytes, output_size);
212
213 if (gzipped_) {
214 UnGzip(bytes, buffer, output_size);
215 } else {
216 memcpy(buffer, bytes->front(), output_size);
217 }
218 result = EndWriteDataRaw(data_pipe.producer_handle.get(), num_bytes);
219 CHECK_EQ(result, MOJO_RESULT_OK);
220
221 client_->OnStartLoadingResponseBody(std::move(data_pipe.consumer_handle));
222
223 ResourceRequestCompletionStatus request_complete_data;
224 request_complete_data.error_code = net::OK;
225 request_complete_data.exists_in_cache = false;
226 request_complete_data.completion_time = base::TimeTicks::Now();
227 request_complete_data.encoded_data_length = output_size;
228 request_complete_data.encoded_body_length = output_size;
229 client_->OnComplete(request_complete_data);
230 delete this;
231 }
232
233 void OnError(int error_code) {
234 ResourceRequestCompletionStatus status;
235 status.error_code = error_code;
236 client_->OnComplete(status);
237 delete this;
238 }
239
240 mojo::AssociatedBinding<mojom::URLLoader> binding_;
241 mojom::URLLoaderClientPtr client_;
242 bool gzipped_;
243 // Replacement dictionary for i18n.
244 const ui::TemplateReplacements* replacements_;
245 base::WeakPtrFactory<URLLoaderImpl> weak_factory_;
246
247 DISALLOW_COPY_AND_ASSIGN(URLLoaderImpl);
248 };
249
250 class WebUIURLLoaderFactory : public mojom::URLLoaderFactory,
251 public FrameTreeNode::Observer {
252 public:
253 WebUIURLLoaderFactory(FrameTreeNode* ftn)
254 : frame_tree_node_id_(ftn->frame_tree_node_id()),
255 resource_context_(ftn->current_frame_host()
256 ->GetProcess()
257 ->GetBrowserContext()
258 ->GetResourceContext()) {
259 ftn->AddObserver(this);
260 }
261
262 ~WebUIURLLoaderFactory() override {}
263
264 mojom::URLLoaderFactoryPtr CreateBinding() {
265 return loader_factory_bindings_.CreateInterfacePtrAndBind(this);
266 }
267
268 // mojom::URLLoaderFactory implementation:
269 void CreateLoaderAndStart(mojom::URLLoaderAssociatedRequest loader,
270 int32_t routing_id,
271 int32_t request_id,
272 uint32_t options,
273 const ResourceRequest& request,
274 mojom::URLLoaderClientPtr client) override {
275 DCHECK_CURRENTLY_ON(BrowserThread::UI);
276 BrowserThread::PostTask(
277 BrowserThread::IO, FROM_HERE,
278 base::BindOnce(&URLLoaderImpl::Create, std::move(loader), request,
279 frame_tree_node_id_, client.PassInterface(),
280 resource_context_));
281 }
282
283 void SyncLoad(int32_t routing_id,
284 int32_t request_id,
285 const ResourceRequest& request,
286 SyncLoadCallback callback) override {
287 NOTREACHED();
288 }
289
290 // FrameTreeNode::Observer implementation:
291 void OnFrameTreeNodeDestroyed(FrameTreeNode* node) override {
292 g_factories.Get().erase(frame_tree_node_id_);
293 }
294
295 private:
296 int frame_tree_node_id_;
297 ResourceContext* resource_context_;
298 mojo::BindingSet<mojom::URLLoaderFactory> loader_factory_bindings_;
299
300 DISALLOW_COPY_AND_ASSIGN(WebUIURLLoaderFactory);
301 };
302
303 } // namespace
304
305 mojom::URLLoaderFactoryPtr GetWebUIURLLoader(FrameTreeNode* node) {
306 int ftn_id = node->frame_tree_node_id();
307 if (g_factories.Get()[ftn_id].get() == nullptr)
308 g_factories.Get()[ftn_id] = base::MakeUnique<WebUIURLLoaderFactory>(node);
309 return g_factories.Get()[ftn_id]->CreateBinding();
310 }
311
312 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698