Web Inspector: Include Beacon and Ping requests in Network tab
[WebKit-https.git] / Source / WebCore / loader / PingLoader.cpp
1 /*
2  * Copyright (C) 2010 Google Inc. All rights reserved.
3  * Copyright (C) 2015 Roopesh Chander (roop@roopc.net)
4  * Copyright (C) 2015-2017 Apple Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following disclaimer
14  * in the documentation and/or other materials provided with the
15  * distribution.
16  *     * Neither the name of Google Inc. nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  */
33
34 #include "config.h"
35 #include "PingLoader.h"
36
37 #include "ContentSecurityPolicy.h"
38 #include "Document.h"
39 #include "Frame.h"
40 #include "FrameLoader.h"
41 #include "FrameLoaderClient.h"
42 #include "InspectorInstrumentation.h"
43 #include "LoaderStrategy.h"
44 #include "NetworkLoadMetrics.h"
45 #include "Page.h"
46 #include "PlatformStrategies.h"
47 #include "ProgressTracker.h"
48 #include "ResourceHandle.h"
49 #include "ResourceLoadInfo.h"
50 #include "ResourceRequest.h"
51 #include "ResourceResponse.h"
52 #include "SecurityOrigin.h"
53 #include "SecurityPolicy.h"
54 #include "UserContentController.h"
55 #include <wtf/text/CString.h>
56
57 namespace WebCore {
58
59 #if !ENABLE(CONTENT_EXTENSIONS)
60
61 // Returns true if we should block the load.
62 static inline bool processContentExtensionRulesForLoad(const Frame&, ResourceRequest&, ResourceType)
63 {
64     return false;
65 }
66
67 #else
68
69 // Returns true if we should block the load.
70 static bool processContentExtensionRulesForLoad(const Frame& frame, ResourceRequest& request, ResourceType resourceType)
71 {
72     auto* documentLoader = frame.loader().documentLoader();
73     if (!documentLoader)
74         return false;
75     auto* page = frame.page();
76     if (!page)
77         return false;
78     auto status = page->userContentProvider().processContentExtensionRulesForLoad(request.url(), resourceType, *documentLoader);
79     applyBlockedStatusToRequest(status, page, request);
80     return status.blockedLoad;
81 }
82
83 #endif
84
85 void PingLoader::loadImage(Frame& frame, const URL& url)
86 {
87     ASSERT(frame.document());
88     auto& document = *frame.document();
89
90     if (!document.securityOrigin().canDisplay(url)) {
91         FrameLoader::reportLocalLoadFailed(&frame, url);
92         return;
93     }
94
95     ResourceRequest request(url);
96     if (processContentExtensionRulesForLoad(frame, request, ResourceType::Image))
97         return;
98
99     document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(request, ContentSecurityPolicy::InsecureRequestType::Load);
100
101     request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0");
102
103     HTTPHeaderMap originalRequestHeader = request.httpHeaderFields();
104
105     String referrer = SecurityPolicy::generateReferrerHeader(document.referrerPolicy(), request.url(), frame.loader().outgoingReferrer());
106     if (!referrer.isEmpty())
107         request.setHTTPReferrer(referrer);
108     frame.loader().addExtraFieldsToSubresourceRequest(request);
109
110     startPingLoad(frame, request, WTFMove(originalRequestHeader), ShouldFollowRedirects::Yes);
111 }
112
113 // http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#hyperlink-auditing
114 void PingLoader::sendPing(Frame& frame, const URL& pingURL, const URL& destinationURL)
115 {
116     ASSERT(frame.document());
117
118     if (!pingURL.protocolIsInHTTPFamily())
119         return;
120
121     ResourceRequest request(pingURL);
122     if (processContentExtensionRulesForLoad(frame, request, ResourceType::Raw))
123         return;
124
125     auto& document = *frame.document();
126     document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(request, ContentSecurityPolicy::InsecureRequestType::Load);
127
128     request.setHTTPMethod("POST");
129     request.setHTTPContentType("text/ping");
130     request.setHTTPBody(FormData::create("PING"));
131     request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0");
132
133     HTTPHeaderMap originalRequestHeader = request.httpHeaderFields();
134
135     frame.loader().addExtraFieldsToSubresourceRequest(request);
136
137     auto& sourceOrigin = document.securityOrigin();
138     FrameLoader::addHTTPOriginIfNeeded(request, sourceOrigin.toString());
139     request.setHTTPHeaderField(HTTPHeaderName::PingTo, destinationURL);
140     if (!SecurityPolicy::shouldHideReferrer(pingURL, frame.loader().outgoingReferrer())) {
141         request.setHTTPHeaderField(HTTPHeaderName::PingFrom, document.url());
142         if (!sourceOrigin.isSameSchemeHostPort(SecurityOrigin::create(pingURL).get())) {
143             String referrer = SecurityPolicy::generateReferrerHeader(document.referrerPolicy(), pingURL, frame.loader().outgoingReferrer());
144             if (!referrer.isEmpty())
145                 request.setHTTPReferrer(referrer);
146         }
147     }
148
149     startPingLoad(frame, request, WTFMove(originalRequestHeader), ShouldFollowRedirects::Yes);
150 }
151
152 void PingLoader::sendViolationReport(Frame& frame, const URL& reportURL, Ref<FormData>&& report, ViolationReportType reportType)
153 {
154     ASSERT(frame.document());
155
156     ResourceRequest request(reportURL);
157     if (processContentExtensionRulesForLoad(frame, request, ResourceType::Raw))
158         return;
159
160     auto& document = *frame.document();
161     document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(request, ContentSecurityPolicy::InsecureRequestType::Load);
162
163     request.setHTTPMethod(ASCIILiteral("POST"));
164     request.setHTTPBody(WTFMove(report));
165     switch (reportType) {
166     case ViolationReportType::ContentSecurityPolicy:
167         request.setHTTPContentType(ASCIILiteral("application/csp-report"));
168         break;
169     case ViolationReportType::XSSAuditor:
170         request.setHTTPContentType(ASCIILiteral("application/json"));
171         break;
172     }
173
174     bool removeCookies = true;
175     if (document.securityOrigin().isSameSchemeHostPort(SecurityOrigin::create(reportURL).get()))
176         removeCookies = false;
177     if (removeCookies)
178         request.setAllowCookies(false);
179
180     HTTPHeaderMap originalRequestHeader = request.httpHeaderFields();
181
182     frame.loader().addExtraFieldsToSubresourceRequest(request);
183
184     String referrer = SecurityPolicy::generateReferrerHeader(document.referrerPolicy(), reportURL, frame.loader().outgoingReferrer());
185     if (!referrer.isEmpty())
186         request.setHTTPReferrer(referrer);
187
188     startPingLoad(frame, request, WTFMove(originalRequestHeader), ShouldFollowRedirects::No);
189 }
190
191 void PingLoader::startPingLoad(Frame& frame, ResourceRequest& request, HTTPHeaderMap&& originalRequestHeaders, ShouldFollowRedirects shouldFollowRedirects)
192 {
193     unsigned long identifier = frame.page()->progress().createUniqueIdentifier();
194     // FIXME: Why activeDocumentLoader? I would have expected documentLoader().
195     // It seems like the PingLoader should be associated with the current
196     // Document in the Frame, but the activeDocumentLoader will be associated
197     // with the provisional DocumentLoader if there is a provisional
198     // DocumentLoader.
199     bool shouldUseCredentialStorage = frame.loader().client().shouldUseCredentialStorage(frame.loader().activeDocumentLoader(), identifier);
200     FetchOptions options;
201     options.credentials = shouldUseCredentialStorage ? FetchOptions::Credentials::Include : FetchOptions::Credentials::Omit;
202     options.redirect = shouldFollowRedirects == ShouldFollowRedirects::Yes ? FetchOptions::Redirect::Follow : FetchOptions::Redirect::Error;
203
204     // FIXME: Move ping loads to normal subresource loading to get normal inspector request instrumentation hooks.
205     InspectorInstrumentation::willSendRequestOfType(&frame, identifier, frame.loader().activeDocumentLoader(), request, InspectorInstrumentation::LoadType::Ping);
206
207     platformStrategies()->loaderStrategy()->startPingLoad(frame, request, WTFMove(originalRequestHeaders), options, [protectedFrame = makeRef(frame), identifier] (const ResourceError& error, const ResourceResponse& response) {
208         if (!response.isNull())
209             InspectorInstrumentation::didReceiveResourceResponse(protectedFrame, identifier, protectedFrame->loader().activeDocumentLoader(), response, nullptr);
210         if (error.isNull()) {
211             NetworkLoadMetrics emptyMetrics;
212             InspectorInstrumentation::didFinishLoading(protectedFrame.ptr(), protectedFrame->loader().activeDocumentLoader(), identifier, emptyMetrics, nullptr);
213         } else
214             InspectorInstrumentation::didFailLoading(protectedFrame.ptr(), protectedFrame->loader().activeDocumentLoader(), identifier, error);
215     });
216 }
217
218 }