1 | /* |
2 | Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de) |
3 | Copyright (C) 2001 Dirk Mueller (mueller@kde.org) |
4 | Copyright (C) 2002 Waldo Bastian (bastian@kde.org) |
5 | Copyright (C) 2004-2017 Apple Inc. All rights reserved. |
6 | Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ |
7 | |
8 | This library is free software; you can redistribute it and/or |
9 | modify it under the terms of the GNU Library General Public |
10 | License as published by the Free Software Foundation; either |
11 | version 2 of the License, or (at your option) any later version. |
12 | |
13 | This library is distributed in the hope that it will be useful, |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | Library General Public License for more details. |
17 | |
18 | You should have received a copy of the GNU Library General Public License |
19 | along with this library; see the file COPYING.LIB. If not, write to |
20 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
21 | Boston, MA 02110-1301, USA. |
22 | |
23 | This class provides all functionality needed for loading images, style sheets and html |
24 | pages from the web. It has a memory cache for these objects. |
25 | */ |
26 | |
27 | #include "config.h" |
28 | #include "CachedResourceLoader.h" |
29 | |
30 | #include "CachedCSSStyleSheet.h" |
31 | #include "CachedFont.h" |
32 | #include "CachedImage.h" |
33 | #include "CachedRawResource.h" |
34 | #include "CachedResourceRequest.h" |
35 | #include "CachedSVGDocument.h" |
36 | #include "CachedSVGFont.h" |
37 | #include "CachedScript.h" |
38 | #include "CachedXSLStyleSheet.h" |
39 | #include "Chrome.h" |
40 | #include "ChromeClient.h" |
41 | #include "ContentExtensionError.h" |
42 | #include "ContentExtensionRule.h" |
43 | #include "ContentRuleListResults.h" |
44 | #include "ContentSecurityPolicy.h" |
45 | #include "CrossOriginAccessControl.h" |
46 | #include "CustomHeaderFields.h" |
47 | #include "DOMWindow.h" |
48 | #include "DiagnosticLoggingClient.h" |
49 | #include "DiagnosticLoggingKeys.h" |
50 | #include "Document.h" |
51 | #include "DocumentLoader.h" |
52 | #include "Frame.h" |
53 | #include "FrameLoader.h" |
54 | #include "FrameLoaderClient.h" |
55 | #include "HTMLElement.h" |
56 | #include "HTMLFrameOwnerElement.h" |
57 | #include "HTTPHeaderField.h" |
58 | #include "LoaderStrategy.h" |
59 | #include "LocalizedStrings.h" |
60 | #include "Logging.h" |
61 | #include "MemoryCache.h" |
62 | #include "Page.h" |
63 | #include "PingLoader.h" |
64 | #include "PlatformStrategies.h" |
65 | #include "RenderElement.h" |
66 | #include "ResourceLoadInfo.h" |
67 | #include "ResourceTiming.h" |
68 | #include "RuntimeEnabledFeatures.h" |
69 | #include "ScriptController.h" |
70 | #include "SecurityOrigin.h" |
71 | #include "SecurityPolicy.h" |
72 | #include "ServiceWorker.h" |
73 | #include "Settings.h" |
74 | #include "StyleSheetContents.h" |
75 | #include "SubresourceLoader.h" |
76 | #include "UserContentController.h" |
77 | #include "UserStyleSheet.h" |
78 | #include <pal/SessionID.h> |
79 | #include <wtf/text/CString.h> |
80 | #include <wtf/text/WTFString.h> |
81 | |
82 | #if ENABLE(APPLICATION_MANIFEST) |
83 | #include "CachedApplicationManifest.h" |
84 | #endif |
85 | |
86 | #if ENABLE(VIDEO_TRACK) |
87 | #include "CachedTextTrack.h" |
88 | #endif |
89 | |
90 | #undef RELEASE_LOG_IF_ALLOWED |
91 | #define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - CachedResourceLoader::" fmt, this, ##__VA_ARGS__) |
92 | |
93 | namespace WebCore { |
94 | |
95 | // Timeout for link preloads to be used after window.onload |
96 | static const Seconds unusedPreloadTimeout { 3_s }; |
97 | |
98 | template <typename T, typename U> |
99 | static inline ResourceErrorOr<CachedResourceHandle<T>> castCachedResourceTo(ResourceErrorOr<CachedResourceHandle<U>>&& cachedResource) |
100 | { |
101 | if (cachedResource) |
102 | return CachedResourceHandle<T> { static_cast<T*>(cachedResource.value().get()) }; |
103 | return makeUnexpected(cachedResource.error()); |
104 | } |
105 | |
106 | static CachedResource* createResource(CachedResource::Type type, CachedResourceRequest&& request, const PAL::SessionID& sessionID, const CookieJar* cookieJar) |
107 | { |
108 | switch (type) { |
109 | case CachedResource::Type::ImageResource: |
110 | return new CachedImage(WTFMove(request), sessionID, cookieJar); |
111 | case CachedResource::Type::CSSStyleSheet: |
112 | return new CachedCSSStyleSheet(WTFMove(request), sessionID, cookieJar); |
113 | case CachedResource::Type::Script: |
114 | return new CachedScript(WTFMove(request), sessionID, cookieJar); |
115 | case CachedResource::Type::SVGDocumentResource: |
116 | return new CachedSVGDocument(WTFMove(request), sessionID, cookieJar); |
117 | #if ENABLE(SVG_FONTS) |
118 | case CachedResource::Type::SVGFontResource: |
119 | return new CachedSVGFont(WTFMove(request), sessionID, cookieJar); |
120 | #endif |
121 | case CachedResource::Type::FontResource: |
122 | return new CachedFont(WTFMove(request), sessionID, cookieJar); |
123 | case CachedResource::Type::Beacon: |
124 | case CachedResource::Type::Ping: |
125 | case CachedResource::Type::MediaResource: |
126 | case CachedResource::Type::RawResource: |
127 | case CachedResource::Type::Icon: |
128 | case CachedResource::Type::MainResource: |
129 | return new CachedRawResource(WTFMove(request), type, sessionID, cookieJar); |
130 | #if ENABLE(XSLT) |
131 | case CachedResource::Type::XSLStyleSheet: |
132 | return new CachedXSLStyleSheet(WTFMove(request), sessionID, cookieJar); |
133 | #endif |
134 | case CachedResource::Type::LinkPrefetch: |
135 | return new CachedResource(WTFMove(request), CachedResource::Type::LinkPrefetch, sessionID, cookieJar); |
136 | #if ENABLE(VIDEO_TRACK) |
137 | case CachedResource::Type::TextTrackResource: |
138 | return new CachedTextTrack(WTFMove(request), sessionID, cookieJar); |
139 | #endif |
140 | #if ENABLE(APPLICATION_MANIFEST) |
141 | case CachedResource::Type::ApplicationManifest: |
142 | return new CachedApplicationManifest(WTFMove(request), sessionID, cookieJar); |
143 | #endif |
144 | } |
145 | ASSERT_NOT_REACHED(); |
146 | return nullptr; |
147 | } |
148 | |
149 | CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader) |
150 | : m_document(nullptr) |
151 | , m_documentLoader(documentLoader) |
152 | , m_requestCount(0) |
153 | , m_unusedPreloadsTimer(*this, &CachedResourceLoader::warnUnusedPreloads) |
154 | , m_garbageCollectDocumentResourcesTimer(*this, &CachedResourceLoader::garbageCollectDocumentResources) |
155 | , m_autoLoadImages(true) |
156 | , m_imagesEnabled(true) |
157 | , m_allowStaleResources(false) |
158 | { |
159 | } |
160 | |
161 | CachedResourceLoader::~CachedResourceLoader() |
162 | { |
163 | m_documentLoader = nullptr; |
164 | m_document = nullptr; |
165 | |
166 | clearPreloads(ClearPreloadsMode::ClearAllPreloads); |
167 | |
168 | // Make sure no requests still point to this CachedResourceLoader |
169 | ASSERT(m_requestCount == 0); |
170 | m_unusedPreloadsTimer.stop(); |
171 | } |
172 | |
173 | CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const |
174 | { |
175 | ASSERT(!resourceURL.isNull()); |
176 | return cachedResource(MemoryCache::removeFragmentIdentifierIfNeeded(m_document->completeURL(resourceURL))); |
177 | } |
178 | |
179 | CachedResource* CachedResourceLoader::cachedResource(const URL& url) const |
180 | { |
181 | ASSERT(!MemoryCache::shouldRemoveFragmentIdentifier(url)); |
182 | return m_documentResources.get(url).get(); |
183 | } |
184 | |
185 | Frame* CachedResourceLoader::frame() const |
186 | { |
187 | return m_documentLoader ? m_documentLoader->frame() : nullptr; |
188 | } |
189 | |
190 | PAL::SessionID CachedResourceLoader::sessionID() const |
191 | { |
192 | auto sessionID = PAL::SessionID::defaultSessionID(); |
193 | if (auto* frame = this->frame()) { |
194 | if (auto* page = frame->page()) |
195 | sessionID = page->sessionID(); |
196 | } |
197 | return sessionID; |
198 | } |
199 | |
200 | ResourceErrorOr<CachedResourceHandle<CachedImage>> CachedResourceLoader::requestImage(CachedResourceRequest&& request) |
201 | { |
202 | if (Frame* frame = this->frame()) { |
203 | if (frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::PageDismissalType::None) { |
204 | if (Document* document = frame->document()) |
205 | request.upgradeInsecureRequestIfNeeded(*document); |
206 | URL requestURL = request.resourceRequest().url(); |
207 | if (requestURL.isValid() && canRequest(CachedResource::Type::ImageResource, requestURL, request, ForPreload::No)) |
208 | PingLoader::loadImage(*frame, requestURL); |
209 | return CachedResourceHandle<CachedImage> { }; |
210 | } |
211 | } |
212 | |
213 | auto defer = clientDefersImage(request.resourceRequest().url()) ? DeferOption::DeferredByClient : DeferOption::NoDefer; |
214 | return castCachedResourceTo<CachedImage>(requestResource(CachedResource::Type::ImageResource, WTFMove(request), ForPreload::No, defer)); |
215 | } |
216 | |
217 | ResourceErrorOr<CachedResourceHandle<CachedFont>> CachedResourceLoader::requestFont(CachedResourceRequest&& request, bool isSVG) |
218 | { |
219 | #if ENABLE(SVG_FONTS) |
220 | if (isSVG) |
221 | return castCachedResourceTo<CachedFont>(requestResource(CachedResource::Type::SVGFontResource, WTFMove(request))); |
222 | #else |
223 | UNUSED_PARAM(isSVG); |
224 | #endif |
225 | return castCachedResourceTo<CachedFont>(requestResource(CachedResource::Type::FontResource, WTFMove(request))); |
226 | } |
227 | |
228 | #if ENABLE(VIDEO_TRACK) |
229 | ResourceErrorOr<CachedResourceHandle<CachedTextTrack>> CachedResourceLoader::requestTextTrack(CachedResourceRequest&& request) |
230 | { |
231 | return castCachedResourceTo<CachedTextTrack>(requestResource(CachedResource::Type::TextTrackResource, WTFMove(request))); |
232 | } |
233 | #endif |
234 | |
235 | ResourceErrorOr<CachedResourceHandle<CachedCSSStyleSheet>> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest&& request) |
236 | { |
237 | return castCachedResourceTo<CachedCSSStyleSheet>(requestResource(CachedResource::Type::CSSStyleSheet, WTFMove(request))); |
238 | } |
239 | |
240 | CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(Page& page, CachedResourceRequest&& request) |
241 | { |
242 | request.setDestinationIfNotSet(FetchOptions::Destination::Style); |
243 | |
244 | ASSERT(document()); |
245 | request.setDomainForCachePartition(*document()); |
246 | |
247 | auto& memoryCache = MemoryCache::singleton(); |
248 | if (request.allowsCaching()) { |
249 | if (CachedResource* existing = memoryCache.resourceForRequest(request.resourceRequest(), sessionID())) { |
250 | if (is<CachedCSSStyleSheet>(*existing)) |
251 | return downcast<CachedCSSStyleSheet>(existing); |
252 | memoryCache.remove(*existing); |
253 | } |
254 | } |
255 | |
256 | request.removeFragmentIdentifierIfNeeded(); |
257 | |
258 | CachedResourceHandle<CachedCSSStyleSheet> userSheet = new CachedCSSStyleSheet(WTFMove(request), page.sessionID(), &page.cookieJar()); |
259 | |
260 | if (userSheet->allowsCaching()) |
261 | memoryCache.add(*userSheet); |
262 | |
263 | userSheet->load(*this); |
264 | return userSheet; |
265 | } |
266 | |
267 | ResourceErrorOr<CachedResourceHandle<CachedScript>> CachedResourceLoader::requestScript(CachedResourceRequest&& request) |
268 | { |
269 | return castCachedResourceTo<CachedScript>(requestResource(CachedResource::Type::Script, WTFMove(request))); |
270 | } |
271 | |
272 | #if ENABLE(XSLT) |
273 | ResourceErrorOr<CachedResourceHandle<CachedXSLStyleSheet>> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest&& request) |
274 | { |
275 | return castCachedResourceTo<CachedXSLStyleSheet>(requestResource(CachedResource::Type::XSLStyleSheet, WTFMove(request))); |
276 | } |
277 | #endif |
278 | |
279 | ResourceErrorOr<CachedResourceHandle<CachedSVGDocument>> CachedResourceLoader::requestSVGDocument(CachedResourceRequest&& request) |
280 | { |
281 | return castCachedResourceTo<CachedSVGDocument>(requestResource(CachedResource::Type::SVGDocumentResource, WTFMove(request))); |
282 | } |
283 | |
284 | ResourceErrorOr<CachedResourceHandle<CachedResource>> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest&& request) |
285 | { |
286 | ASSERT(frame()); |
287 | ASSERT(type == CachedResource::Type::LinkPrefetch); |
288 | return requestResource(type, WTFMove(request)); |
289 | } |
290 | |
291 | ResourceErrorOr<CachedResourceHandle<CachedRawResource>> CachedResourceLoader::requestMedia(CachedResourceRequest&& request) |
292 | { |
293 | // FIXME: Assert request.options().destination is FetchOptions::Destination::{Audio, Video}. |
294 | return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::MediaResource, WTFMove(request))); |
295 | } |
296 | |
297 | ResourceErrorOr<CachedResourceHandle<CachedRawResource>> CachedResourceLoader::requestIcon(CachedResourceRequest&& request) |
298 | { |
299 | return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::Icon, WTFMove(request))); |
300 | } |
301 | |
302 | ResourceErrorOr<CachedResourceHandle<CachedRawResource>> CachedResourceLoader::requestRawResource(CachedResourceRequest&& request) |
303 | { |
304 | return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::RawResource, WTFMove(request))); |
305 | } |
306 | |
307 | ResourceErrorOr<CachedResourceHandle<CachedRawResource>> CachedResourceLoader::requestBeaconResource(CachedResourceRequest&& request) |
308 | { |
309 | ASSERT(request.options().destination == FetchOptions::Destination::EmptyString); |
310 | return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::Beacon, WTFMove(request))); |
311 | } |
312 | |
313 | ResourceErrorOr<CachedResourceHandle<CachedRawResource>> CachedResourceLoader::requestPingResource(CachedResourceRequest&& request) |
314 | { |
315 | ASSERT(request.options().destination == FetchOptions::Destination::EmptyString); |
316 | return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::Ping, WTFMove(request))); |
317 | } |
318 | |
319 | ResourceErrorOr<CachedResourceHandle<CachedRawResource>> CachedResourceLoader::requestMainResource(CachedResourceRequest&& request) |
320 | { |
321 | return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::MainResource, WTFMove(request))); |
322 | } |
323 | |
324 | #if ENABLE(APPLICATION_MANIFEST) |
325 | ResourceErrorOr<CachedResourceHandle<CachedApplicationManifest>> CachedResourceLoader::requestApplicationManifest(CachedResourceRequest&& request) |
326 | { |
327 | return castCachedResourceTo<CachedApplicationManifest>(requestResource(CachedResource::Type::ApplicationManifest, WTFMove(request))); |
328 | } |
329 | #endif // ENABLE(APPLICATION_MANIFEST) |
330 | |
331 | static MixedContentChecker::ContentType contentTypeFromResourceType(CachedResource::Type type) |
332 | { |
333 | switch (type) { |
334 | // https://w3c.github.io/webappsec-mixed-content/#category-optionally-blockable |
335 | // Editor's Draft, 11 February 2016 |
336 | // 3.1. Optionally-blockable Content |
337 | case CachedResource::Type::ImageResource: |
338 | case CachedResource::Type::MediaResource: |
339 | return MixedContentChecker::ContentType::ActiveCanWarn; |
340 | |
341 | case CachedResource::Type::CSSStyleSheet: |
342 | case CachedResource::Type::Script: |
343 | case CachedResource::Type::FontResource: |
344 | return MixedContentChecker::ContentType::Active; |
345 | |
346 | #if ENABLE(SVG_FONTS) |
347 | case CachedResource::Type::SVGFontResource: |
348 | return MixedContentChecker::ContentType::Active; |
349 | #endif |
350 | |
351 | case CachedResource::Type::Beacon: |
352 | case CachedResource::Type::Ping: |
353 | case CachedResource::Type::RawResource: |
354 | case CachedResource::Type::Icon: |
355 | case CachedResource::Type::SVGDocumentResource: |
356 | return MixedContentChecker::ContentType::Active; |
357 | #if ENABLE(XSLT) |
358 | case CachedResource::Type::XSLStyleSheet: |
359 | return MixedContentChecker::ContentType::Active; |
360 | #endif |
361 | |
362 | case CachedResource::Type::LinkPrefetch: |
363 | return MixedContentChecker::ContentType::Active; |
364 | |
365 | #if ENABLE(VIDEO_TRACK) |
366 | case CachedResource::Type::TextTrackResource: |
367 | return MixedContentChecker::ContentType::Active; |
368 | #endif |
369 | #if ENABLE(APPLICATION_MANIFEST) |
370 | case CachedResource::Type::ApplicationManifest: |
371 | return MixedContentChecker::ContentType::Active; |
372 | #endif |
373 | default: |
374 | ASSERT_NOT_REACHED(); |
375 | return MixedContentChecker::ContentType::Active; |
376 | } |
377 | } |
378 | |
379 | bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const URL& url) const |
380 | { |
381 | if (!canRequestInContentDispositionAttachmentSandbox(type, url)) |
382 | return false; |
383 | |
384 | switch (type) { |
385 | case CachedResource::Type::Script: |
386 | #if ENABLE(XSLT) |
387 | case CachedResource::Type::XSLStyleSheet: |
388 | #endif |
389 | case CachedResource::Type::SVGDocumentResource: |
390 | case CachedResource::Type::CSSStyleSheet: |
391 | // These resource can inject script into the current document (Script, |
392 | // XSL) or exfiltrate the content of the current document (CSS). |
393 | if (Frame* frame = this->frame()) { |
394 | if (!frame->loader().mixedContentChecker().canRunInsecureContent(m_document->securityOrigin(), url)) |
395 | return false; |
396 | Frame& top = frame->tree().top(); |
397 | if (&top != frame && !top.loader().mixedContentChecker().canRunInsecureContent(top.document()->securityOrigin(), url)) |
398 | return false; |
399 | } |
400 | break; |
401 | #if ENABLE(VIDEO_TRACK) |
402 | case CachedResource::Type::TextTrackResource: |
403 | #endif |
404 | case CachedResource::Type::MediaResource: |
405 | case CachedResource::Type::RawResource: |
406 | case CachedResource::Type::Icon: |
407 | case CachedResource::Type::ImageResource: |
408 | #if ENABLE(SVG_FONTS) |
409 | case CachedResource::Type::SVGFontResource: |
410 | #endif |
411 | case CachedResource::Type::FontResource: { |
412 | // These resources can corrupt only the frame's pixels. |
413 | if (Frame* frame = this->frame()) { |
414 | if (!frame->loader().mixedContentChecker().canDisplayInsecureContent(m_document->securityOrigin(), contentTypeFromResourceType(type), url, MixedContentChecker::AlwaysDisplayInNonStrictMode::Yes)) |
415 | return false; |
416 | Frame& topFrame = frame->tree().top(); |
417 | if (!topFrame.loader().mixedContentChecker().canDisplayInsecureContent(topFrame.document()->securityOrigin(), contentTypeFromResourceType(type), url)) |
418 | return false; |
419 | } |
420 | break; |
421 | } |
422 | case CachedResource::Type::MainResource: |
423 | case CachedResource::Type::Beacon: |
424 | case CachedResource::Type::Ping: |
425 | case CachedResource::Type::LinkPrefetch: |
426 | // Prefetch cannot affect the current document. |
427 | #if ENABLE(APPLICATION_MANIFEST) |
428 | case CachedResource::Type::ApplicationManifest: |
429 | #endif |
430 | break; |
431 | } |
432 | return true; |
433 | } |
434 | |
435 | bool CachedResourceLoader::allowedByContentSecurityPolicy(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, ContentSecurityPolicy::RedirectResponseReceived redirectResponseReceived) const |
436 | { |
437 | if (options.contentSecurityPolicyImposition == ContentSecurityPolicyImposition::SkipPolicyCheck) |
438 | return true; |
439 | |
440 | ASSERT(m_document); |
441 | ASSERT(m_document->contentSecurityPolicy()); |
442 | |
443 | switch (type) { |
444 | #if ENABLE(XSLT) |
445 | case CachedResource::Type::XSLStyleSheet: |
446 | #endif |
447 | case CachedResource::Type::Script: |
448 | if (!m_document->contentSecurityPolicy()->allowScriptFromSource(url, redirectResponseReceived)) |
449 | return false; |
450 | break; |
451 | case CachedResource::Type::CSSStyleSheet: |
452 | if (!m_document->contentSecurityPolicy()->allowStyleFromSource(url, redirectResponseReceived)) |
453 | return false; |
454 | break; |
455 | case CachedResource::Type::SVGDocumentResource: |
456 | case CachedResource::Type::Icon: |
457 | case CachedResource::Type::ImageResource: |
458 | if (!m_document->contentSecurityPolicy()->allowImageFromSource(url, redirectResponseReceived)) |
459 | return false; |
460 | break; |
461 | #if ENABLE(SVG_FONTS) |
462 | case CachedResource::Type::SVGFontResource: |
463 | #endif |
464 | case CachedResource::Type::FontResource: |
465 | if (!m_document->contentSecurityPolicy()->allowFontFromSource(url, redirectResponseReceived)) |
466 | return false; |
467 | break; |
468 | case CachedResource::Type::MediaResource: |
469 | #if ENABLE(VIDEO_TRACK) |
470 | case CachedResource::Type::TextTrackResource: |
471 | #endif |
472 | if (!m_document->contentSecurityPolicy()->allowMediaFromSource(url, redirectResponseReceived)) |
473 | return false; |
474 | break; |
475 | case CachedResource::Type::Beacon: |
476 | case CachedResource::Type::Ping: |
477 | case CachedResource::Type::RawResource: |
478 | return true; |
479 | #if ENABLE(APPLICATION_MANIFEST) |
480 | case CachedResource::Type::ApplicationManifest: |
481 | if (!m_document->contentSecurityPolicy()->allowManifestFromSource(url, redirectResponseReceived)) |
482 | return false; |
483 | break; |
484 | #endif |
485 | default: |
486 | ASSERT_NOT_REACHED(); |
487 | } |
488 | |
489 | return true; |
490 | } |
491 | |
492 | static inline bool isSameOriginDataURL(const URL& url, const ResourceLoaderOptions& options) |
493 | { |
494 | // FIXME: Remove same-origin data URL flag since it was removed from fetch spec (https://github.com/whatwg/fetch/issues/381). |
495 | return url.protocolIsData() && options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set; |
496 | } |
497 | |
498 | // Security checks defined in https://fetch.spec.whatwg.org/#main-fetch step 2 and 5. |
499 | bool CachedResourceLoader::canRequest(CachedResource::Type type, const URL& url, const CachedResourceRequest& request, ForPreload forPreload) |
500 | { |
501 | auto& options = request.options(); |
502 | |
503 | if (document() && !document()->securityOrigin().canDisplay(url)) { |
504 | if (forPreload == ForPreload::No) |
505 | FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength()); |
506 | LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay" ); |
507 | return false; |
508 | } |
509 | |
510 | if (options.mode == FetchOptions::Mode::SameOrigin && !m_document->securityOrigin().canRequest(url) && !isSameOriginDataURL(url, options)) { |
511 | printAccessDeniedMessage(url); |
512 | return false; |
513 | } |
514 | |
515 | if (options.mode == FetchOptions::Mode::NoCors && options.redirect != FetchOptions::Redirect::Follow && type != CachedResource::Type::Ping) { |
516 | ASSERT(type != CachedResource::Type::MainResource); |
517 | frame()->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "No-Cors mode requires follow redirect mode"_s ); |
518 | return false; |
519 | } |
520 | |
521 | if (!allowedByContentSecurityPolicy(type, url, options, ContentSecurityPolicy::RedirectResponseReceived::No)) |
522 | return false; |
523 | |
524 | // SVG Images have unique security rules that prevent all subresource requests except for data urls. |
525 | if (type != CachedResource::Type::MainResource && frame() && frame()->page()) { |
526 | if (frame()->page()->chrome().client().isSVGImageChromeClient() && !url.protocolIsData()) |
527 | return false; |
528 | } |
529 | |
530 | // Last of all, check for insecure content. We do this last so that when folks block insecure content with a CSP policy, they don't get a warning. |
531 | // They'll still get a warning in the console about CSP blocking the load. |
532 | |
533 | // FIXME: Should we consider whether the request is for preload here? |
534 | if (!checkInsecureContent(type, url)) |
535 | return false; |
536 | |
537 | return true; |
538 | } |
539 | |
540 | // FIXME: Should we find a way to know whether the redirection is for a preload request like we do for CachedResourceLoader::canRequest? |
541 | bool CachedResourceLoader::canRequestAfterRedirection(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options) const |
542 | { |
543 | if (document() && !document()->securityOrigin().canDisplay(url)) { |
544 | FrameLoader::reportLocalLoadFailed(frame(), url.stringCenterEllipsizedToLength()); |
545 | LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay" ); |
546 | return false; |
547 | } |
548 | |
549 | // FIXME: According to https://fetch.spec.whatwg.org/#http-redirect-fetch, we should check that the URL is HTTP(s) except if in navigation mode. |
550 | // But we currently allow at least data URLs to be loaded. |
551 | |
552 | if (options.mode == FetchOptions::Mode::SameOrigin && !m_document->securityOrigin().canRequest(url)) { |
553 | printAccessDeniedMessage(url); |
554 | return false; |
555 | } |
556 | |
557 | if (!allowedByContentSecurityPolicy(type, url, options, ContentSecurityPolicy::RedirectResponseReceived::Yes)) |
558 | return false; |
559 | |
560 | // Last of all, check for insecure content. We do this last so that when folks block insecure content with a CSP policy, they don't get a warning. |
561 | // They'll still get a warning in the console about CSP blocking the load. |
562 | if (!checkInsecureContent(type, url)) |
563 | return false; |
564 | |
565 | return true; |
566 | } |
567 | |
568 | bool CachedResourceLoader::updateRequestAfterRedirection(CachedResource::Type type, ResourceRequest& request, const ResourceLoaderOptions& options) |
569 | { |
570 | ASSERT(m_documentLoader); |
571 | if (auto* document = m_documentLoader->cachedResourceLoader().document()) |
572 | upgradeInsecureResourceRequestIfNeeded(request, *document); |
573 | |
574 | // FIXME: We might want to align the checks done here with the ones done in CachedResourceLoader::requestResource, content extensions blocking in particular. |
575 | |
576 | return canRequestAfterRedirection(type, request.url(), options); |
577 | } |
578 | |
579 | bool CachedResourceLoader::canRequestInContentDispositionAttachmentSandbox(CachedResource::Type type, const URL& url) const |
580 | { |
581 | Document* document; |
582 | |
583 | // FIXME: Do we want to expand this to all resource types that the mixed content checker would consider active content? |
584 | switch (type) { |
585 | case CachedResource::Type::MainResource: |
586 | if (auto ownerElement = frame() ? frame()->ownerElement() : nullptr) { |
587 | document = &ownerElement->document(); |
588 | break; |
589 | } |
590 | return true; |
591 | case CachedResource::Type::CSSStyleSheet: |
592 | document = m_document.get(); |
593 | break; |
594 | default: |
595 | return true; |
596 | } |
597 | |
598 | if (!document->shouldEnforceContentDispositionAttachmentSandbox() || document->securityOrigin().canRequest(url)) |
599 | return true; |
600 | |
601 | String message = "Unsafe attempt to load URL " + url.stringCenterEllipsizedToLength() + " from document with Content-Disposition: attachment at URL " + document->url().stringCenterEllipsizedToLength() + "." ; |
602 | document->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message); |
603 | return false; |
604 | } |
605 | |
606 | bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(const CachedResourceRequest& request, CachedResource& resource, ResourceError& error) |
607 | { |
608 | if (!frame() || resource.status() != CachedResource::Cached) |
609 | return true; |
610 | |
611 | ResourceRequest newRequest = ResourceRequest(resource.url()); |
612 | newRequest.setInitiatorIdentifier(request.resourceRequest().initiatorIdentifier()); |
613 | if (auto inspectorInitiatorNodeIdentifier = request.resourceRequest().inspectorInitiatorNodeIdentifier()) |
614 | newRequest.setInspectorInitiatorNodeIdentifier(*inspectorInitiatorNodeIdentifier); |
615 | if (request.resourceRequest().hiddenFromInspector()) |
616 | newRequest.setHiddenFromInspector(true); |
617 | frame()->loader().loadedResourceFromMemoryCache(resource, newRequest, error); |
618 | |
619 | // FIXME <http://webkit.org/b/113251>: If the delegate modifies the request's |
620 | // URL, it is no longer appropriate to use this CachedResource. |
621 | return !newRequest.isNull(); |
622 | } |
623 | |
624 | bool CachedResourceLoader::shouldUpdateCachedResourceWithCurrentRequest(const CachedResource& resource, const CachedResourceRequest& request) |
625 | { |
626 | // WebKit is not supporting CORS for fonts (https://bugs.webkit.org/show_bug.cgi?id=86817), no need to update the resource before reusing it. |
627 | if (resource.type() == CachedResource::Type::FontResource) |
628 | return false; |
629 | |
630 | #if ENABLE(SVG_FONTS) |
631 | if (resource.type() == CachedResource::Type::SVGFontResource) |
632 | return false; |
633 | #endif |
634 | |
635 | #if ENABLE(XSLT) |
636 | // Load is same-origin, we do not check for CORS. |
637 | if (resource.type() == CachedResource::Type::XSLStyleSheet) |
638 | return false; |
639 | #endif |
640 | |
641 | // FIXME: We should enable resource reuse for these resource types |
642 | switch (resource.type()) { |
643 | case CachedResource::Type::SVGDocumentResource: |
644 | return false; |
645 | case CachedResource::Type::MainResource: |
646 | return false; |
647 | case CachedResource::Type::LinkPrefetch: |
648 | return false; |
649 | default: |
650 | break; |
651 | } |
652 | |
653 | if (resource.options().mode != request.options().mode || !serializedOriginsMatch(request.origin(), resource.origin())) |
654 | return true; |
655 | |
656 | if (resource.options().redirect != request.options().redirect && resource.hasRedirections()) |
657 | return true; |
658 | |
659 | return false; |
660 | } |
661 | |
662 | static inline bool isResourceSuitableForDirectReuse(const CachedResource& resource, const CachedResourceRequest& request) |
663 | { |
664 | // FIXME: For being loaded requests, the response tainting may not be correctly computed if the fetch mode is not the same. |
665 | // Even if the fetch mode is the same, we are not sure that the resource can be reused (Vary: Origin header for instance). |
666 | // We should find a way to improve this. |
667 | if (resource.status() != CachedResource::Cached) |
668 | return false; |
669 | |
670 | // If the cached resource has not followed redirections, it is incomplete and we should not use it. |
671 | // Let's make sure the memory cache has no such resource. |
672 | ASSERT(resource.response().type() != ResourceResponse::Type::Opaqueredirect); |
673 | |
674 | // We could support redirect modes other than Follow in case of a redirected resource. |
675 | // This case is rare and is not worth optimizing currently. |
676 | if (request.options().redirect != FetchOptions::Redirect::Follow && resource.hasRedirections()) |
677 | return false; |
678 | |
679 | // FIXME: Implement reuse of cached raw resources. |
680 | if (resource.type() == CachedResource::Type::RawResource || resource.type() == CachedResource::Type::MediaResource) |
681 | return false; |
682 | |
683 | if (resource.type() == CachedResource::Type::Beacon || resource.type() == CachedResource::Type::Ping) |
684 | return false; |
685 | |
686 | return true; |
687 | } |
688 | |
689 | CachedResourceHandle<CachedResource> CachedResourceLoader::updateCachedResourceWithCurrentRequest(const CachedResource& resource, CachedResourceRequest&& request, const PAL::SessionID& sessionID, const CookieJar* cookieJar) |
690 | { |
691 | if (!isResourceSuitableForDirectReuse(resource, request)) { |
692 | request.setCachingPolicy(CachingPolicy::DisallowCaching); |
693 | return loadResource(resource.type(), WTFMove(request), cookieJar); |
694 | } |
695 | |
696 | auto resourceHandle = createResource(resource.type(), WTFMove(request), sessionID, cookieJar); |
697 | resourceHandle->loadFrom(resource); |
698 | return resourceHandle; |
699 | } |
700 | |
701 | static inline void logMemoryCacheResourceRequest(Frame* frame, const String& key, const String& description) |
702 | { |
703 | if (!frame || !frame->page()) |
704 | return; |
705 | frame->page()->diagnosticLoggingClient().logDiagnosticMessage(key, description, ShouldSample::Yes); |
706 | } |
707 | |
708 | void CachedResourceLoader::prepareFetch(CachedResource::Type type, CachedResourceRequest& request) |
709 | { |
710 | // Implementing step 1 to 7 of https://fetch.spec.whatwg.org/#fetching |
711 | auto* document = this->document(); |
712 | |
713 | if (document) { |
714 | if (!request.origin()) |
715 | request.setOrigin(document->securityOrigin()); |
716 | #if ENABLE(SERVICE_WORKER) |
717 | request.setClientIdentifierIfNeeded(document->identifier()); |
718 | if (auto* activeServiceWorker = document->activeServiceWorker()) |
719 | request.setSelectedServiceWorkerRegistrationIdentifierIfNeeded(activeServiceWorker->registrationIdentifier()); |
720 | #endif |
721 | } |
722 | |
723 | request.setAcceptHeaderIfNone(type); |
724 | |
725 | // Accept-Language value is handled in underlying port-specific code. |
726 | // FIXME: Decide whether to support client hints |
727 | } |
728 | |
729 | void CachedResourceLoader::(CachedResource::Type type, CachedResourceRequest& request) |
730 | { |
731 | // Implementing steps 7 to 12 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch |
732 | |
733 | // FIXME: We should reconcile handling of MainResource with other resources. |
734 | if (type != CachedResource::Type::MainResource) { |
735 | // In some cases we may try to load resources in frameless documents. Such loads always fail. |
736 | // FIXME: We shouldn't need to do the check on frame. |
737 | if (auto* frame = this->frame()) |
738 | request.updateReferrerOriginAndUserAgentHeaders(frame->loader()); |
739 | } |
740 | |
741 | request.updateAccordingCacheMode(); |
742 | request.updateAcceptEncodingHeader(); |
743 | } |
744 | |
745 | static FetchOptions::Destination destinationForType(CachedResource::Type type) |
746 | { |
747 | switch (type) { |
748 | case CachedResource::Type::MainResource: |
749 | case CachedResource::Type::SVGDocumentResource: |
750 | return FetchOptions::Destination::Document; |
751 | case CachedResource::Type::ImageResource: |
752 | case CachedResource::Type::Icon: |
753 | return FetchOptions::Destination::Image; |
754 | case CachedResource::Type::CSSStyleSheet: |
755 | return FetchOptions::Destination::Style; |
756 | case CachedResource::Type::Script: |
757 | return FetchOptions::Destination::Script; |
758 | case CachedResource::Type::FontResource: |
759 | #if ENABLE(SVG_FONTS) |
760 | case CachedResource::Type::SVGFontResource: |
761 | #endif |
762 | return FetchOptions::Destination::Font; |
763 | #if ENABLE(XSLT) |
764 | case CachedResource::Type::XSLStyleSheet: |
765 | return FetchOptions::Destination::Xslt; |
766 | #endif |
767 | #if ENABLE(VIDEO_TRACK) |
768 | case CachedResource::Type::TextTrackResource: |
769 | return FetchOptions::Destination::Track; |
770 | #endif |
771 | #if ENABLE(APPLICATION_MANIFEST) |
772 | case CachedResource::Type::ApplicationManifest: |
773 | return FetchOptions::Destination::Manifest; |
774 | #endif |
775 | case CachedResource::Type::Beacon: |
776 | case CachedResource::Type::Ping: |
777 | case CachedResource::Type::LinkPrefetch: |
778 | case CachedResource::Type::RawResource: |
779 | case CachedResource::Type::MediaResource: |
780 | // The caller is responsible for setting the appropriate destination. |
781 | return FetchOptions::Destination::EmptyString; |
782 | } |
783 | ASSERT_NOT_REACHED(); |
784 | return FetchOptions::Destination::EmptyString; |
785 | } |
786 | |
787 | ResourceErrorOr<CachedResourceHandle<CachedResource>> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest&& request, ForPreload forPreload, DeferOption defer) |
788 | { |
789 | request.setDestinationIfNotSet(destinationForType(type)); |
790 | |
791 | // Entry point to https://fetch.spec.whatwg.org/#main-fetch. |
792 | std::unique_ptr<ResourceRequest> originalRequest; |
793 | if (CachedResource::shouldUsePingLoad(type) || request.options().destination == FetchOptions::Destination::EmptyString) { |
794 | originalRequest = std::make_unique<ResourceRequest>(request.resourceRequest()); |
795 | originalRequest->clearHTTPReferrer(); |
796 | originalRequest->clearHTTPOrigin(); |
797 | } |
798 | |
799 | if (Document* document = this->document()) |
800 | request.upgradeInsecureRequestIfNeeded(*document); |
801 | |
802 | request.updateReferrerPolicy(document() ? document()->referrerPolicy() : ReferrerPolicy::NoReferrerWhenDowngrade); |
803 | URL url = request.resourceRequest().url(); |
804 | |
805 | LOG(ResourceLoading, "CachedResourceLoader::requestResource '%.255s', charset '%s', priority=%d, forPreload=%u" , url.stringCenterEllipsizedToLength().latin1().data(), request.charset().latin1().data(), request.priority() ? static_cast<int>(request.priority().value()) : -1, forPreload == ForPreload::Yes); |
806 | |
807 | if (!url.isValid()) { |
808 | RELEASE_LOG_IF_ALLOWED("requestResource: URL is invalid (frame = %p)" , frame()); |
809 | return makeUnexpected(ResourceError { errorDomainWebKitInternal, 0, url, "URL is invalid"_s }); |
810 | } |
811 | |
812 | prepareFetch(type, request); |
813 | |
814 | // We are passing url as well as request, as request url may contain a fragment identifier. |
815 | if (!canRequest(type, url, request, forPreload)) { |
816 | RELEASE_LOG_IF_ALLOWED("requestResource: Not allowed to request resource (frame = %p)" , frame()); |
817 | return makeUnexpected(ResourceError { errorDomainWebKitInternal, 0, url, "Not allowed to request resource"_s , ResourceError::Type::AccessControl }); |
818 | } |
819 | |
820 | #if ENABLE(CONTENT_EXTENSIONS) |
821 | if (frame() && frame()->page() && m_documentLoader) { |
822 | const auto& resourceRequest = request.resourceRequest(); |
823 | auto* page = frame()->page(); |
824 | auto results = page->userContentProvider().processContentRuleListsForLoad(resourceRequest.url(), ContentExtensions::toResourceType(type), *m_documentLoader); |
825 | bool blockedLoad = results.summary.blockedLoad; |
826 | bool madeHTTPS = results.summary.madeHTTPS; |
827 | request.applyResults(WTFMove(results), page); |
828 | if (blockedLoad) { |
829 | RELEASE_LOG_IF_ALLOWED("requestResource: Resource blocked by content blocker (frame = %p)" , frame()); |
830 | if (type == CachedResource::Type::MainResource) { |
831 | CachedResourceHandle<CachedResource> resource = createResource(type, WTFMove(request), page->sessionID(), &page->cookieJar()); |
832 | ASSERT(resource); |
833 | resource->error(CachedResource::Status::LoadError); |
834 | resource->setResourceError(ResourceError(ContentExtensions::WebKitContentBlockerDomain, 0, resourceRequest.url(), WEB_UI_STRING("The URL was blocked by a content blocker" , "WebKitErrorBlockedByContentBlocker description" ))); |
835 | return resource; |
836 | } |
837 | return makeUnexpected(ResourceError { errorDomainWebKitInternal, 0, url, "Resource blocked by content blocker"_s , ResourceError::Type::AccessControl }); |
838 | } |
839 | if (madeHTTPS |
840 | && type == CachedResource::Type::MainResource |
841 | && m_documentLoader->isLoadingMainResource()) { |
842 | // This is to make sure the correct 'new' URL shows in the location bar. |
843 | m_documentLoader->frameLoader()->client().dispatchDidChangeProvisionalURL(); |
844 | } |
845 | url = request.resourceRequest().url(); // The content extension could have changed it from http to https. |
846 | url = MemoryCache::removeFragmentIdentifierIfNeeded(url); // Might need to remove fragment identifier again. |
847 | } |
848 | #endif |
849 | |
850 | if (frame() && m_documentLoader && !m_documentLoader->customHeaderFields().isEmpty()) { |
851 | bool sameOriginRequest = false; |
852 | auto requestedOrigin = SecurityOrigin::create(url); |
853 | if (type == CachedResource::Type::MainResource) { |
854 | if (frame()->isMainFrame()) |
855 | sameOriginRequest = true; |
856 | else if (auto* topDocument = frame()->mainFrame().document()) |
857 | sameOriginRequest = topDocument->securityOrigin().isSameSchemeHostPort(requestedOrigin.get()); |
858 | } else if (document()) { |
859 | sameOriginRequest = document()->topDocument().securityOrigin().isSameSchemeHostPort(requestedOrigin.get()) |
860 | && document()->securityOrigin().isSameSchemeHostPort(requestedOrigin.get()); |
861 | } |
862 | for (auto& fields : m_documentLoader->customHeaderFields()) { |
863 | if (sameOriginRequest || fields.thirdPartyDomainsMatch(url)) { |
864 | for (auto& field : fields.fields) |
865 | request.resourceRequest().setHTTPHeaderField(field.name(), field.value()); |
866 | } |
867 | } |
868 | } |
869 | |
870 | LoadTiming loadTiming; |
871 | loadTiming.markStartTimeAndFetchStart(); |
872 | InitiatorContext initiatorContext = request.options().initiatorContext; |
873 | |
874 | if (request.resourceRequest().url().protocolIsInHTTPFamily()) |
875 | updateHTTPRequestHeaders(type, request); |
876 | |
877 | auto& memoryCache = MemoryCache::singleton(); |
878 | if (request.allowsCaching() && memoryCache.disabled()) |
879 | m_documentResources.remove(url.string()); |
880 | |
881 | // See if we can use an existing resource from the cache. |
882 | CachedResourceHandle<CachedResource> resource; |
883 | if (document()) |
884 | request.setDomainForCachePartition(*document()); |
885 | |
886 | if (request.allowsCaching()) |
887 | resource = memoryCache.resourceForRequest(request.resourceRequest(), sessionID()); |
888 | |
889 | if (resource && request.isLinkPreload() && !resource->isLinkPreload()) |
890 | resource->setLinkPreload(); |
891 | |
892 | logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheUsageKey(), resource ? DiagnosticLoggingKeys::inMemoryCacheKey() : DiagnosticLoggingKeys::notInMemoryCacheKey()); |
893 | |
894 | auto* cookieJar = document() && document()->page() ? &document()->page()->cookieJar() : nullptr; |
895 | |
896 | RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get(), forPreload, defer); |
897 | switch (policy) { |
898 | case Reload: |
899 | memoryCache.remove(*resource); |
900 | FALLTHROUGH; |
901 | case Load: |
902 | if (resource) |
903 | logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheEntryDecisionKey(), DiagnosticLoggingKeys::unusedKey()); |
904 | resource = loadResource(type, WTFMove(request), cookieJar); |
905 | break; |
906 | case Revalidate: |
907 | if (resource) |
908 | logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheEntryDecisionKey(), DiagnosticLoggingKeys::revalidatingKey()); |
909 | resource = revalidateResource(WTFMove(request), *resource); |
910 | break; |
911 | case Use: |
912 | ASSERT(resource); |
913 | if (request.options().mode == FetchOptions::Mode::NoCors) { |
914 | if (auto error = validateCrossOriginResourcePolicy(*request.origin(), request.resourceRequest().url(), resource->response())) |
915 | return makeUnexpected(WTFMove(*error)); |
916 | } |
917 | if (shouldUpdateCachedResourceWithCurrentRequest(*resource, request)) { |
918 | resource = updateCachedResourceWithCurrentRequest(*resource, WTFMove(request), document()->page()->sessionID(), cookieJar); |
919 | if (resource->status() != CachedResource::Status::Cached) |
920 | policy = Load; |
921 | } else { |
922 | ResourceError error; |
923 | if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(request, *resource, error)) |
924 | return makeUnexpected(WTFMove(error)); |
925 | logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::memoryCacheEntryDecisionKey(), DiagnosticLoggingKeys::usedKey()); |
926 | loadTiming.setResponseEnd(MonotonicTime::now()); |
927 | |
928 | memoryCache.resourceAccessed(*resource); |
929 | |
930 | if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled() && document() && !resource->isLoading()) { |
931 | auto resourceTiming = ResourceTiming::fromCache(url, request.initiatorName(), loadTiming, resource->response(), *request.origin()); |
932 | if (initiatorContext == InitiatorContext::Worker) { |
933 | ASSERT(is<CachedRawResource>(resource.get())); |
934 | downcast<CachedRawResource>(resource.get())->finishedTimingForWorkerLoad(WTFMove(resourceTiming)); |
935 | } else { |
936 | ASSERT(initiatorContext == InitiatorContext::Document); |
937 | m_resourceTimingInfo.storeResourceTimingInitiatorInformation(resource, request.initiatorName(), frame()); |
938 | m_resourceTimingInfo.addResourceTiming(*resource.get(), *document(), WTFMove(resourceTiming)); |
939 | } |
940 | } |
941 | |
942 | if (forPreload == ForPreload::No) |
943 | resource->setLoadPriority(request.priority()); |
944 | } |
945 | break; |
946 | } |
947 | ASSERT(resource); |
948 | resource->setOriginalRequest(WTFMove(originalRequest)); |
949 | |
950 | if (forPreload == ForPreload::No && resource->loader() && resource->ignoreForRequestCount()) { |
951 | resource->setIgnoreForRequestCount(false); |
952 | incrementRequestCount(*resource); |
953 | } |
954 | |
955 | if ((policy != Use || resource->stillNeedsLoad()) && defer == DeferOption::NoDefer) { |
956 | resource->load(*this); |
957 | |
958 | // We don't support immediate loads, but we do support immediate failure. |
959 | if (resource->errorOccurred()) { |
960 | if (resource->allowsCaching() && resource->inCache()) |
961 | memoryCache.remove(*resource); |
962 | |
963 | auto resourceError = resource->resourceError(); |
964 | // Synchronous cancellations are likely due to access control. |
965 | if (resourceError.isNull() || resourceError.isCancellation()) |
966 | return makeUnexpected(ResourceError { String(), 0, url, String(), ResourceError::Type::AccessControl }); |
967 | return makeUnexpected(resourceError); |
968 | } |
969 | } |
970 | |
971 | if (document() && !document()->loadEventFinished() && !resource->resourceRequest().url().protocolIsData()) |
972 | m_validatedURLs.add(resource->resourceRequest().url()); |
973 | |
974 | ASSERT(resource->url() == url.string()); |
975 | m_documentResources.set(resource->url(), resource); |
976 | return resource; |
977 | } |
978 | |
979 | void CachedResourceLoader::documentDidFinishLoadEvent() |
980 | { |
981 | m_validatedURLs.clear(); |
982 | |
983 | // If m_preloads is not empty here, it's full of link preloads, |
984 | // as speculative preloads were cleared at DCL. |
985 | if (m_preloads && m_preloads->size() && !m_unusedPreloadsTimer.isActive()) |
986 | m_unusedPreloadsTimer.startOneShot(unusedPreloadTimeout); |
987 | } |
988 | |
989 | void CachedResourceLoader::stopUnusedPreloadsTimer() |
990 | { |
991 | m_unusedPreloadsTimer.stop(); |
992 | } |
993 | |
994 | CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(CachedResourceRequest&& request, CachedResource& resource) |
995 | { |
996 | ASSERT(resource.inCache()); |
997 | auto& memoryCache = MemoryCache::singleton(); |
998 | ASSERT(!memoryCache.disabled()); |
999 | ASSERT(resource.canUseCacheValidator()); |
1000 | ASSERT(!resource.resourceToRevalidate()); |
1001 | ASSERT(resource.sessionID() == sessionID()); |
1002 | ASSERT(resource.allowsCaching()); |
1003 | |
1004 | CachedResourceHandle<CachedResource> newResource = createResource(resource.type(), WTFMove(request), resource.sessionID(), resource.cookieJar()); |
1005 | |
1006 | LOG(ResourceLoading, "Resource %p created to revalidate %p" , newResource.get(), &resource); |
1007 | newResource->setResourceToRevalidate(&resource); |
1008 | |
1009 | memoryCache.remove(resource); |
1010 | memoryCache.add(*newResource); |
1011 | |
1012 | if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) |
1013 | m_resourceTimingInfo.storeResourceTimingInitiatorInformation(newResource, newResource->initiatorName(), frame()); |
1014 | |
1015 | return newResource; |
1016 | } |
1017 | |
1018 | CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest&& request, const CookieJar* cookieJar) |
1019 | { |
1020 | auto& memoryCache = MemoryCache::singleton(); |
1021 | ASSERT(!request.allowsCaching() || !memoryCache.resourceForRequest(request.resourceRequest(), sessionID()) |
1022 | || request.resourceRequest().cachePolicy() == ResourceRequestCachePolicy::DoNotUseAnyCache || request.resourceRequest().cachePolicy() == ResourceRequestCachePolicy::ReloadIgnoringCacheData || request.resourceRequest().cachePolicy() == ResourceRequestCachePolicy::RefreshAnyCacheData); |
1023 | |
1024 | LOG(ResourceLoading, "Loading CachedResource for '%s'." , request.resourceRequest().url().stringCenterEllipsizedToLength().latin1().data()); |
1025 | |
1026 | CachedResourceHandle<CachedResource> resource = createResource(type, WTFMove(request), sessionID(), cookieJar); |
1027 | |
1028 | if (resource->allowsCaching()) |
1029 | memoryCache.add(*resource); |
1030 | |
1031 | if (RuntimeEnabledFeatures::sharedFeatures().resourceTimingEnabled()) |
1032 | m_resourceTimingInfo.storeResourceTimingInitiatorInformation(resource, resource->initiatorName(), frame()); |
1033 | |
1034 | return resource; |
1035 | } |
1036 | |
1037 | static void logRevalidation(const String& reason, DiagnosticLoggingClient& logClient) |
1038 | { |
1039 | logClient.logDiagnosticMessage(DiagnosticLoggingKeys::cachedResourceRevalidationReasonKey(), reason, ShouldSample::Yes); |
1040 | } |
1041 | |
1042 | static void logResourceRevalidationDecision(CachedResource::RevalidationDecision reason, const Frame* frame) |
1043 | { |
1044 | if (!frame || !frame->page()) |
1045 | return; |
1046 | auto& logClient = frame->page()->diagnosticLoggingClient(); |
1047 | switch (reason) { |
1048 | case CachedResource::RevalidationDecision::No: |
1049 | break; |
1050 | case CachedResource::RevalidationDecision::YesDueToExpired: |
1051 | logRevalidation(DiagnosticLoggingKeys::isExpiredKey(), logClient); |
1052 | break; |
1053 | case CachedResource::RevalidationDecision::YesDueToNoStore: |
1054 | logRevalidation(DiagnosticLoggingKeys::noStoreKey(), logClient); |
1055 | break; |
1056 | case CachedResource::RevalidationDecision::YesDueToNoCache: |
1057 | logRevalidation(DiagnosticLoggingKeys::noCacheKey(), logClient); |
1058 | break; |
1059 | case CachedResource::RevalidationDecision::YesDueToCachePolicy: |
1060 | logRevalidation(DiagnosticLoggingKeys::reloadKey(), logClient); |
1061 | break; |
1062 | } |
1063 | } |
1064 | |
1065 | CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, CachedResourceRequest& cachedResourceRequest, CachedResource* existingResource, ForPreload forPreload, DeferOption defer) const |
1066 | { |
1067 | auto& request = cachedResourceRequest.resourceRequest(); |
1068 | |
1069 | if (!existingResource) |
1070 | return Load; |
1071 | |
1072 | if (request.cachePolicy() == ResourceRequestCachePolicy::DoNotUseAnyCache || request.cachePolicy() == ResourceRequestCachePolicy::ReloadIgnoringCacheData) |
1073 | return Load; |
1074 | |
1075 | if (request.cachePolicy() == ResourceRequestCachePolicy::RefreshAnyCacheData) |
1076 | return Reload; |
1077 | |
1078 | #if ENABLE(SERVICE_WORKER) |
1079 | // FIXME: We should validate/specify this behavior. |
1080 | if (cachedResourceRequest.options().serviceWorkerRegistrationIdentifier != existingResource->options().serviceWorkerRegistrationIdentifier) { |
1081 | LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading because selected service worker differs" ); |
1082 | return Reload; |
1083 | } |
1084 | #endif |
1085 | |
1086 | // We already have a preload going for this URL. |
1087 | if (forPreload == ForPreload::Yes && existingResource->isPreloaded()) |
1088 | return Use; |
1089 | |
1090 | // If the same URL has been loaded as a different type, we need to reload. |
1091 | if (existingResource->type() != type) { |
1092 | LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch." ); |
1093 | logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonTypeMismatchKey()); |
1094 | return Reload; |
1095 | } |
1096 | |
1097 | if (!existingResource->varyHeaderValuesMatch(request)) |
1098 | return Reload; |
1099 | |
1100 | auto* textDecoder = existingResource->textResourceDecoder(); |
1101 | if (textDecoder && !textDecoder->hasEqualEncodingForCharset(cachedResourceRequest.charset())) { |
1102 | if (!existingResource->hasUnknownEncoding()) |
1103 | return Reload; |
1104 | existingResource->setHasUnknownEncoding(false); |
1105 | existingResource->setEncoding(cachedResourceRequest.charset()); |
1106 | } |
1107 | |
1108 | // FIXME: We should use the same cache policy for all resource types. The raw resource policy is overly strict |
1109 | // while the normal subresource policy is too loose. |
1110 | if (existingResource->isMainOrMediaOrIconOrRawResource() && frame()) { |
1111 | bool strictPolicyDisabled = frame()->loader().isStrictRawResourceValidationPolicyDisabledForTesting(); |
1112 | bool canReuseRawResource = strictPolicyDisabled || downcast<CachedRawResource>(*existingResource).canReuse(request); |
1113 | if (!canReuseRawResource) |
1114 | return Reload; |
1115 | } |
1116 | |
1117 | // Conditional requests should have failed canReuse check. |
1118 | ASSERT(!request.isConditional()); |
1119 | |
1120 | // Do not load from cache if images are not enabled. The load for this image will be blocked in CachedImage::load. |
1121 | if (defer == DeferOption::DeferredByClient) |
1122 | return Reload; |
1123 | |
1124 | // Don't reload resources while pasting or if cache mode allows stale resources. |
1125 | if (m_allowStaleResources || cachedResourceRequest.options().cache == FetchOptions::Cache::ForceCache || cachedResourceRequest.options().cache == FetchOptions::Cache::OnlyIfCached) |
1126 | return Use; |
1127 | |
1128 | ASSERT(cachedResourceRequest.options().cache == FetchOptions::Cache::Default || cachedResourceRequest.options().cache == FetchOptions::Cache::NoCache); |
1129 | |
1130 | // Always use preloads. |
1131 | if (existingResource->isPreloaded()) |
1132 | return Use; |
1133 | |
1134 | // We can find resources that are being validated from cache only when validation is just successfully completing. |
1135 | if (existingResource->validationCompleting()) |
1136 | return Use; |
1137 | ASSERT(!existingResource->validationInProgress()); |
1138 | |
1139 | auto cachePolicy = this->cachePolicy(type, request.url()); |
1140 | |
1141 | // Validate the redirect chain. |
1142 | bool cachePolicyIsHistoryBuffer = cachePolicy == CachePolicyHistoryBuffer; |
1143 | if (!existingResource->redirectChainAllowsReuse(cachePolicyIsHistoryBuffer ? ReuseExpiredRedirection : DoNotReuseExpiredRedirection)) { |
1144 | LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to not cached or expired redirections." ); |
1145 | logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonRedirectChainKey()); |
1146 | return Reload; |
1147 | } |
1148 | |
1149 | // CachePolicyHistoryBuffer uses the cache except if this is a main resource with "cache-control: no-store". |
1150 | if (cachePolicyIsHistoryBuffer) { |
1151 | // FIXME: Ignoring "cache-control: no-cache" for sub-resources on history navigation but not the main |
1152 | // resource is inconsistent. We should probably harmonize this. |
1153 | if (!existingResource->response().cacheControlContainsNoStore() || type != CachedResource::Type::MainResource) |
1154 | return Use; |
1155 | } |
1156 | |
1157 | // Don't reuse resources with Cache-control: no-store. |
1158 | if (existingResource->response().cacheControlContainsNoStore()) { |
1159 | LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store." ); |
1160 | logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonNoStoreKey()); |
1161 | return Reload; |
1162 | } |
1163 | |
1164 | // If credentials were sent with the previous request and won't be |
1165 | // with this one, or vice versa, re-fetch the resource. |
1166 | // |
1167 | // This helps with the case where the server sends back |
1168 | // "Access-Control-Allow-Origin: *" all the time, but some of the |
1169 | // client's requests are made without CORS and some with. |
1170 | if (existingResource->resourceRequest().allowCookies() != request.allowCookies() || existingResource->options().credentials != cachedResourceRequest.options().credentials) { |
1171 | LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to difference in credentials settings." ); |
1172 | logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonCredentialSettingsKey()); |
1173 | return Reload; |
1174 | } |
1175 | |
1176 | // During the initial load, avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to. |
1177 | if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url())) |
1178 | return Use; |
1179 | |
1180 | // CachePolicyReload always reloads |
1181 | if (cachePolicy == CachePolicyReload) { |
1182 | LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload." ); |
1183 | logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonReloadKey()); |
1184 | return Reload; |
1185 | } |
1186 | |
1187 | // We'll try to reload the resource if it failed last time. |
1188 | if (existingResource->errorOccurred()) { |
1189 | LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state" ); |
1190 | logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonErrorKey()); |
1191 | return Reload; |
1192 | } |
1193 | |
1194 | if (existingResource->isLoading()) { |
1195 | // Do not use cached main resources that are still loading because sharing |
1196 | // loading CachedResources in this case causes issues with regards to cancellation. |
1197 | // If one of the DocumentLoader clients decides to cancel the load, then the load |
1198 | // would be cancelled for all other DocumentLoaders as well. |
1199 | if (type == CachedResource::Type::MainResource) |
1200 | return Reload; |
1201 | // For cached subresources that are still loading we ignore the cache policy. |
1202 | return Use; |
1203 | } |
1204 | |
1205 | auto revalidationDecision = existingResource->makeRevalidationDecision(cachePolicy); |
1206 | logResourceRevalidationDecision(revalidationDecision, frame()); |
1207 | |
1208 | // Check if the cache headers requires us to revalidate (cache expiration for example). |
1209 | if (revalidationDecision != CachedResource::RevalidationDecision::No) { |
1210 | // See if the resource has usable ETag or Last-modified headers. |
1211 | if (existingResource->canUseCacheValidator()) |
1212 | return Revalidate; |
1213 | |
1214 | // No, must reload. |
1215 | LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators." ); |
1216 | logMemoryCacheResourceRequest(frame(), DiagnosticLoggingKeys::inMemoryCacheKey(), DiagnosticLoggingKeys::unusedReasonMustRevalidateNoValidatorKey()); |
1217 | return Reload; |
1218 | } |
1219 | |
1220 | return Use; |
1221 | } |
1222 | |
1223 | void CachedResourceLoader::printAccessDeniedMessage(const URL& url) const |
1224 | { |
1225 | if (url.isNull()) |
1226 | return; |
1227 | |
1228 | if (!frame()) |
1229 | return; |
1230 | |
1231 | String message; |
1232 | if (!m_document || m_document->url().isNull()) |
1233 | message = makeString("Unsafe attempt to load URL " , url.stringCenterEllipsizedToLength(), '.'); |
1234 | else |
1235 | message = makeString("Unsafe attempt to load URL " , url.stringCenterEllipsizedToLength(), " from origin " , m_document->origin(), ". Domains, protocols and ports must match.\n" ); |
1236 | |
1237 | frame()->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message); |
1238 | } |
1239 | |
1240 | void CachedResourceLoader::setAutoLoadImages(bool enable) |
1241 | { |
1242 | if (enable == m_autoLoadImages) |
1243 | return; |
1244 | |
1245 | m_autoLoadImages = enable; |
1246 | |
1247 | if (!m_autoLoadImages) |
1248 | return; |
1249 | |
1250 | reloadImagesIfNotDeferred(); |
1251 | } |
1252 | |
1253 | void CachedResourceLoader::setImagesEnabled(bool enable) |
1254 | { |
1255 | if (enable == m_imagesEnabled) |
1256 | return; |
1257 | |
1258 | m_imagesEnabled = enable; |
1259 | |
1260 | if (!m_imagesEnabled) |
1261 | return; |
1262 | |
1263 | reloadImagesIfNotDeferred(); |
1264 | } |
1265 | |
1266 | bool CachedResourceLoader::clientDefersImage(const URL&) const |
1267 | { |
1268 | return !m_imagesEnabled; |
1269 | } |
1270 | |
1271 | bool CachedResourceLoader::shouldPerformImageLoad(const URL& url) const |
1272 | { |
1273 | return m_autoLoadImages || url.protocolIsData(); |
1274 | } |
1275 | |
1276 | bool CachedResourceLoader::shouldDeferImageLoad(const URL& url) const |
1277 | { |
1278 | return clientDefersImage(url) || !shouldPerformImageLoad(url); |
1279 | } |
1280 | |
1281 | void CachedResourceLoader::reloadImagesIfNotDeferred() |
1282 | { |
1283 | for (auto& resource : m_documentResources.values()) { |
1284 | if (is<CachedImage>(*resource) && resource->stillNeedsLoad() && !clientDefersImage(resource->url())) |
1285 | downcast<CachedImage>(*resource).load(*this); |
1286 | } |
1287 | } |
1288 | |
1289 | CachePolicy CachedResourceLoader::cachePolicy(CachedResource::Type type, const URL& url) const |
1290 | { |
1291 | Frame* frame = this->frame(); |
1292 | if (!frame) |
1293 | return CachePolicyVerify; |
1294 | |
1295 | if (type != CachedResource::Type::MainResource) |
1296 | return frame->loader().subresourceCachePolicy(url); |
1297 | |
1298 | if (Page* page = frame->page()) { |
1299 | if (page->isResourceCachingDisabled()) |
1300 | return CachePolicyReload; |
1301 | } |
1302 | |
1303 | switch (frame->loader().loadType()) { |
1304 | case FrameLoadType::ReloadFromOrigin: |
1305 | case FrameLoadType::Reload: |
1306 | return CachePolicyReload; |
1307 | case FrameLoadType::Back: |
1308 | case FrameLoadType::Forward: |
1309 | case FrameLoadType::IndexedBackForward: |
1310 | // Do not revalidate cached main resource on back/forward navigation. |
1311 | return CachePolicyHistoryBuffer; |
1312 | default: |
1313 | return CachePolicyVerify; |
1314 | } |
1315 | } |
1316 | |
1317 | void CachedResourceLoader::loadDone(LoadCompletionType type, bool shouldPerformPostLoadActions) |
1318 | { |
1319 | RefPtr<DocumentLoader> protectDocumentLoader(m_documentLoader); |
1320 | RefPtr<Document> protectDocument(m_document.get()); |
1321 | |
1322 | ASSERT(shouldPerformPostLoadActions || type == LoadCompletionType::Cancel); |
1323 | |
1324 | if (frame()) |
1325 | frame()->loader().loadDone(type); |
1326 | |
1327 | if (shouldPerformPostLoadActions) |
1328 | performPostLoadActions(); |
1329 | |
1330 | if (!m_garbageCollectDocumentResourcesTimer.isActive()) |
1331 | m_garbageCollectDocumentResourcesTimer.startOneShot(0_s); |
1332 | } |
1333 | |
1334 | // Garbage collecting m_documentResources is a workaround for the |
1335 | // CachedResourceHandles on the RHS being strong references. Ideally this |
1336 | // would be a weak map, however CachedResourceHandles perform additional |
1337 | // bookkeeping on CachedResources, so instead pseudo-GC them -- when the |
1338 | // reference count reaches 1, m_documentResources is the only reference, so |
1339 | // remove it from the map. |
1340 | void CachedResourceLoader::garbageCollectDocumentResources() |
1341 | { |
1342 | LOG(ResourceLoading, "CachedResourceLoader %p garbageCollectDocumentResources" , this); |
1343 | |
1344 | typedef Vector<String, 10> StringVector; |
1345 | StringVector resourcesToDelete; |
1346 | |
1347 | for (auto& resource : m_documentResources) { |
1348 | LOG(ResourceLoading, " cached resource %p - hasOneHandle %d" , resource.value.get(), resource.value->hasOneHandle()); |
1349 | |
1350 | if (resource.value->hasOneHandle()) |
1351 | resourcesToDelete.append(resource.key); |
1352 | } |
1353 | |
1354 | for (auto& resource : resourcesToDelete) |
1355 | m_documentResources.remove(resource); |
1356 | } |
1357 | |
1358 | void CachedResourceLoader::performPostLoadActions() |
1359 | { |
1360 | platformStrategies()->loaderStrategy()->servePendingRequests(); |
1361 | } |
1362 | |
1363 | void CachedResourceLoader::incrementRequestCount(const CachedResource& resource) |
1364 | { |
1365 | if (resource.ignoreForRequestCount()) |
1366 | return; |
1367 | |
1368 | ++m_requestCount; |
1369 | } |
1370 | |
1371 | void CachedResourceLoader::decrementRequestCount(const CachedResource& resource) |
1372 | { |
1373 | if (resource.ignoreForRequestCount()) |
1374 | return; |
1375 | |
1376 | --m_requestCount; |
1377 | ASSERT(m_requestCount > -1); |
1378 | } |
1379 | |
1380 | ResourceErrorOr<CachedResourceHandle<CachedResource>> CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest&& request) |
1381 | { |
1382 | if (request.charset().isEmpty() && (type == CachedResource::Type::Script || type == CachedResource::Type::CSSStyleSheet)) |
1383 | request.setCharset(m_document->charset()); |
1384 | |
1385 | auto resource = requestResource(type, WTFMove(request), ForPreload::Yes); |
1386 | if (resource && (!m_preloads || !m_preloads->contains(resource.value().get()))) { |
1387 | auto resourceValue = resource.value(); |
1388 | // Fonts need special treatment since just creating the resource doesn't trigger a load. |
1389 | if (type == CachedResource::Type::FontResource) |
1390 | downcast<CachedFont>(resourceValue.get())->beginLoadIfNeeded(*this); |
1391 | resourceValue->increasePreloadCount(); |
1392 | |
1393 | if (!m_preloads) |
1394 | m_preloads = std::make_unique<ListHashSet<CachedResource*>>(); |
1395 | m_preloads->add(resourceValue.get()); |
1396 | } |
1397 | return resource; |
1398 | } |
1399 | |
1400 | void CachedResourceLoader::warnUnusedPreloads() |
1401 | { |
1402 | if (!m_preloads) |
1403 | return; |
1404 | for (const auto& resource : *m_preloads) { |
1405 | if (resource && resource->isLinkPreload() && resource->preloadResult() == CachedResource::PreloadResult::PreloadNotReferenced && document()) { |
1406 | document()->addConsoleMessage(MessageSource::Other, MessageLevel::Warning, |
1407 | "The resource " + resource->url().string() + |
1408 | " was preloaded using link preload but not used within a few seconds from the window's load event. Please make sure it wasn't preloaded for nothing." ); |
1409 | } |
1410 | } |
1411 | } |
1412 | |
1413 | bool CachedResourceLoader::isPreloaded(const String& urlString) const |
1414 | { |
1415 | const URL& url = m_document->completeURL(urlString); |
1416 | |
1417 | if (m_preloads) { |
1418 | for (auto& resource : *m_preloads) { |
1419 | if (resource->url() == url) |
1420 | return true; |
1421 | } |
1422 | } |
1423 | return false; |
1424 | } |
1425 | |
1426 | void CachedResourceLoader::clearPreloads(ClearPreloadsMode mode) |
1427 | { |
1428 | if (!m_preloads) |
1429 | return; |
1430 | |
1431 | std::unique_ptr<ListHashSet<CachedResource*>> remainingLinkPreloads; |
1432 | for (auto* resource : *m_preloads) { |
1433 | ASSERT(resource); |
1434 | if (mode == ClearPreloadsMode::ClearSpeculativePreloads && resource->isLinkPreload()) { |
1435 | if (!remainingLinkPreloads) |
1436 | remainingLinkPreloads = std::make_unique<ListHashSet<CachedResource*>>(); |
1437 | remainingLinkPreloads->add(resource); |
1438 | continue; |
1439 | } |
1440 | resource->decreasePreloadCount(); |
1441 | bool deleted = resource->deleteIfPossible(); |
1442 | if (!deleted && resource->preloadResult() == CachedResource::PreloadResult::PreloadNotReferenced) |
1443 | MemoryCache::singleton().remove(*resource); |
1444 | } |
1445 | m_preloads = WTFMove(remainingLinkPreloads); |
1446 | } |
1447 | |
1448 | const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions() |
1449 | { |
1450 | static NeverDestroyed<ResourceLoaderOptions> options( |
1451 | SendCallbackPolicy::SendCallbacks, |
1452 | ContentSniffingPolicy::SniffContent, |
1453 | DataBufferingPolicy::BufferData, |
1454 | StoredCredentialsPolicy::Use, |
1455 | ClientCredentialPolicy::MayAskClientForCredentials, |
1456 | FetchOptions::Credentials::Include, |
1457 | SecurityCheckPolicy::DoSecurityCheck, |
1458 | FetchOptions::Mode::NoCors, |
1459 | CertificateInfoPolicy::DoNotIncludeCertificateInfo, |
1460 | ContentSecurityPolicyImposition::DoPolicyCheck, |
1461 | DefersLoadingPolicy::AllowDefersLoading, |
1462 | CachingPolicy::AllowCaching); |
1463 | return options; |
1464 | } |
1465 | |
1466 | bool CachedResourceLoader::isAlwaysOnLoggingAllowed() const |
1467 | { |
1468 | return m_documentLoader ? m_documentLoader->isAlwaysOnLoggingAllowed() : true; |
1469 | } |
1470 | |
1471 | } |
1472 | |