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

Side by Side Diff: third_party/WebKit/Source/web/AssociatedURLLoader.cpp

Issue 2399463007: AssociatedURLLoader shouldn't derive from WebURLLoader (Closed)
Patch Set: Rebase Created 4 years, 2 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 /*
2 * Copyright (C) 2010, 2011, 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "web/AssociatedURLLoader.h"
32
33 #include "core/dom/ContextLifecycleObserver.h"
34 #include "core/fetch/CrossOriginAccessControl.h"
35 #include "core/fetch/FetchUtils.h"
36 #include "core/loader/DocumentThreadableLoader.h"
37 #include "core/loader/DocumentThreadableLoaderClient.h"
38 #include "platform/Timer.h"
39 #include "platform/exported/WrappedResourceRequest.h"
40 #include "platform/exported/WrappedResourceResponse.h"
41 #include "platform/network/HTTPParsers.h"
42 #include "platform/network/ResourceError.h"
43 #include "public/platform/WebHTTPHeaderVisitor.h"
44 #include "public/platform/WebString.h"
45 #include "public/platform/WebURLError.h"
46 #include "public/platform/WebURLLoaderClient.h"
47 #include "public/platform/WebURLRequest.h"
48 #include "public/web/WebDataSource.h"
49 #include "web/WebLocalFrameImpl.h"
50 #include "wtf/HashSet.h"
51 #include "wtf/PtrUtil.h"
52 #include "wtf/text/WTFString.h"
53 #include <limits.h>
54 #include <memory>
55
56 namespace blink {
57
58 namespace {
59
60 class HTTPRequestHeaderValidator : public WebHTTPHeaderVisitor {
61 WTF_MAKE_NONCOPYABLE(HTTPRequestHeaderValidator);
62
63 public:
64 HTTPRequestHeaderValidator() : m_isSafe(true) {}
65 ~HTTPRequestHeaderValidator() override {}
66
67 void visitHeader(const WebString& name, const WebString& value) override;
68 bool isSafe() const { return m_isSafe; }
69
70 private:
71 bool m_isSafe;
72 };
73
74 void HTTPRequestHeaderValidator::visitHeader(const WebString& name,
75 const WebString& value) {
76 m_isSafe = m_isSafe && isValidHTTPToken(name) &&
77 !FetchUtils::isForbiddenHeaderName(name) &&
78 isValidHTTPHeaderValue(value);
79 }
80
81 } // namespace
82
83 // This class bridges the interface differences between WebCore and WebKit
84 // loader clients.
85 // It forwards its ThreadableLoaderClient notifications to a WebURLLoaderClient.
86 class AssociatedURLLoader::ClientAdapter final
87 : public DocumentThreadableLoaderClient {
88 WTF_MAKE_NONCOPYABLE(ClientAdapter);
89
90 public:
91 static std::unique_ptr<ClientAdapter> create(AssociatedURLLoader*,
92 WebURLLoaderClient*,
93 const WebURLLoaderOptions&);
94
95 // ThreadableLoaderClient
96 void didSendData(unsigned long long /*bytesSent*/,
97 unsigned long long /*totalBytesToBeSent*/) override;
98 void didReceiveResponse(unsigned long,
99 const ResourceResponse&,
100 std::unique_ptr<WebDataConsumerHandle>) override;
101 void didDownloadData(int /*dataLength*/) override;
102 void didReceiveData(const char*, unsigned /*dataLength*/) override;
103 void didReceiveCachedMetadata(const char*, int /*dataLength*/) override;
104 void didFinishLoading(unsigned long /*identifier*/,
105 double /*finishTime*/) override;
106 void didFail(const ResourceError&) override;
107 void didFailRedirectCheck() override;
108
109 // DocumentThreadableLoaderClient
110 bool willFollowRedirect(
111 const ResourceRequest& /*newRequest*/,
112 const ResourceResponse& /*redirectResponse*/) override;
113
114 // Sets an error to be reported back to the client, asychronously.
115 void setDelayedError(const ResourceError&);
116
117 // Enables forwarding of error notifications to the WebURLLoaderClient. These
118 // must be deferred until after the call to
119 // AssociatedURLLoader::loadAsynchronously() completes.
120 void enableErrorNotifications();
121
122 // Stops loading and releases the DocumentThreadableLoader as early as
123 // possible.
124 WebURLLoaderClient* releaseClient() {
125 WebURLLoaderClient* client = m_client;
126 m_client = nullptr;
127 return client;
128 }
129
130 private:
131 ClientAdapter(AssociatedURLLoader*,
132 WebURLLoaderClient*,
133 const WebURLLoaderOptions&);
134
135 void notifyError(TimerBase*);
136
137 AssociatedURLLoader* m_loader;
138 WebURLLoaderClient* m_client;
139 WebURLLoaderOptions m_options;
140 WebURLError m_error;
141
142 Timer<ClientAdapter> m_errorTimer;
143 bool m_enableErrorNotifications;
144 bool m_didFail;
145 };
146
147 std::unique_ptr<AssociatedURLLoader::ClientAdapter>
148 AssociatedURLLoader::ClientAdapter::create(AssociatedURLLoader* loader,
149 WebURLLoaderClient* client,
150 const WebURLLoaderOptions& options) {
151 return wrapUnique(new ClientAdapter(loader, client, options));
152 }
153
154 AssociatedURLLoader::ClientAdapter::ClientAdapter(
155 AssociatedURLLoader* loader,
156 WebURLLoaderClient* client,
157 const WebURLLoaderOptions& options)
158 : m_loader(loader),
159 m_client(client),
160 m_options(options),
161 m_errorTimer(this, &ClientAdapter::notifyError),
162 m_enableErrorNotifications(false),
163 m_didFail(false) {
164 DCHECK(m_loader);
165 DCHECK(m_client);
166 }
167
168 bool AssociatedURLLoader::ClientAdapter::willFollowRedirect(
169 const ResourceRequest& newRequest,
170 const ResourceResponse& redirectResponse) {
171 if (!m_client)
172 return true;
173
174 WrappedResourceRequest wrappedNewRequest(newRequest);
175 WrappedResourceResponse wrappedRedirectResponse(redirectResponse);
176 return m_client->willFollowRedirect(m_loader, wrappedNewRequest,
177 wrappedRedirectResponse);
178 }
179
180 void AssociatedURLLoader::ClientAdapter::didSendData(
181 unsigned long long bytesSent,
182 unsigned long long totalBytesToBeSent) {
183 if (!m_client)
184 return;
185
186 m_client->didSendData(m_loader, bytesSent, totalBytesToBeSent);
187 }
188
189 void AssociatedURLLoader::ClientAdapter::didReceiveResponse(
190 unsigned long,
191 const ResourceResponse& response,
192 std::unique_ptr<WebDataConsumerHandle> handle) {
193 DCHECK(!handle);
194 if (!m_client)
195 return;
196
197 if (m_options.exposeAllResponseHeaders ||
198 m_options.crossOriginRequestPolicy !=
199 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl) {
200 // Use the original ResourceResponse.
201 m_client->didReceiveResponse(m_loader, WrappedResourceResponse(response));
202 return;
203 }
204
205 HTTPHeaderSet exposedHeaders;
206 extractCorsExposedHeaderNamesList(response, exposedHeaders);
207 HTTPHeaderSet blockedHeaders;
208 for (const auto& header : response.httpHeaderFields()) {
209 if (FetchUtils::isForbiddenResponseHeaderName(header.key) ||
210 (!isOnAccessControlResponseHeaderWhitelist(header.key) &&
211 !exposedHeaders.contains(header.key)))
212 blockedHeaders.add(header.key);
213 }
214
215 if (blockedHeaders.isEmpty()) {
216 // Use the original ResourceResponse.
217 m_client->didReceiveResponse(m_loader, WrappedResourceResponse(response));
218 return;
219 }
220
221 // If there are blocked headers, copy the response so we can remove them.
222 WebURLResponse validatedResponse = WrappedResourceResponse(response);
223 for (const auto& header : blockedHeaders)
224 validatedResponse.clearHTTPHeaderField(header);
225 m_client->didReceiveResponse(m_loader, validatedResponse);
226 }
227
228 void AssociatedURLLoader::ClientAdapter::didDownloadData(int dataLength) {
229 if (!m_client)
230 return;
231
232 m_client->didDownloadData(m_loader, dataLength, -1);
233 }
234
235 void AssociatedURLLoader::ClientAdapter::didReceiveData(const char* data,
236 unsigned dataLength) {
237 if (!m_client)
238 return;
239
240 CHECK_LE(dataLength, static_cast<unsigned>(std::numeric_limits<int>::max()));
241
242 m_client->didReceiveData(m_loader, data, dataLength, -1, dataLength);
243 }
244
245 void AssociatedURLLoader::ClientAdapter::didReceiveCachedMetadata(
246 const char* data,
247 int dataLength) {
248 if (!m_client)
249 return;
250
251 m_client->didReceiveCachedMetadata(m_loader, data, dataLength);
252 }
253
254 void AssociatedURLLoader::ClientAdapter::didFinishLoading(
255 unsigned long identifier,
256 double finishTime) {
257 if (!m_client)
258 return;
259
260 m_loader->clientAdapterDone();
261
262 releaseClient()->didFinishLoading(
263 m_loader, finishTime, WebURLLoaderClient::kUnknownEncodedDataLength);
264 // |this| may be dead here.
265 }
266
267 void AssociatedURLLoader::ClientAdapter::didFail(const ResourceError& error) {
268 if (!m_client)
269 return;
270
271 m_loader->clientAdapterDone();
272
273 m_didFail = true;
274 m_error = WebURLError(error);
275 if (m_enableErrorNotifications)
276 notifyError(&m_errorTimer);
277 }
278
279 void AssociatedURLLoader::ClientAdapter::didFailRedirectCheck() {
280 didFail(ResourceError());
281 }
282
283 void AssociatedURLLoader::ClientAdapter::enableErrorNotifications() {
284 m_enableErrorNotifications = true;
285 // If an error has already been received, start a timer to report it to the
286 // client after AssociatedURLLoader::loadAsynchronously has returned to the
287 // caller.
288 if (m_didFail)
289 m_errorTimer.startOneShot(0, BLINK_FROM_HERE);
290 }
291
292 void AssociatedURLLoader::ClientAdapter::notifyError(TimerBase* timer) {
293 DCHECK_EQ(timer, &m_errorTimer);
294
295 if (m_client)
296 releaseClient()->didFail(m_loader, m_error);
297 // |this| may be dead here.
298 }
299
300 class AssociatedURLLoader::Observer final : public GarbageCollected<Observer>,
301 public ContextLifecycleObserver {
302 USING_GARBAGE_COLLECTED_MIXIN(Observer);
303
304 public:
305 Observer(AssociatedURLLoader* parent, Document* document)
306 : ContextLifecycleObserver(document), m_parent(parent) {}
307
308 void dispose() {
309 m_parent = nullptr;
310 clearContext();
311 }
312
313 void contextDestroyed() override {
314 if (m_parent)
315 m_parent->documentDestroyed();
316 }
317
318 DEFINE_INLINE_VIRTUAL_TRACE() { ContextLifecycleObserver::trace(visitor); }
319
320 AssociatedURLLoader* m_parent;
321 };
322
323 AssociatedURLLoader::AssociatedURLLoader(WebLocalFrameImpl* frameImpl,
324 const WebURLLoaderOptions& options)
325 : m_client(nullptr),
326 m_options(options),
327 m_observer(new Observer(this, frameImpl->frame()->document())) {}
328
329 AssociatedURLLoader::~AssociatedURLLoader() {
330 cancel();
331 }
332
333 #define STATIC_ASSERT_ENUM(a, b) \
334 static_assert(static_cast<int>(a) == static_cast<int>(b), \
335 "mismatching enum: " #a)
336
337 STATIC_ASSERT_ENUM(WebURLLoaderOptions::CrossOriginRequestPolicyDeny,
338 DenyCrossOriginRequests);
339 STATIC_ASSERT_ENUM(
340 WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl,
341 UseAccessControl);
342 STATIC_ASSERT_ENUM(WebURLLoaderOptions::CrossOriginRequestPolicyAllow,
343 AllowCrossOriginRequests);
344
345 STATIC_ASSERT_ENUM(WebURLLoaderOptions::ConsiderPreflight, ConsiderPreflight);
346 STATIC_ASSERT_ENUM(WebURLLoaderOptions::ForcePreflight, ForcePreflight);
347 STATIC_ASSERT_ENUM(WebURLLoaderOptions::PreventPreflight, PreventPreflight);
348
349 void AssociatedURLLoader::loadSynchronously(const WebURLRequest& request,
350 WebURLResponse& response,
351 WebURLError& error,
352 WebData& data,
353 int64_t& encodedDataLength) {
354 DCHECK(0); // Synchronous loading is not supported.
355 }
356
357 void AssociatedURLLoader::loadAsynchronously(const WebURLRequest& request,
358 WebURLLoaderClient* client) {
359 DCHECK(!m_client);
360 DCHECK(!m_loader);
361 DCHECK(!m_clientAdapter);
362
363 DCHECK(client);
364
365 bool allowLoad = true;
366 WebURLRequest newRequest(request);
367 if (m_options.untrustedHTTP) {
368 WebString method = newRequest.httpMethod();
369 allowLoad = m_observer && isValidHTTPToken(method) &&
370 FetchUtils::isUsefulMethod(method);
371 if (allowLoad) {
372 newRequest.setHTTPMethod(FetchUtils::normalizeMethod(method));
373 HTTPRequestHeaderValidator validator;
374 newRequest.visitHTTPHeaderFields(&validator);
375 allowLoad = validator.isSafe();
376 }
377 }
378
379 m_client = client;
380 m_clientAdapter = ClientAdapter::create(this, client, m_options);
381
382 if (allowLoad) {
383 ThreadableLoaderOptions options;
384 options.preflightPolicy =
385 static_cast<PreflightPolicy>(m_options.preflightPolicy);
386 options.crossOriginRequestPolicy = static_cast<CrossOriginRequestPolicy>(
387 m_options.crossOriginRequestPolicy);
388
389 ResourceLoaderOptions resourceLoaderOptions;
390 resourceLoaderOptions.allowCredentials = m_options.allowCredentials
391 ? AllowStoredCredentials
392 : DoNotAllowStoredCredentials;
393 resourceLoaderOptions.dataBufferingPolicy = DoNotBufferData;
394
395 const ResourceRequest& webcoreRequest = newRequest.toResourceRequest();
396 if (webcoreRequest.requestContext() ==
397 WebURLRequest::RequestContextUnspecified) {
398 // FIXME: We load URLs without setting a TargetType (and therefore a
399 // request context) in several places in content/
400 // (P2PPortAllocatorSession::AllocateLegacyRelaySession, for example).
401 // Remove this once those places are patched up.
402 newRequest.setRequestContext(WebURLRequest::RequestContextInternal);
403 }
404
405 Document* document = toDocument(m_observer->lifecycleContext());
406 DCHECK(document);
407 m_loader = DocumentThreadableLoader::create(
408 *document, m_clientAdapter.get(), options, resourceLoaderOptions);
409 m_loader->start(webcoreRequest);
410 }
411
412 if (!m_loader) {
413 // FIXME: return meaningful error codes.
414 m_clientAdapter->didFail(ResourceError());
415 }
416 m_clientAdapter->enableErrorNotifications();
417 }
418
419 void AssociatedURLLoader::cancel() {
420 disposeObserver();
421 cancelLoader();
422 releaseClient();
423 }
424
425 void AssociatedURLLoader::clientAdapterDone() {
426 disposeObserver();
427 releaseClient();
428 }
429
430 void AssociatedURLLoader::cancelLoader() {
431 if (!m_clientAdapter)
432 return;
433
434 // Prevent invocation of the WebURLLoaderClient methods.
435 m_clientAdapter->releaseClient();
436
437 if (m_loader) {
438 m_loader->cancel();
439 m_loader = nullptr;
440 }
441 m_clientAdapter.reset();
442 }
443
444 void AssociatedURLLoader::setDefersLoading(bool defersLoading) {
445 if (m_loader)
446 m_loader->setDefersLoading(defersLoading);
447 }
448
449 void AssociatedURLLoader::setLoadingTaskRunner(blink::WebTaskRunner*) {
450 // TODO(alexclarke): Maybe support this one day if it proves worthwhile.
451 }
452
453 void AssociatedURLLoader::documentDestroyed() {
454 disposeObserver();
455 cancelLoader();
456
457 if (!m_client)
458 return;
459
460 releaseClient()->didFail(this, ResourceError());
461 // |this| may be dead here.
462 }
463
464 void AssociatedURLLoader::disposeObserver() {
465 if (!m_observer)
466 return;
467
468 // TODO(tyoshino): Remove this assert once Document is fixed so that
469 // contextDestroyed() is invoked for all kinds of Documents.
470 //
471 // Currently, the method of detecting Document destruction implemented here
472 // doesn't work for all kinds of Documents. In case we reached here after
473 // the Oilpan is destroyed, we just crash the renderer process to prevent
474 // UaF.
475 //
476 // We could consider just skipping the rest of code in case
477 // ThreadState::current() is null. However, the fact we reached here
478 // without cancelling the loader means that it's possible there're some
479 // non-Blink non-on-heap objects still facing on-heap Blink objects. E.g.
480 // there could be a WebURLLoader instance behind the
481 // DocumentThreadableLoader instance. So, for safety, we chose to just
482 // crash here.
483 RELEASE_ASSERT(ThreadState::current());
484
485 m_observer->dispose();
486 m_observer = nullptr;
487 }
488
489 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/web/AssociatedURLLoader.h ('k') | third_party/WebKit/Source/web/AssociatedURLLoaderTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698