1 /*
2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights
3 * reserved.
4 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
5 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved.
6 * (http://www.torchmobile.com/)
7 * Copyright (C) 2008 Alp Toker <alp@atoker.com>
8 * Copyright (C) Research In Motion Limited 2009. All rights reserved.
9 * Copyright (C) 2011 Kris Jordan <krisjordan@gmail.com>
10 * Copyright (C) 2011 Google Inc. All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
22 * its contributors may be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
26 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
29 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
32 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 #include "third_party/blink/renderer/core/loader/frame_loader.h"
38
39 #include <memory>
40 #include <utility>
41
42 #include "base/auto_reset.h"
43 #include "base/unguessable_token.h"
44 #include "build/build_config.h"
45 #include "mojo/public/cpp/bindings/pending_remote.h"
46 #include "services/metrics/public/cpp/ukm_builders.h"
47 #include "services/metrics/public/cpp/ukm_recorder.h"
48 #include "services/network/public/cpp/features.h"
49 #include "services/network/public/cpp/web_sandbox_flags.h"
50 #include "services/network/public/mojom/web_sandbox_flags.mojom-blink.h"
51 #include "third_party/blink/public/common/features.h"
52 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
53 #include "third_party/blink/public/mojom/frame/navigation_initiator.mojom-blink.h"
54 #include "third_party/blink/public/mojom/loader/request_context_frame_type.mojom-blink.h"
55 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
56 #include "third_party/blink/public/platform/task_type.h"
57 #include "third_party/blink/public/platform/web_content_settings_client.h"
58 #include "third_party/blink/public/platform/web_mixed_content.h"
59 #include "third_party/blink/public/platform/web_mixed_content_context_type.h"
60 #include "third_party/blink/public/platform/web_url_request.h"
61 #include "third_party/blink/public/web/web_frame_load_type.h"
62 #include "third_party/blink/public/web/web_history_item.h"
63 #include "third_party/blink/public/web/web_navigation_params.h"
64 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
65 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
66 #include "third_party/blink/renderer/core/dom/document_init.h"
67 #include "third_party/blink/renderer/core/dom/element.h"
68 #include "third_party/blink/renderer/core/dom/ignore_opens_during_unload_count_incrementer.h"
69 #include "third_party/blink/renderer/core/events/page_transition_event.h"
70 #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
71 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
72 #include "third_party/blink/renderer/core/frame/csp/csp_source.h"
73 #include "third_party/blink/renderer/core/frame/csp/navigation_initiator_impl.h"
74 #include "third_party/blink/renderer/core/frame/frame_console.h"
75 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
76 #include "third_party/blink/renderer/core/frame/local_frame.h"
77 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
78 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
79 #include "third_party/blink/renderer/core/frame/settings.h"
80 #include "third_party/blink/renderer/core/frame/visual_viewport.h"
81 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
82 #include "third_party/blink/renderer/core/html/forms/html_form_element.h"
83 #include "third_party/blink/renderer/core/html/html_frame_owner_element.h"
84 #include "third_party/blink/renderer/core/html_names.h"
85 #include "third_party/blink/renderer/core/inspector/console_message.h"
86 #include "third_party/blink/renderer/core/inspector/identifiers_factory.h"
87 #include "third_party/blink/renderer/core/loader/appcache/application_cache_host.h"
88 #include "third_party/blink/renderer/core/loader/document_load_timing.h"
89 #include "third_party/blink/renderer/core/loader/document_loader.h"
90 #include "third_party/blink/renderer/core/loader/form_submission.h"
91 #include "third_party/blink/renderer/core/loader/frame_load_request.h"
92 #include "third_party/blink/renderer/core/loader/mixed_content_checker.h"
93 #include "third_party/blink/renderer/core/loader/progress_tracker.h"
94 #include "third_party/blink/renderer/core/page/chrome_client.h"
95 #include "third_party/blink/renderer/core/page/frame_tree.h"
96 #include "third_party/blink/renderer/core/page/page.h"
97 #include "third_party/blink/renderer/core/page/plugin_data.h"
98 #include "third_party/blink/renderer/core/page/plugin_script_forbidden_scope.h"
99 #include "third_party/blink/renderer/core/page/scrolling/fragment_anchor.h"
100 #include "third_party/blink/renderer/core/page/scrolling/scrolling_coordinator.h"
101 #include "third_party/blink/renderer/core/page/scrolling/text_fragment_anchor.h"
102 #include "third_party/blink/renderer/core/page/viewport_description.h"
103 #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
104 #include "third_party/blink/renderer/core/probe/core_probes.h"
105 #include "third_party/blink/renderer/core/scroll/scroll_animator_base.h"
106 #include "third_party/blink/renderer/core/svg/graphics/svg_image.h"
107 #include "third_party/blink/renderer/core/xml/parser/xml_document_parser.h"
108 #include "third_party/blink/renderer/platform/bindings/dom_wrapper_world.h"
109 #include "third_party/blink/renderer/platform/bindings/microtask.h"
110 #include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
111 #include "third_party/blink/renderer/platform/bindings/v8_dom_activity_logger.h"
112 #include "third_party/blink/renderer/platform/exported/wrapped_resource_request.h"
113 #include "third_party/blink/renderer/platform/heap/heap.h"
114 #include "third_party/blink/renderer/platform/instrumentation/instance_counters.h"
115 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
116 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
117 #include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
118 #include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
119 #include "third_party/blink/renderer/platform/mhtml/archive_resource.h"
120 #include "third_party/blink/renderer/platform/mhtml/mhtml_archive.h"
121 #include "third_party/blink/renderer/platform/network/content_security_policy_response_headers.h"
122 #include "third_party/blink/renderer/platform/network/http_parsers.h"
123 #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
124 #include "third_party/blink/renderer/platform/network/network_utils.h"
125 #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
126 #include "third_party/blink/renderer/platform/web_test_support.h"
127 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
128 #include "third_party/blink/renderer/platform/weborigin/security_policy.h"
129 #include "third_party/blink/renderer/platform/wtf/assertions.h"
130 #include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
131 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
132
133 namespace blink {
134
135 namespace {
136
ApplyOriginPolicy(ContentSecurityPolicy * csp,const WebOriginPolicy & origin_policy)137 void ApplyOriginPolicy(ContentSecurityPolicy* csp,
138 const WebOriginPolicy& origin_policy) {
139 // When this function is called. The following lines of code happen
140 // consecutively:
141 // 1) A new empty set of CSP is created.
142 // 2) CSP(s) from the HTTP response are appended.
143 // 3) CSP(s) from the OriginPolicy are appended. [HERE]
144 //
145 // As a result, at the beginning of this function, the set of CSP must not
146 // contain any OriginPolicy's CSP yet.
147 //
148 // TODO(arthursonzogni): HasPolicyFromSource(...) is used only in this DCHECK,
149 // consider removing this function.
150 DCHECK(!csp->HasPolicyFromSource(
151 network::mojom::ContentSecurityPolicySource::kOriginPolicy));
152
153 for (const auto& policy : origin_policy.content_security_policies) {
154 csp->DidReceiveHeader(
155 policy, network::mojom::ContentSecurityPolicyType::kEnforce,
156 network::mojom::ContentSecurityPolicySource::kOriginPolicy);
157 }
158
159 for (const auto& policy :
160 origin_policy.content_security_policies_report_only) {
161 csp->DidReceiveHeader(
162 policy, network::mojom::ContentSecurityPolicyType::kReport,
163 network::mojom::ContentSecurityPolicySource::kOriginPolicy);
164 }
165 }
166
167 } // namespace
168
IsBackForwardLoadType(WebFrameLoadType type)169 bool IsBackForwardLoadType(WebFrameLoadType type) {
170 return type == WebFrameLoadType::kBackForward;
171 }
172
IsReloadLoadType(WebFrameLoadType type)173 bool IsReloadLoadType(WebFrameLoadType type) {
174 return type == WebFrameLoadType::kReload ||
175 type == WebFrameLoadType::kReloadBypassingCache;
176 }
177
NeedsHistoryItemRestore(WebFrameLoadType type)178 bool FrameLoader::NeedsHistoryItemRestore(WebFrameLoadType type) {
179 return type == WebFrameLoadType::kBackForward || IsReloadLoadType(type);
180 }
181
ResourceRequestForReload(WebFrameLoadType frame_load_type,ClientRedirectPolicy client_redirect_policy)182 ResourceRequest FrameLoader::ResourceRequestForReload(
183 WebFrameLoadType frame_load_type,
184 ClientRedirectPolicy client_redirect_policy) {
185 DCHECK(IsReloadLoadType(frame_load_type));
186 const auto cache_mode =
187 frame_load_type == WebFrameLoadType::kReloadBypassingCache
188 ? mojom::FetchCacheMode::kBypassCache
189 : mojom::FetchCacheMode::kValidateCache;
190 if (!document_loader_ || !document_loader_->GetHistoryItem())
191 return ResourceRequest();
192
193 ResourceRequest request =
194 document_loader_->GetHistoryItem()->GenerateResourceRequest(cache_mode);
195 request.SetRequestorOrigin(frame_->GetSecurityContext()->GetSecurityOrigin());
196
197 // ClientRedirectPolicy is an indication that this load was triggered by some
198 // direct interaction with the page. If this reload is not a client redirect,
199 // we should reuse the referrer from the original load of the current
200 // document. If this reload is a client redirect (e.g., location.reload()), it
201 // was initiated by something in the current document and should therefore
202 // show the current document's url as the referrer.
203 if (client_redirect_policy == ClientRedirectPolicy::kClientRedirect) {
204 LocalDOMWindow* window = frame_->DomWindow();
205 Referrer referrer = SecurityPolicy::GenerateReferrer(
206 window->GetReferrerPolicy(), window->Url(), window->OutgoingReferrer());
207 request.SetReferrerString(referrer.referrer);
208 request.SetReferrerPolicy(referrer.referrer_policy);
209 }
210
211 request.SetSkipServiceWorker(frame_load_type ==
212 WebFrameLoadType::kReloadBypassingCache);
213 return request;
214 }
215
FrameLoader(LocalFrame * frame)216 FrameLoader::FrameLoader(LocalFrame* frame)
217 : frame_(frame),
218 progress_tracker_(MakeGarbageCollected<ProgressTracker>(frame)),
219 // Frames need to inherit the sandbox flags of their parent frame.
220 // These can be fixed at construction time, because the only actions that
221 // trigger a sandbox flags change in the parent will necessarily detach
222 // this frame.
223 forced_sandbox_flags_(
224 frame_->Tree().Parent()
225 ? frame_->Tree().Parent()->GetSecurityContext()->GetSandboxFlags()
226 : network::mojom::blink::WebSandboxFlags::kNone),
227 dispatching_did_clear_window_object_in_main_world_(false),
228 virtual_time_pauser_(
229 frame_->GetFrameScheduler()->CreateWebScopedVirtualTimePauser(
230 "FrameLoader",
231 WebScopedVirtualTimePauser::VirtualTaskDuration::kInstant)) {
232 DCHECK(frame_);
233
234 TRACE_EVENT_OBJECT_CREATED_WITH_ID("loading", "FrameLoader", this);
235 TakeObjectSnapshot();
236 }
237
~FrameLoader()238 FrameLoader::~FrameLoader() {
239 DCHECK_EQ(state_, State::kDetached);
240 }
241
Trace(Visitor * visitor) const242 void FrameLoader::Trace(Visitor* visitor) const {
243 visitor->Trace(frame_);
244 visitor->Trace(progress_tracker_);
245 visitor->Trace(document_loader_);
246 visitor->Trace(last_origin_window_csp_);
247 }
248
Init()249 void FrameLoader::Init() {
250 ScriptForbiddenScope forbid_scripts;
251
252 auto navigation_params = std::make_unique<WebNavigationParams>();
253 navigation_params->url = KURL(g_empty_string);
254 navigation_params->frame_policy =
255 frame_->Owner() ? frame_->Owner()->GetFramePolicy() : FramePolicy();
256
257 DocumentLoader* new_document_loader = Client()->CreateDocumentLoader(
258 frame_, kWebNavigationTypeOther, CreateCSPForInitialEmptyDocument(),
259 std::move(navigation_params), nullptr /* extra_data */);
260
261 CommitDocumentLoader(new_document_loader, base::nullopt, nullptr,
262 CommitReason::kInitialization);
263
264 frame_->GetDocument()->CancelParsing();
265
266 // Suppress finish notifications for initial empty documents, since they don't
267 // generate start notifications.
268 document_loader_->SetSentDidFinishLoad();
269 // Ensure that the frame sees the correct page lifecycle state.
270 frame_->OnPageLifecycleStateUpdated();
271
272 TakeObjectSnapshot();
273
274 state_ = State::kInitialized;
275 }
276
Client() const277 LocalFrameClient* FrameLoader::Client() const {
278 return frame_->Client();
279 }
280
SetDefersLoading(WebURLLoader::DeferType defers)281 void FrameLoader::SetDefersLoading(WebURLLoader::DeferType defers) {
282 if (frame_->GetDocument())
283 frame_->GetDocument()->Fetcher()->SetDefersLoading(defers);
284 if (document_loader_)
285 document_loader_->SetDefersLoading(defers);
286 }
287
SaveScrollAnchor()288 void FrameLoader::SaveScrollAnchor() {
289 if (!document_loader_ || !document_loader_->GetHistoryItem() ||
290 !frame_->View())
291 return;
292
293 // Shouldn't clobber anything if we might still restore later.
294 if (NeedsHistoryItemRestore(document_loader_->LoadType()) &&
295 !document_loader_->GetInitialScrollState().was_scrolled_by_user)
296 return;
297
298 HistoryItem* history_item = document_loader_->GetHistoryItem();
299 if (ScrollableArea* layout_scrollable_area =
300 frame_->View()->LayoutViewport()) {
301 ScrollAnchor* scroll_anchor = layout_scrollable_area->GetScrollAnchor();
302 DCHECK(scroll_anchor);
303
304 const SerializedAnchor& serialized_anchor =
305 scroll_anchor->GetSerializedAnchor();
306 if (serialized_anchor.IsValid()) {
307 history_item->SetScrollAnchorData(
308 {serialized_anchor.selector,
309 gfx::PointF(serialized_anchor.relative_offset.X(),
310 serialized_anchor.relative_offset.Y()),
311 serialized_anchor.simhash});
312 }
313 }
314 }
315
SaveScrollState()316 void FrameLoader::SaveScrollState() {
317 if (!document_loader_ || !document_loader_->GetHistoryItem() ||
318 !frame_->View())
319 return;
320
321 // Shouldn't clobber anything if we might still restore later.
322 if (NeedsHistoryItemRestore(document_loader_->LoadType()) &&
323 !document_loader_->GetInitialScrollState().was_scrolled_by_user)
324 return;
325
326 HistoryItem* history_item = document_loader_->GetHistoryItem();
327 // For performance reasons, we don't save scroll anchors as often as we save
328 // scroll offsets. In order to avoid keeping around a stale anchor, we clear
329 // it when the saved scroll offset changes.
330 history_item->SetScrollAnchorData(ScrollAnchorData());
331 if (ScrollableArea* layout_scrollable_area = frame_->View()->LayoutViewport())
332 history_item->SetScrollOffset(layout_scrollable_area->GetScrollOffset());
333 history_item->SetVisualViewportScrollOffset(ToScrollOffset(
334 frame_->GetPage()->GetVisualViewport().VisibleRect().Location()));
335
336 if (frame_->IsMainFrame())
337 history_item->SetPageScaleFactor(frame_->GetPage()->PageScaleFactor());
338
339 Client()->DidUpdateCurrentHistoryItem();
340 }
341
DispatchUnloadEvent(SecurityOrigin * committing_origin,base::Optional<Document::UnloadEventTiming> * timing)342 void FrameLoader::DispatchUnloadEvent(
343 SecurityOrigin* committing_origin,
344 base::Optional<Document::UnloadEventTiming>* timing) {
345 FrameNavigationDisabler navigation_disabler(*frame_);
346 SaveScrollState();
347
348 if (!SVGImage::IsInSVGImage(frame_->GetDocument()))
349 frame_->GetDocument()->DispatchUnloadEvents(committing_origin, timing);
350 }
351
DidExplicitOpen()352 void FrameLoader::DidExplicitOpen() {
353 probe::LifecycleEvent(frame_, GetDocumentLoader(), "init",
354 base::TimeTicks::Now().since_origin().InSecondsF());
355 if (empty_document_status_ == EmptyDocumentStatus::kOnlyEmpty)
356 empty_document_status_ = EmptyDocumentStatus::kOnlyEmptyButExplicitlyOpened;
357
358 // Only model a document.open() as part of a navigation if its parent is not
359 // done or in the process of completing.
360 if (Frame* parent = frame_->Tree().Parent()) {
361 auto* parent_local_frame = DynamicTo<LocalFrame>(parent);
362 if ((parent_local_frame &&
363 parent_local_frame->GetDocument()->LoadEventStillNeeded()) ||
364 (parent->IsRemoteFrame() && parent->IsLoading())) {
365 progress_tracker_->ProgressStarted();
366 }
367 }
368 }
369
FinishedParsing()370 void FrameLoader::FinishedParsing() {
371 if (state_ == State::kUninitialized)
372 return;
373
374 progress_tracker_->FinishedParsing();
375
376 frame_->GetLocalFrameHostRemote().DidFinishDocumentLoad();
377
378 if (Client()) {
379 ScriptForbiddenScope forbid_scripts;
380 Client()->DispatchDidFinishDocumentLoad();
381 }
382
383 if (Client()) {
384 Client()->RunScriptsAtDocumentReady(
385 document_loader_ ? document_loader_->IsCommittedButEmpty() : true);
386 }
387
388 if (frame_->View()) {
389 ProcessFragment(frame_->GetDocument()->Url(), document_loader_->LoadType(),
390 kNavigationToDifferentDocument);
391 }
392
393 frame_->GetDocument()->CheckCompleted();
394 }
395
396 // TODO(dgozman): we are calling this method too often, hoping that it
397 // does not do anything when navigation is in progress, or when loading
398 // has finished already. We should call it at the right times.
DidFinishNavigation(NavigationFinishState state)399 void FrameLoader::DidFinishNavigation(NavigationFinishState state) {
400 // Only declare the whole frame finished if the committed navigation is done
401 // and there is no provisional navigation in progress.
402 if ((document_loader_ && !document_loader_->SentDidFinishLoad()) ||
403 HasProvisionalNavigation()) {
404 return;
405 }
406
407 // This code in this block is meant to prepare a document for display, but
408 // this code may also run when swapping out a provisional frame. In that case,
409 // skip the display work.
410 if (frame_->IsLoading() && !frame_->IsProvisional()) {
411 progress_tracker_->ProgressCompleted();
412 // Retry restoring scroll offset since finishing loading disables content
413 // size clamping.
414 RestoreScrollPositionAndViewState();
415 if (document_loader_)
416 document_loader_->SetLoadType(WebFrameLoadType::kStandard);
417 frame_->FinishedLoading(state);
418 }
419
420 // When a subframe finishes loading, the parent should check if *all*
421 // subframes have finished loading (which may mean that the parent can declare
422 // that the parent itself has finished loading). This local-subframe-focused
423 // code has a remote-subframe equivalent in
424 // WebRemoteFrameImpl::DidStopLoading.
425 Frame* parent = frame_->Tree().Parent();
426 if (parent)
427 parent->CheckCompleted();
428 }
429
Opener()430 Frame* FrameLoader::Opener() {
431 return frame_->Opener();
432 }
433
SetOpener(LocalFrame * opener)434 void FrameLoader::SetOpener(LocalFrame* opener) {
435 // If the frame is already detached, the opener has already been cleared.
436 frame_->SetOpener(opener);
437 }
438
AllowPlugins(ReasonForCallingAllowPlugins reason)439 bool FrameLoader::AllowPlugins(ReasonForCallingAllowPlugins reason) {
440 // With Oilpan, a FrameLoader might be accessed after the Page has been
441 // detached. FrameClient will not be accessible, so bail early.
442 if (!Client())
443 return false;
444 Settings* settings = frame_->GetSettings();
445 bool allowed = settings && settings->GetPluginsEnabled();
446 if (!allowed && reason == kAboutToInstantiatePlugin) {
447 if (auto* settings_client = frame_->GetContentSettingsClient())
448 settings_client->DidNotAllowPlugins();
449 }
450 return allowed;
451 }
452
DetachDocumentLoader(Member<DocumentLoader> & loader,bool flush_microtask_queue)453 void FrameLoader::DetachDocumentLoader(Member<DocumentLoader>& loader,
454 bool flush_microtask_queue) {
455 if (!loader)
456 return;
457
458 FrameNavigationDisabler navigation_disabler(*frame_);
459 loader->DetachFromFrame(flush_microtask_queue);
460 loader = nullptr;
461 }
462
DidFinishSameDocumentNavigation(const KURL & url,WebFrameLoadType frame_load_type,HistoryItem * history_item)463 void FrameLoader::DidFinishSameDocumentNavigation(
464 const KURL& url,
465 WebFrameLoadType frame_load_type,
466 HistoryItem* history_item) {
467 // If we have a state object, we cannot also be a new navigation.
468 scoped_refptr<SerializedScriptValue> state_object =
469 history_item ? history_item->StateObject() : nullptr;
470 DCHECK(!state_object || frame_load_type == WebFrameLoadType::kBackForward);
471
472 // onpopstate might change view state, so stash for later restore.
473 base::Optional<HistoryItem::ViewState> view_state;
474 if (history_item) {
475 view_state = history_item->GetViewState();
476 }
477
478 frame_->DomWindow()->StatePopped(state_object
479 ? std::move(state_object)
480 : SerializedScriptValue::NullValue());
481
482 if (view_state) {
483 RestoreScrollPositionAndViewState(frame_load_type, *view_state,
484 history_item->ScrollRestorationType());
485 }
486
487 // We need to scroll to the fragment whether or not a hash change occurred,
488 // since the user might have scrolled since the previous navigation.
489 ProcessFragment(url, frame_load_type, kNavigationWithinSameDocument);
490
491 TakeObjectSnapshot();
492 }
493
DetermineFrameLoadType(const KURL & url,const AtomicString & http_method,bool has_origin_window,const KURL & failing_url,WebFrameLoadType frame_load_type)494 WebFrameLoadType FrameLoader::DetermineFrameLoadType(
495 const KURL& url,
496 const AtomicString& http_method,
497 bool has_origin_window,
498 const KURL& failing_url,
499 WebFrameLoadType frame_load_type) {
500 // TODO(dgozman): this method is rewriting the load type, which makes it hard
501 // to reason about various navigations and their desired load type. We should
502 // untangle it and detect the load type at the proper place. See, for example,
503 // location.assign() block below.
504 // Achieving that is complicated due to similar conditions in many places
505 // both in the renderer and in the browser.
506 if (frame_load_type == WebFrameLoadType::kStandard ||
507 frame_load_type == WebFrameLoadType::kReplaceCurrentItem) {
508 if (frame_->Tree().Parent() &&
509 empty_document_status_ == EmptyDocumentStatus::kOnlyEmpty) {
510 return WebFrameLoadType::kReplaceCurrentItem;
511 }
512 if (!frame_->Tree().Parent() && !Client()->BackForwardLength()) {
513 if (Opener() && url.IsEmpty())
514 return WebFrameLoadType::kReplaceCurrentItem;
515 return WebFrameLoadType::kStandard;
516 }
517 }
518 if (frame_load_type != WebFrameLoadType::kStandard)
519 return frame_load_type;
520
521 if (url == document_loader_->UrlForHistory()) {
522 if (http_method == http_names::kPOST)
523 return WebFrameLoadType::kStandard;
524 if (!has_origin_window)
525 return WebFrameLoadType::kReload;
526 return WebFrameLoadType::kReplaceCurrentItem;
527 }
528
529 if (failing_url == document_loader_->UrlForHistory() &&
530 document_loader_->LoadType() == WebFrameLoadType::kReload)
531 return WebFrameLoadType::kReload;
532
533 if (url.IsEmpty() && failing_url.IsEmpty()) {
534 return WebFrameLoadType::kReplaceCurrentItem;
535 }
536
537 return WebFrameLoadType::kStandard;
538 }
539
AllowRequestForThisFrame(const FrameLoadRequest & request)540 bool FrameLoader::AllowRequestForThisFrame(const FrameLoadRequest& request) {
541 // If no origin Document* was specified, skip remaining security checks and
542 // assume the caller has fully initialized the FrameLoadRequest.
543 if (!request.GetOriginWindow())
544 return true;
545
546 const KURL& url = request.GetResourceRequest().Url();
547 if (url.ProtocolIsJavaScript()) {
548 // Check the CSP of the caller (the "source browsing context") if required,
549 // as per https://html.spec.whatwg.org/C/#javascript-protocol.
550 bool javascript_url_is_allowed =
551 request.GetOriginWindow()
552 ->GetContentSecurityPolicyForWorld(request.JavascriptWorld().get())
553 ->AllowInline(ContentSecurityPolicy::InlineType::kNavigation,
554 frame_->DeprecatedLocalOwner(), url.GetString(),
555 String() /* nonce */,
556 request.GetOriginWindow()->Url(),
557 OrdinalNumber::First());
558
559 if (!javascript_url_is_allowed)
560 return false;
561
562 if (frame_->Owner() && ((frame_->Owner()->GetFramePolicy().sandbox_flags &
563 network::mojom::blink::WebSandboxFlags::kOrigin) !=
564 network::mojom::blink::WebSandboxFlags::kNone)) {
565 return false;
566 }
567 }
568
569 if (!request.CanDisplay(url)) {
570 request.GetOriginWindow()->AddConsoleMessage(
571 MakeGarbageCollected<ConsoleMessage>(
572 mojom::ConsoleMessageSource::kSecurity,
573 mojom::ConsoleMessageLevel::kError,
574 "Not allowed to load local resource: " + url.ElidedString()));
575 return false;
576 }
577 return true;
578 }
579
DetermineNavigationType(WebFrameLoadType frame_load_type,bool is_form_submission,bool have_event)580 static WebNavigationType DetermineNavigationType(
581 WebFrameLoadType frame_load_type,
582 bool is_form_submission,
583 bool have_event) {
584 bool is_reload = IsReloadLoadType(frame_load_type);
585 bool is_back_forward = IsBackForwardLoadType(frame_load_type);
586 if (is_form_submission) {
587 return (is_reload || is_back_forward) ? kWebNavigationTypeFormResubmitted
588 : kWebNavigationTypeFormSubmitted;
589 }
590 if (have_event)
591 return kWebNavigationTypeLinkClicked;
592 if (is_reload)
593 return kWebNavigationTypeReload;
594 if (is_back_forward)
595 return kWebNavigationTypeBackForward;
596 return kWebNavigationTypeOther;
597 }
598
599 static mojom::blink::RequestContextType
DetermineRequestContextFromNavigationType(const WebNavigationType navigation_type)600 DetermineRequestContextFromNavigationType(
601 const WebNavigationType navigation_type) {
602 switch (navigation_type) {
603 case kWebNavigationTypeLinkClicked:
604 return mojom::blink::RequestContextType::HYPERLINK;
605
606 case kWebNavigationTypeOther:
607 return mojom::blink::RequestContextType::LOCATION;
608
609 case kWebNavigationTypeFormResubmitted:
610 case kWebNavigationTypeFormSubmitted:
611 return mojom::blink::RequestContextType::FORM;
612
613 case kWebNavigationTypeBackForward:
614 case kWebNavigationTypeReload:
615 return mojom::blink::RequestContextType::INTERNAL;
616 }
617 NOTREACHED();
618 return mojom::blink::RequestContextType::HYPERLINK;
619 }
620
621 static network::mojom::RequestDestination
DetermineRequestDestinationFromNavigationType(const WebNavigationType navigation_type)622 DetermineRequestDestinationFromNavigationType(
623 const WebNavigationType navigation_type) {
624 switch (navigation_type) {
625 case kWebNavigationTypeLinkClicked:
626 case kWebNavigationTypeOther:
627 case kWebNavigationTypeFormResubmitted:
628 case kWebNavigationTypeFormSubmitted:
629 return network::mojom::RequestDestination::kDocument;
630 case kWebNavigationTypeBackForward:
631 case kWebNavigationTypeReload:
632 return network::mojom::RequestDestination::kEmpty;
633 }
634 NOTREACHED();
635 return network::mojom::RequestDestination::kDocument;
636 }
637
StartNavigation(FrameLoadRequest & request,WebFrameLoadType frame_load_type)638 void FrameLoader::StartNavigation(FrameLoadRequest& request,
639 WebFrameLoadType frame_load_type) {
640 CHECK(!IsBackForwardLoadType(frame_load_type));
641 DCHECK(request.GetTriggeringEventInfo() != TriggeringEventInfo::kUnknown);
642 DCHECK(frame_->GetDocument());
643 if (HTMLFrameOwnerElement* element = frame_->DeprecatedLocalOwner())
644 element->CancelPendingLazyLoad();
645
646 ResourceRequest& resource_request = request.GetResourceRequest();
647 const KURL& url = resource_request.Url();
648 LocalDOMWindow* origin_window = request.GetOriginWindow();
649
650 TRACE_EVENT2("navigation", "FrameLoader::StartNavigation", "url",
651 url.GetString().Utf8(), "load_type",
652 static_cast<int>(frame_load_type));
653
654 resource_request.SetHasUserGesture(
655 LocalFrame::HasTransientUserActivation(frame_));
656
657 if (!AllowRequestForThisFrame(request))
658 return;
659
660 // Block renderer-initiated loads of data: and filesystem: URLs in the top
661 // frame.
662 //
663 // If the mime type of the data URL is supported, the URL will
664 // eventually be rendered, so block it here. Otherwise, the load might be
665 // handled by a plugin or end up as a download, so allow it to let the
666 // embedder figure out what to do with it. Navigations to filesystem URLs are
667 // always blocked here.
668 if (frame_->IsMainFrame() && origin_window &&
669 !frame_->Client()->AllowContentInitiatedDataUrlNavigations(
670 origin_window->Url()) &&
671 (url.ProtocolIs("filesystem") ||
672 (url.ProtocolIsData() &&
673 network_utils::IsDataURLMimeTypeSupported(url)))) {
674 frame_->GetDocument()->AddConsoleMessage(
675 MakeGarbageCollected<ConsoleMessage>(
676 mojom::ConsoleMessageSource::kSecurity,
677 mojom::ConsoleMessageLevel::kError,
678 "Not allowed to navigate top frame to " + url.Protocol() +
679 " URL: " + url.ElidedString()));
680 return;
681 }
682
683 // TODO(dgozman): merge page dismissal check and FrameNavigationDisabler.
684 if (!frame_->IsNavigationAllowed() ||
685 frame_->GetDocument()->PageDismissalEventBeingDispatched() !=
686 Document::kNoDismissal) {
687 return;
688 }
689
690 frame_load_type = DetermineFrameLoadType(
691 resource_request.Url(), resource_request.HttpMethod(), origin_window,
692 KURL(), frame_load_type);
693
694 bool same_document_navigation =
695 request.GetNavigationPolicy() == kNavigationPolicyCurrentTab &&
696 ShouldPerformFragmentNavigation(
697 request.Form(), resource_request.HttpMethod(), frame_load_type, url);
698
699 // Perform same document navigation.
700 if (same_document_navigation) {
701 document_loader_->CommitSameDocumentNavigation(
702 url, frame_load_type, nullptr, request.ClientRedirect(), origin_window,
703 request.GetTriggeringEventInfo() != TriggeringEventInfo::kNotFromEvent,
704 nullptr /* extra_data */);
705 return;
706 }
707
708 // If we're navigating and there's still a text fragment permission token on
709 // the document loader, it means this navigation didn't try to invoke a text
710 // fragment. In this case, we want to propagate this to the next document to
711 // allow text-fragments across client-side redirects.
712 bool text_fragment_token = GetDocumentLoader()->ConsumeTextFragmentToken();
713
714 resource_request.SetHasTextFragmentToken(text_fragment_token);
715
716 WebNavigationType navigation_type = DetermineNavigationType(
717 frame_load_type, resource_request.HttpBody() || request.Form(),
718 request.GetTriggeringEventInfo() != TriggeringEventInfo::kNotFromEvent);
719 mojom::blink::RequestContextType request_context_type =
720 DetermineRequestContextFromNavigationType(navigation_type);
721
722 // TODO(lyf): handle `frame` context type. https://crbug.com/1019716
723 if (mojom::blink::RequestContextType::LOCATION == request_context_type &&
724 !frame_->IsMainFrame()) {
725 request_context_type = mojom::blink::RequestContextType::IFRAME;
726 }
727 resource_request.SetRequestContext(request_context_type);
728 resource_request.SetRequestDestination(
729 DetermineRequestDestinationFromNavigationType(navigation_type));
730 request.SetFrameType(frame_->IsMainFrame()
731 ? mojom::RequestContextFrameType::kTopLevel
732 : mojom::RequestContextFrameType::kNested);
733
734 mojo::PendingRemote<mojom::blink::NavigationInitiator> navigation_initiator;
735 WTF::Vector<network::mojom::blink::ContentSecurityPolicyPtr> initiator_csp;
736 network::mojom::blink::CSPSourcePtr initiator_self_source;
737 if (origin_window && origin_window->GetContentSecurityPolicy()
738 ->ExperimentalFeaturesEnabled()) {
739 ContentSecurityPolicy* origin_window_csp =
740 origin_window->GetContentSecurityPolicy();
741 CSPSource* origin_window_csp_self_source =
742 origin_window_csp->GetSelfSource();
743
744 initiator_csp = origin_window_csp->ExposeForNavigationalChecks();
745 if (origin_window_csp_self_source) {
746 initiator_self_source =
747 origin_window_csp_self_source->ExposeForNavigationalChecks();
748 }
749 NavigationInitiatorImpl::From(*origin_window)
750 .BindReceiver(navigation_initiator.InitWithNewPipeAndPassReceiver());
751 }
752
753 // Record the document that has initiated this navigation. It will be used at
754 // navigation commit time for inheritance.
755 // TODO(arthursonzogni): This looks very fragile. It seems easy to confuse the
756 // FrameLoader by starting several navigations in a row. We should get rid of
757 // this.
758 last_origin_window_csp_ = MakeGarbageCollected<ContentSecurityPolicy>();
759 if (origin_window && origin_window->GetContentSecurityPolicy()) {
760 last_origin_window_csp_->CopyStateFrom(
761 origin_window->GetContentSecurityPolicy());
762 }
763
764 // Record the latest requiredCSP value that will be used when loading the
765 // document at navigation commit time.
766 RecordLatestRequiredCSP();
767
768 // TODO(arthursonzogni): 'frame-src' check is disabled on the
769 // renderer side, but is enforced on the browser side.
770 // See http://crbug.com/692595 for understanding why it
771 // can't be enforced on both sides instead.
772
773 // 'form-action' check in the frame that is navigating is disabled on the
774 // renderer side, but is enforced on the browser side instead.
775 // N.B. check in the frame that initiates the navigation stills occurs in
776 // blink and is not enforced on the browser-side.
777 // TODO(arthursonzogni) The 'form-action' check should be fully disabled
778 // in blink, except when the form submission doesn't trigger a navigation
779 // (i.e. javascript urls). Please see https://crbug.com/701749.
780
781 // Report-only CSP headers are checked in browser.
782 const FetchClientSettingsObject* fetch_client_settings_object = nullptr;
783 if (origin_window) {
784 fetch_client_settings_object = &origin_window->Fetcher()
785 ->GetProperties()
786 .GetFetchClientSettingsObject();
787 }
788 ModifyRequestForCSP(resource_request, fetch_client_settings_object,
789 origin_window, request.GetFrameType());
790
791 DCHECK(Client()->HasWebView());
792 // Check for non-escaped new lines in the url.
793 if (url.PotentiallyDanglingMarkup() && url.ProtocolIsInHTTPFamily()) {
794 Deprecation::CountDeprecation(
795 origin_window, WebFeature::kCanRequestURLHTTPContainingNewline);
796 return;
797 }
798
799 if (url.ProtocolIsJavaScript()) {
800 if (!origin_window ||
801 origin_window->CanExecuteScripts(kAboutToExecuteScript)) {
802 frame_->GetDocument()->ProcessJavaScriptUrl(url,
803 request.JavascriptWorld());
804 }
805 return;
806 }
807
808 if (frame_->IsMainFrame())
809 LocalFrame::ConsumeTransientUserActivation(frame_);
810
811 // The main resource request gets logged here, because V8DOMActivityLogger
812 // is looked up based on the current v8::Context. When the request actually
813 // begins, the v8::Context may no longer be on the stack.
814 if (V8DOMActivityLogger* activity_logger =
815 V8DOMActivityLogger::CurrentActivityLoggerIfIsolatedWorld()) {
816 if (!DocumentLoader::WillLoadUrlAsEmpty(url)) {
817 Vector<String> argv;
818 argv.push_back("Main resource");
819 argv.push_back(url.GetString());
820 activity_logger->LogEvent("blinkRequestResource", argv.size(),
821 argv.data());
822 }
823 }
824
825 if (request.ClientRedirectReason() != ClientNavigationReason::kNone) {
826 probe::FrameRequestedNavigation(frame_, frame_, url,
827 request.ClientRedirectReason(),
828 request.GetNavigationPolicy());
829 }
830
831 const network::mojom::IPAddressSpace initiator_address_space =
832 origin_window ? origin_window->AddressSpace()
833 : network::mojom::IPAddressSpace::kUnknown;
834
835 // TODO(crbug.com/896041): Instead of just bypassing the CSP for navigations
836 // from isolated world, ideally we should enforce the isolated world CSP by
837 // plumbing the correct CSP to the browser.
838 using CSPDisposition = network::mojom::CSPDisposition;
839 CSPDisposition should_check_main_world_csp =
840 ContentSecurityPolicy::ShouldBypassMainWorld(
841 request.JavascriptWorld().get())
842 ? CSPDisposition::DO_NOT_CHECK
843 : CSPDisposition::CHECK;
844 Client()->BeginNavigation(
845 resource_request, request.GetFrameType(), origin_window,
846 nullptr /* document_loader */, navigation_type,
847 request.GetNavigationPolicy(), frame_load_type,
848 request.ClientRedirect() == ClientRedirectPolicy::kClientRedirect,
849 request.GetTriggeringEventInfo(), request.Form(),
850 should_check_main_world_csp, request.GetBlobURLToken(),
851 request.GetInputStartTime(), request.HrefTranslate().GetString(),
852 request.Impression(), std::move(initiator_csp),
853 std::move(initiator_self_source), initiator_address_space,
854 std::move(navigation_initiator));
855 }
856
FillStaticResponseIfNeeded(WebNavigationParams * params,LocalFrame * frame)857 static void FillStaticResponseIfNeeded(WebNavigationParams* params,
858 LocalFrame* frame) {
859 if (params->is_static_data)
860 return;
861
862 const KURL& url = params->url;
863 // See WebNavigationParams for special case explanations.
864 if (url.IsAboutSrcdocURL()) {
865 // TODO(dgozman): instead of reaching to the owner here, we could instead:
866 // - grab the "srcdoc" value when starting a navigation right in the owner;
867 // - pass it around through BeginNavigation to CommitNavigation as |data|;
868 // - use it here instead of re-reading from the owner.
869 // This way we will get rid of extra dependency between starting and
870 // committing navigation.
871 String srcdoc;
872 HTMLFrameOwnerElement* owner_element = frame->DeprecatedLocalOwner();
873 if (!IsA<HTMLIFrameElement>(owner_element) ||
874 !owner_element->FastHasAttribute(html_names::kSrcdocAttr)) {
875 // Cannot retrieve srcdoc content anymore (perhaps, the attribute was
876 // cleared) - load empty instead.
877 } else {
878 srcdoc = owner_element->FastGetAttribute(html_names::kSrcdocAttr);
879 DCHECK(!srcdoc.IsNull());
880 }
881 WebNavigationParams::FillStaticResponse(params, "text/html", "UTF-8",
882 StringUTF8Adaptor(srcdoc));
883 return;
884 }
885
886 MHTMLArchive* archive = nullptr;
887 if (auto* parent = DynamicTo<LocalFrame>(frame->Tree().Parent()))
888 archive = parent->Loader().GetDocumentLoader()->Archive();
889 if (archive && !url.ProtocolIsData()) {
890 // If we have an archive loaded in some ancestor frame, we should
891 // retrieve document content from that archive. This is different from
892 // loading an archive into this frame, which will be handled separately
893 // once we load the body and parse it as an archive.
894 params->body_loader.reset();
895 ArchiveResource* archive_resource = archive->SubresourceForURL(url);
896 if (archive_resource) {
897 SharedBuffer* archive_data = archive_resource->Data();
898 WebNavigationParams::FillStaticResponse(
899 params, archive_resource->MimeType(),
900 archive_resource->TextEncoding(),
901 base::make_span(archive_data->Data(), archive_data->size()));
902 } else {
903 // The requested archive resource does not exist. In an ideal world, this
904 // would commit as a failed navigation, but the browser doesn't know
905 // anything about what resources are available in the archive. Just
906 // synthesize an empty document so that something commits still.
907 // TODO(https://crbug.com/1112965): remove these special cases by adding
908 // an URLLoaderFactory implementation for MHTML archives.
909 WebNavigationParams::FillStaticResponse(
910 params, "text/html", "UTF-8",
911 "<html><body>"
912 "<!-- failed to find resource in MHTML archive -->"
913 "</body></html>");
914 }
915 }
916
917 // Checking whether a URL would load as empty (e.g. about:blank) must be done
918 // after checking for content with the corresponding URL in the MHTML archive,
919 // since MHTML archives can define custom content to load for about:blank...
920 //
921 // Note that no static response needs to be filled here; instead, this is
922 // synthesised later by `DocumentLoader::InitializeEmptyResponse()`.
923 if (DocumentLoader::WillLoadUrlAsEmpty(params->url))
924 return;
925
926 const String& mime_type = params->response.MimeType();
927 if (MIMETypeRegistry::IsSupportedMIMEType(mime_type))
928 return;
929
930 PluginData* plugin_data = frame->GetPluginData();
931 if (!mime_type.IsEmpty() && plugin_data &&
932 plugin_data->SupportsMimeType(mime_type)) {
933 return;
934 }
935
936 // Typically, PlzNavigate checks that the MIME type can be handled on the
937 // browser side before sending it to the renderer. However, there are rare
938 // scenarios where it's possible for the renderer to send a commit request
939 // with a MIME type the renderer cannot handle:
940 //
941 // - (hypothetical) some sort of race between enabling/disabling plugins
942 // and when it's checked by the navigation URL loader / handled in the
943 // renderer.
944 // - mobile emulation disables plugins on the renderer side, but the browser
945 // navigation code is not aware of this.
946 //
947 // Similar to the missing archive resource case above, synthesise a resource
948 // to commit.
949 WebNavigationParams::FillStaticResponse(
950 params, "text/html", "UTF-8",
951 "<html><body>"
952 "<!-- no enabled plugin supports this MIME type -->"
953 "</body></html>");
954 }
955
956 // The browser navigation code should never send a `CommitNavigation()` request
957 // that fails this check.
AssertCanNavigate(WebNavigationParams * params,LocalFrame * frame)958 static void AssertCanNavigate(WebNavigationParams* params, LocalFrame* frame) {
959 if (params->is_static_data)
960 return;
961
962 if (DocumentLoader::WillLoadUrlAsEmpty(params->url))
963 return;
964
965 int status_code = params->response.HttpStatusCode();
966 // If the server sends 204 or 205, this means the server does not want to
967 // replace the page contents. However, PlzNavigate should have handled it
968 // browser-side and never sent a commit request to the renderer.
969 if (status_code == 204 || status_code == 205)
970 CHECK(false);
971
972 // If the server attached a Content-Disposition indicating that the resource
973 // is an attachment, this is actually a download. However, PlzNavigate should
974 // have handled it browser-side and never sent a commit request to the
975 // renderer.
976 if (IsContentDispositionAttachment(
977 params->response.HttpHeaderField(http_names::kContentDisposition))) {
978 CHECK(false);
979 }
980 }
981
CommitNavigation(std::unique_ptr<WebNavigationParams> navigation_params,std::unique_ptr<WebDocumentLoader::ExtraData> extra_data,CommitReason commit_reason)982 void FrameLoader::CommitNavigation(
983 std::unique_ptr<WebNavigationParams> navigation_params,
984 std::unique_ptr<WebDocumentLoader::ExtraData> extra_data,
985 CommitReason commit_reason) {
986 DCHECK(document_loader_);
987 DCHECK(frame_->GetDocument());
988 DCHECK(Client()->HasWebView());
989
990 if (!frame_->IsNavigationAllowed() ||
991 frame_->GetDocument()->PageDismissalEventBeingDispatched() !=
992 Document::kNoDismissal) {
993 // Any of the checks above should not be necessary.
994 // Unfortunately, in the case of sync IPCs like print() there might be
995 // reentrancy and, for example, frame detach happening.
996 // See fast/loader/detach-while-printing.html for a repro.
997 // TODO(https://crbug.com/862088): we should probably ignore print()
998 // call in this case instead.
999 return;
1000 }
1001
1002 // TODO(dgozman): figure out the better place for this check
1003 // to cancel lazy load both on start and commit. Perhaps
1004 // CancelProvisionalLoaderForNewNavigation() is a good one.
1005 HTMLFrameOwnerElement* frame_owner = frame_->DeprecatedLocalOwner();
1006 if (frame_owner)
1007 frame_owner->CancelPendingLazyLoad();
1008
1009 navigation_params->frame_load_type = DetermineFrameLoadType(
1010 navigation_params->url, navigation_params->http_method,
1011 false /* has_origin_window */, navigation_params->unreachable_url,
1012 navigation_params->frame_load_type);
1013
1014 // Note: we might actually classify this navigation as same document
1015 // right here in the following circumstances:
1016 // - the loader has already committed a navigation and notified the browser
1017 // process which did not receive a message about that just yet;
1018 // - meanwhile, the browser process sent us a command to commit this new
1019 // "cross-document" navigation, while it's actually same-document
1020 // with regards to the last commit.
1021 // In this rare case, we intentionally proceed as cross-document.
1022
1023 RecordLatestRequiredCSP();
1024
1025 if (!CancelProvisionalLoaderForNewNavigation())
1026 return;
1027
1028 FillStaticResponseIfNeeded(navigation_params.get(), frame_);
1029 AssertCanNavigate(navigation_params.get(), frame_);
1030
1031 // Keep track of the current Document HistoryItem as the new DocumentLoader
1032 // might need to copy state from it. Note that the current DocumentLoader
1033 // should always exist, as the initial empty document is committed through
1034 // FrameLoader::Init.
1035 HistoryItem* previous_history_item = GetDocumentLoader()->GetHistoryItem();
1036
1037 // Check if the CSP of the response should block the new document from
1038 // committing before unloading the current document. This will allow to report
1039 // violations and display console messages properly.
1040 ContentSecurityPolicy* content_security_policy = CreateCSP(
1041 navigation_params->url, navigation_params->response.ToResourceResponse(),
1042 navigation_params->origin_policy, last_origin_window_csp_.Release(),
1043 commit_reason);
1044
1045 for (auto& csp : navigation_params->forced_content_security_policies) {
1046 content_security_policy->AddPolicyFromHeaderValue(
1047 csp, network::mojom::ContentSecurityPolicyType::kEnforce,
1048 network::mojom::ContentSecurityPolicySource::kHTTP);
1049 }
1050
1051 // The navigation to the initial empty document is committed directly by Blink
1052 // and doesn't have a policy container, so we keep the frame's policy
1053 // container (which was inherited by the parent/opener) in that case.
1054 if (navigation_params->policy_container) {
1055 frame_->SetPolicyContainer(PolicyContainer::CreateFromWebPolicyContainer(
1056 std::move(navigation_params->policy_container)));
1057 }
1058
1059 base::Optional<Document::UnloadEventTiming> unload_timing;
1060 FrameSwapScope frame_swap_scope(frame_owner);
1061 {
1062 base::AutoReset<bool> scoped_committing(&committing_navigation_, true);
1063
1064 progress_tracker_->ProgressStarted();
1065 // In DocumentLoader, the matching DidCommitLoad messages are only called
1066 // for kRegular commits. Skip them here, too, to ensure we match
1067 // start/commit message pairs.
1068 if (commit_reason == CommitReason::kRegular) {
1069 frame_->GetFrameScheduler()->DidStartProvisionalLoad(
1070 frame_->IsMainFrame());
1071 probe::DidStartProvisionalLoad(frame_);
1072 }
1073
1074 DCHECK(Client()->HasWebView());
1075 scoped_refptr<SecurityOrigin> security_origin =
1076 SecurityOrigin::Create(navigation_params->url);
1077 if (!DetachDocument(security_origin.get(), &unload_timing))
1078 return;
1079 }
1080
1081 tls_version_warning_origins_.clear();
1082
1083 if (!DocumentLoader::WillLoadUrlAsEmpty(navigation_params->url))
1084 empty_document_status_ = EmptyDocumentStatus::kNonEmpty;
1085
1086 // TODO(dgozman): navigation type should probably be passed by the caller.
1087 // It seems incorrect to pass |false| for |have_event| and then use
1088 // determined navigation type to update resource request.
1089 WebNavigationType navigation_type = DetermineNavigationType(
1090 navigation_params->frame_load_type,
1091 !navigation_params->http_body.IsNull(), false /* have_event */);
1092
1093 // TODO(dgozman): get rid of provisional document loader and most of the code
1094 // below. We should probably call DocumentLoader::CommitNavigation directly.
1095 DocumentLoader* new_document_loader = Client()->CreateDocumentLoader(
1096 frame_, navigation_type, content_security_policy,
1097 std::move(navigation_params), std::move(extra_data));
1098
1099 CommitDocumentLoader(new_document_loader, unload_timing,
1100 previous_history_item, commit_reason);
1101
1102 RestoreScrollPositionAndViewState();
1103
1104 TakeObjectSnapshot();
1105 }
1106
WillStartNavigation(const WebNavigationInfo & info)1107 bool FrameLoader::WillStartNavigation(const WebNavigationInfo& info) {
1108 if (!CancelProvisionalLoaderForNewNavigation())
1109 return false;
1110
1111 progress_tracker_->ProgressStarted();
1112 client_navigation_ = std::make_unique<ClientNavigationState>();
1113 client_navigation_->url = info.url_request.Url();
1114 frame_->GetFrameScheduler()->DidStartProvisionalLoad(frame_->IsMainFrame());
1115 probe::DidStartProvisionalLoad(frame_);
1116 virtual_time_pauser_.PauseVirtualTime();
1117 TakeObjectSnapshot();
1118 return true;
1119 }
1120
StopAllLoaders(bool abort_client)1121 void FrameLoader::StopAllLoaders(bool abort_client) {
1122 if (!frame_->IsNavigationAllowed() ||
1123 frame_->GetDocument()->PageDismissalEventBeingDispatched() !=
1124 Document::kNoDismissal) {
1125 return;
1126 }
1127
1128 // This method could be called from within this method, e.g. through plugin
1129 // detach. Avoid infinite recursion by disabling navigations.
1130 FrameNavigationDisabler navigation_disabler(*frame_);
1131
1132 for (Frame* child = frame_->Tree().FirstChild(); child;
1133 child = child->Tree().NextSibling()) {
1134 if (auto* child_local_frame = DynamicTo<LocalFrame>(child))
1135 child_local_frame->Loader().StopAllLoaders(abort_client);
1136 }
1137
1138 frame_->GetDocument()->CancelParsing();
1139 if (document_loader_)
1140 document_loader_->StopLoading();
1141 if (abort_client)
1142 CancelClientNavigation();
1143 else
1144 ClearClientNavigation();
1145 frame_->CancelFormSubmission();
1146 DidFinishNavigation(FrameLoader::NavigationFinishState::kSuccess);
1147
1148 TakeObjectSnapshot();
1149 }
1150
DidAccessInitialDocument()1151 void FrameLoader::DidAccessInitialDocument() {
1152 if (frame_->IsMainFrame() && !has_accessed_initial_document_) {
1153 has_accessed_initial_document_ = true;
1154 // Forbid script execution to prevent re-entering V8, since this is called
1155 // from a binding security check.
1156 ScriptForbiddenScope forbid_scripts;
1157 frame_->GetLocalFrameHostRemote().DidAccessInitialDocument();
1158 }
1159 }
1160
DetachDocument(SecurityOrigin * committing_origin,base::Optional<Document::UnloadEventTiming> * timing)1161 bool FrameLoader::DetachDocument(
1162 SecurityOrigin* committing_origin,
1163 base::Optional<Document::UnloadEventTiming>* timing) {
1164 PluginScriptForbiddenScope forbid_plugin_destructor_scripting;
1165 ClientNavigationState* client_navigation = client_navigation_.get();
1166
1167 // Don't allow this frame to navigate anymore. This line is needed for
1168 // navigation triggered from children's unload handlers. Blocking navigations
1169 // triggered from this frame's unload handler is already covered in
1170 // DispatchUnloadEvent().
1171 FrameNavigationDisabler navigation_disabler(*frame_);
1172 // Don't allow any new child frames to load in this frame: attaching a new
1173 // child frame during or after detaching children results in an attached frame
1174 // on a detached DOM tree, which is bad.
1175 SubframeLoadingDisabler disabler(frame_->GetDocument());
1176 // https://html.spec.whatwg.org/C/browsing-the-web.html#unload-a-document
1177 // The ignore-opens-during-unload counter of a Document must be incremented
1178 // both when unloading itself and when unloading its descendants.
1179 IgnoreOpensDuringUnloadCountIncrementer ignore_opens_during_unload(
1180 frame_->GetDocument());
1181 if (document_loader_)
1182 DispatchUnloadEvent(committing_origin, timing);
1183 frame_->DetachChildren();
1184 // The previous calls to dispatchUnloadEvent() and detachChildren() can
1185 // execute arbitrary script via things like unload events. If the executed
1186 // script causes the current frame to be detached, we need to abandon the
1187 // current load.
1188 if (!frame_->Client())
1189 return false;
1190 // FrameNavigationDisabler should prevent another load from starting.
1191 DCHECK_EQ(client_navigation_.get(), client_navigation);
1192 // detachFromFrame() will abort XHRs that haven't completed, which can trigger
1193 // event listeners for 'abort'. These event listeners might call
1194 // window.stop(), which will in turn detach the provisional document loader.
1195 // At this point, the provisional document loader should not detach, because
1196 // then the FrameLoader would not have any attached DocumentLoaders. This is
1197 // guaranteed by FrameNavigationDisabler above.
1198 if (document_loader_)
1199 DetachDocumentLoader(document_loader_, true);
1200 // 'abort' listeners can also detach the frame.
1201 if (!frame_->Client())
1202 return false;
1203 // FrameNavigationDisabler should prevent another load from starting.
1204 DCHECK_EQ(client_navigation_.get(), client_navigation);
1205
1206 // No more events will be dispatched so detach the Document.
1207 // TODO(yoav): Should we also be nullifying domWindow's document (or
1208 // domWindow) since the doc is now detached?
1209 if (frame_->GetDocument())
1210 frame_->GetDocument()->Shutdown();
1211 document_loader_ = nullptr;
1212
1213 return true;
1214 }
1215
CommitDocumentLoader(DocumentLoader * document_loader,const base::Optional<Document::UnloadEventTiming> & unload_timing,HistoryItem * previous_history_item,CommitReason commit_reason)1216 void FrameLoader::CommitDocumentLoader(
1217 DocumentLoader* document_loader,
1218 const base::Optional<Document::UnloadEventTiming>& unload_timing,
1219 HistoryItem* previous_history_item,
1220 CommitReason commit_reason) {
1221 document_loader_ = document_loader;
1222 CHECK(document_loader_);
1223
1224 document_loader_->SetCommitReason(commit_reason);
1225
1226 virtual_time_pauser_.PauseVirtualTime();
1227 document_loader_->StartLoading();
1228 virtual_time_pauser_.UnpauseVirtualTime();
1229
1230 if (commit_reason != CommitReason::kInitialization) {
1231 // Following the call to StartLoading, the DocumentLoader state has taken
1232 // into account all redirects that happened during navigation. Its
1233 // HistoryItem can be properly updated for the commit, using the HistoryItem
1234 // of the previous Document.
1235 document_loader_->SetHistoryItemStateForCommit(
1236 previous_history_item, document_loader_->LoadType(),
1237 DocumentLoader::HistoryNavigationType::kDifferentDocument);
1238 }
1239
1240 // Update the DocumentLoadTiming with the timings from the previous document
1241 // unload event.
1242 if (unload_timing.has_value()) {
1243 document_loader_->GetTiming().SetHasSameOriginAsPreviousDocument(true);
1244 document_loader_->GetTiming().MarkUnloadEventStart(
1245 unload_timing->unload_event_start);
1246 document_loader_->GetTiming().MarkUnloadEventEnd(
1247 unload_timing->unload_event_end);
1248 }
1249
1250 TakeObjectSnapshot();
1251
1252 Client()->TransitionToCommittedForNewPage();
1253
1254 document_loader_->CommitNavigation();
1255 }
1256
RestoreScrollPositionAndViewState()1257 void FrameLoader::RestoreScrollPositionAndViewState() {
1258 if (RuntimeEnabledFeatures::ForceLoadAtTopEnabled(frame_->DomWindow()) ||
1259 !frame_->GetPage() || !GetDocumentLoader() ||
1260 !GetDocumentLoader()->GetHistoryItem() ||
1261 !GetDocumentLoader()->GetHistoryItem()->GetViewState() ||
1262 !GetDocumentLoader()->NavigationScrollAllowed()) {
1263 return;
1264 }
1265 RestoreScrollPositionAndViewState(
1266 GetDocumentLoader()->LoadType(),
1267 *GetDocumentLoader()->GetHistoryItem()->GetViewState(),
1268 GetDocumentLoader()->GetHistoryItem()->ScrollRestorationType());
1269 }
1270
RestoreScrollPositionAndViewState(WebFrameLoadType load_type,const HistoryItem::ViewState & view_state,mojom::blink::ScrollRestorationType scroll_restoration_type)1271 void FrameLoader::RestoreScrollPositionAndViewState(
1272 WebFrameLoadType load_type,
1273 const HistoryItem::ViewState& view_state,
1274 mojom::blink::ScrollRestorationType scroll_restoration_type) {
1275 LocalFrameView* view = frame_->View();
1276 if (!view || !view->LayoutViewport() || !frame_->IsAttached() ||
1277 frame_->GetDocument()->IsInitialEmptyDocument()) {
1278 return;
1279 }
1280 if (!NeedsHistoryItemRestore(load_type))
1281 return;
1282
1283 view->LayoutViewport()->SetPendingHistoryRestoreScrollOffset(
1284 view_state,
1285 scroll_restoration_type != mojom::blink::ScrollRestorationType::kManual);
1286 view->GetScrollableArea()->SetPendingHistoryRestoreScrollOffset(
1287 view_state,
1288 scroll_restoration_type != mojom::blink::ScrollRestorationType::kManual);
1289
1290 view->ScheduleAnimation();
1291 }
1292
UserAgent() const1293 String FrameLoader::UserAgent() const {
1294 String user_agent = Client()->UserAgent();
1295 probe::ApplyUserAgentOverride(probe::ToCoreProbeSink(frame_->GetDocument()),
1296 &user_agent);
1297 return user_agent;
1298 }
1299
UserAgentMetadata() const1300 base::Optional<blink::UserAgentMetadata> FrameLoader::UserAgentMetadata()
1301 const {
1302 return Client()->UserAgentMetadata();
1303 }
1304
Detach()1305 void FrameLoader::Detach() {
1306 frame_->GetDocument()->CancelParsing();
1307 DetachDocumentLoader(document_loader_);
1308 ClearClientNavigation();
1309 committing_navigation_ = false;
1310 DidFinishNavigation(FrameLoader::NavigationFinishState::kSuccess);
1311
1312 if (progress_tracker_) {
1313 progress_tracker_->Dispose();
1314 progress_tracker_.Clear();
1315 }
1316
1317 TRACE_EVENT_OBJECT_DELETED_WITH_ID("loading", "FrameLoader", this);
1318 state_ = State::kDetached;
1319 virtual_time_pauser_.UnpauseVirtualTime();
1320 }
1321
MaybeRenderFallbackContent()1322 bool FrameLoader::MaybeRenderFallbackContent() {
1323 DCHECK(frame_->Owner() && frame_->Owner()->CanRenderFallbackContent());
1324 // |client_navigation_| can be null here:
1325 // 1. We asked client to navigation through BeginNavigation();
1326 // 2. Meanwhile, another navigation has been started, e.g. to about:srcdoc.
1327 // This navigation has been processed, |client_navigation_| has been
1328 // reset, and browser process was informed about cancellation.
1329 // 3. Before the cancellation reached the browser process, it decided that
1330 // first navigation has failed and asks to commit the failed navigation.
1331 // 4. We come here, while |client_navigation_| is null.
1332 // TODO(dgozman): shouldn't we abandon the commit of navigation failure
1333 // because we've already notified the client about cancellation? This needs
1334 // to be double-checked, perhaps this is dead code.
1335 if (!client_navigation_)
1336 return false;
1337
1338 frame_->Owner()->RenderFallbackContent(frame_);
1339 ClearClientNavigation();
1340 DidFinishNavigation(FrameLoader::NavigationFinishState::kSuccess);
1341 return true;
1342 }
1343
ShouldPerformFragmentNavigation(bool is_form_submission,const String & http_method,WebFrameLoadType load_type,const KURL & url)1344 bool FrameLoader::ShouldPerformFragmentNavigation(bool is_form_submission,
1345 const String& http_method,
1346 WebFrameLoadType load_type,
1347 const KURL& url) {
1348 // We don't do this if we are submitting a form with method other than "GET",
1349 // explicitly reloading, currently displaying a frameset, or if the URL does
1350 // not have a fragment.
1351 return EqualIgnoringASCIICase(http_method, http_names::kGET) &&
1352 !IsReloadLoadType(load_type) &&
1353 load_type != WebFrameLoadType::kBackForward &&
1354 url.HasFragmentIdentifier() &&
1355 // For provisional LocalFrame, there is no real document loaded and
1356 // the initial empty document should not be considered, so there is
1357 // no way to get a same-document load in this case.
1358 !frame_->IsProvisional() &&
1359 EqualIgnoringFragmentIdentifier(frame_->GetDocument()->Url(), url)
1360 // We don't want to just scroll if a link from within a frameset is
1361 // trying to reload the frameset into _top.
1362 && !frame_->GetDocument()->IsFrameSet();
1363 }
1364
ProcessFragment(const KURL & url,WebFrameLoadType frame_load_type,LoadStartType load_start_type)1365 void FrameLoader::ProcessFragment(const KURL& url,
1366 WebFrameLoadType frame_load_type,
1367 LoadStartType load_start_type) {
1368 LocalFrameView* view = frame_->View();
1369 if (!view)
1370 return;
1371
1372 // Leaking scroll position to a cross-origin ancestor would permit the
1373 // so-called "framesniffing" attack.
1374 Frame* boundary_frame =
1375 url.HasFragmentIdentifier()
1376 ? frame_->FindUnsafeParentScrollPropagationBoundary()
1377 : nullptr;
1378
1379 // FIXME: Handle RemoteFrames
1380 if (auto* boundary_local_frame = DynamicTo<LocalFrame>(boundary_frame))
1381 boundary_local_frame->View()->SetSafeToPropagateScrollToParent(false);
1382
1383 const bool is_same_document_navigation =
1384 load_start_type == kNavigationWithinSameDocument;
1385
1386 // Pages can opt-in to manual scroll restoration so the page will handle
1387 // restoring the past scroll offset during a history navigation. In these
1388 // cases we assume the scroll was restored from history (by the page).
1389 const bool uses_manual_scroll_restoration =
1390 frame_load_type == WebFrameLoadType::kBackForward &&
1391 GetDocumentLoader()->GetHistoryItem() &&
1392 GetDocumentLoader()->GetHistoryItem()->ScrollRestorationType() ==
1393 mojom::blink::ScrollRestorationType::kManual;
1394
1395 // If we restored a scroll position from history, we shouldn't clobber it
1396 // with the fragment.
1397 const bool will_restore_scroll_from_history =
1398 GetDocumentLoader()->GetInitialScrollState().did_restore_from_history ||
1399 uses_manual_scroll_restoration;
1400
1401 // Scrolling at load can be blocked by document policy (or the equivalent
1402 // ForceLoadAtTop REF currently in origin trial). This policy applies only to
1403 // cross-document navigations.
1404 const bool blocked_by_policy =
1405 !is_same_document_navigation &&
1406 (RuntimeEnabledFeatures::ForceLoadAtTopEnabled(frame_->DomWindow()) ||
1407 !GetDocumentLoader()->NavigationScrollAllowed());
1408
1409 // We should avoid scrolling the fragment if it would clobber a history
1410 // restored scroll state but still allow it on same document navigations
1411 // after (i.e. if we navigate back and restore the scroll position, the user
1412 // should still be able to click on a same-document fragment link and have it
1413 // jump to the anchor).
1414 const bool is_same_document_non_history_nav =
1415 is_same_document_navigation && !IsBackForwardLoadType(frame_load_type);
1416
1417 const bool block_fragment_scroll =
1418 blocked_by_policy ||
1419 (will_restore_scroll_from_history && !is_same_document_non_history_nav);
1420
1421 view->ProcessUrlFragment(url, is_same_document_navigation,
1422 !block_fragment_scroll);
1423
1424 if (auto* boundary_local_frame = DynamicTo<LocalFrame>(boundary_frame))
1425 boundary_local_frame->View()->SetSafeToPropagateScrollToParent(true);
1426 }
1427
ShouldClose(bool is_reload)1428 bool FrameLoader::ShouldClose(bool is_reload) {
1429 Page* page = frame_->GetPage();
1430 if (!page || !page->GetChromeClient().CanOpenBeforeUnloadConfirmPanel())
1431 return true;
1432
1433 HeapVector<Member<LocalFrame>> descendant_frames;
1434 for (Frame* child = frame_->Tree().FirstChild(); child;
1435 child = child->Tree().TraverseNext(frame_)) {
1436 // FIXME: There is not yet any way to dispatch events to out-of-process
1437 // frames.
1438 if (auto* child_local_frame = DynamicTo<LocalFrame>(child))
1439 descendant_frames.push_back(child_local_frame);
1440 }
1441
1442 {
1443 FrameNavigationDisabler navigation_disabler(*frame_);
1444 bool did_allow_navigation = false;
1445
1446 // https://html.spec.whatwg.org/C/browsing-the-web.html#prompt-to-unload-a-document
1447
1448 // First deal with this frame.
1449 IgnoreOpensDuringUnloadCountIncrementer ignore_opens_during_unload(
1450 frame_->GetDocument());
1451 if (!frame_->GetDocument()->DispatchBeforeUnloadEvent(
1452 &page->GetChromeClient(), is_reload, did_allow_navigation))
1453 return false;
1454
1455 // Then deal with descendent frames.
1456 for (Member<LocalFrame>& descendant_frame : descendant_frames) {
1457 if (!descendant_frame->Tree().IsDescendantOf(frame_))
1458 continue;
1459
1460 // There is some confusion in the spec around what counters should be
1461 // incremented for a descendant browsing context:
1462 // https://github.com/whatwg/html/issues/3899
1463 //
1464 // Here for implementation ease, we use the current spec behavior, which
1465 // is to increment only the counter of the Document on which this is
1466 // called, and that of the Document we are firing the beforeunload event
1467 // on -- not any intermediate Documents that may be the parent of the
1468 // frame being unloaded but is not root Document.
1469 IgnoreOpensDuringUnloadCountIncrementer
1470 ignore_opens_during_unload_descendant(
1471 descendant_frame->GetDocument());
1472 if (!descendant_frame->GetDocument()->DispatchBeforeUnloadEvent(
1473 &page->GetChromeClient(), is_reload, did_allow_navigation))
1474 return false;
1475 }
1476 }
1477
1478 // Now that none of the unloading frames canceled the BeforeUnload, tell each
1479 // of them so they can advance to the appropriate load state.
1480 frame_->GetDocument()->BeforeUnloadDoneWillUnload();
1481 for (Member<LocalFrame>& descendant_frame : descendant_frames) {
1482 if (!descendant_frame->Tree().IsDescendantOf(frame_))
1483 continue;
1484 descendant_frame->GetDocument()->BeforeUnloadDoneWillUnload();
1485 }
1486
1487 return true;
1488 }
1489
DidDropNavigation()1490 void FrameLoader::DidDropNavigation() {
1491 if (!client_navigation_)
1492 return;
1493 // TODO(dgozman): should we ClearClientNavigation instead and not
1494 // notify the client in response to its own call?
1495 CancelClientNavigation();
1496 DidFinishNavigation(FrameLoader::NavigationFinishState::kSuccess);
1497
1498 // Forcibly instantiate WindowProxy for initial frame document.
1499 // This is only required when frame navigation is aborted, e.g. due to
1500 // mixed content.
1501 // TODO(lushnikov): this should be done in Init for initial empty doc, but
1502 // that breaks extensions abusing SetForceMainWorldInitialization setting
1503 // and relying on the number of created window proxies.
1504 Settings* settings = frame_->GetSettings();
1505 if (settings && settings->GetForceMainWorldInitialization()) {
1506 // Forcibly instantiate WindowProxy.
1507 frame_->DomWindow()->GetScriptController().WindowProxy(
1508 DOMWrapperWorld::MainWorld());
1509 }
1510 }
1511
CancelProvisionalLoaderForNewNavigation()1512 bool FrameLoader::CancelProvisionalLoaderForNewNavigation() {
1513 // This seems to correspond to step 9 of the specification:
1514 // "9. Abort the active document of browsingContext."
1515 // https://html.spec.whatwg.org/C/#navigate
1516 frame_->GetDocument()->Abort();
1517 // document.onreadystatechange can fire in Abort(), which can:
1518 // 1) Detach this frame.
1519 // 2) Stop the provisional DocumentLoader (i.e window.stop()).
1520 if (!frame_->GetPage())
1521 return false;
1522
1523 // For client navigations, don't send failure callbacks when simply
1524 // replacing client navigation with a DocumentLoader.
1525 ClearClientNavigation();
1526
1527 // Cancel pending form submissions so they don't take precedence over this.
1528 frame_->CancelFormSubmission();
1529
1530 return true;
1531 }
1532
ClearClientNavigation()1533 void FrameLoader::ClearClientNavigation() {
1534 if (!client_navigation_)
1535 return;
1536 client_navigation_.reset();
1537 probe::DidFailProvisionalLoad(frame_);
1538 virtual_time_pauser_.UnpauseVirtualTime();
1539 }
1540
CancelClientNavigation()1541 void FrameLoader::CancelClientNavigation() {
1542 if (!client_navigation_)
1543 return;
1544 ResourceError error = ResourceError::CancelledError(client_navigation_->url);
1545 ClearClientNavigation();
1546 if (WebPluginContainerImpl* plugin = frame_->GetWebPluginContainer())
1547 plugin->DidFailLoading(error);
1548 Client()->AbortClientNavigation();
1549 }
1550
DispatchDocumentElementAvailable()1551 void FrameLoader::DispatchDocumentElementAvailable() {
1552 ScriptForbiddenScope forbid_scripts;
1553
1554 // Notify the browser about non-blank documents loading in the top frame.
1555 KURL url = frame_->GetDocument()->Url();
1556 if (url.IsValid() && !url.IsAboutBlankURL()) {
1557 if (frame_->IsMainFrame()) {
1558 // For now, don't remember plugin zoom values. We don't want to mix them
1559 // with normal web content (i.e. a fixed layout plugin would usually want
1560 // them different).
1561 frame_->GetLocalFrameHostRemote().DocumentAvailableInMainFrame(
1562 frame_->GetDocument()->IsPluginDocument());
1563 }
1564 }
1565
1566 Client()->DocumentElementAvailable();
1567 }
1568
RunScriptsAtDocumentElementAvailable()1569 void FrameLoader::RunScriptsAtDocumentElementAvailable() {
1570 Client()->RunScriptsAtDocumentElementAvailable();
1571 // The frame might be detached at this point.
1572 }
1573
ForceSandboxFlags(network::mojom::blink::WebSandboxFlags flags)1574 void FrameLoader::ForceSandboxFlags(
1575 network::mojom::blink::WebSandboxFlags flags) {
1576 forced_sandbox_flags_ |= flags;
1577 }
1578
DispatchDidClearDocumentOfWindowObject()1579 void FrameLoader::DispatchDidClearDocumentOfWindowObject() {
1580 if (state_ == State::kUninitialized)
1581 return;
1582
1583 Settings* settings = frame_->GetSettings();
1584 LocalDOMWindow* window = frame_->DomWindow();
1585 if (settings && settings->GetForceMainWorldInitialization()) {
1586 // Forcibly instantiate WindowProxy, even if script is disabled.
1587 window->GetScriptController().WindowProxy(DOMWrapperWorld::MainWorld());
1588 }
1589 probe::DidClearDocumentOfWindowObject(frame_);
1590 if (!window->CanExecuteScripts(kNotAboutToExecuteScript))
1591 return;
1592
1593 if (dispatching_did_clear_window_object_in_main_world_)
1594 return;
1595 base::AutoReset<bool> in_did_clear_window_object(
1596 &dispatching_did_clear_window_object_in_main_world_, true);
1597 // We just cleared the document, not the entire window object, but for the
1598 // embedder that's close enough.
1599 Client()->DispatchDidClearWindowObjectInMainWorld();
1600 }
1601
DispatchDidClearWindowObjectInMainWorld()1602 void FrameLoader::DispatchDidClearWindowObjectInMainWorld() {
1603 if (!frame_->DomWindow()->CanExecuteScripts(kNotAboutToExecuteScript))
1604 return;
1605
1606 if (dispatching_did_clear_window_object_in_main_world_)
1607 return;
1608 base::AutoReset<bool> in_did_clear_window_object(
1609 &dispatching_did_clear_window_object_in_main_world_, true);
1610 Client()->DispatchDidClearWindowObjectInMainWorld();
1611 }
1612
1613 network::mojom::blink::WebSandboxFlags
PendingEffectiveSandboxFlags() const1614 FrameLoader::PendingEffectiveSandboxFlags() const {
1615 network::mojom::blink::WebSandboxFlags flags = forced_sandbox_flags_;
1616 if (FrameOwner* frame_owner = frame_->Owner())
1617 flags |= frame_owner->GetFramePolicy().sandbox_flags;
1618 return flags;
1619 }
1620
ModifyRequestForCSP(ResourceRequest & resource_request,const FetchClientSettingsObject * fetch_client_settings_object,LocalDOMWindow * window_for_logging,mojom::RequestContextFrameType frame_type) const1621 void FrameLoader::ModifyRequestForCSP(
1622 ResourceRequest& resource_request,
1623 const FetchClientSettingsObject* fetch_client_settings_object,
1624 LocalDOMWindow* window_for_logging,
1625 mojom::RequestContextFrameType frame_type) const {
1626 if (!base::FeatureList::IsEnabled(network::features::kOutOfBlinkCSPEE) &&
1627 !RequiredCSP().IsEmpty()) {
1628 DCHECK(
1629 ContentSecurityPolicy::IsValidCSPAttr(RequiredCSP().GetString(), ""));
1630 resource_request.SetHttpHeaderField(http_names::kSecRequiredCSP,
1631 RequiredCSP());
1632 }
1633
1634 // Tack an 'Upgrade-Insecure-Requests' header to outgoing navigational
1635 // requests, as described in
1636 // https://w3c.github.io/webappsec-upgrade-insecure-requests/#feature-detect
1637 if (frame_type != mojom::RequestContextFrameType::kNone) {
1638 // Early return if the request has already been upgraded.
1639 if (!resource_request.HttpHeaderField(http_names::kUpgradeInsecureRequests)
1640 .IsNull()) {
1641 return;
1642 }
1643
1644 resource_request.SetHttpHeaderField(http_names::kUpgradeInsecureRequests,
1645 "1");
1646 }
1647
1648 MixedContentChecker::UpgradeInsecureRequest(
1649 resource_request, fetch_client_settings_object, window_for_logging,
1650 frame_type, frame_->GetContentSettingsClient());
1651 }
1652
ReportLegacyTLSVersion(const KURL & url,bool is_subresource,bool is_ad_resource)1653 void FrameLoader::ReportLegacyTLSVersion(const KURL& url,
1654 bool is_subresource,
1655 bool is_ad_resource) {
1656 document_loader_->GetUseCounterHelper().Count(
1657 is_subresource
1658 ? WebFeature::kLegacyTLSVersionInSubresource
1659 : (frame_->Tree().Parent()
1660 ? WebFeature::kLegacyTLSVersionInSubframeMainResource
1661 : WebFeature::kLegacyTLSVersionInMainFrameResource),
1662 frame_.Get());
1663
1664 // For non-main-frame loads, we have to use the main frame's document for
1665 // the UKM recorder and source ID.
1666 auto& root = frame_->LocalFrameRoot();
1667 ukm::builders::Net_LegacyTLSVersion(root.GetDocument()->UkmSourceID())
1668 .SetIsMainFrame(frame_->IsMainFrame())
1669 .SetIsSubresource(is_subresource)
1670 .SetIsAdResource(is_ad_resource)
1671 .Record(root.GetDocument()->UkmRecorder());
1672
1673 // Web tests use an outdated server on macOS. See https://crbug.com/936515.
1674 #if defined(OS_MAC)
1675 if (WebTestSupport::IsRunningWebTest())
1676 return;
1677 #endif
1678
1679 String origin = SecurityOrigin::Create(url)->ToString();
1680 // To prevent log spam, only log the message once per origin.
1681 if (tls_version_warning_origins_.Contains(origin))
1682 return;
1683
1684 // After |kMaxSecurityWarningMessages| warnings, stop printing messages to the
1685 // console. At exactly |kMaxSecurityWarningMessages| warnings, print a message
1686 // that additional resources on the page use legacy certificates without
1687 // specifying which exact resources. Before |kMaxSecurityWarningMessages|
1688 // messages, print the exact resource URL in the message to help the developer
1689 // pinpoint the problematic resources.
1690 const size_t kMaxSecurityWarningMessages = 10;
1691 size_t num_warnings = tls_version_warning_origins_.size();
1692 if (num_warnings > kMaxSecurityWarningMessages)
1693 return;
1694
1695 String console_message;
1696 if (num_warnings == kMaxSecurityWarningMessages) {
1697 console_message =
1698 "Additional resources on this page were loaded with TLS 1.0 or TLS "
1699 "1.1, which are deprecated and will be disabled in the future. Once "
1700 "disabled, users will be prevented from loading these resources. "
1701 "Servers should enable TLS 1.2 or later. See "
1702 "https://www.chromestatus.com/feature/5654791610957824 for more "
1703 "information.";
1704 } else {
1705 console_message =
1706 "The connection used to load resources from " + origin +
1707 " used TLS 1.0 or TLS "
1708 "1.1, which are deprecated and will be disabled in the future. Once "
1709 "disabled, users will be prevented from loading these resources. The "
1710 "server should enable TLS 1.2 or later. See "
1711 "https://www.chromestatus.com/feature/5654791610957824 for more "
1712 "information.";
1713 }
1714 tls_version_warning_origins_.insert(origin);
1715 // To avoid spamming the console, use verbose message level for subframe
1716 // resources, and only use the warning level for main-frame resources.
1717 frame_->Console().AddMessage(MakeGarbageCollected<ConsoleMessage>(
1718 mojom::ConsoleMessageSource::kOther,
1719 frame_->IsMainFrame() ? mojom::ConsoleMessageLevel::kWarning
1720 : mojom::ConsoleMessageLevel::kVerbose,
1721 console_message));
1722 }
1723
RecordLatestRequiredCSP()1724 void FrameLoader::RecordLatestRequiredCSP() {
1725 required_csp_ =
1726 frame_->Owner() ? frame_->Owner()->RequiredCsp() : g_null_atom;
1727 }
1728
ToTracedValue() const1729 std::unique_ptr<TracedValue> FrameLoader::ToTracedValue() const {
1730 auto traced_value = std::make_unique<TracedValue>();
1731 traced_value->BeginDictionary("frame");
1732 traced_value->SetString("id_ref", IdentifiersFactory::FrameId(frame_.Get()));
1733 traced_value->EndDictionary();
1734 traced_value->SetBoolean("isLoadingMainFrame", frame_->IsMainFrame());
1735 traced_value->SetString(
1736 "documentLoaderURL",
1737 document_loader_ ? document_loader_->Url().GetString() : String());
1738 return traced_value;
1739 }
1740
TakeObjectSnapshot() const1741 inline void FrameLoader::TakeObjectSnapshot() const {
1742 if (state_ == State::kDetached) {
1743 // We already logged TRACE_EVENT_OBJECT_DELETED_WITH_ID in detach().
1744 return;
1745 }
1746 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID("loading", "FrameLoader", this,
1747 ToTracedValue());
1748 }
1749
CreateCSPForInitialEmptyDocument() const1750 ContentSecurityPolicy* FrameLoader::CreateCSPForInitialEmptyDocument() const {
1751 ContentSecurityPolicy* csp = MakeGarbageCollected<ContentSecurityPolicy>();
1752
1753 Frame* owner_frame = frame_->Parent() ? frame_->Parent() : frame_->Opener();
1754 if (owner_frame) {
1755 ContentSecurityPolicy* owner_csp =
1756 owner_frame->GetSecurityContext()->GetContentSecurityPolicy();
1757 csp->CopyStateFrom(owner_csp);
1758 }
1759
1760 return csp;
1761 }
1762
CreateCSP(const KURL & url,const ResourceResponse & response,const base::Optional<WebOriginPolicy> & origin_policy,ContentSecurityPolicy * initiator_csp,CommitReason commit_reason)1763 ContentSecurityPolicy* FrameLoader::CreateCSP(
1764 const KURL& url,
1765 const ResourceResponse& response,
1766 const base::Optional<WebOriginPolicy>& origin_policy,
1767 ContentSecurityPolicy* initiator_csp,
1768 CommitReason commit_reason) {
1769 // about:srcdoc inherits CSP from its parent.
1770 if (url.IsAboutSrcdocURL()) {
1771 ContentSecurityPolicy* csp = MakeGarbageCollected<ContentSecurityPolicy>();
1772 csp->CopyStateFrom(frame_->Tree()
1773 .Parent()
1774 ->GetSecurityContext()
1775 ->GetContentSecurityPolicy());
1776 return csp;
1777 }
1778
1779 // Documents constructed by XSLTProcessor inherit CSP from the previous
1780 // Document.
1781 if (commit_reason == CommitReason::kXSLT) {
1782 ContentSecurityPolicy* csp = MakeGarbageCollected<ContentSecurityPolicy>();
1783 csp->CopyStateFrom(frame_->DomWindow()->GetContentSecurityPolicy());
1784 return csp;
1785 }
1786
1787 // Documents with a local-scheme inherits CSP from their navigation initiator.
1788 bool is_local_scheme = url.IsEmpty() || url.ProtocolIsAbout() ||
1789 url.ProtocolIsData() || url.ProtocolIs("blob") ||
1790 url.ProtocolIs("filesystem");
1791 if (is_local_scheme) {
1792 if (initiator_csp)
1793 return initiator_csp;
1794 return MakeGarbageCollected<ContentSecurityPolicy>();
1795 }
1796
1797 // In the main case (outside of the ones above), CSP(s) are NOT inherited.
1798 // Otherwise, it would allow a malicious parent/opener to block some
1799 // iframe/popup's script at a fine-grained level.
1800
1801 ContentSecurityPolicy* csp = MakeGarbageCollected<ContentSecurityPolicy>();
1802 csp->SetOverrideURLForSelf(response.CurrentRequestUrl());
1803
1804 if (frame_->GetSettings()->BypassCSP())
1805 return csp; // Empty CSP.
1806
1807 // Parse CSP from the HTTP response.
1808 csp->DidReceiveHeaders(ContentSecurityPolicyResponseHeaders(response));
1809
1810 // Retrieve CSP stored in the OriginPolicy.
1811 if (origin_policy)
1812 ApplyOriginPolicy(csp, origin_policy.value());
1813
1814 // Plugin inherits plugin's CSP from their navigation initiator.
1815 DocumentInit::Type document_type =
1816 DocumentInit::ComputeDocumentType(frame_, url, response.MimeType());
1817 if (document_type == DocumentInit::Type::kPlugin) {
1818 Frame* owner_frame = frame_->Parent() ? frame_->Parent() : frame_->Opener();
1819 ContentSecurityPolicy* owner_csp =
1820 owner_frame
1821 ? owner_frame->GetSecurityContext()->GetContentSecurityPolicy()
1822 : nullptr;
1823 // TODO(andypaicu): This should always inherit the origin document's plugin
1824 // types but because this could be a OOPIF document it might not have
1825 // access. In this situation we fallback on using the parent/opener:
1826 ContentSecurityPolicy* inherited_csp =
1827 initiator_csp ? initiator_csp : owner_csp;
1828 if (inherited_csp)
1829 csp->CopyPluginTypesFrom(inherited_csp);
1830 }
1831
1832 // When the embedder used the 'required-csp', its embeddee must either:
1833 // 1) Use the 'allow-csp' header for opting in inheriting them.
1834 // 2) Ensure its own CSP subsume them, or it will be blocked.
1835 //
1836 // See:
1837 // - https://w3c.github.io/webappsec-cspee/#required-csp-header
1838 // - https://w3c.github.io/webappsec-cspee/#allow-csp-from-header
1839 if (RequiredCSP().IsEmpty() ||
1840 base::FeatureList::IsEnabled(network::features::kOutOfBlinkCSPEE)) {
1841 return csp;
1842 }
1843
1844 const SecurityOrigin* parent_security_origin =
1845 frame_->Tree().Parent()->GetSecurityContext()->GetSecurityOrigin();
1846 if (parent_security_origin &&
1847 ContentSecurityPolicy::ShouldEnforceEmbeddersPolicy(
1848 response, parent_security_origin)) {
1849 csp->AddPolicyFromHeaderValue(
1850 RequiredCSP(), network::mojom::ContentSecurityPolicyType::kEnforce,
1851 network::mojom::ContentSecurityPolicySource::kHTTP);
1852 } else {
1853 auto* required_csp = MakeGarbageCollected<ContentSecurityPolicy>();
1854 required_csp->AddPolicyFromHeaderValue(
1855 RequiredCSP(), network::mojom::ContentSecurityPolicyType::kEnforce,
1856 network::mojom::ContentSecurityPolicySource::kHTTP);
1857 if (!required_csp->Subsumes(*csp))
1858 return nullptr; // Document blocked.
1859 }
1860
1861 return csp;
1862 }
1863
1864 } // namespace blink
1865