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