CachedResourceLoader should set headers of the HTTP request prior checking for the...
[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, 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     m_options.mode = FetchOptions::Mode::Cors;
95     m_options.credentials = equalLettersIgnoringASCIICase(mode, "use-credentials") ? FetchOptions::Credentials::Include : FetchOptions::Credentials::SameOrigin;
96     m_options.allowCredentials = equalLettersIgnoringASCIICase(mode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials;
97
98     WebCore::updateRequestForAccessControl(m_resourceRequest, *document.securityOrigin(), m_options.allowCredentials);
99 }
100
101 void CachedResourceRequest::updateForAccessControl(Document& document)
102 {
103     ASSERT(m_options.mode == FetchOptions::Mode::Cors);
104     ASSERT(document.securityOrigin());
105
106     m_origin = document.securityOrigin();
107     WebCore::updateRequestForAccessControl(m_resourceRequest, *m_origin, m_options.allowCredentials);
108 }
109
110 void upgradeInsecureResourceRequestIfNeeded(ResourceRequest& request, Document& document)
111 {
112     URL url = request.url();
113
114     ASSERT(document.contentSecurityPolicy());
115     document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(url, ContentSecurityPolicy::InsecureRequestType::Load);
116
117     if (url == request.url())
118         return;
119
120     request.setURL(url);
121 }
122
123 void CachedResourceRequest::upgradeInsecureRequestIfNeeded(Document& document)
124 {
125     upgradeInsecureResourceRequestIfNeeded(m_resourceRequest, document);
126 }
127
128 #if ENABLE(CACHE_PARTITIONING)
129 void CachedResourceRequest::setDomainForCachePartition(Document& document)
130 {
131     ASSERT(document.topOrigin());
132     m_resourceRequest.setDomainForCachePartition(document.topOrigin()->domainForCachePartition());
133 }
134 #endif
135
136 static inline String acceptHeaderValueFromType(CachedResource::Type type)
137 {
138     switch (type) {
139     case CachedResource::Type::MainResource:
140         return ASCIILiteral("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
141     case CachedResource::Type::ImageResource:
142         return ASCIILiteral("image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5");
143     case CachedResource::Type::CSSStyleSheet:
144         return ASCIILiteral("text/css,*/*;q=0.1");
145     case CachedResource::Type::SVGDocumentResource:
146         return ASCIILiteral("image/svg+xml");
147 #if ENABLE(XSLT)
148     case CachedResource::Type::XSLStyleSheet:
149         // FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example.
150         return ASCIILiteral("text/xml,application/xml,application/xhtml+xml,text/xsl,application/rss+xml,application/atom+xml");
151 #endif
152     default:
153         return ASCIILiteral("*/*");
154     }
155 }
156
157 void CachedResourceRequest::setAcceptHeaderIfNone(CachedResource::Type type)
158 {
159     if (!m_resourceRequest.hasHTTPHeader(HTTPHeaderName::Accept))
160         m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::Accept, acceptHeaderValueFromType(type));
161 }
162
163 void CachedResourceRequest::updateAccordingCacheMode()
164 {
165     if (m_options.cache == FetchOptions::Cache::Default
166         && (m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfModifiedSince)
167             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfNoneMatch)
168             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfUnmodifiedSince)
169             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfMatch)
170             || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfRange)))
171         m_options.cache = FetchOptions::Cache::NoStore;
172
173     switch (m_options.cache) {
174     case FetchOptions::Cache::NoCache:
175         m_resourceRequest.setCachePolicy(RefreshAnyCacheData);
176         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::maxAge0());
177         break;
178     case FetchOptions::Cache::NoStore:
179         m_options.cachingPolicy = CachingPolicy::DisallowCaching;
180         m_resourceRequest.setCachePolicy(DoNotUseAnyCache);
181         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
182         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
183         break;
184     case FetchOptions::Cache::Reload:
185         m_resourceRequest.setCachePolicy(ReloadIgnoringCacheData);
186         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
187         m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
188         break;
189     case FetchOptions::Cache::Default:
190         break;
191     case FetchOptions::Cache::ForceCache:
192         m_resourceRequest.setCachePolicy(ReturnCacheDataElseLoad);
193         break;
194     case FetchOptions::Cache::OnlyIfCached:
195         m_resourceRequest.setCachePolicy(ReturnCacheDataDontLoad);
196         break;
197     }
198 }
199
200 void CachedResourceRequest::removeFragmentIdentifierIfNeeded()
201 {
202     URL url = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url());
203     if (url.string() != m_resourceRequest.url())
204         m_resourceRequest.setURL(url);
205 }
206
207 #if ENABLE(CONTENT_EXTENSIONS)
208 void CachedResourceRequest::applyBlockedStatus(const ContentExtensions::BlockedStatus& blockedStatus)
209 {
210     ContentExtensions::applyBlockedStatusToRequest(blockedStatus, m_resourceRequest);
211 }
212 #endif
213
214 void CachedResourceRequest::updateReferrerOriginAndUserAgentHeaders(FrameLoader& frameLoader, ReferrerPolicy defaultPolicy)
215 {
216     // Implementing step 7 to 9 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
217
218     String outgoingOrigin;
219     String outgoingReferrer = m_resourceRequest.httpReferrer();
220     if (!outgoingReferrer.isNull())
221         outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
222     else {
223         outgoingReferrer = frameLoader.outgoingReferrer();
224         outgoingOrigin = frameLoader.outgoingOrigin();
225     }
226
227     // FIXME: Refactor SecurityPolicy::generateReferrerHeader to align with new terminology used in https://w3c.github.io/webappsec-referrer-policy.
228     switch (m_options.referrerPolicy) {
229     case FetchOptions::ReferrerPolicy::EmptyString: {
230         outgoingReferrer = SecurityPolicy::generateReferrerHeader(defaultPolicy, m_resourceRequest.url(), outgoingReferrer);
231         break; }
232     case FetchOptions::ReferrerPolicy::NoReferrerWhenDowngrade:
233         outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Default, m_resourceRequest.url(), outgoingReferrer);
234         break;
235     case FetchOptions::ReferrerPolicy::NoReferrer:
236         outgoingReferrer = String();
237         break;
238     case FetchOptions::ReferrerPolicy::Origin:
239         outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Origin, m_resourceRequest.url(), outgoingReferrer);
240         break;
241     case FetchOptions::ReferrerPolicy::OriginWhenCrossOrigin:
242         if (isRequestCrossOrigin(m_origin.get(), m_resourceRequest.url(), m_options))
243             outgoingReferrer = SecurityPolicy::generateReferrerHeader(ReferrerPolicy::Origin, m_resourceRequest.url(), outgoingReferrer);
244         break;
245     case FetchOptions::ReferrerPolicy::UnsafeUrl:
246         break;
247     };
248
249     if (outgoingReferrer.isEmpty())
250         m_resourceRequest.clearHTTPReferrer();
251     else
252         m_resourceRequest.setHTTPReferrer(outgoingReferrer);
253     FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin);
254
255     frameLoader.applyUserAgent(m_resourceRequest);
256 }
257
258 bool isRequestCrossOrigin(SecurityOrigin* origin, const URL& requestURL, const ResourceLoaderOptions& options)
259 {
260     if (!origin)
261         return false;
262
263     // 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.
264     if (options.mode == FetchOptions::Mode::SameOrigin)
265         return false;
266
267     // FIXME: We should remove options.sameOriginDataURLFlag once https://github.com/whatwg/fetch/issues/393 is fixed.
268     if (requestURL.protocolIsData() && options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set)
269         return false;
270
271     return !origin->canRequest(requestURL);
272 }
273
274 } // namespace WebCore