[WTF] Import std::optional reference implementation as WTF::Optional
[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(PassRefPtr<Element> element)
63 {
64     ASSERT(!m_initiatorElement && m_initiatorName.isEmpty());
65     m_initiatorElement = element;
66 }
67
68 void CachedResourceRequest::setInitiator(const AtomicString& name)
69 {
70     ASSERT(!m_initiatorElement && m_initiatorName.isEmpty());
71     m_initiatorName = name;
72 }
73
74 const AtomicString& CachedResourceRequest::initiatorName() const
75 {
76     if (m_initiatorElement)
77         return m_initiatorElement->localName();
78     if (!m_initiatorName.isEmpty())
79         return m_initiatorName;
80
81     static NeverDestroyed<AtomicString> defaultName("resource", AtomicString::ConstructFromLiteral);
82     return defaultName;
83 }
84
85 void CachedResourceRequest::setAsPotentiallyCrossOrigin(const String& mode, Document& document)
86 {
87     ASSERT(m_options.mode == FetchOptions::Mode::NoCors);
88     ASSERT(document.securityOrigin());
89
90     m_origin = document.securityOrigin();
91
92     if (mode.isNull())
93         return;
94
95     m_options.mode = FetchOptions::Mode::Cors;
96
97     FetchOptions::Credentials credentials = equalLettersIgnoringASCIICase(mode, "omit")
98         ? FetchOptions::Credentials::Omit : equalLettersIgnoringASCIICase(mode, "use-credentials")
99         ? FetchOptions::Credentials::Include : FetchOptions::Credentials::SameOrigin;
100     m_options.credentials = credentials;
101     m_options.allowCredentials = credentials == FetchOptions::Credentials::Include ? AllowStoredCredentials : DoNotAllowStoredCredentials;
102     WebCore::updateRequestForAccessControl(m_resourceRequest, *document.securityOrigin(), m_options.allowCredentials);
103 }
104
105 void CachedResourceRequest::updateForAccessControl(Document& document)
106 {
107     ASSERT(m_options.mode == FetchOptions::Mode::Cors);
108     ASSERT(document.securityOrigin());
109
110     m_origin = document.securityOrigin();
111     WebCore::updateRequestForAccessControl(m_resourceRequest, *m_origin, m_options.allowCredentials);
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 #if ENABLE(CACHE_PARTITIONING)
133 void CachedResourceRequest::setDomainForCachePartition(Document& document)
134 {
135     ASSERT(document.topOrigin());
136     m_resourceRequest.setDomainForCachePartition(document.topOrigin()->domainForCachePartition());
137 }
138 #endif
139
140 static inline String acceptHeaderValueFromType(CachedResource::Type type)
141 {
142     switch (type) {
143     case CachedResource::Type::MainResource:
144         return ASCIILiteral("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
145     case CachedResource::Type::ImageResource:
146         return ASCIILiteral("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5");
147     case CachedResource::Type::CSSStyleSheet:
148         return ASCIILiteral("text/css,*/*;q=0.1");
149     case CachedResource::Type::SVGDocumentResource:
150         return ASCIILiteral("image/svg+xml");
151 #if ENABLE(XSLT)
152     case CachedResource::Type::XSLStyleSheet:
153         // FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example.
154         return ASCIILiteral("text/xml,application/xml,application/xhtml+xml,text/xsl,application/rss+xml,application/atom+xml");
155 #endif
156     default:
157         return ASCIILiteral("*/*");
158     }
159 }
160
161 void CachedResourceRequest::setAcceptHeaderIfNone(CachedResource::Type type)
162 {
163     if (!m_resourceRequest.hasHTTPHeader(HTTPHeaderName::Accept))
164         m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::Accept, acceptHeaderValueFromType(type));
165 }
166
167 void CachedResourceRequest::updateAccordingCacheMode()
168 {
169     if (m_options.cache == FetchOptions::Cache::Default
170         && (m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfModifiedSince)
171             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfNoneMatch)
172             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfUnmodifiedSince)
173             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfMatch)
174             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfRange)))
175         m_options.cache = FetchOptions::Cache::NoStore;
176
177     switch (m_options.cache) {
178     case FetchOptions::Cache::NoCache:
179         m_resourceRequest.setCachePolicy(RefreshAnyCacheData);
180         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::maxAge0());
181         break;
182     case FetchOptions::Cache::NoStore:
183         m_options.cachingPolicy = CachingPolicy::DisallowCaching;
184         m_resourceRequest.setCachePolicy(DoNotUseAnyCache);
185         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
186         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
187         break;
188     case FetchOptions::Cache::Reload:
189         m_resourceRequest.setCachePolicy(ReloadIgnoringCacheData);
190         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
191         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
192         break;
193     case FetchOptions::Cache::Default:
194         break;
195     case FetchOptions::Cache::ForceCache:
196         m_resourceRequest.setCachePolicy(ReturnCacheDataElseLoad);
197         break;
198     case FetchOptions::Cache::OnlyIfCached:
199         m_resourceRequest.setCachePolicy(ReturnCacheDataDontLoad);
200         break;
201     }
202 }
203
204 void CachedResourceRequest::removeFragmentIdentifierIfNeeded()
205 {
206     URL url = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url());
207     if (url.string() != m_resourceRequest.url())
208         m_resourceRequest.setURL(url);
209 }
210
211 #if ENABLE(CONTENT_EXTENSIONS)
212 void CachedResourceRequest::applyBlockedStatus(const ContentExtensions::BlockedStatus& blockedStatus)
213 {
214     ContentExtensions::applyBlockedStatusToRequest(blockedStatus, m_resourceRequest);
215 }
216 #endif
217
218 void CachedResourceRequest::updateReferrerOriginAndUserAgentHeaders(FrameLoader& frameLoader, ReferrerPolicy defaultPolicy)
219 {
220     // Implementing step 7 to 9 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
221
222     String outgoingOrigin;
223     String outgoingReferrer = m_resourceRequest.httpReferrer();
224     if (!outgoingReferrer.isNull())
225         outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
226     else {
227         outgoingReferrer = frameLoader.outgoingReferrer();
228         outgoingOrigin = frameLoader.outgoingOrigin();
229     }
230
231     // FIXME: Refactor SecurityPolicy::generateReferrerHeader to align with new terminology used in https://w3c.github.io/webappsec-referrer-policy.
232     switch (m_options.referrerPolicy) {
233     case FetchOptions::ReferrerPolicy::EmptyString: {
234         outgoingReferrer = SecurityPolicy::generateReferrerHeader(defaultPolicy, m_resourceRequest.url(), outgoingReferrer);
235         break; }
236     case FetchOptions::ReferrerPolicy::NoReferrerWhenDowngrade:
237         outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Default, m_resourceRequest.url(), outgoingReferrer);
238         break;
239     case FetchOptions::ReferrerPolicy::NoReferrer:
240         outgoingReferrer = String();
241         break;
242     case FetchOptions::ReferrerPolicy::Origin:
243         outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Origin, m_resourceRequest.url(), outgoingReferrer);
244         break;
245     case FetchOptions::ReferrerPolicy::OriginWhenCrossOrigin:
246         if (isRequestCrossOrigin(m_origin.get(), m_resourceRequest.url(), m_options))
247             outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Origin, m_resourceRequest.url(), outgoingReferrer);
248         break;
249     case FetchOptions::ReferrerPolicy::UnsafeUrl:
250         break;
251     };
252
253     if (outgoingReferrer.isEmpty())
254         m_resourceRequest.clearHTTPReferrer();
255     else
256         m_resourceRequest.setHTTPReferrer(outgoingReferrer);
257     FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin);
258
259     frameLoader.applyUserAgent(m_resourceRequest);
260 }
261
262 bool isRequestCrossOrigin(SecurityOrigin* origin, const URL& requestURL, const ResourceLoaderOptions& options)
263 {
264     if (!origin)
265         return false;
266
267     // 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.
268     if (options.mode == FetchOptions::Mode::SameOrigin)
269         return false;
270
271     // FIXME: We should remove options.sameOriginDataURLFlag once https://github.com/whatwg/fetch/issues/393 is fixed.
272     if (requestURL.protocolIsData() && options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set)
273         return false;
274
275     return !origin->canRequest(requestURL);
276 }
277
278 } // namespace WebCore