Requests handled by Service Worker should not go through preflighting
[WebKit-https.git] / Source / WebCore / loader / cache / CachedResourceRequest.cpp
1 /*
2  * Copyright (C) 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "CachedResourceRequest.h"
28
29 #include "CachedResourceLoader.h"
30 #include "ContentExtensionActions.h"
31 #include "CrossOriginAccessControl.h"
32 #include "Document.h"
33 #include "Element.h"
34 #include "FrameLoader.h"
35 #include "HTTPHeaderValues.h"
36 #include "MemoryCache.h"
37 #include "SecurityPolicy.h"
38 #include <wtf/NeverDestroyed.h>
39
40 namespace WebCore {
41
42 CachedResourceRequest::CachedResourceRequest(ResourceRequest&& resourceRequest, const ResourceLoaderOptions& options, std::optional<ResourceLoadPriority> priority, String&& charset)
43     : m_resourceRequest(WTFMove(resourceRequest))
44     , m_charset(WTFMove(charset))
45     , m_options(options)
46     , m_priority(priority)
47     , m_fragmentIdentifier(splitFragmentIdentifierFromRequestURL(m_resourceRequest))
48 {
49 }
50
51 String CachedResourceRequest::splitFragmentIdentifierFromRequestURL(ResourceRequest& request)
52 {
53     if (!MemoryCache::shouldRemoveFragmentIdentifier(request.url()))
54         return { };
55     URL url = request.url();
56     String fragmentIdentifier = url.fragmentIdentifier();
57     url.removeFragmentIdentifier();
58     request.setURL(url);
59     return fragmentIdentifier;
60 }
61
62 void CachedResourceRequest::setInitiator(Element& element)
63 {
64     ASSERT(!m_initiatorElement);
65     ASSERT(m_initiatorName.isEmpty());
66     m_initiatorElement = &element;
67 }
68
69 void CachedResourceRequest::setInitiator(const AtomicString& name)
70 {
71     ASSERT(!m_initiatorElement);
72     ASSERT(m_initiatorName.isEmpty());
73     m_initiatorName = name;
74 }
75
76 const AtomicString& CachedResourceRequest::initiatorName() const
77 {
78     if (m_initiatorElement)
79         return m_initiatorElement->localName();
80     if (!m_initiatorName.isEmpty())
81         return m_initiatorName;
82
83     static NeverDestroyed<AtomicString> defaultName("other", AtomicString::ConstructFromLiteral);
84     return defaultName;
85 }
86
87 void CachedResourceRequest::setAsPotentiallyCrossOrigin(const String& mode, Document& document)
88 {
89     ASSERT(m_options.mode == FetchOptions::Mode::NoCors);
90
91     m_origin = &document.securityOrigin();
92
93     if (mode.isNull())
94         return;
95
96     m_options.mode = FetchOptions::Mode::Cors;
97
98     FetchOptions::Credentials credentials = equalLettersIgnoringASCIICase(mode, "omit")
99         ? FetchOptions::Credentials::Omit : equalLettersIgnoringASCIICase(mode, "use-credentials")
100         ? FetchOptions::Credentials::Include : FetchOptions::Credentials::SameOrigin;
101     m_options.credentials = credentials;
102     m_options.storedCredentialsPolicy = credentials == FetchOptions::Credentials::Include ? StoredCredentialsPolicy::Use : StoredCredentialsPolicy::DoNotUse;
103     WebCore::updateRequestForAccessControl(m_resourceRequest, document.securityOrigin(), m_options.storedCredentialsPolicy);
104 }
105
106 void CachedResourceRequest::updateForAccessControl(Document& document)
107 {
108     ASSERT(m_options.mode == FetchOptions::Mode::Cors);
109
110     m_origin = &document.securityOrigin();
111     WebCore::updateRequestForAccessControl(m_resourceRequest, *m_origin, m_options.storedCredentialsPolicy);
112 }
113
114 void upgradeInsecureResourceRequestIfNeeded(ResourceRequest& request, Document& document)
115 {
116     URL url = request.url();
117
118     ASSERT(document.contentSecurityPolicy());
119     document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(url, ContentSecurityPolicy::InsecureRequestType::Load);
120
121     if (url == request.url())
122         return;
123
124     request.setURL(url);
125 }
126
127 void CachedResourceRequest::upgradeInsecureRequestIfNeeded(Document& document)
128 {
129     upgradeInsecureResourceRequestIfNeeded(m_resourceRequest, document);
130 }
131
132 void CachedResourceRequest::setDomainForCachePartition(Document& document)
133 {
134     m_resourceRequest.setDomainForCachePartition(document.topOrigin().domainForCachePartition());
135 }
136
137 void CachedResourceRequest::setDomainForCachePartition(const String& domain)
138 {
139     m_resourceRequest.setDomainForCachePartition(domain);
140 }
141
142 static inline String acceptHeaderValueFromType(CachedResource::Type type)
143 {
144     switch (type) {
145     case CachedResource::Type::MainResource:
146         return ASCIILiteral("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
147     case CachedResource::Type::ImageResource:
148         return ASCIILiteral("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5");
149     case CachedResource::Type::CSSStyleSheet:
150         return ASCIILiteral("text/css,*/*;q=0.1");
151     case CachedResource::Type::SVGDocumentResource:
152         return ASCIILiteral("image/svg+xml");
153 #if ENABLE(XSLT)
154     case CachedResource::Type::XSLStyleSheet:
155         // FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example.
156         return ASCIILiteral("text/xml,application/xml,application/xhtml+xml,text/xsl,application/rss+xml,application/atom+xml");
157 #endif
158     default:
159         return ASCIILiteral("*/*");
160     }
161 }
162
163 void CachedResourceRequest::setAcceptHeaderIfNone(CachedResource::Type type)
164 {
165     if (!m_resourceRequest.hasHTTPHeader(HTTPHeaderName::Accept))
166         m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::Accept, acceptHeaderValueFromType(type));
167 }
168
169 void CachedResourceRequest::updateAccordingCacheMode()
170 {
171     if (m_options.cache == FetchOptions::Cache::Default
172         && (m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfModifiedSince)
173             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfNoneMatch)
174             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfUnmodifiedSince)
175             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfMatch)
176             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfRange)))
177         m_options.cache = FetchOptions::Cache::NoStore;
178
179     switch (m_options.cache) {
180     case FetchOptions::Cache::NoCache:
181         m_resourceRequest.setCachePolicy(RefreshAnyCacheData);
182         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::maxAge0());
183         break;
184     case FetchOptions::Cache::NoStore:
185         m_options.cachingPolicy = CachingPolicy::DisallowCaching;
186         m_resourceRequest.setCachePolicy(DoNotUseAnyCache);
187         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
188         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
189         break;
190     case FetchOptions::Cache::Reload:
191         m_resourceRequest.setCachePolicy(ReloadIgnoringCacheData);
192         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
193         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
194         break;
195     case FetchOptions::Cache::Default:
196         break;
197     case FetchOptions::Cache::ForceCache:
198         m_resourceRequest.setCachePolicy(ReturnCacheDataElseLoad);
199         break;
200     case FetchOptions::Cache::OnlyIfCached:
201         m_resourceRequest.setCachePolicy(ReturnCacheDataDontLoad);
202         break;
203     }
204 }
205
206 void CachedResourceRequest::removeFragmentIdentifierIfNeeded()
207 {
208     URL url = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url());
209     if (url.string() != m_resourceRequest.url())
210         m_resourceRequest.setURL(url);
211 }
212
213 #if ENABLE(CONTENT_EXTENSIONS)
214
215 void CachedResourceRequest::applyBlockedStatus(const ContentExtensions::BlockedStatus& blockedStatus, Page* page)
216 {
217     ContentExtensions::applyBlockedStatusToRequest(blockedStatus, page, m_resourceRequest);
218 }
219
220 #endif
221
222 void CachedResourceRequest::updateReferrerOriginAndUserAgentHeaders(FrameLoader& frameLoader, ReferrerPolicy defaultPolicy)
223 {
224     // Implementing step 7 to 9 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
225
226     String outgoingOrigin;
227     String outgoingReferrer = m_resourceRequest.httpReferrer();
228     if (!outgoingReferrer.isNull())
229         outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
230     else {
231         outgoingReferrer = frameLoader.outgoingReferrer();
232         outgoingOrigin = frameLoader.outgoingOrigin();
233     }
234
235     switch (m_options.referrerPolicy) {
236     case ReferrerPolicy::EmptyString:
237         outgoingReferrer = SecurityPolicy::generateReferrerHeader(defaultPolicy, m_resourceRequest.url(), outgoingReferrer);
238         break;
239     default:
240         outgoingReferrer = SecurityPolicy::generateReferrerHeader(m_options.referrerPolicy, m_resourceRequest.url(), outgoingReferrer);
241         break;
242     };
243
244     if (outgoingReferrer.isEmpty())
245         m_resourceRequest.clearHTTPReferrer();
246     else
247         m_resourceRequest.setHTTPReferrer(outgoingReferrer);
248     FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin);
249
250     frameLoader.applyUserAgent(m_resourceRequest);
251 }
252
253 bool isRequestCrossOrigin(SecurityOrigin* origin, const URL& requestURL, const ResourceLoaderOptions& options)
254 {
255     if (!origin)
256         return false;
257
258     // Using same origin mode guarantees the loader will not do a cross-origin load, so we let it take care of it and just return false.
259     if (options.mode == FetchOptions::Mode::SameOrigin)
260         return false;
261
262     // FIXME: We should remove options.sameOriginDataURLFlag once https://github.com/whatwg/fetch/issues/393 is fixed.
263     if (requestURL.protocolIsData() && options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set)
264         return false;
265
266     return !origin->canRequest(requestURL);
267 }
268
269 void CachedResourceRequest::setDestinationIfNotSet(FetchOptions::Destination destination)
270 {
271     if (m_options.destination != FetchOptions::Destination::EmptyString)
272         return;
273     m_options.destination = destination;
274 }
275
276 #if ENABLE(SERVICE_WORKER)
277 void CachedResourceRequest::setSelectedServiceWorkerIdentifierIfNeeded(ServiceWorkerIdentifier identifier)
278 {
279     if (isNonSubresourceRequest(m_options.destination))
280         return;
281     if (isPotentialNavigationOrSubresourceRequest(m_options.destination))
282         return;
283
284     if (m_options.serviceWorkersMode == ServiceWorkersMode::None)
285         return;
286     if (m_options.serviceWorkerIdentifier)
287         return;
288
289     m_options.serviceWorkerIdentifier = identifier;
290 }
291 #endif
292
293 } // namespace WebCore