Bug Summary

File:Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/CachedResourceLoader.cpp
Warning:line 1734, column 15
Local variable 'resource' is uncounted and unsafe

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple arm64-apple-ios26.2.0-simulator -O3 -Wundef-prefix=TARGET_OS_ -Werror=undef-prefix -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name UnifiedSource289.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=osx -analyzer-checker=security.insecureAPI.decodeValueOfObjCType -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=non-leaf -ffp-contract=on -fno-rounding-math -target-sdk-version=26.2 -fdepfile-entry=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/SDKSettings.json -fcompatibility-qualified-id-block-type-checking -fvisibility-inlines-hidden-static-local-var -fno-modulemap-allow-subdirectory-search -fdefine-target-os-macros -enable-tlsdesc -target-cpu apple-m1 -target-feature +v8.4a -target-feature +aes -target-feature +altnzcv -target-feature +ccdp -target-feature +ccpp -target-feature +complxnum -target-feature +crc -target-feature +dotprod -target-feature +flagm -target-feature +fp-armv8 -target-feature +fp16fml -target-feature +fptoint -target-feature +fullfp16 -target-feature +jsconv -target-feature +lse -target-feature +neon -target-feature +pauth -target-feature +perfmon -target-feature +predres -target-feature +ras -target-feature +rcpc -target-feature +rdm -target-feature +sb -target-feature +sha2 -target-feature +sha3 -target-feature +specrestrict -target-feature +ssbs -target-abi darwinpcs -dwarf-ext-refs -fmodule-format=obj -debug-info-kind=standalone -dwarf-version=5 -debugger-tuning=lldb -fdebug-compilation-dir=/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore -target-linker-version 1230.1 -fcoverage-compilation-dir=/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore -resource-dir /Users/buildbot/Library/Developer/Toolchains/swift-webkit.xctoolchain/usr/lib/clang/21 -dependency-file /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebCore/WebCore/normal/arm64/UnifiedSource289.d -skip-unused-modulemap-deps -MT dependencies -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk -iquote /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-generated-files.hmap -iquote /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-project-headers.hmap -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/PAL/ThirdParty/dav1d/include -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/SDKAdditions/usr/include -iframework /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/SDKAdditions/System/Library/Frameworks -iframework /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/System/Library/PrivateFrameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/Frameworks -include-pch /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/PrecompiledHeaders/SharedPrecompiledHeaders/11721353671419897710/WebCorePrefix.h.gch -D _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -D NDEBUG -D BUILDING_WEBKIT -D GL_SILENCE_DEPRECATION=1 -D GLES_SILENCE_DEPRECATION=1 -D __clang_analyzer__ -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-own-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-all-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/include -I PAL -I ForwardingHeaders -I /usr/include/libxslt -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include/libxml2 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebCore -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/WebKitAdditions -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/WebKitAdditions -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/usr/local/include -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/webrtc -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/webrtc -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/webm -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/webm -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/PAL.build/PAL.build/DerivedSources -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/PAL.build/Release-iphonesimulator/PAL.build/DerivedSources -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/DerivedSources-normal/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/DerivedSources/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/DerivedSources -F/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator -F/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator -I/usr/local/include -stdlib=libc++ -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include -internal-isystem /Users/buildbot/Library/Developer/Toolchains/swift-webkit.xctoolchain/usr/lib/clang/21/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/Frameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/SubFrameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/Library/Frameworks -Wno-trigraphs -Werror -Wno-missing-field-initializers -Wmissing-prototypes -Wunreachable-code -Wnon-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wsign-compare -Wno-shorten-64-to-32 -Wnewline-eof -Wno-c++11-extensions -Wno-implicit-fallthrough -Wdeprecated-declarations -Winvalid-offsetof -Wno-sign-conversion -Winfinite-recursion -Wmove -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wrange-loop-analysis -Wno-semicolon-before-method-body -Wall -Wc99-designator -Wconditional-uninitialized -Wextra -Wdeprecated-enum-enum-conversion -Wdeprecated-enum-float-conversion -Wenum-float-conversion -Wfinal-dtor-non-final-class -Wformat=2 -Wmisleading-indentation -Wpointer-to-int-cast -Wreorder-init-list -Wundef -Wunused-but-set-variable -Wvla -Werror=unguarded-availability -Wno-elaborated-enum-base -Wcast-qual -Wchar-subscripts -Wextra-tokens -Winit-self -Wmissing-noreturn -Wpacked -Wpointer-arith -Wredundant-decls -Wwrite-strings -Wexit-time-destructors -Wglobal-constructors -Wtautological-compare -Wimplicit-fallthrough -Wvla -Wno-unknown-warning-option -Wliteral-conversion -Wthread-safety -Wno-profile-instr-out-of-date -Wno-profile-instr-unprofiled -Wno-backend-plugin -Wunsafe-buffer-usage -std=c++2b -fdeprecated-macro -ferror-limit 19 -fmacro-backtrace-limit=0 -fvisibility=hidden -fvisibility-inlines-hidden -stack-protector 1 -fblocks -fencode-extended-block-signature -fno-rtti -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fno-cxx-modules -fno-implicit-modules -fskip-odr-check-in-gmf -fpascal-strings -fmax-type-align=16 -fdiagnostics-show-note-include-stack -vectorize-loops -vectorize-slp -fsafe-buffer-usage-suggestions -analyzer-output=html -analyzer-config report-in-main-source-file=true -analyzer-config nullability:NoDiagnoseCallsToSystemHeaders=true -analyzer-checker optin.osx.cocoa.localizability.NonLocalizedStringChecker -analyzer-checker security.insecureAPI.UncheckedReturn -analyzer-checker security.insecureAPI.getpw -analyzer-checker security.insecureAPI.gets -analyzer-checker security.insecureAPI.mkstemp -analyzer-checker security.insecureAPI.mktemp -analyzer-disable-checker security.insecureAPI.rand -analyzer-disable-checker security.insecureAPI.strcpy -analyzer-checker security.insecureAPI.vfork -analyzer-disable-checker alpha,apiModeling,core,cplusplus,deadcode,debug,fuchsia,nullability,optin,osx,security,unix,webkit -analyzer-checker alpha.webkit.ForwardDeclChecker,alpha.webkit.MemoryUnsafeCastChecker,alpha.webkit.NoDeleteChecker,alpha.webkit.NoUncheckedPtrMemberChecker,alpha.webkit.NoUnretainedMemberChecker,alpha.webkit.RetainPtrCtorAdoptChecker,alpha.webkit.UncheckedCallArgsChecker,alpha.webkit.UncheckedLocalVarsChecker,alpha.webkit.UncountedCallArgsChecker,alpha.webkit.UncountedLocalVarsChecker,alpha.webkit.UnretainedCallArgsChecker,alpha.webkit.UnretainedLambdaCapturesChecker,alpha.webkit.UnretainedLocalVarsChecker,webkit.NoUncountedMemberChecker,webkit.RefCntblBaseVirtualDtor,webkit.UncountedLambdaCapturesChecker -analyzer-config max-nodes=10000000 -analyzer-config verbose-report-filename=true -fdwarf2-cfi-asm -o /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebCore/WebCore/normal/arm64/UnifiedSource289.plist -x c++ /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebCore/unified-sources/UnifiedSource289.cpp
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-2025 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 "CachePolicy.h"
31#include "CachedCSSStyleSheet.h"
32#include "CachedFont.h"
33#include "CachedImage.h"
34#include "CachedRawResource.h"
35#include "CachedResourceRequest.h"
36#include "CachedSVGDocument.h"
37#include "CachedSVGFont.h"
38#include "CachedScript.h"
39#include "CachedTextTrack.h"
40#include "CachedXSLStyleSheet.h"
41#include "Chrome.h"
42#include "ChromeClient.h"
43#include "ContentExtensionError.h"
44#include "ContentExtensionRule.h"
45#include "ContentRuleListResults.h"
46#include "ContentSecurityPolicy.h"
47#include "CookieJar.h"
48#include "CrossOriginAccessControl.h"
49#include "CrossOriginOpenerPolicy.h"
50#include "CustomHeaderFields.h"
51#include "DNS.h"
52#include "DateComponents.h"
53#include "DocumentInlines.h"
54#include "DocumentLoader.h"
55#include "DocumentPage.h"
56#include "DocumentQuirks.h"
57#include "DocumentResourceLoader.h"
58#include "DocumentSecurityOrigin.h"
59#include "FrameDestructionObserverInlines.h"
60#include "FrameInlines.h"
61#include "FrameLoadRequest.h"
62#include "FrameLoader.h"
63#include "HTMLElement.h"
64#include "HTMLFrameOwnerElement.h"
65#include "HTTPHeaderField.h"
66#include "InspectorInstrumentation.h"
67#include "IntegrityPolicy.h"
68#include "LegacySchemeRegistry.h"
69#include "LoaderStrategy.h"
70#include "LocalDOMWindow.h"
71#include "LocalFrame.h"
72#include "LocalFrameLoaderClient.h"
73#include "LocalizedStrings.h"
74#include "Logging.h"
75#include "MemoryCache.h"
76#include "OriginAccessPatterns.h"
77#include "Page.h"
78#include "PingLoader.h"
79#include "PlatformStrategies.h"
80#include "RenderElement.h"
81#include "ResourceLoadInfo.h"
82#include "ResourceTiming.h"
83#include "SVGElementTypeHelpers.h"
84#include "SVGImage.h"
85#include "ScriptController.h"
86#include "SecurityOrigin.h"
87#include "SecurityPolicy.h"
88#include "ServiceWorker.h"
89#include "Settings.h"
90#include "StyleSheetContents.h"
91#include "SubresourceLoader.h"
92#include "UserContentController.h"
93#include "UserStyleSheet.h"
94#include <pal/SessionID.h>
95#include <wtf/RuntimeApplicationChecks.h>
96#include <wtf/Scope.h>
97#include <wtf/text/CString.h>
98#include <wtf/text/MakeString.h>
99#include <wtf/text/WTFString.h>
100
101#if ENABLE(APPLICATION_MANIFEST)(defined 1 && 1)
102#include "CachedApplicationManifest.h"
103#endif
104
105#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
106#import <pal/system/ios/Device.h>
107#endif
108
109#undef CACHEDRESOURCELOADER_RELEASE_LOG
110#define PAGE_ID(frame) (frame.pageID() ? frame.pageID()->toUInt64() : 0)
111#define FRAME_ID(frame) (frame.frameID().toUInt64())
112#define CACHEDRESOURCELOADER_RELEASE_LOG(fmt, ...) RELEASE_LOG(Network, "%p - CachedResourceLoader::" fmt, this, ##__VA_ARGS__)clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option"
clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang
::suppress]] __extension__({ os_log_t _log_tmp = (LogNetwork.
osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT
); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__
({ clang diagnostic push clang diagnostic ignored "-Wvla" clang
diagnostic error "-Wformat" _Static_assert(__builtin_constant_p
("%p - CachedResourceLoader::" fmt), "format/label/description argument must be a string constant"
); __attribute__((section("__TEXT,__oslogstring,cstring_literals"
),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG17"
) = "%p - CachedResourceLoader::" fmt; uint8_t _Alignas(16) __attribute__
((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size
("%p - CachedResourceLoader::" fmt, this, ##__VA_ARGS__)]; _os_log_impl
(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t
*)__builtin_os_log_format(_os_fmt_buf, "%p - CachedResourceLoader::"
fmt, this, ##__VA_ARGS__), (uint32_t)sizeof(_os_fmt_buf)) clang
diagnostic pop ; }); } }) clang diagnostic pop
113#define CACHEDRESOURCELOADER_RELEASE_LOG_WITH_FRAME(fmt, frame, ...) RELEASE_LOG(Network, "%p - [pageID=%" PRIu64 ", frameID=%" PRIu64 "] CachedResourceLoader::" fmt, this, PAGE_ID(frame), FRAME_ID(frame), ##__VA_ARGS__)clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option"
clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang
::suppress]] __extension__({ os_log_t _log_tmp = (LogNetwork.
osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT
); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__
({ clang diagnostic push clang diagnostic ignored "-Wvla" clang
diagnostic error "-Wformat" _Static_assert(__builtin_constant_p
("%p - [pageID=%" "ll" "u" ", frameID=%" "ll" "u" "] CachedResourceLoader::"
fmt), "format/label/description argument must be a string constant"
); __attribute__((section("__TEXT,__oslogstring,cstring_literals"
),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG18"
) = "%p - [pageID=%" "ll" "u" ", frameID=%" "ll" "u" "] CachedResourceLoader::"
fmt; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("%p - [pageID=%" "ll" "u"
", frameID=%" "ll" "u" "] CachedResourceLoader::" fmt, this,
PAGE_ID(frame), FRAME_ID(frame), ##__VA_ARGS__)]; _os_log_impl
(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t
*)__builtin_os_log_format(_os_fmt_buf, "%p - [pageID=%" "ll"
"u" ", frameID=%" "ll" "u" "] CachedResourceLoader::" fmt, this
, PAGE_ID(frame), FRAME_ID(frame), ##__VA_ARGS__), (uint32_t)
sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic
pop
114
115namespace WebCore {
116
117// Timeout for link preloads to be used after window.onload
118static const Seconds unusedPreloadTimeout { 3_s };
119
120template <typename T>
121static inline ResourceErrorOr<Ref<T>> castCachedResourceTo(ResourceErrorOr<Ref<CachedResource>>&& cachedResource)
122{
123 if (cachedResource)
124 return downcast<T>(WTF::move(cachedResource.value()));
125 return makeUnexpected(cachedResource.error());
126}
127
128static ScriptTrackingPrivacyProtectionsEnabled requiresScriptTrackingPrivacyProtections(const Document* document, const ResourceRequest& request)
129{
130 if (!document) [[unlikely]]
131 return ScriptTrackingPrivacyProtectionsEnabled::No;
132
133 RefPtr page = document->page();
134 if (!page) [[unlikely]]
135 return ScriptTrackingPrivacyProtectionsEnabled::No;
136
137 return page->requiresScriptTrackingPrivacyProtections(request.url()) ? ScriptTrackingPrivacyProtectionsEnabled::Yes : ScriptTrackingPrivacyProtectionsEnabled::No;
138}
139
140static Ref<CachedResource> createResource(CachedResource::Type type, CachedResourceRequest&& request, PAL::SessionID sessionID, const CookieJar* cookieJar, const Settings& settings, const Document* document)
141{
142 switch (type) {
143 case CachedResource::Type::ImageResource:
144 return adoptRef(*new CachedImage(WTF::move(request), sessionID, cookieJar));
145 case CachedResource::Type::CSSStyleSheet:
146 return adoptRef(*new CachedCSSStyleSheet(WTF::move(request), sessionID, cookieJar));
147 case CachedResource::Type::JSON:
148 case CachedResource::Type::Script:
149 return adoptRef(*new CachedScript(WTF::move(request), sessionID, cookieJar, requiresScriptTrackingPrivacyProtections(document, request.resourceRequest())));
150 case CachedResource::Type::SVGDocumentResource:
151 return adoptRef(*new CachedSVGDocument(WTF::move(request), sessionID, cookieJar, settings));
152 case CachedResource::Type::SVGFontResource:
153 return adoptRef(*new CachedSVGFont(WTF::move(request), sessionID, cookieJar, settings));
154 case CachedResource::Type::FontResource:
155 return adoptRef(*new CachedFont(WTF::move(request), sessionID, cookieJar));
156 case CachedResource::Type::Beacon:
157 case CachedResource::Type::Ping:
158 case CachedResource::Type::MediaResource:
159#if ENABLE(MODEL_ELEMENT)(defined 1 && 1)
160 case CachedResource::Type::EnvironmentMapResource:
161 case CachedResource::Type::ModelResource:
162#endif
163 case CachedResource::Type::RawResource:
164 case CachedResource::Type::Icon:
165 case CachedResource::Type::MainResource:
166 return adoptRef(*new CachedRawResource(WTF::move(request), type, sessionID, cookieJar));
167#if ENABLE(XSLT)(defined 1 && 1)
168 case CachedResource::Type::XSLStyleSheet:
169 return adoptRef(*new CachedXSLStyleSheet(WTF::move(request), sessionID, cookieJar));
170#endif
171 case CachedResource::Type::LinkPrefetch:
172 return adoptRef(*new CachedResource(WTF::move(request), CachedResource::Type::LinkPrefetch, sessionID, cookieJar));
173#if ENABLE(VIDEO)(defined 1 && 1)
174 case CachedResource::Type::TextTrackResource:
175 return adoptRef(*new CachedTextTrack(WTF::move(request), sessionID, cookieJar));
176#endif
177#if ENABLE(APPLICATION_MANIFEST)(defined 1 && 1)
178 case CachedResource::Type::ApplicationManifest:
179 return adoptRef(*new CachedApplicationManifest(WTF::move(request), sessionID, cookieJar));
180#endif
181 }
182 RELEASE_ASSERT_NOT_REACHED()do { WTF::isIntegralOrPointerType(); compilerFenceForCrash();
WTFCrashWithInfo(182, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/CachedResourceLoader.cpp"
, __PRETTY_FUNCTION__ ); } while (false)
;
183}
184
185static Ref<CachedResource> createResource(CachedResourceRequest&& request, CachedResource& resource, const Document* document)
186{
187 switch (resource.type()) {
188 case CachedResource::Type::ImageResource:
189 return adoptRef(*new CachedImage(WTF::move(request), resource.sessionID(), resource.cookieJar()));
190 case CachedResource::Type::CSSStyleSheet:
191 return adoptRef(*new CachedCSSStyleSheet(WTF::move(request), resource.sessionID(), resource.cookieJar()));
192 case CachedResource::Type::JSON:
193 case CachedResource::Type::Script:
194 return adoptRef(*new CachedScript(WTF::move(request), resource.sessionID(), resource.cookieJar(), requiresScriptTrackingPrivacyProtections(document, request.resourceRequest())));
195 case CachedResource::Type::SVGDocumentResource:
196 return adoptRef(*new CachedSVGDocument(WTF::move(request), downcast<CachedSVGDocument>(resource)));
197 case CachedResource::Type::SVGFontResource:
198 return adoptRef(*new CachedSVGFont(WTF::move(request), downcast<CachedSVGFont>(resource)));
199 case CachedResource::Type::FontResource:
200 return adoptRef(*new CachedFont(WTF::move(request), resource.sessionID(), resource.cookieJar()));
201 case CachedResource::Type::Beacon:
202 case CachedResource::Type::Ping:
203 case CachedResource::Type::MediaResource:
204 case CachedResource::Type::RawResource:
205 case CachedResource::Type::Icon:
206 case CachedResource::Type::MainResource:
207#if ENABLE(MODEL_ELEMENT)(defined 1 && 1)
208 case CachedResource::Type::EnvironmentMapResource:
209 case CachedResource::Type::ModelResource:
210#endif
211 return adoptRef(*new CachedRawResource(WTF::move(request), resource.type(), resource.sessionID(), resource.cookieJar()));
212#if ENABLE(XSLT)(defined 1 && 1)
213 case CachedResource::Type::XSLStyleSheet:
214 return adoptRef(*new CachedXSLStyleSheet(WTF::move(request), resource.sessionID(), resource.cookieJar()));
215#endif
216 case CachedResource::Type::LinkPrefetch:
217 return adoptRef(*new CachedResource(WTF::move(request), CachedResource::Type::LinkPrefetch, resource.sessionID(), resource.cookieJar()));
218#if ENABLE(VIDEO)(defined 1 && 1)
219 case CachedResource::Type::TextTrackResource:
220 return adoptRef(*new CachedTextTrack(WTF::move(request), resource.sessionID(), resource.cookieJar()));
221#endif
222#if ENABLE(APPLICATION_MANIFEST)(defined 1 && 1)
223 case CachedResource::Type::ApplicationManifest:
224 return adoptRef(*new CachedApplicationManifest(WTF::move(request), resource.sessionID(), resource.cookieJar()));
225#endif
226 }
227 RELEASE_ASSERT_NOT_REACHED()do { WTF::isIntegralOrPointerType(); compilerFenceForCrash();
WTFCrashWithInfo(227, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/cache/CachedResourceLoader.cpp"
, __PRETTY_FUNCTION__ ); } while (false)
;
228}
229
230CachedResourceLoader::CachedResourceLoader(DocumentLoader* documentLoader)
231 : m_documentLoader(documentLoader)
232 , m_unusedPreloadsTimer(*this, &CachedResourceLoader::warnUnusedPreloads)
233 , m_garbageCollectDocumentResourcesTimer(*this, &CachedResourceLoader::garbageCollectDocumentResources)
234 , m_keepaliveRequestTracker(KeepaliveRequestTracker::create())
235{
236}
237
238CachedResourceLoader::~CachedResourceLoader()
239{
240 m_documentLoader = nullptr;
241 m_document = nullptr;
242
243 clearPreloads(ClearPreloadsMode::ClearAllPreloads);
244
245 // Make sure no requests still point to this CachedResourceLoader
246 ASSERT(m_requestCount == 0)((void)0);
247 m_unusedPreloadsTimer.stop();
248}
249
250CachedResource* CachedResourceLoader::cachedResource(const String& resourceURL) const
251{
252 ASSERT(!resourceURL.isNull())((void)0);
253 RefPtr document = m_document.get();
254 ASSERT(document)((void)0);
255 return document ? cachedResource(MemoryCache::removeFragmentIdentifierIfNeeded(document->completeURL(resourceURL))) : nullptr;
256}
257
258CachedResource* CachedResourceLoader::cachedResource(const URL& url) const
259{
260 ASSERT(!MemoryCache::shouldRemoveFragmentIdentifier(url))((void)0);
261 return m_documentResources.get(url.string()).get();
262}
263
264LocalFrame* NODELETE[[clang::annotate_type("webkit.nodelete")]] CachedResourceLoader::frame() const
265{
266 return m_documentLoader ? m_documentLoader->frame() : nullptr;
267}
268
269ResourceErrorOr<RefPtr<CachedImage>> CachedResourceLoader::requestImage(CachedResourceRequest&& request, ImageLoading imageLoading)
270{
271 if (RefPtr frame = this->frame()) {
272 if (frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::PageDismissalType::None) {
273 bool isRequestUpgradable { false };
274 if (RefPtr document = frame->document()) {
275 isRequestUpgradable = MixedContentChecker::shouldUpgradeInsecureContent(*frame, MixedContentChecker::IsUpgradable::Yes, request.resourceRequest().url(), request.options().destination, request.options().initiator);
276 request.upgradeInsecureRequestIfNeeded(*document, isRequestUpgradable ? ContentSecurityPolicy::AlwaysUpgradeRequest::Yes : ContentSecurityPolicy::AlwaysUpgradeRequest::No);
277 }
278 URL requestURL = request.resourceRequest().url();
279 if (requestURL.isValid() && canRequest(CachedResource::Type::ImageResource, requestURL, request.options(), ForPreload::No, isRequestUpgradable ? MixedContentChecker::IsUpgradable::Yes : MixedContentChecker::IsUpgradable::No, request.isLinkPreload()))
280 PingLoader::loadImage(*frame, WTF::move(requestURL));
281 return { nullptr };
282 }
283 }
284
285 if (imageLoading == ImageLoading::Immediate)
286 imageLoading = clientDefersImage(request.resourceRequest().url());
287 auto result = castCachedResourceTo<CachedImage>(requestResource(CachedResource::Type::ImageResource, WTF::move(request), ForPreload::No, imageLoading));
288 if (!result)
289 return makeUnexpected(result.error());
290 return RefPtr { WTF::move(result.value()) };
291}
292
293ResourceErrorOr<Ref<CachedFont>> CachedResourceLoader::requestFont(CachedResourceRequest&& request, bool isSVG)
294{
295 if (isSVG)
296 return castCachedResourceTo<CachedFont>(requestResource(CachedResource::Type::SVGFontResource, WTF::move(request)));
297 return castCachedResourceTo<CachedFont>(requestResource(CachedResource::Type::FontResource, WTF::move(request)));
298}
299
300#if ENABLE(VIDEO)(defined 1 && 1)
301ResourceErrorOr<Ref<CachedTextTrack>> CachedResourceLoader::requestTextTrack(CachedResourceRequest&& request)
302{
303 return castCachedResourceTo<CachedTextTrack>(requestResource(CachedResource::Type::TextTrackResource, WTF::move(request)));
304}
305#endif
306
307ResourceErrorOr<Ref<CachedCSSStyleSheet>> CachedResourceLoader::requestCSSStyleSheet(CachedResourceRequest&& request)
308{
309 return castCachedResourceTo<CachedCSSStyleSheet>(requestResource(CachedResource::Type::CSSStyleSheet, WTF::move(request)));
310}
311
312CachedResourceHandle<CachedCSSStyleSheet> CachedResourceLoader::requestUserCSSStyleSheet(Page& page, CachedResourceRequest&& request)
313{
314 request.setDestinationIfNotSet(FetchOptions::Destination::Style);
315
316 ASSERT(document())((void)0);
317 if (RefPtr document = this->document())
318 request.setDomainForCachePartition(*document);
319
320 Ref memoryCache = MemoryCache::singleton();
321 if (request.allowsCaching()) {
322 if (RefPtr existing = memoryCache->resourceForRequest(request.resourceRequest(), page.sessionID())) {
323 if (auto* cssStyleSheet = dynamicDowncast<CachedCSSStyleSheet>(*existing))
324 return *cssStyleSheet;
325 memoryCache->remove(*existing);
326 }
327 }
328
329 request.removeFragmentIdentifierIfNeeded();
330
331 RefPtr userSheet = adoptRef(*new CachedCSSStyleSheet(WTF::move(request), page.sessionID(), protect(page.cookieJar()).ptr()));
332
333 if (userSheet->allowsCaching())
334 memoryCache->add(*userSheet);
335
336 userSheet->load(*this);
337 return userSheet;
338}
339
340ResourceErrorOr<Ref<CachedScript>> CachedResourceLoader::requestScript(CachedResourceRequest&& request)
341{
342 return castCachedResourceTo<CachedScript>(requestResource(
343 request.options().destination == FetchOptionsDestination::Json ? CachedResource::Type::JSON : CachedResource::Type::Script, WTF::move(request)));
344}
345
346#if ENABLE(XSLT)(defined 1 && 1)
347ResourceErrorOr<Ref<CachedXSLStyleSheet>> CachedResourceLoader::requestXSLStyleSheet(CachedResourceRequest&& request)
348{
349 return castCachedResourceTo<CachedXSLStyleSheet>(requestResource(CachedResource::Type::XSLStyleSheet, WTF::move(request)));
350}
351#endif
352
353ResourceErrorOr<Ref<CachedSVGDocument>> CachedResourceLoader::requestSVGDocument(CachedResourceRequest&& request)
354{
355 return castCachedResourceTo<CachedSVGDocument>(requestResource(CachedResource::Type::SVGDocumentResource, WTF::move(request)));
356}
357
358ResourceErrorOr<Ref<CachedResource>> CachedResourceLoader::requestLinkResource(CachedResource::Type type, CachedResourceRequest&& request)
359{
360 ASSERT(frame())((void)0);
361 ASSERT(type == CachedResource::Type::LinkPrefetch)((void)0);
362 return requestResource(type, WTF::move(request));
363}
364
365ResourceErrorOr<Ref<CachedRawResource>> CachedResourceLoader::requestMedia(CachedResourceRequest&& request)
366{
367 auto resourceType = CachedResource::Type::MediaResource;
368 switch (request.options().destination) {
369 case FetchOptions::Destination::Audio:
370 case FetchOptions::Destination::Video:
371 break;
372 case FetchOptions::Destination::Environmentmap:
373#if ENABLE(MODEL_ELEMENT)(defined 1 && 1)
374 resourceType = CachedResource::Type::EnvironmentMapResource;
375 break;
376#endif
377 case FetchOptions::Destination::Model:
378#if ENABLE(MODEL_ELEMENT)(defined 1 && 1)
379 resourceType = CachedResource::Type::ModelResource;
380 break;
381#endif
382 default:
383 ASSERT_NOT_REACHED()((void)0);
384 }
385
386 return castCachedResourceTo<CachedRawResource>(requestResource(resourceType, WTF::move(request)));
387}
388
389ResourceErrorOr<Ref<CachedRawResource>> CachedResourceLoader::requestIcon(CachedResourceRequest&& request)
390{
391 return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::Icon, WTF::move(request)));
392}
393
394ResourceErrorOr<Ref<CachedRawResource>> CachedResourceLoader::requestRawResource(CachedResourceRequest&& request)
395{
396 return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::RawResource, WTF::move(request)));
397}
398
399ResourceErrorOr<Ref<CachedRawResource>> CachedResourceLoader::requestBeaconResource(CachedResourceRequest&& request)
400{
401 ASSERT(request.options().destination == FetchOptions::Destination::EmptyString)((void)0);
402 return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::Beacon, WTF::move(request)));
403}
404
405ResourceErrorOr<Ref<CachedRawResource>> CachedResourceLoader::requestPingResource(CachedResourceRequest&& request)
406{
407 ASSERT(request.options().destination == FetchOptions::Destination::EmptyString || request.options().destination == FetchOptions::Destination::Report)((void)0);
408 return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::Ping, WTF::move(request)));
409}
410
411ResourceErrorOr<Ref<CachedRawResource>> CachedResourceLoader::requestMainResource(CachedResourceRequest&& request)
412{
413 return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::MainResource, WTF::move(request)));
414}
415
416#if ENABLE(APPLICATION_MANIFEST)(defined 1 && 1)
417ResourceErrorOr<Ref<CachedApplicationManifest>> CachedResourceLoader::requestApplicationManifest(CachedResourceRequest&& request)
418{
419 return castCachedResourceTo<CachedApplicationManifest>(requestResource(CachedResource::Type::ApplicationManifest, WTF::move(request)));
420}
421#endif // ENABLE(APPLICATION_MANIFEST)
422
423#if ENABLE(MODEL_ELEMENT)(defined 1 && 1)
424ResourceErrorOr<Ref<CachedRawResource>> CachedResourceLoader::requestEnvironmentMapResource(CachedResourceRequest&& request)
425{
426 return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::EnvironmentMapResource, WTF::move(request)));
427}
428
429ResourceErrorOr<Ref<CachedRawResource>> CachedResourceLoader::requestModelResource(CachedResourceRequest&& request)
430{
431 return castCachedResourceTo<CachedRawResource>(requestResource(CachedResource::Type::ModelResource, WTF::move(request)));
432}
433#endif
434
435static MixedContentChecker::IsUpgradable NODELETE[[clang::annotate_type("webkit.nodelete")]] isUpgradableTypeFromResourceType(CachedResource::Type type)
436{
437 // https://www.w3.org/TR/mixed-content/#category-upgradeable
438 // Editor’s Draft, 23 February 2023
439 // 3.1. Upgradeable Content
440 if (type == CachedResource::Type::ImageResource
441#if ENABLE(MODEL_ELEMENT)(defined 1 && 1)
442 || type == CachedResource::Type::EnvironmentMapResource
443 || type == CachedResource::Type::ModelResource
444#endif
445 || type == CachedResource::Type::MediaResource)
446 return MixedContentChecker::IsUpgradable::Yes;
447
448 return MixedContentChecker::IsUpgradable::No;
449}
450
451bool CachedResourceLoader::checkInsecureContent(CachedResource::Type type, const URL& url, MixedContentChecker::IsUpgradable isRequestUpgradable) const
452{
453 if (!canRequestInContentDispositionAttachmentSandbox(type, url))
454 return false;
455
456 switch (type) {
457 case CachedResource::Type::JSON:
458 case CachedResource::Type::Script:
459#if ENABLE(XSLT)(defined 1 && 1)
460 case CachedResource::Type::XSLStyleSheet:
461#endif
462 case CachedResource::Type::SVGDocumentResource:
463 case CachedResource::Type::CSSStyleSheet:
464#if ENABLE(VIDEO)(defined 1 && 1)
465 case CachedResource::Type::TextTrackResource:
466#endif
467#if ENABLE(MODEL_ELEMENT)(defined 1 && 1)
468 case CachedResource::Type::EnvironmentMapResource:
469 case CachedResource::Type::ModelResource:
470#endif
471 case CachedResource::Type::MediaResource:
472 case CachedResource::Type::RawResource:
473 case CachedResource::Type::Icon:
474 case CachedResource::Type::ImageResource:
475 case CachedResource::Type::SVGFontResource:
476 case CachedResource::Type::Beacon:
477 case CachedResource::Type::Ping:
478 case CachedResource::Type::FontResource: {
479 if (RefPtr frame = this->frame()) {
480 if (MixedContentChecker::shouldBlockRequest(*frame, url, isRequestUpgradable))
481 return false;
482 }
483 break;
484 }
485 case CachedResource::Type::MainResource:
486 case CachedResource::Type::LinkPrefetch:
487 // Prefetch cannot affect the current document.
488#if ENABLE(APPLICATION_MANIFEST)(defined 1 && 1)
489 case CachedResource::Type::ApplicationManifest:
490#endif
491 break;
492 }
493 return true;
494}
495
496bool CachedResourceLoader::allowedByContentSecurityPolicy(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, ContentSecurityPolicy::RedirectResponseReceived redirectResponseReceived, const URL& preRedirectURL, bool shouldReportViolationAsConsoleMessage) const
497{
498 if (options.contentSecurityPolicyImposition == ContentSecurityPolicyImposition::SkipPolicyCheck)
499 return true;
500
501 RefPtr document = m_document.get();
502 ASSERT(document)((void)0);
503 if (!document)
504 return true;
505
506 ASSERT(document->contentSecurityPolicy())((void)0);
507 CheckedPtr contentSecurityPolicy = document->contentSecurityPolicy();
508 if (!contentSecurityPolicy)
509 return true;
510
511 contentSecurityPolicy->setIsReportingToConsoleEnabled(shouldReportViolationAsConsoleMessage);
512 auto scope = makeScopeExit([&] {
513 contentSecurityPolicy->setIsReportingToConsoleEnabled(true);
514 });
515
516 // All content loaded through embed or object elements goes through object-src: https://www.w3.org/TR/CSP3/#directive-object-src.
517 if (options.loadedFromPluginElement == LoadedFromPluginElement::Yes
518 && !contentSecurityPolicy->allowObjectFromSource(url, redirectResponseReceived, preRedirectURL))
519 return false;
520
521 switch (type) {
522#if ENABLE(XSLT)(defined 1 && 1)
523 case CachedResource::Type::XSLStyleSheet:
524#endif
525 case CachedResource::Type::JSON:
526 case CachedResource::Type::Script:
527 if (!contentSecurityPolicy->allowScriptFromSource(url, redirectResponseReceived, preRedirectURL, options.integrity, options.nonce))
528 return false;
529 break;
530 case CachedResource::Type::CSSStyleSheet:
531 if (!contentSecurityPolicy->allowStyleFromSource(url, redirectResponseReceived, preRedirectURL, options.nonce))
532 return false;
533 break;
534 case CachedResource::Type::SVGDocumentResource:
535 case CachedResource::Type::Icon:
536 case CachedResource::Type::ImageResource:
537 if (!contentSecurityPolicy->allowImageFromSource(url, redirectResponseReceived, preRedirectURL))
538 return false;
539 break;
540 case CachedResource::Type::LinkPrefetch:
541 if (!contentSecurityPolicy->allowPrefetchFromSource(url, redirectResponseReceived, preRedirectURL))
542 return false;
543 break;
544 case CachedResource::Type::SVGFontResource:
545 case CachedResource::Type::FontResource:
546 if (!contentSecurityPolicy->allowFontFromSource(url, redirectResponseReceived, preRedirectURL))
547 return false;
548 break;
549 case CachedResource::Type::MediaResource:
550#if ENABLE(VIDEO)(defined 1 && 1)
551 case CachedResource::Type::TextTrackResource:
552#endif
553 if (!contentSecurityPolicy->allowMediaFromSource(url, redirectResponseReceived, preRedirectURL))
554 return false;
555 break;
556 case CachedResource::Type::Beacon:
557 case CachedResource::Type::Ping:
558 case CachedResource::Type::RawResource:
559 case CachedResource::Type::MainResource:
560#if ENABLE(MODEL_ELEMENT)(defined 1 && 1)
561 case CachedResource::Type::EnvironmentMapResource:
562 case CachedResource::Type::ModelResource:
563#endif
564 return true;
565#if ENABLE(APPLICATION_MANIFEST)(defined 1 && 1)
566 case CachedResource::Type::ApplicationManifest:
567 if (!contentSecurityPolicy->allowManifestFromSource(url, redirectResponseReceived, preRedirectURL))
568 return false;
569 break;
570#endif
571 default:
572 ASSERT_NOT_REACHED()((void)0);
573 }
574
575 return true;
576}
577
578static inline bool isSameOriginDataURL(const URL& url, const ResourceLoaderOptions& options)
579{
580 // FIXME: Remove same-origin data URL flag since it was removed from fetch spec (https://github.com/whatwg/fetch/issues/381).
581 return url.protocolIsData() && options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set;
582}
583
584// Security checks defined in https://fetch.spec.whatwg.org/#main-fetch.
585bool CachedResourceLoader::canRequest(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, ForPreload forPreload, MixedContentChecker::IsUpgradable isRequestUpgradable, bool isLinkPreload)
586{
587 if (RefPtr document = m_document.get()) {
588 if (!protect(document->securityOrigin())->canDisplay(url, OriginAccessPatternsForWebProcess::singleton())) {
589 if (forPreload == ForPreload::No)
590 FrameLoader::reportLocalLoadFailed(protect(frame()).get(), url.stringCenterEllipsizedToLength());
591 LOG(ResourceLoading, "CachedResourceLoader::requestResource URL was not allowed by SecurityOrigin::canDisplay")((void)0);
592 return false;
593 }
594
595 if (options.mode == FetchOptions::Mode::SameOrigin && !protect(document->securityOrigin())->canRequest(url, OriginAccessPatternsForWebProcess::singleton()) && !isSameOriginDataURL(url, options)) {
596 printAccessDeniedMessage(url);
597 return false;
598 }
599
600 if (options.mode == FetchOptions::Mode::NoCors && !protect(document->securityOrigin())->canRequest(url, OriginAccessPatternsForWebProcess::singleton()) && options.redirect != FetchOptions::Redirect::Follow && type != CachedResource::Type::Ping) {
601 ASSERT(type != CachedResource::Type::MainResource)((void)0);
602 if (RefPtr frame = this->frame()) {
603 if (RefPtr frameDocument = frame->document())
604 frameDocument->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "No-Cors mode requires follow redirect mode"_s);
605 }
606 return false;
607 }
608
609 bool shouldReportViolationAsConsoleMessage = forPreload == ForPreload::No || isLinkPreload;
610 if (!allowedByContentSecurityPolicy(type, url, options, ContentSecurityPolicy::RedirectResponseReceived::No, { }, shouldReportViolationAsConsoleMessage))
611 return false;
612
613 if (shouldRequestBeBlockedByIntegrityPolicy(*document, options, url))
614 return false;
615 }
616
617 // SVG Images have unique security rules that prevent all subresource requests except for data urls.
618 if (type != CachedResource::Type::MainResource && frame()) {
619 if (RefPtr page = frame()->page()) {
620 if (page->chrome().client().isSVGImageChromeClient() && !url.protocolIsData())
621 return false;
622 }
623 }
624
625 // 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.
626 // They'll still get a warning in the console about CSP blocking the load.
627
628 // FIXME: Should we consider whether the request is for preload here?
629 if (!checkInsecureContent(type, url, isRequestUpgradable))
630 return false;
631
632 return true;
633}
634
635// FIXME: Should we find a way to know whether the redirection is for a preload request like we do for CachedResourceLoader::canRequest?
636bool CachedResourceLoader::canRequestAfterRedirection(CachedResource::Type type, const URL& url, const ResourceLoaderOptions& options, const URL& preRedirectURL) const
637{
638 if (RefPtr document = m_document.get()) {
639 if (!protect(document->securityOrigin())->canDisplay(url, OriginAccessPatternsForWebProcess::singleton())) {
640 FrameLoader::reportLocalLoadFailed(protect(frame()).get(), url.stringCenterEllipsizedToLength());
641 LOG(ResourceLoading, "CachedResourceLoader::canRequestAfterRedirection URL was not allowed by SecurityOrigin::canDisplay")((void)0);
642 CACHEDRESOURCELOADER_RELEASE_LOG("canRequestAfterRedirection: URL was not allowed by SecurityOrigin::canDisplay");
643 return false;
644 }
645
646 // 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.
647 // But we currently allow at least data URLs to be loaded.
648
649 if (options.mode == FetchOptions::Mode::SameOrigin && !protect(document->securityOrigin())->canRequest(url, OriginAccessPatternsForWebProcess::singleton())) {
650 CACHEDRESOURCELOADER_RELEASE_LOG("canRequestAfterRedirection: URL was not allowed by SecurityOrigin::canRequest");
651 printAccessDeniedMessage(url);
652 return false;
653 }
654
655 if (!allowedByContentSecurityPolicy(type, url, options, ContentSecurityPolicy::RedirectResponseReceived::Yes, preRedirectURL)) {
656 CACHEDRESOURCELOADER_RELEASE_LOG("canRequestAfterRedirection: URL was not allowed by content policy");
657 return false;
658 }
659 }
660
661 // 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.
662 // They'll still get a warning in the console about CSP blocking the load.
663 if (!checkInsecureContent(type, url, MixedContentChecker::IsUpgradable::No)) {
664 CACHEDRESOURCELOADER_RELEASE_LOG("canRequestAfterRedirection: URL was not allowed because content is insecure");
665 return false;
666 }
667
668 return true;
669}
670
671// Created by binding generator.
672String convertEnumerationToString(FetchOptions::Destination);
673String convertEnumerationToString(FetchOptions::Mode);
674
675const String& convertEnumerationToString(FetchMetadataSite enumerationValue)
676{
677 static NeverDestroyed<const String> none(MAKE_STATIC_STRING_IMPL("none")([] { static StaticStringImpl impl("none"); return &impl;
}())
);
678 static NeverDestroyed<const String> sameOrigin(MAKE_STATIC_STRING_IMPL("same-origin")([] { static StaticStringImpl impl("same-origin"); return &
impl; }())
);
679 static NeverDestroyed<const String> sameSite(MAKE_STATIC_STRING_IMPL("same-site")([] { static StaticStringImpl impl("same-site"); return &
impl; }())
);
680 static NeverDestroyed<const String> crossSite(MAKE_STATIC_STRING_IMPL("cross-site")([] { static StaticStringImpl impl("cross-site"); return &
impl; }())
);
681
682 switch (enumerationValue) {
683 case FetchMetadataSite::None:
684 return none;
685 case FetchMetadataSite::SameOrigin:
686 return sameOrigin;
687 case FetchMetadataSite::SameSite:
688 return sameSite;
689 case FetchMetadataSite::CrossSite:
690 return crossSite;
691 }
692
693 ASSERT_NOT_REACHED()((void)0);
694 return emptyString();
695}
696
697static void updateRequestFetchMetadataHeaders(ResourceRequest& request, const ResourceLoaderOptions& options, FetchMetadataSite site)
698{
699 // Implementing step 13 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch as of 22 Feb 2022
700 // https://w3c.github.io/webappsec-fetch-metadata/#fetch-integration
701 Ref requestOrigin = SecurityOrigin::create(request.url());
702 if (!requestOrigin->isPotentiallyTrustworthy())
703 return;
704
705 String destinationString;
706 // The Fetch IDL documents this as "" while FetchMetadata expects "empty".
707 if (options.destination == FetchOptions::Destination::EmptyString || options.loadedFromFetch == LoadedFromFetch::Yes)
708 destinationString = "empty"_s;
709 else
710 destinationString = convertEnumerationToString(options.destination);
711
712 request.setHTTPHeaderField(HTTPHeaderName::SecFetchDest, WTF::move(destinationString));
713 request.setHTTPHeaderField(HTTPHeaderName::SecFetchMode, convertEnumerationToString(options.mode));
714 request.setHTTPHeaderField(HTTPHeaderName::SecFetchSite, convertEnumerationToString(site));
715}
716
717static FetchMetadataSite computeFetchMetadataSiteInternal(const ResourceRequest& request, CachedResource::Type type, FetchOptions::Mode mode, const SecurityOrigin* originalOrigin, const LocalFrame* frame, FetchMetadataSite originalSite, bool isDirectlyUserInitiatedRequest)
718{
719 ASSERT(frame || originalOrigin)((void)0);
720
721 // This is true when a user causes a request, such as entering a URL.
722 if (mode == FetchOptions::Mode::Navigate && type == CachedResource::Type::MainResource && isDirectlyUserInitiatedRequest)
723 return FetchMetadataSite::None;
724
725 Ref contextOrigin = originalOrigin ? *originalOrigin : protect(frame->document())->securityOrigin();
726 if (type == CachedResource::Type::MainResource && frame && frame->loader().activeDocumentLoader()) {
727 if (auto& request = frame->loader().activeDocumentLoader()->triggeringAction().requester())
728 contextOrigin = request->securityOrigin;
729 }
730
731 // If this is a redirect we start with the old value.
732 // The value can never get more "secure" so a full redirect
733 // chain only degrades towards cross-site.
734 Ref requestOrigin = SecurityOrigin::create(request.url());
735 if ((originalSite == FetchMetadataSite::SameOrigin || originalSite == FetchMetadataSite::None) && contextOrigin->isSameOriginAs(requestOrigin))
736 return FetchMetadataSite::SameOrigin;
737 if (originalSite != FetchMetadataSite::CrossSite && contextOrigin->isSameSiteAs(requestOrigin))
738 return FetchMetadataSite::SameSite;
739 return FetchMetadataSite::CrossSite;
740}
741
742FetchMetadataSite CachedResourceLoader::computeFetchMetadataSite(const ResourceRequest& request, CachedResource::Type type, FetchOptions::Mode mode, const LocalFrame& frame, bool isDirectlyUserInitiatedRequest)
743{
744 return computeFetchMetadataSiteInternal(request, type, mode, nullptr, &frame, FetchMetadataSite::SameOrigin, isDirectlyUserInitiatedRequest);
745}
746
747FetchMetadataSite CachedResourceLoader::computeFetchMetadataSiteAfterRedirection(const ResourceRequest& request, CachedResource::Type type, FetchOptions::Mode mode, const SecurityOrigin& originalOrigin, FetchMetadataSite originalSite, bool isDirectlyUserInitiatedRequest)
748{
749 return computeFetchMetadataSiteInternal(request, type, mode, &originalOrigin, nullptr, originalSite, isDirectlyUserInitiatedRequest);
750}
751
752static bool shouldPerformHTTPSUpgrade(const URL& originalURL, const URL& newURL, const LocalFrame& frame, CachedResource::Type type, bool isHTTPSByDefaultEnabled, OptionSet<AdvancedPrivacyProtections> advancedPrivacyProtections, HTTPSByDefaultMode httpsByDefaultMode)
753{
754 if (!frame.isMainFrame() || type != CachedResource::Type::MainResource)
755 return false;
756
757 bool isSameSiteNavigation = (originalURL.isEmpty() || RegistrableDomain(newURL) == RegistrableDomain(originalURL));
758 const bool isSameSiteBypassEnabled =
759 isSameSiteNavigation
760 && (advancedPrivacyProtections.contains(AdvancedPrivacyProtections::HTTPSOnlyExplicitlyBypassedForDomain) || frame.loader().shouldNavigateWithHTTP(isSameSiteNavigation) || originalURL.protocolIs("http"_s));
761
762 if (RefPtr document = frame.document(); document && document->quirks().shouldNotAutoUpgradeToHTTPSNavigation(newURL))
763 return false;
764
765 return (isHTTPSByDefaultEnabled || httpsByDefaultMode != HTTPSByDefaultMode::Disabled)
766 && newURL.protocolIs("http"_s)
767 && !isSameSiteBypassEnabled
768 && !frame.loader().isHTTPFallbackInProgressOrUpgradeDisabled();
769}
770
771bool CachedResourceLoader::updateRequestAfterRedirection(CachedResource::Type type, ResourceRequest& request, const ResourceLoaderOptions& options, FetchMetadataSite site, const URL& preRedirectURL, const URL& lastRedirectURL)
772{
773 ASSERT(m_documentLoader)((void)0);
774 if (!m_documentLoader)
775 return true;
776
777 if (RefPtr document = m_documentLoader->cachedResourceLoader().document()) {
778 bool alwaysUpgradeMixedContent = document->frame() ? MixedContentChecker::shouldUpgradeInsecureContent(Ref { *document->frame() }, isUpgradableTypeFromResourceType(type), request.url(), options.destination, options.initiator) : false;
779 upgradeInsecureResourceRequestIfNeeded(request, *document, alwaysUpgradeMixedContent ? ContentSecurityPolicy::AlwaysUpgradeRequest::Yes : ContentSecurityPolicy::AlwaysUpgradeRequest::No);
780 }
781
782 RefPtr frame = m_documentLoader->frame();
783 if (frame) {
784 RefPtr page = frame->page();
785 bool isHTTPSByDefaultEnabled = page ? page->settings().httpsByDefault() : false;
786 if (shouldPerformHTTPSUpgrade(lastRedirectURL, request.url(), *frame, type, isHTTPSByDefaultEnabled, m_documentLoader->advancedPrivacyProtections(), m_documentLoader->httpsByDefaultMode())) {
787 auto portsForUpgradingInsecureScheme = page ? std::optional { page->portsForUpgradingInsecureSchemeForTesting() } : std::nullopt;
788 auto upgradePort = (portsForUpgradingInsecureScheme && request.url().port() == portsForUpgradingInsecureScheme->first) ? std::optional { portsForUpgradingInsecureScheme->second } : std::nullopt;
789 request.upgradeInsecureRequestIfNeeded(ShouldUpgradeLocalhostAndIPAddress::No, upgradePort);
790 }
791 }
792
793 // FIXME: We might want to align the checks done here with the ones done in CachedResourceLoader::requestResource, content extensions blocking in particular.
794
795 Ref requestOrigin = SecurityOrigin::create(request.url());
796 Ref preRedirectOrigin = SecurityOrigin::create(preRedirectURL);
797
798 // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.2
799 // Any cross-site redirect makes a request not same-site.
800 if (!requestOrigin->isSameSiteAs(preRedirectOrigin))
801 request.setIsSameSite(false);
802
803 if (frame) {
804 RefPtr frameDocument = frame->document();
805 if (!frameDocument || !frameDocument->quirks().shouldDisableFetchMetadata()) {
806 // In the case of a protocol downgrade we strip all FetchMetadata headers.
807 // Otherwise we add or update FetchMetadata.
808 if (!requestOrigin->isPotentiallyTrustworthy()) {
809 request.removeHTTPHeaderField(HTTPHeaderName::SecFetchDest);
810 request.removeHTTPHeaderField(HTTPHeaderName::SecFetchMode);
811 request.removeHTTPHeaderField(HTTPHeaderName::SecFetchSite);
812 } else
813 updateRequestFetchMetadataHeaders(request, options, site);
814 }
815 }
816
817 return canRequestAfterRedirection(type, request.url(), options, preRedirectURL);
818}
819
820bool CachedResourceLoader::canRequestInContentDispositionAttachmentSandbox(CachedResource::Type type, const URL& url) const
821{
822 RefPtr<Document> document;
823
824 // FIXME: Do we want to expand this to all resource types that the mixed content checker would consider active content?
825 switch (type) {
826 case CachedResource::Type::MainResource:
827 if (RefPtr ownerElement = frame() ? frame()->ownerElement() : nullptr) {
828 document = ownerElement->document();
829 break;
830 }
831 return true;
832 case CachedResource::Type::CSSStyleSheet:
833 document = m_document.get();
834 break;
835 default:
836 return true;
837 }
838
839 ASSERT(document)((void)0);
840 if (!document)
841 return true;
842
843 if (!document->shouldEnforceContentDispositionAttachmentSandbox() || protect(document->securityOrigin())->canRequest(url, OriginAccessPatternsForWebProcess::singleton()))
844 return true;
845
846 auto message = makeString("Unsafe attempt to load URL "_s, url.stringCenterEllipsizedToLength(), " from document with Content-Disposition: attachment at URL "_s, document->url().stringCenterEllipsizedToLength(), '.');
847 document->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
848 return false;
849}
850
851bool CachedResourceLoader::shouldContinueAfterNotifyingLoadedFromMemoryCache(const CachedResourceRequest& request, CachedResource& resource, ResourceError& error)
852{
853 if (!frame() || resource.status() != CachedResource::Cached)
854 return true;
855
856 ResourceRequest newRequest = ResourceRequest(URL { resource.url() });
857 newRequest.setRequester(request.resourceRequest().requester());
858 newRequest.setInitiatorIdentifier(request.resourceRequest().initiatorIdentifier());
859 if (auto inspectorInitiatorNodeIdentifier = request.resourceRequest().inspectorInitiatorNodeIdentifier())
860 newRequest.setInspectorInitiatorNodeIdentifier(*inspectorInitiatorNodeIdentifier);
861 if (request.resourceRequest().hiddenFromInspector())
862 newRequest.setHiddenFromInspector(true);
863 protect(frame())->loader().loadedResourceFromMemoryCache(resource, newRequest, error);
864
865 // FIXME <http://webkit.org/b/113251>: If the delegate modifies the request's
866 // URL, it is no longer appropriate to use this CachedResource.
867 return !newRequest.isNull();
868}
869
870bool CachedResourceLoader::shouldUpdateCachedResourceWithCurrentRequest(const CachedResource& resource, const CachedResourceRequest& request)
871{
872 // 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.
873 if (resource.type() == CachedResource::Type::FontResource)
874 return false;
875
876 if (resource.type() == CachedResource::Type::SVGFontResource)
877 return false;
878
879#if ENABLE(XSLT)(defined 1 && 1)
880 // Load is same-origin, we do not check for CORS.
881 if (resource.type() == CachedResource::Type::XSLStyleSheet)
882 return false;
883#endif
884
885 // FIXME: We should enable resource reuse for these resource types
886 switch (resource.type()) {
887 case CachedResource::Type::SVGDocumentResource:
888 return false;
889 case CachedResource::Type::MainResource:
890 return false;
891 case CachedResource::Type::LinkPrefetch:
892 return false;
893 default:
894 break;
895 }
896
897 if (resource.options().cachingPolicy == CachingPolicy::AllowCachingMainResourcePrefetch)
898 return false;
899
900 if (resource.options().mode != request.options().mode || !serializedOriginsMatch(protect(request.origin()), protect(resource.origin())))
901 return true;
902
903 if (resource.options().redirect != request.options().redirect && resource.hasRedirections())
904 return true;
905
906 return false;
907}
908
909static inline bool NODELETE[[clang::annotate_type("webkit.nodelete")]] isResourceSuitableForDirectReuse(const CachedResource& resource, const CachedResourceRequest& request)
910{
911 // FIXME: For being loaded requests, the response tainting may not be correctly computed if the fetch mode is not the same.
912 // Even if the fetch mode is the same, we are not sure that the resource can be reused (Vary: Origin header for instance).
913 // We should find a way to improve this.
914 if (resource.status() != CachedResource::Cached)
915 return false;
916
917 // If the cached resource has not followed redirections, it is incomplete and we should not use it.
918 // Let's make sure the memory cache has no such resource.
919 ASSERT(resource.response().type() != ResourceResponse::Type::Opaqueredirect)((void)0);
920
921 // We could support redirect modes other than Follow in case of a redirected resource.
922 // This case is rare and is not worth optimizing currently.
923 if (request.options().redirect != FetchOptions::Redirect::Follow && resource.hasRedirections())
924 return false;
925
926 // FIXME: Implement reuse of cached raw resources.
927 if ((resource.type() == CachedResource::Type::RawResource || resource.type() == CachedResource::Type::MediaResource) && resource.options().cachingPolicy != CachingPolicy::AllowCachingMainResourcePrefetch)
928 return false;
929
930 if (resource.type() == CachedResource::Type::Beacon || resource.type() == CachedResource::Type::Ping)
931 return false;
932
933 return true;
934}
935
936Ref<CachedResource> CachedResourceLoader::updateCachedResourceWithCurrentRequest(const CachedResource& resource, CachedResourceRequest&& request, PAL::SessionID sessionID, const CookieJar& cookieJar, const Settings& settings)
937{
938 if (!isResourceSuitableForDirectReuse(resource, request)) {
939 request.setCachingPolicy(CachingPolicy::DisallowCaching);
940 return loadResource(resource.type(), sessionID, WTF::move(request), cookieJar, settings, MayAddToMemoryCache::Yes);
941 }
942
943 // TODO: we're using the caching policy as a flag to signal that this is a prefetch match. We'd be better off with an explicit prefetch match flag.
944 if (resource.options().cachingPolicy == CachingPolicy::AllowCachingMainResourcePrefetch)
945 request.setCachingPolicy(CachingPolicy::AllowCachingMainResourcePrefetch);
946 auto resourceHandle = createResource(resource.type(), WTF::move(request), sessionID, &cookieJar, settings, protect(document()).get());
947 resourceHandle->loadFrom(resource);
948 return resourceHandle;
949}
950
951void CachedResourceLoader::prepareFetch(CachedResource::Type type, CachedResourceRequest& request)
952{
953 // Implementing step 1.1 to 1.6 of https://fetch.spec.whatwg.org/#concept-fetch.
954 if (RefPtr document = this->document()) {
955 if (!request.origin())
956 request.setOrigin(document->securityOrigin());
957 request.setClientIdentifierIfNeeded(document->identifier());
958 if (RefPtr activeServiceWorker = document->activeServiceWorker())
959 request.setSelectedServiceWorkerRegistrationIdentifierIfNeeded(activeServiceWorker->registrationIdentifier());
960 }
961
962 request.setAcceptHeaderIfNone(type);
963
964 // Accept-Language value is handled in underlying port-specific code.
965 // FIXME: Decide whether to support client hints
966}
967
968void CachedResourceLoader::updateHTTPRequestHeaders(FrameLoader& frameLoader, CachedResource::Type type, CachedResourceRequest& request)
969{
970 // Implementing steps 11 to 19 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch as of 22 Feb 2022.
971
972 // FIXME: We should reconcile handling of MainResource with other resources.
973 if (type != CachedResource::Type::MainResource && request.options().cachingPolicy != CachingPolicy::AllowCachingMainResourcePrefetch)
974 request.updateReferrerAndOriginHeaders(frameLoader);
975 // FetchMetadata depends on PSL to determine same-site relationships and without this
976 // ability it is best to not set any FetchMetadata headers as sites generally expect
977 // all of them or none.
978 Ref frame = frameLoader.frame();
979 if (frame->document() && !protect(frame->document())->quirks().shouldDisableFetchMetadata()) {
980 auto site = computeFetchMetadataSite(request.resourceRequest(), type, request.options().mode, frame, frame->isMainFrame() && m_documentLoader && m_documentLoader->isRequestFromClientOrUserInput());
981 updateRequestFetchMetadataHeaders(request.resourceRequest(), request.options(), site);
982 }
983 request.updateUserAgentHeader(frameLoader);
984
985 if (frame->loader().loadType() == FrameLoadType::ReloadFromOrigin)
986 request.updateCacheModeIfNeeded(cachePolicy(type, request.resourceRequest().url()));
987 request.updateAccordingCacheMode();
988 request.updateAcceptEncodingHeader();
989}
990
991static FetchOptions::Destination NODELETE[[clang::annotate_type("webkit.nodelete")]] destinationForType(CachedResource::Type type, LocalFrame& frame)
992{
993 switch (type) {
994 case CachedResource::Type::MainResource:
995 return frame.isMainFrame() ? FetchOptions::Destination::Document : FetchOptions::Destination::Iframe;
996 case CachedResource::Type::SVGDocumentResource:
997 return FetchOptions::Destination::Document;
998 case CachedResource::Type::ImageResource:
999 case CachedResource::Type::Icon:
1000 return FetchOptions::Destination::Image;
1001 case CachedResource::Type::CSSStyleSheet:
1002 return FetchOptions::Destination::Style;
1003 case CachedResource::Type::JSON:
1004 return FetchOptions::Destination::Json;
1005 case CachedResource::Type::Script:
1006 return FetchOptions::Destination::Script;
1007 case CachedResource::Type::FontResource:
1008 case CachedResource::Type::SVGFontResource:
1009 return FetchOptions::Destination::Font;
1010#if ENABLE(XSLT)(defined 1 && 1)
1011 case CachedResource::Type::XSLStyleSheet:
1012 return FetchOptions::Destination::Xslt;
1013#endif
1014#if ENABLE(VIDEO)(defined 1 && 1)
1015 case CachedResource::Type::TextTrackResource:
1016 return FetchOptions::Destination::Track;
1017#endif
1018#if ENABLE(APPLICATION_MANIFEST)(defined 1 && 1)
1019 case CachedResource::Type::ApplicationManifest:
1020 return FetchOptions::Destination::Manifest;
1021#endif
1022 case CachedResource::Type::Beacon:
1023 case CachedResource::Type::Ping:
1024 case CachedResource::Type::LinkPrefetch:
1025 case CachedResource::Type::RawResource:
1026 case CachedResource::Type::MediaResource:
1027#if ENABLE(MODEL_ELEMENT)(defined 1 && 1)
1028 case CachedResource::Type::EnvironmentMapResource:
1029 case CachedResource::Type::ModelResource:
1030#endif
1031 // The caller is responsible for setting the appropriate destination.
1032 return FetchOptions::Destination::EmptyString;
1033 }
1034 ASSERT_NOT_REACHED()((void)0);
1035 return FetchOptions::Destination::EmptyString;
1036}
1037
1038static inline bool isSVGImageCachedResource(const CachedResource* resource)
1039{
1040 auto* cachedImage = dynamicDowncast<CachedImage>(resource);
1041 return cachedImage && is<SVGImage>(cachedImage->image());
1042}
1043
1044static inline SVGImage* cachedResourceSVGImage(CachedResource* resource)
1045{
1046 auto* cachedImage = dynamicDowncast<CachedImage>(resource);
1047 return cachedImage ? dynamicDowncast<SVGImage>(cachedImage->image()) : nullptr;
1048}
1049
1050static bool NODELETE[[clang::annotate_type("webkit.nodelete")]] computeMayAddToMemoryCache(const CachedResourceRequest& newRequest, const CachedResource* existingResource)
1051{
1052 return !existingResource || !existingResource->isPreloaded() || newRequest.options().serviceWorkersMode != ServiceWorkersMode::None || existingResource->options().serviceWorkersMode == ServiceWorkersMode::None;
1053}
1054
1055ResourceErrorOr<Ref<CachedResource>> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest&& request, ForPreload forPreload, ImageLoading imageLoading)
1056{
1057 URL url = request.resourceRequest().url();
1058 RefPtr framePtr = this->frame();
1059 if (!framePtr) {
1060 CACHEDRESOURCELOADER_RELEASE_LOG("requestResource: failed because no frame");
1061 return makeUnexpected(ResourceError { errorDomainWebKitInternal, 0, url, "Invalid loader state"_s });
1062 }
1063 Ref frame = framePtr.releaseNonNull();
1064
1065 RefPtr pagePtr = frame->page();
1066 if (!pagePtr) {
1067 CACHEDRESOURCELOADER_RELEASE_LOG("requestResource: failed because no page");
1068 return makeUnexpected(ResourceError { errorDomainWebKitInternal, 0, url, "Invalid loader state"_s });
1069 }
1070 Ref page = pagePtr.releaseNonNull();
1071
1072 if (!url.isValid()) {
1073 CACHEDRESOURCELOADER_RELEASE_LOG_WITH_FRAME("requestResource: URL is invalid", frame.get());
1074 return makeUnexpected(ResourceError { errorDomainWebKitInternal, 0, url, "URL is invalid"_s });
1075 }
1076
1077 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)((void)0);
1078
1079 request.setDestinationIfNotSet(destinationForType(type, frame));
1080
1081 // Entry point to https://fetch.spec.whatwg.org/#main-fetch.
1082 std::unique_ptr<ResourceRequest> originalRequest;
1083 if (CachedResource::shouldUsePingLoad(type) || request.options().destination == FetchOptions::Destination::EmptyString) {
1084 originalRequest = makeUnique<ResourceRequest>(request.resourceRequest());
1085 originalRequest->clearHTTPReferrer();
1086 originalRequest->clearHTTPOrigin();
1087 }
1088
1089 prepareFetch(type, request);
1090
1091 if (request.options().destination == FetchOptions::Destination::Document || request.options().destination == FetchOptions::Destination::Iframe) {
1092 // FIXME: Identify HSTS cases and avoid adding the header. <https://bugs.webkit.org/show_bug.cgi?id=157885>
1093 if (!url.protocolIs("https"_s))
1094 request.resourceRequest().setHTTPHeaderField(HTTPHeaderName::UpgradeInsecureRequests, "1"_s);
1095 }
1096
1097 bool isRequestUpgradable { false };
1098 RefPtr document = m_document.get();
1099 if (document) {
1100 isRequestUpgradable = MixedContentChecker::shouldUpgradeInsecureContent(frame, isUpgradableTypeFromResourceType(type), url, request.options().destination, request.options().initiator);
1101 request.upgradeInsecureRequestIfNeeded(*document, isRequestUpgradable ? ContentSecurityPolicy::AlwaysUpgradeRequest::Yes : ContentSecurityPolicy::AlwaysUpgradeRequest::No);
1102 url = request.resourceRequest().url();
1103 }
1104
1105 URL committedDocumentURL { frame->document() ? frame->document()->url() : URL { } };
1106 if (RefPtr documentLoader = m_documentLoader.get()) {
1107 if (shouldPerformHTTPSUpgrade(committedDocumentURL, request.resourceRequest().url(), frame, type, page->settings().httpsByDefault(), documentLoader->advancedPrivacyProtections(), documentLoader->httpsByDefaultMode())) {
1108 auto portsForUpgradingInsecureScheme = page->portsForUpgradingInsecureSchemeForTesting();
1109 auto upgradePort = (portsForUpgradingInsecureScheme && request.resourceRequest().url().port() == portsForUpgradingInsecureScheme->first) ? std::optional { portsForUpgradingInsecureScheme->second } : std::nullopt;
1110 request.resourceRequest().upgradeInsecureRequestIfNeeded(ShouldUpgradeLocalhostAndIPAddress::No, upgradePort);
1111 url = request.resourceRequest().url();
1112 }
1113 }
1114
1115 // We are passing url as well as request, as request url may contain a fragment identifier.
1116 if (!canRequest(type, url, request.options(), forPreload, isRequestUpgradable ? MixedContentChecker::IsUpgradable::Yes : MixedContentChecker::IsUpgradable::No, request.isLinkPreload())) {
1117 CACHEDRESOURCELOADER_RELEASE_LOG_WITH_FRAME("requestResource: Not allowed to request resource", frame.get());
1118 return makeUnexpected(ResourceError { errorDomainWebKitInternal, 0, url, "Not allowed to request resource"_s, ResourceError::Type::AccessControl });
1119 }
1120
1121 if (!ResourceLoader::isPortAllowed(url)) {
1122 if (forPreload == ForPreload::No)
1123 FrameLoader::reportBlockedLoadFailed(frame, url);
1124 CACHEDRESOURCELOADER_RELEASE_LOG_WITH_FRAME("CachedResourceLoader::requestResource URL has a blocked port", frame.get());
1125 return makeUnexpected(frame->loader().blockedError(request.resourceRequest()));
1126 }
1127
1128 if (isIPAddressDisallowed(url)) {
1129 FrameLoader::reportBlockedLoadFailed(frame, url);
1130 CACHEDRESOURCELOADER_RELEASE_LOG_WITH_FRAME("CachedResourceLoader::requestResource URL has a disallowed address", frame.get());
1131 return makeUnexpected(frame->loader().blockedError(request.resourceRequest()));
1132 }
1133
1134 request.updateReferrerPolicy(document ? document->referrerPolicy() : ReferrerPolicy::Default);
1135
1136 if (InspectorInstrumentation::willIntercept(frame.ptr(), request.resourceRequest()))
1137 request.setCachingPolicy(CachingPolicy::DisallowCaching);
1138
1139 if (RefPtr documentLoader = m_documentLoader.get()) {
1140 bool madeHTTPS { request.resourceRequest().wasSchemeOptimisticallyUpgraded() };
1141#if ENABLE(CONTENT_EXTENSIONS)(defined 1 && 1)
1142 const auto& resourceRequest = request.resourceRequest();
1143 RefPtr userContentProvider = frame->userContentProvider();
1144 if (request.options().shouldEnableContentExtensionsCheck == ShouldEnableContentExtensionsCheck::Yes && userContentProvider) {
1145 RegistrableDomain originalDomain { resourceRequest.url() };
1146 const URL& redirectFromURL = (documentLoader->isContinuingLoadAfterNavigationPolicyDecision() && documentLoader->originalRequest().url() != resourceRequest.url()) ? documentLoader->originalRequest().url() : URL { };
1147 auto results = userContentProvider->processContentRuleListsForLoad(page, resourceRequest.url(), ContentExtensions::toResourceType(type, request.resourceRequest().requester(), frame->isMainFrame()), *documentLoader, redirectFromURL);
1148 madeHTTPS = results.summary.madeHTTPS;
1149 bool shouldBlock = results.shouldBlock();
1150 std::optional<String> consoleMessage;
1151 if (shouldBlock && document)
1152 consoleMessage = ContentExtensions::customTrackerBlockingMessageForConsole(results, resourceRequest.url());
1153 request.applyResults(WTF::move(results), page.ptr());
1154 if (shouldBlock) {
1155 CACHEDRESOURCELOADER_RELEASE_LOG_WITH_FRAME("requestResource: Resource blocked by content blocker", frame.get());
1156 if (type == CachedResource::Type::MainResource) {
1157 auto resource = createResource(type, WTF::move(request), page->sessionID(), protect(page->cookieJar()).ptr(), page->settings(), document.get());
1158 resource->error(CachedResource::Status::LoadError);
1159 resource->setResourceError(ResourceError(ContentExtensions::WebKitContentBlockerDomain, 0, resourceRequest.url(), WEB_UI_STRING("The URL was blocked by a content blocker", "WebKitErrorBlockedByContentBlocker description")WebCore::localizedString(((CFStringRef) __builtin___CFStringMakeConstantString
("" "The URL was blocked by a content blocker" "")))
));
1160 return WTF::move(resource);
1161 }
1162 if (document && consoleMessage)
1163 document->addConsoleMessage(MessageSource::ContentBlocker, MessageLevel::Info, WTF::move(*consoleMessage));
1164 return makeUnexpected(ResourceError { errorDomainWebKitInternal, 0, url, "Resource blocked by content blocker"_s, ResourceError::Type::AccessControl });
1165 }
1166 if (type == CachedResource::Type::MainResource && RegistrableDomain { resourceRequest.url() } != originalDomain) {
1167 FrameLoadRequest frameLoadRequest(frame, ResourceRequest(URL { resourceRequest.url() }));
1168 frameLoadRequest.setIsContentRuleListRedirect(true);
1169 frame->loader().load(WTF::move(frameLoadRequest));
1170 return makeUnexpected(ResourceError { errorDomainWebKitInternal, 0, url, "Loading in a new process"_s, ResourceError::Type::Cancellation });
1171 }
1172 }
1173#endif
1174 // FIXME: Propagate a better error code
1175 bool isHTTPSOnlyActive = (m_documentLoader->httpsByDefaultMode() == HTTPSByDefaultMode::UpgradeWithUserMediatedFallback || m_documentLoader->httpsByDefaultMode() == HTTPSByDefaultMode::UpgradeAndNoFallback)
1176 && !m_documentLoader->advancedPrivacyProtections().contains(AdvancedPrivacyProtections::HTTPSOnlyExplicitlyBypassedForDomain)
1177 && !frame->loader().isHTTPFallbackInProgressOrUpgradeDisabled()
1178 && !(committedDocumentURL.protocolIs("http"_s) && request.resourceRequest().isSameSite());
1179 if (!madeHTTPS && !LegacySchemeRegistry::shouldTreatURLSchemeAsSecure(request.resourceRequest().url().protocol()) && type == CachedResource::Type::MainResource && isHTTPSOnlyActive)
1180 return makeUnexpected(platformStrategies()->loaderStrategy()->httpNavigationWithHTTPSOnlyError(request.resourceRequest()));
1181
1182 if (madeHTTPS
1183 && type == CachedResource::Type::MainResource
1184 && m_documentLoader->isLoadingMainResource()) {
1185 // This is to make sure the correct 'new' URL shows in the location bar.
1186 protect(protect(m_documentLoader)->frameLoader())->client().dispatchDidChangeProvisionalURL();
1187 }
1188 url = request.resourceRequest().url(); // The content extension could have changed it from http to https.
1189 url = MemoryCache::removeFragmentIdentifierIfNeeded(url); // Might need to remove fragment identifier again.
1190
1191 if (!m_documentLoader->customHeaderFields().isEmpty()) {
1192 bool sameOriginRequest = false;
1193 Ref requestedOrigin = SecurityOrigin::create(url);
1194 if (type == CachedResource::Type::MainResource) {
1195 if (frame->isMainFrame())
1196 sameOriginRequest = true;
1197 else if (RefPtr topDocument = page->localTopDocument())
1198 sameOriginRequest = protect(topDocument->securityOrigin())->isSameSchemeHostPort(requestedOrigin.get());
1199 } else if (document) {
1200 sameOriginRequest = protect(document->topOrigin())->isSameSchemeHostPort(requestedOrigin.get())
1201 && protect(document->securityOrigin())->isSameSchemeHostPort(requestedOrigin.get());
1202 }
1203 for (auto& fields : m_documentLoader->customHeaderFields()) {
1204 if (sameOriginRequest || fields.thirdPartyDomainsMatch(url)) {
1205 for (auto& field : fields.fields)
1206 request.resourceRequest().setHTTPHeaderField(field.name(), field.value());
1207 }
1208 }
1209 }
1210 }
1211
1212 ResourceLoadTiming loadTiming;
1213 loadTiming.markStartTime();
1214 InitiatorContext initiatorContext = request.options().initiatorContext;
1215
1216 if (request.resourceRequest().url().protocolIsInHTTPFamily())
1217 updateHTTPRequestHeaders(frame->loader(), type, request);
1218 request.disableCachingIfNeeded();
1219
1220 Ref memoryCache = MemoryCache::singleton();
1221 if (request.allowsCaching() && memoryCache->disabled())
1222 m_documentResources.remove(url.string());
1223
1224 // See if we can use an existing resource from the cache.
1225 RefPtr<CachedResource> resource;
1226 CheckedPtr<ContentSecurityPolicy> contentSecurityPolicy;
1227 if (document) {
1228 request.setDomainForCachePartition(*document);
1229 request.resourceRequest().setFirstPartyForCookies(document->firstPartyForCookies());
1230 contentSecurityPolicy = document->contentSecurityPolicy();
1231 }
1232
1233 if (request.allowsCaching())
1234 resource = memoryCache->resourceForRequest(request.resourceRequest(), page->sessionID());
1235
1236 if (resource && request.isLinkPreload() && !resource->isLinkPreload())
1237 resource->setLinkPreload();
1238
1239 Ref cookieJar = page->cookieJar();
1240
1241 FrameLoader::addSameSiteInfoToRequestIfNeeded(request.resourceRequest(), document.get());
1242
1243 bool ignoreForRequestCount = request.ignoreForRequestCount();
1244 auto mayAddToMemoryCache = computeMayAddToMemoryCache(request, resource.get()) ? MayAddToMemoryCache::Yes : MayAddToMemoryCache::No;
1245 RevalidationPolicy policy = determineRevalidationPolicy(type, request, resource.get(), forPreload, imageLoading);
1246 switch (policy) {
1247 case Reload:
1248 if (mayAddToMemoryCache == MayAddToMemoryCache::Yes)
1249 memoryCache->remove(*resource);
1250 [[fallthrough]];
1251 case Load:
1252 if (resource && mayAddToMemoryCache == MayAddToMemoryCache::Yes)
1253 memoryCache->remove(*resource);
1254 resource = loadResource(type, page->sessionID(), WTF::move(request), cookieJar, page->settings(), mayAddToMemoryCache);
1255 break;
1256 case Revalidate:
1257 resource = revalidateResource(WTF::move(request), *resource);
1258 break;
1259 case Use:
1260 ASSERT(resource)((void)0);
1261 if (request.options().mode == FetchOptions::Mode::Navigate && !frame->isMainFrame()) {
1262 RefPtr localParentFrame = dynamicDowncast<LocalFrame>(frame->tree().parent());
1263 if (RefPtr parentDocument = localParentFrame ? localParentFrame->document() : nullptr) {
1264 auto coep = parentDocument->crossOriginEmbedderPolicy().value;
1265 if (auto error = validateCrossOriginResourcePolicy(coep, protect(parentDocument->securityOrigin()), request.resourceRequest().url(), resource->response(), ForNavigation::Yes, OriginAccessPatternsForWebProcess::singleton()))
1266 return makeUnexpected(WTF::move(*error));
1267 }
1268 }
1269 // Per the Fetch specification, the "cross-origin resource policy check" should only occur in the HTTP Fetch case (https://fetch.spec.whatwg.org/#concept-http-fetch).
1270 // However, per https://fetch.spec.whatwg.org/#main-fetch, if the request URL's protocol is "data:", then we should perform a scheme fetch which would end up
1271 // returning a response WITHOUT performing an HTTP fetch (and thus no CORP check).
1272 if (request.options().mode == FetchOptions::Mode::NoCors && !url.protocolIsData()) {
1273 auto coep = document ? document->crossOriginEmbedderPolicy().value : CrossOriginEmbedderPolicyValue::UnsafeNone;
1274 if (auto error = validateCrossOriginResourcePolicy(coep, *protect(request.origin()), request.resourceRequest().url(), resource->response(), ForNavigation::No, OriginAccessPatternsForWebProcess::singleton()))
1275 return makeUnexpected(WTF::move(*error));
1276 }
1277 if (request.options().mode == FetchOptions::Mode::NoCors) {
1278 if (auto error = validateRangeRequestedFlag(request.resourceRequest(), resource->response()))
1279 return makeUnexpected(WTF::move(*error));
1280 }
1281 if (shouldUpdateCachedResourceWithCurrentRequest(*resource, request)) {
1282 resource = updateCachedResourceWithCurrentRequest(*resource, WTF::move(request), page->sessionID(), cookieJar, page->settings());
1283 if (resource->status() != CachedResource::Status::Cached)
1284 policy = Load;
1285 } else {
1286 ResourceError error;
1287 if (!shouldContinueAfterNotifyingLoadedFromMemoryCache(request, *resource, error))
1288 return makeUnexpected(WTF::move(error));
1289 loadTiming.markEndTime();
1290
1291 memoryCache->resourceAccessed(*resource);
1292
1293 if (document && !resource->isLoading()) {
1294 Box<NetworkLoadMetrics> metrics;
1295 RefPtr documentLoader = document->loader();
1296 // Use the actual network load metrics if this is the first time loading this resource and it was loaded
1297 // for this document because it may have been speculatively preloaded.
1298 if (auto metricsFromResource = resource->takeNetworkLoadMetrics(); metricsFromResource && documentLoader && metricsFromResource->redirectStart >= documentLoader->timing().timeOrigin())
1299 metrics = WTF::move(metricsFromResource);
1300 auto resourceTiming = ResourceTiming::fromMemoryCache(url, request.initiatorType(), loadTiming, resource->response(), metrics ? *metrics : NetworkLoadMetrics::emptyMetrics(), *protect(request.origin()));
1301 if (initiatorContext == InitiatorContext::Worker)
1302 downcast<CachedRawResource>(resource.get())->finishedTimingForWorkerLoad(WTF::move(resourceTiming));
1303 else {
1304 ASSERT(initiatorContext == InitiatorContext::Document)((void)0);
1305 m_resourceTimingInfo.storeResourceTimingInitiatorInformation(*resource, request.initiatorType(), frame.ptr());
1306 m_resourceTimingInfo.addResourceTiming(*resource.get(), *document, WTF::move(resourceTiming));
1307 }
1308 }
1309
1310 if (forPreload == ForPreload::No)
1311 resource->setLoadPriority(request.priority(), request.fetchPriority());
1312 }
1313 break;
1314 }
1315 ASSERT(resource)((void)0);
1316 resource->setOriginalRequest(WTF::move(originalRequest));
1317
1318 if ((type == CachedResource::Type::Script || type == CachedResource::Type::JSON) && contentSecurityPolicy) {
1319 auto hashes = contentSecurityPolicy->hashesToReport();
1320 if (!hashes.isEmpty())
1321 resource->setIsHashReportingNeeded();
1322 }
1323
1324 if (RefPtr subresourceLoader = resource->loader(); forPreload == ForPreload::No && subresourceLoader && resource->ignoreForRequestCount()) {
1325 subresourceLoader->clearRequestCountTracker();
1326 resource->setIgnoreForRequestCount(false);
1327 subresourceLoader->resetRequestCountTracker(*this, *resource);
1328 ignoreForRequestCount = false;
1329 }
1330
1331 if ((policy != Use || resource->stillNeedsLoad()) && imageLoading == ImageLoading::Immediate) {
1332 resource->load(*this);
1333
1334 // We don't support immediate loads, but we do support immediate failure.
1335 if (resource->errorOccurred()) {
1336 if (resource->allowsCaching() && resource->inCache())
1337 memoryCache->remove(*resource);
1338
1339 auto resourceError = resource->resourceError();
1340 // Synchronous cancellations are likely due to access control.
1341 if (resourceError.isNull() || resourceError.isCancellation())
1342 return makeUnexpected(ResourceError { String(), 0, url, String(), ResourceError::Type::AccessControl });
1343 return makeUnexpected(resourceError);
1344 }
1345 } else if (policy == Use && !ignoreForRequestCount && resource->isLoading() && resource->loader()) {
1346 ++m_requestCount;
1347 resource->whenLoaded([weakThis = WeakPtr { *this }] {
1348 RefPtr protectedThis = weakThis.get();
1349 if (!protectedThis)
1350 return;
1351 if (--protectedThis->m_requestCount)
1352 return;
1353 if (RefPtr frame = protectedThis->frame())
1354 frame->loader().checkCompleted();
1355 });
1356 }
1357
1358 if (document && !document->loadEventFinished() && !resource->resourceRequest().url().protocolIsData())
1359 m_validatedURLs.add(resource->resourceRequest().url());
1360
1361 ASSERT(resource->url() == url.string())((void)0);
1362 m_documentResources.set(resource->url().string(), CachedResourceHandle { *resource });
1363 frame->loader().client().didLoadFromRegistrableDomain(RegistrableDomain(resource->resourceRequest().url()));
1364 return resource.releaseNonNull();
1365}
1366
1367void CachedResourceLoader::documentDidFinishLoadEvent()
1368{
1369 m_validatedURLs.clear();
1370
1371 // If m_preloads is not empty here, it's full of link preloads,
1372 // as speculative preloads were cleared at DCL.
1373 if (m_preloads && !m_preloads->isEmpty() && !m_unusedPreloadsTimer.isActive())
1374 m_unusedPreloadsTimer.startOneShot(unusedPreloadTimeout);
1375}
1376
1377void CachedResourceLoader::stopUnusedPreloadsTimer()
1378{
1379 m_unusedPreloadsTimer.stop();
1380}
1381
1382Ref<CachedResource> CachedResourceLoader::revalidateResource(CachedResourceRequest&& request, CachedResource& resource)
1383{
1384 ASSERT(resource.inCache())((void)0);
1385 Ref memoryCache = MemoryCache::singleton();
1386 ASSERT(!memoryCache->disabled())((void)0);
1387 ASSERT(resource.canUseCacheValidator())((void)0);
1388 ASSERT(!resource.resourceToRevalidate())((void)0);
1389 ASSERT(resource.allowsCaching())((void)0);
1390
1391 auto newResource = createResource(WTF::move(request), resource, protect(document()).get());
1392
1393 LOG(ResourceLoading, "Resource %p created to revalidate %p", newResource.ptr(), &resource)((void)0);
1394 newResource->setResourceToRevalidate(&resource);
1395
1396 memoryCache->remove(resource);
1397 memoryCache->add(newResource);
1398
1399 m_resourceTimingInfo.storeResourceTimingInitiatorInformation(newResource, newResource->initiatorType(), protect(frame()).get());
1400
1401 return newResource;
1402}
1403
1404Ref<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, PAL::SessionID sessionID, CachedResourceRequest&& request, const CookieJar& cookieJar, const Settings& settings, MayAddToMemoryCache mayAddToMemoryCache)
1405{
1406 Ref memoryCache = MemoryCache::singleton();
1407 ASSERT(!request.allowsCaching() || mayAddToMemoryCache == MayAddToMemoryCache::No || !memoryCache->resourceForRequest(request.resourceRequest(), sessionID)((void)0)
1408 || request.resourceRequest().cachePolicy() == ResourceRequestCachePolicy::DoNotUseAnyCache || request.resourceRequest().cachePolicy() == ResourceRequestCachePolicy::ReloadIgnoringCacheData || request.resourceRequest().cachePolicy() == ResourceRequestCachePolicy::RefreshAnyCacheData)((void)0);
1409
1410 LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.resourceRequest().url().stringCenterEllipsizedToLength().latin1().data())((void)0);
1411
1412 auto resource = createResource(type, WTF::move(request), sessionID, &cookieJar, settings, protect(document()).get());
1413
1414 if (resource->allowsCaching() && mayAddToMemoryCache == MayAddToMemoryCache::Yes)
1415 memoryCache->add(resource);
1416
1417 m_resourceTimingInfo.storeResourceTimingInitiatorInformation(resource, resource->initiatorType(), protect(frame()).get());
1418
1419 return resource;
1420}
1421
1422static inline bool mustReloadFromServiceWorkerOptions(const ResourceLoaderOptions& options, const ResourceLoaderOptions& cachedOptions)
1423{
1424 if (cachedOptions.cachingPolicy == CachingPolicy::AllowCachingMainResourcePrefetch)
1425 return false;
1426
1427 // FIXME: We should validate/specify this behavior.
1428 if (options.serviceWorkerRegistrationIdentifier != cachedOptions.serviceWorkerRegistrationIdentifier)
1429 return true;
1430
1431 if (options.serviceWorkersMode == cachedOptions.serviceWorkersMode)
1432 return false;
1433
1434 return cachedOptions.mode == FetchOptions::Mode::Navigate || cachedOptions.destination == FetchOptions::Destination::Worker || cachedOptions.destination == FetchOptions::Destination::Sharedworker;
1435}
1436
1437CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalidationPolicy(CachedResource::Type type, CachedResourceRequest& cachedResourceRequest, CachedResource* existingResource, ForPreload forPreload, ImageLoading imageLoading) const
1438{
1439 auto& request = cachedResourceRequest.resourceRequest();
1440
1441 if (!existingResource)
1442 return Load;
1443
1444 if (request.cachePolicy() == ResourceRequestCachePolicy::DoNotUseAnyCache || request.cachePolicy() == ResourceRequestCachePolicy::ReloadIgnoringCacheData)
1445 return Load;
1446
1447 if (request.cachePolicy() == ResourceRequestCachePolicy::RefreshAnyCacheData)
1448 return Reload;
1449
1450 if (mustReloadFromServiceWorkerOptions(cachedResourceRequest.options(), existingResource->options())) {
1451 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading because selected service worker may differ")((void)0);
1452 return Reload;
1453 }
1454
1455 // We already have a preload going for this URL.
1456 if (forPreload == ForPreload::Yes && existingResource->isPreloaded())
1457 return Use;
1458
1459 // If the same URL has been loaded as a different type, we need to reload.
1460 if (existingResource->type() != type && existingResource->options().cachingPolicy != CachingPolicy::AllowCachingMainResourcePrefetch) {
1461 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to type mismatch.")((void)0);
1462 return Reload;
1463 }
1464
1465 if (!existingResource->varyHeaderValuesMatch(request))
1466 return Reload;
1467
1468 RefPtr textDecoder = existingResource->textResourceDecoder();
1469 if (textDecoder && !textDecoder->hasEqualEncodingForCharset(cachedResourceRequest.charset())) {
1470 if (!existingResource->hasUnknownEncoding())
1471 return Reload;
1472 existingResource->setHasUnknownEncoding(false);
1473 existingResource->setEncoding(cachedResourceRequest.charset());
1474 }
1475
1476 // FIXME: We should use the same cache policy for all resource types. The raw resource policy is overly strict
1477 // while the normal subresource policy is too loose.
1478 if (existingResource->isMainOrMediaOrIconOrRawResource() && frame()) {
1479 bool strictPolicyDisabled = frame()->loader().isStrictRawResourceValidationPolicyDisabledForTesting();
1480 bool canReuseRawResource = strictPolicyDisabled || downcast<CachedRawResource>(*existingResource).canReuse(request);
1481 if (!canReuseRawResource)
1482 return Reload;
1483 }
1484
1485 // Conditional requests should have failed canReuse check.
1486 ASSERT(!request.isConditional())((void)0);
1487
1488 // Do not load from cache if images are not enabled. The load for this image will be blocked in CachedImage::load.
1489 if (imageLoading == ImageLoading::DeferredUntilVisible)
1490 return Reload;
1491
1492 // Don't reload resources while pasting or if cache mode allows stale resources.
1493 if (m_allowStaleResources || cachedResourceRequest.options().cache == FetchOptions::Cache::ForceCache || cachedResourceRequest.options().cache == FetchOptions::Cache::OnlyIfCached)
1494 return Use;
1495
1496 ASSERT(cachedResourceRequest.options().cache == FetchOptions::Cache::Default || cachedResourceRequest.options().cache == FetchOptions::Cache::NoCache)((void)0);
1497
1498 // Always use preloads.
1499 if (existingResource->isPreloaded())
1500 return Use;
1501
1502 // We can find resources that are being validated from cache only when validation is just successfully completing.
1503 if (existingResource->validationCompleting())
1504 return Use;
1505 ASSERT(!existingResource->validationInProgress())((void)0);
1506
1507 if (RefPtr cachedImage = dynamicDowncast<CachedImage>(*existingResource); cachedImage && cachedImage->canSkipRevalidation(*this, cachedResourceRequest))
1508 return Use;
1509
1510 auto cachePolicy = this->cachePolicy(type, request.url());
1511
1512 // Validate the redirect chain.
1513 bool cachePolicyIsHistoryBuffer = cachePolicy == CachePolicy::HistoryBuffer || existingResource->options().cachingPolicy == CachingPolicy::AllowCachingMainResourcePrefetch;
1514 if (!existingResource->redirectChainAllowsReuse(cachePolicyIsHistoryBuffer ? ReuseExpiredRedirection : DoNotReuseExpiredRedirection)) {
1515 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to not cached or expired redirections.")((void)0);
1516 return Reload;
1517 }
1518
1519 // CachePolicy::HistoryBuffer uses the cache except if this is a main resource with "cache-control: no-store".
1520 if (cachePolicyIsHistoryBuffer) {
1521 // FIXME: Ignoring "cache-control: no-cache" for sub-resources on history navigation but not the main
1522 // resource is inconsistent. We should probably harmonize this.
1523 if (!existingResource->response().cacheControlContainsNoStore() || type != CachedResource::Type::MainResource)
1524 return Use;
1525 }
1526
1527 // Don't reuse resources with Cache-control: no-store.
1528 if (existingResource->response().cacheControlContainsNoStore() && existingResource->options().cachingPolicy != CachingPolicy::AllowCachingMainResourcePrefetch) {
1529 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to Cache-control: no-store.")((void)0);
1530 return Reload;
1531 }
1532
1533 // If credentials were sent with the previous request and won't be
1534 // with this one, or vice versa, re-fetch the resource.
1535 //
1536 // This helps with the case where the server sends back
1537 // "Access-Control-Allow-Origin: *" all the time, but some of the
1538 // client's requests are made without CORS and some with.
1539 if (existingResource->resourceRequest().allowCookies() != request.allowCookies() || existingResource->options().credentials != cachedResourceRequest.options().credentials) {
1540 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to difference in credentials settings.")((void)0);
1541 return Reload;
1542 }
1543
1544 // During the initial load, avoid loading the same resource multiple times for a single document, even if the cache policies would tell us to.
1545 if (document() && !document()->loadEventFinished() && m_validatedURLs.contains(existingResource->url()))
1546 return Use;
1547
1548 // CachePolicy::Reload always reloads
1549 if (cachePolicy == CachePolicy::Reload) {
1550 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to CachePolicyReload.")((void)0);
1551 return Reload;
1552 }
1553
1554 // We'll try to reload the resource if it failed last time.
1555 if (existingResource->errorOccurred()) {
1556 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicye reloading due to resource being in the error state")((void)0);
1557 return Reload;
1558 }
1559
1560 if (existingResource->isLoading()) {
1561 // Do not use cached main resources that are still loading because sharing
1562 // loading CachedResources in this case causes issues with regards to cancellation.
1563 // If one of the DocumentLoader clients decides to cancel the load, then the load
1564 // would be cancelled for all other DocumentLoaders as well.
1565 if (type == CachedResource::Type::MainResource)
1566 return Reload;
1567 // For cached subresources that are still loading we ignore the cache policy.
1568 return Use;
1569 }
1570
1571 auto revalidationDecision = existingResource->makeRevalidationDecision(cachePolicy);
1572
1573 // Check if the cache headers requires us to revalidate (cache expiration for example).
1574 if (revalidationDecision != CachedResource::RevalidationDecision::No && existingResource->options().cachingPolicy != CachingPolicy::AllowCachingMainResourcePrefetch) {
1575 // See if the resource has usable ETag or Last-modified headers.
1576 if (existingResource->canUseCacheValidator()) {
1577 // Revalidating will mean exposing headers to the service worker, let's reload given the service worker already handled it.
1578 if (cachedResourceRequest.options().serviceWorkerRegistrationIdentifier)
1579 return Reload;
1580 // Do not revalidate responses with the COOP header since they may cause a process-swap
1581 // and we could not make the swap work if the server responded with a 304.
1582 if (cachedResourceRequest.options().mode == FetchOptions::Mode::Navigate
1583 && obtainCrossOriginOpenerPolicy(existingResource->response()).value != CrossOriginOpenerPolicyValue::UnsafeNone) {
1584 return Reload;
1585 }
1586 return Revalidate;
1587 }
1588
1589 // No, must reload.
1590 LOG(ResourceLoading, "CachedResourceLoader::determineRevalidationPolicy reloading due to missing cache validators.")((void)0);
1591 return Reload;
1592 }
1593
1594 // FIXME: We should be able to USE the data here, but cannot currently due to a bug
1595 // concerning redirect and URL fragments. https://bugs.webkit.org/show_bug.cgi?id=258934
1596 if (cachedResourceRequest.hasFragmentIdentifier() && existingResource->hasRedirections())
1597 return Load;
1598
1599 return Use;
1600}
1601
1602void CachedResourceLoader::printAccessDeniedMessage(const URL& url) const
1603{
1604 if (url.isNull())
1605 return;
1606
1607 RefPtr frame = this->frame();
1608 if (!frame)
1609 return;
1610
1611 String message;
1612 RefPtr document = m_document.get();
1613 if (!document || document->url().isNull())
1614 message = makeString("Unsafe attempt to load URL "_s, url.stringCenterEllipsizedToLength(), '.');
1615 else
1616 message = makeString("Unsafe attempt to load URL "_s, url.stringCenterEllipsizedToLength(), " from origin "_s, protect(document->securityOrigin())->toString(), ". Domains, protocols and ports must match.\n"_s);
1617
1618 if (RefPtr frameDocument = frame->document())
1619 frameDocument->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
1620}
1621
1622void CachedResourceLoader::setAutoLoadImages(bool enable)
1623{
1624 if (enable == m_autoLoadImages)
1625 return;
1626
1627 m_autoLoadImages = enable;
1628
1629 if (!m_autoLoadImages)
1630 return;
1631
1632 reloadImagesIfNotDeferred();
1633}
1634
1635void CachedResourceLoader::setImagesEnabled(bool enable)
1636{
1637 if (enable == m_imagesEnabled)
1638 return;
1639
1640 m_imagesEnabled = enable;
1641
1642 if (!m_imagesEnabled)
1643 return;
1644
1645 reloadImagesIfNotDeferred();
1646}
1647
1648ImageLoading CachedResourceLoader::clientDefersImage(const URL&) const
1649{
1650 return m_imagesEnabled ? ImageLoading::Immediate : ImageLoading::DeferredUntilVisible;
1651}
1652
1653bool CachedResourceLoader::shouldPerformImageLoad(const URL& url) const
1654{
1655 return m_autoLoadImages || url.protocolIsData();
1656}
1657
1658bool CachedResourceLoader::shouldDeferImageLoad(const URL& url) const
1659{
1660 return clientDefersImage(url) == ImageLoading::DeferredUntilVisible || !shouldPerformImageLoad(url);
1661}
1662
1663void CachedResourceLoader::reloadImagesIfNotDeferred()
1664{
1665 for (auto& resource : m_documentResources.values()) {
1666 RefPtr image = dynamicDowncast<CachedImage>(*resource);
1667 if (image && resource->stillNeedsLoad() && clientDefersImage(resource->url()) == ImageLoading::Immediate)
1668 image->load(*this);
1669 }
1670}
1671
1672CachePolicy CachedResourceLoader::cachePolicy(CachedResource::Type type, const URL& url) const
1673{
1674 RefPtr frame = this->frame();
1675 if (!frame)
1676 return CachePolicy::Verify;
1677
1678 if (type != CachedResource::Type::MainResource)
1679 return frame->loader().subresourceCachePolicy(url);
1680
1681 if (RefPtr page = frame->page()) {
1682 if (page->isResourceCachingDisabledByWebInspector())
1683 return CachePolicy::Reload;
1684 }
1685
1686 switch (frame->loader().loadType()) {
1687 case FrameLoadType::ReloadFromOrigin:
1688 case FrameLoadType::Reload:
1689 return CachePolicy::Reload;
1690 case FrameLoadType::Back:
1691 case FrameLoadType::Forward:
1692 case FrameLoadType::IndexedBackForward:
1693 // Do not revalidate cached main resource on back/forward navigation.
1694 return CachePolicy::HistoryBuffer;
1695 default:
1696 return CachePolicy::Verify;
1697 }
1698}
1699
1700void CachedResourceLoader::clearDocumentLoader()
1701{
1702 m_documentLoader = nullptr;
1703}
1704
1705void CachedResourceLoader::loadDone(LoadCompletionType type, bool shouldPerformPostLoadActions)
1706{
1707 RefPtr protectedDocumentLoader { m_documentLoader.get() };
1708 RefPtr protectedDocument { m_document.get() };
1709
1710 ASSERT(shouldPerformPostLoadActions || type == LoadCompletionType::Cancel)((void)0);
1711
1712 if (RefPtr frame = this->frame())
1713 frame->loader().loadDone(type);
1714
1715 if (shouldPerformPostLoadActions)
1716 performPostLoadActions();
1717
1718 if (!m_garbageCollectDocumentResourcesTimer.isActive())
1719 m_garbageCollectDocumentResourcesTimer.startOneShot(0_s);
1720}
1721
1722// Garbage collecting m_documentResources is a workaround for the
1723// CachedResourceHandles on the RHS being strong references. Ideally this
1724// would be a weak map, however CachedResourceHandles perform additional
1725// bookkeeping on CachedResources, so instead pseudo-GC them -- when the
1726// reference count reaches 1, m_documentResources is the only reference, so
1727// remove it from the map.
1728void CachedResourceLoader::garbageCollectDocumentResources()
1729{
1730 typedef Vector<String, 10> StringVector;
1731 StringVector resourcesToDelete;
1732
1733 for (auto& resourceEntry : m_documentResources) {
1734 auto& resource = *resourceEntry.value;
Local variable 'resource' is uncounted and unsafe
1735
1736 if (resource.refCount() == (resource.inCache() ? 2u : 1u) && !resource.loader() && !resource.isPreloaded()) {
1737 resourcesToDelete.append(resourceEntry.key);
1738 m_resourceTimingInfo.removeResourceTiming(resource);
1739 }
1740 }
1741
1742 LOG_WITH_STREAM(ResourceLoading, stream << "CachedResourceLoader " << this << " garbageCollectDocumentResources - deleting " << resourcesToDelete.size() << " resources")((void)0);
1743
1744 for (auto& resource : resourcesToDelete)
1745 m_documentResources.remove(resource);
1746}
1747
1748void CachedResourceLoader::performPostLoadActions()
1749{
1750 platformStrategies()->loaderStrategy()->servePendingRequests();
1751}
1752
1753void CachedResourceLoader::incrementRequestCount(const CachedResource& resource)
1754{
1755 if (resource.ignoreForRequestCount())
1756 return;
1757
1758 ++m_requestCount;
1759}
1760
1761void CachedResourceLoader::decrementRequestCount(const CachedResource& resource)
1762{
1763 if (resource.ignoreForRequestCount())
1764 return;
1765
1766 --m_requestCount;
1767 ASSERT(m_requestCount > -1)((void)0);
1768}
1769
1770void CachedResourceLoader::notifyFinished(const CachedResource& resource)
1771{
1772 if (isSVGImageCachedResource(&resource))
1773 m_cachedSVGImagesURLs.add(resource.url());
1774}
1775
1776Vector<Ref<SVGImage>> CachedResourceLoader::allCachedSVGImages() const
1777{
1778 Vector<Ref<SVGImage>> allCachedSVGImages;
1779
1780 for (auto& cachedSVGImageURL : m_cachedSVGImagesURLs) {
1781 RefPtr resource = cachedResource(cachedSVGImageURL);
1782 if (RefPtr image = cachedResourceSVGImage(resource.get()))
1783 allCachedSVGImages.append(image.releaseNonNull());
1784 }
1785
1786 return allCachedSVGImages;
1787}
1788
1789ResourceErrorOr<Ref<CachedResource>> CachedResourceLoader::preload(CachedResource::Type type, CachedResourceRequest&& request)
1790{
1791 if (InspectorInstrumentation::willIntercept(protect(frame()).get(), request.resourceRequest()))
1792 return makeUnexpected(ResourceError { errorDomainWebKitInternal, 0, request.resourceRequest().url(), "Inspector intercept"_s });
1793
1794 RefPtr document = m_document.get();
1795 ASSERT(document)((void)0);
1796 if (request.charset().isEmpty() && document && (type == CachedResource::Type::Script || type == CachedResource::Type::JSON || type == CachedResource::Type::CSSStyleSheet))
1797 request.setCharset(document->charset());
1798
1799 auto resource = requestResource(type, WTF::move(request), ForPreload::Yes);
1800 if (resource && (!m_preloads || !m_preloads->contains(resource.value().ptr()))) {
1801 auto& resourceValue = resource.value();
1802 // Fonts need special treatment since just creating the resource doesn't trigger a load.
1803 if (RefPtr cachedFont = dynamicDowncast<CachedFont>(resourceValue.get()))
1804 cachedFont->beginLoadIfNeeded(*this);
1805 resourceValue->setIsPreloaded(true);
1806
1807 if (!m_preloads)
1808 m_preloads = makeUnique<ListHashSet<RefPtr<CachedResource>>>();
1809 m_preloads->add(resourceValue.ptr());
1810 }
1811 return resource;
1812}
1813
1814void CachedResourceLoader::warnUnusedPreloads()
1815{
1816 if (!m_preloads)
1817 return;
1818
1819 RefPtr document = this->document();
1820 if (!document)
1821 return;
1822
1823 for (const auto& resource : *m_preloads) {
1824 if (resource->isLinkPreload() && resource->preloadResult() == CachedResource::PreloadResult::PreloadNotReferenced) {
1825 document->addConsoleMessage(MessageSource::Other, MessageLevel::Warning,
1826 makeString("The resource "_s, resource->url().string(),
1827 " 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."_s));
1828 }
1829 }
1830}
1831
1832bool CachedResourceLoader::isPreloaded(const String& urlString) const
1833{
1834 RefPtr document = m_document.get();
1835 ASSERT(document)((void)0);
1836 if (!document)
1837 return false;
1838
1839 const URL& url = document->completeURL(urlString);
1840
1841 if (!m_preloads)
1842 return false;
1843
1844 for (auto& resource : *m_preloads) {
1845 if (resource->url() == url)
1846 return true;
1847 }
1848 return false;
1849}
1850
1851void CachedResourceLoader::clearPreloads(ClearPreloadsMode mode)
1852{
1853 if (!m_preloads)
1854 return;
1855
1856 std::unique_ptr<ListHashSet<RefPtr<CachedResource>>> remainingLinkPreloads;
1857 for (auto& resource : *m_preloads) {
1858 if (mode == ClearPreloadsMode::ClearSpeculativePreloads && resource->isLinkPreload()) {
1859 if (!remainingLinkPreloads)
1860 remainingLinkPreloads = makeUnique<ListHashSet<RefPtr<CachedResource>>>();
1861 remainingLinkPreloads->add(resource);
1862 continue;
1863 }
1864 resource->setIsPreloaded(false);
1865 if (resource->preloadResult() == CachedResource::PreloadResult::PreloadNotReferenced)
1866 MemoryCache::singleton().remove(*resource);
1867 }
1868 m_preloads = WTF::move(remainingLinkPreloads);
1869}
1870
1871Vector<CachedResourceHandle<CachedResource>> CachedResourceLoader::visibleResourcesToPrioritize()
1872{
1873 if (!document())
1874 return { };
1875
1876 Vector<CachedResourceHandle<CachedResource>> toPrioritize;
1877
1878 for (auto& resource : m_documentResources.values()) {
1879 RefPtr cachedImage = dynamicDowncast<CachedImage>(*resource);
1880 if (!cachedImage)
1881 continue;
1882 if (!cachedImage->isLoading())
1883 continue;
1884 if (!cachedImage->url().protocolIsInHTTPFamily())
1885 continue;
1886 if (!cachedImage->loader())
1887 continue;
1888 if (!cachedImage->isVisibleInViewport(*protect(document())))
1889 continue;
1890 toPrioritize.append(WTF::move(cachedImage));
1891 }
1892
1893 return toPrioritize;
1894}
1895
1896const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions()
1897{
1898 static NeverDestroyed<ResourceLoaderOptions> options(
1899 SendCallbackPolicy::SendCallbacks,
1900 ContentSniffingPolicy::SniffContent,
1901 DataBufferingPolicy::BufferData,
1902 StoredCredentialsPolicy::Use,
1903 ClientCredentialPolicy::MayAskClientForCredentials,
1904 FetchOptions::Credentials::Include,
1905 SecurityCheckPolicy::DoSecurityCheck,
1906 FetchOptions::Mode::NoCors,
1907 CertificateInfoPolicy::DoNotIncludeCertificateInfo,
1908 ContentSecurityPolicyImposition::DoPolicyCheck,
1909 DefersLoadingPolicy::AllowDefersLoading,
1910 CachingPolicy::AllowCaching);
1911 return options;
1912}
1913
1914}
1915
1916#undef PAGE_ID
1917#undef FRAME_ID
1918#undef CACHEDRESOURCELOADER_RELEASE_LOG
1919#undef CACHEDRESOURCELOADER_RELEASE_LOG_WITH_FRAME