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 "ImageDecoder.h" |
37 | #include "MemoryCache.h" |
38 | #include "ServiceWorkerRegistrationData.h" |
39 | #include <wtf/NeverDestroyed.h> |
40 | |
41 | namespace WebCore { |
42 | |
43 | CachedResourceRequest::CachedResourceRequest(ResourceRequest&& resourceRequest, const ResourceLoaderOptions& options, Optional<ResourceLoadPriority> priority, String&& charset) |
44 | : m_resourceRequest(WTFMove(resourceRequest)) |
45 | , m_charset(WTFMove(charset)) |
46 | , m_options(options) |
47 | , m_priority(priority) |
48 | , m_fragmentIdentifier(splitFragmentIdentifierFromRequestURL(m_resourceRequest)) |
49 | { |
50 | } |
51 | |
52 | String CachedResourceRequest::splitFragmentIdentifierFromRequestURL(ResourceRequest& request) |
53 | { |
54 | if (!MemoryCache::shouldRemoveFragmentIdentifier(request.url())) |
55 | return { }; |
56 | URL url = request.url(); |
57 | String fragmentIdentifier = url.fragmentIdentifier(); |
58 | url.removeFragmentIdentifier(); |
59 | request.setURL(url); |
60 | return fragmentIdentifier; |
61 | } |
62 | |
63 | void CachedResourceRequest::setInitiator(Element& element) |
64 | { |
65 | ASSERT(!m_initiatorElement); |
66 | ASSERT(m_initiatorName.isEmpty()); |
67 | m_initiatorElement = &element; |
68 | } |
69 | |
70 | void CachedResourceRequest::setInitiator(const AtomicString& name) |
71 | { |
72 | ASSERT(!m_initiatorElement); |
73 | ASSERT(m_initiatorName.isEmpty()); |
74 | m_initiatorName = name; |
75 | } |
76 | |
77 | const AtomicString& CachedResourceRequest::initiatorName() const |
78 | { |
79 | if (m_initiatorElement) |
80 | return m_initiatorElement->localName(); |
81 | if (!m_initiatorName.isEmpty()) |
82 | return m_initiatorName; |
83 | |
84 | static NeverDestroyed<AtomicString> defaultName("other" , AtomicString::ConstructFromLiteral); |
85 | return defaultName; |
86 | } |
87 | |
88 | void CachedResourceRequest::deprecatedSetAsPotentiallyCrossOrigin(const String& mode, Document& document) |
89 | { |
90 | ASSERT(m_options.mode == FetchOptions::Mode::NoCors); |
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.storedCredentialsPolicy = credentials == FetchOptions::Credentials::Include ? StoredCredentialsPolicy::Use : StoredCredentialsPolicy::DoNotUse; |
104 | updateRequestForAccessControl(m_resourceRequest, document.securityOrigin(), m_options.storedCredentialsPolicy); |
105 | } |
106 | |
107 | void CachedResourceRequest::updateForAccessControl(Document& document) |
108 | { |
109 | ASSERT(m_options.mode == FetchOptions::Mode::Cors); |
110 | |
111 | m_origin = &document.securityOrigin(); |
112 | updateRequestForAccessControl(m_resourceRequest, *m_origin, m_options.storedCredentialsPolicy); |
113 | } |
114 | |
115 | void upgradeInsecureResourceRequestIfNeeded(ResourceRequest& request, Document& document) |
116 | { |
117 | URL url = request.url(); |
118 | |
119 | ASSERT(document.contentSecurityPolicy()); |
120 | document.contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(url, ContentSecurityPolicy::InsecureRequestType::Load); |
121 | |
122 | if (url == request.url()) |
123 | return; |
124 | |
125 | request.setURL(url); |
126 | } |
127 | |
128 | void CachedResourceRequest::upgradeInsecureRequestIfNeeded(Document& document) |
129 | { |
130 | upgradeInsecureResourceRequestIfNeeded(m_resourceRequest, document); |
131 | } |
132 | |
133 | void CachedResourceRequest::setDomainForCachePartition(Document& document) |
134 | { |
135 | m_resourceRequest.setDomainForCachePartition(document.domainForCachePartition()); |
136 | } |
137 | |
138 | void CachedResourceRequest::setDomainForCachePartition(const String& domain) |
139 | { |
140 | m_resourceRequest.setDomainForCachePartition(domain); |
141 | } |
142 | |
143 | static inline String (CachedResource::Type type) |
144 | { |
145 | switch (type) { |
146 | case CachedResource::Type::MainResource: |
147 | return "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"_s ; |
148 | case CachedResource::Type::ImageResource: |
149 | if (ImageDecoder::supportsMediaType(ImageDecoder::MediaType::Video)) |
150 | return "image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5"_s ; |
151 | return "image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5"_s ; |
152 | case CachedResource::Type::CSSStyleSheet: |
153 | return "text/css,*/*;q=0.1"_s ; |
154 | case CachedResource::Type::SVGDocumentResource: |
155 | return "image/svg+xml"_s ; |
156 | #if ENABLE(XSLT) |
157 | case CachedResource::Type::XSLStyleSheet: |
158 | // FIXME: This should accept more general xml formats */*+xml, image/svg+xml for example. |
159 | return "text/xml,application/xml,application/xhtml+xml,text/xsl,application/rss+xml,application/atom+xml"_s ; |
160 | #endif |
161 | default: |
162 | return "*/*"_s ; |
163 | } |
164 | } |
165 | |
166 | void CachedResourceRequest::(CachedResource::Type type) |
167 | { |
168 | if (!m_resourceRequest.hasHTTPHeader(HTTPHeaderName::Accept)) |
169 | m_resourceRequest.setHTTPHeaderField(HTTPHeaderName::Accept, acceptHeaderValueFromType(type)); |
170 | } |
171 | |
172 | void CachedResourceRequest::updateAccordingCacheMode() |
173 | { |
174 | if (m_options.cache == FetchOptions::Cache::Default |
175 | && (m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfModifiedSince) |
176 | || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfNoneMatch) |
177 | || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfUnmodifiedSince) |
178 | || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfMatch) |
179 | || m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::IfRange))) |
180 | m_options.cache = FetchOptions::Cache::NoStore; |
181 | |
182 | switch (m_options.cache) { |
183 | case FetchOptions::Cache::NoCache: |
184 | m_resourceRequest.setCachePolicy(ResourceRequestCachePolicy::RefreshAnyCacheData); |
185 | m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::maxAge0()); |
186 | break; |
187 | case FetchOptions::Cache::NoStore: |
188 | m_options.cachingPolicy = CachingPolicy::DisallowCaching; |
189 | m_resourceRequest.setCachePolicy(ResourceRequestCachePolicy::DoNotUseAnyCache); |
190 | m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache()); |
191 | m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache()); |
192 | break; |
193 | case FetchOptions::Cache::Reload: |
194 | m_resourceRequest.setCachePolicy(ResourceRequestCachePolicy::ReloadIgnoringCacheData); |
195 | m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache()); |
196 | m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache()); |
197 | break; |
198 | case FetchOptions::Cache::Default: |
199 | break; |
200 | case FetchOptions::Cache::ForceCache: |
201 | m_resourceRequest.setCachePolicy(ResourceRequestCachePolicy::ReturnCacheDataElseLoad); |
202 | break; |
203 | case FetchOptions::Cache::OnlyIfCached: |
204 | m_resourceRequest.setCachePolicy(ResourceRequestCachePolicy::ReturnCacheDataDontLoad); |
205 | break; |
206 | } |
207 | } |
208 | |
209 | void CachedResourceRequest::() |
210 | { |
211 | if (!m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::Range)) |
212 | return; |
213 | |
214 | // FIXME: rdar://problem/40879225. Media engines triggering the load should not set this Accept-Encoding header. |
215 | ASSERT(!m_resourceRequest.hasHTTPHeaderField(HTTPHeaderName::AcceptEncoding) || m_options.destination == FetchOptions::Destination::Audio || m_options.destination == FetchOptions::Destination::Video); |
216 | |
217 | m_resourceRequest.addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::AcceptEncoding, "identity"_s ); |
218 | } |
219 | |
220 | void CachedResourceRequest::removeFragmentIdentifierIfNeeded() |
221 | { |
222 | URL url = MemoryCache::removeFragmentIdentifierIfNeeded(m_resourceRequest.url()); |
223 | if (url.string() != m_resourceRequest.url()) |
224 | m_resourceRequest.setURL(url); |
225 | } |
226 | |
227 | #if ENABLE(CONTENT_EXTENSIONS) |
228 | |
229 | void CachedResourceRequest::applyResults(ContentRuleListResults&& results, Page* page) |
230 | { |
231 | ContentExtensions::applyResultsToRequest(WTFMove(results), page, m_resourceRequest); |
232 | } |
233 | |
234 | #endif |
235 | |
236 | void CachedResourceRequest::updateReferrerPolicy(ReferrerPolicy defaultPolicy) |
237 | { |
238 | if (m_options.referrerPolicy == ReferrerPolicy::EmptyString) |
239 | m_options.referrerPolicy = defaultPolicy; |
240 | } |
241 | |
242 | void CachedResourceRequest::updateReferrerOriginAndUserAgentHeaders(FrameLoader& frameLoader) |
243 | { |
244 | // Implementing step 9 to 11 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch as of 16 March 2018 |
245 | String outgoingReferrer = frameLoader.outgoingReferrer(); |
246 | String outgoingOrigin = frameLoader.outgoingOrigin(); |
247 | if (m_resourceRequest.hasHTTPReferrer()) { |
248 | outgoingReferrer = m_resourceRequest.httpReferrer(); |
249 | outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString(); |
250 | } |
251 | updateRequestReferrer(m_resourceRequest, m_options.referrerPolicy, outgoingReferrer); |
252 | |
253 | FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin); |
254 | |
255 | frameLoader.applyUserAgentIfNeeded(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 | void CachedResourceRequest::setDestinationIfNotSet(FetchOptions::Destination destination) |
275 | { |
276 | if (m_options.destination != FetchOptions::Destination::EmptyString) |
277 | return; |
278 | m_options.destination = destination; |
279 | } |
280 | |
281 | #if ENABLE(SERVICE_WORKER) |
282 | void CachedResourceRequest::setClientIdentifierIfNeeded(DocumentIdentifier clientIdentifier) |
283 | { |
284 | if (!m_options.clientIdentifier) |
285 | m_options.clientIdentifier = clientIdentifier; |
286 | } |
287 | |
288 | void CachedResourceRequest::setSelectedServiceWorkerRegistrationIdentifierIfNeeded(ServiceWorkerRegistrationIdentifier identifier) |
289 | { |
290 | if (isNonSubresourceRequest(m_options.destination)) |
291 | return; |
292 | if (isPotentialNavigationOrSubresourceRequest(m_options.destination)) |
293 | return; |
294 | |
295 | if (m_options.serviceWorkersMode == ServiceWorkersMode::None) |
296 | return; |
297 | if (m_options.serviceWorkerRegistrationIdentifier) |
298 | return; |
299 | |
300 | m_options.serviceWorkerRegistrationIdentifier = identifier; |
301 | } |
302 | |
303 | void CachedResourceRequest::setNavigationServiceWorkerRegistrationData(const Optional<ServiceWorkerRegistrationData>& data) |
304 | { |
305 | if (!data || !data->activeWorker) { |
306 | m_options.serviceWorkersMode = ServiceWorkersMode::None; |
307 | return; |
308 | } |
309 | m_options.serviceWorkerRegistrationIdentifier = data->identifier; |
310 | } |
311 | #endif |
312 | |
313 | } // namespace WebCore |
314 | |