Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 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 "config.h" |
| 32 | #include "AssociatedURLLoader.h" |
| 33 | |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 34 | #include "WebDataSource.h" |
| 35 | #include "WebFrameImpl.h" |
Torne (Richard Coles) | 8abfc58 | 2013-09-12 12:10:38 +0100 | [diff] [blame] | 36 | #include "core/fetch/CrossOriginAccessControl.h" |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 37 | #include "core/loader/DocumentThreadableLoader.h" |
| 38 | #include "core/loader/DocumentThreadableLoaderClient.h" |
Torne (Richard Coles) | 53e740f | 2013-05-09 18:38:43 +0100 | [diff] [blame] | 39 | #include "core/xml/XMLHttpRequest.h" |
Torne (Richard Coles) | 1e20218 | 2013-10-18 15:46:42 +0100 | [diff] [blame] | 40 | #include "platform/Timer.h" |
Torne (Richard Coles) | 19cde67 | 2013-11-06 12:28:04 +0000 | [diff] [blame] | 41 | #include "platform/exported/WrappedResourceRequest.h" |
| 42 | #include "platform/exported/WrappedResourceResponse.h" |
Torne (Richard Coles) | 1e20218 | 2013-10-18 15:46:42 +0100 | [diff] [blame] | 43 | #include "platform/network/HTTPParsers.h" |
| 44 | #include "platform/network/ResourceError.h" |
Torne (Richard Coles) | 5267f70 | 2013-06-11 10:57:24 +0100 | [diff] [blame] | 45 | #include "public/platform/WebHTTPHeaderVisitor.h" |
| 46 | #include "public/platform/WebString.h" |
| 47 | #include "public/platform/WebURLError.h" |
| 48 | #include "public/platform/WebURLLoaderClient.h" |
| 49 | #include "public/platform/WebURLRequest.h" |
Ben Murdoch | 591b958 | 2013-07-10 11:41:44 +0100 | [diff] [blame] | 50 | #include "wtf/HashSet.h" |
| 51 | #include "wtf/text/WTFString.h" |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 52 | |
| 53 | using namespace WebCore; |
| 54 | using namespace WTF; |
| 55 | |
Torne (Richard Coles) | 51b2906 | 2013-11-28 11:56:03 +0000 | [diff] [blame] | 56 | namespace blink { |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 57 | |
| 58 | namespace { |
| 59 | |
| 60 | class HTTPRequestHeaderValidator : public WebHTTPHeaderVisitor { |
| 61 | WTF_MAKE_NONCOPYABLE(HTTPRequestHeaderValidator); |
| 62 | public: |
| 63 | HTTPRequestHeaderValidator() : m_isSafe(true) { } |
| 64 | |
| 65 | void visitHeader(const WebString& name, const WebString& value); |
| 66 | bool isSafe() const { return m_isSafe; } |
| 67 | |
| 68 | private: |
| 69 | bool m_isSafe; |
| 70 | }; |
| 71 | |
| 72 | typedef HashSet<String, CaseFoldingHash> HTTPHeaderSet; |
| 73 | |
| 74 | void HTTPRequestHeaderValidator::visitHeader(const WebString& name, const WebString& value) |
| 75 | { |
| 76 | m_isSafe = m_isSafe && isValidHTTPToken(name) && XMLHttpRequest::isAllowedHTTPHeader(name) && isValidHTTPHeaderValue(value); |
| 77 | } |
| 78 | |
| 79 | // FIXME: Remove this and use WebCore code that does the same thing. |
| 80 | class HTTPResponseHeaderValidator : public WebHTTPHeaderVisitor { |
| 81 | WTF_MAKE_NONCOPYABLE(HTTPResponseHeaderValidator); |
| 82 | public: |
| 83 | HTTPResponseHeaderValidator(bool usingAccessControl) : m_usingAccessControl(usingAccessControl) { } |
| 84 | |
| 85 | void visitHeader(const WebString& name, const WebString& value); |
| 86 | const HTTPHeaderSet& blockedHeaders(); |
| 87 | |
| 88 | private: |
| 89 | HTTPHeaderSet m_exposedHeaders; |
| 90 | HTTPHeaderSet m_blockedHeaders; |
| 91 | bool m_usingAccessControl; |
| 92 | }; |
| 93 | |
| 94 | void HTTPResponseHeaderValidator::visitHeader(const WebString& name, const WebString& value) |
| 95 | { |
| 96 | String headerName(name); |
| 97 | if (m_usingAccessControl) { |
| 98 | if (equalIgnoringCase(headerName, "access-control-expose-headers")) |
| 99 | parseAccessControlExposeHeadersAllowList(value, m_exposedHeaders); |
| 100 | else if (!isOnAccessControlResponseHeaderWhitelist(headerName)) |
| 101 | m_blockedHeaders.add(name); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | const HTTPHeaderSet& HTTPResponseHeaderValidator::blockedHeaders() |
| 106 | { |
| 107 | // Remove exposed headers from the blocked set. |
| 108 | if (!m_exposedHeaders.isEmpty()) { |
| 109 | // Don't allow Set-Cookie headers to be exposed. |
| 110 | m_exposedHeaders.remove("set-cookie"); |
| 111 | m_exposedHeaders.remove("set-cookie2"); |
| 112 | // Block Access-Control-Expose-Header itself. It could be exposed later. |
| 113 | m_blockedHeaders.add("access-control-expose-headers"); |
| 114 | HTTPHeaderSet::const_iterator end = m_exposedHeaders.end(); |
| 115 | for (HTTPHeaderSet::const_iterator it = m_exposedHeaders.begin(); it != end; ++it) |
| 116 | m_blockedHeaders.remove(*it); |
| 117 | } |
| 118 | |
| 119 | return m_blockedHeaders; |
| 120 | } |
| 121 | |
| 122 | } |
| 123 | |
| 124 | // This class bridges the interface differences between WebCore and WebKit loader clients. |
| 125 | // It forwards its ThreadableLoaderClient notifications to a WebURLLoaderClient. |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame] | 126 | class AssociatedURLLoader::ClientAdapter FINAL : public DocumentThreadableLoaderClient { |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 127 | WTF_MAKE_NONCOPYABLE(ClientAdapter); |
| 128 | public: |
| 129 | static PassOwnPtr<ClientAdapter> create(AssociatedURLLoader*, WebURLLoaderClient*, const WebURLLoaderOptions&); |
| 130 | |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame] | 131 | virtual void didSendData(unsigned long long /*bytesSent*/, unsigned long long /*totalBytesToBeSent*/) OVERRIDE; |
| 132 | virtual void willSendRequest(ResourceRequest& /*newRequest*/, const ResourceResponse& /*redirectResponse*/) OVERRIDE; |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 133 | |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame] | 134 | virtual void didReceiveResponse(unsigned long, const ResourceResponse&) OVERRIDE; |
| 135 | virtual void didDownloadData(int /*dataLength*/) OVERRIDE; |
| 136 | virtual void didReceiveData(const char*, int /*dataLength*/) OVERRIDE; |
| 137 | virtual void didReceiveCachedMetadata(const char*, int /*dataLength*/) OVERRIDE; |
| 138 | virtual void didFinishLoading(unsigned long /*identifier*/, double /*finishTime*/) OVERRIDE; |
| 139 | virtual void didFail(const ResourceError&) OVERRIDE; |
| 140 | virtual void didFailRedirectCheck() OVERRIDE; |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 141 | |
| 142 | // Sets an error to be reported back to the client, asychronously. |
| 143 | void setDelayedError(const ResourceError&); |
| 144 | |
| 145 | // Enables forwarding of error notifications to the WebURLLoaderClient. These must be |
| 146 | // deferred until after the call to AssociatedURLLoader::loadAsynchronously() completes. |
| 147 | void enableErrorNotifications(); |
| 148 | |
| 149 | // Stops loading and releases the DocumentThreadableLoader as early as possible. |
Ben Murdoch | 02772c6 | 2013-07-26 10:21:05 +0100 | [diff] [blame] | 150 | void clearClient() { m_client = 0; } |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 151 | |
| 152 | private: |
| 153 | ClientAdapter(AssociatedURLLoader*, WebURLLoaderClient*, const WebURLLoaderOptions&); |
| 154 | |
| 155 | void notifyError(Timer<ClientAdapter>*); |
| 156 | |
| 157 | AssociatedURLLoader* m_loader; |
| 158 | WebURLLoaderClient* m_client; |
| 159 | WebURLLoaderOptions m_options; |
| 160 | WebURLError m_error; |
| 161 | |
| 162 | Timer<ClientAdapter> m_errorTimer; |
| 163 | bool m_enableErrorNotifications; |
| 164 | bool m_didFail; |
| 165 | }; |
| 166 | |
| 167 | PassOwnPtr<AssociatedURLLoader::ClientAdapter> AssociatedURLLoader::ClientAdapter::create(AssociatedURLLoader* loader, WebURLLoaderClient* client, const WebURLLoaderOptions& options) |
| 168 | { |
| 169 | return adoptPtr(new ClientAdapter(loader, client, options)); |
| 170 | } |
| 171 | |
| 172 | AssociatedURLLoader::ClientAdapter::ClientAdapter(AssociatedURLLoader* loader, WebURLLoaderClient* client, const WebURLLoaderOptions& options) |
| 173 | : m_loader(loader) |
| 174 | , m_client(client) |
| 175 | , m_options(options) |
| 176 | , m_errorTimer(this, &ClientAdapter::notifyError) |
| 177 | , m_enableErrorNotifications(false) |
| 178 | , m_didFail(false) |
| 179 | { |
| 180 | ASSERT(m_loader); |
| 181 | ASSERT(m_client); |
| 182 | } |
| 183 | |
| 184 | void AssociatedURLLoader::ClientAdapter::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse) |
| 185 | { |
| 186 | if (!m_client) |
| 187 | return; |
| 188 | |
| 189 | WrappedResourceRequest wrappedNewRequest(newRequest); |
| 190 | WrappedResourceResponse wrappedRedirectResponse(redirectResponse); |
| 191 | m_client->willSendRequest(m_loader, wrappedNewRequest, wrappedRedirectResponse); |
| 192 | } |
| 193 | |
| 194 | void AssociatedURLLoader::ClientAdapter::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) |
| 195 | { |
| 196 | if (!m_client) |
| 197 | return; |
| 198 | |
| 199 | m_client->didSendData(m_loader, bytesSent, totalBytesToBeSent); |
| 200 | } |
| 201 | |
| 202 | void AssociatedURLLoader::ClientAdapter::didReceiveResponse(unsigned long, const ResourceResponse& response) |
| 203 | { |
Ben Murdoch | 07a852d | 2014-03-31 11:51:52 +0100 | [diff] [blame] | 204 | if (!m_client) |
| 205 | return; |
| 206 | |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 207 | // Try to use the original ResourceResponse if possible. |
| 208 | WebURLResponse validatedResponse = WrappedResourceResponse(response); |
| 209 | HTTPResponseHeaderValidator validator(m_options.crossOriginRequestPolicy == WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl); |
| 210 | if (!m_options.exposeAllResponseHeaders) |
| 211 | validatedResponse.visitHTTPHeaderFields(&validator); |
| 212 | |
| 213 | // If there are blocked headers, copy the response so we can remove them. |
| 214 | const HTTPHeaderSet& blockedHeaders = validator.blockedHeaders(); |
| 215 | if (!blockedHeaders.isEmpty()) { |
| 216 | validatedResponse = WebURLResponse(validatedResponse); |
| 217 | HTTPHeaderSet::const_iterator end = blockedHeaders.end(); |
| 218 | for (HTTPHeaderSet::const_iterator it = blockedHeaders.begin(); it != end; ++it) |
| 219 | validatedResponse.clearHTTPHeaderField(*it); |
| 220 | } |
| 221 | m_client->didReceiveResponse(m_loader, validatedResponse); |
| 222 | } |
| 223 | |
| 224 | void AssociatedURLLoader::ClientAdapter::didDownloadData(int dataLength) |
| 225 | { |
| 226 | if (!m_client) |
| 227 | return; |
| 228 | |
Torne (Richard Coles) | c0e19a6 | 2013-08-30 15:15:11 +0100 | [diff] [blame] | 229 | m_client->didDownloadData(m_loader, dataLength, -1); |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 230 | } |
| 231 | |
| 232 | void AssociatedURLLoader::ClientAdapter::didReceiveData(const char* data, int dataLength) |
| 233 | { |
| 234 | if (!m_client) |
| 235 | return; |
| 236 | |
| 237 | m_client->didReceiveData(m_loader, data, dataLength, -1); |
| 238 | } |
| 239 | |
| 240 | void AssociatedURLLoader::ClientAdapter::didReceiveCachedMetadata(const char* data, int dataLength) |
| 241 | { |
| 242 | if (!m_client) |
| 243 | return; |
| 244 | |
| 245 | m_client->didReceiveCachedMetadata(m_loader, data, dataLength); |
| 246 | } |
| 247 | |
| 248 | void AssociatedURLLoader::ClientAdapter::didFinishLoading(unsigned long identifier, double finishTime) |
| 249 | { |
| 250 | if (!m_client) |
| 251 | return; |
| 252 | |
Torne (Richard Coles) | 0938029 | 2014-02-21 12:17:33 +0000 | [diff] [blame] | 253 | m_client->didFinishLoading(m_loader, finishTime, WebURLLoaderClient::kUnknownEncodedDataLength); |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 254 | } |
| 255 | |
| 256 | void AssociatedURLLoader::ClientAdapter::didFail(const ResourceError& error) |
| 257 | { |
| 258 | if (!m_client) |
| 259 | return; |
| 260 | |
| 261 | m_didFail = true; |
| 262 | m_error = WebURLError(error); |
| 263 | if (m_enableErrorNotifications) |
| 264 | notifyError(&m_errorTimer); |
| 265 | } |
| 266 | |
| 267 | void AssociatedURLLoader::ClientAdapter::didFailRedirectCheck() |
| 268 | { |
| 269 | m_loader->cancel(); |
| 270 | } |
| 271 | |
| 272 | void AssociatedURLLoader::ClientAdapter::setDelayedError(const ResourceError& error) |
| 273 | { |
| 274 | didFail(error); |
| 275 | } |
| 276 | |
| 277 | void AssociatedURLLoader::ClientAdapter::enableErrorNotifications() |
| 278 | { |
| 279 | m_enableErrorNotifications = true; |
| 280 | // If an error has already been received, start a timer to report it to the client |
| 281 | // after AssociatedURLLoader::loadAsynchronously has returned to the caller. |
| 282 | if (m_didFail) |
Torne (Richard Coles) | d5428f3 | 2014-03-18 10:21:16 +0000 | [diff] [blame] | 283 | m_errorTimer.startOneShot(0, FROM_HERE); |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 284 | } |
| 285 | |
| 286 | void AssociatedURLLoader::ClientAdapter::notifyError(Timer<ClientAdapter>* timer) |
| 287 | { |
| 288 | ASSERT_UNUSED(timer, timer == &m_errorTimer); |
| 289 | |
| 290 | m_client->didFail(m_loader, m_error); |
| 291 | } |
| 292 | |
| 293 | AssociatedURLLoader::AssociatedURLLoader(PassRefPtr<WebFrameImpl> frameImpl, const WebURLLoaderOptions& options) |
| 294 | : m_frameImpl(frameImpl) |
| 295 | , m_options(options) |
| 296 | , m_client(0) |
| 297 | { |
| 298 | ASSERT(m_frameImpl); |
| 299 | } |
| 300 | |
| 301 | AssociatedURLLoader::~AssociatedURLLoader() |
| 302 | { |
| 303 | cancel(); |
| 304 | } |
| 305 | |
| 306 | #define COMPILE_ASSERT_MATCHING_ENUM(webkit_name, webcore_name) \ |
Torne (Richard Coles) | 51b2906 | 2013-11-28 11:56:03 +0000 | [diff] [blame] | 307 | COMPILE_ASSERT(static_cast<int>(blink::webkit_name) == static_cast<int>(WebCore::webcore_name), mismatching_enums) |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 308 | |
| 309 | COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::CrossOriginRequestPolicyDeny, DenyCrossOriginRequests); |
| 310 | COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl, UseAccessControl); |
| 311 | COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::CrossOriginRequestPolicyAllow, AllowCrossOriginRequests); |
| 312 | |
Torne (Richard Coles) | 51b2906 | 2013-11-28 11:56:03 +0000 | [diff] [blame] | 313 | COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::ConsiderPreflight, ConsiderPreflight); |
| 314 | COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::ForcePreflight, ForcePreflight); |
| 315 | COMPILE_ASSERT_MATCHING_ENUM(WebURLLoaderOptions::PreventPreflight, PreventPreflight); |
| 316 | |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 317 | void AssociatedURLLoader::loadSynchronously(const WebURLRequest& request, WebURLResponse& response, WebURLError& error, WebData& data) |
| 318 | { |
| 319 | ASSERT(0); // Synchronous loading is not supported. |
| 320 | } |
| 321 | |
| 322 | void AssociatedURLLoader::loadAsynchronously(const WebURLRequest& request, WebURLLoaderClient* client) |
| 323 | { |
| 324 | ASSERT(!m_client); |
| 325 | |
| 326 | m_client = client; |
| 327 | ASSERT(m_client); |
| 328 | |
| 329 | bool allowLoad = true; |
| 330 | WebURLRequest newRequest(request); |
| 331 | if (m_options.untrustedHTTP) { |
| 332 | WebString method = newRequest.httpMethod(); |
| 333 | allowLoad = isValidHTTPToken(method) && XMLHttpRequest::isAllowedHTTPMethod(method); |
| 334 | if (allowLoad) { |
| 335 | newRequest.setHTTPMethod(XMLHttpRequest::uppercaseKnownHTTPMethod(method)); |
| 336 | HTTPRequestHeaderValidator validator; |
| 337 | newRequest.visitHTTPHeaderFields(&validator); |
| 338 | allowLoad = validator.isSafe(); |
| 339 | } |
| 340 | } |
| 341 | |
| 342 | m_clientAdapter = ClientAdapter::create(this, m_client, m_options); |
| 343 | |
| 344 | if (allowLoad) { |
| 345 | ThreadableLoaderOptions options; |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 346 | options.sniffContent = m_options.sniffContent ? SniffContent : DoNotSniffContent; |
| 347 | options.allowCredentials = m_options.allowCredentials ? AllowStoredCredentials : DoNotAllowStoredCredentials; |
Torne (Richard Coles) | 51b2906 | 2013-11-28 11:56:03 +0000 | [diff] [blame] | 348 | options.preflightPolicy = static_cast<WebCore::PreflightPolicy>(m_options.preflightPolicy); |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 349 | options.crossOriginRequestPolicy = static_cast<WebCore::CrossOriginRequestPolicy>(m_options.crossOriginRequestPolicy); |
Torne (Richard Coles) | 926b001 | 2013-03-28 15:32:48 +0000 | [diff] [blame] | 350 | options.dataBufferingPolicy = DoNotBufferData; |
Torne (Richard Coles) | 5c87bf8 | 2012-11-14 11:46:17 +0000 | [diff] [blame] | 351 | |
| 352 | const ResourceRequest& webcoreRequest = newRequest.toResourceRequest(); |
| 353 | Document* webcoreDocument = m_frameImpl->frame()->document(); |
| 354 | m_loader = DocumentThreadableLoader::create(webcoreDocument, m_clientAdapter.get(), webcoreRequest, options); |
| 355 | } else { |
| 356 | // FIXME: return meaningful error codes. |
| 357 | m_clientAdapter->setDelayedError(ResourceError()); |
| 358 | } |
| 359 | m_clientAdapter->enableErrorNotifications(); |
| 360 | } |
| 361 | |
| 362 | void AssociatedURLLoader::cancel() |
| 363 | { |
| 364 | if (m_clientAdapter) |
| 365 | m_clientAdapter->clearClient(); |
| 366 | if (m_loader) |
| 367 | m_loader->cancel(); |
| 368 | } |
| 369 | |
| 370 | void AssociatedURLLoader::setDefersLoading(bool defersLoading) |
| 371 | { |
| 372 | if (m_loader) |
| 373 | m_loader->setDefersLoading(defersLoading); |
| 374 | } |
| 375 | |
Torne (Richard Coles) | 51b2906 | 2013-11-28 11:56:03 +0000 | [diff] [blame] | 376 | } // namespace blink |