6d0c22119ae34b1cd04fa3f10a5346ef61b0625e
[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("resource", 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     ASSERT(document.securityOrigin());
91
92     m_origin = document.securityOrigin();
93
94     if (mode.isNull())
95         return;
96
97     m_options.mode = FetchOptions::Mode::Cors;
98
99     FetchOptions::Credentials credentials = equalLettersIgnoringASCIICase(mode, "omit")
100         ? FetchOptions::Credentials::Omit : equalLettersIgnoringASCIICase(mode, "use-credentials")
101         ? FetchOptions::Credentials::Include : FetchOptions::Credentials::SameOrigin;
102     m_options.credentials = credentials;
103     m_options.allowCredentials = credentials == FetchOptions::Credentials::Include ? AllowStoredCredentials : DoNotAllowStoredCredentials;
104     WebCore::updateRequestForAccessControl(m_resourceRequest, *document.securityOrigin(), m_options.allowCredentials);
105 }
106
107 void CachedResourceRequest::updateForAccessControl(Document& document)
108 {
109     ASSERT(m_options.mode == FetchOptions::Mode::Cors);
110     ASSERT(document.securityOrigin());
111
112     m_origin = document.securityOrigin();
113     WebCore::updateRequestForAccessControl(m_resourceRequest, *m_origin, m_options.allowCredentials);
114 }
115
116 void upgradeInsecureResourceRequestIfNeeded(ResourceRequest& request, Document& document)
117 {
118     URL url = request.url();
119
120     ASSERT(document.contentSecurityPolicy());
121     document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(url, ContentSecurityPolicy::InsecureRequestType::Load);
122
123     if (url == request.url())
124         return;
125
126     request.setURL(url);
127 }
128
129 void CachedResourceRequest::upgradeInsecureRequestIfNeeded(Document& document)
130 {
131     upgradeInsecureResourceRequestIfNeeded(m_resourceRequest, document);
132 }
133
134 #if ENABLE(CACHE_PARTITIONING)
135 void CachedResourceRequest::setDomainForCachePartition(Document& document)
136 {
137     ASSERT(document.topOrigin());
138     m_resourceRequest.setDomainForCachePartition(document.topOrigin()->domainForCachePartition());
139 }
140 #endif
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)
216 {
217     ContentExtensions::applyBlockedStatusToRequest(blockedStatus, 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     // FIXME: Refactor SecurityPolicy::generateReferrerHeader to align with new terminology used in https://w3c.github.io/webappsec-referrer-policy.
236     switch (m_options.referrerPolicy) {
237     case FetchOptions::ReferrerPolicy::EmptyString: {
238         outgoingReferrer = SecurityPolicy::generateReferrerHeader(defaultPolicy, m_resourceRequest.url(), outgoingReferrer);
239         break; }
240     case FetchOptions::ReferrerPolicy::NoReferrerWhenDowngrade:
241         outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Default, m_resourceRequest.url(), outgoingReferrer);
242         break;
243     case FetchOptions::ReferrerPolicy::NoReferrer:
244         outgoingReferrer = String();
245         break;
246     case FetchOptions::ReferrerPolicy::Origin:
247         outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Origin, m_resourceRequest.url(), outgoingReferrer);
248         break;
249     case FetchOptions::ReferrerPolicy::OriginWhenCrossOrigin:
250         if (isRequestCrossOrigin(m_origin.get(), m_resourceRequest.url(), m_options))
251             outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Origin, m_resourceRequest.url(), outgoingReferrer);
252         break;
253     case FetchOptions::ReferrerPolicy::UnsafeUrl:
254         break;
255     };
256
257     if (outgoingReferrer.isEmpty())
258         m_resourceRequest.clearHTTPReferrer();
259     else
260         m_resourceRequest.setHTTPReferrer(outgoingReferrer);
261     FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin);
262
263     frameLoader.applyUserAgent(m_resourceRequest);
264 }
265
266 bool isRequestCrossOrigin(SecurityOrigin* origin, const URL& requestURL, const ResourceLoaderOptions& options)
267 {
268     if (!origin)
269         return false;
270
271     // 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.
272     if (options.mode == FetchOptions::Mode::SameOrigin)
273         return false;
274
275     // FIXME: We should remove options.sameOriginDataURLFlag once https://github.com/whatwg/fetch/issues/393 is fixed.
276     if (requestURL.protocolIsData() && options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set)
277         return false;
278
279     return !origin->canRequest(requestURL);
280 }
281
282 } // namespace WebCore