1 /*
2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2011 Google 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
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include "third_party/blink/renderer/core/loader/document_loader.h"
31
32 #include <memory>
33 #include <utility>
34
35 #include "base/auto_reset.h"
36 #include "base/metrics/histogram_macros.h"
37 #include "base/time/default_tick_clock.h"
38 #include "build/chromeos_buildflags.h"
39 #include "services/network/public/cpp/web_sandbox_flags.h"
40 #include "services/network/public/mojom/web_sandbox_flags.mojom-blink.h"
41 #include "third_party/blink/public/common/features.h"
42 #include "third_party/blink/public/mojom/commit_result/commit_result.mojom-blink.h"
43 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
44 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
45 #include "third_party/blink/public/platform/web_url_request.h"
46 #include "third_party/blink/renderer/core/dom/document.h"
47 #include "third_party/blink/renderer/core/dom/document_init.h"
48 #include "third_party/blink/renderer/core/dom/document_parser.h"
49 #include "third_party/blink/renderer/core/dom/events/event.h"
50 #include "third_party/blink/renderer/core/dom/scriptable_document_parser.h"
51 #include "third_party/blink/renderer/core/dom/weak_identifier_map.h"
52 #include "third_party/blink/renderer/core/execution_context/security_context_init.h"
53 #include "third_party/blink/renderer/core/execution_context/window_agent.h"
54 #include "third_party/blink/renderer/core/execution_context/window_agent_factory.h"
55 #include "third_party/blink/renderer/core/feature_policy/document_policy_parser.h"
56 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
57 #include "third_party/blink/renderer/core/frame/deprecation.h"
58 #include "third_party/blink/renderer/core/frame/frame_console.h"
59 #include "third_party/blink/renderer/core/frame/intervention.h"
60 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
61 #include "third_party/blink/renderer/core/frame/local_frame.h"
62 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
63 #include "third_party/blink/renderer/core/frame/settings.h"
64 #include "third_party/blink/renderer/core/html/html_document.h"
65 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
66 #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
67 #include "third_party/blink/renderer/core/input/event_handler.h"
68 #include "third_party/blink/renderer/core/inspector/console_message.h"
69 #include "third_party/blink/renderer/core/inspector/inspector_trace_events.h"
70 #include "third_party/blink/renderer/core/inspector/main_thread_debugger.h"
71 #include "third_party/blink/renderer/core/loader/alternate_signed_exchange_resource_info.h"
72 #include "third_party/blink/renderer/core/loader/appcache/application_cache_host_for_frame.h"
73 #include "third_party/blink/renderer/core/loader/frame_fetch_context.h"
74 #include "third_party/blink/renderer/core/loader/frame_loader.h"
75 #include "third_party/blink/renderer/core/loader/idleness_detector.h"
76 #include "third_party/blink/renderer/core/loader/interactive_detector.h"
77 #include "third_party/blink/renderer/core/loader/mixed_content_checker.h"
78 #include "third_party/blink/renderer/core/loader/prefetched_signed_exchange_manager.h"
79 #include "third_party/blink/renderer/core/loader/preload_helper.h"
80 #include "third_party/blink/renderer/core/loader/private/frame_client_hints_preferences_context.h"
81 #include "third_party/blink/renderer/core/loader/progress_tracker.h"
82 #include "third_party/blink/renderer/core/loader/subresource_filter.h"
83 #include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
84 #include "third_party/blink/renderer/core/page/frame_tree.h"
85 #include "third_party/blink/renderer/core/page/page.h"
86 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h"
87 #include "third_party/blink/renderer/core/probe/core_probes.h"
88 #include "third_party/blink/renderer/core/timing/dom_window_performance.h"
89 #include "third_party/blink/renderer/core/timing/window_performance.h"
90 #include "third_party/blink/renderer/core/xml/document_xslt.h"
91 #include "third_party/blink/renderer/platform/bindings/microtask.h"
92 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
93 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
94 #include "third_party/blink/renderer/platform/heap/heap.h"
95 #include "third_party/blink/renderer/platform/loader/cors/cors.h"
96 #include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
97 #include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
98 #include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
99 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
100 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
101 #include "third_party/blink/renderer/platform/loader/fetch/resource_timing_info.h"
102 #include "third_party/blink/renderer/platform/loader/fetch/source_keyed_cached_metadata_handler.h"
103 #include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h"
104 #include "third_party/blink/renderer/platform/loader/ftp_directory_listing.h"
105 #include "third_party/blink/renderer/platform/loader/static_data_navigation_body_loader.h"
106 #include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
107 #include "third_party/blink/renderer/platform/mhtml/mhtml_archive.h"
108 #include "third_party/blink/renderer/platform/network/encoded_form_data.h"
109 #include "third_party/blink/renderer/platform/network/http_names.h"
110 #include "third_party/blink/renderer/platform/network/http_parsers.h"
111 #include "third_party/blink/renderer/platform/network/network_utils.h"
112 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
113 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
114 #include "third_party/blink/renderer/platform/web_test_support.h"
115 #include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
116 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
117 #include "third_party/blink/renderer/platform/wtf/assertions.h"
118 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
119 #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
120 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
121 #include "third_party/blink/renderer/platform/wtf/vector.h"
122
123 namespace blink {
124
125 namespace {
CopyInitiatorOriginTrials(const WebVector<int> & initiator_origin_trial_features)126 Vector<OriginTrialFeature> CopyInitiatorOriginTrials(
127 const WebVector<int>& initiator_origin_trial_features) {
128 Vector<OriginTrialFeature> result;
129 for (auto feature : initiator_origin_trial_features) {
130 // Convert from int to OriginTrialFeature. These values are passed between
131 // blink navigations. OriginTrialFeature isn't visible outside of blink (and
132 // doesn't need to be) so the values are transferred outside of blink as
133 // ints and casted to OriginTrialFeature once being processed in blink.
134 result.push_back(static_cast<OriginTrialFeature>(feature));
135 }
136 return result;
137 }
138
CopyForceEnabledOriginTrials(const WebVector<WebString> & force_enabled_origin_trials)139 Vector<String> CopyForceEnabledOriginTrials(
140 const WebVector<WebString>& force_enabled_origin_trials) {
141 Vector<String> result;
142 result.ReserveInitialCapacity(
143 SafeCast<wtf_size_t>(force_enabled_origin_trials.size()));
144 for (const auto& trial : force_enabled_origin_trials)
145 result.push_back(trial);
146 return result;
147 }
148
IsPagePopupRunningInWebTest(LocalFrame * frame)149 bool IsPagePopupRunningInWebTest(LocalFrame* frame) {
150 return frame && frame->GetPage()->GetChromeClient().IsPopup() &&
151 WebTestSupport::IsRunningWebTest();
152 }
153
154 } // namespace
155
DocumentLoader(LocalFrame * frame,WebNavigationType navigation_type,ContentSecurityPolicy * content_security_policy,std::unique_ptr<WebNavigationParams> navigation_params)156 DocumentLoader::DocumentLoader(
157 LocalFrame* frame,
158 WebNavigationType navigation_type,
159 ContentSecurityPolicy* content_security_policy,
160 std::unique_ptr<WebNavigationParams> navigation_params)
161 : params_(std::move(navigation_params)),
162 url_(params_->url),
163 http_method_(static_cast<String>(params_->http_method)),
164 referrer_(Referrer(params_->referrer.IsEmpty()
165 ? Referrer::NoReferrer()
166 : static_cast<String>(params_->referrer),
167 params_->referrer_policy)),
168 http_body_(params_->http_body),
169 http_content_type_(static_cast<String>(params_->http_content_type)),
170 origin_policy_(params_->origin_policy),
171 requestor_origin_(params_->requestor_origin),
172 unreachable_url_(params_->unreachable_url),
173 ip_address_space_(params_->ip_address_space),
174 grant_load_local_resources_(params_->grant_load_local_resources),
175 force_fetch_cache_mode_(params_->force_fetch_cache_mode),
176 frame_policy_(params_->frame_policy.value_or(FramePolicy())),
177 frame_(frame),
178 // For back/forward navigations, the browser passed a history item to use
179 // at commit time in |params_|. Set it as the current history item of this
180 // DocumentLoader. For other navigations, |history_item_| will be created
181 // when the FrameLoader calls SetHistoryItemStateForCommit.
182 history_item_(IsBackForwardLoadType(params_->frame_load_type)
183 ? params_->history_item
184 : nullptr),
185 original_url_(params_->url),
186 original_referrer_(referrer_),
187 response_(params_->response.ToResourceResponse()),
188 load_type_(params_->frame_load_type),
189 is_client_redirect_(params_->is_client_redirect),
190 // TODO(japhet): This is needed because the browser process DCHECKs if the
191 // first entry we commit in a new frame has replacement set. It's unclear
192 // whether the DCHECK is right, investigate removing this special case.
193 // TODO(dgozman): we should get rid of this boolean field, and make client
194 // responsible for it's own view of "replaces current item", based on the
195 // frame load type.
196 replaces_current_history_item_(
197 load_type_ == WebFrameLoadType::kReplaceCurrentItem &&
198 (!frame_->Loader().Opener() || !url_.IsEmpty())),
199 data_received_(false),
200 // The input CSP is null when the CSP check done in the FrameLoader failed
201 content_security_policy_(
202 content_security_policy
203 ? content_security_policy
204 : MakeGarbageCollected<ContentSecurityPolicy>()),
205 was_blocked_by_csp_(!content_security_policy),
206 // Loading the document was blocked by the CSP check. Pretend that this
207 // was an empty document instead and don't reuse the original URL. More
208 // details in: https://crbug.com/622385.
209 // TODO(https://crbug.com/555418) Remove this once XFO moves to the
210 // browser.
211
212 // Update |origin_to_commit_| to contain an opaque origin with precursor
213 // information that is consistent with the final request URL.
214 // Note: this doesn't use |url_| for the origin calculation, because
215 // redirects are not yet accounted for (this happens later in
216 // StartLoadingInternal).
217 origin_to_commit_(
218 was_blocked_by_csp_
219 ? blink::SecurityOrigin::Create(response_.CurrentRequestUrl())
220 ->DeriveNewOpaqueOrigin()
221 : params_->origin_to_commit.IsNull()
222 ? nullptr
223 : params_->origin_to_commit.Get()->IsolatedCopy()),
224 navigation_type_(navigation_type),
225 document_load_timing_(*this),
226 service_worker_network_provider_(
227 std::move(params_->service_worker_network_provider)),
228 was_blocked_by_document_policy_(false),
229 state_(kNotStarted),
230 in_commit_data_(false),
231 data_buffer_(SharedBuffer::Create()),
232 devtools_navigation_token_(params_->devtools_navigation_token),
233 had_sticky_activation_(params_->is_user_activated),
234 had_transient_activation_(
235 LocalFrame::HasTransientUserActivation(frame_) ||
236 params_->had_transient_activation),
237 is_browser_initiated_(params_->is_browser_initiated),
238 was_discarded_(params_->was_discarded),
239 loading_srcdoc_(url_.IsAboutSrcdocURL()),
240 loading_url_as_empty_document_(!params_->is_static_data &&
241 WillLoadUrlAsEmpty(url_)),
242 web_bundle_physical_url_(params_->web_bundle_physical_url),
243 web_bundle_claimed_url_(params_->web_bundle_claimed_url),
244 ukm_source_id_(params_->document_ukm_source_id),
245 clock_(params_->tick_clock ? params_->tick_clock
246 : base::DefaultTickClock::GetInstance()),
247 initiator_origin_trial_features_(
248 CopyInitiatorOriginTrials(params_->initiator_origin_trial_features)),
249 force_enabled_origin_trials_(
250 CopyForceEnabledOriginTrials(params_->force_enabled_origin_trials)),
251 origin_isolated_(params_->origin_isolated),
252 is_cross_browsing_context_group_navigation_(
253 params_->is_cross_browsing_context_group_navigation) {
254 DCHECK(frame_);
255
256 // See `archive_` attribute documentation.
257 if (!frame_->IsMainFrame()) {
258 if (auto* parent = DynamicTo<LocalFrame>(frame_->Tree().Parent()))
259 archive_ = parent->Loader().GetDocumentLoader()->archive_;
260 }
261
262 // Determine if this document should have a text fragment permission token.
263 // We can either generate a new one from this navigation, if it's user
264 // activated, or receive one propagated from the prior navigation that didn't
265 // consume its token.
266 has_text_fragment_token_ = TextFragmentAnchor::GenerateNewToken(*this) ||
267 params_->has_text_fragment_token;
268
269 if (frame_->IsMainFrame()) {
270 previews_state_ = params_->previews_state;
271 } else {
272 // Subframes inherit previews state from the main frame.
273 if (auto* parent = DynamicTo<LocalFrame>(frame_->Tree().Parent()))
274 previews_state_ = parent->Loader().GetDocumentLoader()->previews_state_;
275 }
276
277 document_policy_ = CreateDocumentPolicy();
278
279 WebNavigationTimings& timings = params_->navigation_timings;
280 if (!timings.input_start.is_null())
281 document_load_timing_.SetInputStart(timings.input_start);
282 if (timings.navigation_start.is_null()) {
283 // If we don't have any navigation timings yet, it starts now.
284 document_load_timing_.SetNavigationStart(clock_->NowTicks());
285 } else {
286 document_load_timing_.SetNavigationStart(timings.navigation_start);
287 if (!timings.redirect_start.is_null()) {
288 document_load_timing_.SetRedirectStart(timings.redirect_start);
289 document_load_timing_.SetRedirectEnd(timings.redirect_end);
290 }
291 if (!timings.fetch_start.is_null()) {
292 // If we started fetching, we should have started the navigation.
293 DCHECK(!timings.navigation_start.is_null());
294 document_load_timing_.SetFetchStart(timings.fetch_start);
295 }
296 }
297
298 // The document URL needs to be added to the head of the list as that is
299 // where the redirects originated.
300 if (is_client_redirect_)
301 redirect_chain_.push_back(frame_->GetDocument()->Url());
302
303 if (was_blocked_by_csp_ || was_blocked_by_document_policy_)
304 ReplaceWithEmptyDocument();
305
306 if (commit_reason_ != CommitReason::kInitialization)
307 redirect_chain_.push_back(url_);
308
309 if (IsBackForwardLoadType(params_->frame_load_type))
310 DCHECK(history_item_);
311 }
312
GetFrameLoader() const313 FrameLoader& DocumentLoader::GetFrameLoader() const {
314 DCHECK(frame_);
315 return frame_->Loader();
316 }
317
GetLocalFrameClient() const318 LocalFrameClient& DocumentLoader::GetLocalFrameClient() const {
319 DCHECK(frame_);
320 LocalFrameClient* client = frame_->Client();
321 // LocalFrame clears its |m_client| only after detaching all DocumentLoaders
322 // (i.e. calls detachFromFrame() which clears |frame_|) owned by the
323 // LocalFrame's FrameLoader. So, if |frame_| is non nullptr, |client| is
324 // also non nullptr.
325 DCHECK(client);
326 return *client;
327 }
328
~DocumentLoader()329 DocumentLoader::~DocumentLoader() {
330 DCHECK(!frame_);
331 DCHECK(!application_cache_host_);
332 DCHECK_EQ(state_, kSentDidFinishLoad);
333 }
334
Trace(Visitor * visitor) const335 void DocumentLoader::Trace(Visitor* visitor) const {
336 visitor->Trace(archive_);
337 visitor->Trace(frame_);
338 visitor->Trace(history_item_);
339 visitor->Trace(parser_);
340 visitor->Trace(subresource_filter_);
341 visitor->Trace(resource_loading_hints_);
342 visitor->Trace(document_load_timing_);
343 visitor->Trace(application_cache_host_);
344 visitor->Trace(content_security_policy_);
345 visitor->Trace(cached_metadata_handler_);
346 visitor->Trace(prefetched_signed_exchange_manager_);
347 visitor->Trace(use_counter_);
348 }
349
MainResourceIdentifier() const350 uint64_t DocumentLoader::MainResourceIdentifier() const {
351 return main_resource_identifier_;
352 }
353
GetNavigationTimingInfo() const354 ResourceTimingInfo* DocumentLoader::GetNavigationTimingInfo() const {
355 return navigation_timing_info_.get();
356 }
357
OriginalUrl() const358 const KURL& DocumentLoader::OriginalUrl() const {
359 return original_url_;
360 }
361
OriginalReferrer() const362 const Referrer& DocumentLoader::OriginalReferrer() const {
363 return original_referrer_;
364 }
365
SetSubresourceFilter(SubresourceFilter * subresource_filter)366 void DocumentLoader::SetSubresourceFilter(
367 SubresourceFilter* subresource_filter) {
368 subresource_filter_ = subresource_filter;
369 }
370
Url() const371 const KURL& DocumentLoader::Url() const {
372 return url_;
373 }
374
HttpMethod() const375 const AtomicString& DocumentLoader::HttpMethod() const {
376 return http_method_;
377 }
378
GetReferrer() const379 const Referrer& DocumentLoader::GetReferrer() const {
380 return referrer_;
381 }
382
SetServiceWorkerNetworkProvider(std::unique_ptr<WebServiceWorkerNetworkProvider> provider)383 void DocumentLoader::SetServiceWorkerNetworkProvider(
384 std::unique_ptr<WebServiceWorkerNetworkProvider> provider) {
385 service_worker_network_provider_ = std::move(provider);
386 }
387
DispatchLinkHeaderPreloads(const ViewportDescription * viewport,PreloadHelper::MediaPreloadPolicy media_policy)388 void DocumentLoader::DispatchLinkHeaderPreloads(
389 const ViewportDescription* viewport,
390 PreloadHelper::MediaPreloadPolicy media_policy) {
391 DCHECK_GE(state_, kCommitted);
392 PreloadHelper::LoadLinksFromHeader(
393 GetResponse().HttpHeaderField(http_names::kLink),
394 GetResponse().CurrentRequestUrl(), *frame_, frame_->GetDocument(),
395 PreloadHelper::kOnlyLoadResources, media_policy, viewport,
396 nullptr /* alternate_resource_info */,
397 nullptr /* recursive_prefetch_token */);
398 }
399
DidChangePerformanceTiming()400 void DocumentLoader::DidChangePerformanceTiming() {
401 if (frame_ && state_ >= kCommitted) {
402 GetLocalFrameClient().DidChangePerformanceTiming();
403 }
404 }
405
DidObserveInputDelay(base::TimeDelta input_delay)406 void DocumentLoader::DidObserveInputDelay(base::TimeDelta input_delay) {
407 if (frame_ && state_ >= kCommitted) {
408 GetLocalFrameClient().DidObserveInputDelay(input_delay);
409 }
410 }
DidObserveLoadingBehavior(LoadingBehaviorFlag behavior)411 void DocumentLoader::DidObserveLoadingBehavior(LoadingBehaviorFlag behavior) {
412 if (frame_) {
413 DCHECK_GE(state_, kCommitted);
414 GetLocalFrameClient().DidObserveLoadingBehavior(behavior);
415 }
416 }
417
418 // static
LoadTypeToCommitType(WebFrameLoadType type)419 WebHistoryCommitType LoadTypeToCommitType(WebFrameLoadType type) {
420 switch (type) {
421 case WebFrameLoadType::kStandard:
422 return kWebStandardCommit;
423 case WebFrameLoadType::kBackForward:
424 return kWebBackForwardCommit;
425 case WebFrameLoadType::kReload:
426 case WebFrameLoadType::kReplaceCurrentItem:
427 case WebFrameLoadType::kReloadBypassingCache:
428 return kWebHistoryInertCommit;
429 }
430 NOTREACHED();
431 return kWebHistoryInertCommit;
432 }
433
CategorizeSinglePageAppNavigation(SameDocumentNavigationSource same_document_navigation_source,WebFrameLoadType frame_load_type)434 static SinglePageAppNavigationType CategorizeSinglePageAppNavigation(
435 SameDocumentNavigationSource same_document_navigation_source,
436 WebFrameLoadType frame_load_type) {
437 // |SinglePageAppNavigationType| falls into this grid according to different
438 // combinations of |WebFrameLoadType| and |SameDocumentNavigationSource|:
439 //
440 // HistoryApi Default
441 // kBackForward illegal otherFragmentNav
442 // !kBackForward sameDocBack/Forward historyPushOrReplace
443 switch (same_document_navigation_source) {
444 case kSameDocumentNavigationDefault:
445 if (frame_load_type == WebFrameLoadType::kBackForward) {
446 return kSPANavTypeSameDocumentBackwardOrForward;
447 }
448 return kSPANavTypeOtherFragmentNavigation;
449 case kSameDocumentNavigationHistoryApi:
450 // It's illegal to have both kSameDocumentNavigationHistoryApi and
451 // WebFrameLoadType::kBackForward.
452 DCHECK(frame_load_type != WebFrameLoadType::kBackForward);
453 return kSPANavTypeHistoryPushStateOrReplaceState;
454 }
455 NOTREACHED();
456 return kSPANavTypeSameDocumentBackwardOrForward;
457 }
458
UpdateForSameDocumentNavigation(const KURL & new_url,SameDocumentNavigationSource same_document_navigation_source,scoped_refptr<SerializedScriptValue> data,mojom::blink::ScrollRestorationType scroll_restoration_type,WebFrameLoadType type,bool is_content_initiated)459 void DocumentLoader::UpdateForSameDocumentNavigation(
460 const KURL& new_url,
461 SameDocumentNavigationSource same_document_navigation_source,
462 scoped_refptr<SerializedScriptValue> data,
463 mojom::blink::ScrollRestorationType scroll_restoration_type,
464 WebFrameLoadType type,
465 bool is_content_initiated) {
466 SinglePageAppNavigationType single_page_app_navigation_type =
467 CategorizeSinglePageAppNavigation(same_document_navigation_source, type);
468 UMA_HISTOGRAM_ENUMERATION(
469 "RendererScheduler.UpdateForSameDocumentNavigationCount",
470 single_page_app_navigation_type, kSPANavTypeCount);
471
472 TRACE_EVENT1("blink", "FrameLoader::updateForSameDocumentNavigation", "url",
473 new_url.GetString().Ascii());
474
475 // Generate start and stop notifications only when loader is completed so that
476 // we don't fire them for fragment redirection that happens in window.onload
477 // handler. See https://bugs.webkit.org/show_bug.cgi?id=31838
478 // Do not fire the notifications if the frame is concurrently navigating away
479 // from the document, since a new document is already loading.
480 bool was_loading = frame_->IsLoading();
481 if (!was_loading)
482 GetLocalFrameClient().DidStartLoading();
483
484 // Update the data source's request with the new URL to fake the URL change
485 frame_->GetDocument()->SetURL(new_url);
486
487 KURL old_url = url_;
488 original_url_ = new_url;
489 url_ = new_url;
490 replaces_current_history_item_ = type != WebFrameLoadType::kStandard;
491 if (same_document_navigation_source == kSameDocumentNavigationHistoryApi) {
492 http_method_ = http_names::kGET;
493 http_body_ = nullptr;
494 }
495 redirect_chain_.clear();
496 if (is_client_redirect_)
497 redirect_chain_.push_back(old_url);
498 redirect_chain_.push_back(new_url);
499
500 // We want to allow same-document text fragment navigations if they're coming
501 // from the browser. Do this only on a standard navigation so that we don't
502 // clobber the token when this is called from e.g. history.replaceState.
503 if (type == WebFrameLoadType::kStandard) {
504 has_text_fragment_token_ =
505 TextFragmentAnchor::GenerateNewTokenForSameDocument(
506 new_url.FragmentIdentifier(), type, is_content_initiated,
507 same_document_navigation_source);
508 }
509
510 SetHistoryItemStateForCommit(
511 history_item_.Get(), type,
512 same_document_navigation_source == kSameDocumentNavigationHistoryApi
513 ? HistoryNavigationType::kHistoryApi
514 : HistoryNavigationType::kFragment);
515 history_item_->SetDocumentState(frame_->GetDocument()->GetDocumentState());
516 if (same_document_navigation_source == kSameDocumentNavigationHistoryApi) {
517 history_item_->SetStateObject(std::move(data));
518 history_item_->SetScrollRestorationType(scroll_restoration_type);
519 }
520 WebHistoryCommitType commit_type = LoadTypeToCommitType(type);
521 frame_->GetFrameScheduler()->DidCommitProvisionalLoad(
522 commit_type == kWebHistoryInertCommit,
523 FrameScheduler::NavigationType::kSameDocument);
524
525 GetLocalFrameClient().DidFinishSameDocumentNavigation(
526 history_item_.Get(), commit_type, is_content_initiated);
527 probe::DidNavigateWithinDocument(frame_);
528 if (!was_loading) {
529 GetLocalFrameClient().DidStopLoading();
530 frame_->UpdateFaviconURL();
531 }
532 }
533
UrlForHistory() const534 const KURL& DocumentLoader::UrlForHistory() const {
535 return UnreachableURL().IsEmpty() ? Url() : UnreachableURL();
536 }
537
SetHistoryItemStateForCommit(HistoryItem * old_item,WebFrameLoadType load_type,HistoryNavigationType navigation_type)538 void DocumentLoader::SetHistoryItemStateForCommit(
539 HistoryItem* old_item,
540 WebFrameLoadType load_type,
541 HistoryNavigationType navigation_type) {
542 if (!history_item_ || !IsBackForwardLoadType(load_type))
543 history_item_ = MakeGarbageCollected<HistoryItem>();
544
545 history_item_->SetURL(UrlForHistory());
546 history_item_->SetReferrer(SecurityPolicy::GenerateReferrer(
547 referrer_.referrer_policy, history_item_->Url(), referrer_.referrer));
548 if (EqualIgnoringASCIICase(http_method_, "POST")) {
549 // FIXME: Eventually we have to make this smart enough to handle the case
550 // where we have a stream for the body to handle the "data interspersed with
551 // files" feature.
552 history_item_->SetFormData(http_body_);
553 history_item_->SetFormContentType(http_content_type_);
554 } else {
555 history_item_->SetFormData(nullptr);
556 history_item_->SetFormContentType(g_null_atom);
557 }
558
559 // Don't propagate state from the old item to the new item if there isn't an
560 // old item (obviously), or if this is a back/forward navigation, since we
561 // explicitly want to restore the state we just committed.
562 if (!old_item || IsBackForwardLoadType(load_type))
563 return;
564 // Don't propagate state from the old item if this is a different-document
565 // navigation, unless the before and after pages are logically related. This
566 // means they have the same url (ignoring fragment) and the new item was
567 // loaded via reload or client redirect.
568 WebHistoryCommitType history_commit_type = LoadTypeToCommitType(load_type);
569 if (navigation_type == HistoryNavigationType::kDifferentDocument &&
570 (history_commit_type != kWebHistoryInertCommit ||
571 !EqualIgnoringFragmentIdentifier(old_item->Url(), history_item_->Url())))
572 return;
573 history_item_->SetDocumentSequenceNumber(old_item->DocumentSequenceNumber());
574
575 history_item_->CopyViewStateFrom(old_item);
576 history_item_->SetScrollRestorationType(old_item->ScrollRestorationType());
577
578 // The item sequence number determines whether items are "the same", such
579 // back/forward navigation between items with the same item sequence number is
580 // a no-op. Only treat this as identical if the navigation did not create a
581 // back/forward entry and the url is identical or it was loaded via
582 // history.replaceState().
583 if (history_commit_type == kWebHistoryInertCommit &&
584 (navigation_type == HistoryNavigationType::kHistoryApi ||
585 old_item->Url() == history_item_->Url())) {
586 history_item_->SetStateObject(old_item->StateObject());
587 history_item_->SetItemSequenceNumber(old_item->ItemSequenceNumber());
588 }
589 }
590
591 mojo::PendingReceiver<mojom::blink::WorkerTimingContainer>
TakePendingWorkerTimingReceiver(int request_id)592 DocumentLoader::TakePendingWorkerTimingReceiver(int request_id) {
593 if (!GetServiceWorkerNetworkProvider())
594 return mojo::NullReceiver();
595 return GetServiceWorkerNetworkProvider()->TakePendingWorkerTimingReceiver(
596 request_id);
597 }
598
BodyCodeCacheReceived(mojo_base::BigBuffer data)599 void DocumentLoader::BodyCodeCacheReceived(mojo_base::BigBuffer data) {
600 if (cached_metadata_handler_) {
601 cached_metadata_handler_->SetSerializedCachedMetadata(std::move(data));
602 }
603 }
604
BodyDataReceived(base::span<const char> data)605 void DocumentLoader::BodyDataReceived(base::span<const char> data) {
606 TRACE_EVENT0("loading", "DocumentLoader::BodyDataReceived");
607 GetFrameLoader().Progress().IncrementProgress(main_resource_identifier_,
608 data.size());
609 probe::DidReceiveData(probe::ToCoreProbeSink(GetFrame()),
610 main_resource_identifier_, this, data.data(),
611 data.size());
612
613 TRACE_EVENT1("loading", "DocumentLoader::HandleData", "length", data.size());
614
615 DCHECK(data.data());
616 DCHECK(data.size());
617 DCHECK(!frame_->GetPage()->Paused());
618 time_of_last_data_received_ = clock_->NowTicks();
619
620 if (listing_ftp_directory_ || loading_main_document_from_mhtml_archive_) {
621 // 1) Ftp directory listings accumulate data buffer and transform it later
622 // to the actual document content.
623 // 2) Mhtml archives accumulate data buffer and parse it as mhtml later
624 // to retrieve the actual document content.
625 data_buffer_->Append(data.data(), data.size());
626 return;
627 }
628
629 ProcessDataBuffer(data.data(), data.size());
630 }
631
BodyLoadingFinished(base::TimeTicks completion_time,int64_t total_encoded_data_length,int64_t total_encoded_body_length,int64_t total_decoded_body_length,bool should_report_corb_blocking,const base::Optional<WebURLError> & error)632 void DocumentLoader::BodyLoadingFinished(
633 base::TimeTicks completion_time,
634 int64_t total_encoded_data_length,
635 int64_t total_encoded_body_length,
636 int64_t total_decoded_body_length,
637 bool should_report_corb_blocking,
638 const base::Optional<WebURLError>& error) {
639 TRACE_EVENT0("loading", "DocumentLoader::BodyLoadingFinished");
640 response_.SetEncodedDataLength(total_encoded_data_length);
641 response_.SetEncodedBodyLength(total_encoded_body_length);
642 response_.SetDecodedBodyLength(total_decoded_body_length);
643
644 if (!error) {
645 GetFrameLoader().Progress().CompleteProgress(main_resource_identifier_);
646 probe::DidFinishLoading(
647 probe::ToCoreProbeSink(GetFrame()), main_resource_identifier_, this,
648 completion_time, total_encoded_data_length, total_decoded_body_length,
649 should_report_corb_blocking);
650 if (response_.IsHTTP()) {
651 // The response is being copied here to pass the Encoded and Decoded
652 // sizes.
653 // TODO(yoav): copy the sizes info directly.
654 navigation_timing_info_->SetFinalResponse(response_);
655 navigation_timing_info_->AddFinalTransferSize(
656 total_encoded_data_length == -1 ? 0 : total_encoded_data_length);
657 if (report_timing_info_to_parent_) {
658 navigation_timing_info_->SetLoadResponseEnd(completion_time);
659 if (state_ >= kCommitted) {
660 // Note that we currently lose timing info for empty documents,
661 // which will be fixed with synchronous commit.
662 // Main resource timing information is reported through the owner
663 // to be passed to the parent frame, if appropriate.
664
665 // TODO(https://crbug.com/900700): Set a Mojo pending receiver for
666 // WorkerTimingContainer in |navigation_timing_info|.
667 frame_->Owner()->AddResourceTiming(*navigation_timing_info_);
668 }
669 frame_->SetShouldSendResourceTimingInfoToParent(false);
670 }
671 }
672 FinishedLoading(completion_time);
673 return;
674 }
675
676 ResourceError resource_error(*error);
677 if (network_utils::IsCertificateTransparencyRequiredError(
678 resource_error.ErrorCode())) {
679 CountUse(WebFeature::kCertificateTransparencyRequiredErrorOnResourceLoad);
680 }
681 GetFrameLoader().Progress().CompleteProgress(main_resource_identifier_);
682 probe::DidFailLoading(probe::ToCoreProbeSink(GetFrame()),
683 main_resource_identifier_, this, resource_error,
684 frame_->GetDevToolsFrameToken());
685 GetFrame()->Console().DidFailLoading(this, main_resource_identifier_,
686 resource_error);
687 LoadFailed(resource_error);
688 }
689
LoadFailed(const ResourceError & error)690 void DocumentLoader::LoadFailed(const ResourceError& error) {
691 TRACE_EVENT1("navigation,rail", "DocumentLoader::LoadFailed", "error",
692 error.ErrorCode());
693 body_loader_.reset();
694 virtual_time_pauser_.UnpauseVirtualTime();
695
696 if (!error.IsCancellation() && frame_->Owner())
697 frame_->Owner()->RenderFallbackContent(frame_);
698
699 WebHistoryCommitType history_commit_type = LoadTypeToCommitType(load_type_);
700 DCHECK_EQ(kCommitted, state_);
701 if (frame_->GetDocument()->Parser())
702 frame_->GetDocument()->Parser()->StopParsing();
703 state_ = kSentDidFinishLoad;
704 GetLocalFrameClient().DispatchDidFailLoad(error, history_commit_type);
705 GetFrameLoader().DidFinishNavigation(
706 FrameLoader::NavigationFinishState::kFailure);
707 DCHECK_EQ(kSentDidFinishLoad, state_);
708 params_ = nullptr;
709 }
710
FinishedLoading(base::TimeTicks finish_time)711 void DocumentLoader::FinishedLoading(base::TimeTicks finish_time) {
712 body_loader_.reset();
713 virtual_time_pauser_.UnpauseVirtualTime();
714
715 DCHECK(commit_reason_ == CommitReason::kInitialization ||
716 !frame_->GetPage()->Paused() ||
717 MainThreadDebugger::Instance()->IsPaused());
718
719 if (listing_ftp_directory_) {
720 data_buffer_ = GenerateFtpDirectoryListingHtml(
721 response_.CurrentRequestUrl(), data_buffer_.get());
722 ProcessDataBuffer();
723 }
724
725 if (loading_main_document_from_mhtml_archive_ && state_ < kCommitted) {
726 // The browser process should block any navigation to an MHTML archive
727 // inside iframes. See NavigationRequest::OnResponseStarted().
728 CHECK(frame_->IsMainFrame());
729
730 archive_ = MHTMLArchive::Create(url_, std::move(data_buffer_));
731 }
732
733 // We should not call FinishedLoading before committing navigation,
734 // except for the mhtml case. When loading an MHTML archive, the whole archive
735 // has to be validated before committing the navigation. The validation
736 // process loads the entire body of the archive, which will move the state to
737 // FinishedLoading.
738 if (!loading_main_document_from_mhtml_archive_)
739 DCHECK_GE(state_, kCommitted);
740
741 base::TimeTicks response_end_time = finish_time;
742 if (response_end_time.is_null())
743 response_end_time = time_of_last_data_received_;
744 if (response_end_time.is_null())
745 response_end_time = clock_->NowTicks();
746 GetTiming().SetResponseEnd(response_end_time);
747
748 if (!frame_)
749 return;
750
751 if (parser_) {
752 if (parser_blocked_count_) {
753 finish_loading_when_parser_resumed_ = true;
754 } else {
755 parser_->Finish();
756 parser_.Clear();
757 }
758 }
759 }
760
HandleRedirect(const KURL & current_request_url)761 void DocumentLoader::HandleRedirect(const KURL& current_request_url) {
762 // Browser process should have already checked that redirecting url is
763 // allowed to display content from the target origin.
764 // When the referrer page is in an unsigned Web Bundle file in local
765 // (eg: file:///tmp/a.wbn), Chrome internally redirects the navigation to the
766 // page (eg: https://example.com/page.html) inside the Web Bundle file
767 // to the file's URL (file:///tmp/a.wbn?https://example.com/page.html). In
768 // this case, CanDisplay() returns false, and web_bundle_claimed_url must not
769 // be null.
770 CHECK(SecurityOrigin::Create(current_request_url)->CanDisplay(url_) ||
771 !params_->web_bundle_claimed_url.IsNull());
772
773 DCHECK(!GetTiming().FetchStart().is_null());
774 redirect_chain_.push_back(url_);
775 GetTiming().AddRedirect(current_request_url, url_);
776 }
777
ShouldReportTimingInfoToParent()778 bool DocumentLoader::ShouldReportTimingInfoToParent() {
779 DCHECK(frame_);
780 // <iframe>s should report the initial navigation requested by the parent
781 // document, but not subsequent navigations.
782 if (!frame_->Owner())
783 return false;
784 // Note that this can be racy since this information is forwarded over IPC
785 // when crossing process boundaries.
786 if (!frame_->should_send_resource_timing_info_to_parent())
787 return false;
788 // Do not report iframe navigation that restored from history, since its
789 // location may have been changed after initial navigation,
790 if (load_type_ == WebFrameLoadType::kBackForward) {
791 // ...and do not report subsequent navigations in the iframe too.
792 frame_->SetShouldSendResourceTimingInfoToParent(false);
793 return false;
794 }
795 return true;
796 }
797
ConsoleError(const String & message)798 void DocumentLoader::ConsoleError(const String& message) {
799 auto* console_message = MakeGarbageCollected<ConsoleMessage>(
800 mojom::ConsoleMessageSource::kSecurity,
801 mojom::ConsoleMessageLevel::kError, message,
802 response_.CurrentRequestUrl(), this, MainResourceIdentifier());
803 frame_->DomWindow()->AddConsoleMessage(console_message);
804 }
805
ReplaceWithEmptyDocument()806 void DocumentLoader::ReplaceWithEmptyDocument() {
807 DCHECK(params_);
808 KURL blocked_url = SecurityOrigin::UrlWithUniqueOpaqueOrigin();
809 original_url_ = blocked_url;
810 url_ = blocked_url;
811 params_->url = blocked_url;
812 WebNavigationParams::FillStaticResponse(params_.get(), "text/html", "UTF-8",
813 "");
814 }
815
CreateDocumentPolicy()816 DocumentPolicy::ParsedDocumentPolicy DocumentLoader::CreateDocumentPolicy() {
817 // For URLs referring to local content to parent frame, they have no way to
818 // specify the document policy they use. If the parent frame requires a
819 // document policy on them, use the required policy as effective policy.
820 if (url_.IsEmpty() || url_.ProtocolIsAbout() || url_.ProtocolIsData() ||
821 url_.ProtocolIs("blob") || url_.ProtocolIs("filesystem"))
822 return {frame_policy_.required_document_policy, {} /* endpoint_map */};
823
824 PolicyParserMessageBuffer header_logger("Document-Policy HTTP header: ");
825 PolicyParserMessageBuffer require_header_logger(
826 "Require-Document-Policy HTTP header: ");
827
828 // Filtering out features that are disabled by origin trial is done
829 // in SecurityContextInit when origin trial context is available.
830 auto parsed_policy =
831 DocumentPolicyParser::Parse(
832 response_.HttpHeaderField(http_names::kDocumentPolicy), header_logger)
833 .value_or(DocumentPolicy::ParsedDocumentPolicy{});
834
835 // |parsed_policy| can have policies that are disabled by origin trial,
836 // but |frame_policy_.required_document_policy| cannot.
837 // It is safe to call |IsPolicyCompatible| as long as required policy is
838 // checked against origin trial.
839 if (!DocumentPolicy::IsPolicyCompatible(
840 frame_policy_.required_document_policy,
841 parsed_policy.feature_state)) {
842 was_blocked_by_document_policy_ = true;
843 // When header policy is less strict than required policy, use required
844 // policy to initialize document policy for the document.
845 parsed_policy = {frame_policy_.required_document_policy,
846 {} /* endpoint_map */};
847 }
848
849 // Initialize required document policy for subtree.
850 //
851 // If the document is blocked by document policy, there won't be content
852 // in the sub-frametree, thus no need to initialize required_policy for
853 // subtree.
854 if (!was_blocked_by_document_policy_) {
855 // Require-Document-Policy header only affects subtree of current document,
856 // but not the current document.
857 const DocumentPolicyFeatureState header_required_policy =
858 DocumentPolicyParser::Parse(
859 response_.HttpHeaderField(http_names::kRequireDocumentPolicy),
860 require_header_logger)
861 .value_or(DocumentPolicy::ParsedDocumentPolicy{})
862 .feature_state;
863 frame_->SetRequiredDocumentPolicy(DocumentPolicy::MergeFeatureState(
864 header_required_policy, frame_policy_.required_document_policy));
865 }
866
867 document_policy_parsing_messages_.AppendVector(header_logger.GetMessages());
868 document_policy_parsing_messages_.AppendVector(
869 require_header_logger.GetMessages());
870
871 return parsed_policy;
872 }
873
HandleResponse()874 void DocumentLoader::HandleResponse() {
875 DCHECK(frame_);
876 application_cache_host_->DidReceiveResponseForMainResource(response_);
877
878 if (response_.CurrentRequestUrl().ProtocolIs("ftp") &&
879 response_.MimeType() == "text/vnd.chromium.ftp-dir") {
880 if (response_.CurrentRequestUrl().Query() == "raw") {
881 // Interpret the FTP LIST command result as text.
882 response_.SetMimeType("text/plain");
883 } else {
884 // FTP directory listing: Make up an HTML for the entries.
885 listing_ftp_directory_ = true;
886 response_.SetMimeType("text/html");
887 }
888 }
889
890 if (frame_->Owner() && response_.IsHTTP() &&
891 !cors::IsOkStatus(response_.HttpStatusCode()))
892 frame_->Owner()->RenderFallbackContent(frame_);
893 }
894
CommitData(const char * bytes,size_t length)895 void DocumentLoader::CommitData(const char* bytes, size_t length) {
896 TRACE_EVENT1("loading", "DocumentLoader::CommitData", "length", length);
897
898 // This can happen if document.close() is called by an event handler while
899 // there's still pending incoming data.
900 // TODO(dgozman): we should stop body loader when stopping the parser to
901 // avoid unnecessary work. This may happen, for example, when we abort current
902 // committed document which is still loading when initiating a new navigation.
903 if (!frame_ || !frame_->GetDocument()->Parsing())
904 return;
905
906 base::AutoReset<bool> reentrancy_protector(&in_commit_data_, true);
907 if (length)
908 data_received_ = true;
909 parser_->AppendBytes(bytes, length);
910 }
911
CommitSameDocumentNavigation(const KURL & url,WebFrameLoadType frame_load_type,HistoryItem * history_item,ClientRedirectPolicy client_redirect_policy,LocalDOMWindow * origin_window,bool has_event,std::unique_ptr<WebDocumentLoader::ExtraData> extra_data)912 mojom::CommitResult DocumentLoader::CommitSameDocumentNavigation(
913 const KURL& url,
914 WebFrameLoadType frame_load_type,
915 HistoryItem* history_item,
916 ClientRedirectPolicy client_redirect_policy,
917 LocalDOMWindow* origin_window,
918 bool has_event,
919 std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) {
920 DCHECK(!IsReloadLoadType(frame_load_type));
921 DCHECK(frame_->GetDocument());
922
923 if (Page* page = frame_->GetPage())
924 page->HistoryNavigationVirtualTimePauser().UnpauseVirtualTime();
925
926 if (!frame_->IsNavigationAllowed())
927 return mojom::CommitResult::Aborted;
928
929 if (!IsBackForwardLoadType(frame_load_type)) {
930 // In the case of non-history navigations, check that this is a
931 // same-document navigation. If not, the navigation should restart as a
932 // cross-document navigation.
933 if (!url.HasFragmentIdentifier() ||
934 !EqualIgnoringFragmentIdentifier(frame_->GetDocument()->Url(), url) ||
935 frame_->GetDocument()->IsFrameSet()) {
936 return mojom::CommitResult::RestartCrossDocument;
937 }
938 }
939
940 // If the requesting document is cross-origin, perform the navigation
941 // asynchronously to minimize the navigator's ability to execute timing
942 // attacks.
943 if (origin_window && !origin_window->GetSecurityOrigin()->CanAccess(
944 frame_->DomWindow()->GetSecurityOrigin())) {
945 frame_->GetTaskRunner(TaskType::kInternalLoading)
946 ->PostTask(
947 FROM_HERE,
948 WTF::Bind(&DocumentLoader::CommitSameDocumentNavigationInternal,
949 WrapWeakPersistent(this), url, frame_load_type,
950 WrapPersistent(history_item), client_redirect_policy,
951 !!origin_window, has_event, std::move(extra_data)));
952 } else {
953 CommitSameDocumentNavigationInternal(url, frame_load_type, history_item,
954 client_redirect_policy, origin_window,
955 has_event, std::move(extra_data));
956 }
957 return mojom::CommitResult::Ok;
958 }
959
CommitSameDocumentNavigationInternal(const KURL & url,WebFrameLoadType frame_load_type,HistoryItem * history_item,ClientRedirectPolicy client_redirect,bool is_content_initiated,bool has_event,std::unique_ptr<WebDocumentLoader::ExtraData> extra_data)960 void DocumentLoader::CommitSameDocumentNavigationInternal(
961 const KURL& url,
962 WebFrameLoadType frame_load_type,
963 HistoryItem* history_item,
964 ClientRedirectPolicy client_redirect,
965 bool is_content_initiated,
966 bool has_event,
967 std::unique_ptr<WebDocumentLoader::ExtraData> extra_data) {
968 // If this function was scheduled to run asynchronously, this DocumentLoader
969 // might have been detached before the task ran.
970 if (!frame_)
971 return;
972
973 if (!IsBackForwardLoadType(frame_load_type)) {
974 SetNavigationType(has_event ? kWebNavigationTypeLinkClicked
975 : kWebNavigationTypeOther);
976 if (history_item_ && url == history_item_->Url())
977 frame_load_type = WebFrameLoadType::kReplaceCurrentItem;
978 }
979
980 // If we have a client navigation for a different document, a fragment
981 // scroll should cancel it.
982 // Note: see fragment-change-does-not-cancel-pending-navigation, where
983 // this does not actually happen.
984 GetFrameLoader().DidFinishNavigation(
985 FrameLoader::NavigationFinishState::kSuccess);
986
987 // GetFrameLoader().DidFinishNavigation can lead to DetachFromFrame so need
988 // to check again if frame_ is null.
989 if (!frame_ || !frame_->GetPage())
990 return;
991 GetFrameLoader().SaveScrollState();
992
993 KURL old_url = frame_->GetDocument()->Url();
994 bool hash_change = EqualIgnoringFragmentIdentifier(url, old_url) &&
995 url.FragmentIdentifier() != old_url.FragmentIdentifier();
996 if (hash_change) {
997 // If we were in the autoscroll/middleClickAutoscroll mode we want to stop
998 // it before following the link to the anchor
999 frame_->GetEventHandler().StopAutoscroll();
1000 frame_->DomWindow()->EnqueueHashchangeEvent(old_url, url);
1001 }
1002 is_client_redirect_ =
1003 client_redirect == ClientRedirectPolicy::kClientRedirect;
1004 bool same_item_sequence_number =
1005 history_item_ && history_item &&
1006 history_item_->ItemSequenceNumber() == history_item->ItemSequenceNumber();
1007 if (history_item)
1008 history_item_ = history_item;
1009 if (extra_data)
1010 GetLocalFrameClient().UpdateDocumentLoader(this, std::move(extra_data));
1011 UpdateForSameDocumentNavigation(url, kSameDocumentNavigationDefault, nullptr,
1012 mojom::blink::ScrollRestorationType::kAuto,
1013 frame_load_type, is_content_initiated);
1014
1015 initial_scroll_state_.was_scrolled_by_user = false;
1016
1017 frame_->GetDocument()->CheckCompleted();
1018
1019 // If the item sequence number didn't change, there's no need to trigger
1020 // popstate, restore scroll positions, or scroll to fragments for this
1021 // same-document navigation. It's possible to get a same-document navigation
1022 // to a same ISN when a history navigation targets a frame that no longer
1023 // exists (https://crbug.com/705550).
1024 if (!same_item_sequence_number) {
1025 GetFrameLoader().DidFinishSameDocumentNavigation(url, frame_load_type,
1026 history_item);
1027 }
1028 }
1029
ProcessDataBuffer(const char * bytes,size_t length)1030 void DocumentLoader::ProcessDataBuffer(const char* bytes, size_t length) {
1031 DCHECK_GE(state_, kCommitted);
1032 if (parser_blocked_count_ || in_commit_data_) {
1033 // 1) If parser is blocked, we buffer data and process it upon resume.
1034 // 2) If this function is reentered, we defer processing of the additional
1035 // data to the top-level invocation. Reentrant calls can occur because
1036 // of web platform (mis-)features that require running a nested run loop:
1037 // - alert(), confirm(), prompt()
1038 // - Detach of plugin elements.
1039 // - Synchronous XMLHTTPRequest
1040 if (bytes)
1041 data_buffer_->Append(bytes, length);
1042 return;
1043 }
1044
1045 if (bytes)
1046 CommitData(bytes, length);
1047 // Process data received in reentrant invocations. Note that the invocations
1048 // of CommitData() may queue more data in reentrant invocations, so iterate
1049 // until it's empty.
1050 for (const auto& span : *data_buffer_)
1051 CommitData(span.data(), span.size());
1052 // All data has been consumed, so flush the buffer.
1053 data_buffer_->Clear();
1054 }
1055
StopLoading()1056 void DocumentLoader::StopLoading() {
1057 if (frame_ && GetFrameLoader().GetDocumentLoader() == this)
1058 frame_->GetDocument()->Fetcher()->StopFetching();
1059 body_loader_.reset();
1060 virtual_time_pauser_.UnpauseVirtualTime();
1061 if (!SentDidFinishLoad())
1062 LoadFailed(ResourceError::CancelledError(Url()));
1063 }
1064
SetDefersLoading(WebURLLoader::DeferType defers)1065 void DocumentLoader::SetDefersLoading(WebURLLoader::DeferType defers) {
1066 defers_loading_ = defers;
1067 if (body_loader_)
1068 body_loader_->SetDefersLoading(defers);
1069 }
1070
DetachFromFrame(bool flush_microtask_queue)1071 void DocumentLoader::DetachFromFrame(bool flush_microtask_queue) {
1072 DCHECK(frame_);
1073 StopLoading();
1074 if (flush_microtask_queue) {
1075 // Flush microtask queue so that they all run on pre-navigation context.
1076 // TODO(dcheng): This is a temporary hack that should be removed. This is
1077 // only here because it's currently not possible to drop the microtasks
1078 // queued for a Document when the Document is navigated away; instead, the
1079 // entire microtask queue needs to be flushed. Unfortunately, running the
1080 // microtasks any later results in violating internal invariants, since
1081 // Blink does not expect the DocumentLoader for a not-yet-detached Document
1082 // to be null. It is also not possible to flush microtasks any earlier,
1083 // since flushing microtasks can only be done after any other JS (which can
1084 // queue additional microtasks) has run. Once it is possible to associate
1085 // microtasks with a v8::Context, remove this hack.
1086 Microtask::PerformCheckpoint(V8PerIsolateData::MainThreadIsolate());
1087 }
1088 ScriptForbiddenScope forbid_scripts;
1089
1090 // If that load cancellation triggered another detach, leave.
1091 // (fast/frames/detach-frame-nested-no-crash.html is an example of this.)
1092 if (!frame_)
1093 return;
1094
1095 if (application_cache_host_) {
1096 application_cache_host_->Detach();
1097 application_cache_host_.Clear();
1098 }
1099 service_worker_network_provider_ = nullptr;
1100 WeakIdentifierMap<DocumentLoader>::NotifyObjectDestroyed(this);
1101 frame_ = nullptr;
1102 }
1103
UnreachableURL() const1104 const KURL& DocumentLoader::UnreachableURL() const {
1105 return unreachable_url_;
1106 }
1107
1108 const base::Optional<blink::mojom::FetchCacheMode>&
ForceFetchCacheMode() const1109 DocumentLoader::ForceFetchCacheMode() const {
1110 return force_fetch_cache_mode_;
1111 }
1112
WillLoadUrlAsEmpty(const KURL & url)1113 bool DocumentLoader::WillLoadUrlAsEmpty(const KURL& url) {
1114 if (url.IsEmpty())
1115 return true;
1116 // Usually, we load urls with about: scheme as empty.
1117 // However, about:srcdoc is only used as a marker for non-existent
1118 // url of iframes with srcdoc attribute, which have possibly non-empty
1119 // content of the srcdoc attribute used as document's html.
1120 if (url.IsAboutSrcdocURL())
1121 return false;
1122 return SchemeRegistry::ShouldLoadURLSchemeAsEmptyDocument(url.Protocol());
1123 }
1124
InitializeEmptyResponse()1125 void DocumentLoader::InitializeEmptyResponse() {
1126 response_ = ResourceResponse(url_);
1127 response_.SetMimeType("text/html");
1128 response_.SetTextEncodingName("utf-8");
1129 }
1130
StartLoading()1131 void DocumentLoader::StartLoading() {
1132 probe::LifecycleEvent(frame_, this, "init",
1133 base::TimeTicks::Now().since_origin().InSecondsF());
1134 StartLoadingInternal();
1135 params_ = nullptr;
1136 }
1137
StartLoadingInternal()1138 void DocumentLoader::StartLoadingInternal() {
1139 GetTiming().MarkNavigationStart();
1140 DCHECK_EQ(state_, kNotStarted);
1141 DCHECK(params_);
1142 state_ = kProvisional;
1143 application_cache_host_ = MakeGarbageCollected<ApplicationCacheHostForFrame>(
1144 this, GetFrame()->Client()->GetBrowserInterfaceBroker(),
1145 GetFrame()->GetTaskRunner(TaskType::kNetworking),
1146 params_->appcache_host_id);
1147
1148 if (url_.IsEmpty() && commit_reason_ != CommitReason::kInitialization)
1149 url_ = BlankURL();
1150
1151 if (loading_url_as_empty_document_) {
1152 InitializeEmptyResponse();
1153 return;
1154 }
1155
1156 body_loader_ = std::move(params_->body_loader);
1157 DCHECK(body_loader_);
1158 DCHECK(!GetTiming().NavigationStart().is_null());
1159 // The fetch has already started in the browser,
1160 // so we don't MarkFetchStart here.
1161 main_resource_identifier_ = CreateUniqueIdentifier();
1162
1163 navigation_timing_info_ = ResourceTimingInfo::Create(
1164 fetch_initiator_type_names::kDocument, GetTiming().NavigationStart(),
1165 mojom::blink::RequestContextType::IFRAME,
1166 network::mojom::RequestDestination::kIframe);
1167 navigation_timing_info_->SetInitialURL(url_);
1168 report_timing_info_to_parent_ = ShouldReportTimingInfoToParent();
1169
1170 virtual_time_pauser_ =
1171 frame_->GetFrameScheduler()->CreateWebScopedVirtualTimePauser(
1172 url_.GetString(),
1173 WebScopedVirtualTimePauser::VirtualTaskDuration::kNonInstant);
1174 virtual_time_pauser_.PauseVirtualTime();
1175
1176 if (!archive_) {
1177 application_cache_host_->WillStartLoadingMainResource(this, url_,
1178 http_method_);
1179 }
1180
1181 // Many parties are interested in resource loading, so we will notify
1182 // them through various DispatchXXX methods on FrameFetchContext.
1183
1184 GetFrameLoader().Progress().WillStartLoading(main_resource_identifier_,
1185 ResourceLoadPriority::kVeryHigh);
1186 probe::WillSendNavigationRequest(probe::ToCoreProbeSink(GetFrame()),
1187 main_resource_identifier_, this, url_,
1188 http_method_, http_body_.get());
1189
1190 for (size_t i = 0; i < params_->redirects.size(); ++i) {
1191 WebNavigationParams::RedirectInfo& redirect = params_->redirects[i];
1192 url_ = redirect.new_url;
1193 AtomicString new_http_method = redirect.new_http_method;
1194 if (http_method_ != new_http_method) {
1195 http_body_ = nullptr;
1196 http_content_type_ = g_null_atom;
1197 http_method_ = new_http_method;
1198 }
1199 if (redirect.new_referrer.IsEmpty()) {
1200 referrer_ =
1201 Referrer(Referrer::NoReferrer(), redirect.new_referrer_policy);
1202 } else {
1203 referrer_ = Referrer(redirect.new_referrer, redirect.new_referrer_policy);
1204 }
1205
1206 // TODO(dgozman): check whether clearing origin policy is intended behavior.
1207 origin_policy_ = base::nullopt;
1208 probe::WillSendNavigationRequest(probe::ToCoreProbeSink(GetFrame()),
1209 main_resource_identifier_, this, url_,
1210 http_method_, http_body_.get());
1211 ResourceResponse redirect_response =
1212 redirect.redirect_response.ToResourceResponse();
1213 navigation_timing_info_->AddRedirect(redirect_response, url_);
1214 HandleRedirect(redirect_response.CurrentRequestUrl());
1215 }
1216
1217 if (!frame_->IsMainFrame()) {
1218 // We only care about detecting embedded private subresources.
1219 //
1220 // TODO(crbug.com/1129326): Revisit this when we have a coherent story for
1221 // top-level navigations.
1222 MixedContentChecker::CheckMixedPrivatePublic(frame_, response_);
1223 }
1224
1225 ApplyClientHintsConfig(params_->enabled_client_hints);
1226 PreloadHelper::LoadLinksFromHeader(
1227 response_.HttpHeaderField(http_names::kLink),
1228 response_.CurrentRequestUrl(), *GetFrame(), nullptr,
1229 PreloadHelper::kDoNotLoadResources, PreloadHelper::kLoadAll,
1230 nullptr /* viewport_description */, nullptr /* alternate_resource_info */,
1231 nullptr /* recursive_prefetch_token */);
1232 if (!frame_->IsMainFrame() && response_.HasMajorCertificateErrors()) {
1233 MixedContentChecker::HandleCertificateError(
1234 response_, mojom::blink::RequestContextType::HYPERLINK,
1235 MixedContentChecker::DecideCheckModeForPlugin(
1236 GetFrame()->GetSettings()),
1237 GetContentSecurityNotifier());
1238 }
1239 GetFrameLoader().Progress().IncrementProgress(main_resource_identifier_,
1240 response_);
1241 probe::DidReceiveResourceResponse(probe::ToCoreProbeSink(GetFrame()),
1242 main_resource_identifier_, this, response_,
1243 nullptr /* resource */);
1244
1245 HandleResponse();
1246
1247 loading_main_document_from_mhtml_archive_ =
1248 EqualIgnoringASCIICase("multipart/related", response_.MimeType()) ||
1249 EqualIgnoringASCIICase("message/rfc822", response_.MimeType());
1250 if (loading_main_document_from_mhtml_archive_) {
1251 // The browser process should block any navigation to an MHTML archive
1252 // inside iframes. See NavigationRequest::OnResponseStarted().
1253 CHECK(frame_->IsMainFrame());
1254
1255 // To commit an mhtml archive synchronously we have to load the whole body
1256 // synchronously and parse it, and it's already loaded in a buffer usually.
1257 // This means we should not defer, and we'll finish loading synchronously
1258 // from StartLoadingBody().
1259 body_loader_->StartLoadingBody(this, false /* use_isolated_code_cache */);
1260 return;
1261 }
1262
1263 InitializePrefetchedSignedExchangeManager();
1264
1265 body_loader_->SetDefersLoading(defers_loading_);
1266 }
1267
StartLoadingResponse()1268 void DocumentLoader::StartLoadingResponse() {
1269 if (!frame_)
1270 return;
1271
1272 CHECK_GE(state_, kCommitted);
1273
1274 // Let the browser process know about all the CSP applied to the document.
1275 // The browser process is enforcing several directives. It needs to know about
1276 // 'frame-src', 'child-src', 'navigate-to', 'upgrade-insecure-request', etc.
1277 //
1278 // It is important to forward all the CSP data before loading the response
1279 // body, otherwise some loaded content might not be blocked.
1280 frame_->DomWindow()->GetContentSecurityPolicy()->ReportAccumulatedHeaders();
1281
1282 CreateParserPostCommit();
1283
1284 // The main document from an MHTML archive is not loaded from its HTTP
1285 // response, but from the main resource within the archive (in the response).
1286 if (loading_main_document_from_mhtml_archive_) {
1287 // If the `archive_` contains a main resource, load the main document from
1288 // the archive, else it will remain empty.
1289 if (ArchiveResource* resource = archive_->MainResource()) {
1290 DCHECK_EQ(archive_->LoadResult(),
1291 mojom::blink::MHTMLLoadResult::kSuccess);
1292
1293 data_buffer_ = resource->Data();
1294 ProcessDataBuffer();
1295 FinishedLoading(base::TimeTicks::Now());
1296 return;
1297 }
1298
1299 // Log attempts loading a malformed archive.
1300 DCHECK_NE(archive_->LoadResult(), mojom::blink::MHTMLLoadResult::kSuccess);
1301 frame_->Console().AddMessage(MakeGarbageCollected<ConsoleMessage>(
1302 mojom::blink::ConsoleMessageSource::kJavaScript,
1303 mojom::blink::ConsoleMessageLevel::kError,
1304 "Malformed multipart archive: " + url_.GetString()));
1305 FinishedLoading(base::TimeTicks::Now());
1306 return;
1307 }
1308
1309 // Empty documents are empty by definition. Nothing to load.
1310 if (loading_url_as_empty_document_) {
1311 FinishedLoading(base::TimeTicks::Now());
1312 return;
1313 }
1314
1315 // TODO(dgozman): why do we stop loading for media documents?
1316 // This seems like a hack.
1317 if (frame_ && frame_->GetDocument()->IsMediaDocument()) {
1318 parser_->Finish();
1319 StopLoading();
1320 return;
1321 }
1322
1323 // Committing can run unload handlers, which can detach this frame or
1324 // stop this loader.
1325 if (!frame_ || !body_loader_)
1326 return;
1327
1328 if (!url_.ProtocolIsInHTTPFamily()) {
1329 // We only support code cache for http family, and browser insists on not
1330 // event asking for code cache with other schemes.
1331 body_loader_->StartLoadingBody(this, false /* use_isolated_code_cache */);
1332 return;
1333 }
1334
1335 bool use_isolated_code_cache =
1336 RuntimeEnabledFeatures::CacheInlineScriptCodeEnabled() &&
1337 ShouldUseIsolatedCodeCache(mojom::blink::RequestContextType::HYPERLINK,
1338 response_);
1339
1340 // The |cached_metadata_handler_| is created, even when
1341 // |use_isolated_code_cache| is false to support the parts that don't
1342 // go throught the site-isolated-code-cache.
1343 auto cached_metadata_sender = CachedMetadataSender::Create(
1344 response_, blink::mojom::CodeCacheType::kJavascript, requestor_origin_);
1345 cached_metadata_handler_ =
1346 MakeGarbageCollected<SourceKeyedCachedMetadataHandler>(
1347 WTF::TextEncoding(), std::move(cached_metadata_sender));
1348
1349 body_loader_->StartLoadingBody(this, use_isolated_code_cache);
1350 }
1351
DidInstallNewDocument(Document * document)1352 void DocumentLoader::DidInstallNewDocument(Document* document) {
1353 frame_->DomWindow()->BindContentSecurityPolicy();
1354
1355 if (history_item_ && IsBackForwardLoadType(load_type_))
1356 document->SetStateForNewControls(history_item_->GetDocumentState());
1357
1358 DCHECK(document->GetFrame());
1359 // TODO(dgozman): modify frame's client hints directly once we commit
1360 // synchronously.
1361 document->GetFrame()->GetClientHintsPreferences().UpdateFrom(
1362 client_hints_preferences_);
1363
1364 const AtomicString& dns_prefetch_control =
1365 response_.HttpHeaderField(http_names::kXDNSPrefetchControl);
1366 if (!dns_prefetch_control.IsEmpty())
1367 document->ParseDNSPrefetchControlHeader(dns_prefetch_control);
1368
1369 String header_content_language =
1370 response_.HttpHeaderField(http_names::kContentLanguage);
1371 if (!header_content_language.IsEmpty()) {
1372 wtf_size_t comma_index = header_content_language.find(',');
1373 // kNotFound == -1 == don't truncate
1374 header_content_language.Truncate(comma_index);
1375 header_content_language =
1376 header_content_language.StripWhiteSpace(IsHTMLSpace<UChar>);
1377 if (!header_content_language.IsEmpty())
1378 document->SetContentLanguage(AtomicString(header_content_language));
1379 }
1380
1381 for (const auto& message : document_policy_parsing_messages_) {
1382 document->AddConsoleMessage(MakeGarbageCollected<ConsoleMessage>(
1383 mojom::blink::ConsoleMessageSource::kOther, message.level,
1384 message.content));
1385 }
1386 document_policy_parsing_messages_.clear();
1387 }
1388
WillCommitNavigation()1389 void DocumentLoader::WillCommitNavigation() {
1390 if (commit_reason_ != CommitReason::kRegular)
1391 return;
1392 probe::WillCommitLoad(frame_, this);
1393 frame_->GetIdlenessDetector()->WillCommitLoad();
1394 }
1395
DidCommitNavigation()1396 void DocumentLoader::DidCommitNavigation() {
1397 if (commit_reason_ != CommitReason::kRegular)
1398 return;
1399
1400 WebHistoryCommitType commit_type = LoadTypeToCommitType(load_type_);
1401 frame_->GetFrameScheduler()->DidCommitProvisionalLoad(
1402 commit_type == kWebHistoryInertCommit,
1403 load_type_ == WebFrameLoadType::kReload
1404 ? FrameScheduler::NavigationType::kReload
1405 : FrameScheduler::NavigationType::kOther);
1406 if (response_.CacheControlContainsNoCache()) {
1407 GetFrame()->GetFrameScheduler()->RegisterStickyFeature(
1408 SchedulingPolicy::Feature::kMainResourceHasCacheControlNoCache,
1409 {SchedulingPolicy::RecordMetricsForBackForwardCache()});
1410 }
1411 if (response_.CacheControlContainsNoStore()) {
1412 GetFrame()->GetFrameScheduler()->RegisterStickyFeature(
1413 SchedulingPolicy::Feature::kMainResourceHasCacheControlNoStore,
1414 {SchedulingPolicy::RecordMetricsForBackForwardCache()});
1415 }
1416
1417 // When a new navigation commits in the frame, subresource loading should be
1418 // resumed.
1419 frame_->ResumeSubresourceLoading();
1420
1421 Document* document = frame_->GetDocument();
1422 InteractiveDetector* interactive_detector =
1423 InteractiveDetector::From(*document);
1424 if (interactive_detector)
1425 interactive_detector->SetNavigationStartTime(GetTiming().NavigationStart());
1426
1427 TRACE_EVENT1("devtools.timeline", "CommitLoad", "data",
1428 inspector_commit_load_event::Data(frame_));
1429
1430 // Needs to run before dispatching preloads, as it may evict the memory cache.
1431 probe::DidCommitLoad(frame_, this);
1432
1433 frame_->GetPage()->DidCommitLoad(frame_);
1434
1435 // Report legacy TLS versions after Page::DidCommitLoad, because the latter
1436 // clears the console.
1437 if (response_.IsLegacyTLSVersion()) {
1438 GetFrameLoader().ReportLegacyTLSVersion(
1439 response_.CurrentRequestUrl(), false /* is_subresource */,
1440 frame_->IsAdSubframe() || frame_->IsAdRoot());
1441 }
1442 }
1443
CalculateSandboxFlags()1444 network::mojom::blink::WebSandboxFlags DocumentLoader::CalculateSandboxFlags() {
1445 auto sandbox_flags = GetFrameLoader().GetForcedSandboxFlags() |
1446 content_security_policy_->GetSandboxMask() |
1447 frame_policy_.sandbox_flags;
1448 if (archive_) {
1449 // The URL of a Document loaded from a MHTML archive is controlled by
1450 // the Content-Location header. This would allow UXSS, since
1451 // Content-Location can be arbitrarily controlled to control the
1452 // Document's URL and origin. Instead, force a Document loaded from a
1453 // MHTML archive to be sandboxed, providing exceptions only for creating
1454 // new windows.
1455 DCHECK(commit_reason_ == CommitReason::kRegular ||
1456 commit_reason_ == CommitReason::kInitialization);
1457 sandbox_flags |= (network::mojom::blink::WebSandboxFlags::kAll &
1458 ~(network::mojom::blink::WebSandboxFlags::kPopups |
1459 network::mojom::blink::WebSandboxFlags::
1460 kPropagatesToAuxiliaryBrowsingContexts));
1461 } else if (commit_reason_ == CommitReason::kXSLT) {
1462 // An XSLT document inherits sandbox flags from the document that create it.
1463 sandbox_flags |= frame_->DomWindow()->GetSandboxFlags();
1464 }
1465 return sandbox_flags;
1466 }
1467
CalculateOrigin(Document * owner_document,network::mojom::blink::WebSandboxFlags sandbox_flags)1468 scoped_refptr<SecurityOrigin> DocumentLoader::CalculateOrigin(
1469 Document* owner_document,
1470 network::mojom::blink::WebSandboxFlags sandbox_flags) {
1471 scoped_refptr<SecurityOrigin> origin;
1472 if (origin_to_commit_) {
1473 // Origin to commit is specified by the browser process, it must be taken
1474 // and used directly. It is currently supplied only for session history
1475 // navigations, where the origin was already calcuated previously and
1476 // stored on the session history entry.
1477 origin = origin_to_commit_;
1478 } else if (IsPagePopupRunningInWebTest(frame_)) {
1479 // If we are a page popup in LayoutTests ensure we use the popup
1480 // owner's security origin so the tests can possibly access the
1481 // document via internals API.
1482 auto* owner_context = frame_->PagePopupOwner()->GetExecutionContext();
1483 origin = owner_context->GetSecurityOrigin()->IsolatedCopy();
1484 } else if (owner_document && owner_document->domWindow()) {
1485 origin = owner_document->domWindow()->GetMutableSecurityOrigin();
1486 } else {
1487 // Otherwise, create an origin that propagates precursor information
1488 // as needed. For non-opaque origins, this creates a standard tuple
1489 // origin, but for opaque origins, it creates an origin with the
1490 // initiator origin as the precursor.
1491 origin = SecurityOrigin::CreateWithReferenceOrigin(url_,
1492 requestor_origin_.get());
1493 }
1494
1495 if ((sandbox_flags & network::mojom::blink::WebSandboxFlags::kOrigin) !=
1496 network::mojom::blink::WebSandboxFlags::kNone) {
1497 auto sandbox_origin = origin->DeriveNewOpaqueOrigin();
1498
1499 // If we're supposed to inherit our security origin from our
1500 // owner, but we're also sandboxed, the only things we inherit are
1501 // the origin's potential trustworthiness and the ability to
1502 // load local resources. The latter lets about:blank iframes in
1503 // file:// URL documents load images and other resources from
1504 // the file system.
1505 //
1506 // Note: Sandboxed about:srcdoc iframe without "allow-same-origin" aren't
1507 // allowed to load user's file, even if its parent can.
1508 if (owner_document) {
1509 if (origin->IsPotentiallyTrustworthy())
1510 sandbox_origin->SetOpaqueOriginIsPotentiallyTrustworthy(true);
1511 if (origin->CanLoadLocalResources() && !loading_srcdoc_)
1512 sandbox_origin->GrantLoadLocalResources();
1513 }
1514 origin = sandbox_origin;
1515 }
1516
1517 if (!frame_->GetSettings()->GetWebSecurityEnabled()) {
1518 // Web security is turned off. We should let this document access
1519 // every other document. This is used primary by testing harnesses for
1520 // web sites.
1521 origin->GrantUniversalAccess();
1522 } else if (origin->IsLocal()) {
1523 if (frame_->GetSettings()->GetAllowUniversalAccessFromFileURLs()) {
1524 // Some clients want local URLs to have universal access, but that
1525 // setting is dangerous for other clients.
1526 origin->GrantUniversalAccess();
1527 } else if (!frame_->GetSettings()->GetAllowFileAccessFromFileURLs()) {
1528 // Some clients do not want local URLs to have access to other local
1529 // URLs.
1530 origin->BlockLocalAccessFromLocalOrigin();
1531 }
1532 }
1533
1534 if (grant_load_local_resources_)
1535 origin->GrantLoadLocalResources();
1536
1537 if (origin->IsOpaque()) {
1538 KURL url = url_.IsEmpty() ? BlankURL() : url_;
1539 if (SecurityOrigin::Create(url)->IsPotentiallyTrustworthy())
1540 origin->SetOpaqueOriginIsPotentiallyTrustworthy(true);
1541 }
1542 return origin;
1543 }
1544
ShouldReuseDOMWindow(LocalDOMWindow * window,SecurityOrigin * security_origin)1545 bool ShouldReuseDOMWindow(LocalDOMWindow* window,
1546 SecurityOrigin* security_origin) {
1547 // Secure transitions can only happen when navigating from the initial empty
1548 // document.
1549 return window && window->document()->IsInitialEmptyDocument() &&
1550 window->GetSecurityOrigin()->CanAccess(security_origin);
1551 }
1552
GetWindowAgentForOrigin(LocalFrame * frame,SecurityOrigin * origin)1553 WindowAgent* GetWindowAgentForOrigin(LocalFrame* frame,
1554 SecurityOrigin* origin) {
1555 // TODO(keishi): Also check if AllowUniversalAccessFromFileURLs might
1556 // dynamically change.
1557 bool has_potential_universal_access_privilege =
1558 !frame->GetSettings()->GetWebSecurityEnabled() ||
1559 frame->GetSettings()->GetAllowUniversalAccessFromFileURLs();
1560 return frame->window_agent_factory().GetAgentForOrigin(
1561 has_potential_universal_access_privilege,
1562 V8PerIsolateData::MainThreadIsolate(), origin);
1563 }
1564
InitializeWindow(Document * owner_document)1565 void DocumentLoader::InitializeWindow(Document* owner_document) {
1566 auto sandbox_flags = CalculateSandboxFlags();
1567 auto security_origin = CalculateOrigin(owner_document, sandbox_flags);
1568
1569 // In some rare cases, we'll re-use a LocalDOMWindow for a new Document. For
1570 // example, when a script calls window.open("..."), the browser gives
1571 // JavaScript a window synchronously but kicks off the load in the window
1572 // asynchronously. Web sites expect that modifications that they make to the
1573 // window object synchronously won't be blown away when the network load
1574 // commits. To make that happen, we "securely transition" the existing
1575 // LocalDOMWindow to the Document that results from the network load. See also
1576 // Document::IsSecureTransitionTo.
1577 if (!ShouldReuseDOMWindow(frame_->DomWindow(), security_origin.get())) {
1578 auto* agent = GetWindowAgentForOrigin(frame_.Get(), security_origin.get());
1579 frame_->SetDOMWindow(MakeGarbageCollected<LocalDOMWindow>(*frame_, agent));
1580
1581 if (origin_policy_.has_value()) {
1582 // Convert from WebVector<WebString> to WTF::Vector<WTF::String>
1583 Vector<String> ids;
1584 for (const auto& id : origin_policy_->ids) {
1585 ids.push_back(id);
1586 }
1587
1588 frame_->DomWindow()->SetOriginPolicyIds(ids);
1589 }
1590
1591 // Inheriting cases use their agent's origin-isolated value, which is set by
1592 // whatever they're inheriting from.
1593 //
1594 // javascript: URLs use the calling page as their Url() value, so we need to
1595 // exclude them explicitly.
1596 //
1597 // TODO(https://crbug.com/1111897): This call is likely to happen happen
1598 // multiple times per agent, since navigations can happen multiple times per
1599 // agent. This is subpar. Currently a DCHECK guards against it happening
1600 // multiple times *with different values*, but ideally we would use a better
1601 // architecture.
1602 if (!Document::ShouldInheritSecurityOriginFromOwner(Url()) &&
1603 commit_reason_ != CommitReason::kJavascriptUrl) {
1604 agent->SetIsOriginIsolated(origin_isolated_);
1605 }
1606 } else {
1607 if (frame_->GetSettings()->GetShouldReuseGlobalForUnownedMainFrame() &&
1608 frame_->IsMainFrame()) {
1609 // When GetShouldReuseGlobalForUnownedMainFrame() causes a main frame's
1610 // window to be reused, we should not inherit the initial empty document's
1611 // Agent, which was a universal access Agent.
1612 // This happens only in android webview.
1613 frame_->DomWindow()->ResetWindowAgent(
1614 GetWindowAgentForOrigin(frame_.Get(), security_origin.get()));
1615 }
1616 frame_->DomWindow()->ClearForReuse();
1617 }
1618
1619 // Now that we have the final window and Agent, ensure the security origin has
1620 // the appropriate agent cluster id. This may derive a new security origin.
1621 security_origin = security_origin->GetOriginForAgentCluster(
1622 frame_->DomWindow()->GetAgent()->cluster_id());
1623
1624 SecurityContext& security_context = frame_->DomWindow()->GetSecurityContext();
1625 security_context.SetContentSecurityPolicy(content_security_policy_.Get());
1626 security_context.ApplySandboxFlags(sandbox_flags);
1627 // Conceptually, SecurityOrigin doesn't have to be initialized after sandbox
1628 // flags are applied, but there's a UseCounter in SetSecurityOrigin() that
1629 // wants to inspect sandbox flags.
1630 security_context.SetSecurityOrigin(std::move(security_origin));
1631 // Requires SecurityOrigin to be initialized.
1632 OriginTrialContext::AddTokensFromHeader(
1633 frame_->DomWindow(), response_.HttpHeaderField(http_names::kOriginTrial));
1634
1635 if (auto* parent = frame_->Tree().Parent()) {
1636 const SecurityContext* parent_context = parent->GetSecurityContext();
1637 security_context.SetInsecureRequestPolicy(
1638 parent_context->GetInsecureRequestPolicy());
1639 for (auto to_upgrade : parent_context->InsecureNavigationsToUpgrade())
1640 security_context.AddInsecureNavigationUpgrade(to_upgrade);
1641 }
1642 frame_->DomWindow()->SetAddressSpace(ip_address_space_);
1643
1644 if (base::FeatureList::IsEnabled(blink::features::kPolicyContainer)) {
1645 // SVG image documents go throught this but don't have a PolicyContainer, so
1646 // ignore them.
1647 if (frame_->GetPolicyContainer()) {
1648 frame_->DomWindow()->SetReferrerPolicy(
1649 frame_->GetPolicyContainer()->GetReferrerPolicy(), false);
1650 }
1651 }
1652 String referrer_policy_header =
1653 response_.HttpHeaderField(http_names::kReferrerPolicy);
1654 if (!referrer_policy_header.IsNull()) {
1655 CountUse(WebFeature::kReferrerPolicyHeader);
1656 frame_->DomWindow()->ParseAndSetReferrerPolicy(referrer_policy_header);
1657 if (base::FeatureList::IsEnabled(blink::features::kPolicyContainer)) {
1658 if (frame_->GetPolicyContainer()) {
1659 frame_->GetPolicyContainer()->UpdateReferrerPolicy(
1660 frame_->DomWindow()->GetReferrerPolicy());
1661 }
1662 }
1663 }
1664 }
1665
CommitNavigation()1666 void DocumentLoader::CommitNavigation() {
1667 DCHECK_LT(state_, kCommitted);
1668 DCHECK(frame_->GetPage());
1669 DCHECK(!frame_->GetDocument() || !frame_->GetDocument()->IsActive());
1670 DCHECK_EQ(frame_->Tree().ChildCount(), 0u);
1671 state_ = kCommitted;
1672
1673 // Prepare a DocumentInit before clearing the frame, because it may need to
1674 // inherit an aliased security context.
1675 Document* owner_document = nullptr;
1676
1677 // TODO(dcheng): This differs from the behavior of both IE and Firefox: the
1678 // origin is inherited from the document that loaded the URL.
1679 if (Document::ShouldInheritSecurityOriginFromOwner(Url())) {
1680 Frame* owner_frame = frame_->Tree().Parent();
1681 if (!owner_frame)
1682 owner_frame = frame_->Loader().Opener();
1683 if (auto* owner_local_frame = DynamicTo<LocalFrame>(owner_frame))
1684 owner_document = owner_local_frame->GetDocument();
1685 }
1686
1687 // Re-validate Document Policy feature before installing the new document.
1688 if (!RuntimeEnabledFeatures::DocumentPolicyEnabled(
1689 owner_document ? owner_document->GetExecutionContext() : nullptr)) {
1690 document_policy_ = DocumentPolicy::ParsedDocumentPolicy{};
1691 }
1692
1693 if (document_policy_.feature_state.contains(
1694 mojom::blink::DocumentPolicyFeature::kForceLoadAtTop)) {
1695 navigation_scroll_allowed_ = !(
1696 document_policy_
1697 .feature_state[mojom::blink::DocumentPolicyFeature::kForceLoadAtTop]
1698 .BoolValue());
1699 }
1700
1701 LocalDOMWindow* previous_window = frame_->DomWindow();
1702 InitializeWindow(owner_document);
1703
1704 SecurityContextInit security_init(frame_->DomWindow());
1705 // FeaturePolicy and DocumentPolicy require SecurityOrigin and origin trials
1706 // to be initialized.
1707 // TODO(iclelland): Add Feature-Policy-Report-Only to Origin Policy.
1708 security_init.ApplyFeaturePolicy(frame_.Get(), response_, origin_policy_,
1709 frame_policy_);
1710 // |document_policy_| is parsed in document loader because it is
1711 // compared with |frame_policy.required_document_policy| to decide
1712 // whether to block the document load or not.
1713 // |report_only_document_policy| does not block the page load. Its
1714 // initialization is delayed to
1715 // SecurityContextInit::InitializeDocumentPolicy(), similar to
1716 // |report_only_feature_policy|.
1717 security_init.ApplyDocumentPolicy(
1718 document_policy_,
1719 response_.HttpHeaderField(http_names::kDocumentPolicyReportOnly));
1720
1721 WillCommitNavigation();
1722
1723 Document* document = frame_->DomWindow()->InstallNewDocument(
1724 DocumentInit::Create()
1725 .WithWindow(frame_->DomWindow(), owner_document)
1726 .ForInitialEmptyDocument(commit_reason_ ==
1727 CommitReason::kInitialization)
1728 .WithURL(Url())
1729 .WithTypeFrom(MimeType())
1730 .WithSrcdocDocument(loading_srcdoc_)
1731 .WithNewRegistrationContext()
1732 .WithWebBundleClaimedUrl(web_bundle_claimed_url_)
1733 .WithUkmSourceId(ukm_source_id_));
1734
1735 RecordUseCountersForCommit();
1736 RecordConsoleMessagesForCommit();
1737
1738 // Clear the user activation state.
1739 // TODO(crbug.com/736415): Clear this bit unconditionally for all frames.
1740 if (frame_->IsMainFrame())
1741 frame_->ClearUserActivation();
1742
1743 // The DocumentLoader was flagged as activated if it needs to notify the frame
1744 // that it was activated before navigation. Update the frame state based on
1745 // the new value.
1746 if (frame_->HadStickyUserActivationBeforeNavigation() !=
1747 had_sticky_activation_) {
1748 frame_->SetHadStickyUserActivationBeforeNavigation(had_sticky_activation_);
1749 frame_->GetLocalFrameHostRemote()
1750 .HadStickyUserActivationBeforeNavigationChanged(had_sticky_activation_);
1751 }
1752
1753 bool should_clear_window_name =
1754 previous_window && frame_->IsMainFrame() && !frame_->Loader().Opener() &&
1755 !frame_->DomWindow()->GetSecurityOrigin()->IsSameOriginWith(
1756 previous_window->GetSecurityOrigin());
1757 if (should_clear_window_name) {
1758 // TODO(andypaicu): experimentalSetNullName will just record the fact
1759 // that the name would be nulled and if the name is accessed after we will
1760 // fire a UseCounter. If we decide to move forward with this change, we'd
1761 // actually clean the name here.
1762 // frame_->tree().setName(g_null_atom);
1763 frame_->Tree().ExperimentalSetNulledName();
1764 }
1765
1766 bool should_clear_cross_browsing_context_group_window_name =
1767 previous_window && frame_->IsMainFrame() && !frame_->Loader().Opener() &&
1768 is_cross_browsing_context_group_navigation_;
1769 if (should_clear_cross_browsing_context_group_window_name) {
1770 // TODO(shuuran): CrossBrowsingContextGroupSetNulledName will just
1771 // record the fact that the name would be nulled and if the name is accessed
1772 // after we will fire a UseCounter. If we decide to move forward with
1773 // this change, we'd actually clean the name here.
1774 // frame_->tree().setName(g_null_atom);
1775 frame_->Tree().CrossBrowsingContextGroupSetNulledName();
1776 }
1777
1778 // MHTML archive's URL is usually a local file. However the main resource
1779 // within the archive has a public URL and must be used to resolve all the
1780 // relative links.
1781 if (loading_main_document_from_mhtml_archive_) {
1782 ArchiveResource* main_resource = archive_->MainResource();
1783 KURL main_resource_url = main_resource ? main_resource->Url() : KURL();
1784 if (!main_resource_url.IsEmpty())
1785 document->SetBaseURLOverride(main_resource_url);
1786 }
1787
1788 if (commit_reason_ == CommitReason::kXSLT)
1789 DocumentXSLT::SetHasTransformSource(*document);
1790
1791 DidInstallNewDocument(document);
1792
1793 // This must be called before the document is opened, otherwise HTML parser
1794 // will use stale values from HTMLParserOption.
1795 DidCommitNavigation();
1796
1797 if (requestor_origin_) {
1798 const scoped_refptr<const SecurityOrigin> url_origin =
1799 SecurityOrigin::Create(Url());
1800
1801 is_same_origin_navigation_ =
1802 requestor_origin_->IsSameOriginWith(url_origin.get()) &&
1803 Url().ProtocolIsInHTTPFamily();
1804 }
1805
1806 // The PaintHolding feature defers compositor commits until content has
1807 // been painted or 500ms have passed, whichever comes first. The additional
1808 // PaintHoldingCrossOrigin feature allows PaintHolding even for cross-origin
1809 // navigations, otherwise only same-origin navigations have deferred commits.
1810 // We also require that this be an html document served via http.
1811 if (base::FeatureList::IsEnabled(blink::features::kPaintHolding) &&
1812 IsA<HTMLDocument>(document) && Url().ProtocolIsInHTTPFamily() &&
1813 (is_same_origin_navigation_ ||
1814 base::FeatureList::IsEnabled(
1815 blink::features::kPaintHoldingCrossOrigin))) {
1816 document->SetDeferredCompositorCommitIsAllowed(true);
1817 } else {
1818 document->SetDeferredCompositorCommitIsAllowed(false);
1819 }
1820
1821 if (response_.IsHTTP() && navigation_timing_info_) {
1822 // The response is being copied here to pass the ServerTiming info.
1823 // TODO(yoav): copy the ServerTiming info directly.
1824 navigation_timing_info_->SetFinalResponse(response_);
1825 }
1826
1827 {
1828 // Notify the browser process about the commit.
1829 FrameNavigationDisabler navigation_disabler(*frame_);
1830 if (commit_reason_ == CommitReason::kInitialization) {
1831 GetLocalFrameClient().DidCreateInitialEmptyDocument();
1832 } else if (IsJavaScriptURLOrXSLTCommit()) {
1833 GetLocalFrameClient().DidCommitDocumentReplacementNavigation(this);
1834 } else {
1835 GetLocalFrameClient().DispatchDidCommitLoad(
1836 history_item_.Get(), LoadTypeToCommitType(load_type_),
1837 previous_window != frame_->DomWindow());
1838 }
1839 // TODO(dgozman): make DidCreateScriptContext notification call currently
1840 // triggered by installing new document happen here, after commit.
1841 }
1842 // Note: this must be called after DispatchDidCommitLoad() for
1843 // metrics to be correctly sent to the browser process.
1844 if (commit_reason_ != CommitReason::kInitialization)
1845 use_counter_.DidCommitLoad(frame_);
1846 if (load_type_ == WebFrameLoadType::kBackForward) {
1847 if (Page* page = frame_->GetPage())
1848 page->HistoryNavigationVirtualTimePauser().UnpauseVirtualTime();
1849 }
1850
1851 // FeaturePolicy is reset in the browser process on commit, so this needs to
1852 // be initialized and replicated to the browser process after commit messages
1853 // are sent.
1854 GetLocalFrameClient().DidSetFramePolicyHeaders(
1855 frame_->DomWindow()->GetSandboxFlags(),
1856 security_init.FeaturePolicyHeader(), document_policy_.feature_state);
1857
1858 // Load the document if needed.
1859 StartLoadingResponse();
1860 }
1861
CreateParserPostCommit()1862 void DocumentLoader::CreateParserPostCommit() {
1863 // DidObserveLoadingBehavior() must be called after DispatchDidCommitLoad() is
1864 // called for the metrics tracking logic to handle it properly.
1865 if (service_worker_network_provider_ &&
1866 service_worker_network_provider_->GetControllerServiceWorkerMode() ==
1867 blink::mojom::ControllerServiceWorkerMode::kControlled) {
1868 GetLocalFrameClient().DidObserveLoadingBehavior(
1869 kLoadingBehaviorServiceWorkerControlled);
1870 }
1871
1872 // Links with media values need more information (like viewport information).
1873 // This happens after the first chunk is parsed in HTMLDocumentParser.
1874 DispatchLinkHeaderPreloads(nullptr /* viewport */,
1875 PreloadHelper::kOnlyLoadNonMedia);
1876
1877 // Initializing origin trials might force window proxy initialization,
1878 // which later triggers CHECK when swapping in via WebFrame::Swap().
1879 // We can safely omit installing original trials on initial empty document
1880 // and wait for the real load.
1881 if (commit_reason_ != CommitReason::kInitialization) {
1882 LocalDOMWindow* window = frame_->DomWindow();
1883 if (frame_->GetSettings()
1884 ->GetForceTouchEventFeatureDetectionForInspector()) {
1885 window->GetOriginTrialContext()->AddFeature(
1886 OriginTrialFeature::kTouchEventFeatureDetection);
1887 }
1888
1889 // Enable any origin trials that have been force enabled for this commit.
1890 window->GetOriginTrialContext()->AddForceEnabledTrials(
1891 force_enabled_origin_trials_);
1892
1893 #if BUILDFLAG(IS_ASH)
1894 // Enable Auto Picture-in-Picture feature for the built-in Chrome OS Video
1895 // Player app.
1896 const url::Origin origin = window->GetSecurityOrigin()->ToUrlOrigin();
1897 if (origin.scheme() == "chrome-extension" &&
1898 origin.DomainIs("jcgeabjmjgoblfofpppfkcoakmfobdko") &&
1899 origin.port() == 0) {
1900 window->GetOriginTrialContext()->AddFeature(
1901 OriginTrialFeature::kAutoPictureInPicture);
1902 }
1903 #endif
1904
1905 OriginTrialContext::ActivateNavigationFeaturesFromInitiator(
1906 window, &initiator_origin_trial_features_);
1907 }
1908
1909 ParserSynchronizationPolicy parsing_policy =
1910 RuntimeEnabledFeatures::ForceSynchronousHTMLParsingEnabled()
1911 ? kAllowDeferredParsing
1912 : kAllowAsynchronousParsing;
1913 if (IsJavaScriptURLOrXSLTCommit() ||
1914 !Document::ThreadedParsingEnabledForTesting()) {
1915 parsing_policy = kForceSynchronousParsing;
1916 }
1917 const AtomicString& encoding = commit_reason_ == CommitReason::kXSLT
1918 ? "UTF-8"
1919 : response_.TextEncodingName();
1920
1921 Document* document = frame_->GetDocument();
1922 parser_ = document->OpenForNavigation(parsing_policy, MimeType(), encoding);
1923
1924 // XSLT processing converts the response into UTF-8 before sending it through
1925 // the DocumentParser, but we should still report the original encoding when
1926 // script queries it via document.characterSet.
1927 if (commit_reason_ == CommitReason::kXSLT) {
1928 DocumentEncodingData data;
1929 data.SetEncoding(WTF::TextEncoding(response_.TextEncodingName()));
1930 document->SetEncodingData(data);
1931 }
1932
1933 // If this is a scriptable parser and there is a resource, register the
1934 // resource's cache handler with the parser.
1935 ScriptableDocumentParser* scriptable_parser =
1936 parser_->AsScriptableDocumentParser();
1937 if (scriptable_parser && cached_metadata_handler_)
1938 scriptable_parser->SetInlineScriptCacheHandler(cached_metadata_handler_);
1939
1940 GetFrameLoader().DispatchDidClearDocumentOfWindowObject();
1941
1942 parser_->SetDocumentWasLoadedAsPartOfNavigation();
1943 if (was_discarded_)
1944 document->SetWasDiscarded(true);
1945 document->MaybeHandleHttpRefresh(
1946 response_.HttpHeaderField(http_names::kRefresh),
1947 Document::kHttpRefreshFromHeader);
1948 ReportPreviewsIntervention();
1949 }
1950
MimeType() const1951 const AtomicString& DocumentLoader::MimeType() const {
1952 // In the case of mhtml archive, |response_| has an archive mime type,
1953 // while the document has a different mime type.
1954 if (loading_main_document_from_mhtml_archive_) {
1955 if (ArchiveResource* main_resource = archive_->MainResource())
1956 return main_resource->MimeType();
1957 }
1958
1959 return response_.MimeType();
1960 }
1961
BlockParser()1962 void DocumentLoader::BlockParser() {
1963 parser_blocked_count_++;
1964 }
1965
ResumeParser()1966 void DocumentLoader::ResumeParser() {
1967 parser_blocked_count_--;
1968 DCHECK_GE(parser_blocked_count_, 0);
1969
1970 if (parser_blocked_count_ != 0)
1971 return;
1972
1973 ProcessDataBuffer();
1974
1975 if (finish_loading_when_parser_resumed_) {
1976 finish_loading_when_parser_resumed_ = false;
1977 parser_->Finish();
1978 parser_.Clear();
1979 }
1980 }
1981
CountUse(mojom::WebFeature feature)1982 void DocumentLoader::CountUse(mojom::WebFeature feature) {
1983 return use_counter_.Count(feature, GetFrame());
1984 }
1985
RecordUseCountersForCommit()1986 void DocumentLoader::RecordUseCountersForCommit() {
1987 // Pre-commit state, count usage the use counter associated with "this"
1988 // (provisional document loader) instead of frame_'s document loader.
1989 if (response_.DidServiceWorkerNavigationPreload())
1990 CountUse(WebFeature::kServiceWorkerNavigationPreload);
1991 if (!frame_->IsMainFrame() && response_.GetCTPolicyCompliance() ==
1992 ResourceResponse::kCTPolicyDoesNotComply) {
1993 // Exclude main-frame navigations; those are tracked elsewhere.
1994 CountUse(
1995 WebFeature::kCertificateTransparencyNonCompliantResourceInSubframe);
1996 }
1997 if (RuntimeEnabledFeatures::ForceLoadAtTopEnabled(frame_->DomWindow()))
1998 CountUse(WebFeature::kForceLoadAtTop);
1999
2000 if (response_.IsSignedExchangeInnerResponse()) {
2001 CountUse(WebFeature::kSignedExchangeInnerResponse);
2002 CountUse(frame_->IsMainFrame()
2003 ? WebFeature::kSignedExchangeInnerResponseInMainFrame
2004 : WebFeature::kSignedExchangeInnerResponseInSubFrame);
2005 }
2006
2007 if (!response_.HttpHeaderField(http_names::kRequireDocumentPolicy).IsNull())
2008 CountUse(WebFeature::kRequireDocumentPolicyHeader);
2009
2010 if (was_blocked_by_document_policy_)
2011 CountUse(WebFeature::kDocumentPolicyCausedPageUnload);
2012
2013 // Required document policy can either come from iframe attribute or HTTP
2014 // header 'Require-Document-Policy'.
2015 if (!frame_policy_.required_document_policy.empty())
2016 CountUse(WebFeature::kRequiredDocumentPolicy);
2017 }
2018
RecordConsoleMessagesForCommit()2019 void DocumentLoader::RecordConsoleMessagesForCommit() {
2020 // Log if the document was blocked by CSP checks now that the new Document has
2021 // been created and console messages will be properly displayed.
2022 if (was_blocked_by_csp_) {
2023 ConsoleError("Refused to display '" +
2024 response_.CurrentRequestUrl().ElidedString() +
2025 "' because it has not opted into the following policy "
2026 "required by its embedder: '" +
2027 GetFrameLoader().RequiredCSP() + "'.");
2028 }
2029
2030 if (was_blocked_by_document_policy_) {
2031 // TODO(chenleihu): Add which document policy violated in error string,
2032 // instead of just displaying serialized required document policy.
2033 ConsoleError(
2034 "Refused to display '" + response_.CurrentRequestUrl().ElidedString() +
2035 "' because it violates the following document policy "
2036 "required by its embedder: '" +
2037 DocumentPolicy::Serialize(frame_policy_.required_document_policy)
2038 .value_or("[Serialization Error]")
2039 .c_str() +
2040 "'.");
2041 }
2042
2043 // Report the ResourceResponse now that the new Document has been created and
2044 // console messages will be properly displayed.
2045 frame_->Console().ReportResourceResponseReceived(
2046 this, main_resource_identifier_, response_);
2047 }
2048
ReportPreviewsIntervention() const2049 void DocumentLoader::ReportPreviewsIntervention() const {
2050 // Only send reports for main frames.
2051 if (!frame_->IsMainFrame())
2052 return;
2053
2054 // Verify that certain types are not on main frame requests.
2055 DCHECK_NE(PreviewsTypes::kClientLoFiAutoReload, previews_state_);
2056 DCHECK_NE(PreviewsTypes::kSubresourceRedirectOn, previews_state_);
2057
2058 static_assert(PreviewsTypes::kPreviewsStateLast ==
2059 PreviewsTypes::kSubresourceRedirectOn,
2060 "If a new Preview type is added, verify that the Intervention "
2061 "Report should be sent (or not sent) for that type.");
2062
2063 // If the preview type is not unspecified, off, or no transform, it is a
2064 // preview that needs to be reported.
2065 if (previews_state_ == PreviewsTypes::kPreviewsUnspecified ||
2066 previews_state_ & PreviewsTypes::kPreviewsOff ||
2067 previews_state_ & PreviewsTypes::kPreviewsNoTransform) {
2068 return;
2069 }
2070
2071 Intervention::GenerateReport(
2072 frame_, "LitePageServed",
2073 "Modified page load behavior on the page because the page was expected "
2074 "to take a long amount of time to load. "
2075 "https://www.chromestatus.com/feature/5148050062311424");
2076 }
2077
ApplyClientHintsConfig(const WebVector<network::mojom::WebClientHintsType> & enabled_client_hints)2078 void DocumentLoader::ApplyClientHintsConfig(
2079 const WebVector<network::mojom::WebClientHintsType>& enabled_client_hints) {
2080 for (auto ch : enabled_client_hints)
2081 client_hints_preferences_.SetShouldSend(ch);
2082 }
2083
InitializePrefetchedSignedExchangeManager()2084 void DocumentLoader::InitializePrefetchedSignedExchangeManager() {
2085 if (params_->prefetched_signed_exchanges.empty())
2086 return;
2087 // |prefetched_signed_exchanges| is set only when the page is loaded from a
2088 // signed exchange.
2089 DCHECK(GetResponse().IsSignedExchangeInnerResponse());
2090 // When the page is loaded from a signed exchange, |last_redirect| must be the
2091 // synthesized redirect for the signed exchange.
2092 DCHECK(params_->redirects.size());
2093 const WebNavigationParams::RedirectInfo& last_redirect =
2094 params_->redirects[params_->redirects.size() - 1];
2095 prefetched_signed_exchange_manager_ =
2096 PrefetchedSignedExchangeManager::MaybeCreate(
2097 GetFrame(),
2098 last_redirect.redirect_response.HttpHeaderField(http_names::kLink),
2099 GetResponse().HttpHeaderField(http_names::kLink),
2100 std::move(params_->prefetched_signed_exchanges));
2101 }
2102
2103 PrefetchedSignedExchangeManager*
GetPrefetchedSignedExchangeManager() const2104 DocumentLoader::GetPrefetchedSignedExchangeManager() const {
2105 return prefetched_signed_exchange_manager_;
2106 }
2107
RemainingTimeToLCPLimit() const2108 base::TimeDelta DocumentLoader::RemainingTimeToLCPLimit() const {
2109 // We shouldn't call this function before navigation start
2110 DCHECK(!document_load_timing_.NavigationStart().is_null());
2111 base::TimeTicks lcp_limit =
2112 document_load_timing_.NavigationStart() +
2113 base::TimeDelta::FromMilliseconds(
2114 features::kAlignFontDisplayAutoTimeoutWithLCPGoalTimeoutParam.Get());
2115 base::TimeTicks now = clock_->NowTicks();
2116 if (now < lcp_limit)
2117 return lcp_limit - now;
2118 return base::TimeDelta();
2119 }
2120
2121 mojom::blink::ContentSecurityNotifier&
GetContentSecurityNotifier()2122 DocumentLoader::GetContentSecurityNotifier() {
2123 if (!content_security_notifier_.is_bound()) {
2124 GetFrame()->Client()->GetBrowserInterfaceBroker().GetInterface(
2125 content_security_notifier_.BindNewPipeAndPassReceiver());
2126 }
2127 return *content_security_notifier_;
2128 }
2129
ConsumeTextFragmentToken()2130 bool DocumentLoader::ConsumeTextFragmentToken() {
2131 bool token_value = has_text_fragment_token_;
2132 has_text_fragment_token_ = false;
2133 return token_value;
2134 }
2135
2136 DEFINE_WEAK_IDENTIFIER_MAP(DocumentLoader)
2137
2138 } // namespace blink
2139