Home | History | Annotate | Download | only in loader
      1 /*
      2  * Copyright (C) 2010 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 
     32 #include "config.h"
     33 #include "core/loader/PingLoader.h"
     34 
     35 #include "core/FetchInitiatorTypeNames.h"
     36 #include "core/dom/Document.h"
     37 #include "core/fetch/FetchContext.h"
     38 #include "core/frame/LocalFrame.h"
     39 #include "core/inspector/InspectorInstrumentation.h"
     40 #include "core/inspector/InspectorTraceEvents.h"
     41 #include "core/loader/FrameLoader.h"
     42 #include "core/loader/FrameLoaderClient.h"
     43 #include "core/loader/UniqueIdentifier.h"
     44 #include "core/page/Page.h"
     45 #include "platform/exported/WrappedResourceRequest.h"
     46 #include "platform/network/ResourceError.h"
     47 #include "platform/network/ResourceRequest.h"
     48 #include "platform/network/ResourceResponse.h"
     49 #include "platform/weborigin/SecurityOrigin.h"
     50 #include "platform/weborigin/SecurityPolicy.h"
     51 #include "public/platform/Platform.h"
     52 #include "public/platform/WebURLLoader.h"
     53 #include "public/platform/WebURLRequest.h"
     54 #include "public/platform/WebURLResponse.h"
     55 #include "wtf/OwnPtr.h"
     56 
     57 namespace blink {
     58 
     59 void PingLoader::loadImage(LocalFrame* frame, const KURL& url)
     60 {
     61     if (!frame->document()->securityOrigin()->canDisplay(url)) {
     62         FrameLoader::reportLocalLoadFailed(frame, url.string());
     63         return;
     64     }
     65 
     66     ResourceRequest request(url);
     67     request.setRequestContext(blink::WebURLRequest::RequestContextPing);
     68     request.setHTTPHeaderField("Cache-Control", "max-age=0");
     69     frame->loader().fetchContext().addAdditionalRequestHeaders(frame->document(), request, FetchSubresource);
     70     frame->loader().fetchContext().setFirstPartyForCookies(request);
     71 
     72     FetchInitiatorInfo initiatorInfo;
     73     initiatorInfo.name = FetchInitiatorTypeNames::ping;
     74     PingLoader::start(frame, request, initiatorInfo);
     75 }
     76 
     77 // http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing
     78 void PingLoader::sendLinkAuditPing(LocalFrame* frame, const KURL& pingURL, const KURL& destinationURL)
     79 {
     80     ResourceRequest request(pingURL);
     81     request.setRequestContext(blink::WebURLRequest::RequestContextPing);
     82     request.setHTTPMethod("POST");
     83     request.setHTTPContentType("text/ping");
     84     request.setHTTPBody(FormData::create("PING"));
     85     request.setHTTPHeaderField("Cache-Control", "max-age=0");
     86     frame->loader().fetchContext().addAdditionalRequestHeaders(frame->document(), request, FetchSubresource);
     87     frame->loader().fetchContext().setFirstPartyForCookies(request);
     88 
     89     RefPtr<SecurityOrigin> pingOrigin = SecurityOrigin::create(pingURL);
     90     // addAdditionalRequestHeaders() will have added a referrer for same origin requests,
     91     // but the spec omits the referrer for same origin.
     92     if (frame->document()->securityOrigin()->isSameSchemeHostPort(pingOrigin.get()))
     93         request.clearHTTPReferrer();
     94 
     95     request.setHTTPHeaderField("Ping-To", AtomicString(destinationURL.string()));
     96 
     97     // Ping-From follows the same rules as the default referrer beahavior for subresource requests.
     98     // FIXME: Should Ping-From obey ReferrerPolicy?
     99     if (!SecurityPolicy::shouldHideReferrer(pingURL, frame->document()->url().string()))
    100         request.setHTTPHeaderField("Ping-From", AtomicString(frame->document()->url().string()));
    101 
    102     FetchInitiatorInfo initiatorInfo;
    103     initiatorInfo.name = FetchInitiatorTypeNames::ping;
    104     PingLoader::start(frame, request, initiatorInfo);
    105 }
    106 
    107 void PingLoader::sendViolationReport(LocalFrame* frame, const KURL& reportURL, PassRefPtr<FormData> report, ViolationReportType type)
    108 {
    109     ResourceRequest request(reportURL);
    110     request.setRequestContext(blink::WebURLRequest::RequestContextPing);
    111     request.setHTTPMethod("POST");
    112     request.setHTTPContentType(type == ContentSecurityPolicyViolationReport ? "application/csp-report" : "application/json");
    113     request.setHTTPBody(report);
    114     frame->loader().fetchContext().addAdditionalRequestHeaders(frame->document(), request, FetchSubresource);
    115     frame->loader().fetchContext().setFirstPartyForCookies(request);
    116 
    117     FetchInitiatorInfo initiatorInfo;
    118     initiatorInfo.name = FetchInitiatorTypeNames::violationreport;
    119     PingLoader::start(frame, request, initiatorInfo, SecurityOrigin::create(reportURL)->isSameSchemeHostPort(frame->document()->securityOrigin()) ? AllowStoredCredentials : DoNotAllowStoredCredentials);
    120 }
    121 
    122 void PingLoader::start(LocalFrame* frame, ResourceRequest& request, const FetchInitiatorInfo& initiatorInfo, StoredCredentials credentialsAllowed)
    123 {
    124     if (!frame->loader().mixedContentChecker()->canRunInsecureContent(frame->document()->securityOrigin(), request.url()))
    125         return;
    126 
    127     OwnPtr<PingLoader> pingLoader = adoptPtr(new PingLoader(frame, request, initiatorInfo, credentialsAllowed));
    128 
    129     // Leak the ping loader, since it will kill itself as soon as it receives a response.
    130     PingLoader* leakedPingLoader ALLOW_UNUSED = pingLoader.leakPtr();
    131 }
    132 
    133 PingLoader::PingLoader(LocalFrame* frame, ResourceRequest& request, const FetchInitiatorInfo& initiatorInfo, StoredCredentials credentialsAllowed)
    134     : PageLifecycleObserver(frame->page())
    135     , m_timeout(this, &PingLoader::timeout)
    136     , m_url(request.url())
    137     , m_identifier(createUniqueIdentifier())
    138 {
    139     frame->loader().client()->didDispatchPingLoader(request.url());
    140 
    141     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResourceSendRequest", "data", InspectorSendRequestEvent::data(m_identifier, frame, request));
    142     TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline.stack"), "CallStack", "stack", InspectorCallStackEvent::currentCallStack());
    143     // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
    144     InspectorInstrumentation::willSendRequest(frame, m_identifier, frame->loader().documentLoader(), request, ResourceResponse(), initiatorInfo);
    145 
    146     m_loader = adoptPtr(blink::Platform::current()->createURLLoader());
    147     ASSERT(m_loader);
    148     blink::WrappedResourceRequest wrappedRequest(request);
    149     wrappedRequest.setAllowStoredCredentials(credentialsAllowed == AllowStoredCredentials);
    150     m_loader->loadAsynchronously(wrappedRequest, this);
    151 
    152     // If the server never responds, FrameLoader won't be able to cancel this load and
    153     // we'll sit here waiting forever. Set a very generous timeout, just in case.
    154     m_timeout.startOneShot(60000, FROM_HERE);
    155 }
    156 
    157 PingLoader::~PingLoader()
    158 {
    159     if (m_loader)
    160         m_loader->cancel();
    161 }
    162 
    163 void PingLoader::didReceiveResponse(blink::WebURLLoader*, const blink::WebURLResponse& response)
    164 {
    165     if (Page* page = this->page()) {
    166         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResourceFinish", "data", InspectorResourceFinishEvent::data(m_identifier, 0, true));
    167         const ResourceResponse& resourceResponse = response.toResourceResponse();
    168         InspectorInstrumentation::didReceiveResourceResponse(page->deprecatedLocalMainFrame(), m_identifier, 0, resourceResponse, 0);
    169         InspectorInstrumentation::didFailLoading(page->deprecatedLocalMainFrame(), m_identifier, ResourceError::cancelledError(m_url));
    170     }
    171     delete this;
    172 }
    173 
    174 void PingLoader::didReceiveData(blink::WebURLLoader*, const char*, int, int)
    175 {
    176     if (Page* page = this->page()) {
    177         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResourceFinish", "data", InspectorResourceFinishEvent::data(m_identifier, 0, true));
    178         InspectorInstrumentation::didFailLoading(page->deprecatedLocalMainFrame(), m_identifier, ResourceError::cancelledError(m_url));
    179     }
    180     delete this;
    181 }
    182 
    183 void PingLoader::didFinishLoading(blink::WebURLLoader*, double, int64_t)
    184 {
    185     if (Page* page = this->page()) {
    186         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResourceFinish", "data", InspectorResourceFinishEvent::data(m_identifier, 0, true));
    187         InspectorInstrumentation::didFailLoading(page->deprecatedLocalMainFrame(), m_identifier, ResourceError::cancelledError(m_url));
    188     }
    189     delete this;
    190 }
    191 
    192 void PingLoader::didFail(blink::WebURLLoader*, const blink::WebURLError& resourceError)
    193 {
    194     if (Page* page = this->page()) {
    195         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResourceFinish", "data", InspectorResourceFinishEvent::data(m_identifier, 0, true));
    196         InspectorInstrumentation::didFailLoading(page->deprecatedLocalMainFrame(), m_identifier, ResourceError(resourceError));
    197     }
    198     delete this;
    199 }
    200 
    201 void PingLoader::timeout(Timer<PingLoader>*)
    202 {
    203     if (Page* page = this->page()) {
    204         TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResourceFinish", "data", InspectorResourceFinishEvent::data(m_identifier, 0, true));
    205         InspectorInstrumentation::didFailLoading(page->deprecatedLocalMainFrame(), m_identifier, ResourceError::cancelledError(m_url));
    206     }
    207     delete this;
    208 }
    209 
    210 }
    211