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