1/*
2 * Copyright (C) 2006-2018 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
5 * Copyright (C) 2008 Alp Toker <alp@atoker.com>
6 * Copyright (C) Research In Motion Limited 2009. All rights reserved.
7 * Copyright (C) 2011 Kris Jordan <krisjordan@gmail.com>
8 * Copyright (C) 2011 Google Inc. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
20 * its contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
27 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35#include "config.h"
36#include "FrameLoader.h"
37
38#include "AXObjectCache.h"
39#include "ApplicationCacheHost.h"
40#include "BackForwardController.h"
41#include "BeforeUnloadEvent.h"
42#include "CachedPage.h"
43#include "CachedResourceLoader.h"
44#include "Chrome.h"
45#include "ChromeClient.h"
46#include "CommonVM.h"
47#include "ContentFilter.h"
48#include "ContentRuleListResults.h"
49#include "ContentSecurityPolicy.h"
50#include "CustomHeaderFields.h"
51#include "DOMWindow.h"
52#include "DatabaseManager.h"
53#include "DiagnosticLoggingClient.h"
54#include "DiagnosticLoggingKeys.h"
55#include "Document.h"
56#include "DocumentLoader.h"
57#include "Editor.h"
58#include "EditorClient.h"
59#include "Element.h"
60#include "Event.h"
61#include "EventHandler.h"
62#include "EventNames.h"
63#include "FloatRect.h"
64#include "FormState.h"
65#include "FormSubmission.h"
66#include "Frame.h"
67#include "FrameLoadRequest.h"
68#include "FrameLoaderClient.h"
69#include "FrameNetworkingContext.h"
70#include "FrameTree.h"
71#include "FrameView.h"
72#include "GCController.h"
73#include "HTMLFormElement.h"
74#include "HTMLInputElement.h"
75#include "HTMLNames.h"
76#include "HTMLObjectElement.h"
77#include "HTMLParserIdioms.h"
78#include "HTTPHeaderNames.h"
79#include "HTTPParsers.h"
80#include "HistoryController.h"
81#include "HistoryItem.h"
82#include "IgnoreOpensDuringUnloadCountIncrementer.h"
83#include "InspectorController.h"
84#include "InspectorInstrumentation.h"
85#include "LinkLoader.h"
86#include "LoadTiming.h"
87#include "LoaderStrategy.h"
88#include "Logging.h"
89#include "MemoryCache.h"
90#include "MemoryRelease.h"
91#include "NavigationDisabler.h"
92#include "NavigationScheduler.h"
93#include "Node.h"
94#include "Page.h"
95#include "PageCache.h"
96#include "PageTransitionEvent.h"
97#include "PerformanceLogging.h"
98#include "PlatformStrategies.h"
99#include "PluginData.h"
100#include "PluginDocument.h"
101#include "PolicyChecker.h"
102#include "ProgressTracker.h"
103#include "ResourceHandle.h"
104#include "ResourceLoadInfo.h"
105#include "ResourceLoadObserver.h"
106#include "ResourceRequest.h"
107#include "SVGDocument.h"
108#include "SVGLocatable.h"
109#include "SVGNames.h"
110#include "SVGViewElement.h"
111#include "SVGViewSpec.h"
112#include "ScriptController.h"
113#include "ScriptSourceCode.h"
114#include "ScrollAnimator.h"
115#include "SecurityOrigin.h"
116#include "SecurityPolicy.h"
117#include "SegmentedString.h"
118#include "SerializedScriptValue.h"
119#include "Settings.h"
120#include "ShouldTreatAsContinuingLoad.h"
121#include "SubframeLoader.h"
122#include "SubresourceLoader.h"
123#include "TextResourceDecoder.h"
124#include "UserContentController.h"
125#include "UserGestureIndicator.h"
126#include "WindowFeatures.h"
127#include "XMLDocumentParser.h"
128#include <dom/ScriptDisallowedScope.h>
129#include <wtf/CompletionHandler.h>
130#include <wtf/URL.h>
131#include <wtf/Ref.h>
132#include <wtf/SetForScope.h>
133#include <wtf/StdLibExtras.h>
134#include <wtf/SystemTracing.h>
135#include <wtf/text/CString.h>
136#include <wtf/text/WTFString.h>
137
138#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
139#include "Archive.h"
140#endif
141
142#if ENABLE(DATA_DETECTION)
143#include "DataDetection.h"
144#endif
145
146#if PLATFORM(IOS_FAMILY)
147#include "DocumentType.h"
148#include "ResourceLoader.h"
149#include "RuntimeApplicationChecks.h"
150#endif
151
152#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - FrameLoader::" fmt, this, ##__VA_ARGS__)
153
154namespace WebCore {
155
156using namespace HTMLNames;
157using namespace SVGNames;
158
159static const char defaultAcceptHeader[] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
160
161bool isBackForwardLoadType(FrameLoadType type)
162{
163 switch (type) {
164 case FrameLoadType::Standard:
165 case FrameLoadType::Reload:
166 case FrameLoadType::ReloadFromOrigin:
167 case FrameLoadType::ReloadExpiredOnly:
168 case FrameLoadType::Same:
169 case FrameLoadType::RedirectWithLockedBackForwardList:
170 case FrameLoadType::Replace:
171 return false;
172 case FrameLoadType::Back:
173 case FrameLoadType::Forward:
174 case FrameLoadType::IndexedBackForward:
175 return true;
176 }
177 ASSERT_NOT_REACHED();
178 return false;
179}
180
181bool isReload(FrameLoadType type)
182{
183 switch (type) {
184 case FrameLoadType::Reload:
185 case FrameLoadType::ReloadFromOrigin:
186 case FrameLoadType::ReloadExpiredOnly:
187 return true;
188 case FrameLoadType::Standard:
189 case FrameLoadType::Same:
190 case FrameLoadType::RedirectWithLockedBackForwardList:
191 case FrameLoadType::Replace:
192 case FrameLoadType::Back:
193 case FrameLoadType::Forward:
194 case FrameLoadType::IndexedBackForward:
195 return false;
196 }
197 ASSERT_NOT_REACHED();
198 return false;
199}
200
201// This is not in the FrameLoader class to emphasize that it does not depend on
202// private FrameLoader data, and to avoid increasing the number of public functions
203// with access to private data. Since only this .cpp file needs it, making it
204// non-member lets us exclude it from the header file, thus keeping FrameLoader.h's
205// API simpler.
206//
207static bool isDocumentSandboxed(Frame& frame, SandboxFlags mask)
208{
209 return frame.document() && frame.document()->isSandboxed(mask);
210}
211
212struct ForbidPromptsScope {
213 ForbidPromptsScope(Page* page) : m_page(page)
214 {
215 if (!m_page)
216 return;
217 m_page->forbidPrompts();
218 }
219
220 ~ForbidPromptsScope()
221 {
222 if (!m_page)
223 return;
224 m_page->allowPrompts();
225 }
226
227 Page* m_page;
228};
229
230class FrameLoader::FrameProgressTracker {
231 WTF_MAKE_FAST_ALLOCATED;
232public:
233 explicit FrameProgressTracker(Frame& frame)
234 : m_frame(frame)
235 , m_inProgress(false)
236 {
237 }
238
239 ~FrameProgressTracker()
240 {
241 if (m_inProgress && m_frame.page())
242 m_frame.page()->progress().progressCompleted(m_frame);
243 }
244
245 void progressStarted()
246 {
247 ASSERT(m_frame.page());
248 if (!m_inProgress)
249 m_frame.page()->progress().progressStarted(m_frame);
250 m_inProgress = true;
251 }
252
253 void progressCompleted()
254 {
255 ASSERT(m_inProgress);
256 ASSERT(m_frame.page());
257 m_inProgress = false;
258 m_frame.page()->progress().progressCompleted(m_frame);
259
260 if (auto pageID = m_frame.loader().client().pageID())
261 platformStrategies()->loaderStrategy()->pageLoadCompleted(pageID.value());
262 }
263
264private:
265 Frame& m_frame;
266 bool m_inProgress;
267};
268
269FrameLoader::FrameLoader(Frame& frame, FrameLoaderClient& client)
270 : m_frame(frame)
271 , m_client(client)
272 , m_policyChecker(std::make_unique<PolicyChecker>(frame))
273 , m_history(std::make_unique<HistoryController>(frame))
274 , m_notifier(frame)
275 , m_subframeLoader(std::make_unique<SubframeLoader>(frame))
276 , m_mixedContentChecker(frame)
277 , m_state(FrameStateProvisional)
278 , m_loadType(FrameLoadType::Standard)
279 , m_quickRedirectComing(false)
280 , m_sentRedirectNotification(false)
281 , m_inStopAllLoaders(false)
282 , m_isExecutingJavaScriptFormAction(false)
283 , m_didCallImplicitClose(true)
284 , m_wasUnloadEventEmitted(false)
285 , m_isComplete(false)
286 , m_needsClear(false)
287 , m_checkTimer(*this, &FrameLoader::checkTimerFired)
288 , m_shouldCallCheckCompleted(false)
289 , m_shouldCallCheckLoadComplete(false)
290 , m_opener(nullptr)
291 , m_loadingFromCachedPage(false)
292 , m_currentNavigationHasShownBeforeUnloadConfirmPanel(false)
293 , m_loadsSynchronously(false)
294 , m_forcedSandboxFlags(SandboxNone)
295{
296}
297
298FrameLoader::~FrameLoader()
299{
300 setOpener(nullptr);
301
302 for (auto& frame : m_openedFrames)
303 frame->loader().m_opener = nullptr;
304
305 m_client.frameLoaderDestroyed();
306
307 if (m_networkingContext)
308 m_networkingContext->invalidate();
309}
310
311void FrameLoader::init()
312{
313 // This somewhat odd set of steps gives the frame an initial empty document.
314 setPolicyDocumentLoader(m_client.createDocumentLoader(ResourceRequest(URL({ }, emptyString())), SubstituteData()).ptr());
315 setProvisionalDocumentLoader(m_policyDocumentLoader.get());
316 m_provisionalDocumentLoader->startLoadingMainResource();
317
318 Ref<Frame> protect(m_frame);
319 m_frame.document()->cancelParsing();
320 m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocument);
321
322 m_networkingContext = m_client.createNetworkingContext();
323 m_progressTracker = std::make_unique<FrameProgressTracker>(m_frame);
324}
325
326void FrameLoader::initForSynthesizedDocument(const URL&)
327{
328 // FIXME: We need to initialize the document URL to the specified URL. Currently the URL is empty and hence
329 // FrameLoader::checkCompleted() will overwrite the URL of the document to be activeDocumentLoader()->documentURL().
330
331 auto loader = m_client.createDocumentLoader(ResourceRequest(URL({ }, emptyString())), SubstituteData());
332 loader->attachToFrame(m_frame);
333 loader->setResponse(ResourceResponse(URL(), "text/html"_s, 0, String()));
334 loader->setCommitted(true);
335 setDocumentLoader(loader.ptr());
336
337 m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocument);
338 m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit);
339 m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad);
340 m_client.transitionToCommittedForNewPage();
341
342 m_didCallImplicitClose = true;
343 m_isComplete = true;
344 m_state = FrameStateComplete;
345 m_needsClear = true;
346
347 m_networkingContext = m_client.createNetworkingContext();
348 m_progressTracker = std::make_unique<FrameProgressTracker>(m_frame);
349}
350
351void FrameLoader::setDefersLoading(bool defers)
352{
353 if (m_documentLoader)
354 m_documentLoader->setDefersLoading(defers);
355 if (m_provisionalDocumentLoader)
356 m_provisionalDocumentLoader->setDefersLoading(defers);
357 if (m_policyDocumentLoader)
358 m_policyDocumentLoader->setDefersLoading(defers);
359 history().setDefersLoading(defers);
360
361 if (!defers) {
362 m_frame.navigationScheduler().startTimer();
363 startCheckCompleteTimer();
364 }
365}
366
367void FrameLoader::checkContentPolicy(const ResourceResponse& response, PolicyCheckIdentifier identifier, ContentPolicyDecisionFunction&& function)
368{
369 if (!activeDocumentLoader()) {
370 // Load was cancelled
371 function(PolicyAction::Ignore, identifier);
372 return;
373 }
374
375 // FIXME: Validate the policy check identifier.
376 client().dispatchDecidePolicyForResponse(response, activeDocumentLoader()->request(), identifier, activeDocumentLoader()->downloadAttribute(), WTFMove(function));
377}
378
379void FrameLoader::changeLocation(FrameLoadRequest&& request)
380{
381 urlSelected(WTFMove(request), nullptr);
382}
383
384void FrameLoader::urlSelected(const URL& url, const String& passedTarget, Event* triggeringEvent, LockHistory lockHistory, LockBackForwardList lockBackForwardList, ShouldSendReferrer shouldSendReferrer, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, Optional<NewFrameOpenerPolicy> openerPolicy, const AtomString& downloadAttribute, const SystemPreviewInfo& systemPreviewInfo, Optional<AdClickAttribution>&& adClickAttribution)
385{
386 auto* frame = lexicalFrameFromCommonVM();
387 auto initiatedByMainFrame = frame && frame->isMainFrame() ? InitiatedByMainFrame::Yes : InitiatedByMainFrame::Unknown;
388
389 NewFrameOpenerPolicy newFrameOpenerPolicy = openerPolicy.valueOr(shouldSendReferrer == NeverSendReferrer ? NewFrameOpenerPolicy::Suppress : NewFrameOpenerPolicy::Allow);
390 urlSelected(FrameLoadRequest(*m_frame.document(), m_frame.document()->securityOrigin(), { url }, passedTarget, lockHistory, lockBackForwardList, shouldSendReferrer, AllowNavigationToInvalidURL::Yes, newFrameOpenerPolicy, shouldOpenExternalURLsPolicy, initiatedByMainFrame, DoNotReplaceDocumentIfJavaScriptURL, downloadAttribute, systemPreviewInfo), triggeringEvent, WTFMove(adClickAttribution));
391}
392
393void FrameLoader::urlSelected(FrameLoadRequest&& frameRequest, Event* triggeringEvent, Optional<AdClickAttribution>&& adClickAttribution)
394{
395 RELEASE_LOG_IF_ALLOWED("urlSelected: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
396
397 Ref<Frame> protect(m_frame);
398
399 if (m_frame.script().executeIfJavaScriptURL(frameRequest.resourceRequest().url(), frameRequest.shouldReplaceDocumentIfJavaScriptURL()))
400 return;
401
402 if (frameRequest.frameName().isEmpty())
403 frameRequest.setFrameName(m_frame.document()->baseTarget());
404
405 addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin());
406 m_frame.document()->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(frameRequest.resourceRequest(), ContentSecurityPolicy::InsecureRequestType::Navigation);
407
408 loadFrameRequest(WTFMove(frameRequest), triggeringEvent, { }, WTFMove(adClickAttribution));
409}
410
411void FrameLoader::submitForm(Ref<FormSubmission>&& submission)
412{
413 ASSERT(submission->method() == FormSubmission::Method::Post || submission->method() == FormSubmission::Method::Get);
414
415 // FIXME: Find a good spot for these.
416 ASSERT(!submission->state().sourceDocument().frame() || submission->state().sourceDocument().frame() == &m_frame);
417
418 if (!m_frame.page())
419 return;
420
421 if (submission->action().isEmpty())
422 return;
423
424 if (isDocumentSandboxed(m_frame, SandboxForms)) {
425 // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
426 m_frame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Blocked form submission to '" + submission->action().stringCenterEllipsizedToLength() + "' because the form's frame is sandboxed and the 'allow-forms' permission is not set.");
427 return;
428 }
429
430 if (WTF::protocolIsJavaScript(submission->action())) {
431 if (!m_frame.document()->contentSecurityPolicy()->allowFormAction(URL(submission->action())))
432 return;
433 m_isExecutingJavaScriptFormAction = true;
434 Ref<Frame> protect(m_frame);
435 m_frame.script().executeIfJavaScriptURL(submission->action(), DoNotReplaceDocumentIfJavaScriptURL);
436 m_isExecutingJavaScriptFormAction = false;
437 return;
438 }
439
440 Frame* targetFrame = findFrameForNavigation(submission->target(), &submission->state().sourceDocument());
441 if (!targetFrame) {
442 if (!DOMWindow::allowPopUp(m_frame) && !UserGestureIndicator::processingUserGesture())
443 return;
444
445 // FIXME: targetFrame can be null for two distinct reasons:
446 // 1. The frame was not found by name, so we should try opening a new window.
447 // 2. The frame was found, but navigating it was not allowed, e.g. by HTML5 sandbox or by origin checks.
448 // Continuing form submission makes no sense in the latter case.
449 // There is a repeat check after timer fires, so this is not a correctness issue.
450
451 targetFrame = &m_frame;
452 } else
453 submission->clearTarget();
454
455 if (!targetFrame->page())
456 return;
457
458 // FIXME: We'd like to remove this altogether and fix the multiple form submission issue another way.
459
460 // We do not want to submit more than one form from the same page, nor do we want to submit a single
461 // form more than once. This flag prevents these from happening; not sure how other browsers prevent this.
462 // The flag is reset in each time we start dispatching a new mouse or key down event, and
463 // also in setView since this part may get reused for a page from the back/forward cache.
464 // The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
465
466 // FIXME: Frame targeting is only one of the ways the submission could end up doing something other
467 // than replacing this frame's content, so this check is flawed. On the other hand, the check is hardly
468 // needed any more now that we reset m_submittedFormURL on each mouse or key down event.
469
470 if (m_frame.tree().isDescendantOf(targetFrame)) {
471 if (m_submittedFormURL == submission->requestURL())
472 return;
473 m_submittedFormURL = submission->requestURL();
474 }
475
476 submission->data().generateFiles(m_frame.document());
477 submission->setReferrer(outgoingReferrer());
478 submission->setOrigin(outgoingOrigin());
479
480 targetFrame->navigationScheduler().scheduleFormSubmission(WTFMove(submission));
481}
482
483void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy)
484{
485 if (m_frame.document() && m_frame.document()->parser())
486 m_frame.document()->parser()->stopParsing();
487
488 if (unloadEventPolicy != UnloadEventPolicyNone)
489 dispatchUnloadEvents(unloadEventPolicy);
490
491 m_isComplete = true; // to avoid calling completed() in finishedParsing()
492 m_didCallImplicitClose = true; // don't want that one either
493
494 if (m_frame.document() && m_frame.document()->parsing()) {
495 finishedParsing();
496 m_frame.document()->setParsing(false);
497 }
498
499 if (auto* document = m_frame.document()) {
500 // FIXME: HTML5 doesn't tell us to set the state to complete when aborting, but we do anyway to match legacy behavior.
501 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10537
502 document->setReadyState(Document::Complete);
503
504 // FIXME: Should the DatabaseManager watch for something like ActiveDOMObject::stop() rather than being special-cased here?
505 DatabaseManager::singleton().stopDatabases(*document, nullptr);
506 }
507
508 policyChecker().stopCheck();
509
510 // FIXME: This will cancel redirection timer, which really needs to be restarted when restoring the frame from b/f cache.
511 m_frame.navigationScheduler().cancel();
512}
513
514void FrameLoader::stop()
515{
516 // http://bugs.webkit.org/show_bug.cgi?id=10854
517 // The frame's last ref may be removed and it will be deleted by checkCompleted().
518 Ref<Frame> protect(m_frame);
519
520 if (DocumentParser* parser = m_frame.document()->parser()) {
521 parser->stopParsing();
522 parser->finish();
523 }
524}
525
526void FrameLoader::willTransitionToCommitted()
527{
528 // This function is called when a frame is still fully in place (not cached, not detached), but will be replaced.
529
530 if (m_frame.editor().hasComposition()) {
531 // The text was already present in DOM, so it's better to confirm than to cancel the composition.
532 m_frame.editor().confirmComposition();
533 if (EditorClient* editorClient = m_frame.editor().client()) {
534 editorClient->respondToChangedSelection(&m_frame);
535 editorClient->discardedComposition(&m_frame);
536 }
537 }
538}
539
540bool FrameLoader::closeURL()
541{
542 history().saveDocumentState();
543
544 Document* currentDocument = m_frame.document();
545 UnloadEventPolicy unloadEventPolicy;
546 if (m_frame.page() && m_frame.page()->chrome().client().isSVGImageChromeClient()) {
547 // If this is the SVGDocument of an SVGImage, no need to dispatch events or recalcStyle.
548 unloadEventPolicy = UnloadEventPolicyNone;
549 } else {
550 // Should only send the pagehide event here if the current document exists and has not been placed in the page cache.
551 unloadEventPolicy = currentDocument && currentDocument->pageCacheState() == Document::NotInPageCache ? UnloadEventPolicyUnloadAndPageHide : UnloadEventPolicyUnloadOnly;
552 }
553
554 stopLoading(unloadEventPolicy);
555
556 m_frame.editor().clearUndoRedoOperations();
557 return true;
558}
559
560bool FrameLoader::didOpenURL()
561{
562 if (m_frame.navigationScheduler().redirectScheduledDuringLoad()) {
563 // A redirect was scheduled before the document was created.
564 // This can happen when one frame changes another frame's location.
565 return false;
566 }
567
568 m_frame.navigationScheduler().cancel();
569 m_frame.editor().clearLastEditCommand();
570
571 m_isComplete = false;
572 m_didCallImplicitClose = false;
573
574 // If we are still in the process of initializing an empty document then
575 // its frame is not in a consistent state for rendering, so avoid setJSStatusBarText
576 // since it may cause clients to attempt to render the frame.
577 if (!m_stateMachine.creatingInitialEmptyDocument()) {
578 DOMWindow* window = m_frame.document()->domWindow();
579 window->setStatus(String());
580 window->setDefaultStatus(String());
581 }
582
583 started();
584
585 return true;
586}
587
588void FrameLoader::didExplicitOpen()
589{
590 m_isComplete = false;
591 m_didCallImplicitClose = false;
592
593 // Calling document.open counts as committing the first real document load.
594 if (!m_stateMachine.committedFirstRealDocumentLoad())
595 m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit);
596
597 m_client.dispatchDidExplicitOpen(m_frame.document() ? m_frame.document()->url() : URL());
598
599 // Prevent window.open(url) -- eg window.open("about:blank") -- from blowing away results
600 // from a subsequent window.document.open / window.document.write call.
601 // Canceling redirection here works for all cases because document.open
602 // implicitly precedes document.write.
603 m_frame.navigationScheduler().cancel();
604}
605
606
607void FrameLoader::cancelAndClear()
608{
609 m_frame.navigationScheduler().cancel();
610
611 if (!m_isComplete)
612 closeURL();
613
614 clear(m_frame.document(), false);
615 m_frame.script().updatePlatformScriptObjects();
616}
617
618static inline bool shouldClearWindowName(const Frame& frame, const Document& newDocument)
619{
620 if (!frame.isMainFrame())
621 return false;
622
623 if (frame.loader().opener())
624 return false;
625
626 return !newDocument.securityOrigin().isSameOriginAs(frame.document()->securityOrigin());
627}
628
629void FrameLoader::clear(Document* newDocument, bool clearWindowProperties, bool clearScriptObjects, bool clearFrameView)
630{
631 m_frame.editor().clear();
632
633 if (!m_needsClear)
634 return;
635 m_needsClear = false;
636
637 if (m_frame.document()->pageCacheState() != Document::InPageCache) {
638 m_frame.document()->cancelParsing();
639 m_frame.document()->stopActiveDOMObjects();
640 bool hadLivingRenderTree = m_frame.document()->hasLivingRenderTree();
641 m_frame.document()->prepareForDestruction();
642 if (hadLivingRenderTree)
643 m_frame.document()->adjustFocusedNodeOnNodeRemoval(*m_frame.document());
644 }
645
646 // Do this after detaching the document so that the unload event works.
647 if (clearWindowProperties) {
648 InspectorInstrumentation::frameWindowDiscarded(m_frame, m_frame.document()->domWindow());
649 m_frame.document()->domWindow()->resetUnlessSuspendedForDocumentSuspension();
650 m_frame.windowProxy().clearJSWindowProxiesNotMatchingDOMWindow(newDocument->domWindow(), m_frame.document()->pageCacheState() == Document::AboutToEnterPageCache);
651
652 if (shouldClearWindowName(m_frame, *newDocument))
653 m_frame.tree().setName(nullAtom());
654 }
655
656 m_frame.selection().prepareForDestruction();
657 m_frame.eventHandler().clear();
658
659 if (clearFrameView && m_frame.view())
660 m_frame.view()->clear();
661
662 // Do not drop the document before the ScriptController and view are cleared
663 // as some destructors might still try to access the document.
664 m_frame.setDocument(nullptr);
665
666 subframeLoader().clear();
667
668 if (clearWindowProperties)
669 m_frame.windowProxy().setDOMWindow(newDocument->domWindow());
670
671 if (clearScriptObjects)
672 m_frame.script().clearScriptObjects();
673
674 m_frame.script().enableEval();
675
676 m_frame.navigationScheduler().clear();
677
678 m_checkTimer.stop();
679 m_shouldCallCheckCompleted = false;
680 m_shouldCallCheckLoadComplete = false;
681
682 if (m_stateMachine.isDisplayingInitialEmptyDocument() && m_stateMachine.committedFirstRealDocumentLoad())
683 m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad);
684}
685
686void FrameLoader::receivedFirstData()
687{
688 dispatchDidCommitLoad(WTF::nullopt);
689 dispatchDidClearWindowObjectsInAllWorlds();
690 dispatchGlobalObjectAvailableInAllWorlds();
691
692 if (!m_documentLoader)
693 return;
694
695 auto& documentLoader = *m_documentLoader;
696 auto& title = documentLoader.title();
697 if (!title.string.isNull())
698 m_client.dispatchDidReceiveTitle(title);
699
700 ASSERT(m_frame.document());
701 auto& document = *m_frame.document();
702
703 LinkLoader::loadLinksFromHeader(documentLoader.response().httpHeaderField(HTTPHeaderName::Link), document.url(), document, LinkLoader::MediaAttributeCheck::MediaAttributeEmpty);
704
705 double delay;
706 String urlString;
707 if (!parseHTTPRefresh(documentLoader.response().httpHeaderField(HTTPHeaderName::Refresh), delay, urlString))
708 return;
709 auto completedURL = urlString.isEmpty() ? document.url() : document.completeURL(urlString);
710 if (!WTF::protocolIsJavaScript(completedURL))
711 m_frame.navigationScheduler().scheduleRedirect(document, delay, completedURL);
712 else {
713 auto message = "Refused to refresh " + document.url().stringCenterEllipsizedToLength() + " to a javascript: URL";
714 document.addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
715 }
716}
717
718void FrameLoader::setOutgoingReferrer(const URL& url)
719{
720 m_outgoingReferrer = url.strippedForUseAsReferrer();
721}
722
723void FrameLoader::didBeginDocument(bool dispatch, ContentSecurityPolicy* previousPolicy)
724{
725 m_needsClear = true;
726 m_isComplete = false;
727 m_didCallImplicitClose = false;
728 m_frame.document()->setReadyState(Document::Loading);
729
730 if (m_pendingStateObject) {
731 m_frame.document()->statePopped(*m_pendingStateObject);
732 m_pendingStateObject = nullptr;
733 }
734
735 if (dispatch)
736 dispatchDidClearWindowObjectsInAllWorlds();
737
738 updateFirstPartyForCookies();
739 m_frame.document()->initContentSecurityPolicy(previousPolicy);
740
741 const Settings& settings = m_frame.settings();
742 m_frame.document()->cachedResourceLoader().setImagesEnabled(settings.areImagesEnabled());
743 m_frame.document()->cachedResourceLoader().setAutoLoadImages(settings.loadsImagesAutomatically());
744
745 if (m_documentLoader) {
746 String dnsPrefetchControl = m_documentLoader->response().httpHeaderField(HTTPHeaderName::XDNSPrefetchControl);
747 if (!dnsPrefetchControl.isEmpty())
748 m_frame.document()->parseDNSPrefetchControlHeader(dnsPrefetchControl);
749
750 m_frame.document()->contentSecurityPolicy()->didReceiveHeaders(ContentSecurityPolicyResponseHeaders(m_documentLoader->response()), referrer(), ContentSecurityPolicy::ReportParsingErrors::No);
751
752 String referrerPolicy = m_documentLoader->response().httpHeaderField(HTTPHeaderName::ReferrerPolicy);
753 if (!referrerPolicy.isNull())
754 m_frame.document()->processReferrerPolicy(referrerPolicy, ReferrerPolicySource::HTTPHeader);
755
756 String headerContentLanguage = m_documentLoader->response().httpHeaderField(HTTPHeaderName::ContentLanguage);
757 if (!headerContentLanguage.isEmpty()) {
758 size_t commaIndex = headerContentLanguage.find(',');
759 headerContentLanguage.truncate(commaIndex); // notFound == -1 == don't truncate
760 headerContentLanguage = stripLeadingAndTrailingHTMLSpaces(headerContentLanguage);
761 if (!headerContentLanguage.isEmpty())
762 m_frame.document()->setContentLanguage(headerContentLanguage);
763 }
764 }
765
766 history().restoreDocumentState();
767}
768
769void FrameLoader::finishedParsing()
770{
771 LOG(Loading, "WebCoreLoading %s: Finished parsing", m_frame.tree().uniqueName().string().utf8().data());
772
773 m_frame.injectUserScripts(InjectAtDocumentEnd);
774
775 if (m_stateMachine.creatingInitialEmptyDocument())
776 return;
777
778 // This can be called from the Frame's destructor, in which case we shouldn't protect ourselves
779 // because doing so will cause us to re-enter the destructor when protector goes out of scope.
780 // Null-checking the FrameView indicates whether or not we're in the destructor.
781 RefPtr<Frame> protector = m_frame.view() ? &m_frame : 0;
782
783 m_client.dispatchDidFinishDocumentLoad();
784
785 scrollToFragmentWithParentBoundary(m_frame.document()->url());
786
787 checkCompleted();
788
789 if (!m_frame.view())
790 return; // We are being destroyed by something checkCompleted called.
791
792 // Check if the scrollbars are really needed for the content.
793 // If not, remove them, relayout, and repaint.
794 m_frame.view()->restoreScrollbar();
795}
796
797void FrameLoader::loadDone(LoadCompletionType type)
798{
799 if (type == LoadCompletionType::Finish)
800 checkCompleted();
801 else
802 scheduleCheckCompleted();
803}
804
805void FrameLoader::subresourceLoadDone(LoadCompletionType type)
806{
807 if (type == LoadCompletionType::Finish)
808 checkLoadComplete();
809 else
810 scheduleCheckLoadComplete();
811}
812
813bool FrameLoader::allChildrenAreComplete() const
814{
815 for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling()) {
816 if (!child->loader().m_isComplete)
817 return false;
818 }
819 return true;
820}
821
822bool FrameLoader::allAncestorsAreComplete() const
823{
824 for (Frame* ancestor = &m_frame; ancestor; ancestor = ancestor->tree().parent()) {
825 if (!ancestor->loader().m_isComplete)
826 return false;
827 }
828 return true;
829}
830
831void FrameLoader::checkCompleted()
832{
833 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::InMainThread::isScriptAllowed());
834 m_shouldCallCheckCompleted = false;
835
836 // Have we completed before?
837 if (m_isComplete)
838 return;
839
840#if ENABLE(VIDEO)
841 // FIXME: Remove this code once https://webkit.org/b/185284 is fixed.
842 if (HTMLMediaElement::isRunningDestructor()) {
843 ASSERT_NOT_REACHED();
844 scheduleCheckCompleted();
845 return;
846 }
847#endif
848
849 // FIXME: It would be better if resource loads were kicked off after render tree update (or didn't complete synchronously).
850 // https://bugs.webkit.org/show_bug.cgi?id=171729
851 if (m_frame.document()->inRenderTreeUpdate()) {
852 scheduleCheckCompleted();
853 return;
854 }
855
856 // Are we still parsing?
857 if (m_frame.document()->parsing())
858 return;
859
860 // Still waiting for images/scripts?
861 if (m_frame.document()->cachedResourceLoader().requestCount())
862 return;
863
864 // Still waiting for elements that don't go through a FrameLoader?
865 if (m_frame.document()->isDelayingLoadEvent())
866 return;
867
868 auto* scriptableParser = m_frame.document()->scriptableDocumentParser();
869 if (scriptableParser && scriptableParser->hasScriptsWaitingForStylesheets())
870 return;
871
872 // Any frame that hasn't completed yet?
873 if (!allChildrenAreComplete())
874 return;
875
876 // Important not to protect earlier in this function, because earlier parts
877 // of this function can be called in the frame's destructor, and it's not legal
878 // to ref an object while it's being destroyed.
879 Ref<Frame> protect(m_frame);
880
881 // OK, completed.
882 m_isComplete = true;
883 m_requestedHistoryItem = nullptr;
884 m_frame.document()->setReadyState(Document::Complete);
885
886#if PLATFORM(IOS_FAMILY)
887 if (m_frame.document()->url().isEmpty()) {
888 // We need to update the document URL of a PDF document to be non-empty so that both back/forward history navigation
889 // between PDF pages and fragment navigation works. See <rdar://problem/9544769> for more details.
890 // FIXME: Is there a better place for this code, say DocumentLoader? Also, we should explicitly only update the URL
891 // of the document when it's a PDFDocument object instead of assuming that a Document object with an empty URL is a PDFDocument.
892 // FIXME: This code is incorrect for a synthesized document (which also has an empty URL). The URL for a synthesized
893 // document should be the URL specified to FrameLoader::initForSynthesizedDocument().
894 m_frame.document()->setURL(activeDocumentLoader()->documentURL());
895 }
896#endif
897
898 checkCallImplicitClose(); // if we didn't do it before
899
900 m_frame.navigationScheduler().startTimer();
901
902 completed();
903 if (m_frame.page())
904 checkLoadComplete();
905}
906
907void FrameLoader::checkTimerFired()
908{
909 checkCompletenessNow();
910}
911
912void FrameLoader::checkCompletenessNow()
913{
914 Ref<Frame> protect(m_frame);
915
916 if (Page* page = m_frame.page()) {
917 if (page->defersLoading())
918 return;
919 }
920 if (m_shouldCallCheckCompleted)
921 checkCompleted();
922 if (m_shouldCallCheckLoadComplete)
923 checkLoadComplete();
924}
925
926void FrameLoader::startCheckCompleteTimer()
927{
928 if (!(m_shouldCallCheckCompleted || m_shouldCallCheckLoadComplete))
929 return;
930 if (m_checkTimer.isActive())
931 return;
932 m_checkTimer.startOneShot(0_s);
933}
934
935void FrameLoader::scheduleCheckCompleted()
936{
937 m_shouldCallCheckCompleted = true;
938 startCheckCompleteTimer();
939}
940
941void FrameLoader::scheduleCheckLoadComplete()
942{
943 m_shouldCallCheckLoadComplete = true;
944 startCheckCompleteTimer();
945}
946
947void FrameLoader::checkCallImplicitClose()
948{
949 if (m_didCallImplicitClose || m_frame.document()->parsing() || m_frame.document()->isDelayingLoadEvent())
950 return;
951
952 if (!allChildrenAreComplete())
953 return; // still got a frame running -> too early
954
955 m_didCallImplicitClose = true;
956 m_wasUnloadEventEmitted = false;
957 m_frame.document()->implicitClose();
958}
959
960void FrameLoader::loadURLIntoChildFrame(const URL& url, const String& referer, Frame* childFrame)
961{
962 RELEASE_LOG_IF_ALLOWED("loadURLIntoChildFrame: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
963
964 ASSERT(childFrame);
965
966#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
967 if (auto activeLoader = activeDocumentLoader()) {
968 if (auto subframeArchive = activeLoader->popArchiveForSubframe(childFrame->tree().uniqueName(), url)) {
969 childFrame->loader().loadArchive(RefPtr<Archive> { subframeArchive }.releaseNonNull());
970 return;
971 }
972 }
973#endif
974
975 // If we're moving in the back/forward list, we might want to replace the content
976 // of this child frame with whatever was there at that point.
977 auto* parentItem = history().currentItem();
978 if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType()) && !m_frame.document()->loadEventFinished()) {
979 if (auto* childItem = parentItem->childItemWithTarget(childFrame->tree().uniqueName())) {
980 childFrame->loader().m_requestedHistoryItem = childItem;
981 childFrame->loader().loadDifferentDocumentItem(*childItem, nullptr, loadType(), MayAttemptCacheOnlyLoadForFormSubmissionItem, ShouldTreatAsContinuingLoad::No);
982 return;
983 }
984 }
985
986 auto* lexicalFrame = lexicalFrameFromCommonVM();
987 auto initiatedByMainFrame = lexicalFrame && lexicalFrame->isMainFrame() ? InitiatedByMainFrame::Yes : InitiatedByMainFrame::Unknown;
988
989 FrameLoadRequest frameLoadRequest { *m_frame.document(), m_frame.document()->securityOrigin(), { url }, "_self"_s, LockHistory::No, LockBackForwardList::Yes, ShouldSendReferrer::MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress, ShouldOpenExternalURLsPolicy::ShouldNotAllow, initiatedByMainFrame };
990 childFrame->loader().loadURL(WTFMove(frameLoadRequest), referer, FrameLoadType::RedirectWithLockedBackForwardList, nullptr, { }, WTF::nullopt, [] { });
991}
992
993#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
994
995void FrameLoader::loadArchive(Ref<Archive>&& archive)
996{
997 RELEASE_LOG_IF_ALLOWED("loadArchive: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
998
999 ArchiveResource* mainResource = archive->mainResource();
1000 ASSERT(mainResource);
1001 if (!mainResource)
1002 return;
1003
1004 ResourceResponse response(URL(), mainResource->mimeType(), mainResource->data().size(), mainResource->textEncoding());
1005 SubstituteData substituteData(&mainResource->data(), URL(), response, SubstituteData::SessionHistoryVisibility::Hidden);
1006
1007 ResourceRequest request(mainResource->url());
1008
1009 auto documentLoader = m_client.createDocumentLoader(request, substituteData);
1010 documentLoader->setArchive(WTFMove(archive));
1011 load(documentLoader.get());
1012}
1013
1014#endif // ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
1015
1016String FrameLoader::outgoingReferrer() const
1017{
1018 // See http://www.whatwg.org/specs/web-apps/current-work/#fetching-resources
1019 // for why we walk the parent chain for srcdoc documents.
1020 Frame* frame = &m_frame;
1021 while (frame && frame->document()->isSrcdocDocument()) {
1022 frame = frame->tree().parent();
1023 // Srcdoc documents cannot be top-level documents, by definition,
1024 // because they need to be contained in iframes with the srcdoc.
1025 ASSERT(frame);
1026 }
1027 if (!frame)
1028 return emptyString();
1029 return frame->loader().m_outgoingReferrer;
1030}
1031
1032String FrameLoader::outgoingOrigin() const
1033{
1034 return m_frame.document()->securityOrigin().toString();
1035}
1036
1037bool FrameLoader::checkIfFormActionAllowedByCSP(const URL& url, bool didReceiveRedirectResponse) const
1038{
1039 if (m_submittedFormURL.isEmpty())
1040 return true;
1041
1042 auto redirectResponseReceived = didReceiveRedirectResponse ? ContentSecurityPolicy::RedirectResponseReceived::Yes : ContentSecurityPolicy::RedirectResponseReceived::No;
1043 return m_frame.document()->contentSecurityPolicy()->allowFormAction(url, redirectResponseReceived);
1044}
1045
1046Frame* FrameLoader::opener()
1047{
1048 return m_opener;
1049}
1050
1051void FrameLoader::setOpener(Frame* opener)
1052{
1053 if (m_opener && !opener)
1054 m_client.didDisownOpener();
1055
1056 if (m_opener) {
1057 // When setOpener is called in ~FrameLoader, opener's m_frameLoader is already cleared.
1058 auto& openerFrameLoader = m_opener == &m_frame ? *this : m_opener->loader();
1059 openerFrameLoader.m_openedFrames.remove(&m_frame);
1060 }
1061 if (opener) {
1062 opener->loader().m_openedFrames.add(&m_frame);
1063 if (auto* page = m_frame.page())
1064 page->setOpenedByDOMWithOpener();
1065 }
1066 m_opener = opener;
1067
1068 if (m_frame.document())
1069 m_frame.document()->initSecurityContext();
1070}
1071
1072// FIXME: This does not belong in FrameLoader!
1073void FrameLoader::handleFallbackContent()
1074{
1075 HTMLFrameOwnerElement* owner = m_frame.ownerElement();
1076 if (!is<HTMLObjectElement>(owner))
1077 return;
1078 downcast<HTMLObjectElement>(*owner).renderFallbackContent();
1079}
1080
1081void FrameLoader::provisionalLoadStarted()
1082{
1083 if (m_stateMachine.firstLayoutDone())
1084 m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad);
1085 m_frame.navigationScheduler().cancel(NewLoadInProgress::Yes);
1086 m_client.provisionalLoadStarted();
1087
1088 if (m_frame.isMainFrame()) {
1089 tracePoint(MainResourceLoadDidStartProvisional);
1090
1091 if (auto* page = m_frame.page())
1092 page->didStartProvisionalLoad();
1093 }
1094}
1095
1096void FrameLoader::resetMultipleFormSubmissionProtection()
1097{
1098 m_submittedFormURL = URL();
1099}
1100
1101void FrameLoader::updateFirstPartyForCookies()
1102{
1103 if (m_frame.tree().parent())
1104 setFirstPartyForCookies(m_frame.tree().parent()->document()->firstPartyForCookies());
1105 else
1106 setFirstPartyForCookies(m_frame.document()->url());
1107}
1108
1109void FrameLoader::setFirstPartyForCookies(const URL& url)
1110{
1111 for (Frame* frame = &m_frame; frame; frame = frame->tree().traverseNext(&m_frame))
1112 frame->document()->setFirstPartyForCookies(url);
1113
1114 RegistrableDomain registrableDomain(url);
1115 for (Frame* frame = &m_frame; frame; frame = frame->tree().traverseNext(&m_frame)) {
1116 if (SecurityPolicy::shouldInheritSecurityOriginFromOwner(frame->document()->url()) || registrableDomain.matches(frame->document()->url()))
1117 frame->document()->setSiteForCookies(url);
1118 }
1119}
1120
1121// This does the same kind of work that didOpenURL does, except it relies on the fact
1122// that a higher level already checked that the URLs match and the scrolling is the right thing to do.
1123void FrameLoader::loadInSameDocument(const URL& url, SerializedScriptValue* stateObject, bool isNewNavigation)
1124{
1125 RELEASE_LOG_IF_ALLOWED("loadInSameDocument: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1126
1127 // If we have a state object, we cannot also be a new navigation.
1128 ASSERT(!stateObject || (stateObject && !isNewNavigation));
1129
1130 // Update the data source's request with the new URL to fake the URL change
1131 URL oldURL = m_frame.document()->url();
1132 m_frame.document()->setURL(url);
1133 setOutgoingReferrer(url);
1134 documentLoader()->replaceRequestURLForSameDocumentNavigation(url);
1135 if (isNewNavigation && !shouldTreatURLAsSameAsCurrent(url) && !stateObject) {
1136 // NB: must happen after replaceRequestURLForSameDocumentNavigation(), since we add
1137 // based on the current request. Must also happen before we openURL and displace the
1138 // scroll position, since adding the BF item will save away scroll state.
1139
1140 // NB2: If we were loading a long, slow doc, and the user fragment navigated before
1141 // it was done, currItem is now set the that slow doc, and prevItem is whatever was
1142 // before it. Adding the b/f item will bump the slow doc down to prevItem, even
1143 // though its load is not yet done. I think this all works out OK, for one because
1144 // we have already saved away the scroll and doc state for the long slow load,
1145 // but it's not an obvious case.
1146
1147 history().updateBackForwardListForFragmentScroll();
1148 }
1149
1150 bool hashChange = equalIgnoringFragmentIdentifier(url, oldURL) && url.fragmentIdentifier() != oldURL.fragmentIdentifier();
1151
1152 history().updateForSameDocumentNavigation();
1153
1154 // If we were in the autoscroll/panScroll mode we want to stop it before following the link to the anchor
1155 if (hashChange)
1156 m_frame.eventHandler().stopAutoscrollTimer();
1157
1158 // It's important to model this as a load that starts and immediately finishes.
1159 // Otherwise, the parent frame may think we never finished loading.
1160 started();
1161
1162 if (auto* ownerElement = m_frame.ownerElement()) {
1163 auto* ownerRenderer = ownerElement->renderer();
1164 auto* view = m_frame.view();
1165 if (is<RenderWidget>(ownerRenderer) && view)
1166 downcast<RenderWidget>(*ownerRenderer).setWidget(view);
1167 }
1168
1169 // We need to scroll to the fragment whether or not a hash change occurred, since
1170 // the user might have scrolled since the previous navigation.
1171 scrollToFragmentWithParentBoundary(url, isNewNavigation);
1172
1173 m_isComplete = false;
1174 checkCompleted();
1175
1176 if (isNewNavigation) {
1177 // This will clear previousItem from the rest of the frame tree that didn't
1178 // doing any loading. We need to make a pass on this now, since for fragment
1179 // navigation we'll not go through a real load and reach Completed state.
1180 checkLoadComplete();
1181 }
1182
1183 m_client.dispatchDidNavigateWithinPage();
1184
1185 m_frame.document()->statePopped(stateObject ? Ref<SerializedScriptValue> { *stateObject } : SerializedScriptValue::nullValue());
1186 m_client.dispatchDidPopStateWithinPage();
1187
1188 if (hashChange) {
1189 m_frame.document()->enqueueHashchangeEvent(oldURL, url);
1190 m_client.dispatchDidChangeLocationWithinPage();
1191 }
1192
1193 // FrameLoaderClient::didFinishLoad() tells the internal load delegate the load finished with no error
1194 m_client.didFinishLoad();
1195}
1196
1197bool FrameLoader::isComplete() const
1198{
1199 return m_isComplete;
1200}
1201
1202void FrameLoader::completed()
1203{
1204 Ref<Frame> protect(m_frame);
1205
1206 for (Frame* descendant = m_frame.tree().traverseNext(&m_frame); descendant; descendant = descendant->tree().traverseNext(&m_frame))
1207 descendant->navigationScheduler().startTimer();
1208
1209 if (Frame* parent = m_frame.tree().parent())
1210 parent->loader().checkCompleted();
1211
1212 if (m_frame.view())
1213 m_frame.view()->maintainScrollPositionAtAnchor(nullptr);
1214}
1215
1216void FrameLoader::started()
1217{
1218 for (Frame* frame = &m_frame; frame; frame = frame->tree().parent())
1219 frame->loader().m_isComplete = false;
1220}
1221
1222void FrameLoader::prepareForLoadStart()
1223{
1224 RELEASE_LOG_IF_ALLOWED("prepareForLoadStart: Starting frame load (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1225
1226 m_progressTracker->progressStarted();
1227 m_client.dispatchDidStartProvisionalLoad();
1228
1229 if (AXObjectCache::accessibilityEnabled()) {
1230 if (AXObjectCache* cache = m_frame.document()->existingAXObjectCache()) {
1231 AXObjectCache::AXLoadingEvent loadingEvent = loadType() == FrameLoadType::Reload ? AXObjectCache::AXLoadingReloaded : AXObjectCache::AXLoadingStarted;
1232 cache->frameLoadingEventNotification(&m_frame, loadingEvent);
1233 }
1234 }
1235}
1236
1237void FrameLoader::setupForReplace()
1238{
1239 m_client.revertToProvisionalState(m_documentLoader.get());
1240 setState(FrameStateProvisional);
1241 m_provisionalDocumentLoader = m_documentLoader;
1242 m_documentLoader = nullptr;
1243 detachChildren();
1244}
1245
1246void FrameLoader::loadFrameRequest(FrameLoadRequest&& request, Event* event, RefPtr<FormState>&& formState, Optional<AdClickAttribution>&& adClickAttribution)
1247{
1248 RELEASE_LOG_IF_ALLOWED("loadFrameRequest: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1249
1250 // Protect frame from getting blown away inside dispatchBeforeLoadEvent in loadWithDocumentLoader.
1251 auto protectFrame = makeRef(m_frame);
1252
1253 URL url = request.resourceRequest().url();
1254
1255 ASSERT(m_frame.document());
1256 if (!request.requesterSecurityOrigin().canDisplay(url)) {
1257 reportLocalLoadFailed(&m_frame, url.stringCenterEllipsizedToLength());
1258 return;
1259 }
1260
1261 String argsReferrer = request.resourceRequest().httpReferrer();
1262 if (argsReferrer.isEmpty())
1263 argsReferrer = outgoingReferrer();
1264
1265 String referrer = SecurityPolicy::generateReferrerHeader(m_frame.document()->referrerPolicy(), url, argsReferrer);
1266 if (request.shouldSendReferrer() == NeverSendReferrer)
1267 referrer = String();
1268
1269 FrameLoadType loadType;
1270 if (request.resourceRequest().cachePolicy() == ResourceRequestCachePolicy::ReloadIgnoringCacheData)
1271 loadType = FrameLoadType::Reload;
1272 else if (request.lockBackForwardList() == LockBackForwardList::Yes)
1273 loadType = FrameLoadType::RedirectWithLockedBackForwardList;
1274 else
1275 loadType = FrameLoadType::Standard;
1276
1277 auto completionHandler = [this, protectedFrame = makeRef(m_frame), formState = makeWeakPtr(formState.get()), frameName = request.frameName()] {
1278 // FIXME: It's possible this targetFrame will not be the same frame that was targeted by the actual
1279 // load if frame names have changed.
1280 Frame* sourceFrame = formState ? formState->sourceDocument().frame() : &m_frame;
1281 if (!sourceFrame)
1282 sourceFrame = &m_frame;
1283 Frame* targetFrame = sourceFrame->loader().findFrameForNavigation(frameName);
1284 if (targetFrame && targetFrame != sourceFrame) {
1285 if (Page* page = targetFrame->page())
1286 page->chrome().focus();
1287 }
1288 };
1289
1290 if (request.resourceRequest().httpMethod() == "POST")
1291 loadPostRequest(WTFMove(request), referrer, loadType, event, WTFMove(formState), WTFMove(completionHandler));
1292 else
1293 loadURL(WTFMove(request), referrer, loadType, event, WTFMove(formState), WTFMove(adClickAttribution), WTFMove(completionHandler));
1294}
1295
1296static ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicyToApply(Frame& currentFrame, InitiatedByMainFrame initiatedByMainFrame, ShouldOpenExternalURLsPolicy propagatedPolicy)
1297{
1298 if (UserGestureIndicator::processingUserGesture())
1299 return ShouldOpenExternalURLsPolicy::ShouldAllow;
1300
1301 if (initiatedByMainFrame == InitiatedByMainFrame::Yes)
1302 return propagatedPolicy;
1303
1304 if (!currentFrame.isMainFrame())
1305 return ShouldOpenExternalURLsPolicy::ShouldNotAllow;
1306
1307 return propagatedPolicy;
1308}
1309
1310static ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicyToApply(Frame& currentFrame, const FrameLoadRequest& frameLoadRequest)
1311{
1312 return shouldOpenExternalURLsPolicyToApply(currentFrame, frameLoadRequest.initiatedByMainFrame(), frameLoadRequest.shouldOpenExternalURLsPolicy());
1313}
1314
1315static void applyShouldOpenExternalURLsPolicyToNewDocumentLoader(Frame& frame, DocumentLoader& documentLoader, InitiatedByMainFrame initiatedByMainFrame, ShouldOpenExternalURLsPolicy propagatedPolicy)
1316{
1317 documentLoader.setShouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicyToApply(frame, initiatedByMainFrame, propagatedPolicy));
1318}
1319
1320static void applyShouldOpenExternalURLsPolicyToNewDocumentLoader(Frame& frame, DocumentLoader& documentLoader, const FrameLoadRequest& frameLoadRequest)
1321{
1322 documentLoader.setShouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicyToApply(frame, frameLoadRequest));
1323}
1324
1325bool FrameLoader::isNavigationAllowed() const
1326{
1327 return m_pageDismissalEventBeingDispatched == PageDismissalType::None && NavigationDisabler::isNavigationAllowed(m_frame);
1328}
1329
1330bool FrameLoader::isStopLoadingAllowed() const
1331{
1332 return m_pageDismissalEventBeingDispatched == PageDismissalType::None;
1333}
1334
1335void FrameLoader::loadURL(FrameLoadRequest&& frameLoadRequest, const String& referrer, FrameLoadType newLoadType, Event* event, RefPtr<FormState>&& formState, Optional<AdClickAttribution>&& adClickAttribution, CompletionHandler<void()>&& completionHandler)
1336{
1337 RELEASE_LOG_IF_ALLOWED("loadURL: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1338
1339 CompletionHandlerCallingScope completionHandlerCaller(WTFMove(completionHandler));
1340 if (m_inStopAllLoaders || m_inClearProvisionalLoadForPolicyCheck)
1341 return;
1342
1343 Ref<Frame> protect(m_frame);
1344
1345 // Anchor target is ignored when the download attribute is set since it will download the hyperlink rather than follow it.
1346 String effectiveFrameName = frameLoadRequest.downloadAttribute().isNull() ? frameLoadRequest.frameName() : String();
1347 AllowNavigationToInvalidURL allowNavigationToInvalidURL = frameLoadRequest.allowNavigationToInvalidURL();
1348 NewFrameOpenerPolicy openerPolicy = frameLoadRequest.newFrameOpenerPolicy();
1349 LockHistory lockHistory = frameLoadRequest.lockHistory();
1350 bool isFormSubmission = formState;
1351
1352 const URL& newURL = frameLoadRequest.resourceRequest().url();
1353 ResourceRequest request(newURL);
1354 if (!referrer.isEmpty()) {
1355 request.setHTTPReferrer(referrer);
1356 auto referrerOrigin = SecurityOrigin::createFromString(referrer);
1357 addHTTPOriginIfNeeded(request, referrerOrigin->toString());
1358 }
1359 if (&m_frame.tree().top() != &m_frame)
1360 request.setDomainForCachePartition(m_frame.tree().top().document()->domainForCachePartition());
1361
1362 addExtraFieldsToRequest(request, newLoadType, true);
1363 if (isReload(newLoadType))
1364 request.setCachePolicy(ResourceRequestCachePolicy::ReloadIgnoringCacheData);
1365
1366 ASSERT(newLoadType != FrameLoadType::Same);
1367
1368 // The search for a target frame is done earlier in the case of form submission.
1369 Frame* targetFrame = isFormSubmission ? nullptr : findFrameForNavigation(effectiveFrameName);
1370 if (targetFrame && targetFrame != &m_frame) {
1371 frameLoadRequest.setFrameName("_self");
1372 targetFrame->loader().loadURL(WTFMove(frameLoadRequest), referrer, newLoadType, event, WTFMove(formState), WTFMove(adClickAttribution), completionHandlerCaller.release());
1373 return;
1374 }
1375
1376 if (!isNavigationAllowed())
1377 return;
1378
1379 NavigationAction action { frameLoadRequest.requester(), request, frameLoadRequest.initiatedByMainFrame(), newLoadType, isFormSubmission, event, frameLoadRequest.shouldOpenExternalURLsPolicy(), frameLoadRequest.downloadAttribute() };
1380 action.setLockHistory(lockHistory);
1381 action.setLockBackForwardList(frameLoadRequest.lockBackForwardList());
1382 if (adClickAttribution && m_frame.isMainFrame())
1383 action.setAdClickAttribution(WTFMove(*adClickAttribution));
1384
1385 if (!targetFrame && !effectiveFrameName.isEmpty()) {
1386 action = action.copyWithShouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicyToApply(m_frame, frameLoadRequest));
1387 policyChecker().checkNewWindowPolicy(WTFMove(action), WTFMove(request), WTFMove(formState), effectiveFrameName, [this, allowNavigationToInvalidURL, openerPolicy, completionHandler = completionHandlerCaller.release()] (const ResourceRequest& request, WeakPtr<FormState>&& formState, const String& frameName, const NavigationAction& action, ShouldContinue shouldContinue) mutable {
1388 continueLoadAfterNewWindowPolicy(request, formState.get(), frameName, action, shouldContinue, allowNavigationToInvalidURL, openerPolicy);
1389 completionHandler();
1390 });
1391 return;
1392 }
1393
1394 RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
1395
1396 bool sameURL = shouldTreatURLAsSameAsCurrent(newURL);
1397 const String& httpMethod = request.httpMethod();
1398
1399 // Make sure to do scroll to fragment processing even if the URL is
1400 // exactly the same so pages with '#' links and DHTML side effects
1401 // work properly.
1402 if (shouldPerformFragmentNavigation(isFormSubmission, httpMethod, newLoadType, newURL)) {
1403 oldDocumentLoader->setTriggeringAction(WTFMove(action));
1404 oldDocumentLoader->setLastCheckedRequest(ResourceRequest());
1405 policyChecker().stopCheck();
1406 policyChecker().setLoadType(newLoadType);
1407 RELEASE_ASSERT(!isBackForwardLoadType(newLoadType) || history().provisionalItem());
1408 policyChecker().checkNavigationPolicy(WTFMove(request), ResourceResponse { } /* redirectResponse */, oldDocumentLoader.get(), WTFMove(formState), [this, protectedFrame = makeRef(m_frame)] (const ResourceRequest& request, WeakPtr<FormState>&&, NavigationPolicyDecision navigationPolicyDecision) {
1409 continueFragmentScrollAfterNavigationPolicy(request, navigationPolicyDecision == NavigationPolicyDecision::ContinueLoad);
1410 }, PolicyDecisionMode::Synchronous);
1411 return;
1412 }
1413
1414 // Must grab this now, since this load may stop the previous load and clear this flag.
1415 bool isRedirect = m_quickRedirectComing;
1416#if USE(SYSTEM_PREVIEW)
1417 bool isSystemPreview = frameLoadRequest.isSystemPreview();
1418 request.setSystemPreview(isSystemPreview);
1419 if (isSystemPreview)
1420 request.setSystemPreviewRect(frameLoadRequest.systemPreviewRect());
1421#endif
1422 loadWithNavigationAction(request, WTFMove(action), lockHistory, newLoadType, WTFMove(formState), allowNavigationToInvalidURL, frameLoadRequest.downloadAttribute(), [this, isRedirect, sameURL, newLoadType, protectedFrame = makeRef(m_frame), completionHandler = completionHandlerCaller.release()] () mutable {
1423 if (isRedirect) {
1424 m_quickRedirectComing = false;
1425 if (m_provisionalDocumentLoader)
1426 m_provisionalDocumentLoader->setIsClientRedirect(true);
1427 else if (m_policyDocumentLoader)
1428 m_policyDocumentLoader->setIsClientRedirect(true);
1429 } else if (sameURL && !isReload(newLoadType)) {
1430 // Example of this case are sites that reload the same URL with a different cookie
1431 // driving the generated content, or a master frame with links that drive a target
1432 // frame, where the user has clicked on the same link repeatedly.
1433 m_loadType = FrameLoadType::Same;
1434 }
1435 completionHandler();
1436 });
1437}
1438
1439SubstituteData FrameLoader::defaultSubstituteDataForURL(const URL& url)
1440{
1441 if (!shouldTreatURLAsSrcdocDocument(url))
1442 return SubstituteData();
1443 auto& srcdoc = m_frame.ownerElement()->attributeWithoutSynchronization(srcdocAttr);
1444 ASSERT(!srcdoc.isNull());
1445 CString encodedSrcdoc = srcdoc.string().utf8();
1446
1447 ResourceResponse response(URL(), "text/html"_s, encodedSrcdoc.length(), "UTF-8"_s);
1448 return SubstituteData(SharedBuffer::create(encodedSrcdoc.data(), encodedSrcdoc.length()), URL(), response, SubstituteData::SessionHistoryVisibility::Hidden);
1449}
1450
1451void FrameLoader::load(FrameLoadRequest&& request)
1452{
1453 RELEASE_LOG_IF_ALLOWED("load (FrameLoadRequest): frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1454
1455 if (m_inStopAllLoaders || m_inClearProvisionalLoadForPolicyCheck)
1456 return;
1457
1458 if (!request.frameName().isEmpty()) {
1459 Frame* frame = findFrameForNavigation(request.frameName());
1460 if (frame) {
1461 request.setShouldCheckNewWindowPolicy(false);
1462 if (&frame->loader() != this) {
1463 frame->loader().load(WTFMove(request));
1464 return;
1465 }
1466 }
1467 }
1468
1469 if (request.shouldCheckNewWindowPolicy()) {
1470 NavigationAction action { request.requester(), request.resourceRequest(), InitiatedByMainFrame::Unknown, NavigationType::Other, request.shouldOpenExternalURLsPolicy() };
1471 policyChecker().checkNewWindowPolicy(WTFMove(action), WTFMove(request.resourceRequest()), { }, request.frameName(), [this] (const ResourceRequest& request, WeakPtr<FormState>&& formState, const String& frameName, const NavigationAction& action, ShouldContinue shouldContinue) {
1472 continueLoadAfterNewWindowPolicy(request, formState.get(), frameName, action, shouldContinue, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress);
1473 });
1474
1475 return;
1476 }
1477
1478 if (!request.hasSubstituteData())
1479 request.setSubstituteData(defaultSubstituteDataForURL(request.resourceRequest().url()));
1480
1481 Ref<DocumentLoader> loader = m_client.createDocumentLoader(request.resourceRequest(), request.substituteData());
1482 loader->setAllowsWebArchiveForMainFrame(request.isRequestFromClientOrUserInput());
1483 addSameSiteInfoToRequestIfNeeded(loader->request());
1484 applyShouldOpenExternalURLsPolicyToNewDocumentLoader(m_frame, loader, request);
1485
1486 if (request.shouldTreatAsContinuingLoad()) {
1487 loader->setClientRedirectSourceForHistory(request.clientRedirectSourceForHistory());
1488 if (request.lockBackForwardList() == LockBackForwardList::Yes) {
1489 loader->setIsClientRedirect(true);
1490 m_loadType = FrameLoadType::RedirectWithLockedBackForwardList;
1491 }
1492 }
1493
1494 SetForScope<LoadContinuingState> continuingLoadGuard(m_currentLoadContinuingState, request.shouldTreatAsContinuingLoad() ? LoadContinuingState::ContinuingWithRequest : LoadContinuingState::NotContinuing);
1495 load(loader.get());
1496}
1497
1498void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, NavigationAction&& action, LockHistory lockHistory, FrameLoadType type, RefPtr<FormState>&& formState, AllowNavigationToInvalidURL allowNavigationToInvalidURL, const String& downloadAttribute, CompletionHandler<void()>&& completionHandler)
1499{
1500 RELEASE_LOG_IF_ALLOWED("loadWithNavigationAction: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1501
1502 Ref<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url()));
1503 loader->setDownloadAttribute(downloadAttribute);
1504 applyShouldOpenExternalURLsPolicyToNewDocumentLoader(m_frame, loader, action.initiatedByMainFrame(), action.shouldOpenExternalURLsPolicy());
1505
1506 if (lockHistory == LockHistory::Yes && m_documentLoader)
1507 loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory().string() : m_documentLoader->clientRedirectSourceForHistory());
1508
1509 loader->setTriggeringAction(WTFMove(action));
1510 if (m_documentLoader)
1511 loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
1512
1513 loadWithDocumentLoader(loader.ptr(), type, WTFMove(formState), allowNavigationToInvalidURL, ShouldTreatAsContinuingLoad::No, WTFMove(completionHandler));
1514}
1515
1516void FrameLoader::load(DocumentLoader& newDocumentLoader)
1517{
1518 RELEASE_LOG_IF_ALLOWED("load (DocumentLoader): frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1519
1520 ResourceRequest& r = newDocumentLoader.request();
1521 addExtraFieldsToMainResourceRequest(r);
1522 FrameLoadType type;
1523
1524 if (shouldTreatURLAsSameAsCurrent(newDocumentLoader.originalRequest().url())) {
1525 r.setCachePolicy(ResourceRequestCachePolicy::ReloadIgnoringCacheData);
1526 type = FrameLoadType::Same;
1527 } else if (shouldTreatURLAsSameAsCurrent(newDocumentLoader.unreachableURL()) && isReload(m_loadType))
1528 type = m_loadType;
1529 else if (m_loadType == FrameLoadType::RedirectWithLockedBackForwardList && ((!newDocumentLoader.unreachableURL().isEmpty() && newDocumentLoader.substituteData().isValid()) || shouldTreatCurrentLoadAsContinuingLoad()))
1530 type = FrameLoadType::RedirectWithLockedBackForwardList;
1531 else
1532 type = FrameLoadType::Standard;
1533
1534 if (m_documentLoader)
1535 newDocumentLoader.setOverrideEncoding(m_documentLoader->overrideEncoding());
1536
1537 // When we loading alternate content for an unreachable URL that we're
1538 // visiting in the history list, we treat it as a reload so the history list
1539 // is appropriately maintained.
1540 //
1541 // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadType::Reload" ...
1542 // shouldn't a more explicit type of reload be defined, that means roughly
1543 // "load without affecting history" ?
1544 if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) {
1545 // shouldReloadToHandleUnreachableURL returns true only when the original load type is back-forward.
1546 // In this case we should save the document state now. Otherwise the state can be lost because load type is
1547 // changed and updateForBackForwardNavigation() will not be called when loading is committed.
1548 history().saveDocumentAndScrollState();
1549
1550 ASSERT(type == FrameLoadType::Standard);
1551 type = FrameLoadType::Reload;
1552 }
1553
1554 loadWithDocumentLoader(&newDocumentLoader, type, nullptr, AllowNavigationToInvalidURL::Yes, ShouldTreatAsContinuingLoad::No);
1555}
1556
1557void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, RefPtr<FormState>&& formState, AllowNavigationToInvalidURL allowNavigationToInvalidURL, ShouldTreatAsContinuingLoad, CompletionHandler<void()>&& completionHandler)
1558{
1559 RELEASE_LOG_IF_ALLOWED("loadWithDocumentLoader: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1560
1561 // Retain because dispatchBeforeLoadEvent may release the last reference to it.
1562 Ref<Frame> protect(m_frame);
1563
1564 CompletionHandlerCallingScope completionHandlerCaller(WTFMove(completionHandler));
1565
1566 ASSERT(m_client.hasWebView());
1567
1568 // Unfortunately the view must be non-nil, this is ultimately due
1569 // to parser requiring a FrameView. We should fix this dependency.
1570
1571 ASSERT(m_frame.view());
1572
1573 if (!isNavigationAllowed())
1574 return;
1575
1576 if (m_frame.document())
1577 m_previousURL = m_frame.document()->url();
1578
1579 const URL& newURL = loader->request().url();
1580
1581 // Only the first iframe navigation or the first iframe navigation after about:blank should be reported.
1582 // https://www.w3.org/TR/resource-timing-2/#resources-included-in-the-performanceresourcetiming-interface
1583 if (m_shouldReportResourceTimingToParentFrame && !m_previousURL.isNull() && m_previousURL != WTF::blankURL())
1584 m_shouldReportResourceTimingToParentFrame = false;
1585
1586 // Log main frame navigation types.
1587 if (m_frame.isMainFrame()) {
1588 if (auto* page = m_frame.page()) {
1589 RELEASE_LOG_IF_ALLOWED("loadWithDocumentLoader: main frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1590 page->mainFrameLoadStarted(newURL, type);
1591 page->performanceLogging().didReachPointOfInterest(PerformanceLogging::MainFrameLoadStarted);
1592 }
1593 }
1594
1595 policyChecker().setLoadType(type);
1596 RELEASE_ASSERT(!isBackForwardLoadType(type) || history().provisionalItem());
1597 bool isFormSubmission = formState;
1598
1599 const String& httpMethod = loader->request().httpMethod();
1600
1601 if (shouldPerformFragmentNavigation(isFormSubmission, httpMethod, policyChecker().loadType(), newURL)) {
1602 RefPtr<DocumentLoader> oldDocumentLoader = m_documentLoader;
1603 NavigationAction action { *m_frame.document(), loader->request(), InitiatedByMainFrame::Unknown, policyChecker().loadType(), isFormSubmission };
1604
1605 oldDocumentLoader->setTriggeringAction(WTFMove(action));
1606 oldDocumentLoader->setLastCheckedRequest(ResourceRequest());
1607 policyChecker().stopCheck();
1608 RELEASE_ASSERT(!isBackForwardLoadType(policyChecker().loadType()) || history().provisionalItem());
1609 policyChecker().checkNavigationPolicy(ResourceRequest(loader->request()), ResourceResponse { } /* redirectResponse */, oldDocumentLoader.get(), WTFMove(formState), [this, protectedFrame = makeRef(m_frame)] (const ResourceRequest& request, WeakPtr<FormState>&&, NavigationPolicyDecision navigationPolicyDecision) {
1610 continueFragmentScrollAfterNavigationPolicy(request, navigationPolicyDecision == NavigationPolicyDecision::ContinueLoad);
1611 }, PolicyDecisionMode::Synchronous);
1612 return;
1613 }
1614
1615 if (Frame* parent = m_frame.tree().parent())
1616 loader->setOverrideEncoding(parent->loader().documentLoader()->overrideEncoding());
1617
1618 policyChecker().stopCheck();
1619 setPolicyDocumentLoader(loader);
1620 if (loader->triggeringAction().isEmpty())
1621 loader->setTriggeringAction({ *m_frame.document(), loader->request(), InitiatedByMainFrame::Unknown, policyChecker().loadType(), isFormSubmission });
1622
1623 if (Element* ownerElement = m_frame.ownerElement()) {
1624 // We skip dispatching the beforeload event if we've already
1625 // committed a real document load because the event would leak
1626 // subsequent activity by the frame which the parent frame isn't
1627 // supposed to learn. For example, if the child frame navigated to
1628 // a new URL, the parent frame shouldn't learn the URL.
1629 if (!m_stateMachine.committedFirstRealDocumentLoad()
1630 && !ownerElement->dispatchBeforeLoadEvent(loader->request().url().string())) {
1631 continueLoadAfterNavigationPolicy(loader->request(), formState.get(), NavigationPolicyDecision::IgnoreLoad, allowNavigationToInvalidURL);
1632 return;
1633 }
1634 }
1635
1636 m_frame.navigationScheduler().cancel(NewLoadInProgress::Yes);
1637
1638 if (shouldTreatCurrentLoadAsContinuingLoad()) {
1639 continueLoadAfterNavigationPolicy(loader->request(), formState.get(), NavigationPolicyDecision::ContinueLoad, allowNavigationToInvalidURL);
1640 return;
1641 }
1642
1643 RELEASE_ASSERT(!isBackForwardLoadType(policyChecker().loadType()) || history().provisionalItem());
1644 policyChecker().checkNavigationPolicy(ResourceRequest(loader->request()), ResourceResponse { } /* redirectResponse */, loader, WTFMove(formState), [this, protectedFrame = makeRef(m_frame), allowNavigationToInvalidURL, completionHandler = completionHandlerCaller.release()] (const ResourceRequest& request, WeakPtr<FormState>&& formState, NavigationPolicyDecision navigationPolicyDecision) mutable {
1645 continueLoadAfterNavigationPolicy(request, formState.get(), navigationPolicyDecision, allowNavigationToInvalidURL);
1646 completionHandler();
1647 }, PolicyDecisionMode::Asynchronous);
1648}
1649
1650void FrameLoader::clearProvisionalLoadForPolicyCheck()
1651{
1652 if (!m_policyDocumentLoader || !m_provisionalDocumentLoader || m_inClearProvisionalLoadForPolicyCheck)
1653 return;
1654
1655 SetForScope<bool> change(m_inClearProvisionalLoadForPolicyCheck, true);
1656 m_provisionalDocumentLoader->stopLoading();
1657 setProvisionalDocumentLoader(nullptr);
1658}
1659
1660void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url)
1661{
1662 ASSERT(!url.isEmpty());
1663 if (!frame)
1664 return;
1665
1666 frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Not allowed to load local resource: " + url);
1667}
1668
1669void FrameLoader::reportBlockedPortFailed(Frame* frame, const String& url)
1670{
1671 ASSERT(!url.isEmpty());
1672 if (!frame)
1673 return;
1674
1675 frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Not allowed to use restricted network port: " + url);
1676}
1677
1678void FrameLoader::reportAuthenticationChallengeBlocked(Frame* frame, const URL& url, const String& reason)
1679{
1680 if (!frame)
1681 return;
1682
1683 frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, makeString("Blocked ", url.stringCenterEllipsizedToLength(), " from asking for credentials because ", reason, '.'));
1684}
1685
1686const ResourceRequest& FrameLoader::initialRequest() const
1687{
1688 return activeDocumentLoader()->originalRequest();
1689}
1690
1691bool FrameLoader::willLoadMediaElementURL(URL& url, Node& initiatorNode)
1692{
1693#if PLATFORM(IOS_FAMILY)
1694 // MobileStore depends on the iOS 4.0 era client delegate method because webView:resource:willSendRequest:redirectResponse:fromDataSource
1695 // doesn't let them tell when a load request is coming from a media element. See <rdar://problem/8266916> for more details.
1696 if (IOSApplication::isMobileStore())
1697 return m_client.shouldLoadMediaElementURL(url);
1698#endif
1699
1700 ResourceRequest request(url);
1701 request.setInspectorInitiatorNodeIdentifier(InspectorInstrumentation::identifierForNode(initiatorNode));
1702
1703 unsigned long identifier;
1704 ResourceError error;
1705 requestFromDelegate(request, identifier, error);
1706 notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, ResourceResponse(url, String(), -1, String()), 0, -1, -1, error);
1707
1708 url = request.url();
1709
1710 return error.isNull();
1711}
1712
1713bool FrameLoader::shouldReloadToHandleUnreachableURL(DocumentLoader& docLoader)
1714{
1715 URL unreachableURL = docLoader.unreachableURL();
1716
1717 if (unreachableURL.isEmpty())
1718 return false;
1719
1720 if (!isBackForwardLoadType(policyChecker().loadType()))
1721 return false;
1722
1723 // We only treat unreachableURLs specially during the delegate callbacks
1724 // for provisional load errors and navigation policy decisions. The former
1725 // case handles well-formed URLs that can't be loaded, and the latter
1726 // case handles malformed URLs and unknown schemes. Loading alternate content
1727 // at other times behaves like a standard load.
1728 if (policyChecker().delegateIsDecidingNavigationPolicy() || policyChecker().delegateIsHandlingUnimplementablePolicy())
1729 return m_policyDocumentLoader && unreachableURL == m_policyDocumentLoader->request().url();
1730
1731 return unreachableURL == m_provisionalLoadErrorBeingHandledURL;
1732}
1733
1734void FrameLoader::reloadWithOverrideEncoding(const String& encoding)
1735{
1736 if (!m_documentLoader)
1737 return;
1738
1739 RELEASE_LOG_IF_ALLOWED("reloadWithOverrideEncoding: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1740
1741 ResourceRequest request = m_documentLoader->request();
1742 URL unreachableURL = m_documentLoader->unreachableURL();
1743 if (!unreachableURL.isEmpty())
1744 request.setURL(unreachableURL);
1745
1746 // FIXME: If the resource is a result of form submission and is not cached, the form will be silently resubmitted.
1747 // We should ask the user for confirmation in this case.
1748 request.setCachePolicy(ResourceRequestCachePolicy::ReturnCacheDataElseLoad);
1749
1750 Ref<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url()));
1751 applyShouldOpenExternalURLsPolicyToNewDocumentLoader(m_frame, loader, InitiatedByMainFrame::Unknown, m_documentLoader->shouldOpenExternalURLsPolicyToPropagate());
1752
1753 setPolicyDocumentLoader(loader.ptr());
1754
1755 loader->setOverrideEncoding(encoding);
1756
1757 loadWithDocumentLoader(loader.ptr(), FrameLoadType::Reload, { }, AllowNavigationToInvalidURL::Yes, ShouldTreatAsContinuingLoad::No);
1758}
1759
1760void FrameLoader::reload(OptionSet<ReloadOption> options)
1761{
1762 if (!m_documentLoader)
1763 return;
1764
1765 // If a window is created by javascript, its main frame can have an empty but non-nil URL.
1766 // Reloading in this case will lose the current contents (see 4151001).
1767 if (m_documentLoader->request().url().isEmpty())
1768 return;
1769
1770 RELEASE_LOG_IF_ALLOWED("reload: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1771
1772 // Replace error-page URL with the URL we were trying to reach.
1773 ResourceRequest initialRequest = m_documentLoader->request();
1774 URL unreachableURL = m_documentLoader->unreachableURL();
1775 if (!unreachableURL.isEmpty())
1776 initialRequest.setURL(unreachableURL);
1777
1778 // Create a new document loader for the reload, this will become m_documentLoader eventually,
1779 // but first it has to be the "policy" document loader, and then the "provisional" document loader.
1780 Ref<DocumentLoader> loader = m_client.createDocumentLoader(initialRequest, defaultSubstituteDataForURL(initialRequest.url()));
1781 loader->setAllowsWebArchiveForMainFrame(m_documentLoader->allowsWebArchiveForMainFrame());
1782 applyShouldOpenExternalURLsPolicyToNewDocumentLoader(m_frame, loader, InitiatedByMainFrame::Unknown, m_documentLoader->shouldOpenExternalURLsPolicyToPropagate());
1783
1784 loader->setUserContentExtensionsEnabled(!options.contains(ReloadOption::DisableContentBlockers));
1785
1786 ResourceRequest& request = loader->request();
1787
1788 // FIXME: We don't have a mechanism to revalidate the main resource without reloading at the moment.
1789 request.setCachePolicy(ResourceRequestCachePolicy::ReloadIgnoringCacheData);
1790
1791 addSameSiteInfoToRequestIfNeeded(request);
1792
1793 // If we're about to re-post, set up action so the application can warn the user.
1794 if (request.httpMethod() == "POST")
1795 loader->setTriggeringAction({ *m_frame.document(), request, InitiatedByMainFrame::Unknown, NavigationType::FormResubmitted });
1796
1797 loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
1798
1799 auto frameLoadTypeForReloadOptions = [] (auto options) {
1800 if (options & ReloadOption::FromOrigin)
1801 return FrameLoadType::ReloadFromOrigin;
1802 if (options & ReloadOption::ExpiredOnly)
1803 return FrameLoadType::ReloadExpiredOnly;
1804 return FrameLoadType::Reload;
1805 };
1806
1807 loadWithDocumentLoader(loader.ptr(), frameLoadTypeForReloadOptions(options), { }, AllowNavigationToInvalidURL::Yes, ShouldTreatAsContinuingLoad::No);
1808}
1809
1810void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItemPolicy)
1811{
1812 if (m_frame.document() && m_frame.document()->pageCacheState() == Document::InPageCache)
1813 return;
1814
1815 if (!isStopLoadingAllowed())
1816 return;
1817
1818 // If this method is called from within this method, infinite recursion can occur (3442218). Avoid this.
1819 if (m_inStopAllLoaders)
1820 return;
1821
1822 // This method might dispatch events.
1823 RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::InMainThread::isScriptAllowed());
1824
1825 // Calling stopLoading() on the provisional document loader can blow away
1826 // the frame from underneath.
1827 Ref<Frame> protect(m_frame);
1828
1829 m_inStopAllLoaders = true;
1830
1831 policyChecker().stopCheck();
1832
1833 // If no new load is in progress, we should clear the provisional item from history
1834 // before we call stopLoading.
1835 if (clearProvisionalItemPolicy == ShouldClearProvisionalItem)
1836 history().setProvisionalItem(nullptr);
1837
1838 for (RefPtr<Frame> child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling())
1839 child->loader().stopAllLoaders(clearProvisionalItemPolicy);
1840 if (m_provisionalDocumentLoader)
1841 m_provisionalDocumentLoader->stopLoading();
1842 if (m_documentLoader)
1843 m_documentLoader->stopLoading();
1844
1845 setProvisionalDocumentLoader(nullptr);
1846
1847 m_inStopAllLoaders = false;
1848}
1849
1850void FrameLoader::stopAllLoadersAndCheckCompleteness()
1851{
1852 stopAllLoaders();
1853
1854 if (!m_checkTimer.isActive())
1855 return;
1856
1857 m_checkTimer.stop();
1858 m_checkingLoadCompleteForDetachment = true;
1859 checkCompletenessNow();
1860 m_checkingLoadCompleteForDetachment = false;
1861}
1862
1863void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete)
1864{
1865 // Calling stopAllLoaders can cause the frame to be deallocated, including the frame loader.
1866 Ref<Frame> protectedFrame(m_frame);
1867
1868 stopAllLoaders();
1869
1870#if PLATFORM(IOS_FAMILY)
1871 // Lay out immediately when stopping to immediately clear the old page if we just committed this one
1872 // but haven't laid out/painted yet.
1873 // FIXME: Is this behavior specific to iOS? Or should we expose a setting to toggle this behavior?
1874 if (m_frame.view() && !m_frame.view()->didFirstLayout())
1875 m_frame.view()->layoutContext().layout();
1876#endif
1877
1878 if (deferCheckLoadComplete)
1879 scheduleCheckLoadComplete();
1880 else if (m_frame.page())
1881 checkLoadComplete();
1882}
1883
1884DocumentLoader* FrameLoader::activeDocumentLoader() const
1885{
1886 if (m_state == FrameStateProvisional)
1887 return m_provisionalDocumentLoader.get();
1888 return m_documentLoader.get();
1889}
1890
1891bool FrameLoader::isLoading() const
1892{
1893 DocumentLoader* docLoader = activeDocumentLoader();
1894 if (!docLoader)
1895 return false;
1896 return docLoader->isLoading();
1897}
1898
1899bool FrameLoader::frameHasLoaded() const
1900{
1901 return m_stateMachine.committedFirstRealDocumentLoad() || (m_provisionalDocumentLoader && !m_stateMachine.creatingInitialEmptyDocument());
1902}
1903
1904void FrameLoader::setDocumentLoader(DocumentLoader* loader)
1905{
1906 if (!loader && !m_documentLoader)
1907 return;
1908
1909 if (loader == m_documentLoader)
1910 return;
1911
1912 ASSERT(loader != m_documentLoader);
1913 ASSERT(!loader || loader->frameLoader() == this);
1914
1915 m_client.prepareForDataSourceReplacement();
1916 detachChildren();
1917
1918 // detachChildren() can trigger this frame's unload event, and therefore
1919 // script can run and do just about anything. For example, an unload event that calls
1920 // document.write("") on its parent frame can lead to a recursive detachChildren()
1921 // invocation for this frame. In that case, we can end up at this point with a
1922 // loader that hasn't been deleted but has been detached from its frame. Such a
1923 // DocumentLoader has been sufficiently detached that we'll end up in an inconsistent
1924 // state if we try to use it.
1925 if (loader && !loader->frame())
1926 return;
1927
1928 if (m_documentLoader)
1929 m_documentLoader->detachFromFrame();
1930
1931 m_documentLoader = loader;
1932}
1933
1934void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader)
1935{
1936 if (m_policyDocumentLoader == loader)
1937 return;
1938
1939 if (loader)
1940 loader->attachToFrame(m_frame);
1941 if (m_policyDocumentLoader
1942 && m_policyDocumentLoader != m_provisionalDocumentLoader
1943 && m_policyDocumentLoader != m_documentLoader)
1944 m_policyDocumentLoader->detachFromFrame();
1945
1946 m_policyDocumentLoader = loader;
1947}
1948
1949void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader)
1950{
1951 ASSERT(!loader || !m_provisionalDocumentLoader);
1952 ASSERT(!loader || loader->frameLoader() == this);
1953
1954 if (m_provisionalDocumentLoader && m_provisionalDocumentLoader != m_documentLoader)
1955 m_provisionalDocumentLoader->detachFromFrame();
1956
1957 m_provisionalDocumentLoader = loader;
1958}
1959
1960void FrameLoader::setState(FrameState newState)
1961{
1962 FrameState oldState = m_state;
1963 m_state = newState;
1964
1965 if (newState == FrameStateProvisional)
1966 provisionalLoadStarted();
1967 else if (newState == FrameStateComplete) {
1968 frameLoadCompleted();
1969 if (m_documentLoader)
1970 m_documentLoader->stopRecordingResponses();
1971 if (m_frame.isMainFrame() && oldState != newState) {
1972 RELEASE_LOG_IF_ALLOWED("setState: main frame load completed (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
1973 m_frame.page()->performanceLogging().didReachPointOfInterest(PerformanceLogging::MainFrameLoadCompleted);
1974 }
1975 }
1976}
1977
1978void FrameLoader::clearProvisionalLoad()
1979{
1980 setProvisionalDocumentLoader(nullptr);
1981 m_progressTracker->progressCompleted();
1982 setState(FrameStateComplete);
1983}
1984
1985void FrameLoader::commitProvisionalLoad()
1986{
1987 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
1988 Ref<Frame> protect(m_frame);
1989
1990 std::unique_ptr<CachedPage> cachedPage;
1991 if (m_loadingFromCachedPage && history().provisionalItem())
1992 cachedPage = PageCache::singleton().take(*history().provisionalItem(), m_frame.page());
1993
1994 LOG(PageCache, "WebCoreLoading %s: About to commit provisional load from previous URL '%s' to new URL '%s' with cached page %p", m_frame.tree().uniqueName().string().utf8().data(),
1995 m_frame.document() ? m_frame.document()->url().stringCenterEllipsizedToLength().utf8().data() : "",
1996 pdl ? pdl->url().stringCenterEllipsizedToLength().utf8().data() : "<no provisional DocumentLoader>", cachedPage.get());
1997
1998 willTransitionToCommitted();
1999
2000 if (!m_frame.tree().parent() && history().currentItem() && history().currentItem() != history().provisionalItem()) {
2001 // Check to see if we need to cache the page we are navigating away from into the back/forward cache.
2002 // We are doing this here because we know for sure that a new page is about to be loaded.
2003 PageCache::singleton().addIfCacheable(*history().currentItem(), m_frame.page());
2004
2005 WebCore::jettisonExpensiveObjectsOnTopLevelNavigation();
2006 }
2007
2008 if (m_loadType != FrameLoadType::Replace)
2009 closeOldDataSources();
2010
2011 if (!cachedPage && !m_stateMachine.creatingInitialEmptyDocument())
2012 m_client.makeRepresentation(pdl.get());
2013
2014 transitionToCommitted(cachedPage.get());
2015
2016 if (pdl && m_documentLoader) {
2017 // Check if the destination page is allowed to access the previous page's timing information.
2018 Ref<SecurityOrigin> securityOrigin(SecurityOrigin::create(pdl->request().url()));
2019 m_documentLoader->timing().setHasSameOriginAsPreviousDocument(securityOrigin.get().canRequest(m_previousURL));
2020 }
2021
2022 // Call clientRedirectCancelledOrFinished() here so that the frame load delegate is notified that the redirect's
2023 // status has changed, if there was a redirect. The frame load delegate may have saved some state about
2024 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are
2025 // just about to commit a new page, there cannot possibly be a pending redirect at this point.
2026 if (m_sentRedirectNotification)
2027 clientRedirectCancelledOrFinished(NewLoadInProgress::No);
2028
2029 if (cachedPage && cachedPage->document()) {
2030#if PLATFORM(IOS_FAMILY)
2031 // FIXME: CachedPage::restore() would dispatch viewport change notification. However UIKit expects load
2032 // commit to happen before any changes to viewport arguments and dealing with this there is difficult.
2033 m_frame.page()->chrome().setDispatchViewportDataDidChangeSuppressed(true);
2034#endif
2035 willRestoreFromCachedPage();
2036
2037 // Start request for the main resource and dispatch didReceiveResponse before the load is committed for
2038 // consistency with all other loads. See https://bugs.webkit.org/show_bug.cgi?id=150927.
2039 ResourceError mainResouceError;
2040 unsigned long mainResourceIdentifier;
2041 ResourceRequest mainResourceRequest(cachedPage->documentLoader()->request());
2042 requestFromDelegate(mainResourceRequest, mainResourceIdentifier, mainResouceError);
2043 notifier().dispatchDidReceiveResponse(cachedPage->documentLoader(), mainResourceIdentifier, cachedPage->documentLoader()->response());
2044
2045 Optional<HasInsecureContent> hasInsecureContent = cachedPage->cachedMainFrame()->hasInsecureContent();
2046
2047 dispatchDidCommitLoad(hasInsecureContent);
2048
2049 // FIXME: This API should be turned around so that we ground CachedPage into the Page.
2050 cachedPage->restore(*m_frame.page());
2051
2052#if PLATFORM(IOS_FAMILY)
2053 m_frame.page()->chrome().setDispatchViewportDataDidChangeSuppressed(false);
2054 m_frame.page()->chrome().dispatchViewportPropertiesDidChange(m_frame.page()->viewportArguments());
2055#endif
2056 m_frame.page()->chrome().dispatchDisabledAdaptationsDidChange(m_frame.page()->disabledAdaptations());
2057
2058 auto& title = m_documentLoader->title();
2059 if (!title.string.isNull())
2060 m_client.dispatchDidReceiveTitle(title);
2061
2062 // Send remaining notifications for the main resource.
2063 notifier().sendRemainingDelegateMessages(m_documentLoader.get(), mainResourceIdentifier, mainResourceRequest, ResourceResponse(),
2064 nullptr, static_cast<int>(m_documentLoader->response().expectedContentLength()), 0, mainResouceError);
2065
2066 checkCompleted();
2067 } else
2068 didOpenURL();
2069
2070 LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame.tree().uniqueName().string().utf8().data(),
2071 m_frame.document() ? m_frame.document()->url().stringCenterEllipsizedToLength().utf8().data() : "");
2072
2073 if (m_loadType == FrameLoadType::Standard && m_documentLoader && m_documentLoader->isClientRedirect())
2074 history().updateForClientRedirect();
2075
2076 if (m_loadingFromCachedPage) {
2077 // Note, didReceiveDocType is expected to be called for cached pages. See <rdar://problem/5906758> for more details.
2078 if (auto* page = m_frame.page())
2079 page->chrome().didReceiveDocType(m_frame);
2080 m_frame.document()->resume(ReasonForSuspension::PageCache);
2081
2082 // Force a layout to update view size and thereby update scrollbars.
2083#if PLATFORM(IOS_FAMILY)
2084 if (!m_client.forceLayoutOnRestoreFromPageCache())
2085 m_frame.view()->forceLayout();
2086#else
2087 m_frame.view()->forceLayout();
2088#endif
2089
2090 // Main resource delegates were already sent, so we skip the first response here.
2091 for (unsigned i = 1; i < m_documentLoader->responses().size(); ++i) {
2092 const auto& response = m_documentLoader->responses()[i];
2093 // FIXME: If the WebKit client changes or cancels the request, this is not respected.
2094 ResourceError error;
2095 unsigned long identifier;
2096 ResourceRequest request(response.url());
2097 requestFromDelegate(request, identifier, error);
2098 // FIXME: If we get a resource with more than 2B bytes, this code won't do the right thing.
2099 // However, with today's computers and networking speeds, this won't happen in practice.
2100 // Could be an issue with a giant local file.
2101 notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, response, 0, static_cast<int>(response.expectedContentLength()), 0, error);
2102 }
2103
2104 // FIXME: Why only this frame and not parent frames?
2105 checkLoadCompleteForThisFrame();
2106 }
2107}
2108
2109void FrameLoader::transitionToCommitted(CachedPage* cachedPage)
2110{
2111 ASSERT(m_client.hasWebView());
2112 ASSERT(m_state == FrameStateProvisional);
2113
2114 if (m_state != FrameStateProvisional)
2115 return;
2116
2117 if (FrameView* view = m_frame.view()) {
2118 if (ScrollAnimator* scrollAnimator = view->existingScrollAnimator())
2119 scrollAnimator->cancelAnimations();
2120 }
2121
2122 m_client.setCopiesOnScroll();
2123 history().updateForCommit();
2124
2125 // The call to closeURL() invokes the unload event handler, which can execute arbitrary
2126 // JavaScript. If the script initiates a new load, we need to abandon the current load,
2127 // or the two will stomp each other.
2128 DocumentLoader* pdl = m_provisionalDocumentLoader.get();
2129 if (m_documentLoader)
2130 closeURL();
2131 if (pdl != m_provisionalDocumentLoader)
2132 return;
2133
2134 if (m_documentLoader)
2135 m_documentLoader->stopLoadingSubresources();
2136 if (m_documentLoader)
2137 m_documentLoader->stopLoadingPlugIns();
2138
2139 // Setting our document loader invokes the unload event handler of our child frames.
2140 // Script can do anything. If the script initiates a new load, we need to abandon the
2141 // current load or the two will stomp each other.
2142 setDocumentLoader(m_provisionalDocumentLoader.get());
2143 if (pdl != m_provisionalDocumentLoader)
2144 return;
2145 setProvisionalDocumentLoader(nullptr);
2146
2147 // Nothing else can interrupt this commit - set the Provisional->Committed transition in stone
2148 setState(FrameStateCommittedPage);
2149
2150 // Handle adding the URL to the back/forward list.
2151 DocumentLoader* dl = m_documentLoader.get();
2152
2153 switch (m_loadType) {
2154 case FrameLoadType::Forward:
2155 case FrameLoadType::Back:
2156 case FrameLoadType::IndexedBackForward:
2157 if (m_frame.page()) {
2158 // If the first load within a frame is a navigation within a back/forward list that was attached
2159 // without any of the items being loaded then we need to update the history in a similar manner as
2160 // for a standard load with the exception of updating the back/forward list (<rdar://problem/8091103>).
2161 if (!m_stateMachine.committedFirstRealDocumentLoad() && m_frame.isMainFrame())
2162 history().updateForStandardLoad(HistoryController::UpdateAllExceptBackForwardList);
2163
2164 history().updateForBackForwardNavigation();
2165
2166 // For cached pages, CachedFrame::restore will take care of firing the popstate event with the history item's state object
2167 if (history().currentItem() && !cachedPage)
2168 m_pendingStateObject = history().currentItem()->stateObject();
2169
2170 // Create a document view for this document, or used the cached view.
2171 if (cachedPage) {
2172 DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader();
2173 ASSERT(cachedDocumentLoader);
2174 cachedDocumentLoader->attachToFrame(m_frame);
2175 m_client.transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame());
2176 } else
2177 m_client.transitionToCommittedForNewPage();
2178 }
2179 break;
2180
2181 case FrameLoadType::Reload:
2182 case FrameLoadType::ReloadFromOrigin:
2183 case FrameLoadType::ReloadExpiredOnly:
2184 case FrameLoadType::Same:
2185 case FrameLoadType::Replace:
2186 history().updateForReload();
2187 m_client.transitionToCommittedForNewPage();
2188 break;
2189
2190 case FrameLoadType::Standard:
2191 history().updateForStandardLoad();
2192 if (m_frame.view())
2193 m_frame.view()->setScrollbarsSuppressed(true);
2194 m_client.transitionToCommittedForNewPage();
2195 break;
2196
2197 case FrameLoadType::RedirectWithLockedBackForwardList:
2198 history().updateForRedirectWithLockedBackForwardList();
2199 m_client.transitionToCommittedForNewPage();
2200 break;
2201 }
2202
2203 m_documentLoader->writer().setMIMEType(dl->responseMIMEType());
2204
2205 // Tell the client we've committed this URL.
2206 ASSERT(m_frame.view());
2207
2208 if (m_stateMachine.creatingInitialEmptyDocument())
2209 return;
2210
2211 if (!m_stateMachine.committedFirstRealDocumentLoad())
2212 m_stateMachine.advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit);
2213}
2214
2215void FrameLoader::clientRedirectCancelledOrFinished(NewLoadInProgress newLoadInProgress)
2216{
2217 // Note that -webView:didCancelClientRedirectForFrame: is called on the frame load delegate even if
2218 // the redirect succeeded. We should either rename this API, or add a new method, like
2219 // -webView:didFinishClientRedirectForFrame:
2220 m_client.dispatchDidCancelClientRedirect();
2221
2222 if (newLoadInProgress == NewLoadInProgress::No)
2223 m_quickRedirectComing = false;
2224
2225 m_sentRedirectNotification = false;
2226}
2227
2228void FrameLoader::clientRedirected(const URL& url, double seconds, WallTime fireDate, LockBackForwardList lockBackForwardList)
2229{
2230 m_client.dispatchWillPerformClientRedirect(url, seconds, fireDate, lockBackForwardList);
2231
2232 // Remember that we sent a redirect notification to the frame load delegate so that when we commit
2233 // the next provisional load, we can send a corresponding -webView:didCancelClientRedirectForFrame:
2234 m_sentRedirectNotification = true;
2235
2236 // If a "quick" redirect comes in, we set a special mode so we treat the next
2237 // load as part of the original navigation. If we don't have a document loader, we have
2238 // no "original" load on which to base a redirect, so we treat the redirect as a normal load.
2239 // Loads triggered by JavaScript form submissions never count as quick redirects.
2240 m_quickRedirectComing = (lockBackForwardList == LockBackForwardList::Yes || history().currentItemShouldBeReplaced()) && m_documentLoader && !m_isExecutingJavaScriptFormAction;
2241}
2242
2243bool FrameLoader::shouldReload(const URL& currentURL, const URL& destinationURL)
2244{
2245 // This function implements the rule: "Don't reload if navigating by fragment within
2246 // the same URL, but do reload if going to a new URL or to the same URL with no
2247 // fragment identifier at all."
2248 if (!destinationURL.hasFragmentIdentifier())
2249 return true;
2250 return !equalIgnoringFragmentIdentifier(currentURL, destinationURL);
2251}
2252
2253void FrameLoader::closeOldDataSources()
2254{
2255 // FIXME: Is it important for this traversal to be postorder instead of preorder?
2256 // If so, add helpers for postorder traversal, and use them. If not, then lets not
2257 // use a recursive algorithm here.
2258 for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling())
2259 child->loader().closeOldDataSources();
2260
2261 if (m_documentLoader)
2262 m_client.dispatchWillClose();
2263
2264 m_client.setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers
2265}
2266
2267void FrameLoader::willRestoreFromCachedPage()
2268{
2269 ASSERT(!m_frame.tree().parent());
2270 ASSERT(m_frame.page());
2271 ASSERT(m_frame.isMainFrame());
2272
2273 m_frame.navigationScheduler().cancel();
2274
2275 // We still have to close the previous part page.
2276 closeURL();
2277
2278 // Delete old status bar messages (if it _was_ activated on last URL).
2279 if (m_frame.script().canExecuteScripts(NotAboutToExecuteScript)) {
2280 DOMWindow* window = m_frame.document()->domWindow();
2281 window->setStatus(String());
2282 window->setDefaultStatus(String());
2283 }
2284}
2285
2286void FrameLoader::open(CachedFrameBase& cachedFrame)
2287{
2288 m_isComplete = false;
2289
2290 // Don't re-emit the load event.
2291 m_didCallImplicitClose = true;
2292
2293 URL url = cachedFrame.url();
2294
2295 // FIXME: I suspect this block of code doesn't do anything.
2296 if (url.protocolIsInHTTPFamily() && !url.host().isEmpty() && url.path().isEmpty())
2297 url.setPath("/");
2298
2299 started();
2300 Document* document = cachedFrame.document();
2301 ASSERT(document);
2302 ASSERT(document->domWindow());
2303
2304 clear(document, true, true, cachedFrame.isMainFrame());
2305
2306 document->setPageCacheState(Document::NotInPageCache);
2307
2308 m_needsClear = true;
2309 m_isComplete = false;
2310 m_didCallImplicitClose = false;
2311 m_outgoingReferrer = url.string();
2312
2313 FrameView* view = cachedFrame.view();
2314
2315 // When navigating to a CachedFrame its FrameView should never be null. If it is we'll crash in creative ways downstream.
2316 ASSERT(view);
2317 view->setWasScrolledByUser(false);
2318
2319 Optional<IntRect> previousViewFrameRect = m_frame.view() ? m_frame.view()->frameRect() : Optional<IntRect>(WTF::nullopt);
2320 m_frame.setView(view);
2321
2322 // Use the previous ScrollView's frame rect.
2323 if (previousViewFrameRect)
2324 view->setFrameRect(previousViewFrameRect.value());
2325
2326 {
2327 // Setting the document builds the render tree and runs post style resolution callbacks that can do anything,
2328 // including loading a child frame before its been re-attached to the frame tree as part of this restore.
2329 // For example, the HTML object element may load its content into a frame in a post style resolution callback.
2330 NavigationDisabler disableNavigation { &m_frame };
2331 m_frame.setDocument(document);
2332 }
2333
2334 document->domWindow()->resumeFromPageCache();
2335
2336 updateFirstPartyForCookies();
2337
2338 cachedFrame.restore();
2339}
2340
2341bool FrameLoader::isHostedByObjectElement() const
2342{
2343 HTMLFrameOwnerElement* owner = m_frame.ownerElement();
2344 return owner && owner->hasTagName(objectTag);
2345}
2346
2347bool FrameLoader::isReplacing() const
2348{
2349 return m_loadType == FrameLoadType::Replace;
2350}
2351
2352void FrameLoader::setReplacing()
2353{
2354 m_loadType = FrameLoadType::Replace;
2355}
2356
2357bool FrameLoader::subframeIsLoading() const
2358{
2359 // It's most likely that the last added frame is the last to load so we walk backwards.
2360 for (Frame* child = m_frame.tree().lastChild(); child; child = child->tree().previousSibling()) {
2361 FrameLoader& childLoader = child->loader();
2362 DocumentLoader* documentLoader = childLoader.documentLoader();
2363 if (documentLoader && documentLoader->isLoadingInAPISense())
2364 return true;
2365 documentLoader = childLoader.provisionalDocumentLoader();
2366 if (documentLoader && documentLoader->isLoadingInAPISense())
2367 return true;
2368 documentLoader = childLoader.policyDocumentLoader();
2369 if (documentLoader)
2370 return true;
2371 }
2372 return false;
2373}
2374
2375void FrameLoader::willChangeTitle(DocumentLoader* loader)
2376{
2377 m_client.willChangeTitle(loader);
2378}
2379
2380FrameLoadType FrameLoader::loadType() const
2381{
2382 return m_loadType;
2383}
2384
2385CachePolicy FrameLoader::subresourceCachePolicy(const URL& url) const
2386{
2387 if (Page* page = m_frame.page()) {
2388 if (page->isResourceCachingDisabled())
2389 return CachePolicyReload;
2390 }
2391
2392 if (m_isComplete)
2393 return CachePolicyVerify;
2394
2395 if (m_loadType == FrameLoadType::ReloadFromOrigin)
2396 return CachePolicyReload;
2397
2398 if (Frame* parentFrame = m_frame.tree().parent()) {
2399 CachePolicy parentCachePolicy = parentFrame->loader().subresourceCachePolicy(url);
2400 if (parentCachePolicy != CachePolicyVerify)
2401 return parentCachePolicy;
2402 }
2403
2404 switch (m_loadType) {
2405 case FrameLoadType::Reload:
2406 return CachePolicyRevalidate;
2407 case FrameLoadType::Back:
2408 case FrameLoadType::Forward:
2409 case FrameLoadType::IndexedBackForward:
2410 return CachePolicyHistoryBuffer;
2411 case FrameLoadType::ReloadFromOrigin:
2412 ASSERT_NOT_REACHED(); // Already handled above.
2413 return CachePolicyReload;
2414 case FrameLoadType::RedirectWithLockedBackForwardList:
2415 case FrameLoadType::Replace:
2416 case FrameLoadType::Same:
2417 case FrameLoadType::Standard:
2418 return CachePolicyVerify;
2419 case FrameLoadType::ReloadExpiredOnly:
2420 // We know about expiration for HTTP and data. Do a normal reload otherwise.
2421 if (!url.protocolIsInHTTPFamily() && !url.protocolIsData())
2422 return CachePolicyReload;
2423 return CachePolicyVerify;
2424 }
2425
2426 RELEASE_ASSERT_NOT_REACHED();
2427 return CachePolicyVerify;
2428}
2429
2430void FrameLoader::dispatchDidFailProvisionalLoad(DocumentLoader& provisionalDocumentLoader, const ResourceError& error)
2431{
2432 m_provisionalLoadErrorBeingHandledURL = provisionalDocumentLoader.url();
2433
2434#if ENABLE(CONTENT_FILTERING)
2435 auto contentFilter = provisionalDocumentLoader.contentFilter();
2436 auto contentFilterWillContinueLoading = false;
2437#endif
2438
2439 auto willContinueLoading = WillContinueLoading::No;
2440 if (history().provisionalItem())
2441 willContinueLoading = WillContinueLoading::Yes;
2442#if ENABLE(CONTENT_FILTERING)
2443 if (contentFilter && contentFilter->willHandleProvisionalLoadFailure(error)) {
2444 willContinueLoading = WillContinueLoading::Yes;
2445 contentFilterWillContinueLoading = true;
2446 }
2447#endif
2448
2449 m_client.dispatchDidFailProvisionalLoad(error, willContinueLoading);
2450
2451#if ENABLE(CONTENT_FILTERING)
2452 if (contentFilterWillContinueLoading)
2453 contentFilter->handleProvisionalLoadFailure(error);
2454#endif
2455
2456 m_provisionalLoadErrorBeingHandledURL = { };
2457}
2458
2459void FrameLoader::checkLoadCompleteForThisFrame()
2460{
2461 ASSERT(m_client.hasWebView());
2462
2463 // FIXME: Should this check be done in checkLoadComplete instead of here?
2464 // FIXME: Why does this one check need to be repeated here, and not the many others from checkCompleted?
2465 if (m_frame.document()->isDelayingLoadEvent())
2466 return;
2467
2468 switch (m_state) {
2469 case FrameStateProvisional: {
2470 // FIXME: Prohibiting any provisional load failures from being sent to clients
2471 // while handling provisional load failures is too heavy. For example, the current
2472 // load will fail to cancel another ongoing load. That might prevent clients' page
2473 // load state being handled properly.
2474 if (!m_provisionalLoadErrorBeingHandledURL.isEmpty())
2475 return;
2476
2477 RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
2478 if (!pdl)
2479 return;
2480
2481 // If we've received any errors we may be stuck in the provisional state and actually complete.
2482 const ResourceError& error = pdl->mainDocumentError();
2483 if (error.isNull())
2484 return;
2485
2486 // Check all children first.
2487 RefPtr<HistoryItem> item;
2488 if (Page* page = m_frame.page())
2489 if (isBackForwardLoadType(loadType()))
2490 // Reset the back forward list to the last committed history item at the top level.
2491 item = page->mainFrame().loader().history().currentItem();
2492
2493 // Only reset if we aren't already going to a new provisional item.
2494 bool shouldReset = !history().provisionalItem();
2495 if (!pdl->isLoadingInAPISense() || pdl->isStopping()) {
2496 RELEASE_LOG_IF_ALLOWED("checkLoadCompleteForThisFrame: Failed provisional load (frame = %p, main = %d, isTimeout = %d, isCancellation = %d, errorCode = %d)", &m_frame, m_frame.isMainFrame(), error.isTimeout(), error.isCancellation(), error.errorCode());
2497
2498 dispatchDidFailProvisionalLoad(*pdl, error);
2499 ASSERT(!pdl->isLoading());
2500
2501 // If we're in the middle of loading multipart data, we need to restore the document loader.
2502 if (isReplacing() && !m_documentLoader.get())
2503 setDocumentLoader(m_provisionalDocumentLoader.get());
2504
2505 // Finish resetting the load state, but only if another load hasn't been started by the
2506 // delegate callback.
2507 if (pdl == m_provisionalDocumentLoader)
2508 clearProvisionalLoad();
2509 else if (activeDocumentLoader()) {
2510 URL unreachableURL = activeDocumentLoader()->unreachableURL();
2511 if (!unreachableURL.isEmpty() && unreachableURL == pdl->request().url())
2512 shouldReset = false;
2513 }
2514 }
2515 if (shouldReset && item)
2516 if (Page* page = m_frame.page()) {
2517 page->backForward().setCurrentItem(*item);
2518 }
2519 return;
2520 }
2521
2522 case FrameStateCommittedPage: {
2523 if (!m_documentLoader)
2524 return;
2525 if (m_documentLoader->isLoadingInAPISense() && !m_documentLoader->isStopping() && !m_checkingLoadCompleteForDetachment)
2526 return;
2527
2528 setState(FrameStateComplete);
2529
2530 // FIXME: Is this subsequent work important if we already navigated away?
2531 // Maybe there are bugs because of that, or extra work we can skip because
2532 // the new page is ready.
2533
2534 m_client.forceLayoutForNonHTML();
2535
2536 // If the user had a scroll point, scroll to it, overriding the anchor point if any.
2537 if (m_frame.page()) {
2538 if (isBackForwardLoadType(m_loadType) || isReload(m_loadType))
2539 history().restoreScrollPositionAndViewState();
2540 }
2541
2542 if (m_stateMachine.creatingInitialEmptyDocument() || !m_stateMachine.committedFirstRealDocumentLoad())
2543 return;
2544
2545 m_progressTracker->progressCompleted();
2546 Page* page = m_frame.page();
2547 if (page) {
2548 if (m_frame.isMainFrame()) {
2549 tracePoint(MainResourceLoadDidEnd);
2550 page->didFinishLoad();
2551 }
2552 }
2553
2554 const ResourceError& error = m_documentLoader->mainDocumentError();
2555
2556 AXObjectCache::AXLoadingEvent loadingEvent;
2557 if (!error.isNull()) {
2558 RELEASE_LOG_IF_ALLOWED("checkLoadCompleteForThisFrame: Finished frame load with error (frame = %p, main = %d, isTimeout = %d, isCancellation = %d, errorCode = %d)", &m_frame, m_frame.isMainFrame(), error.isTimeout(), error.isCancellation(), error.errorCode());
2559 m_client.dispatchDidFailLoad(error);
2560 loadingEvent = AXObjectCache::AXLoadingFailed;
2561 } else {
2562 RELEASE_LOG_IF_ALLOWED("checkLoadCompleteForThisFrame: Finished frame load (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
2563#if ENABLE(DATA_DETECTION)
2564 auto* document = m_frame.document();
2565 if (m_frame.settings().dataDetectorTypes() != DataDetectorTypeNone && document) {
2566 if (auto* documentElement = document->documentElement()) {
2567 RefPtr<Range> documentRange = makeRange(firstPositionInNode(documentElement), lastPositionInNode(documentElement));
2568 m_frame.setDataDetectionResults(DataDetection::detectContentInRange(documentRange, m_frame.settings().dataDetectorTypes(), m_client.dataDetectionContext()));
2569 if (m_frame.isMainFrame())
2570 m_client.dispatchDidFinishDataDetection(m_frame.dataDetectionResults());
2571 }
2572 }
2573#endif
2574 m_client.dispatchDidFinishLoad();
2575 loadingEvent = AXObjectCache::AXLoadingFinished;
2576 }
2577
2578 // Notify accessibility.
2579 if (auto* document = m_frame.document()) {
2580 if (AXObjectCache* cache = document->existingAXObjectCache())
2581 cache->frameLoadingEventNotification(&m_frame, loadingEvent);
2582 }
2583
2584 // The above calls to dispatchDidFinishLoad() might have detached the Frame
2585 // from its Page and also might have caused Page to be deleted.
2586 // Don't assume 'page' is still available to use.
2587 if (m_frame.isMainFrame() && m_frame.page()) {
2588 ASSERT(&m_frame.page()->mainFrame() == &m_frame);
2589 m_frame.page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageLoadedKey(), emptyString(), error.isNull() ? DiagnosticLoggingResultPass : DiagnosticLoggingResultFail, ShouldSample::Yes);
2590 }
2591
2592 return;
2593 }
2594
2595 case FrameStateComplete:
2596 m_loadType = FrameLoadType::Standard;
2597 frameLoadCompleted();
2598 return;
2599 }
2600
2601 ASSERT_NOT_REACHED();
2602}
2603
2604void FrameLoader::setOriginalURLForDownloadRequest(ResourceRequest& request)
2605{
2606 // FIXME: Rename firstPartyForCookies back to mainDocumentURL. It was a mistake to think that it was only used for cookies.
2607 // The originalURL is defined as the URL of the page where the download was initiated.
2608 URL originalURL;
2609 auto* initiator = m_frame.document();
2610 if (initiator) {
2611 originalURL = initiator->firstPartyForCookies();
2612 // If there is no main document URL, it means that this document is newly opened and just for download purpose.
2613 // In this case, we need to set the originalURL to this document's opener's main document URL.
2614 if (originalURL.isEmpty() && opener() && opener()->document()) {
2615 originalURL = opener()->document()->firstPartyForCookies();
2616 initiator = opener()->document();
2617 }
2618 }
2619 // If the originalURL is the same as the requested URL, we are processing a download
2620 // initiated directly without a page and do not need to specify the originalURL.
2621 if (originalURL == request.url())
2622 request.setFirstPartyForCookies(URL());
2623 else
2624 request.setFirstPartyForCookies(originalURL);
2625 addSameSiteInfoToRequestIfNeeded(request, initiator);
2626}
2627
2628void FrameLoader::didReachLayoutMilestone(OptionSet<LayoutMilestone> milestones)
2629{
2630 ASSERT(m_frame.isMainFrame());
2631
2632 m_client.dispatchDidReachLayoutMilestone(milestones);
2633}
2634
2635void FrameLoader::didFirstLayout()
2636{
2637#if PLATFORM(IOS_FAMILY)
2638 // Only send layout-related delegate callbacks synchronously for the main frame to
2639 // avoid reentering layout for the main frame while delivering a layout-related delegate
2640 // callback for a subframe.
2641 if (&m_frame != &m_frame.page()->mainFrame())
2642 return;
2643#endif
2644 if (m_frame.page() && isBackForwardLoadType(m_loadType))
2645 history().restoreScrollPositionAndViewState();
2646
2647 if (m_stateMachine.committedFirstRealDocumentLoad() && !m_stateMachine.isDisplayingInitialEmptyDocument() && !m_stateMachine.firstLayoutDone())
2648 m_stateMachine.advanceTo(FrameLoaderStateMachine::FirstLayoutDone);
2649}
2650
2651void FrameLoader::frameLoadCompleted()
2652{
2653 // Note: Can be called multiple times.
2654
2655 m_client.frameLoadCompleted();
2656
2657 history().updateForFrameLoadCompleted();
2658
2659 // After a canceled provisional load, firstLayoutDone is false.
2660 // Reset it to true if we're displaying a page.
2661 if (m_documentLoader && m_stateMachine.committedFirstRealDocumentLoad() && !m_stateMachine.isDisplayingInitialEmptyDocument() && !m_stateMachine.firstLayoutDone())
2662 m_stateMachine.advanceTo(FrameLoaderStateMachine::FirstLayoutDone);
2663}
2664
2665void FrameLoader::detachChildren()
2666{
2667 // detachChildren() will fire the unload event in each subframe and the
2668 // HTML specification states that the parent document's ignore-opens-during-unload counter while
2669 // this event is being fired in its subframes:
2670 // https://html.spec.whatwg.org/multipage/browsers.html#unload-a-document
2671 IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(m_frame.document());
2672
2673 // Any subframe inserted by unload event handlers executed in the loop below will not get unloaded
2674 // because we create a copy of the subframes list before looping. Therefore, it would be unsafe to
2675 // allow loading of subframes at this point.
2676 SubframeLoadingDisabler subframeLoadingDisabler(m_frame.document());
2677
2678 Vector<Ref<Frame>, 16> childrenToDetach;
2679 childrenToDetach.reserveInitialCapacity(m_frame.tree().childCount());
2680 for (Frame* child = m_frame.tree().lastChild(); child; child = child->tree().previousSibling())
2681 childrenToDetach.uncheckedAppend(*child);
2682 for (auto& child : childrenToDetach)
2683 child->loader().detachFromParent();
2684}
2685
2686void FrameLoader::closeAndRemoveChild(Frame& child)
2687{
2688 child.tree().detachFromParent();
2689
2690 child.setView(nullptr);
2691 if (child.ownerElement() && child.page())
2692 child.page()->decrementSubframeCount();
2693 child.willDetachPage();
2694 child.detachFromPage();
2695
2696 m_frame.tree().removeChild(child);
2697}
2698
2699// Called every time a resource is completely loaded or an error is received.
2700void FrameLoader::checkLoadComplete()
2701{
2702 m_shouldCallCheckLoadComplete = false;
2703
2704 if (!m_frame.page())
2705 return;
2706
2707 ASSERT(m_client.hasWebView());
2708
2709 // FIXME: Always traversing the entire frame tree is a bit inefficient, but
2710 // is currently needed in order to null out the previous history item for all frames.
2711 Vector<Ref<Frame>, 16> frames;
2712 for (Frame* frame = &m_frame.mainFrame(); frame; frame = frame->tree().traverseNext())
2713 frames.append(*frame);
2714
2715 // To process children before their parents, iterate the vector backwards.
2716 for (auto frame = frames.rbegin(); frame != frames.rend(); ++frame) {
2717 if ((*frame)->page())
2718 (*frame)->loader().checkLoadCompleteForThisFrame();
2719 }
2720}
2721
2722int FrameLoader::numPendingOrLoadingRequests(bool recurse) const
2723{
2724 if (!recurse)
2725 return m_frame.document()->cachedResourceLoader().requestCount();
2726
2727 int count = 0;
2728 for (Frame* frame = &m_frame; frame; frame = frame->tree().traverseNext(&m_frame))
2729 count += frame->document()->cachedResourceLoader().requestCount();
2730 return count;
2731}
2732
2733String FrameLoader::userAgent(const URL& url) const
2734{
2735 String userAgent;
2736
2737 if (auto* documentLoader = m_frame.mainFrame().loader().activeDocumentLoader())
2738 userAgent = documentLoader->customUserAgent();
2739
2740 InspectorInstrumentation::applyUserAgentOverride(m_frame, userAgent);
2741
2742 if (!userAgent.isEmpty())
2743 return userAgent;
2744
2745 return m_client.userAgent(url);
2746}
2747
2748String FrameLoader::userAgentForJavaScript(const URL& url) const
2749{
2750 String userAgent;
2751
2752 if (auto* documentLoader = m_frame.mainFrame().loader().activeDocumentLoader()) {
2753 if (m_frame.settings().needsSiteSpecificQuirks())
2754 userAgent = documentLoader->customJavaScriptUserAgentAsSiteSpecificQuirks();
2755 if (userAgent.isEmpty())
2756 userAgent = documentLoader->customUserAgent();
2757 }
2758
2759 InspectorInstrumentation::applyUserAgentOverride(m_frame, userAgent);
2760
2761 if (!userAgent.isEmpty())
2762 return userAgent;
2763
2764 return m_client.userAgent(url);
2765}
2766
2767String FrameLoader::navigatorPlatform() const
2768{
2769 if (auto* documentLoader = m_frame.mainFrame().loader().activeDocumentLoader()) {
2770 auto& customNavigatorPlatform = documentLoader->customNavigatorPlatform();
2771 if (!customNavigatorPlatform.isEmpty())
2772 return customNavigatorPlatform;
2773 }
2774 return String();
2775}
2776
2777void FrameLoader::dispatchOnloadEvents()
2778{
2779 m_client.dispatchDidDispatchOnloadEvents();
2780
2781 if (documentLoader())
2782 documentLoader()->dispatchOnloadEvents();
2783}
2784
2785void FrameLoader::frameDetached()
2786{
2787 // Calling stopAllLoadersAndCheckCompleteness() can cause the frame to be deallocated, including the frame loader.
2788 Ref<Frame> protectedFrame(m_frame);
2789
2790 if (m_checkTimer.isActive()) {
2791 m_checkTimer.stop();
2792 checkCompletenessNow();
2793 }
2794
2795 if (m_frame.document()->pageCacheState() != Document::InPageCache) {
2796 stopAllLoadersAndCheckCompleteness();
2797 m_frame.document()->stopActiveDOMObjects();
2798 }
2799
2800 detachFromParent();
2801}
2802
2803void FrameLoader::detachFromParent()
2804{
2805 // Calling stopAllLoaders() can cause the frame to be deallocated, including the frame loader.
2806 Ref<Frame> protect(m_frame);
2807
2808 closeURL();
2809 history().saveScrollPositionAndViewStateToItem(history().currentItem());
2810 detachChildren();
2811 if (m_frame.document()->pageCacheState() != Document::InPageCache) {
2812 // stopAllLoaders() needs to be called after detachChildren() if the document is not in the page cache,
2813 // because detachedChildren() will trigger the unload event handlers of any child frames, and those event
2814 // handlers might start a new subresource load in this frame.
2815 stopAllLoaders();
2816 }
2817
2818 InspectorInstrumentation::frameDetachedFromParent(m_frame);
2819
2820 detachViewsAndDocumentLoader();
2821
2822 m_progressTracker = nullptr;
2823
2824 if (Frame* parent = m_frame.tree().parent()) {
2825 parent->loader().closeAndRemoveChild(m_frame);
2826 parent->loader().scheduleCheckCompleted();
2827 parent->loader().scheduleCheckLoadComplete();
2828 } else {
2829 m_frame.setView(nullptr);
2830 m_frame.willDetachPage();
2831 m_frame.detachFromPage();
2832 }
2833}
2834
2835void FrameLoader::detachViewsAndDocumentLoader()
2836{
2837 m_client.detachedFromParent2();
2838 setDocumentLoader(nullptr);
2839 m_client.detachedFromParent3();
2840}
2841
2842void FrameLoader::addExtraFieldsToSubresourceRequest(ResourceRequest& request)
2843{
2844 addExtraFieldsToRequest(request, m_loadType, false);
2845}
2846
2847void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request)
2848{
2849 // FIXME: Using m_loadType seems wrong for some callers.
2850 // If we are only preparing to load the main resource, that is previous load's load type!
2851 addExtraFieldsToRequest(request, m_loadType, true);
2852
2853 // Upgrade-Insecure-Requests should only be added to main resource requests
2854 addHTTPUpgradeInsecureRequestsIfNeeded(request);
2855}
2856
2857ResourceRequestCachePolicy FrameLoader::defaultRequestCachingPolicy(const ResourceRequest& request, FrameLoadType loadType, bool isMainResource)
2858{
2859 if (m_overrideCachePolicyForTesting)
2860 return m_overrideCachePolicyForTesting.value();
2861
2862 if (isMainResource) {
2863 if (isReload(loadType) || request.isConditional())
2864 return ResourceRequestCachePolicy::ReloadIgnoringCacheData;
2865
2866 return ResourceRequestCachePolicy::UseProtocolCachePolicy;
2867 }
2868
2869 if (request.isConditional())
2870 return ResourceRequestCachePolicy::ReloadIgnoringCacheData;
2871
2872 if (documentLoader()->isLoadingInAPISense()) {
2873 // If we inherit cache policy from a main resource, we use the DocumentLoader's
2874 // original request cache policy for two reasons:
2875 // 1. For POST requests, we mutate the cache policy for the main resource,
2876 // but we do not want this to apply to subresources
2877 // 2. Delegates that modify the cache policy using willSendRequest: should
2878 // not affect any other resources. Such changes need to be done
2879 // per request.
2880 ResourceRequestCachePolicy mainDocumentOriginalCachePolicy = documentLoader()->originalRequest().cachePolicy();
2881 // Back-forward navigations try to load main resource from cache only to avoid re-submitting form data, and start over (with a warning dialog) if that fails.
2882 // This policy is set on initial request too, but should not be inherited.
2883 return (mainDocumentOriginalCachePolicy == ResourceRequestCachePolicy::ReturnCacheDataDontLoad) ? ResourceRequestCachePolicy::ReturnCacheDataElseLoad : mainDocumentOriginalCachePolicy;
2884 }
2885
2886 return ResourceRequestCachePolicy::UseProtocolCachePolicy;
2887}
2888
2889void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool isMainResource)
2890{
2891 // If the request came from a previous process due to process-swap-on-navigation then we should not modify the request.
2892 if (m_currentLoadContinuingState == LoadContinuingState::ContinuingWithRequest)
2893 return;
2894
2895 // Don't set the cookie policy URL if it's already been set.
2896 // But make sure to set it on all requests regardless of protocol, as it has significance beyond the cookie policy (<rdar://problem/6616664>).
2897 bool isMainFrameMainResource = isMainResource && m_frame.isMainFrame();
2898 if (request.firstPartyForCookies().isEmpty()) {
2899 if (isMainFrameMainResource)
2900 request.setFirstPartyForCookies(request.url());
2901 else if (Document* document = m_frame.document())
2902 request.setFirstPartyForCookies(document->firstPartyForCookies());
2903 }
2904
2905 if (request.isSameSiteUnspecified()) {
2906 auto* initiator = m_frame.document();
2907 if (isMainResource) {
2908 auto* ownerFrame = m_frame.tree().parent();
2909 if (!ownerFrame && m_stateMachine.isDisplayingInitialEmptyDocument())
2910 ownerFrame = m_opener;
2911 if (ownerFrame)
2912 initiator = ownerFrame->document();
2913 ASSERT(ownerFrame || m_frame.isMainFrame());
2914 }
2915 addSameSiteInfoToRequestIfNeeded(request, initiator);
2916 }
2917 request.setIsTopSite(isMainFrameMainResource);
2918
2919 Page* page = frame().page();
2920 bool hasSpecificCachePolicy = request.cachePolicy() != ResourceRequestCachePolicy::UseProtocolCachePolicy;
2921
2922 if (page && page->isResourceCachingDisabled()) {
2923 request.setCachePolicy(ResourceRequestCachePolicy::ReloadIgnoringCacheData);
2924 loadType = FrameLoadType::ReloadFromOrigin;
2925 } else if (!hasSpecificCachePolicy)
2926 request.setCachePolicy(defaultRequestCachingPolicy(request, loadType, isMainResource));
2927
2928 // The remaining modifications are only necessary for HTTP and HTTPS.
2929 if (!request.url().isEmpty() && !request.url().protocolIsInHTTPFamily())
2930 return;
2931
2932 if (!hasSpecificCachePolicy && request.cachePolicy() == ResourceRequestCachePolicy::ReloadIgnoringCacheData) {
2933 if (loadType == FrameLoadType::Reload)
2934 request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0");
2935 else if (loadType == FrameLoadType::ReloadFromOrigin) {
2936 request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "no-cache");
2937 request.setHTTPHeaderField(HTTPHeaderName::Pragma, "no-cache");
2938 }
2939 }
2940
2941 if (m_overrideResourceLoadPriorityForTesting)
2942 request.setPriority(m_overrideResourceLoadPriorityForTesting.value());
2943
2944 applyUserAgentIfNeeded(request);
2945
2946 if (isMainResource)
2947 request.setHTTPAccept(defaultAcceptHeader);
2948
2949 // Make sure we send the Origin header.
2950 addHTTPOriginIfNeeded(request, String());
2951
2952 // Only set fallback array if it's still empty (later attempts may be incorrect, see bug 117818).
2953 if (request.responseContentDispositionEncodingFallbackArray().isEmpty()) {
2954 // Always try UTF-8. If that fails, try frame encoding (if any) and then the default.
2955 request.setResponseContentDispositionEncodingFallbackArray("UTF-8", m_frame.document()->encoding(), m_frame.settings().defaultTextEncodingName());
2956 }
2957}
2958
2959void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, const String& origin)
2960{
2961 if (!request.httpOrigin().isEmpty())
2962 return; // Request already has an Origin header.
2963
2964 // Don't send an Origin header for GET or HEAD to avoid privacy issues.
2965 // For example, if an intranet page has a hyperlink to an external web
2966 // site, we don't want to include the Origin of the request because it
2967 // will leak the internal host name. Similar privacy concerns have lead
2968 // to the widespread suppression of the Referer header at the network
2969 // layer.
2970 if (request.httpMethod() == "GET" || request.httpMethod() == "HEAD")
2971 return;
2972
2973 // For non-GET and non-HEAD methods, always send an Origin header so the
2974 // server knows we support this feature.
2975
2976 if (origin.isEmpty()) {
2977 // If we don't know what origin header to attach, we attach the value
2978 // for an empty origin.
2979 request.setHTTPOrigin(SecurityOrigin::createUnique()->toString());
2980 return;
2981 }
2982
2983 request.setHTTPOrigin(origin);
2984}
2985
2986// Implements the "'Same-site' and 'cross-site' Requests" algorithm from <https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00#section-2.1>.
2987// The algorithm is ammended to treat URLs that inherit their security origin from their owner (e.g. about:blank)
2988// as same-site. This matches the behavior of Chrome and Firefox.
2989void FrameLoader::addSameSiteInfoToRequestIfNeeded(ResourceRequest& request, const Document* initiator)
2990{
2991 if (!request.isSameSiteUnspecified())
2992 return;
2993 if (!initiator) {
2994 request.setIsSameSite(true);
2995 return;
2996 }
2997 if (SecurityPolicy::shouldInheritSecurityOriginFromOwner(request.url())) {
2998 request.setIsSameSite(true);
2999 return;
3000 }
3001 request.setIsSameSite(areRegistrableDomainsEqual(initiator->siteForCookies(), request.url()));
3002}
3003
3004void FrameLoader::addHTTPUpgradeInsecureRequestsIfNeeded(ResourceRequest& request)
3005{
3006 if (request.url().protocolIs("https")) {
3007 // FIXME: Identify HSTS cases and avoid adding the header. <https://bugs.webkit.org/show_bug.cgi?id=157885>
3008 return;
3009 }
3010
3011 request.setHTTPHeaderField(HTTPHeaderName::UpgradeInsecureRequests, "1"_s);
3012}
3013
3014void FrameLoader::loadPostRequest(FrameLoadRequest&& request, const String& referrer, FrameLoadType loadType, Event* event, RefPtr<FormState>&& formState, CompletionHandler<void()>&& completionHandler)
3015{
3016 RELEASE_LOG_IF_ALLOWED("loadPostRequest: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
3017
3018 String frameName = request.frameName();
3019 LockHistory lockHistory = request.lockHistory();
3020 AllowNavigationToInvalidURL allowNavigationToInvalidURL = request.allowNavigationToInvalidURL();
3021 NewFrameOpenerPolicy openerPolicy = request.newFrameOpenerPolicy();
3022
3023 const ResourceRequest& inRequest = request.resourceRequest();
3024 const URL& url = inRequest.url();
3025 const String& contentType = inRequest.httpContentType();
3026 String origin = inRequest.httpOrigin();
3027
3028 ResourceRequest workingResourceRequest(url);
3029
3030 if (!referrer.isEmpty())
3031 workingResourceRequest.setHTTPReferrer(referrer);
3032 workingResourceRequest.setHTTPOrigin(origin);
3033 workingResourceRequest.setHTTPMethod("POST");
3034 workingResourceRequest.setHTTPBody(inRequest.httpBody());
3035 workingResourceRequest.setHTTPContentType(contentType);
3036 addExtraFieldsToRequest(workingResourceRequest, loadType, true);
3037
3038 if (Document* document = m_frame.document())
3039 document->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(workingResourceRequest, ContentSecurityPolicy::InsecureRequestType::Load);
3040
3041 NavigationAction action { request.requester(), workingResourceRequest, request.initiatedByMainFrame(), loadType, true, event, request.shouldOpenExternalURLsPolicy(), request.downloadAttribute() };
3042
3043 if (!frameName.isEmpty()) {
3044 // The search for a target frame is done earlier in the case of form submission.
3045 if (auto* targetFrame = formState ? nullptr : findFrameForNavigation(frameName)) {
3046 targetFrame->loader().loadWithNavigationAction(workingResourceRequest, WTFMove(action), lockHistory, loadType, WTFMove(formState), allowNavigationToInvalidURL, { }, WTFMove(completionHandler));
3047 return;
3048 }
3049
3050 policyChecker().checkNewWindowPolicy(WTFMove(action), WTFMove(workingResourceRequest), WTFMove(formState), frameName, [this, allowNavigationToInvalidURL, openerPolicy, completionHandler = WTFMove(completionHandler)] (const ResourceRequest& request, WeakPtr<FormState>&& formState, const String& frameName, const NavigationAction& action, ShouldContinue shouldContinue) mutable {
3051 continueLoadAfterNewWindowPolicy(request, formState.get(), frameName, action, shouldContinue, allowNavigationToInvalidURL, openerPolicy);
3052 completionHandler();
3053 });
3054 return;
3055 }
3056
3057 // must grab this now, since this load may stop the previous load and clear this flag
3058 bool isRedirect = m_quickRedirectComing;
3059 loadWithNavigationAction(workingResourceRequest, WTFMove(action), lockHistory, loadType, WTFMove(formState), allowNavigationToInvalidURL, { }, [this, isRedirect, protectedFrame = makeRef(m_frame), completionHandler = WTFMove(completionHandler)] () mutable {
3060 if (isRedirect) {
3061 m_quickRedirectComing = false;
3062 if (m_provisionalDocumentLoader)
3063 m_provisionalDocumentLoader->setIsClientRedirect(true);
3064 else if (m_policyDocumentLoader)
3065 m_policyDocumentLoader->setIsClientRedirect(true);
3066 }
3067 completionHandler();
3068 });
3069}
3070
3071unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, ClientCredentialPolicy clientCredentialPolicy, const FetchOptions& options, const HTTPHeaderMap& originalRequestHeaders, ResourceError& error, ResourceResponse& response, RefPtr<SharedBuffer>& data)
3072{
3073 ASSERT(m_frame.document());
3074 String referrer = SecurityPolicy::generateReferrerHeader(m_frame.document()->referrerPolicy(), request.url(), outgoingReferrer());
3075
3076 ResourceRequest initialRequest = request;
3077 initialRequest.setTimeoutInterval(10);
3078
3079 if (!referrer.isEmpty())
3080 initialRequest.setHTTPReferrer(referrer);
3081 addHTTPOriginIfNeeded(initialRequest, outgoingOrigin());
3082
3083 initialRequest.setFirstPartyForCookies(m_frame.mainFrame().loader().documentLoader()->request().url());
3084
3085 addExtraFieldsToSubresourceRequest(initialRequest);
3086
3087 unsigned long identifier = 0;
3088 ResourceRequest newRequest(initialRequest);
3089 requestFromDelegate(newRequest, identifier, error);
3090
3091#if ENABLE(CONTENT_EXTENSIONS)
3092 if (error.isNull()) {
3093 if (auto* page = m_frame.page()) {
3094 if (m_documentLoader) {
3095 auto results = page->userContentProvider().processContentRuleListsForLoad(newRequest.url(), ContentExtensions::ResourceType::Raw, *m_documentLoader);
3096 bool blockedLoad = results.summary.blockedLoad;
3097 ContentExtensions::applyResultsToRequest(WTFMove(results), page, newRequest);
3098 if (blockedLoad) {
3099 newRequest = { };
3100 error = ResourceError(errorDomainWebKitInternal, 0, initialRequest.url(), emptyString());
3101 response = { };
3102 data = nullptr;
3103 }
3104 }
3105 }
3106 }
3107#endif
3108
3109 m_frame.document()->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(newRequest, ContentSecurityPolicy::InsecureRequestType::Load);
3110
3111 if (error.isNull()) {
3112 ASSERT(!newRequest.isNull());
3113
3114 if (!documentLoader()->applicationCacheHost().maybeLoadSynchronously(newRequest, error, response, data)) {
3115 Vector<char> buffer;
3116 platformStrategies()->loaderStrategy()->loadResourceSynchronously(*this, identifier, newRequest, clientCredentialPolicy, options, originalRequestHeaders, error, response, buffer);
3117 data = SharedBuffer::create(WTFMove(buffer));
3118 documentLoader()->applicationCacheHost().maybeLoadFallbackSynchronously(newRequest, error, response, data);
3119 ResourceLoadObserver::shared().logSubresourceLoading(&m_frame, newRequest, response);
3120 }
3121 }
3122 notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, response, data ? data->data() : nullptr, data ? data->size() : 0, -1, error);
3123 return identifier;
3124}
3125
3126const ResourceRequest& FrameLoader::originalRequest() const
3127{
3128 return activeDocumentLoader()->originalRequestCopy();
3129}
3130
3131void FrameLoader::receivedMainResourceError(const ResourceError& error)
3132{
3133 // Retain because the stop may release the last reference to it.
3134 Ref<Frame> protect(m_frame);
3135
3136 RefPtr<DocumentLoader> loader = activeDocumentLoader();
3137 // FIXME: Don't want to do this if an entirely new load is going, so should check
3138 // that both data sources on the frame are either this or nil.
3139 stop();
3140 if (m_client.shouldFallBack(error))
3141 handleFallbackContent();
3142
3143 if (m_state == FrameStateProvisional && m_provisionalDocumentLoader) {
3144 if (m_submittedFormURL == m_provisionalDocumentLoader->originalRequestCopy().url())
3145 m_submittedFormURL = URL();
3146
3147 // We might have made a page cache item, but now we're bailing out due to an error before we ever
3148 // transitioned to the new page (before WebFrameState == commit). The goal here is to restore any state
3149 // so that the existing view (that wenever got far enough to replace) can continue being used.
3150 history().invalidateCurrentItemCachedPage();
3151
3152 // Call clientRedirectCancelledOrFinished here so that the frame load delegate is notified that the redirect's
3153 // status has changed, if there was a redirect. The frame load delegate may have saved some state about
3154 // the redirect in its -webView:willPerformClientRedirectToURL:delay:fireDate:forFrame:. Since we are definitely
3155 // not going to use this provisional resource, as it was cancelled, notify the frame load delegate that the redirect
3156 // has ended.
3157 if (m_sentRedirectNotification)
3158 clientRedirectCancelledOrFinished(NewLoadInProgress::No);
3159 }
3160
3161 checkCompleted();
3162 if (m_frame.page())
3163 checkLoadComplete();
3164}
3165
3166void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequest& request, bool shouldContinue)
3167{
3168 m_quickRedirectComing = false;
3169
3170 if (!shouldContinue)
3171 return;
3172
3173 // Calling stopLoading() on the provisional document loader can cause the underlying
3174 // frame to be deallocated.
3175 Ref<Frame> protectedFrame(m_frame);
3176
3177 // If we have a provisional request for a different document, a fragment scroll should cancel it.
3178 if (m_provisionalDocumentLoader && !equalIgnoringFragmentIdentifier(m_provisionalDocumentLoader->request().url(), request.url())) {
3179 m_provisionalDocumentLoader->stopLoading();
3180 setProvisionalDocumentLoader(nullptr);
3181 }
3182
3183 bool isRedirect = m_quickRedirectComing || policyChecker().loadType() == FrameLoadType::RedirectWithLockedBackForwardList;
3184 loadInSameDocument(request.url(), 0, !isRedirect);
3185}
3186
3187bool FrameLoader::shouldPerformFragmentNavigation(bool isFormSubmission, const String& httpMethod, FrameLoadType loadType, const URL& url)
3188{
3189 // We don't do this if we are submitting a form with method other than "GET", explicitly reloading,
3190 // currently displaying a frameset, or if the URL does not have a fragment.
3191 // These rules were originally based on what KHTML was doing in KHTMLPart::openURL.
3192
3193 // FIXME: What about load types other than Standard and Reload?
3194
3195 return (!isFormSubmission || equalLettersIgnoringASCIICase(httpMethod, "get"))
3196 && !isReload(loadType)
3197 && loadType != FrameLoadType::Same
3198 && m_frame.document()->pageCacheState() != Document::InPageCache
3199 && !shouldReload(m_frame.document()->url(), url)
3200 // We don't want to just scroll if a link from within a
3201 // frameset is trying to reload the frameset into _top.
3202 && !m_frame.document()->isFrameSet();
3203}
3204
3205static bool itemAllowsScrollRestoration(HistoryItem* historyItem)
3206{
3207 return !historyItem || historyItem->shouldRestoreScrollPosition();
3208}
3209
3210static bool isSameDocumentReload(bool isNewNavigation, FrameLoadType loadType)
3211{
3212 return !isNewNavigation && !isBackForwardLoadType(loadType);
3213}
3214
3215void FrameLoader::scrollToFragmentWithParentBoundary(const URL& url, bool isNewNavigation)
3216{
3217 FrameView* view = m_frame.view();
3218 if (!view)
3219 return;
3220
3221 if (isSameDocumentReload(isNewNavigation, m_loadType) || itemAllowsScrollRestoration(history().currentItem()))
3222 view->scrollToFragment(url);
3223}
3224
3225bool FrameLoader::shouldClose()
3226{
3227 Page* page = m_frame.page();
3228 if (!page)
3229 return true;
3230 if (!page->chrome().canRunBeforeUnloadConfirmPanel())
3231 return true;
3232
3233 // Store all references to each subframe in advance since beforeunload's event handler may modify frame
3234 Vector<Ref<Frame>, 16> targetFrames;
3235 targetFrames.append(m_frame);
3236 for (Frame* child = m_frame.tree().firstChild(); child; child = child->tree().traverseNext(&m_frame))
3237 targetFrames.append(*child);
3238
3239 bool shouldClose = false;
3240 {
3241 NavigationDisabler navigationDisabler(&m_frame);
3242 IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(m_frame.document());
3243 size_t i;
3244
3245 for (i = 0; i < targetFrames.size(); i++) {
3246 if (!targetFrames[i]->tree().isDescendantOf(&m_frame))
3247 continue;
3248 if (!targetFrames[i]->loader().dispatchBeforeUnloadEvent(page->chrome(), this))
3249 break;
3250 }
3251
3252 if (i == targetFrames.size())
3253 shouldClose = true;
3254 }
3255
3256 if (!shouldClose)
3257 m_submittedFormURL = URL();
3258
3259 m_currentNavigationHasShownBeforeUnloadConfirmPanel = false;
3260 return shouldClose;
3261}
3262
3263void FrameLoader::dispatchUnloadEvents(UnloadEventPolicy unloadEventPolicy)
3264{
3265 if (!m_frame.document())
3266 return;
3267
3268 // We store the frame's page in a local variable because the frame might get detached inside dispatchEvent.
3269 ForbidPromptsScope forbidPrompts(m_frame.page());
3270 IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(m_frame.document());
3271
3272 if (m_didCallImplicitClose && !m_wasUnloadEventEmitted) {
3273 auto* currentFocusedElement = m_frame.document()->focusedElement();
3274 if (is<HTMLInputElement>(currentFocusedElement))
3275 downcast<HTMLInputElement>(*currentFocusedElement).endEditing();
3276 if (m_pageDismissalEventBeingDispatched == PageDismissalType::None) {
3277 if (unloadEventPolicy == UnloadEventPolicyUnloadAndPageHide) {
3278 m_pageDismissalEventBeingDispatched = PageDismissalType::PageHide;
3279 m_frame.document()->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, m_frame.document()->pageCacheState() == Document::AboutToEnterPageCache), m_frame.document());
3280 }
3281
3282 // FIXME: update Page Visibility state here.
3283 // https://bugs.webkit.org/show_bug.cgi?id=116770
3284
3285 if (m_frame.document()->pageCacheState() == Document::NotInPageCache) {
3286 Ref<Event> unloadEvent(Event::create(eventNames().unloadEvent, Event::CanBubble::No, Event::IsCancelable::No));
3287 // The DocumentLoader (and thus its LoadTiming) might get destroyed
3288 // while dispatching the event, so protect it to prevent writing the end
3289 // time into freed memory.
3290 RefPtr<DocumentLoader> documentLoader = m_provisionalDocumentLoader;
3291 m_pageDismissalEventBeingDispatched = PageDismissalType::Unload;
3292 if (documentLoader && documentLoader->timing().startTime() && !documentLoader->timing().unloadEventStart() && !documentLoader->timing().unloadEventEnd()) {
3293 auto& timing = documentLoader->timing();
3294 timing.markUnloadEventStart();
3295 m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document());
3296 timing.markUnloadEventEnd();
3297 } else
3298 m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document());
3299 }
3300 }
3301 m_pageDismissalEventBeingDispatched = PageDismissalType::None;
3302 m_wasUnloadEventEmitted = true;
3303 }
3304
3305 // Dispatching the unload event could have made m_frame.document() null.
3306 if (!m_frame.document())
3307 return;
3308
3309 if (m_frame.document()->pageCacheState() != Document::NotInPageCache)
3310 return;
3311
3312 // Don't remove event listeners from a transitional empty document (see bug 28716 for more information).
3313 bool keepEventListeners = m_stateMachine.isDisplayingInitialEmptyDocument() && m_provisionalDocumentLoader
3314 && m_frame.document()->isSecureTransitionTo(m_provisionalDocumentLoader->url());
3315
3316 if (!keepEventListeners)
3317 m_frame.document()->removeAllEventListeners();
3318}
3319
3320static bool shouldAskForNavigationConfirmation(Document& document, const BeforeUnloadEvent& event)
3321{
3322 // Confirmation dialog should not be displayed when the allow-modals flag is not set.
3323 if (document.isSandboxed(SandboxModals))
3324 return false;
3325
3326 bool userDidInteractWithPage = document.topDocument().userDidInteractWithPage();
3327 // Web pages can request we ask for confirmation before navigating by:
3328 // - Cancelling the BeforeUnloadEvent (modern way)
3329 // - Setting the returnValue attribute on the BeforeUnloadEvent to a non-empty string.
3330 // - Returning a non-empty string from the event handler, which is then set as returnValue
3331 // attribute on the BeforeUnloadEvent.
3332 return userDidInteractWithPage && (event.defaultPrevented() || !event.returnValue().isEmpty());
3333}
3334
3335bool FrameLoader::dispatchBeforeUnloadEvent(Chrome& chrome, FrameLoader* frameLoaderBeingNavigated)
3336{
3337 DOMWindow* domWindow = m_frame.document()->domWindow();
3338 if (!domWindow)
3339 return true;
3340
3341 RefPtr<Document> document = m_frame.document();
3342 if (!document->bodyOrFrameset())
3343 return true;
3344
3345 Ref<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create();
3346 m_pageDismissalEventBeingDispatched = PageDismissalType::BeforeUnload;
3347
3348 {
3349 ForbidPromptsScope forbidPrompts(m_frame.page());
3350 domWindow->dispatchEvent(beforeUnloadEvent, domWindow->document());
3351 }
3352
3353 m_pageDismissalEventBeingDispatched = PageDismissalType::None;
3354
3355 if (!beforeUnloadEvent->defaultPrevented())
3356 document->defaultEventHandler(beforeUnloadEvent.get());
3357
3358 if (!shouldAskForNavigationConfirmation(*document, beforeUnloadEvent))
3359 return true;
3360
3361 // If the navigating FrameLoader has already shown a beforeunload confirmation panel for the current navigation attempt,
3362 // this frame is not allowed to cause another one to be shown.
3363 if (frameLoaderBeingNavigated->m_currentNavigationHasShownBeforeUnloadConfirmPanel) {
3364 document->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Blocked attempt to show multiple beforeunload confirmation dialogs for the same navigation."_s);
3365 return true;
3366 }
3367
3368 // We should only display the beforeunload dialog for an iframe if its SecurityOrigin matches all
3369 // ancestor frame SecurityOrigins up through the navigating FrameLoader.
3370 if (frameLoaderBeingNavigated != this) {
3371 Frame* parentFrame = m_frame.tree().parent();
3372 while (parentFrame) {
3373 Document* parentDocument = parentFrame->document();
3374 if (!parentDocument)
3375 return true;
3376 if (!m_frame.document() || !m_frame.document()->securityOrigin().canAccess(parentDocument->securityOrigin())) {
3377 document->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Blocked attempt to show beforeunload confirmation dialog on behalf of a frame with different security origin. Protocols, domains, and ports must match."_s);
3378 return true;
3379 }
3380
3381 if (&parentFrame->loader() == frameLoaderBeingNavigated)
3382 break;
3383
3384 parentFrame = parentFrame->tree().parent();
3385 }
3386
3387 // The navigatingFrameLoader should always be in our ancestory.
3388 ASSERT(parentFrame);
3389 ASSERT(&parentFrame->loader() == frameLoaderBeingNavigated);
3390 }
3391
3392 frameLoaderBeingNavigated->m_currentNavigationHasShownBeforeUnloadConfirmPanel = true;
3393
3394 String text = document->displayStringModifiedByEncoding(beforeUnloadEvent->returnValue());
3395 return chrome.runBeforeUnloadConfirmPanel(text, m_frame);
3396}
3397
3398void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& request, FormState* formState, NavigationPolicyDecision navigationPolicyDecision, AllowNavigationToInvalidURL allowNavigationToInvalidURL)
3399{
3400 // If we loaded an alternate page to replace an unreachableURL, we'll get in here with a
3401 // nil policyDataSource because loading the alternate page will have passed
3402 // through this method already, nested; otherwise, policyDataSource should still be set.
3403 ASSERT(m_policyDocumentLoader || !m_provisionalDocumentLoader->unreachableURL().isEmpty());
3404
3405 bool isTargetItem = history().provisionalItem() ? history().provisionalItem()->isTargetItem() : false;
3406
3407 bool urlIsDisallowed = allowNavigationToInvalidURL == AllowNavigationToInvalidURL::No && !request.url().isValid();
3408 bool canContinue = navigationPolicyDecision == NavigationPolicyDecision::ContinueLoad && shouldClose() && !urlIsDisallowed;
3409
3410 if (!canContinue) {
3411 RELEASE_LOG_IF_ALLOWED("continueLoadAfterNavigationPolicy: can't continue loading frame due to the following reasons ("
3412 "frame = %p, "
3413 "main = %d, "
3414 "allowNavigationToInvalidURL = %d, "
3415 "requestURLIsValid = %d, "
3416 "navigationPolicyDecision = %d)",
3417 &m_frame,
3418 m_frame.isMainFrame(),
3419 static_cast<int>(allowNavigationToInvalidURL),
3420 request.url().isValid(),
3421 static_cast<int>(navigationPolicyDecision));
3422
3423 // If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we
3424 // need to report that the client redirect was cancelled.
3425 // FIXME: The client should be told about ignored non-quick redirects, too.
3426 if (m_quickRedirectComing)
3427 clientRedirectCancelledOrFinished(NewLoadInProgress::No);
3428
3429 if (navigationPolicyDecision == NavigationPolicyDecision::StopAllLoads) {
3430 stopAllLoaders();
3431 m_checkTimer.stop();
3432 }
3433
3434 setPolicyDocumentLoader(nullptr);
3435 checkCompleted();
3436
3437 if (navigationPolicyDecision != NavigationPolicyDecision::StopAllLoads)
3438 checkLoadComplete();
3439
3440 // If the navigation request came from the back/forward menu, and we punt on it, we have the
3441 // problem that we have optimistically moved the b/f cursor already, so move it back. For sanity,
3442 // we only do this when punting a navigation for the target frame or top-level frame.
3443 if ((isTargetItem || m_frame.isMainFrame()) && isBackForwardLoadType(policyChecker().loadType())) {
3444 if (Page* page = m_frame.page()) {
3445 if (HistoryItem* resetItem = m_frame.mainFrame().loader().history().currentItem())
3446 page->backForward().setCurrentItem(*resetItem);
3447 }
3448 }
3449 return;
3450 }
3451
3452 FrameLoadType type = policyChecker().loadType();
3453 // A new navigation is in progress, so don't clear the history's provisional item.
3454 stopAllLoaders(ShouldNotClearProvisionalItem);
3455
3456 // <rdar://problem/6250856> - In certain circumstances on pages with multiple frames, stopAllLoaders()
3457 // might detach the current FrameLoader, in which case we should bail on this newly defunct load.
3458 if (!m_frame.page()) {
3459 RELEASE_LOG_IF_ALLOWED("continueLoadAfterNavigationPolicy: can't continue loading frame because it became defunct (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
3460 return;
3461 }
3462
3463 setProvisionalDocumentLoader(m_policyDocumentLoader.get());
3464 m_loadType = type;
3465 setState(FrameStateProvisional);
3466
3467 setPolicyDocumentLoader(nullptr);
3468
3469 if (isBackForwardLoadType(type)) {
3470 auto& diagnosticLoggingClient = m_frame.page()->diagnosticLoggingClient();
3471 if (history().provisionalItem()->isInPageCache()) {
3472 diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::retrievalKey(), DiagnosticLoggingResultPass, ShouldSample::Yes);
3473 loadProvisionalItemFromCachedPage();
3474 RELEASE_LOG_IF_ALLOWED("continueLoadAfterNavigationPolicy: can't continue loading frame because it will be loaded from cache (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
3475 return;
3476 }
3477 diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::retrievalKey(), DiagnosticLoggingResultFail, ShouldSample::Yes);
3478 }
3479
3480 CompletionHandler<void()> completionHandler = [this, protectedFrame = makeRef(m_frame)] () mutable {
3481 if (!m_provisionalDocumentLoader) {
3482 RELEASE_LOG_IF_ALLOWED("continueLoadAfterNavigationPolicy completionHandler: Frame load canceled #1 (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
3483 return;
3484 }
3485
3486 prepareForLoadStart();
3487
3488 // The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader,
3489 // so we need to null check it again.
3490 if (!m_provisionalDocumentLoader) {
3491 RELEASE_LOG_IF_ALLOWED("prepareForLoadStart completionHandler: Frame load canceled #2 (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
3492 return;
3493 }
3494
3495 DocumentLoader* activeDocLoader = activeDocumentLoader();
3496 if (activeDocLoader && activeDocLoader->isLoadingMainResource()) {
3497 RELEASE_LOG_IF_ALLOWED("prepareForLoadStart completionHandler: Main frame already being loaded (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
3498 return;
3499 }
3500
3501 m_loadingFromCachedPage = false;
3502
3503 m_provisionalDocumentLoader->startLoadingMainResource();
3504 };
3505
3506 if (!formState) {
3507 completionHandler();
3508 return;
3509 }
3510
3511 m_client.dispatchWillSubmitForm(*formState, WTFMove(completionHandler));
3512}
3513
3514void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& request,
3515 FormState* formState, const String& frameName, const NavigationAction& action, ShouldContinue shouldContinue, AllowNavigationToInvalidURL allowNavigationToInvalidURL, NewFrameOpenerPolicy openerPolicy)
3516{
3517 if (shouldContinue != ShouldContinue::Yes)
3518 return;
3519
3520 Ref<Frame> frame(m_frame);
3521 RefPtr<Frame> mainFrame = m_client.dispatchCreatePage(action);
3522 if (!mainFrame)
3523 return;
3524
3525 SandboxFlags sandboxFlags = frame->loader().effectiveSandboxFlags();
3526 if (sandboxFlags & SandboxPropagatesToAuxiliaryBrowsingContexts)
3527 mainFrame->loader().forceSandboxFlags(sandboxFlags);
3528
3529 if (!equalIgnoringASCIICase(frameName, "_blank"))
3530 mainFrame->tree().setName(frameName);
3531
3532 mainFrame->page()->setOpenedByDOM();
3533 mainFrame->loader().m_client.dispatchShow();
3534 if (openerPolicy == NewFrameOpenerPolicy::Allow) {
3535 mainFrame->loader().setOpener(frame.ptr());
3536 mainFrame->document()->setReferrerPolicy(frame->document()->referrerPolicy());
3537 }
3538
3539 NavigationAction newAction { *frame->document(), request, InitiatedByMainFrame::Unknown, NavigationType::Other, action.shouldOpenExternalURLsPolicy(), nullptr, action.downloadAttribute() };
3540 mainFrame->loader().loadWithNavigationAction(request, WTFMove(newAction), LockHistory::No, FrameLoadType::Standard, formState, allowNavigationToInvalidURL);
3541}
3542
3543void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& identifier, ResourceError& error)
3544{
3545 ASSERT(!request.isNull());
3546
3547 identifier = 0;
3548 if (Page* page = m_frame.page()) {
3549 identifier = page->progress().createUniqueIdentifier();
3550 notifier().assignIdentifierToInitialRequest(identifier, m_documentLoader.get(), request);
3551 }
3552
3553 ResourceRequest newRequest(request);
3554 notifier().dispatchWillSendRequest(m_documentLoader.get(), identifier, newRequest, ResourceResponse());
3555
3556 if (newRequest.isNull())
3557 error = cancelledError(request);
3558 else
3559 error = ResourceError();
3560
3561 request = newRequest;
3562}
3563
3564void FrameLoader::loadedResourceFromMemoryCache(CachedResource& resource, ResourceRequest& newRequest, ResourceError& error)
3565{
3566 Page* page = m_frame.page();
3567 if (!page)
3568 return;
3569
3570 if (!resource.shouldSendResourceLoadCallbacks() || m_documentLoader->haveToldClientAboutLoad(resource.url()))
3571 return;
3572
3573 // Main resource delegate messages are synthesized in MainResourceLoader, so we must not send them here.
3574 if (resource.type() == CachedResource::Type::MainResource)
3575 return;
3576
3577 if (!page->areMemoryCacheClientCallsEnabled()) {
3578 InspectorInstrumentation::didLoadResourceFromMemoryCache(*page, m_documentLoader.get(), &resource);
3579 m_documentLoader->recordMemoryCacheLoadForFutureClientNotification(resource.resourceRequest());
3580 m_documentLoader->didTellClientAboutLoad(resource.url());
3581 return;
3582 }
3583
3584 if (m_client.dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), newRequest, resource.response(), resource.encodedSize())) {
3585 InspectorInstrumentation::didLoadResourceFromMemoryCache(*page, m_documentLoader.get(), &resource);
3586 m_documentLoader->didTellClientAboutLoad(resource.url());
3587 return;
3588 }
3589
3590 unsigned long identifier;
3591 requestFromDelegate(newRequest, identifier, error);
3592
3593 ResourceResponse response = resource.response();
3594 response.setSource(ResourceResponse::Source::MemoryCache);
3595 notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, newRequest, response, 0, resource.encodedSize(), 0, error);
3596}
3597
3598void FrameLoader::applyUserAgentIfNeeded(ResourceRequest& request)
3599{
3600 if (!request.hasHTTPHeaderField(HTTPHeaderName::UserAgent)) {
3601 String userAgent = this->userAgent(request.url());
3602 ASSERT(!userAgent.isNull());
3603 request.setHTTPUserAgent(userAgent);
3604 }
3605}
3606
3607bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, const URL& url, unsigned long requestIdentifier)
3608{
3609 Frame& topFrame = m_frame.tree().top();
3610 if (&m_frame == &topFrame)
3611 return false;
3612
3613 XFrameOptionsDisposition disposition = parseXFrameOptionsHeader(content);
3614
3615 switch (disposition) {
3616 case XFrameOptionsSameOrigin: {
3617 auto origin = SecurityOrigin::create(url);
3618 if (!origin->isSameSchemeHostPort(topFrame.document()->securityOrigin()))
3619 return true;
3620 for (Frame* frame = m_frame.tree().parent(); frame; frame = frame->tree().parent()) {
3621 if (!origin->isSameSchemeHostPort(frame->document()->securityOrigin()))
3622 return true;
3623 }
3624 return false;
3625 }
3626 case XFrameOptionsDeny:
3627 return true;
3628 case XFrameOptionsAllowAll:
3629 return false;
3630 case XFrameOptionsConflict:
3631 m_frame.document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Multiple 'X-Frame-Options' headers with conflicting values ('" + content + "') encountered when loading '" + url.stringCenterEllipsizedToLength() + "'. Falling back to 'DENY'.", requestIdentifier);
3632 return true;
3633 case XFrameOptionsInvalid:
3634 m_frame.document()->addConsoleMessage(MessageSource::JS, MessageLevel::Error, "Invalid 'X-Frame-Options' header encountered when loading '" + url.stringCenterEllipsizedToLength() + "': '" + content + "' is not a recognized directive. The header will be ignored.", requestIdentifier);
3635 return false;
3636 case XFrameOptionsNone:
3637 return false;
3638 }
3639 ASSERT_NOT_REACHED();
3640 return false;
3641}
3642
3643void FrameLoader::loadProvisionalItemFromCachedPage()
3644{
3645 DocumentLoader* provisionalLoader = provisionalDocumentLoader();
3646 LOG(PageCache, "WebCorePageCache: Loading provisional DocumentLoader %p with URL '%s' from CachedPage", provisionalDocumentLoader(), provisionalDocumentLoader()->url().stringCenterEllipsizedToLength().utf8().data());
3647
3648 prepareForLoadStart();
3649
3650 m_loadingFromCachedPage = true;
3651
3652 // Should have timing data from previous time(s) the page was shown.
3653 ASSERT(provisionalLoader->timing().startTime());
3654 provisionalLoader->resetTiming();
3655 provisionalLoader->timing().markStartTime();
3656
3657 provisionalLoader->setCommitted(true);
3658 commitProvisionalLoad();
3659}
3660
3661bool FrameLoader::shouldTreatURLAsSameAsCurrent(const URL& url) const
3662{
3663 if (!history().currentItem())
3664 return false;
3665 return url == history().currentItem()->url() || url == history().currentItem()->originalURL();
3666}
3667
3668bool FrameLoader::shouldTreatURLAsSrcdocDocument(const URL& url) const
3669{
3670 if (!equalLettersIgnoringASCIICase(url.string(), "about:srcdoc"))
3671 return false;
3672 HTMLFrameOwnerElement* ownerElement = m_frame.ownerElement();
3673 if (!ownerElement)
3674 return false;
3675 if (!ownerElement->hasTagName(iframeTag))
3676 return false;
3677 return ownerElement->hasAttributeWithoutSynchronization(srcdocAttr);
3678}
3679
3680Frame* FrameLoader::findFrameForNavigation(const AtomString& name, Document* activeDocument)
3681{
3682 // FIXME: Eventually all callers should supply the actual activeDocument so we can call canNavigate with the right document.
3683 if (!activeDocument)
3684 activeDocument = m_frame.document();
3685
3686 auto* frame = m_frame.tree().find(name, activeDocument->frame() ? *activeDocument->frame() : m_frame);
3687
3688 if (!activeDocument->canNavigate(frame))
3689 return nullptr;
3690
3691 return frame;
3692}
3693
3694void FrameLoader::loadSameDocumentItem(HistoryItem& item)
3695{
3696 ASSERT(item.documentSequenceNumber() == history().currentItem()->documentSequenceNumber());
3697
3698 Ref<Frame> protect(m_frame);
3699
3700 // Save user view state to the current history item here since we don't do a normal load.
3701 // FIXME: Does form state need to be saved here too?
3702 history().saveScrollPositionAndViewStateToItem(history().currentItem());
3703 if (FrameView* view = m_frame.view())
3704 view->setWasScrolledByUser(false);
3705
3706 history().setCurrentItem(item);
3707
3708 // loadInSameDocument() actually changes the URL and notifies load delegates of a "fake" load
3709 loadInSameDocument(item.url(), item.stateObject(), false);
3710
3711 // Restore user view state from the current history item here since we don't do a normal load.
3712 history().restoreScrollPositionAndViewState();
3713}
3714
3715// FIXME: This function should really be split into a couple pieces, some of
3716// which should be methods of HistoryController and some of which should be
3717// methods of FrameLoader.
3718void FrameLoader::loadDifferentDocumentItem(HistoryItem& item, HistoryItem* fromItem, FrameLoadType loadType, FormSubmissionCacheLoadPolicy cacheLoadPolicy, ShouldTreatAsContinuingLoad shouldTreatAsContinuingLoad)
3719{
3720 RELEASE_LOG_IF_ALLOWED("loadDifferentDocumentItem: frame load started (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
3721
3722 Ref<Frame> protectedFrame(m_frame);
3723
3724 // History items should not be reported to the parent.
3725 m_shouldReportResourceTimingToParentFrame = false;
3726
3727 // Remember this item so we can traverse any child items as child frames load
3728 history().setProvisionalItem(&item);
3729
3730 auto initiatedByMainFrame = InitiatedByMainFrame::Unknown;
3731
3732 SetForScope<LoadContinuingState> continuingLoadGuard(m_currentLoadContinuingState, shouldTreatAsContinuingLoad == ShouldTreatAsContinuingLoad::Yes ? LoadContinuingState::ContinuingWithHistoryItem : LoadContinuingState::NotContinuing);
3733
3734 if (CachedPage* cachedPage = PageCache::singleton().get(item, m_frame.page())) {
3735 auto documentLoader = cachedPage->documentLoader();
3736 m_client.updateCachedDocumentLoader(*documentLoader);
3737
3738 auto action = NavigationAction { *m_frame.document(), documentLoader->request(), initiatedByMainFrame, loadType, false };
3739 action.setTargetBackForwardItem(item);
3740 action.setSourceBackForwardItem(fromItem);
3741 documentLoader->setTriggeringAction(WTFMove(action));
3742
3743 documentLoader->setLastCheckedRequest(ResourceRequest());
3744 loadWithDocumentLoader(documentLoader, loadType, { }, AllowNavigationToInvalidURL::Yes, shouldTreatAsContinuingLoad);
3745 return;
3746 }
3747
3748 URL itemURL = item.url();
3749 URL itemOriginalURL = item.originalURL();
3750 URL currentURL;
3751 if (documentLoader())
3752 currentURL = documentLoader()->url();
3753 RefPtr<FormData> formData = item.formData();
3754
3755 ResourceRequest request(itemURL);
3756
3757 if (!item.referrer().isNull())
3758 request.setHTTPReferrer(item.referrer());
3759
3760 ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy = shouldOpenExternalURLsPolicyToApply(m_frame, initiatedByMainFrame, item.shouldOpenExternalURLsPolicy());
3761 bool isFormSubmission = false;
3762 Event* event = nullptr;
3763
3764 // If this was a repost that failed the page cache, we might try to repost the form.
3765 NavigationAction action;
3766 if (formData) {
3767 formData->generateFiles(m_frame.document());
3768
3769 request.setHTTPMethod("POST");
3770 request.setHTTPBody(WTFMove(formData));
3771 request.setHTTPContentType(item.formContentType());
3772 auto securityOrigin = SecurityOrigin::createFromString(item.referrer());
3773 addHTTPOriginIfNeeded(request, securityOrigin->toString());
3774 addHTTPUpgradeInsecureRequestsIfNeeded(request);
3775
3776 // Make sure to add extra fields to the request after the Origin header is added for the FormData case.
3777 // See https://bugs.webkit.org/show_bug.cgi?id=22194 for more discussion.
3778 addExtraFieldsToRequest(request, loadType, true);
3779
3780 // FIXME: Slight hack to test if the NSURL cache contains the page we're going to.
3781 // We want to know this before talking to the policy delegate, since it affects whether
3782 // we show the DoYouReallyWantToRepost nag.
3783 //
3784 // This trick has a small bug (3123893) where we might find a cache hit, but then
3785 // have the item vanish when we try to use it in the ensuing nav. This should be
3786 // extremely rare, but in that case the user will get an error on the navigation.
3787
3788 if (cacheLoadPolicy == MayAttemptCacheOnlyLoadForFormSubmissionItem) {
3789 request.setCachePolicy(ResourceRequestCachePolicy::ReturnCacheDataDontLoad);
3790 action = { *m_frame.document(), request, initiatedByMainFrame, loadType, isFormSubmission, event, shouldOpenExternalURLsPolicy };
3791 } else {
3792 request.setCachePolicy(ResourceRequestCachePolicy::ReturnCacheDataElseLoad);
3793 action = { *m_frame.document(), request, initiatedByMainFrame, NavigationType::FormResubmitted, shouldOpenExternalURLsPolicy, event };
3794 }
3795 } else {
3796 switch (loadType) {
3797 case FrameLoadType::Reload:
3798 case FrameLoadType::ReloadFromOrigin:
3799 case FrameLoadType::ReloadExpiredOnly:
3800 request.setCachePolicy(ResourceRequestCachePolicy::ReloadIgnoringCacheData);
3801 break;
3802 case FrameLoadType::Back:
3803 case FrameLoadType::Forward:
3804 case FrameLoadType::IndexedBackForward: {
3805#if PLATFORM(COCOA)
3806 bool allowStaleData = true;
3807#else
3808 bool allowStaleData = !item.wasRestoredFromSession();
3809#endif
3810 if (allowStaleData)
3811 request.setCachePolicy(ResourceRequestCachePolicy::ReturnCacheDataElseLoad);
3812 item.setWasRestoredFromSession(false);
3813 break;
3814 }
3815 case FrameLoadType::Standard:
3816 case FrameLoadType::RedirectWithLockedBackForwardList:
3817 break;
3818 case FrameLoadType::Same:
3819 case FrameLoadType::Replace:
3820 ASSERT_NOT_REACHED();
3821 }
3822
3823 addExtraFieldsToRequest(request, loadType, true);
3824
3825 ResourceRequest requestForOriginalURL(request);
3826 requestForOriginalURL.setURL(itemOriginalURL);
3827 action = { *m_frame.document(), requestForOriginalURL, initiatedByMainFrame, loadType, isFormSubmission, event, shouldOpenExternalURLsPolicy };
3828 }
3829
3830 action.setTargetBackForwardItem(item);
3831 action.setSourceBackForwardItem(fromItem);
3832
3833 loadWithNavigationAction(request, WTFMove(action), LockHistory::No, loadType, { }, AllowNavigationToInvalidURL::Yes);
3834}
3835
3836// Loads content into this frame, as specified by history item
3837void FrameLoader::loadItem(HistoryItem& item, HistoryItem* fromItem, FrameLoadType loadType, ShouldTreatAsContinuingLoad shouldTreatAsContinuingLoad)
3838{
3839 m_requestedHistoryItem = &item;
3840 HistoryItem* currentItem = history().currentItem();
3841 bool sameDocumentNavigation = currentItem && item.shouldDoSameDocumentNavigationTo(*currentItem);
3842
3843 if (sameDocumentNavigation)
3844 loadSameDocumentItem(item);
3845 else
3846 loadDifferentDocumentItem(item, fromItem, loadType, MayAttemptCacheOnlyLoadForFormSubmissionItem, shouldTreatAsContinuingLoad);
3847}
3848
3849void FrameLoader::retryAfterFailedCacheOnlyMainResourceLoad()
3850{
3851 ASSERT(m_state == FrameStateProvisional);
3852 ASSERT(!m_loadingFromCachedPage);
3853 ASSERT(history().provisionalItem());
3854 ASSERT(history().provisionalItem()->formData());
3855 ASSERT(history().provisionalItem() == m_requestedHistoryItem.get());
3856
3857 FrameLoadType loadType = m_loadType;
3858 HistoryItem& item = *history().provisionalItem();
3859
3860 stopAllLoaders(ShouldNotClearProvisionalItem);
3861 loadDifferentDocumentItem(item, history().currentItem(), loadType, MayNotAttemptCacheOnlyLoadForFormSubmissionItem, ShouldTreatAsContinuingLoad::No);
3862}
3863
3864ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const
3865{
3866 ResourceError error = m_client.cancelledError(request);
3867 error.setType(ResourceError::Type::Cancellation);
3868 return error;
3869}
3870
3871ResourceError FrameLoader::blockedByContentBlockerError(const ResourceRequest& request) const
3872{
3873 return m_client.blockedByContentBlockerError(request);
3874}
3875
3876ResourceError FrameLoader::blockedError(const ResourceRequest& request) const
3877{
3878 ResourceError error = m_client.blockedError(request);
3879 error.setType(ResourceError::Type::Cancellation);
3880 return error;
3881}
3882
3883#if ENABLE(CONTENT_FILTERING)
3884ResourceError FrameLoader::blockedByContentFilterError(const ResourceRequest& request) const
3885{
3886 ResourceError error = m_client.blockedByContentFilterError(request);
3887 error.setType(ResourceError::Type::General);
3888 return error;
3889}
3890#endif
3891
3892#if PLATFORM(IOS_FAMILY)
3893RetainPtr<CFDictionaryRef> FrameLoader::connectionProperties(ResourceLoader* loader)
3894{
3895 return m_client.connectionProperties(loader->documentLoader(), loader->identifier());
3896}
3897#endif
3898
3899ReferrerPolicy FrameLoader::effectiveReferrerPolicy() const
3900{
3901 if (auto* parentFrame = m_frame.tree().parent())
3902 return parentFrame->document()->referrerPolicy();
3903 if (m_opener)
3904 return m_opener->document()->referrerPolicy();
3905 return ReferrerPolicy::NoReferrerWhenDowngrade;
3906}
3907
3908String FrameLoader::referrer() const
3909{
3910 return m_documentLoader ? m_documentLoader->request().httpReferrer() : emptyString();
3911}
3912
3913void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds()
3914{
3915 if (!m_frame.script().canExecuteScripts(NotAboutToExecuteScript))
3916 return;
3917
3918 Vector<Ref<DOMWrapperWorld>> worlds;
3919 ScriptController::getAllWorlds(worlds);
3920 for (auto& world : worlds)
3921 dispatchDidClearWindowObjectInWorld(world);
3922}
3923
3924void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld& world)
3925{
3926 if (!m_frame.script().canExecuteScripts(NotAboutToExecuteScript) || !m_frame.windowProxy().existingJSWindowProxy(world))
3927 return;
3928
3929 m_client.dispatchDidClearWindowObjectInWorld(world);
3930
3931 if (Page* page = m_frame.page())
3932 page->inspectorController().didClearWindowObjectInWorld(m_frame, world);
3933
3934 InspectorInstrumentation::didClearWindowObjectInWorld(m_frame, world);
3935}
3936
3937void FrameLoader::dispatchGlobalObjectAvailableInAllWorlds()
3938{
3939 Vector<Ref<DOMWrapperWorld>> worlds;
3940 ScriptController::getAllWorlds(worlds);
3941 for (auto& world : worlds)
3942 m_client.dispatchGlobalObjectAvailable(world);
3943}
3944
3945SandboxFlags FrameLoader::effectiveSandboxFlags() const
3946{
3947 SandboxFlags flags = m_forcedSandboxFlags;
3948 if (Frame* parentFrame = m_frame.tree().parent())
3949 flags |= parentFrame->document()->sandboxFlags();
3950 if (HTMLFrameOwnerElement* ownerElement = m_frame.ownerElement())
3951 flags |= ownerElement->sandboxFlags();
3952 return flags;
3953}
3954
3955void FrameLoader::didChangeTitle(DocumentLoader* loader)
3956{
3957 m_client.didChangeTitle(loader);
3958
3959 if (loader == m_documentLoader) {
3960 // Must update the entries in the back-forward list too.
3961 history().setCurrentItemTitle(loader->title());
3962 // This must go through the WebFrame because it has the right notion of the current b/f item.
3963 m_client.setTitle(loader->title(), loader->urlForHistory());
3964 m_client.setMainFrameDocumentReady(true); // update observers with new DOMDocument
3965 m_client.dispatchDidReceiveTitle(loader->title());
3966 }
3967
3968#if ENABLE(REMOTE_INSPECTOR)
3969 if (m_frame.isMainFrame())
3970 m_frame.page()->remoteInspectorInformationDidChange();
3971#endif
3972}
3973
3974void FrameLoader::dispatchDidCommitLoad(Optional<HasInsecureContent> initialHasInsecureContent)
3975{
3976 if (m_stateMachine.creatingInitialEmptyDocument())
3977 return;
3978
3979 m_client.dispatchDidCommitLoad(initialHasInsecureContent);
3980
3981 if (m_frame.isMainFrame()) {
3982 m_frame.page()->resetSeenPlugins();
3983 m_frame.page()->resetSeenMediaEngines();
3984 }
3985
3986 InspectorInstrumentation::didCommitLoad(m_frame, m_documentLoader.get());
3987
3988#if ENABLE(REMOTE_INSPECTOR)
3989 if (m_frame.isMainFrame())
3990 m_frame.page()->remoteInspectorInformationDidChange();
3991#endif
3992}
3993
3994void FrameLoader::tellClientAboutPastMemoryCacheLoads()
3995{
3996 ASSERT(m_frame.page());
3997 ASSERT(m_frame.page()->areMemoryCacheClientCallsEnabled());
3998
3999 if (!m_documentLoader)
4000 return;
4001
4002 Vector<ResourceRequest> pastLoads;
4003 m_documentLoader->takeMemoryCacheLoadsForClientNotification(pastLoads);
4004
4005 for (auto& pastLoad : pastLoads) {
4006 CachedResource* resource = MemoryCache::singleton().resourceForRequest(pastLoad, m_frame.page()->sessionID());
4007
4008 // FIXME: These loads, loaded from cache, but now gone from the cache by the time
4009 // Page::setMemoryCacheClientCallsEnabled(true) is called, will not be seen by the client.
4010 // Consider if there's some efficient way of remembering enough to deliver this client call.
4011 // We have the URL, but not the rest of the response or the length.
4012 if (!resource)
4013 continue;
4014
4015 ResourceRequest request(resource->url());
4016 m_client.dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), request, resource->response(), resource->encodedSize());
4017 }
4018}
4019
4020NetworkingContext* FrameLoader::networkingContext() const
4021{
4022 return m_networkingContext.get();
4023}
4024
4025void FrameLoader::loadProgressingStatusChanged()
4026{
4027 if (auto* view = m_frame.mainFrame().view())
4028 view->loadProgressingStatusChanged();
4029}
4030
4031void FrameLoader::forcePageTransitionIfNeeded()
4032{
4033 m_client.forcePageTransitionIfNeeded();
4034}
4035
4036void FrameLoader::clearTestingOverrides()
4037{
4038 m_overrideCachePolicyForTesting = WTF::nullopt;
4039 m_overrideResourceLoadPriorityForTesting = WTF::nullopt;
4040 m_isStrictRawResourceValidationPolicyDisabledForTesting = false;
4041}
4042
4043bool FrameLoader::isAlwaysOnLoggingAllowed() const
4044{
4045 return frame().isAlwaysOnLoggingAllowed();
4046}
4047
4048bool FrameLoaderClient::hasHTMLView() const
4049{
4050 return true;
4051}
4052
4053RefPtr<Frame> createWindow(Frame& openerFrame, Frame& lookupFrame, FrameLoadRequest&& request, const WindowFeatures& features, bool& created)
4054{
4055 ASSERT(!features.dialog || request.frameName().isEmpty());
4056
4057 created = false;
4058
4059 // FIXME: Provide line number information with respect to the opener's document.
4060 if (WTF::protocolIsJavaScript(request.resourceRequest().url()) && !openerFrame.document()->contentSecurityPolicy()->allowJavaScriptURLs(openerFrame.document()->url(), { }))
4061 return nullptr;
4062
4063 if (!request.frameName().isEmpty() && !equalIgnoringASCIICase(request.frameName(), "_blank")) {
4064 if (RefPtr<Frame> frame = lookupFrame.loader().findFrameForNavigation(request.frameName(), openerFrame.document())) {
4065 if (!equalIgnoringASCIICase(request.frameName(), "_self")) {
4066 if (Page* page = frame->page())
4067 page->chrome().focus();
4068 }
4069 return frame;
4070 }
4071 }
4072
4073 // Sandboxed frames cannot open new auxiliary browsing contexts.
4074 if (isDocumentSandboxed(openerFrame, SandboxPopups)) {
4075 // FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
4076 openerFrame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Blocked opening '" + request.resourceRequest().url().stringCenterEllipsizedToLength() + "' in a new window because the request was made in a sandboxed frame whose 'allow-popups' permission is not set.");
4077 return nullptr;
4078 }
4079
4080 // FIXME: Setting the referrer should be the caller's responsibility.
4081 String referrer = SecurityPolicy::generateReferrerHeader(openerFrame.document()->referrerPolicy(), request.resourceRequest().url(), openerFrame.loader().outgoingReferrer());
4082 if (!referrer.isEmpty())
4083 request.resourceRequest().setHTTPReferrer(referrer);
4084 FrameLoader::addHTTPOriginIfNeeded(request.resourceRequest(), openerFrame.loader().outgoingOrigin());
4085 FrameLoader::addHTTPUpgradeInsecureRequestsIfNeeded(request.resourceRequest());
4086 FrameLoader::addSameSiteInfoToRequestIfNeeded(request.resourceRequest(), openerFrame.document());
4087
4088 Page* oldPage = openerFrame.page();
4089 if (!oldPage)
4090 return nullptr;
4091
4092 ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy = shouldOpenExternalURLsPolicyToApply(openerFrame, request);
4093 NavigationAction action { request.requester(), request.resourceRequest(), request.initiatedByMainFrame(), NavigationType::Other, shouldOpenExternalURLsPolicy };
4094 Page* page = oldPage->chrome().createWindow(openerFrame, request, features, action);
4095 if (!page)
4096 return nullptr;
4097
4098 RefPtr<Frame> frame = &page->mainFrame();
4099
4100 if (isDocumentSandboxed(openerFrame, SandboxPropagatesToAuxiliaryBrowsingContexts))
4101 frame->loader().forceSandboxFlags(openerFrame.document()->sandboxFlags());
4102
4103 if (!equalIgnoringASCIICase(request.frameName(), "_blank"))
4104 frame->tree().setName(request.frameName());
4105
4106 page->chrome().setToolbarsVisible(features.toolBarVisible || features.locationBarVisible);
4107
4108 if (!frame->page())
4109 return nullptr;
4110 page->chrome().setStatusbarVisible(features.statusBarVisible);
4111
4112 if (!frame->page())
4113 return nullptr;
4114 page->chrome().setScrollbarsVisible(features.scrollbarsVisible);
4115
4116 if (!frame->page())
4117 return nullptr;
4118 page->chrome().setMenubarVisible(features.menuBarVisible);
4119
4120 if (!frame->page())
4121 return nullptr;
4122 page->chrome().setResizable(features.resizable);
4123
4124 // 'x' and 'y' specify the location of the window, while 'width' and 'height'
4125 // specify the size of the viewport. We can only resize the window, so adjust
4126 // for the difference between the window size and the viewport size.
4127
4128 // FIXME: We should reconcile the initialization of viewport arguments between iOS and non-IOS.
4129#if !PLATFORM(IOS_FAMILY)
4130 FloatSize viewportSize = page->chrome().pageRect().size();
4131 FloatRect windowRect = page->chrome().windowRect();
4132 if (features.x)
4133 windowRect.setX(*features.x);
4134 if (features.y)
4135 windowRect.setY(*features.y);
4136 // Zero width and height mean using default size, not minumum one.
4137 if (features.width && *features.width)
4138 windowRect.setWidth(*features.width + (windowRect.width() - viewportSize.width()));
4139 if (features.height && *features.height)
4140 windowRect.setHeight(*features.height + (windowRect.height() - viewportSize.height()));
4141
4142 // Ensure non-NaN values, minimum size as well as being within valid screen area.
4143 FloatRect newWindowRect = DOMWindow::adjustWindowRect(*page, windowRect);
4144
4145 if (!frame->page())
4146 return nullptr;
4147 page->chrome().setWindowRect(newWindowRect);
4148#else
4149 // On iOS, width and height refer to the viewport dimensions.
4150 ViewportArguments arguments;
4151 // Zero width and height mean using default size, not minimum one.
4152 if (features.width && *features.width)
4153 arguments.width = *features.width;
4154 if (features.height && *features.height)
4155 arguments.height = *features.height;
4156 frame->setViewportArguments(arguments);
4157#endif
4158
4159 if (!frame->page())
4160 return nullptr;
4161 page->chrome().show();
4162
4163 created = true;
4164 return frame;
4165}
4166
4167bool FrameLoader::shouldSuppressTextInputFromEditing() const
4168{
4169 return m_frame.settings().shouldSuppressTextInputFromEditingDuringProvisionalNavigation() && m_state == FrameStateProvisional;
4170}
4171
4172} // namespace WebCore
4173