Bug Summary

File:Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/loader/DocumentThreadableLoader.cpp
Warning:line 262, column 59
Call argument for 'this' parameter 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 UnifiedSource279.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/UnifiedSource279.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/UnifiedSource279.plist -x c++ /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebCore/unified-sources/UnifiedSource279.cpp
1/*
2 * Copyright (C) 2011, 2012 Google Inc. All rights reserved.
3 * Copyright (C) 2011-2025 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33#include "DocumentThreadableLoader.h"
34
35#include "CachedRawResource.h"
36#include "CachedResourceRequest.h"
37#include "CachedResourceRequestInitiatorTypes.h"
38#include "CrossOriginAccessControl.h"
39#include "CrossOriginPreflightChecker.h"
40#include "CrossOriginPreflightResultCache.h"
41#include "DocumentInlines.h"
42#include "DocumentLoader.h"
43#include "DocumentResourceLoader.h"
44#include "DocumentView.h"
45#include "FrameDestructionObserverInlines.h"
46#include "FrameLoader.h"
47#include "InspectorInstrumentation.h"
48#include "InspectorNetworkAgent.h"
49#include "LegacySchemeRegistry.h"
50#include "LoaderStrategy.h"
51#include "LocalDOMWindow.h"
52#include "LocalFrame.h"
53#include "MixedContentChecker.h"
54#include "OriginAccessPatterns.h"
55#include "Performance.h"
56#include "PlatformStrategies.h"
57#include "ProgressTracker.h"
58#include "ResourceError.h"
59#include "ResourceRequest.h"
60#include "ResourceTiming.h"
61#include "SecurityOrigin.h"
62#include "Settings.h"
63#include "SharedBuffer.h"
64#include "SubresourceIntegrity.h"
65#include "SubresourceLoader.h"
66#include "ThreadableLoaderClient.h"
67#include <wtf/Assertions.h>
68#include <wtf/Ref.h>
69#include <wtf/text/MakeString.h>
70
71namespace WebCore {
72
73void DocumentThreadableLoader::loadResourceSynchronously(Document& document, ResourceRequest&& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options, RefPtr<SecurityOrigin>&& origin, std::unique_ptr<ContentSecurityPolicy>&& contentSecurityPolicy, std::optional<CrossOriginEmbedderPolicy>&& crossOriginEmbedderPolicy)
74{
75 // The loader will be deleted as soon as this function exits.
76 Ref loader = adoptRef(*new DocumentThreadableLoader(document, client, LoadSynchronously, WTF::move(request), options, WTF::move(origin), WTF::move(contentSecurityPolicy), WTF::move(crossOriginEmbedderPolicy), String(), ShouldLogError::Yes));
77 ASSERT(loader->hasOneRef())((void)0);
78}
79
80void DocumentThreadableLoader::loadResourceSynchronously(Document& document, ResourceRequest&& request, ThreadableLoaderClient& client, const ThreadableLoaderOptions& options)
81{
82 loadResourceSynchronously(document, WTF::move(request), client, options, nullptr, nullptr, std::nullopt);
83}
84
85RefPtr<DocumentThreadableLoader> DocumentThreadableLoader::create(Document& document, ThreadableLoaderClient& client,
86ResourceRequest&& request, const ThreadableLoaderOptions& options, RefPtr<SecurityOrigin>&& origin,
87std::unique_ptr<ContentSecurityPolicy>&& contentSecurityPolicy, std::optional<CrossOriginEmbedderPolicy>&& crossOriginEmbedderPolicy, String&& referrer, ShouldLogError shouldLogError)
88{
89 RefPtr loader = adoptRef(new DocumentThreadableLoader(document, client, LoadAsynchronously, WTF::move(request), options, WTF::move(origin), WTF::move(contentSecurityPolicy), WTF::move(crossOriginEmbedderPolicy), WTF::move(referrer), shouldLogError));
90 if (!loader->isLoading())
91 loader = nullptr;
92 return loader;
93}
94
95RefPtr<DocumentThreadableLoader> DocumentThreadableLoader::create(Document& document, ThreadableLoaderClient& client, ResourceRequest&& request, const ThreadableLoaderOptions& options, String&& referrer)
96{
97 return create(document, client, WTF::move(request), options, nullptr, nullptr, std::nullopt, WTF::move(referrer), ShouldLogError::Yes);
98}
99
100static inline bool shouldPerformSecurityChecks()
101{
102 return platformStrategies()->loaderStrategy()->shouldPerformSecurityChecks();
103}
104
105bool DocumentThreadableLoader::shouldSetHTTPHeadersToKeep() const
106{
107 if (m_options.mode == FetchOptions::Mode::Cors && shouldPerformSecurityChecks())
108 return true;
109
110 if (m_options.serviceWorkersMode == ServiceWorkersMode::All && m_async)
111 return m_options.serviceWorkerRegistrationIdentifier || m_document->activeServiceWorker();
112
113 return false;
114}
115
116DocumentThreadableLoader::DocumentThreadableLoader(Document& document, ThreadableLoaderClient& client, BlockingBehavior blockingBehavior, ResourceRequest&& request, const ThreadableLoaderOptions& options, RefPtr<SecurityOrigin>&& origin, std::unique_ptr<ContentSecurityPolicy>&& contentSecurityPolicy, std::optional<CrossOriginEmbedderPolicy>&& crossOriginEmbedderPolicy, String&& referrer, ShouldLogError shouldLogError)
117 : m_client(client)
118 , m_document(document)
119 , m_options(options)
120 , m_origin(WTF::move(origin))
121 , m_referrer(WTF::move(referrer))
122 , m_sameOriginRequest(protect(securityOrigin())->canRequest(request.url(), OriginAccessPatternsForWebProcess::singleton()))
123 , m_simpleRequest(true)
124 , m_async(blockingBehavior == LoadAsynchronously)
125 , m_delayCallbacksForIntegrityCheck(!m_options.integrity.isEmpty())
126 , m_contentSecurityPolicy(WTF::move(contentSecurityPolicy))
127 , m_crossOriginEmbedderPolicy(WTF::move(crossOriginEmbedderPolicy))
128 , m_shouldLogError(shouldLogError)
129{
130 relaxAdoptionRequirement();
131
132 // Setting a referrer header is only supported in the async code path.
133 ASSERT(m_async || m_referrer.isEmpty())((void)0);
134
135 RefPtr page = document.page();
136 if (!m_async && (!page || !page->areSynchronousLoadsAllowed())) {
137 document.didRejectSyncXHRDuringPageDismissal();
138 logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, request.url(), "Synchronous loads are not allowed at this time"_s));
139 return;
140 }
141
142 // Referrer and Origin headers should be set after the preflight if any.
143 ASSERT(!request.hasHTTPReferrer() && !request.hasHTTPOrigin())((void)0);
144
145 ASSERT_WITH_SECURITY_IMPLICATION(isAllowedByContentSecurityPolicy(request.url(), ContentSecurityPolicy::RedirectResponseReceived::No))((void)0);
146
147 m_options.storedCredentialsPolicy = (m_options.credentials == FetchOptions::Credentials::Include || (m_options.credentials == FetchOptions::Credentials::SameOrigin && m_sameOriginRequest)) ? StoredCredentialsPolicy::Use : StoredCredentialsPolicy::DoNotUse;
148
149 ASSERT(!request.httpHeaderFields().contains(HTTPHeaderName::Origin))((void)0);
150
151 // Copy headers if we need to replay the request after a redirection.
152 if (m_options.mode == FetchOptions::Mode::Cors)
153 m_originalHeaders = request.httpHeaderFields();
154
155 if (shouldSetHTTPHeadersToKeep())
156 m_options.httpHeadersToKeep = httpHeadersToKeepFromCleaning(request.httpHeaderFields());
157
158 bool shouldDisableCORS = false;
159 if (page) {
160 shouldDisableCORS = page->hasInjectedUserScript() && LegacySchemeRegistry::isUserExtensionScheme(request.url().protocol());
161 shouldDisableCORS |= page->shouldDisableCorsForRequestTo(request.url());
162 }
163
164 if (shouldDisableCORS) {
165 m_options.mode = FetchOptions::Mode::NoCors;
166 m_options.filteringPolicy = ResponseFilteringPolicy::Disable;
167 m_responsesCanBeOpaque = false;
168 }
169
170 m_options.cspResponseHeaders = m_options.contentSecurityPolicyEnforcement != ContentSecurityPolicyEnforcement::DoNotEnforce ? protect(this->contentSecurityPolicy())->responseHeaders() : ContentSecurityPolicyResponseHeaders { };
171 m_options.crossOriginEmbedderPolicy = this->crossOriginEmbedderPolicy();
172
173 // As per step 11 of https://fetch.spec.whatwg.org/#main-fetch, data scheme (if same-origin data-URL flag is set) and about scheme are considered same-origin.
174 if (request.url().protocolIsData())
175 m_sameOriginRequest = options.sameOriginDataURLFlag == SameOriginDataURLFlag::Set;
176
177 if (m_sameOriginRequest || m_options.mode == FetchOptions::Mode::NoCors || m_options.mode == FetchOptions::Mode::Navigate) {
178 loadRequest(WTF::move(request), SecurityCheckPolicy::DoSecurityCheck);
179 return;
180 }
181
182 if (m_options.mode == FetchOptions::Mode::SameOrigin) {
183 logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, request.url(), "Cross origin requests are not allowed when using same-origin fetch mode."_s));
184 return;
185 }
186
187 makeCrossOriginAccessRequest(WTF::move(request));
188}
189
190bool DocumentThreadableLoader::checkURLSchemeAsCORSEnabled(const URL& url)
191{
192 // Cross-origin requests are only allowed for HTTP and registered schemes. We would catch this when checking response headers later, but there is no reason to send a request that's guaranteed to be denied.
193 if (!LegacySchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(url.protocol())) {
194 logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, url, "Cross origin requests are only supported for HTTP."_s, ResourceError::Type::AccessControl));
195 return false;
196 }
197 return true;
198}
199
200void DocumentThreadableLoader::makeCrossOriginAccessRequest(ResourceRequest&& request)
201{
202 ASSERT(m_options.mode == FetchOptions::Mode::Cors)((void)0);
203
204 if ((m_options.preflightPolicy == PreflightPolicy::Consider && isSimpleCrossOriginAccessRequest(request.httpMethod(), request.httpHeaderFields())) || m_options.preflightPolicy == PreflightPolicy::Prevent || shouldPerformSecurityChecks()) {
205 if (checkURLSchemeAsCORSEnabled(request.url()))
206 makeSimpleCrossOriginAccessRequest(WTF::move(request));
207 } else {
208 Ref document = *m_document;
209 if (m_options.serviceWorkersMode == ServiceWorkersMode::All && m_async) {
210 if (m_options.serviceWorkerRegistrationIdentifier || document->activeServiceWorker()) {
211 ASSERT(!m_bypassingPreflightForServiceWorkerRequest)((void)0);
212 m_bypassingPreflightForServiceWorkerRequest = WTF::move(request);
213 m_options.serviceWorkersMode = ServiceWorkersMode::Only;
214 loadRequest(ResourceRequest { m_bypassingPreflightForServiceWorkerRequest.value() }, SecurityCheckPolicy::SkipSecurityCheck);
215 return;
216 }
217 }
218 if (!checkURLSchemeAsCORSEnabled(request.url()))
219 return;
220
221 m_simpleRequest = false;
222 if (RefPtr page = document->page(); page && CrossOriginPreflightResultCache::singleton().canSkipPreflight(page->sessionID(), document->clientOrigin(), request.url(), m_options.storedCredentialsPolicy, request.httpMethod(), request.httpHeaderFields()))
223 preflightSuccess(WTF::move(request));
224 else
225 makeCrossOriginAccessRequestWithPreflight(WTF::move(request));
226 }
227}
228
229void DocumentThreadableLoader::makeSimpleCrossOriginAccessRequest(ResourceRequest&& request)
230{
231 ASSERT(m_options.preflightPolicy != PreflightPolicy::Force || shouldPerformSecurityChecks())((void)0);
232 ASSERT(m_options.preflightPolicy == PreflightPolicy::Prevent || isSimpleCrossOriginAccessRequest(request.httpMethod(), request.httpHeaderFields()) || shouldPerformSecurityChecks())((void)0);
233
234 updateRequestForAccessControl(request, protect(securityOrigin()), m_options.storedCredentialsPolicy);
235 loadRequest(WTF::move(request), SecurityCheckPolicy::DoSecurityCheck);
236}
237
238void DocumentThreadableLoader::makeCrossOriginAccessRequestWithPreflight(ResourceRequest&& request)
239{
240 if (m_async) {
241 Ref preflightChecker = CrossOriginPreflightChecker::create(*this, WTF::move(request));
242 m_preflightChecker = preflightChecker.copyRef();
243 preflightChecker->startPreflight();
244 return;
245 }
246 CrossOriginPreflightChecker::doPreflight(*this, WTF::move(request));
247}
248
249DocumentThreadableLoader::~DocumentThreadableLoader()
250{
251 if (RefPtr resource = m_resource.get())
252 resource->removeClient(*this);
253}
254
255void DocumentThreadableLoader::cancel()
256{
257 Ref protectedThis { *this };
258
259 // Cancel can re-enter and m_resource might be null here as a result.
260 if (RefPtr client = m_client.get(); client && m_resource) {
261 // FIXME: This error is sent to the client in didFail(), so it should not be an internal one. Use LocalFrameLoaderClient::cancelledError() instead.
262 ResourceError error(errorDomainWebKitInternal, 0, m_resource->url(), "Load cancelled"_s, ResourceError::Type::Cancellation);
Call argument for 'this' parameter is uncounted and unsafe
263 client->didFail(m_document->identifier(), error); // May destroy the client.
264 }
265 clearResource();
266 m_client = nullptr;
267}
268
269void DocumentThreadableLoader::computeIsDone()
270{
271 if (!m_async || m_preflightChecker || !m_resource) {
272 if (RefPtr client = m_client.get())
273 client->notifyIsDone(m_async && !m_preflightChecker && !m_resource);
274 return;
275 }
276 platformStrategies()->loaderStrategy()->isResourceLoadFinished(*protect(m_resource), [weakThis = WeakPtr { *this }](bool isDone) {
277 RefPtr protectedThis = weakThis.get();
278 if (!protectedThis)
279 return;
280 if (RefPtr client = protectedThis->m_client.get())
281 client->notifyIsDone(isDone);
282 });
283}
284
285void DocumentThreadableLoader::setDefersLoading(bool value)
286{
287 if (RefPtr resource = m_resource.get())
288 resource->setDefersLoading(value);
289 if (RefPtr preflightChecker = m_preflightChecker)
290 preflightChecker->setDefersLoading(value);
291}
292
293void DocumentThreadableLoader::clearResource()
294{
295 // Script can cancel and restart a request reentrantly within removeClient(),
296 // which could lead to calling CachedResource::removeClient() multiple times for
297 // this DocumentThreadableLoader. Save off a copy of m_resource and clear it to
298 // prevent the reentrancy.
299 if (RefPtr resource = m_resource.get()) {
300 m_resource = nullptr;
301 resource->removeClient(*this);
302 }
303 m_preflightChecker = nullptr;
304}
305
306void DocumentThreadableLoader::redirectReceived(CachedResource& resource, ResourceRequest&& request, const ResourceResponse& redirectResponse, CompletionHandler<void(ResourceRequest&&)>&& completionHandler)
307{
308 ASSERT(m_client)((void)0);
309 ASSERT_UNUSED(resource, &resource == m_resource)((void)resource);
310
311 Ref protectedThis { *this };
312 --m_options.maxRedirectCount;
313
314 m_responseURL = request.url();
315
316 // FIXME: We restrict this check to Fetch API for the moment, as this might disrupt WorkerScriptLoader.
317 // Reassess this check based on https://github.com/whatwg/fetch/issues/393 discussions.
318 // We should also disable that check in navigation mode.
319 if (!request.url().protocolIsInHTTPFamily() && m_options.initiatorType == cachedResourceRequestInitiatorTypes().fetch) {
320 reportRedirectionWithBadScheme(request.url());
321 clearResource();
322 return completionHandler(WTF::move(request));
323 }
324
325 if (platformStrategies()->loaderStrategy()->havePerformedSecurityChecks(redirectResponse)) {
326 completionHandler(WTF::move(request));
327 return;
328 }
329
330 if (!isAllowedByContentSecurityPolicy(request.url(), redirectResponse.isNull() ? ContentSecurityPolicy::RedirectResponseReceived::No : ContentSecurityPolicy::RedirectResponseReceived::Yes, redirectResponse.url())) {
331 reportContentSecurityPolicyError(redirectResponse.url());
332 clearResource();
333 return completionHandler(WTF::move(request));
334 }
335
336 // Allow same origin requests to continue after allowing clients to audit the redirect.
337 if (isAllowedRedirect(request.url()))
338 return completionHandler(WTF::move(request));
339
340 // Force any subsequent request to use these checks.
341 m_sameOriginRequest = false;
342
343 ASSERT(m_resource)((void)0);
344 ASSERT(m_originalHeaders)((void)0);
345
346 // Use a unique for subsequent loads if needed.
347 // https://fetch.spec.whatwg.org/#concept-http-redirect-fetch (Step 10).
348 ASSERT(m_options.mode == FetchOptions::Mode::Cors)((void)0);
349 if (!protect(securityOrigin())->canRequest(redirectResponse.url(), OriginAccessPatternsForWebProcess::singleton()) && !protocolHostAndPortAreEqual(redirectResponse.url(), request.url()))
350 m_origin = SecurityOrigin::createOpaque();
351
352 // Except in case where preflight is needed, loading should be able to continue on its own.
353 // But we also handle credentials here if it is restricted to SameOrigin.
354 if (m_options.credentials != FetchOptions::Credentials::SameOrigin && m_simpleRequest && isSimpleCrossOriginAccessRequest(request.httpMethod(), *m_originalHeaders))
355 return completionHandler(WTF::move(request));
356
357 if (m_options.credentials == FetchOptions::Credentials::SameOrigin)
358 m_options.storedCredentialsPolicy = StoredCredentialsPolicy::DoNotUse;
359
360 clearResource();
361
362 m_referrer = request.httpReferrer();
363 if (m_referrer.isNull())
364 m_options.referrerPolicy = ReferrerPolicy::NoReferrer;
365
366 // Let's fetch the request with the original headers (equivalent to request cloning specified by fetch algorithm).
367 // Do not copy the Authorization header if removed by the network layer.
368 if (!request.httpHeaderFields().contains(HTTPHeaderName::Authorization))
369 m_originalHeaders->remove(HTTPHeaderName::Authorization);
370 request.setHTTPHeaderFields(*m_originalHeaders);
371
372 if (redirectResponse.source() != ResourceResponse::Source::ServiceWorker && redirectResponse.source() != ResourceResponse::Source::MemoryCache)
373 m_options.serviceWorkersMode = ServiceWorkersMode::None;
374 makeCrossOriginAccessRequest(ResourceRequest(request));
375 completionHandler(WTF::move(request));
376}
377
378void DocumentThreadableLoader::dataSent(CachedResource& resource, unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
379{
380 RefPtr client = m_client.get();
381 ASSERT(client)((void)0);
382 ASSERT_UNUSED(resource, &resource == m_resource)((void)resource);
383 client->didSendData(bytesSent, totalBytesToBeSent);
384}
385
386void DocumentThreadableLoader::responseReceived(const CachedResource& resource, const ResourceResponse& response, CompletionHandler<void()>&& completionHandler)
387{
388 ASSERT_UNUSED(resource, &resource == m_resource)((void)resource);
389 ResourceResponse responseWithCorrectFragmentIdentifier;
390 if (response.source() != ResourceResponse::Source::ServiceWorker && response.url().fragmentIdentifier() != m_responseURL.fragmentIdentifier()) {
391 responseWithCorrectFragmentIdentifier = response;
392 responseWithCorrectFragmentIdentifier.setURL(URL { m_responseURL });
393 }
394
395 if (!m_responsesCanBeOpaque) {
396 ResourceResponse responseWithoutTainting = responseWithCorrectFragmentIdentifier.isNull() ? response : WTF::move(responseWithCorrectFragmentIdentifier);
397 responseWithoutTainting.setTainting(ResourceResponse::Tainting::Basic);
398 didReceiveResponse(*m_resource->resourceLoaderIdentifier(), WTF::move(responseWithoutTainting));
399 } else
400 didReceiveResponse(*m_resource->resourceLoaderIdentifier(), responseWithCorrectFragmentIdentifier.isNull() ? ResourceResponse { response } : WTF::move(responseWithCorrectFragmentIdentifier));
401
402 if (completionHandler)
403 completionHandler();
404}
405
406void DocumentThreadableLoader::didReceiveResponse(ResourceLoaderIdentifier identifier, ResourceResponse&& response)
407{
408 RefPtr client = m_client.get();
409 ASSERT(client)((void)0);
410 ASSERT(response.type() != ResourceResponse::Type::Error)((void)0);
411
412 // https://fetch.spec.whatwg.org/commit-snapshots/6257e220d70f560a037e46f1b4206325400db8dc/#main-fetch step 17.
413 if (response.source() == ResourceResponse::Source::ServiceWorker && response.url() != m_resource->url()) {
414 if (!isResponseAllowedByContentSecurityPolicy(response)) {
415 reportContentSecurityPolicyError(response.url());
416 return;
417 }
418 }
419
420 InspectorInstrumentation::didReceiveThreadableLoaderResponse(protect(*m_document), *this, identifier);
421
422 if (m_delayCallbacksForIntegrityCheck)
423 return;
424
425 if (options().filteringPolicy == ResponseFilteringPolicy::Disable) {
426 client->didReceiveResponse(m_document->identifier(), identifier, WTF::move(response));
427 return;
428 }
429
430 if (response.type() == ResourceResponse::Type::Default) {
431 client->didReceiveResponse(m_document->identifier(), identifier, ResourceResponse::filter(response, m_options.credentials == FetchOptions::Credentials::Include ? ResourceResponse::PerformExposeAllHeadersCheck::No : ResourceResponse::PerformExposeAllHeadersCheck::Yes));
432 client = nullptr; // Avoid calling didFinishLoading() if the call to didReceiveResponse() causes the client to go away.
433 if (response.tainting() == ResourceResponse::Tainting::Opaque) {
434 clearResource();
435 if (RefPtr client = m_client.get())
436 client->didFinishLoading(m_document->identifier(), identifier, { });
437 }
438 return;
439 }
440 ASSERT(response.type() == ResourceResponse::Type::Opaqueredirect || response.source() == ResourceResponse::Source::ServiceWorker || response.source() == ResourceResponse::Source::MemoryCache)((void)0);
441 client->didReceiveResponse(m_document->identifier(), identifier, WTF::move(response));
442}
443
444void DocumentThreadableLoader::dataReceived(CachedResource& resource, const SharedBuffer& buffer)
445{
446 ASSERT_UNUSED(resource, &resource == m_resource)((void)resource);
447 didReceiveData(buffer);
448}
449
450void DocumentThreadableLoader::didReceiveData(const SharedBuffer& buffer)
451{
452 RefPtr client = m_client.get();
453 ASSERT(client)((void)0);
454
455 if (m_delayCallbacksForIntegrityCheck)
456 return;
457
458 client->didReceiveData(buffer);
459}
460
461void DocumentThreadableLoader::finishedTimingForWorkerLoad(CachedResource& resource, const ResourceTiming& resourceTiming)
462{
463 ASSERT(m_client)((void)0);
464 ASSERT_UNUSED(resource, &resource == m_resource)((void)resource);
465
466 finishedTimingForWorkerLoad(resourceTiming);
467}
468
469void DocumentThreadableLoader::finishedTimingForWorkerLoad(const ResourceTiming& resourceTiming)
470{
471 ASSERT(m_options.initiatorContext == InitiatorContext::Worker)((void)0);
472
473 Ref { *m_client }->didFinishTiming(resourceTiming);
474}
475
476void DocumentThreadableLoader::notifyFinished(CachedResource& resource, const NetworkLoadMetrics& metrics, LoadWillContinueInAnotherProcess)
477{
478 ASSERT(m_client)((void)0);
479 ASSERT_UNUSED(resource, &resource == m_resource)((void)resource);
480
481 if (m_resource->errorOccurred())
482 didFail(m_resource->resourceLoaderIdentifier(), m_resource->resourceError());
483 else
484 didFinishLoading(m_resource->resourceLoaderIdentifier(), metrics);
485}
486
487void DocumentThreadableLoader::didFinishLoading(std::optional<ResourceLoaderIdentifier> identifier, const NetworkLoadMetrics& metrics)
488{
489 RefPtr document = m_document.get();
490 if (!document)
491 return;
492
493 RefPtr client = m_client.get();
494 ASSERT(client)((void)0);
495
496 if (m_delayCallbacksForIntegrityCheck) {
497 RefPtr resource = m_resource.get();
498 if (!matchIntegrityMetadata(*resource, m_options.integrity)) {
499 reportIntegrityMetadataError(*resource, m_options.integrity);
500 return;
501 }
502
503 auto response = resource->response();
504
505 RefPtr<SharedBuffer> buffer;
506 if (RefPtr resourceBuffer = resource->resourceBuffer())
507 buffer = resourceBuffer->makeContiguous();
508 if (options().filteringPolicy == ResponseFilteringPolicy::Disable) {
509 client->didReceiveResponse(document->identifier(), identifier, WTF::move(response));
510 if (buffer)
511 client->didReceiveData(*buffer);
512 } else {
513 ASSERT(response.type() == ResourceResponse::Type::Default)((void)0);
514
515 client->didReceiveResponse(document->identifier(), identifier, ResourceResponse::filter(response, m_options.credentials == FetchOptions::Credentials::Include ? ResourceResponse::PerformExposeAllHeadersCheck::No : ResourceResponse::PerformExposeAllHeadersCheck::Yes));
516 if (buffer)
517 client->didReceiveData(*buffer);
518 }
519 }
520
521 client->didFinishLoading(document->identifier(), identifier, metrics);
522}
523
524void DocumentThreadableLoader::didFail(std::optional<ResourceLoaderIdentifier>, const ResourceError& error)
525{
526 ASSERT(m_client)((void)0);
527 if (m_bypassingPreflightForServiceWorkerRequest && error.isCancellation()) {
528 clearResource();
529
530 m_options.serviceWorkersMode = ServiceWorkersMode::None;
531 makeCrossOriginAccessRequestWithPreflight(*std::exchange(m_bypassingPreflightForServiceWorkerRequest, std::nullopt));
532 return;
533 }
534
535 if (m_shouldLogError == ShouldLogError::Yes)
536 logError(protect(*m_document), error, m_options.initiatorType);
537
538 RefPtr document = m_document.get();
539 if (!document)
540 return;
541 if (RefPtr client = m_client.get())
542 client->didFail(document->identifier(), error); // May cause the client to get destroyed.
543}
544
545void DocumentThreadableLoader::preflightSuccess(ResourceRequest&& request)
546{
547 ResourceRequest actualRequest(WTF::move(request));
548 updateRequestForAccessControl(actualRequest, protect(securityOrigin()), m_options.storedCredentialsPolicy);
549
550 m_preflightChecker = nullptr;
551
552 // It should be ok to skip the security check since we already asked about the preflight request.
553 loadRequest(WTF::move(actualRequest), SecurityCheckPolicy::SkipSecurityCheck);
554}
555
556void DocumentThreadableLoader::preflightFailure(std::optional<ResourceLoaderIdentifier> identifier, const ResourceError& error)
557{
558 m_preflightChecker = nullptr;
559
560 RefPtr frame = m_document->frame();
561 if (identifier)
562 InspectorInstrumentation::didFailLoading(frame.get(), protect(frame->loader().documentLoader()), *identifier, error);
563
564 if (m_shouldLogError == ShouldLogError::Yes)
565 logError(protect(*m_document), error, m_options.initiatorType);
566
567 if (RefPtr client = m_client.get())
568 client->didFail(m_document->identifier(), error);
569}
570
571void DocumentThreadableLoader::loadRequest(ResourceRequest&& request, SecurityCheckPolicy securityCheck)
572{
573 Ref<DocumentThreadableLoader> protectedThis(*this);
574
575 // Any credential should have been removed from the cross-site requests.
576 m_responseURL = request.url();
577
578 const URL& requestURL = request.url();
579 m_options.securityCheck = securityCheck;
580 ASSERT(m_sameOriginRequest || !requestURL.hasCredentials())((void)0);
581
582 if (!m_referrer.isNull())
583 request.setHTTPReferrer(m_referrer);
584
585 if (m_async) {
586 ResourceLoaderOptions options = m_options;
587 options.loadedFromFetch = m_options.initiatorType == cachedResourceRequestInitiatorTypes().fetch ? LoadedFromFetch::Yes : LoadedFromFetch::No;
588 options.clientCredentialPolicy = m_sameOriginRequest ? ClientCredentialPolicy::MayAskClientForCredentials : ClientCredentialPolicy::CannotAskClientForCredentials;
589 options.contentSecurityPolicyImposition = ContentSecurityPolicyImposition::SkipPolicyCheck;
590
591 // If there is integrity metadata to validate, we must buffer.
592 if (!m_options.integrity.isEmpty())
593 options.dataBufferingPolicy = DataBufferingPolicy::BufferData;
594
595 request.setAllowCookies(m_options.storedCredentialsPolicy == StoredCredentialsPolicy::Use);
596 CachedResourceRequest newRequest(WTF::move(request), options);
597 newRequest.setInitiatorType(AtomString { m_options.initiatorType });
598 newRequest.setOrigin(protect(securityOrigin()));
599
600 ASSERT(!m_resource)((void)0);
601 if (RefPtr resource = std::exchange(m_resource, nullptr).get())
602 resource->removeClient(*this);
603
604 auto cachedResource = protect(protect(*m_document)->cachedResourceLoader())->requestRawResource(WTF::move(newRequest));
605 m_resource = nullptr;
606 if (cachedResource)
607 m_resource = WTF::move(cachedResource.value());
608 if (RefPtr resource = m_resource.get())
609 resource->addClient(*this);
610 else
611 logErrorAndFail(cachedResource.error());
612 return;
613 }
614
615 // If credentials mode is 'Omit', we should disable cookie sending.
616 ASSERT(m_options.credentials != FetchOptions::Credentials::Omit)((void)0);
617
618 ResourceLoadTiming loadTiming;
619 loadTiming.markStartTime();
620
621 // FIXME: ThreadableLoaderOptions.sniffContent is not supported for synchronous requests.
622 RefPtr frame = m_document->frame();
623 if (!frame)
624 return;
625
626 if (MixedContentChecker::shouldBlockRequest(*frame, requestURL))
627 return;
628
629 RefPtr<SharedBuffer> data;
630 ResourceError error;
631 ResourceResponse response;
632 Ref frameLoader = frame->loader();
633 auto identifier = frameLoader->loadResourceSynchronously(request, m_options.clientCredentialPolicy, m_options, *m_originalHeaders, error, response, data);
634
635 loadTiming.markEndTime();
636
637 if (!error.isNull() && response.httpStatusCode() <= 0) {
638 if (requestURL.protocolIsFile()) {
639 // We don't want XMLHttpRequest to raise an exception for file:// resources, see <rdar://problem/4962298>.
640 // FIXME: XMLHttpRequest quirks should be in XMLHttpRequest code, not in DocumentThreadableLoader.cpp.
641 didReceiveResponse(identifier, WTF::move(response));
642 didFinishLoading(identifier, { });
643 return;
644 }
645 logErrorAndFail(error);
646 return;
647 }
648
649 if (response.containsInvalidHTTPHeaders()) {
650 didFail(identifier, badResponseHeadersError(request.url()));
651 return;
652 }
653
654 if (!shouldPerformSecurityChecks()) {
655 // FIXME: FrameLoader::loadSynchronously() does not tell us whether a redirect happened or not, so we guess by comparing the
656 // request and response URLs. This isn't a perfect test though, since a server can serve a redirect to the same URL that was
657 // requested. Also comparing the request and response URLs as strings will fail if the requestURL still has its credentials.
658 bool didRedirect = requestURL != response.url();
659 if (didRedirect) {
660 if (!isAllowedByContentSecurityPolicy(response.url(), ContentSecurityPolicy::RedirectResponseReceived::Yes)) {
661 reportContentSecurityPolicyError(requestURL);
662 return;
663 }
664 if (!isAllowedRedirect(response.url())) {
665 reportCrossOriginResourceSharingError(requestURL);
666 return;
667 }
668 }
669
670 if (!m_sameOriginRequest) {
671 if (m_options.mode == FetchOptions::Mode::NoCors)
672 response.setTainting(ResourceResponse::Tainting::Opaque);
673 else {
674 ASSERT(m_options.mode == FetchOptions::Mode::Cors)((void)0);
675 response.setTainting(ResourceResponse::Tainting::Cors);
676 auto accessControlCheckResult = passesAccessControlCheck(response, m_options.storedCredentialsPolicy, protect(securityOrigin()), &CrossOriginAccessControlCheckDisabler::singleton());
677 if (!accessControlCheckResult) {
678 logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, response.url(), accessControlCheckResult.error(), ResourceError::Type::AccessControl));
679 return;
680 }
681 }
682 }
683 }
684
685 const auto* timing = response.deprecatedNetworkLoadMetricsOrNull();
686 auto resourceTiming = ResourceTiming::fromSynchronousLoad(requestURL, m_options.initiatorType, loadTiming, timing ? *timing : NetworkLoadMetrics::emptyMetrics(), response, protect(securityOrigin()));
687
688 didReceiveResponse(identifier, WTF::move(response));
689
690 if (data)
691 didReceiveData(*data);
692
693 if (options().initiatorContext == InitiatorContext::Worker)
694 finishedTimingForWorkerLoad(resourceTiming);
695 else {
696 if (RefPtr window = document().window())
697 protect(window->performance())->addResourceTiming(WTF::move(resourceTiming));
698 }
699
700 didFinishLoading(identifier, { });
701}
702
703bool DocumentThreadableLoader::isAllowedByContentSecurityPolicy(const URL& url, ContentSecurityPolicy::RedirectResponseReceived redirectResponseReceived, const URL& preRedirectURL)
704{
705 switch (m_options.contentSecurityPolicyEnforcement) {
706 case ContentSecurityPolicyEnforcement::DoNotEnforce:
707 return true;
708 case ContentSecurityPolicyEnforcement::EnforceWorkerSrcDirective:
709 return protect(contentSecurityPolicy())->allowWorkerFromSource(url, redirectResponseReceived, preRedirectURL);
710 case ContentSecurityPolicyEnforcement::EnforceConnectSrcDirective:
711 return protect(contentSecurityPolicy())->allowConnectToSource(url, redirectResponseReceived, preRedirectURL);
712 case ContentSecurityPolicyEnforcement::EnforceScriptSrcDirective:
713 return protect(contentSecurityPolicy())->allowScriptFromSource(url, redirectResponseReceived, preRedirectURL, m_options.integrity, m_options.nonce);
714 }
715 ASSERT_NOT_REACHED()((void)0);
716 return false;
717}
718
719bool DocumentThreadableLoader::isResponseAllowedByContentSecurityPolicy(const ResourceResponse& response)
720{
721 return isAllowedByContentSecurityPolicy(response.url(), ContentSecurityPolicy::RedirectResponseReceived::Yes, { });
722}
723
724bool DocumentThreadableLoader::isAllowedRedirect(const URL& url)
725{
726 if (m_options.mode == FetchOptions::Mode::NoCors)
727 return true;
728
729 return m_sameOriginRequest && protect(securityOrigin())->canRequest(url, OriginAccessPatternsForWebProcess::singleton());
730}
731
732SecurityOrigin& DocumentThreadableLoader::securityOrigin() const
733{
734 return m_origin ? *m_origin : protect(m_document)->securityOrigin();
735}
736
737Ref<SecurityOrigin> DocumentThreadableLoader::topOrigin() const
738{
739 return protect(*m_document)->topOrigin();
740}
741
742const ContentSecurityPolicy& DocumentThreadableLoader::contentSecurityPolicy() const
743{
744 if (m_contentSecurityPolicy)
745 return *m_contentSecurityPolicy.get();
746 ASSERT(m_document->contentSecurityPolicy())((void)0);
747 return *m_document->contentSecurityPolicy();
748}
749
750const CrossOriginEmbedderPolicy& DocumentThreadableLoader::crossOriginEmbedderPolicy() const
751{
752 if (m_crossOriginEmbedderPolicy)
753 return *m_crossOriginEmbedderPolicy;
754 return m_document->crossOriginEmbedderPolicy();
755}
756
757void DocumentThreadableLoader::reportRedirectionWithBadScheme(const URL& url)
758{
759 logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, url, "Redirection to URL with a scheme that is not HTTP(S)."_s, ResourceError::Type::AccessControl));
760}
761
762void DocumentThreadableLoader::reportContentSecurityPolicyError(const URL& url)
763{
764 logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, url, "Blocked by Content Security Policy."_s, ResourceError::Type::AccessControl));
765}
766
767void DocumentThreadableLoader::reportCrossOriginResourceSharingError(const URL& url)
768{
769 logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, url, "Cross-origin redirection denied by Cross-Origin Resource Sharing policy."_s, ResourceError::Type::AccessControl));
770}
771
772void DocumentThreadableLoader::reportIntegrityMetadataError(const CachedResource& resource, const String& expectedMetadata)
773{
774 logErrorAndFail(ResourceError(errorDomainWebKitInternal, 0, resource.url(), makeString("Failed integrity metadata check. "_s, integrityMismatchDescription(resource, expectedMetadata)), ResourceError::Type::AccessControl));
775}
776
777void DocumentThreadableLoader::logErrorAndFail(const ResourceError& error)
778{
779 if (m_shouldLogError == ShouldLogError::Yes) {
780 Ref document = *m_document;
781 if (error.isAccessControl() && error.domain() != InspectorNetworkAgent::errorDomain() && !error.localizedDescription().isEmpty())
782 document->addConsoleMessage(MessageSource::Security, MessageLevel::Error, error.localizedDescription());
783 logError(document, error, m_options.initiatorType);
784 }
785 ASSERT(m_client)((void)0);
786 if (RefPtr client = m_client.get())
787 client->didFail(m_document->identifier(), error); // May cause the client to get destroyed.
788}
789
790} // namespace WebCore