summaryrefslogtreecommitdiff
path: root/Source/WebCore/loader/FrameLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/loader/FrameLoader.cpp')
-rw-r--r--Source/WebCore/loader/FrameLoader.cpp1942
1 files changed, 1089 insertions, 853 deletions
diff --git a/Source/WebCore/loader/FrameLoader.cpp b/Source/WebCore/loader/FrameLoader.cpp
index c3cc3c559..f5a0a3ed1 100644
--- a/Source/WebCore/loader/FrameLoader.cpp
+++ b/Source/WebCore/loader/FrameLoader.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
* Copyright (C) 2008 Alp Toker <alp@atoker.com>
@@ -12,13 +12,13 @@
* are met:
*
* 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
+ * notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
+ * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -43,13 +43,13 @@
#include "CachedResourceLoader.h"
#include "Chrome.h"
#include "ChromeClient.h"
-#include "Console.h"
+#include "ContentFilter.h"
#include "ContentSecurityPolicy.h"
-#include "DOMImplementation.h"
#include "DOMWindow.h"
#include "DatabaseManager.h"
+#include "DiagnosticLoggingClient.h"
+#include "DiagnosticLoggingKeys.h"
#include "Document.h"
-#include "DocumentLoadTiming.h"
#include "DocumentLoader.h"
#include "Editor.h"
#include "EditorClient.h"
@@ -65,37 +65,47 @@
#include "FrameNetworkingContext.h"
#include "FrameTree.h"
#include "FrameView.h"
-#include "HTMLAnchorElement.h"
+#include "GCController.h"
#include "HTMLFormElement.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
#include "HTMLObjectElement.h"
#include "HTMLParserIdioms.h"
+#include "HTTPHeaderNames.h"
#include "HTTPParsers.h"
#include "HistoryController.h"
#include "HistoryItem.h"
#include "IconController.h"
+#include "IgnoreOpensDuringUnloadCountIncrementer.h"
#include "InspectorController.h"
#include "InspectorInstrumentation.h"
+#include "LinkLoader.h"
+#include "LoadTiming.h"
#include "LoaderStrategy.h"
#include "Logging.h"
-#include "MIMETypeRegistry.h"
#include "MainFrame.h"
#include "MemoryCache.h"
+#include "MemoryRelease.h"
+#include "NoEventDispatchAssertion.h"
#include "Page.h"
-#include "PageActivityAssertionToken.h"
#include "PageCache.h"
#include "PageTransitionEvent.h"
+#include "PerformanceLogging.h"
#include "PlatformStrategies.h"
#include "PluginData.h"
-#include "PluginDatabase.h"
#include "PluginDocument.h"
#include "PolicyChecker.h"
#include "ProgressTracker.h"
+#include "PublicSuffix.h"
#include "ResourceHandle.h"
+#include "ResourceLoadInfo.h"
+#include "ResourceLoadObserver.h"
#include "ResourceRequest.h"
-#include "SchemeRegistry.h"
-#include "ScriptCallStack.h"
+#include "SVGDocument.h"
+#include "SVGLocatable.h"
+#include "SVGNames.h"
+#include "SVGViewElement.h"
+#include "SVGViewSpec.h"
#include "ScriptController.h"
#include "ScriptSourceCode.h"
#include "ScrollAnimator.h"
@@ -106,6 +116,7 @@
#include "Settings.h"
#include "SubframeLoader.h"
#include "TextResourceDecoder.h"
+#include "UserContentController.h"
#include "WindowFeatures.h"
#include "XMLDocumentParser.h"
#include <wtf/CurrentTime.h>
@@ -114,61 +125,44 @@
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>
-#if ENABLE(SHARED_WORKERS)
-#include "SharedWorkerRepository.h"
-#endif
-
-#if ENABLE(SVG)
-#include "SVGDocument.h"
-#include "SVGLocatable.h"
-#include "SVGNames.h"
-#include "SVGPreserveAspectRatio.h"
-#include "SVGSVGElement.h"
-#include "SVGViewElement.h"
-#include "SVGViewSpec.h"
-#endif
-
#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
#include "Archive.h"
#endif
+#if ENABLE(DATA_DETECTION)
+#include "DataDetection.h"
+#endif
+
#if PLATFORM(IOS)
#include "DocumentType.h"
-#include "MemoryPressureHandler.h"
#include "ResourceLoader.h"
-#include "RuntimeApplicationChecksIOS.h"
-#include "SystemMemory.h"
+#include "RuntimeApplicationChecks.h"
#include "WKContentObservation.h"
#endif
+#define RELEASE_LOG_IF_ALLOWED(fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), Network, "%p - FrameLoader::" fmt, this, ##__VA_ARGS__)
+
namespace WebCore {
using namespace HTMLNames;
-
-#if ENABLE(SVG)
using namespace SVGNames;
-#endif
static const char defaultAcceptHeader[] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
-#if PLATFORM(IOS)
-const int memoryLevelThresholdToPrunePageCache = 20;
-#endif
-
bool isBackForwardLoadType(FrameLoadType type)
{
switch (type) {
- case FrameLoadTypeStandard:
- case FrameLoadTypeReload:
- case FrameLoadTypeReloadFromOrigin:
- case FrameLoadTypeSame:
- case FrameLoadTypeRedirectWithLockedBackForwardList:
- case FrameLoadTypeReplace:
- return false;
- case FrameLoadTypeBack:
- case FrameLoadTypeForward:
- case FrameLoadTypeIndexedBackForward:
- return true;
+ case FrameLoadType::Standard:
+ case FrameLoadType::Reload:
+ case FrameLoadType::ReloadFromOrigin:
+ case FrameLoadType::Same:
+ case FrameLoadType::RedirectWithLockedBackForwardList:
+ case FrameLoadType::Replace:
+ return false;
+ case FrameLoadType::Back:
+ case FrameLoadType::Forward:
+ case FrameLoadType::IndexedBackForward:
+ return true;
}
ASSERT_NOT_REACHED();
return false;
@@ -180,12 +174,31 @@ bool isBackForwardLoadType(FrameLoadType type)
// non-member lets us exclude it from the header file, thus keeping FrameLoader.h's
// API simpler.
//
-static bool isDocumentSandboxed(Frame* frame, SandboxFlags mask)
+static bool isDocumentSandboxed(Frame& frame, SandboxFlags mask)
{
- return frame->document() && frame->document()->isSandboxed(mask);
+ return frame.document() && frame.document()->isSandboxed(mask);
}
+struct ForbidPromptsScope {
+ ForbidPromptsScope(Page* page) : m_page(page)
+ {
+ if (!m_page)
+ return;
+ m_page->forbidPrompts();
+ }
+
+ ~ForbidPromptsScope()
+ {
+ if (!m_page)
+ return;
+ m_page->allowPrompts();
+ }
+
+ Page* m_page;
+};
+
class FrameLoader::FrameProgressTracker {
+ WTF_MAKE_FAST_ALLOCATED;
public:
explicit FrameProgressTracker(Frame& frame)
: m_frame(frame)
@@ -230,23 +243,20 @@ FrameLoader::FrameLoader(Frame& frame, FrameLoaderClient& client)
, m_icon(std::make_unique<IconController>(frame))
, m_mixedContentChecker(frame)
, m_state(FrameStateProvisional)
- , m_loadType(FrameLoadTypeStandard)
- , m_delegateIsHandlingProvisionalLoadError(false)
+ , m_loadType(FrameLoadType::Standard)
, m_quickRedirectComing(false)
, m_sentRedirectNotification(false)
, m_inStopAllLoaders(false)
, m_isExecutingJavaScriptFormAction(false)
, m_didCallImplicitClose(true)
, m_wasUnloadEventEmitted(false)
- , m_pageDismissalEventBeingDispatched(NoDismissal)
, m_isComplete(false)
, m_needsClear(false)
- , m_checkTimer(this, &FrameLoader::checkTimerFired)
+ , m_checkTimer(*this, &FrameLoader::checkTimerFired)
, m_shouldCallCheckCompleted(false)
, m_shouldCallCheckLoadComplete(false)
, m_opener(nullptr)
, m_loadingFromCachedPage(false)
- , m_suppressOpenerInNewFrame(false)
, m_currentNavigationHasShownBeforeUnloadConfirmPanel(false)
, m_loadsSynchronously(false)
, m_forcedSandboxFlags(SandboxNone)
@@ -257,9 +267,8 @@ FrameLoader::~FrameLoader()
{
setOpener(nullptr);
- HashSet<Frame*>::iterator end = m_openedFrames.end();
- for (HashSet<Frame*>::iterator it = m_openedFrames.begin(); it != end; ++it)
- (*it)->loader().m_opener = 0;
+ for (auto& frame : m_openedFrames)
+ frame->loader().m_opener = nullptr;
m_client.frameLoaderDestroyed();
@@ -270,7 +279,7 @@ FrameLoader::~FrameLoader()
void FrameLoader::init()
{
// This somewhat odd set of steps gives the frame an initial empty document.
- setPolicyDocumentLoader(m_client.createDocumentLoader(ResourceRequest(URL(ParsedURLString, emptyString())), SubstituteData()).get());
+ setPolicyDocumentLoader(m_client.createDocumentLoader(ResourceRequest(URL(ParsedURLString, emptyString())), SubstituteData()).ptr());
setProvisionalDocumentLoader(m_policyDocumentLoader.get());
m_provisionalDocumentLoader->startLoadingMainResource();
@@ -289,8 +298,8 @@ void FrameLoader::initForSynthesizedDocument(const URL&)
// FrameLoader::checkCompleted() will overwrite the URL of the document to be activeDocumentLoader()->documentURL().
RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(ResourceRequest(URL(ParsedURLString, emptyString())), SubstituteData());
- loader->setFrame(&m_frame);
- loader->setResponse(ResourceResponse(URL(), ASCIILiteral("text/html"), 0, String(), String()));
+ loader->attachToFrame(m_frame);
+ loader->setResponse(ResourceResponse(URL(), ASCIILiteral("text/html"), 0, String()));
loader->setCommitted(true);
setDocumentLoader(loader.get());
@@ -305,6 +314,7 @@ void FrameLoader::initForSynthesizedDocument(const URL&)
m_needsClear = true;
m_networkingContext = m_client.createNetworkingContext();
+ m_progressTracker = std::make_unique<FrameProgressTracker>(m_frame);
}
#endif
@@ -324,60 +334,50 @@ void FrameLoader::setDefersLoading(bool defers)
}
}
-void FrameLoader::changeLocation(SecurityOrigin* securityOrigin, const URL& url, const String& referrer, bool lockHistory, bool lockBackForwardList, bool refresh)
+void FrameLoader::changeLocation(const FrameLoadRequest& request)
{
- urlSelected(FrameLoadRequest(securityOrigin, ResourceRequest(url, referrer, refresh ? ReloadIgnoringCacheData : UseProtocolCachePolicy), "_self"),
- 0, lockHistory, lockBackForwardList, MaybeSendReferrer, ReplaceDocumentIfJavaScriptURL);
+ urlSelected(request, nullptr);
}
-void FrameLoader::urlSelected(const URL& url, const String& passedTarget, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, ShouldSendReferrer shouldSendReferrer)
+void FrameLoader::urlSelected(const URL& url, const String& passedTarget, Event* triggeringEvent, LockHistory lockHistory, LockBackForwardList lockBackForwardList, ShouldSendReferrer shouldSendReferrer, ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy, std::optional<NewFrameOpenerPolicy> openerPolicy, const AtomicString& downloadAttribute)
{
- urlSelected(FrameLoadRequest(m_frame.document()->securityOrigin(), ResourceRequest(url), passedTarget),
- triggeringEvent, lockHistory, lockBackForwardList, shouldSendReferrer, DoNotReplaceDocumentIfJavaScriptURL);
+ NewFrameOpenerPolicy newFrameOpenerPolicy = openerPolicy.value_or(shouldSendReferrer == NeverSendReferrer ? NewFrameOpenerPolicy::Suppress : NewFrameOpenerPolicy::Allow);
+ urlSelected(FrameLoadRequest(m_frame.document()->securityOrigin(), ResourceRequest(url), passedTarget, lockHistory, lockBackForwardList, shouldSendReferrer, AllowNavigationToInvalidURL::Yes, newFrameOpenerPolicy, DoNotReplaceDocumentIfJavaScriptURL, shouldOpenExternalURLsPolicy, downloadAttribute), triggeringEvent);
}
-// The shouldReplaceDocumentIfJavaScriptURL parameter will go away when the FIXME to eliminate the
-// corresponding parameter from ScriptController::executeIfJavaScriptURL() is addressed.
-void FrameLoader::urlSelected(const FrameLoadRequest& passedRequest, PassRefPtr<Event> triggeringEvent, bool lockHistory, bool lockBackForwardList, ShouldSendReferrer shouldSendReferrer, ShouldReplaceDocumentIfJavaScriptURL shouldReplaceDocumentIfJavaScriptURL)
+void FrameLoader::urlSelected(const FrameLoadRequest& passedRequest, Event* triggeringEvent)
{
- ASSERT(!m_suppressOpenerInNewFrame);
-
Ref<Frame> protect(m_frame);
FrameLoadRequest frameRequest(passedRequest);
- if (m_frame.script().executeIfJavaScriptURL(frameRequest.resourceRequest().url(), shouldReplaceDocumentIfJavaScriptURL))
+ if (m_frame.script().executeIfJavaScriptURL(frameRequest.resourceRequest().url(), frameRequest.shouldReplaceDocumentIfJavaScriptURL()))
return;
if (frameRequest.frameName().isEmpty())
frameRequest.setFrameName(m_frame.document()->baseTarget());
- if (shouldSendReferrer == NeverSendReferrer)
- m_suppressOpenerInNewFrame = true;
addHTTPOriginIfNeeded(frameRequest.resourceRequest(), outgoingOrigin());
+ m_frame.document()->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(frameRequest.resourceRequest(), ContentSecurityPolicy::InsecureRequestType::Navigation);
- loadFrameRequest(frameRequest, lockHistory, lockBackForwardList, triggeringEvent, 0, shouldSendReferrer);
-
- m_suppressOpenerInNewFrame = false;
+ loadFrameRequest(frameRequest, triggeringEvent, nullptr);
}
-void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission)
+void FrameLoader::submitForm(Ref<FormSubmission>&& submission)
{
- ASSERT(submission->method() == FormSubmission::PostMethod || submission->method() == FormSubmission::GetMethod);
+ ASSERT(submission->method() == FormSubmission::Method::Post || submission->method() == FormSubmission::Method::Get);
// FIXME: Find a good spot for these.
- ASSERT(submission->data());
- ASSERT(submission->state());
- ASSERT(!submission->state()->sourceDocument()->frame() || submission->state()->sourceDocument()->frame() == &m_frame);
-
+ ASSERT(!submission->state().sourceDocument().frame() || submission->state().sourceDocument().frame() == &m_frame);
+
if (!m_frame.page())
return;
-
+
if (submission->action().isEmpty())
return;
- if (isDocumentSandboxed(&m_frame, SandboxForms)) {
+ if (isDocumentSandboxed(m_frame, SandboxForms)) {
// FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
- m_frame.document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Blocked form submission to '" + submission->action().stringCenterEllipsizedToLength() + "' because the form's frame is sandboxed and the 'allow-forms' permission is not set.");
+ 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.");
return;
}
@@ -391,12 +391,12 @@ void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission)
return;
}
- Frame* targetFrame = findFrameForNavigation(submission->target(), submission->state()->sourceDocument());
+ Frame* targetFrame = findFrameForNavigation(submission->target(), &submission->state().sourceDocument());
if (!targetFrame) {
- if (!DOMWindow::allowPopUp(&m_frame) && !ScriptController::processingUserGesture())
+ if (!DOMWindow::allowPopUp(m_frame) && !ScriptController::processingUserGesture())
return;
- // FIXME: targetFrame can be 0 for two distinct reasons:
+ // FIXME: targetFrame can be null for two distinct reasons:
// 1. The frame was not found by name, so we should try opening a new window.
// 2. The frame was found, but navigating it was not allowed, e.g. by HTML5 sandbox or by origin checks.
// Continuing form submission makes no sense in the latter case.
@@ -413,7 +413,7 @@ void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission)
// We do not want to submit more than one form from the same page, nor do we want to submit a single
// form more than once. This flag prevents these from happening; not sure how other browsers prevent this.
- // The flag is reset in each time we start handle a new mouse or key down event, and
+ // The flag is reset in each time we start dispatching a new mouse or key down event, and
// also in setView since this part may get reused for a page from the back/forward cache.
// The form multi-submit logic here is only needed when we are submitting a form that affects this frame.
@@ -427,11 +427,11 @@ void FrameLoader::submitForm(PassRefPtr<FormSubmission> submission)
m_submittedFormURL = submission->requestURL();
}
- submission->data()->generateFiles(m_frame.document());
+ submission->data().generateFiles(m_frame.document());
submission->setReferrer(outgoingReferrer());
submission->setOrigin(outgoingOrigin());
- targetFrame->navigationScheduler().scheduleFormSubmission(submission);
+ targetFrame->navigationScheduler().scheduleFormSubmission(WTFMove(submission));
}
void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy)
@@ -439,55 +439,8 @@ void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy)
if (m_frame.document() && m_frame.document()->parser())
m_frame.document()->parser()->stopParsing();
- if (unloadEventPolicy != UnloadEventPolicyNone) {
- if (m_frame.document()) {
- if (m_didCallImplicitClose && !m_wasUnloadEventEmitted) {
- Element* currentFocusedElement = m_frame.document()->focusedElement();
- if (currentFocusedElement && currentFocusedElement->toInputElement())
- currentFocusedElement->toInputElement()->endEditing();
- if (m_pageDismissalEventBeingDispatched == NoDismissal) {
- if (unloadEventPolicy == UnloadEventPolicyUnloadAndPageHide) {
- m_pageDismissalEventBeingDispatched = PageHideDismissal;
- m_frame.document()->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, m_frame.document()->inPageCache()), m_frame.document());
- }
-
- // FIXME: update Page Visibility state here.
- // https://bugs.webkit.org/show_bug.cgi?id=116770
-
- if (!m_frame.document()->inPageCache()) {
- RefPtr<Event> unloadEvent(Event::create(eventNames().unloadEvent, false, false));
- // The DocumentLoader (and thus its DocumentLoadTiming) might get destroyed
- // while dispatching the event, so protect it to prevent writing the end
- // time into freed memory.
- RefPtr<DocumentLoader> documentLoader = m_provisionalDocumentLoader;
- m_pageDismissalEventBeingDispatched = UnloadDismissal;
- if (documentLoader && !documentLoader->timing()->unloadEventStart() && !documentLoader->timing()->unloadEventEnd()) {
- DocumentLoadTiming* timing = documentLoader->timing();
- ASSERT(timing->navigationStart());
- timing->markUnloadEventStart();
- m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document());
- timing->markUnloadEventEnd();
- } else
- m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document());
- }
- }
- m_pageDismissalEventBeingDispatched = NoDismissal;
- if (m_frame.document())
- m_frame.document()->updateStyleIfNeeded();
- m_wasUnloadEventEmitted = true;
- }
- }
-
- // Dispatching the unload event could have made m_frame.document() null.
- if (m_frame.document() && !m_frame.document()->inPageCache()) {
- // Don't remove event listeners from a transitional empty document (see bug 28716 for more information).
- bool keepEventListeners = m_stateMachine.isDisplayingInitialEmptyDocument() && m_provisionalDocumentLoader
- && m_frame.document()->isSecureTransitionTo(m_provisionalDocumentLoader->url());
-
- if (!keepEventListeners)
- m_frame.document()->removeAllEventListeners();
- }
- }
+ if (unloadEventPolicy != UnloadEventPolicyNone)
+ dispatchUnloadEvents(unloadEventPolicy);
m_isComplete = true; // to avoid calling completed() in finishedParsing()
m_didCallImplicitClose = true; // don't want that one either
@@ -497,15 +450,13 @@ void FrameLoader::stopLoading(UnloadEventPolicy unloadEventPolicy)
m_frame.document()->setParsing(false);
}
- if (Document* doc = m_frame.document()) {
+ if (auto* document = m_frame.document()) {
// FIXME: HTML5 doesn't tell us to set the state to complete when aborting, but we do anyway to match legacy behavior.
// http://www.w3.org/Bugs/Public/show_bug.cgi?id=10537
- doc->setReadyState(Document::Complete);
+ document->setReadyState(Document::Complete);
-#if ENABLE(SQL_DATABASE)
// FIXME: Should the DatabaseManager watch for something like ActiveDOMObject::stop() rather than being special-cased here?
- DatabaseManager::manager().stopDatabases(doc, 0);
-#endif
+ DatabaseManager::singleton().stopDatabases(*document, nullptr);
}
// FIXME: This will cancel redirection timer, which really needs to be restarted when restoring the frame from b/f cache.
@@ -533,18 +484,28 @@ void FrameLoader::willTransitionToCommitted()
if (m_frame.editor().hasComposition()) {
// The text was already present in DOM, so it's better to confirm than to cancel the composition.
m_frame.editor().confirmComposition();
- if (EditorClient* editorClient = m_frame.editor().client())
+ if (EditorClient* editorClient = m_frame.editor().client()) {
editorClient->respondToChangedSelection(&m_frame);
+ editorClient->discardedComposition(&m_frame);
+ }
}
}
bool FrameLoader::closeURL()
{
history().saveDocumentState();
-
- // Should only send the pagehide event here if the current document exists and has not been placed in the page cache.
+
Document* currentDocument = m_frame.document();
- stopLoading(currentDocument && !currentDocument->inPageCache() ? UnloadEventPolicyUnloadAndPageHide : UnloadEventPolicyUnloadOnly);
+ UnloadEventPolicy unloadEventPolicy;
+ if (m_frame.page() && m_frame.page()->chrome().client().isSVGImageChromeClient()) {
+ // If this is the SVGDocument of an SVGImage, no need to dispatch events or recalcStyle.
+ unloadEventPolicy = UnloadEventPolicyNone;
+ } else {
+ // Should only send the pagehide event here if the current document exists and has not been placed in the page cache.
+ unloadEventPolicy = currentDocument && currentDocument->pageCacheState() == Document::NotInPageCache ? UnloadEventPolicyUnloadAndPageHide : UnloadEventPolicyUnloadOnly;
+ }
+
+ stopLoading(unloadEventPolicy);
m_frame.editor().clearUndoRedoOperations();
return true;
@@ -606,6 +567,17 @@ void FrameLoader::cancelAndClear()
m_frame.script().updatePlatformScriptObjects();
}
+static inline bool shouldClearWindowName(const Frame& frame, const Document& newDocument)
+{
+ if (!frame.isMainFrame())
+ return false;
+
+ if (frame.loader().opener())
+ return false;
+
+ return !newDocument.securityOrigin().isSameOriginAs(frame.document()->securityOrigin());
+}
+
void FrameLoader::clear(Document* newDocument, bool clearWindowProperties, bool clearScriptObjects, bool clearFrameView)
{
m_frame.editor().clear();
@@ -614,33 +586,43 @@ void FrameLoader::clear(Document* newDocument, bool clearWindowProperties, bool
return;
m_needsClear = false;
- if (!m_frame.document()->inPageCache()) {
+ if (m_frame.document()->pageCacheState() != Document::InPageCache) {
m_frame.document()->cancelParsing();
m_frame.document()->stopActiveDOMObjects();
- if (m_frame.document()->hasLivingRenderTree()) {
- m_frame.document()->prepareForDestruction();
- m_frame.document()->removeFocusedNodeOfSubtree(m_frame.document());
- }
+ bool hadLivingRenderTree = m_frame.document()->hasLivingRenderTree();
+ m_frame.document()->prepareForDestruction();
+ if (hadLivingRenderTree)
+ m_frame.document()->removeFocusedNodeOfSubtree(*m_frame.document());
}
// Do this after detaching the document so that the unload event works.
if (clearWindowProperties) {
- InspectorInstrumentation::frameWindowDiscarded(&m_frame, m_frame.document()->domWindow());
- m_frame.document()->domWindow()->resetUnlessSuspendedForPageCache();
- m_frame.script().clearWindowShell(newDocument->domWindow(), m_frame.document()->inPageCache());
+ InspectorInstrumentation::frameWindowDiscarded(m_frame, m_frame.document()->domWindow());
+ m_frame.document()->domWindow()->resetUnlessSuspendedForDocumentSuspension();
+ m_frame.script().clearWindowShellsNotMatchingDOMWindow(newDocument->domWindow(), m_frame.document()->pageCacheState() == Document::AboutToEnterPageCache);
+
+ if (shouldClearWindowName(m_frame, *newDocument))
+ m_frame.tree().setName(nullAtom);
}
m_frame.selection().prepareForDestruction();
- m_frame.eventHandler().clear();
+
+ // We may call this code during object destruction, so need to make sure eventHandler is present.
+ if (auto eventHandler = m_frame.eventHandlerPtr())
+ eventHandler->clear();
+
if (clearFrameView && m_frame.view())
m_frame.view()->clear();
// Do not drop the document before the ScriptController and view are cleared
// as some destructors might still try to access the document.
- m_frame.setDocument(0);
+ m_frame.setDocument(nullptr);
subframeLoader().clear();
+ if (clearWindowProperties)
+ m_frame.script().setDOMWindowForWindowShell(newDocument->domWindow());
+
if (clearScriptObjects)
m_frame.script().clearScriptObjects();
@@ -658,36 +640,35 @@ void FrameLoader::clear(Document* newDocument, bool clearWindowProperties, bool
void FrameLoader::receivedFirstData()
{
- dispatchDidCommitLoad();
+ dispatchDidCommitLoad(std::nullopt);
dispatchDidClearWindowObjectsInAllWorlds();
dispatchGlobalObjectAvailableInAllWorlds();
if (m_documentLoader) {
- StringWithDirection ptitle = m_documentLoader->title();
- // If we have a title let the WebView know about it.
- if (!ptitle.isNull())
- m_client.dispatchDidReceiveTitle(ptitle);
+ auto& title = m_documentLoader->title();
+ if (!title.string.isNull())
+ m_client.dispatchDidReceiveTitle(title);
}
if (!m_documentLoader)
return;
- if (m_frame.document()->isViewSource())
- return;
+
+ ASSERT(m_frame.document());
+ auto& document = *m_frame.document();
+
+ ASSERT(m_frame.document());
+ LinkLoader::loadLinksFromHeader(m_documentLoader->response().httpHeaderField(HTTPHeaderName::Link), m_frame.document()->url(), *m_frame.document());
double delay;
- String url;
- if (!parseHTTPRefresh(m_documentLoader->response().httpHeaderField("Refresh"), false, delay, url))
+ String urlString;
+ if (!parseHTTPRefresh(m_documentLoader->response().httpHeaderField(HTTPHeaderName::Refresh), delay, urlString))
return;
- if (url.isEmpty())
- url = m_frame.document()->url().string();
- else
- url = m_frame.document()->completeURL(url).string();
-
- if (!protocolIsJavaScript(url))
- m_frame.navigationScheduler().scheduleRedirect(delay, url);
+ auto completedURL = urlString.isEmpty() ? document.url() : document.completeURL(urlString);
+ if (!protocolIsJavaScript(completedURL))
+ m_frame.navigationScheduler().scheduleRedirect(document, delay, completedURL);
else {
- String message = "Refused to refresh " + m_frame.document()->url().stringCenterEllipsizedToLength() + " to a javascript: URL";
- m_frame.document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, message);
+ auto message = "Refused to refresh " + document.url().stringCenterEllipsizedToLength() + " to a javascript: URL";
+ m_frame.document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
}
}
@@ -704,8 +685,8 @@ void FrameLoader::didBeginDocument(bool dispatch)
m_frame.document()->setReadyState(Document::Loading);
if (m_pendingStateObject) {
- m_frame.document()->statePopped(m_pendingStateObject.get());
- m_pendingStateObject.clear();
+ m_frame.document()->statePopped(*m_pendingStateObject);
+ m_pendingStateObject = nullptr;
}
if (dispatch)
@@ -715,31 +696,17 @@ void FrameLoader::didBeginDocument(bool dispatch)
m_frame.document()->initContentSecurityPolicy();
const Settings& settings = m_frame.settings();
- m_frame.document()->cachedResourceLoader()->setImagesEnabled(settings.areImagesEnabled());
- m_frame.document()->cachedResourceLoader()->setAutoLoadImages(settings.loadsImagesAutomatically());
+ m_frame.document()->cachedResourceLoader().setImagesEnabled(settings.areImagesEnabled());
+ m_frame.document()->cachedResourceLoader().setAutoLoadImages(settings.loadsImagesAutomatically());
if (m_documentLoader) {
- String dnsPrefetchControl = m_documentLoader->response().httpHeaderField("X-DNS-Prefetch-Control");
+ String dnsPrefetchControl = m_documentLoader->response().httpHeaderField(HTTPHeaderName::XDNSPrefetchControl);
if (!dnsPrefetchControl.isEmpty())
m_frame.document()->parseDNSPrefetchControlHeader(dnsPrefetchControl);
- String policyValue = m_documentLoader->response().httpHeaderField("Content-Security-Policy");
- if (!policyValue.isEmpty())
- m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::Enforce);
-
- policyValue = m_documentLoader->response().httpHeaderField("Content-Security-Policy-Report-Only");
- if (!policyValue.isEmpty())
- m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::Report);
-
- policyValue = m_documentLoader->response().httpHeaderField("X-WebKit-CSP");
- if (!policyValue.isEmpty())
- m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::PrefixedEnforce);
+ m_frame.document()->contentSecurityPolicy()->didReceiveHeaders(ContentSecurityPolicyResponseHeaders(m_documentLoader->response()), ContentSecurityPolicy::ReportParsingErrors::No);
- policyValue = m_documentLoader->response().httpHeaderField("X-WebKit-CSP-Report-Only");
- if (!policyValue.isEmpty())
- m_frame.document()->contentSecurityPolicy()->didReceiveHeader(policyValue, ContentSecurityPolicy::PrefixedReport);
-
- String headerContentLanguage = m_documentLoader->response().httpHeaderField("Content-Language");
+ String headerContentLanguage = m_documentLoader->response().httpHeaderField(HTTPHeaderName::ContentLanguage);
if (!headerContentLanguage.isEmpty()) {
size_t commaIndex = headerContentLanguage.find(',');
headerContentLanguage.truncate(commaIndex); // notFound == -1 == don't truncate
@@ -766,6 +733,8 @@ void FrameLoader::finishedParsing()
m_client.dispatchDidFinishDocumentLoad();
+ scrollToFragmentWithParentBoundary(m_frame.document()->url());
+
checkCompleted();
if (!m_frame.view())
@@ -774,7 +743,6 @@ void FrameLoader::finishedParsing()
// Check if the scrollbars are really needed for the content.
// If not, remove them, relayout, and repaint.
m_frame.view()->restoreScrollbar();
- scrollToFragmentWithParentBoundary(m_frame.document()->url());
}
void FrameLoader::loadDone()
@@ -802,32 +770,47 @@ bool FrameLoader::allAncestorsAreComplete() const
void FrameLoader::checkCompleted()
{
- Ref<Frame> protect(m_frame);
m_shouldCallCheckCompleted = false;
// Have we completed before?
if (m_isComplete)
return;
+ // FIXME: It would be better if resource loads were kicked off after render tree update (or didn't complete synchronously).
+ // https://bugs.webkit.org/show_bug.cgi?id=171729
+ if (m_frame.document()->inRenderTreeUpdate()) {
+ scheduleCheckCompleted();
+ return;
+ }
+
// Are we still parsing?
if (m_frame.document()->parsing())
return;
// Still waiting for images/scripts?
- if (m_frame.document()->cachedResourceLoader()->requestCount())
+ if (m_frame.document()->cachedResourceLoader().requestCount())
return;
// Still waiting for elements that don't go through a FrameLoader?
if (m_frame.document()->isDelayingLoadEvent())
return;
+ auto* scriptableParser = m_frame.document()->scriptableDocumentParser();
+ if (scriptableParser && scriptableParser->hasScriptsWaitingForStylesheets())
+ return;
+
// Any frame that hasn't completed yet?
if (!allChildrenAreComplete())
return;
+ // Important not to protect earlier in this function, because earlier parts
+ // of this function can be called in the frame's destructor, and it's not legal
+ // to ref an object while it's being destroyed.
+ Ref<Frame> protect(m_frame);
+
// OK, completed.
m_isComplete = true;
- m_requestedHistoryItem = 0;
+ m_requestedHistoryItem = nullptr;
m_frame.document()->setReadyState(Document::Complete);
#if PLATFORM(IOS)
@@ -851,7 +834,7 @@ void FrameLoader::checkCompleted()
checkLoadComplete();
}
-void FrameLoader::checkTimerFired(Timer<FrameLoader>&)
+void FrameLoader::checkTimerFired()
{
Ref<Frame> protect(m_frame);
@@ -904,110 +887,78 @@ void FrameLoader::loadURLIntoChildFrame(const URL& url, const String& referer, F
ASSERT(childFrame);
#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
- RefPtr<Archive> subframeArchive = activeDocumentLoader()->popArchiveForSubframe(childFrame->tree().uniqueName(), url);
- if (subframeArchive) {
- childFrame->loader().loadArchive(subframeArchive.release());
- return;
+ if (auto activeLoader = activeDocumentLoader()) {
+ if (auto subframeArchive = activeLoader->popArchiveForSubframe(childFrame->tree().uniqueName(), url)) {
+ childFrame->loader().loadArchive(RefPtr<Archive> { subframeArchive }.releaseNonNull());
+ return;
+ }
}
-#endif // ENABLE(WEB_ARCHIVE)
+#endif
- HistoryItem* parentItem = history().currentItem();
// If we're moving in the back/forward list, we might want to replace the content
// of this child frame with whatever was there at that point.
- if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType())
- && !m_frame.document()->loadEventFinished()) {
- HistoryItem* childItem = parentItem->childItemWithTarget(childFrame->tree().uniqueName());
- if (childItem) {
+ auto* parentItem = history().currentItem();
+ if (parentItem && parentItem->children().size() && isBackForwardLoadType(loadType()) && !m_frame.document()->loadEventFinished()) {
+ if (auto* childItem = parentItem->childItemWithTarget(childFrame->tree().uniqueName())) {
childFrame->loader().m_requestedHistoryItem = childItem;
- childFrame->loader().loadDifferentDocumentItem(childItem, loadType(), MayAttemptCacheOnlyLoadForFormSubmissionItem);
+ childFrame->loader().loadDifferentDocumentItem(*childItem, loadType(), MayAttemptCacheOnlyLoadForFormSubmissionItem);
return;
}
}
- childFrame->loader().loadURL(url, referer, "_self", false, FrameLoadTypeRedirectWithLockedBackForwardList, 0, 0);
+ FrameLoadRequest frameLoadRequest(m_frame.document()->securityOrigin(), ResourceRequest(url), "_self", LockHistory::No, LockBackForwardList::Yes, ShouldSendReferrer::MaybeSendReferrer, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress, ReplaceDocumentIfJavaScriptURL, ShouldOpenExternalURLsPolicy::ShouldNotAllow);
+ childFrame->loader().loadURL(frameLoadRequest, referer, FrameLoadType::RedirectWithLockedBackForwardList, 0, 0);
}
#if ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
-void FrameLoader::loadArchive(PassRefPtr<Archive> archive)
+
+void FrameLoader::loadArchive(Ref<Archive>&& archive)
{
ArchiveResource* mainResource = archive->mainResource();
ASSERT(mainResource);
if (!mainResource)
return;
-
- SubstituteData substituteData(mainResource->data(), mainResource->mimeType(), mainResource->textEncoding(), URL());
+
+ ResourceResponse response(URL(), mainResource->mimeType(), mainResource->data().size(), mainResource->textEncoding());
+ SubstituteData substituteData(&mainResource->data(), URL(), response, SubstituteData::SessionHistoryVisibility::Hidden);
ResourceRequest request(mainResource->url());
-#if PLATFORM(MAC)
- request.applyWebArchiveHackForMail();
-#endif
- RefPtr<DocumentLoader> documentLoader = m_client.createDocumentLoader(request, substituteData);
- documentLoader->setArchive(archive.get());
- load(documentLoader.get());
+ auto documentLoader = m_client.createDocumentLoader(request, substituteData);
+ documentLoader->setArchive(WTFMove(archive));
+ load(documentLoader.ptr());
}
-#endif // ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
-
-ObjectContentType FrameLoader::defaultObjectContentType(const URL& url, const String& mimeTypeIn, bool shouldPreferPlugInsForImages)
-{
- String mimeType = mimeTypeIn;
-
- if (mimeType.isEmpty())
- mimeType = mimeTypeFromURL(url);
-
-#if !PLATFORM(MAC) && !PLATFORM(EFL) // Mac has no PluginDatabase, nor does EFL
- if (mimeType.isEmpty()) {
- String decodedPath = decodeURLEscapeSequences(url.path());
- mimeType = PluginDatabase::installedPlugins()->MIMETypeForExtension(decodedPath.substring(decodedPath.reverseFind('.') + 1));
- }
-#endif
-
- if (mimeType.isEmpty())
- return ObjectContentFrame; // Go ahead and hope that we can display the content.
-
-#if !PLATFORM(MAC) && !PLATFORM(EFL) // Mac has no PluginDatabase, nor does EFL
- bool plugInSupportsMIMEType = PluginDatabase::installedPlugins()->isMIMETypeRegistered(mimeType);
-#else
- bool plugInSupportsMIMEType = false;
-#endif
- if (MIMETypeRegistry::isSupportedImageMIMEType(mimeType))
- return shouldPreferPlugInsForImages && plugInSupportsMIMEType ? WebCore::ObjectContentNetscapePlugin : WebCore::ObjectContentImage;
-
- if (plugInSupportsMIMEType)
- return WebCore::ObjectContentNetscapePlugin;
-
- if (MIMETypeRegistry::isSupportedNonImageMIMEType(mimeType))
- return WebCore::ObjectContentFrame;
-
- return WebCore::ObjectContentNone;
-}
+#endif // ENABLE(WEB_ARCHIVE) || ENABLE(MHTML)
String FrameLoader::outgoingReferrer() const
{
// See http://www.whatwg.org/specs/web-apps/current-work/#fetching-resources
// for why we walk the parent chain for srcdoc documents.
Frame* frame = &m_frame;
- while (frame->document()->isSrcdocDocument()) {
+ while (frame && frame->document()->isSrcdocDocument()) {
frame = frame->tree().parent();
// Srcdoc documents cannot be top-level documents, by definition,
// because they need to be contained in iframes with the srcdoc.
ASSERT(frame);
}
+ if (!frame)
+ return emptyString();
return frame->loader().m_outgoingReferrer;
}
String FrameLoader::outgoingOrigin() const
{
- return m_frame.document()->securityOrigin()->toString();
+ return m_frame.document()->securityOrigin().toString();
}
-bool FrameLoader::checkIfFormActionAllowedByCSP(const URL& url) const
+bool FrameLoader::checkIfFormActionAllowedByCSP(const URL& url, bool didReceiveRedirectResponse) const
{
if (m_submittedFormURL.isEmpty())
return true;
- return m_frame.document()->contentSecurityPolicy()->allowFormAction(url);
+ auto redirectResponseReceived = didReceiveRedirectResponse ? ContentSecurityPolicy::RedirectResponseReceived::Yes : ContentSecurityPolicy::RedirectResponseReceived::No;
+ return m_frame.document()->contentSecurityPolicy()->allowFormAction(url, redirectResponseReceived);
}
Frame* FrameLoader::opener()
@@ -1034,9 +985,9 @@ void FrameLoader::setOpener(Frame* opener)
void FrameLoader::handleFallbackContent()
{
HTMLFrameOwnerElement* owner = m_frame.ownerElement();
- if (!owner || !owner->hasTagName(objectTag))
+ if (!is<HTMLObjectElement>(owner))
return;
- toHTMLObjectElement(owner)->renderFallbackContent();
+ downcast<HTMLObjectElement>(*owner).renderFallbackContent();
}
void FrameLoader::provisionalLoadStarted()
@@ -1045,6 +996,11 @@ void FrameLoader::provisionalLoadStarted()
m_stateMachine.advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad);
m_frame.navigationScheduler().cancel(true);
m_client.provisionalLoadStarted();
+
+ if (m_frame.isMainFrame()) {
+ if (auto* page = m_frame.page())
+ page->didStartProvisionalLoad();
+ }
}
void FrameLoader::resetMultipleFormSubmissionProtection()
@@ -1068,7 +1024,7 @@ void FrameLoader::setFirstPartyForCookies(const URL& url)
// This does the same kind of work that didOpenURL does, except it relies on the fact
// that a higher level already checked that the URLs match and the scrolling is the right thing to do.
-void FrameLoader::loadInSameDocument(const URL& url, PassRefPtr<SerializedScriptValue> stateObject, bool isNewNavigation)
+void FrameLoader::loadInSameDocument(const URL& url, SerializedScriptValue* stateObject, bool isNewNavigation)
{
// If we have a state object, we cannot also be a new navigation.
ASSERT(!stateObject || (stateObject && !isNewNavigation));
@@ -1121,7 +1077,7 @@ void FrameLoader::loadInSameDocument(const URL& url, PassRefPtr<SerializedScript
m_client.dispatchDidNavigateWithinPage();
- m_frame.document()->statePopped(stateObject ? stateObject : SerializedScriptValue::nullValue());
+ m_frame.document()->statePopped(stateObject ? Ref<SerializedScriptValue> { *stateObject } : SerializedScriptValue::nullValue());
m_client.dispatchDidPopStateWithinPage();
if (hashChange) {
@@ -1149,45 +1105,25 @@ void FrameLoader::completed()
parent->loader().checkCompleted();
if (m_frame.view())
- m_frame.view()->maintainScrollPositionAtAnchor(0);
- m_activityAssertion = nullptr;
+ m_frame.view()->maintainScrollPositionAtAnchor(nullptr);
}
void FrameLoader::started()
{
- if (m_frame.page())
- m_activityAssertion = m_frame.page()->createActivityToken();
for (Frame* frame = &m_frame; frame; frame = frame->tree().parent())
frame->loader().m_isComplete = false;
}
-void FrameLoader::prepareForHistoryNavigation()
-{
- // If there is no currentItem, but we still want to engage in
- // history navigation we need to manufacture one, and update
- // the state machine of this frame to impersonate having
- // loaded it.
- RefPtr<HistoryItem> currentItem = history().currentItem();
- if (!currentItem) {
- currentItem = HistoryItem::create();
- currentItem->setLastVisitWasFailure(true);
- history().setCurrentItem(currentItem.get());
- m_frame.page()->backForward().setCurrentItem(currentItem.get());
-
- ASSERT(stateMachine()->isDisplayingInitialEmptyDocument());
- stateMachine()->advanceTo(FrameLoaderStateMachine::DisplayingInitialEmptyDocumentPostCommit);
- stateMachine()->advanceTo(FrameLoaderStateMachine::CommittedFirstRealLoad);
- }
-}
-
void FrameLoader::prepareForLoadStart()
{
+ RELEASE_LOG_IF_ALLOWED("prepareForLoadStart: Starting frame load (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
+
m_progressTracker->progressStarted();
m_client.dispatchDidStartProvisionalLoad();
if (AXObjectCache::accessibilityEnabled()) {
if (AXObjectCache* cache = m_frame.document()->existingAXObjectCache()) {
- AXObjectCache::AXLoadingEvent loadingEvent = loadType() == FrameLoadTypeReload ? AXObjectCache::AXLoadingReloaded : AXObjectCache::AXLoadingStarted;
+ AXObjectCache::AXLoadingEvent loadingEvent = loadType() == FrameLoadType::Reload ? AXObjectCache::AXLoadingReloaded : AXObjectCache::AXLoadingStarted;
cache->frameLoadingEventNotification(&m_frame, loadingEvent);
}
}
@@ -1198,15 +1134,14 @@ void FrameLoader::setupForReplace()
m_client.revertToProvisionalState(m_documentLoader.get());
setState(FrameStateProvisional);
m_provisionalDocumentLoader = m_documentLoader;
- m_documentLoader = 0;
+ m_documentLoader = nullptr;
detachChildren();
}
-void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHistory, bool lockBackForwardList,
- PassRefPtr<Event> event, PassRefPtr<FormState> formState, ShouldSendReferrer shouldSendReferrer)
-{
+void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, Event* event, FormState* formState)
+{
// Protect frame from getting blown away inside dispatchBeforeLoadEvent in loadWithDocumentLoader.
- Ref<Frame> protect(m_frame);
+ auto protectFrame = makeRef(m_frame);
URL url = request.resourceRequest().url();
@@ -1221,25 +1156,25 @@ void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHis
argsReferrer = outgoingReferrer();
String referrer = SecurityPolicy::generateReferrerHeader(m_frame.document()->referrerPolicy(), url, argsReferrer);
- if (shouldSendReferrer == NeverSendReferrer)
+ if (request.shouldSendReferrer() == NeverSendReferrer)
referrer = String();
-
+
FrameLoadType loadType;
if (request.resourceRequest().cachePolicy() == ReloadIgnoringCacheData)
- loadType = FrameLoadTypeReload;
- else if (lockBackForwardList)
- loadType = FrameLoadTypeRedirectWithLockedBackForwardList;
+ loadType = FrameLoadType::Reload;
+ else if (request.lockBackForwardList() == LockBackForwardList::Yes)
+ loadType = FrameLoadType::RedirectWithLockedBackForwardList;
else
- loadType = FrameLoadTypeStandard;
+ loadType = FrameLoadType::Standard;
if (request.resourceRequest().httpMethod() == "POST")
- loadPostRequest(request.resourceRequest(), referrer, request.frameName(), lockHistory, loadType, event, formState.get());
+ loadPostRequest(request, referrer, loadType, event, formState);
else
- loadURL(request.resourceRequest().url(), referrer, request.frameName(), lockHistory, loadType, event, formState.get());
+ loadURL(request, referrer, loadType, event, formState);
// FIXME: It's possible this targetFrame will not be the same frame that was targeted by the actual
// load if frame names have changed.
- Frame* sourceFrame = formState ? formState->sourceDocument()->frame() : &m_frame;
+ Frame* sourceFrame = formState ? formState->sourceDocument().frame() : &m_frame;
if (!sourceFrame)
sourceFrame = &m_frame;
Frame* targetFrame = sourceFrame->loader().findFrameForNavigation(request.frameName());
@@ -1249,48 +1184,67 @@ void FrameLoader::loadFrameRequest(const FrameLoadRequest& request, bool lockHis
}
}
-void FrameLoader::loadURL(const URL& newURL, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType newLoadType,
- PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState)
+static ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicyToApply(Frame& sourceFrame, ShouldOpenExternalURLsPolicy propagatedPolicy)
+{
+ if (!sourceFrame.isMainFrame())
+ return ShouldOpenExternalURLsPolicy::ShouldNotAllow;
+ if (ScriptController::processingUserGesture())
+ return ShouldOpenExternalURLsPolicy::ShouldAllow;
+ return propagatedPolicy;
+}
+
+bool FrameLoader::isNavigationAllowed() const
+{
+ return m_pageDismissalEventBeingDispatched == PageDismissalType::None && NavigationDisabler::isNavigationAllowed();
+}
+
+void FrameLoader::loadURL(const FrameLoadRequest& frameLoadRequest, const String& referrer, FrameLoadType newLoadType, Event* event, FormState* formState)
{
if (m_inStopAllLoaders)
return;
Ref<Frame> protect(m_frame);
- RefPtr<FormState> formState = prpFormState;
+ String frameName = frameLoadRequest.frameName();
+ AllowNavigationToInvalidURL allowNavigationToInvalidURL = frameLoadRequest.allowNavigationToInvalidURL();
+ NewFrameOpenerPolicy openerPolicy = frameLoadRequest.newFrameOpenerPolicy();
+ LockHistory lockHistory = frameLoadRequest.lockHistory();
bool isFormSubmission = formState;
-
+
+ const URL& newURL = frameLoadRequest.resourceRequest().url();
ResourceRequest request(newURL);
if (!referrer.isEmpty()) {
request.setHTTPReferrer(referrer);
RefPtr<SecurityOrigin> referrerOrigin = SecurityOrigin::createFromString(referrer);
addHTTPOriginIfNeeded(request, referrerOrigin->toString());
}
-#if ENABLE(CACHE_PARTITIONING)
if (&m_frame.tree().top() != &m_frame)
- request.setCachePartition(m_frame.tree().top().document()->securityOrigin()->cachePartition());
-#endif
+ request.setDomainForCachePartition(m_frame.tree().top().document()->securityOrigin().domainForCachePartition());
+
addExtraFieldsToRequest(request, newLoadType, true);
- if (newLoadType == FrameLoadTypeReload || newLoadType == FrameLoadTypeReloadFromOrigin)
+ if (newLoadType == FrameLoadType::Reload || newLoadType == FrameLoadType::ReloadFromOrigin)
request.setCachePolicy(ReloadIgnoringCacheData);
- ASSERT(newLoadType != FrameLoadTypeSame);
+ ASSERT(newLoadType != FrameLoadType::Same);
// The search for a target frame is done earlier in the case of form submission.
Frame* targetFrame = isFormSubmission ? 0 : findFrameForNavigation(frameName);
if (targetFrame && targetFrame != &m_frame) {
- targetFrame->loader().loadURL(newURL, referrer, "_self", lockHistory, newLoadType, event, formState.release());
+ FrameLoadRequest newFrameLoadRequest(frameLoadRequest);
+ newFrameLoadRequest.setFrameName("_self");
+ targetFrame->loader().loadURL(newFrameLoadRequest, referrer, newLoadType, event, formState);
return;
}
- if (m_pageDismissalEventBeingDispatched != NoDismissal)
+ if (!isNavigationAllowed())
return;
- NavigationAction action(request, newLoadType, isFormSubmission, event);
+ NavigationAction action(request, newLoadType, isFormSubmission, event, frameLoadRequest.shouldOpenExternalURLsPolicy(), frameLoadRequest.downloadAttribute());
if (!targetFrame && !frameName.isEmpty()) {
- policyChecker().checkNewWindowPolicy(action, request, formState.release(), frameName, [this](const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) {
- continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue);
+ action = action.copyWithShouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicyToApply(m_frame, frameLoadRequest.shouldOpenExternalURLsPolicy()));
+ policyChecker().checkNewWindowPolicy(action, request, formState, frameName, [this, allowNavigationToInvalidURL, openerPolicy] (const ResourceRequest& request, FormState* formState, const String& frameName, const NavigationAction& action, bool shouldContinue) {
+ continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue, allowNavigationToInvalidURL, openerPolicy);
});
return;
}
@@ -1308,7 +1262,7 @@ void FrameLoader::loadURL(const URL& newURL, const String& referrer, const Strin
oldDocumentLoader->setLastCheckedRequest(ResourceRequest());
policyChecker().stopCheck();
policyChecker().setLoadType(newLoadType);
- policyChecker().checkNavigationPolicy(request, oldDocumentLoader.get(), formState.release(), [this](const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue) {
+ policyChecker().checkNavigationPolicy(request, false /* didReceiveRedirectResponse */, oldDocumentLoader.get(), formState, [this] (const ResourceRequest& request, FormState*, bool shouldContinue) {
continueFragmentScrollAfterNavigationPolicy(request, shouldContinue);
});
return;
@@ -1316,16 +1270,16 @@ void FrameLoader::loadURL(const URL& newURL, const String& referrer, const Strin
// must grab this now, since this load may stop the previous load and clear this flag
bool isRedirect = m_quickRedirectComing;
- loadWithNavigationAction(request, action, lockHistory, newLoadType, formState.release());
+ loadWithNavigationAction(request, action, lockHistory, newLoadType, formState, allowNavigationToInvalidURL);
if (isRedirect) {
m_quickRedirectComing = false;
if (m_provisionalDocumentLoader)
m_provisionalDocumentLoader->setIsClientRedirect(true);
- } else if (sameURL && newLoadType != FrameLoadTypeReload && newLoadType != FrameLoadTypeReloadFromOrigin) {
+ } else if (sameURL && newLoadType != FrameLoadType::Reload && newLoadType != FrameLoadType::ReloadFromOrigin) {
// Example of this case are sites that reload the same URL with a different cookie
// driving the generated content, or a master frame with links that drive a target
// frame, where the user has clicked on the same link repeatedly.
- m_loadType = FrameLoadTypeSame;
+ m_loadType = FrameLoadType::Same;
}
}
@@ -1333,10 +1287,12 @@ SubstituteData FrameLoader::defaultSubstituteDataForURL(const URL& url)
{
if (!shouldTreatURLAsSrcdocDocument(url))
return SubstituteData();
- String srcdoc = m_frame.ownerElement()->fastGetAttribute(srcdocAttr);
+ String srcdoc = m_frame.ownerElement()->attributeWithoutSynchronization(srcdocAttr);
ASSERT(!srcdoc.isNull());
CString encodedSrcdoc = srcdoc.utf8();
- return SubstituteData(SharedBuffer::create(encodedSrcdoc.data(), encodedSrcdoc.length()), "text/html", "UTF-8", URL());
+
+ ResourceResponse response(URL(), ASCIILiteral("text/html"), encodedSrcdoc.length(), ASCIILiteral("UTF-8"));
+ return SubstituteData(SharedBuffer::create(encodedSrcdoc.data(), encodedSrcdoc.length()), URL(), response, SubstituteData::SessionHistoryVisibility::Hidden);
}
void FrameLoader::load(const FrameLoadRequest& passedRequest)
@@ -1358,8 +1314,9 @@ void FrameLoader::load(const FrameLoadRequest& passedRequest)
}
if (request.shouldCheckNewWindowPolicy()) {
- policyChecker().checkNewWindowPolicy(NavigationAction(request.resourceRequest(), NavigationTypeOther), request.resourceRequest(), nullptr, request.frameName(), [this](const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) {
- continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue);
+ NavigationAction action(request.resourceRequest(), NavigationType::Other, passedRequest.shouldOpenExternalURLsPolicy());
+ policyChecker().checkNewWindowPolicy(action, request.resourceRequest(), nullptr, request.frameName(), [this] (const ResourceRequest& request, FormState* formState, const String& frameName, const NavigationAction& action, bool shouldContinue) {
+ continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue, AllowNavigationToInvalidURL::Yes, NewFrameOpenerPolicy::Suppress);
});
return;
@@ -1368,23 +1325,25 @@ void FrameLoader::load(const FrameLoadRequest& passedRequest)
if (!request.hasSubstituteData())
request.setSubstituteData(defaultSubstituteDataForURL(request.resourceRequest().url()));
- RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(request.resourceRequest(), request.substituteData());
- if (request.lockHistory() && m_documentLoader)
- loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory().string() : m_documentLoader->clientRedirectSourceForHistory());
- load(loader.get());
+ Ref<DocumentLoader> loader = m_client.createDocumentLoader(request.resourceRequest(), request.substituteData());
+ applyShouldOpenExternalURLsPolicyToNewDocumentLoader(loader, request.shouldOpenExternalURLsPolicy());
+
+ load(loader.ptr());
}
-void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, bool lockHistory, FrameLoadType type, PassRefPtr<FormState> formState)
+void FrameLoader::loadWithNavigationAction(const ResourceRequest& request, const NavigationAction& action, LockHistory lockHistory, FrameLoadType type, FormState* formState, AllowNavigationToInvalidURL allowNavigationToInvalidURL)
{
- RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url()));
- if (lockHistory && m_documentLoader)
+ Ref<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url()));
+ applyShouldOpenExternalURLsPolicyToNewDocumentLoader(loader, action.shouldOpenExternalURLsPolicy());
+
+ if (lockHistory == LockHistory::Yes && m_documentLoader)
loader->setClientRedirectSourceForHistory(m_documentLoader->didCreateGlobalHistoryEntry() ? m_documentLoader->urlForHistory().string() : m_documentLoader->clientRedirectSourceForHistory());
loader->setTriggeringAction(action);
if (m_documentLoader)
loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
- loadWithDocumentLoader(loader.get(), type, formState);
+ loadWithDocumentLoader(loader.ptr(), type, formState, allowNavigationToInvalidURL);
}
void FrameLoader::load(DocumentLoader* newDocumentLoader)
@@ -1395,11 +1354,13 @@ void FrameLoader::load(DocumentLoader* newDocumentLoader)
if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->originalRequest().url())) {
r.setCachePolicy(ReloadIgnoringCacheData);
- type = FrameLoadTypeSame;
- } else if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->unreachableURL()) && m_loadType == FrameLoadTypeReload)
- type = FrameLoadTypeReload;
+ type = FrameLoadType::Same;
+ } else if (shouldTreatURLAsSameAsCurrent(newDocumentLoader->unreachableURL()) && m_loadType == FrameLoadType::Reload)
+ type = FrameLoadType::Reload;
+ else if (m_loadType == FrameLoadType::RedirectWithLockedBackForwardList && !newDocumentLoader->unreachableURL().isEmpty() && newDocumentLoader->substituteData().isValid())
+ type = FrameLoadType::RedirectWithLockedBackForwardList;
else
- type = FrameLoadTypeStandard;
+ type = FrameLoadType::Standard;
if (m_documentLoader)
newDocumentLoader->setOverrideEncoding(m_documentLoader->overrideEncoding());
@@ -1408,7 +1369,7 @@ void FrameLoader::load(DocumentLoader* newDocumentLoader)
// visiting in the history list, we treat it as a reload so the history list
// is appropriately maintained.
//
- // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadTypeReload" ...
+ // FIXME: This seems like a dangerous overloading of the meaning of "FrameLoadType::Reload" ...
// shouldn't a more explicit type of reload be defined, that means roughly
// "load without affecting history" ?
if (shouldReloadToHandleUnreachableURL(newDocumentLoader)) {
@@ -1417,14 +1378,55 @@ void FrameLoader::load(DocumentLoader* newDocumentLoader)
// changed and updateForBackForwardNavigation() will not be called when loading is committed.
history().saveDocumentAndScrollState();
- ASSERT(type == FrameLoadTypeStandard);
- type = FrameLoadTypeReload;
+ ASSERT(type == FrameLoadType::Standard);
+ type = FrameLoadType::Reload;
}
- loadWithDocumentLoader(newDocumentLoader, type, 0);
+ loadWithDocumentLoader(newDocumentLoader, type, 0, AllowNavigationToInvalidURL::Yes);
+}
+
+static void logNavigation(MainFrame& frame, const URL& destinationURL, FrameLoadType type)
+{
+ if (!frame.page())
+ return;
+
+ String navigationDescription;
+ switch (type) {
+ case FrameLoadType::Standard:
+ navigationDescription = ASCIILiteral("standard");
+ break;
+ case FrameLoadType::Back:
+ navigationDescription = ASCIILiteral("back");
+ break;
+ case FrameLoadType::Forward:
+ navigationDescription = ASCIILiteral("forward");
+ break;
+ case FrameLoadType::IndexedBackForward:
+ navigationDescription = ASCIILiteral("indexedBackForward");
+ break;
+ case FrameLoadType::Reload:
+ navigationDescription = ASCIILiteral("reload");
+ break;
+ case FrameLoadType::Same:
+ navigationDescription = ASCIILiteral("same");
+ break;
+ case FrameLoadType::ReloadFromOrigin:
+ navigationDescription = ASCIILiteral("reloadFromOrigin");
+ break;
+ case FrameLoadType::Replace:
+ case FrameLoadType::RedirectWithLockedBackForwardList:
+ // Not logging those for now.
+ return;
+ }
+ frame.page()->diagnosticLoggingClient().logDiagnosticMessage(DiagnosticLoggingKeys::navigationKey(), navigationDescription, ShouldSample::No);
+#if ENABLE(PUBLIC_SUFFIX_LIST)
+ String domain = topPrivatelyControlledDomain(destinationURL.host());
+ if (!domain.isEmpty())
+ frame.page()->diagnosticLoggingClient().logDiagnosticMessageWithEnhancedPrivacy(DiagnosticLoggingKeys::domainVisitedKey(), domain, ShouldSample::No);
+#endif
}
-void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, PassRefPtr<FormState> prpFormState)
+void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType type, FormState* formState, AllowNavigationToInvalidURL allowNavigationToInvalidURL)
{
// Retain because dispatchBeforeLoadEvent may release the last reference to it.
Ref<Frame> protect(m_frame);
@@ -1436,17 +1438,23 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t
ASSERT(m_frame.view());
- if (m_pageDismissalEventBeingDispatched != NoDismissal)
+ if (!isNavigationAllowed())
return;
if (m_frame.document())
m_previousURL = m_frame.document()->url();
+ const URL& newURL = loader->request().url();
+
+ // Log main frame navigation types.
+ if (m_frame.isMainFrame()) {
+ logNavigation(static_cast<MainFrame&>(m_frame), newURL, type);
+ static_cast<MainFrame&>(m_frame).performanceLogging().didReachPointOfInterest(PerformanceLogging::MainFrameLoadStarted);
+ }
+
policyChecker().setLoadType(type);
- RefPtr<FormState> formState = prpFormState;
bool isFormSubmission = formState;
- const URL& newURL = loader->request().url();
const String& httpMethod = loader->request().httpMethod();
if (shouldPerformFragmentNavigation(isFormSubmission, httpMethod, policyChecker().loadType(), newURL)) {
@@ -1456,7 +1464,7 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t
oldDocumentLoader->setTriggeringAction(action);
oldDocumentLoader->setLastCheckedRequest(ResourceRequest());
policyChecker().stopCheck();
- policyChecker().checkNavigationPolicy(loader->request(), oldDocumentLoader.get(), formState, [this](const ResourceRequest& request, PassRefPtr<FormState>, bool shouldContinue) {
+ policyChecker().checkNavigationPolicy(loader->request(), false /* didReceiveRedirectResponse */, oldDocumentLoader.get(), formState, [this] (const ResourceRequest& request, FormState*, bool shouldContinue) {
continueFragmentScrollAfterNavigationPolicy(request, shouldContinue);
});
return;
@@ -1478,13 +1486,13 @@ void FrameLoader::loadWithDocumentLoader(DocumentLoader* loader, FrameLoadType t
// a new URL, the parent frame shouldn't learn the URL.
if (!m_stateMachine.committedFirstRealDocumentLoad()
&& !ownerElement->dispatchBeforeLoadEvent(loader->request().url().string())) {
- continueLoadAfterNavigationPolicy(loader->request(), formState, false);
+ continueLoadAfterNavigationPolicy(loader->request(), formState, false, allowNavigationToInvalidURL);
return;
}
}
- policyChecker().checkNavigationPolicy(loader->request(), loader, formState, [this](const ResourceRequest& request, PassRefPtr<FormState> formState, bool shouldContinue) {
- continueLoadAfterNavigationPolicy(request, formState, shouldContinue);
+ policyChecker().checkNavigationPolicy(loader->request(), false /* didReceiveRedirectResponse */, loader, formState, [this, allowNavigationToInvalidURL] (const ResourceRequest& request, FormState* formState, bool shouldContinue) {
+ continueLoadAfterNavigationPolicy(request, formState, shouldContinue, allowNavigationToInvalidURL);
});
}
@@ -1494,7 +1502,16 @@ void FrameLoader::reportLocalLoadFailed(Frame* frame, const String& url)
if (!frame)
return;
- frame->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "Not allowed to load local resource: " + url);
+ frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Not allowed to load local resource: " + url);
+}
+
+void FrameLoader::reportBlockedPortFailed(Frame* frame, const String& url)
+{
+ ASSERT(!url.isEmpty());
+ if (!frame)
+ return;
+
+ frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, "Not allowed to use restricted network port: " + url);
}
const ResourceRequest& FrameLoader::initialRequest() const
@@ -1507,7 +1524,7 @@ bool FrameLoader::willLoadMediaElementURL(URL& url)
#if PLATFORM(IOS)
// MobileStore depends on the iOS 4.0 era client delegate method because webView:resource:willSendRequest:redirectResponse:fromDataSource
// doesn't let them tell when a load request is coming from a media element. See <rdar://problem/8266916> for more details.
- if (applicationIsMobileStore())
+ if (IOSApplication::isMobileStore())
return m_client.shouldLoadMediaElementURL(url);
#endif
@@ -1516,7 +1533,7 @@ bool FrameLoader::willLoadMediaElementURL(URL& url)
unsigned long identifier;
ResourceError error;
requestFromDelegate(request, identifier, error);
- notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, ResourceResponse(url, String(), -1, String(), String()), 0, -1, -1, error);
+ notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, ResourceResponse(url, String(), -1, String()), 0, -1, -1, error);
url = request.url();
@@ -1538,13 +1555,10 @@ bool FrameLoader::shouldReloadToHandleUnreachableURL(DocumentLoader* docLoader)
// case handles well-formed URLs that can't be loaded, and the latter
// case handles malformed URLs and unknown schemes. Loading alternate content
// at other times behaves like a standard load.
- DocumentLoader* compareDocumentLoader = 0;
if (policyChecker().delegateIsDecidingNavigationPolicy() || policyChecker().delegateIsHandlingUnimplementablePolicy())
- compareDocumentLoader = m_policyDocumentLoader.get();
- else if (m_delegateIsHandlingProvisionalLoadError)
- compareDocumentLoader = m_provisionalDocumentLoader.get();
+ return m_policyDocumentLoader && unreachableURL == m_policyDocumentLoader->request().url();
- return compareDocumentLoader && unreachableURL == compareDocumentLoader->request().url();
+ return unreachableURL == m_provisionalLoadErrorBeingHandledURL;
}
void FrameLoader::reloadWithOverrideEncoding(const String& encoding)
@@ -1561,28 +1575,17 @@ void FrameLoader::reloadWithOverrideEncoding(const String& encoding)
// We should ask the user for confirmation in this case.
request.setCachePolicy(ReturnCacheDataElseLoad);
- RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url()));
- setPolicyDocumentLoader(loader.get());
-
- loader->setOverrideEncoding(encoding);
-
- loadWithDocumentLoader(loader.get(), FrameLoadTypeReload, 0);
-}
+ Ref<DocumentLoader> loader = m_client.createDocumentLoader(request, defaultSubstituteDataForURL(request.url()));
+ applyShouldOpenExternalURLsPolicyToNewDocumentLoader(loader, m_documentLoader->shouldOpenExternalURLsPolicyToPropagate());
-void FrameLoader::reloadWithOverrideURL(const URL& overrideUrl, bool endToEndReload)
-{
- if (!m_documentLoader)
- return;
+ setPolicyDocumentLoader(loader.ptr());
- if (overrideUrl.isEmpty())
- return;
+ loader->setOverrideEncoding(encoding);
- ResourceRequest request = m_documentLoader->request();
- request.setURL(overrideUrl);
- reloadWithRequest(request, endToEndReload);
+ loadWithDocumentLoader(loader.ptr(), FrameLoadType::Reload, 0, AllowNavigationToInvalidURL::Yes);
}
-void FrameLoader::reload(bool endToEndReload)
+void FrameLoader::reload(bool endToEndReload, bool contentBlockersEnabled)
{
if (!m_documentLoader)
return;
@@ -1598,17 +1601,13 @@ void FrameLoader::reload(bool endToEndReload)
if (!unreachableURL.isEmpty())
initialRequest.setURL(unreachableURL);
- reloadWithRequest(initialRequest, endToEndReload);
-}
-
-void FrameLoader::reloadWithRequest(const ResourceRequest& initialRequest, bool endToEndReload)
-{
- ASSERT(m_documentLoader);
-
// Create a new document loader for the reload, this will become m_documentLoader eventually,
// but first it has to be the "policy" document loader, and then the "provisional" document loader.
- RefPtr<DocumentLoader> loader = m_client.createDocumentLoader(initialRequest, defaultSubstituteDataForURL(initialRequest.url()));
+ Ref<DocumentLoader> loader = m_client.createDocumentLoader(initialRequest, defaultSubstituteDataForURL(initialRequest.url()));
+ applyShouldOpenExternalURLsPolicyToNewDocumentLoader(loader, m_documentLoader->shouldOpenExternalURLsPolicyToPropagate());
+ loader->setUserContentExtensionsEnabled(contentBlockersEnabled);
+
ResourceRequest& request = loader->request();
// FIXME: We don't have a mechanism to revalidate the main resource without reloading at the moment.
@@ -1616,17 +1615,17 @@ void FrameLoader::reloadWithRequest(const ResourceRequest& initialRequest, bool
// If we're about to re-post, set up action so the application can warn the user.
if (request.httpMethod() == "POST")
- loader->setTriggeringAction(NavigationAction(request, NavigationTypeFormResubmitted));
+ loader->setTriggeringAction(NavigationAction(request, NavigationType::FormResubmitted));
loader->setOverrideEncoding(m_documentLoader->overrideEncoding());
- loadWithDocumentLoader(loader.get(), endToEndReload ? FrameLoadTypeReloadFromOrigin : FrameLoadTypeReload, 0);
+ loadWithDocumentLoader(loader.ptr(), endToEndReload ? FrameLoadType::ReloadFromOrigin : FrameLoadType::Reload, 0, AllowNavigationToInvalidURL::Yes);
}
void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItemPolicy)
{
- ASSERT(!m_frame.document() || !m_frame.document()->inPageCache());
- if (m_pageDismissalEventBeingDispatched != NoDismissal)
+ ASSERT(!m_frame.document() || m_frame.document()->pageCacheState() != Document::InPageCache);
+ if (!isNavigationAllowed())
return;
// If this method is called from within this method, infinite recursion can occur (3442218). Avoid this.
@@ -1644,7 +1643,7 @@ void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItem
// If no new load is in progress, we should clear the provisional item from history
// before we call stopLoading.
if (clearProvisionalItemPolicy == ShouldClearProvisionalItem)
- history().setProvisionalItem(0);
+ history().setProvisionalItem(nullptr);
for (RefPtr<Frame> child = m_frame.tree().firstChild(); child; child = child->tree().nextSibling())
child->loader().stopAllLoaders(clearProvisionalItemPolicy);
@@ -1653,7 +1652,7 @@ void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItem
if (m_documentLoader)
m_documentLoader->stopLoading();
- setProvisionalDocumentLoader(0);
+ setProvisionalDocumentLoader(nullptr);
m_checkTimer.stop();
@@ -1662,6 +1661,9 @@ void FrameLoader::stopAllLoaders(ClearProvisionalItemPolicy clearProvisionalItem
void FrameLoader::stopForUserCancel(bool deferCheckLoadComplete)
{
+ // Calling stopAllLoaders can cause the frame to be deallocated, including the frame loader.
+ Ref<Frame> protectedFrame(m_frame);
+
stopAllLoaders();
#if PLATFORM(IOS)
@@ -1731,7 +1733,7 @@ void FrameLoader::setPolicyDocumentLoader(DocumentLoader* loader)
return;
if (loader)
- loader->setFrame(&m_frame);
+ loader->attachToFrame(m_frame);
if (m_policyDocumentLoader
&& m_policyDocumentLoader != m_provisionalDocumentLoader
&& m_policyDocumentLoader != m_documentLoader)
@@ -1752,7 +1754,8 @@ void FrameLoader::setProvisionalDocumentLoader(DocumentLoader* loader)
}
void FrameLoader::setState(FrameState newState)
-{
+{
+ FrameState oldState = m_state;
m_state = newState;
if (newState == FrameStateProvisional)
@@ -1761,12 +1764,14 @@ void FrameLoader::setState(FrameState newState)
frameLoadCompleted();
if (m_documentLoader)
m_documentLoader->stopRecordingResponses();
+ if (m_frame.isMainFrame() && oldState != newState)
+ static_cast<MainFrame&>(m_frame).didCompleteLoad();
}
}
void FrameLoader::clearProvisionalLoad()
{
- setProvisionalDocumentLoader(0);
+ setProvisionalDocumentLoader(nullptr);
m_progressTracker->progressCompleted();
setState(FrameStateComplete);
}
@@ -1777,42 +1782,24 @@ void FrameLoader::commitProvisionalLoad()
Ref<Frame> protect(m_frame);
std::unique_ptr<CachedPage> cachedPage;
- if (m_loadingFromCachedPage)
- cachedPage = pageCache()->take(history().provisionalItem());
+ if (m_loadingFromCachedPage && history().provisionalItem())
+ cachedPage = PageCache::singleton().take(*history().provisionalItem(), m_frame.page());
LOG(PageCache, "WebCoreLoading %s: About to commit provisional load from previous URL '%s' to new URL '%s'", m_frame.tree().uniqueName().string().utf8().data(),
m_frame.document() ? m_frame.document()->url().stringCenterEllipsizedToLength().utf8().data() : "",
pdl ? pdl->url().stringCenterEllipsizedToLength().utf8().data() : "<no provisional DocumentLoader>");
-#if PLATFORM(IOS)
- // In the case where we are not navigating to a cached page, and the system is under (speculative) memory pressure,
- // we can try to preemptively release some of the pages in the cache.
- // FIXME: Right now the capacity is 1 on iOS devices with 256 MB of RAM, so this will always blow away the whole
- // page cache. We could still preemptively prune the page cache while navigating to a cached page if capacity > 1.
- // See <rdar://problem/11779846> for more details.
- if (!cachedPage) {
- if (memoryPressureHandler().hasReceivedMemoryPressure()) {
- LOG(MemoryPressure, "Pruning page cache because under memory pressure at: %s", __PRETTY_FUNCTION__);
- LOG(PageCache, "Pruning page cache to 0 due to memory pressure");
- // Don't cache any page if we are under memory pressure.
- pageCache()->pruneToCapacityNow(0);
- } else if (systemMemoryLevel() <= memoryLevelThresholdToPrunePageCache) {
- LOG(MemoryPressure, "Pruning page cache because system memory level is %d at: %s", systemMemoryLevel(), __PRETTY_FUNCTION__);
- LOG(PageCache, "Pruning page cache to %d due to low memory (level %d less or equal to %d threshold)", pageCache()->capacity() / 2, systemMemoryLevel(), memoryLevelThresholdToPrunePageCache);
- pageCache()->pruneToCapacityNow(pageCache()->capacity() / 2);
- }
- }
-#endif
-
willTransitionToCommitted();
- // Check to see if we need to cache the page we are navigating away from into the back/forward cache.
- // We are doing this here because we know for sure that a new page is about to be loaded.
- HistoryItem* item = history().currentItem();
- if (!m_frame.tree().parent() && pageCache()->canCache(m_frame.page()) && !item->isInPageCache())
- pageCache()->add(item, *m_frame.page());
+ if (!m_frame.tree().parent() && history().currentItem()) {
+ // Check to see if we need to cache the page we are navigating away from into the back/forward cache.
+ // We are doing this here because we know for sure that a new page is about to be loaded.
+ PageCache::singleton().addIfCacheable(*history().currentItem(), m_frame.page());
+
+ WebCore::jettisonExpensiveObjectsOnTopLevelNavigation();
+ }
- if (m_loadType != FrameLoadTypeReplace)
+ if (m_loadType != FrameLoadType::Replace)
closeOldDataSources();
if (!cachedPage && !m_stateMachine.creatingInitialEmptyDocument())
@@ -1822,8 +1809,8 @@ void FrameLoader::commitProvisionalLoad()
if (pdl && m_documentLoader) {
// Check if the destination page is allowed to access the previous page's timing information.
- RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::create(pdl->request().url());
- m_documentLoader->timing()->setHasSameOriginAsPreviousDocument(securityOrigin->canRequest(m_previousURL));
+ Ref<SecurityOrigin> securityOrigin(SecurityOrigin::create(pdl->request().url()));
+ m_documentLoader->timing().setHasSameOriginAsPreviousDocument(securityOrigin.get().canRequest(m_previousURL));
}
// Call clientRedirectCancelledOrFinished() here so that the frame load delegate is notified that the redirect's
@@ -1839,21 +1826,44 @@ void FrameLoader::commitProvisionalLoad()
// commit to happen before any changes to viewport arguments and dealing with this there is difficult.
m_frame.page()->chrome().setDispatchViewportDataDidChangeSuppressed(true);
#endif
- prepareForCachedPageRestore();
+ willRestoreFromCachedPage();
+
+ // Start request for the main resource and dispatch didReceiveResponse before the load is committed for
+ // consistency with all other loads. See https://bugs.webkit.org/show_bug.cgi?id=150927.
+ ResourceError mainResouceError;
+ unsigned long mainResourceIdentifier;
+ ResourceRequest mainResourceRequest(cachedPage->documentLoader()->request());
+ requestFromDelegate(mainResourceRequest, mainResourceIdentifier, mainResouceError);
+ notifier().dispatchDidReceiveResponse(cachedPage->documentLoader(), mainResourceIdentifier, cachedPage->documentLoader()->response());
+
+ std::optional<HasInsecureContent> hasInsecureContent = cachedPage->cachedMainFrame()->hasInsecureContent();
+
+ {
+ // Do not dispatch DOM events as their JavaScript listeners could cause the page to be put
+ // into the page cache before we have finished restoring it from the page cache.
+ NoEventDispatchAssertion assertNoEventDispatch;
+
+ // FIXME: This API should be turned around so that we ground CachedPage into the Page.
+ cachedPage->restore(*m_frame.page());
+ }
+
+ dispatchDidCommitLoad(hasInsecureContent);
- // FIXME: This API should be turned around so that we ground CachedPage into the Page.
- cachedPage->restore(*m_frame.page());
+ didRestoreFromCachedPage();
- dispatchDidCommitLoad();
#if PLATFORM(IOS)
m_frame.page()->chrome().setDispatchViewportDataDidChangeSuppressed(false);
m_frame.page()->chrome().dispatchViewportPropertiesDidChange(m_frame.page()->viewportArguments());
#endif
- // If we have a title let the WebView know about it.
- StringWithDirection title = m_documentLoader->title();
- if (!title.isNull())
+
+ auto& title = m_documentLoader->title();
+ if (!title.string.isNull())
m_client.dispatchDidReceiveTitle(title);
+ // Send remaining notifications for the main resource.
+ notifier().sendRemainingDelegateMessages(m_documentLoader.get(), mainResourceIdentifier, mainResourceRequest, ResourceResponse(),
+ nullptr, static_cast<int>(m_documentLoader->response().expectedContentLength()), 0, mainResouceError);
+
checkCompleted();
} else
didOpenURL();
@@ -1861,28 +1871,26 @@ void FrameLoader::commitProvisionalLoad()
LOG(Loading, "WebCoreLoading %s: Finished committing provisional load to URL %s", m_frame.tree().uniqueName().string().utf8().data(),
m_frame.document() ? m_frame.document()->url().stringCenterEllipsizedToLength().utf8().data() : "");
- if (m_loadType == FrameLoadTypeStandard && m_documentLoader->isClientRedirect())
+ if (m_loadType == FrameLoadType::Standard && m_documentLoader->isClientRedirect())
history().updateForClientRedirect();
if (m_loadingFromCachedPage) {
-#if PLATFORM(IOS)
// Note, didReceiveDocType is expected to be called for cached pages. See <rdar://problem/5906758> for more details.
- if (m_frame.document()->doctype() && m_frame.page())
- m_frame.page()->chrome().didReceiveDocType(&m_frame);
-#endif
- m_frame.document()->documentDidResumeFromPageCache();
+ if (auto* page = m_frame.page())
+ page->chrome().didReceiveDocType(m_frame);
+ m_frame.document()->resume(ActiveDOMObject::PageCache);
// Force a layout to update view size and thereby update scrollbars.
#if PLATFORM(IOS)
- m_client.forceLayoutWithoutRecalculatingStyles();
+ if (!m_client.forceLayoutOnRestoreFromPageCache())
+ m_frame.view()->forceLayout();
#else
m_frame.view()->forceLayout();
#endif
- const ResponseVector& responses = m_documentLoader->responses();
- size_t count = responses.size();
- for (size_t i = 0; i < count; i++) {
- const ResourceResponse& response = responses[i];
+ // Main resource delegates were already sent, so we skip the first response here.
+ for (unsigned i = 1; i < m_documentLoader->responses().size(); ++i) {
+ const auto& response = m_documentLoader->responses()[i];
// FIXME: If the WebKit client changes or cancels the request, this is not respected.
ResourceError error;
unsigned long identifier;
@@ -1924,82 +1932,77 @@ void FrameLoader::transitionToCommitted(CachedPage* cachedPage)
if (pdl != m_provisionalDocumentLoader)
return;
- // Nothing else can interupt this commit - set the Provisional->Committed transition in stone
if (m_documentLoader)
m_documentLoader->stopLoadingSubresources();
if (m_documentLoader)
m_documentLoader->stopLoadingPlugIns();
+ // Setting our document loader invokes the unload event handler of our child frames.
+ // Script can do anything. If the script initiates a new load, we need to abandon the
+ // current load or the two will stomp each other.
setDocumentLoader(m_provisionalDocumentLoader.get());
- setProvisionalDocumentLoader(0);
-
- if (pdl != m_documentLoader) {
- ASSERT(m_state == FrameStateComplete);
+ if (pdl != m_provisionalDocumentLoader)
return;
- }
+ setProvisionalDocumentLoader(nullptr);
+ // Nothing else can interupt this commit - set the Provisional->Committed transition in stone
setState(FrameStateCommittedPage);
-#if ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS)
- if (m_frame.isMainFrame())
- m_frame.page()->chrome().client().needTouchEvents(false);
-#endif
-
// Handle adding the URL to the back/forward list.
DocumentLoader* dl = m_documentLoader.get();
switch (m_loadType) {
- case FrameLoadTypeForward:
- case FrameLoadTypeBack:
- case FrameLoadTypeIndexedBackForward:
- if (m_frame.page()) {
- // If the first load within a frame is a navigation within a back/forward list that was attached
- // without any of the items being loaded then we need to update the history in a similar manner as
- // for a standard load with the exception of updating the back/forward list (<rdar://problem/8091103>).
- if (!m_stateMachine.committedFirstRealDocumentLoad() && m_frame.isMainFrame())
- history().updateForStandardLoad(HistoryController::UpdateAllExceptBackForwardList);
-
- history().updateForBackForwardNavigation();
-
- // For cached pages, CachedFrame::restore will take care of firing the popstate event with the history item's state object
- if (history().currentItem() && !cachedPage)
- m_pendingStateObject = history().currentItem()->stateObject();
-
- // Create a document view for this document, or used the cached view.
- if (cachedPage) {
- DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader();
- ASSERT(cachedDocumentLoader);
- cachedDocumentLoader->setFrame(&m_frame);
- m_client.transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame());
- } else
- m_client.transitionToCommittedForNewPage();
- }
- break;
-
- case FrameLoadTypeReload:
- case FrameLoadTypeReloadFromOrigin:
- case FrameLoadTypeSame:
- case FrameLoadTypeReplace:
- history().updateForReload();
- m_client.transitionToCommittedForNewPage();
- break;
-
- case FrameLoadTypeStandard:
- history().updateForStandardLoad();
- if (m_frame.view())
- m_frame.view()->setScrollbarsSuppressed(true);
- m_client.transitionToCommittedForNewPage();
- break;
-
- case FrameLoadTypeRedirectWithLockedBackForwardList:
- history().updateForRedirectWithLockedBackForwardList();
- m_client.transitionToCommittedForNewPage();
- break;
-
- // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is).
- // An exception should be thrown if we're in the FrameLoadTypeUninitialized state.
- default:
- ASSERT_NOT_REACHED();
+ case FrameLoadType::Forward:
+ case FrameLoadType::Back:
+ case FrameLoadType::IndexedBackForward:
+ if (m_frame.page()) {
+ // If the first load within a frame is a navigation within a back/forward list that was attached
+ // without any of the items being loaded then we need to update the history in a similar manner as
+ // for a standard load with the exception of updating the back/forward list (<rdar://problem/8091103>).
+ if (!m_stateMachine.committedFirstRealDocumentLoad() && m_frame.isMainFrame())
+ history().updateForStandardLoad(HistoryController::UpdateAllExceptBackForwardList);
+
+ history().updateForBackForwardNavigation();
+
+ // For cached pages, CachedFrame::restore will take care of firing the popstate event with the history item's state object
+ if (history().currentItem() && !cachedPage)
+ m_pendingStateObject = history().currentItem()->stateObject();
+
+ // Create a document view for this document, or used the cached view.
+ if (cachedPage) {
+ DocumentLoader* cachedDocumentLoader = cachedPage->documentLoader();
+ ASSERT(cachedDocumentLoader);
+ cachedDocumentLoader->attachToFrame(m_frame);
+ m_client.transitionToCommittedFromCachedFrame(cachedPage->cachedMainFrame());
+ } else
+ m_client.transitionToCommittedForNewPage();
+ }
+ break;
+
+ case FrameLoadType::Reload:
+ case FrameLoadType::ReloadFromOrigin:
+ case FrameLoadType::Same:
+ case FrameLoadType::Replace:
+ history().updateForReload();
+ m_client.transitionToCommittedForNewPage();
+ break;
+
+ case FrameLoadType::Standard:
+ history().updateForStandardLoad();
+ if (m_frame.view())
+ m_frame.view()->setScrollbarsSuppressed(true);
+ m_client.transitionToCommittedForNewPage();
+ break;
+
+ case FrameLoadType::RedirectWithLockedBackForwardList:
+ history().updateForRedirectWithLockedBackForwardList();
+ m_client.transitionToCommittedForNewPage();
+ break;
+
+ // FIXME Remove this check when dummy ds is removed (whatever "dummy ds" is).
+ // An exception should be thrown if we're in the FrameLoadTypeUninitialized state.
+ default:
+ ASSERT_NOT_REACHED();
}
m_documentLoader->writer().setMIMEType(dl->responseMIMEType());
@@ -2027,7 +2030,7 @@ void FrameLoader::clientRedirectCancelledOrFinished(bool cancelWithLoadInProgres
m_sentRedirectNotification = false;
}
-void FrameLoader::clientRedirected(const URL& url, double seconds, double fireDate, bool lockBackForwardList)
+void FrameLoader::clientRedirected(const URL& url, double seconds, double fireDate, LockBackForwardList lockBackForwardList)
{
m_client.dispatchWillPerformClientRedirect(url, seconds, fireDate);
@@ -2039,7 +2042,7 @@ void FrameLoader::clientRedirected(const URL& url, double seconds, double fireDa
// load as part of the original navigation. If we don't have a document loader, we have
// no "original" load on which to base a redirect, so we treat the redirect as a normal load.
// Loads triggered by JavaScript form submissions never count as quick redirects.
- m_quickRedirectComing = (lockBackForwardList || history().currentItemShouldBeReplaced()) && m_documentLoader && !m_isExecutingJavaScriptFormAction;
+ m_quickRedirectComing = (lockBackForwardList == LockBackForwardList::Yes || history().currentItemShouldBeReplaced()) && m_documentLoader && !m_isExecutingJavaScriptFormAction;
}
bool FrameLoader::shouldReload(const URL& currentURL, const URL& destinationURL)
@@ -2066,7 +2069,7 @@ void FrameLoader::closeOldDataSources()
m_client.setMainFrameDocumentReady(false); // stop giving out the actual DOMDocument to observers
}
-void FrameLoader::prepareForCachedPageRestore()
+void FrameLoader::willRestoreFromCachedPage()
{
ASSERT(!m_frame.tree().parent());
ASSERT(m_frame.page());
@@ -2085,6 +2088,31 @@ void FrameLoader::prepareForCachedPageRestore()
}
}
+void FrameLoader::didRestoreFromCachedPage()
+{
+ // Dispatching JavaScript events can cause frame destruction.
+ auto& mainFrame = m_frame.page()->mainFrame();
+ Vector<Ref<Frame>> childFrames;
+ for (auto* child = mainFrame.tree().traverseNextInPostOrderWithWrap(true); child; child = child->tree().traverseNextInPostOrderWithWrap(false))
+ childFrames.append(*child);
+
+ for (auto& child : childFrames) {
+ if (!child->tree().isDescendantOf(&mainFrame))
+ continue;
+ auto* document = child->document();
+ if (!document)
+ continue;
+
+ // FIXME: Update Page Visibility state here.
+ // https://bugs.webkit.org/show_bug.cgi?id=116770
+ document->dispatchPageshowEvent(PageshowEventPersisted);
+
+ auto* historyItem = child->loader().history().currentItem();
+ if (historyItem && historyItem->stateObject())
+ document->dispatchPopstateEvent(historyItem->stateObject());
+ }
+}
+
void FrameLoader::open(CachedFrameBase& cachedFrame)
{
m_isComplete = false;
@@ -2105,7 +2133,7 @@ void FrameLoader::open(CachedFrameBase& cachedFrame)
clear(document, true, true, cachedFrame.isMainFrame());
- document->setInPageCache(false);
+ document->setPageCacheState(Document::NotInPageCache);
m_needsClear = true;
m_isComplete = false;
@@ -2118,13 +2146,15 @@ void FrameLoader::open(CachedFrameBase& cachedFrame)
ASSERT(view);
view->setWasScrolledByUser(false);
- // Use the current ScrollView's frame rect.
- if (m_frame.view())
- view->setFrameRect(m_frame.view()->frameRect());
+ std::optional<IntRect> previousViewFrameRect = m_frame.view() ? m_frame.view()->frameRect() : std::optional<IntRect>(std::nullopt);
m_frame.setView(view);
+
+ // Use the previous ScrollView's frame rect.
+ if (previousViewFrameRect)
+ view->setFrameRect(previousViewFrameRect.value());
m_frame.setDocument(document);
- document->domWindow()->resumeFromPageCache();
+ document->domWindow()->resumeFromDocumentSuspension();
updateFirstPartyForCookies();
@@ -2139,12 +2169,12 @@ bool FrameLoader::isHostedByObjectElement() const
bool FrameLoader::isReplacing() const
{
- return m_loadType == FrameLoadTypeReplace;
+ return m_loadType == FrameLoadType::Replace;
}
void FrameLoader::setReplacing()
{
- m_loadType = FrameLoadTypeReplace;
+ m_loadType = FrameLoadType::Replace;
}
bool FrameLoader::subframeIsLoading() const
@@ -2177,10 +2207,15 @@ FrameLoadType FrameLoader::loadType() const
CachePolicy FrameLoader::subresourceCachePolicy() const
{
+ if (Page* page = m_frame.page()) {
+ if (page->isResourceCachingDisabled())
+ return CachePolicyReload;
+ }
+
if (m_isComplete)
return CachePolicyVerify;
- if (m_loadType == FrameLoadTypeReloadFromOrigin)
+ if (m_loadType == FrameLoadType::ReloadFromOrigin)
return CachePolicyReload;
if (Frame* parentFrame = m_frame.tree().parent()) {
@@ -2189,18 +2224,24 @@ CachePolicy FrameLoader::subresourceCachePolicy() const
return parentCachePolicy;
}
- if (m_loadType == FrameLoadTypeReload)
- return CachePolicyRevalidate;
-
- const ResourceRequest& request(documentLoader()->request());
-#if PLATFORM(MAC)
- if (request.cachePolicy() == ReloadIgnoringCacheData && !equalIgnoringCase(request.httpMethod(), "post") && ResourceRequest::useQuickLookResourceCachingQuirks())
+ switch (m_loadType) {
+ case FrameLoadType::Reload:
return CachePolicyRevalidate;
-#endif
-
- if (request.cachePolicy() == ReturnCacheDataElseLoad)
+ case FrameLoadType::Back:
+ case FrameLoadType::Forward:
+ case FrameLoadType::IndexedBackForward:
return CachePolicyHistoryBuffer;
+ case FrameLoadType::ReloadFromOrigin:
+ ASSERT_NOT_REACHED(); // Already handled above.
+ return CachePolicyReload;
+ case FrameLoadType::RedirectWithLockedBackForwardList:
+ case FrameLoadType::Replace:
+ case FrameLoadType::Same:
+ case FrameLoadType::Standard:
+ return CachePolicyVerify;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
return CachePolicyVerify;
}
@@ -2210,7 +2251,11 @@ void FrameLoader::checkLoadCompleteForThisFrame()
switch (m_state) {
case FrameStateProvisional: {
- if (m_delegateIsHandlingProvisionalLoadError)
+ // FIXME: Prohibiting any provisional load failures from being sent to clients
+ // while handling provisional load failures is too heavy. For example, the current
+ // load will fail to cancel another ongoing load. That might prevent clients' page
+ // load state being handled properly.
+ if (!m_provisionalLoadErrorBeingHandledURL.isEmpty())
return;
RefPtr<DocumentLoader> pdl = m_provisionalDocumentLoader;
@@ -2232,9 +2277,13 @@ void FrameLoader::checkLoadCompleteForThisFrame()
// Only reset if we aren't already going to a new provisional item.
bool shouldReset = !history().provisionalItem();
if (!pdl->isLoadingInAPISense() || pdl->isStopping()) {
- m_delegateIsHandlingProvisionalLoadError = true;
+ m_provisionalLoadErrorBeingHandledURL = m_provisionalDocumentLoader->url();
m_client.dispatchDidFailProvisionalLoad(error);
- m_delegateIsHandlingProvisionalLoadError = false;
+#if ENABLE(CONTENT_FILTERING)
+ if (auto contentFilter = pdl->contentFilter())
+ contentFilter->handleProvisionalLoadFailure(error);
+#endif
+ m_provisionalLoadErrorBeingHandledURL = { };
ASSERT(!pdl->isLoading());
@@ -2275,7 +2324,7 @@ void FrameLoader::checkLoadCompleteForThisFrame()
// If the user had a scroll point, scroll to it, overriding the anchor point if any.
if (m_frame.page()) {
- if (isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadTypeReload || m_loadType == FrameLoadTypeReloadFromOrigin)
+ if (isBackForwardLoadType(m_loadType) || m_loadType == FrameLoadType::Reload || m_loadType == FrameLoadType::ReloadFromOrigin)
history().restoreScrollPositionAndViewState();
}
@@ -2283,31 +2332,55 @@ void FrameLoader::checkLoadCompleteForThisFrame()
return;
m_progressTracker->progressCompleted();
- if (Page* page = m_frame.page()) {
+ Page* page = m_frame.page();
+ if (page) {
if (m_frame.isMainFrame())
- page->resetRelevantPaintedObjectCounter();
+ page->didFinishLoad();
}
const ResourceError& error = dl->mainDocumentError();
AXObjectCache::AXLoadingEvent loadingEvent;
if (!error.isNull()) {
+ 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());
m_client.dispatchDidFailLoad(error);
loadingEvent = AXObjectCache::AXLoadingFailed;
} else {
+ RELEASE_LOG_IF_ALLOWED("checkLoadCompleteForThisFrame: Finished frame load (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
+#if ENABLE(DATA_DETECTION)
+ auto* document = m_frame.document();
+ if (m_frame.settings().dataDetectorTypes() != DataDetectorTypeNone && document) {
+ if (auto* documentElement = document->documentElement()) {
+ RefPtr<Range> documentRange = makeRange(firstPositionInNode(documentElement), lastPositionInNode(documentElement));
+ m_frame.setDataDetectionResults(DataDetection::detectContentInRange(documentRange, m_frame.settings().dataDetectorTypes(), m_client.dataDetectionContext()));
+ if (m_frame.isMainFrame())
+ m_client.dispatchDidFinishDataDetection(m_frame.dataDetectionResults());
+ }
+ }
+#endif
m_client.dispatchDidFinishLoad();
loadingEvent = AXObjectCache::AXLoadingFinished;
}
// Notify accessibility.
- if (AXObjectCache* cache = m_frame.document()->existingAXObjectCache())
- cache->frameLoadingEventNotification(&m_frame, loadingEvent);
+ if (auto* document = m_frame.document()) {
+ if (AXObjectCache* cache = document->existingAXObjectCache())
+ cache->frameLoadingEventNotification(&m_frame, loadingEvent);
+ }
+
+ // The above calls to dispatchDidFinishLoad() might have detached the Frame
+ // from its Page and also might have caused Page to be deleted.
+ // Don't assume 'page' is still available to use.
+ if (m_frame.isMainFrame() && m_frame.page()) {
+ ASSERT(&m_frame.page()->mainFrame() == &m_frame);
+ m_frame.page()->diagnosticLoggingClient().logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageLoadedKey(), emptyString(), error.isNull() ? DiagnosticLoggingResultPass : DiagnosticLoggingResultFail, ShouldSample::Yes);
+ }
return;
}
case FrameStateComplete:
- m_loadType = FrameLoadTypeStandard;
+ m_loadType = FrameLoadType::Standard;
frameLoadCompleted();
return;
}
@@ -2324,76 +2397,46 @@ void FrameLoader::continueLoadAfterWillSubmitForm()
// The load might be cancelled inside of prepareForLoadStart(), nulling out the m_provisionalDocumentLoader,
// so we need to null check it again.
- if (!m_provisionalDocumentLoader)
+ if (!m_provisionalDocumentLoader) {
+ RELEASE_LOG_IF_ALLOWED("continueLoadAfterWillSubmitForm: Frame load canceled (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
return;
+ }
DocumentLoader* activeDocLoader = activeDocumentLoader();
- if (activeDocLoader && activeDocLoader->isLoadingMainResource())
+ if (activeDocLoader && activeDocLoader->isLoadingMainResource()) {
+ RELEASE_LOG_IF_ALLOWED("continueLoadAfterWillSubmitForm: Main frame already being loaded (frame = %p, main = %d)", &m_frame, m_frame.isMainFrame());
return;
+ }
m_loadingFromCachedPage = false;
m_provisionalDocumentLoader->startLoadingMainResource();
}
-static URL originatingURLFromBackForwardList(Page* page)
-{
- // FIXME: Can this logic be replaced with m_frame.document()->firstPartyForCookies()?
- // It has the same meaning of "page a user thinks is the current one".
-
- URL originalURL;
- int backCount = page->backForward().backCount();
- for (int backIndex = 0; backIndex <= backCount; backIndex++) {
- // FIXME: At one point we had code here to check a "was user gesture" flag.
- // Do we need to restore that logic?
- HistoryItem* historyItem = page->backForward().itemAtIndex(-backIndex);
- if (!historyItem)
- continue;
-
- originalURL = historyItem->originalURL();
- if (!originalURL.isNull())
- return originalURL;
- }
-
- return URL();
-}
-
void FrameLoader::setOriginalURLForDownloadRequest(ResourceRequest& request)
{
+ // FIXME: Rename firstPartyForCookies back to mainDocumentURL. It was a mistake to think that it was only used for cookies.
+ // The originalURL is defined as the URL of the page where the download was initiated.
URL originalURL;
-
- // If there is no referrer, assume that the download was initiated directly, so current document is
- // completely unrelated to it. See <rdar://problem/5294691>.
- // FIXME: Referrer is not sent in many other cases, so we will often miss this important information.
- // Find a better way to decide whether the download was unrelated to current document.
- if (!request.httpReferrer().isNull()) {
- // find the first item in the history that was originated by the user
- originalURL = originatingURLFromBackForwardList(m_frame.page());
- }
-
- if (originalURL.isNull())
- originalURL = request.url();
-
- if (!originalURL.protocol().isEmpty() && !originalURL.host().isEmpty()) {
- unsigned port = originalURL.port();
-
- // Original URL is needed to show the user where a file was downloaded from. We should make a URL that won't result in downloading the file again.
- // FIXME: Using host-only URL is a very heavy-handed approach. We should attempt to provide the actual page where the download was initiated from, as a reminder to the user.
- String hostOnlyURLString;
- if (port)
- hostOnlyURLString = makeString(originalURL.protocol(), "://", originalURL.host(), ":", String::number(port));
- else
- hostOnlyURLString = makeString(originalURL.protocol(), "://", originalURL.host());
-
- // FIXME: Rename firstPartyForCookies back to mainDocumentURL. It was a mistake to think that it was only used for cookies.
- request.setFirstPartyForCookies(URL(URL(), hostOnlyURLString));
- }
+ if (m_frame.document()) {
+ originalURL = m_frame.document()->firstPartyForCookies();
+ // If there is no main document URL, it means that this document is newly opened and just for download purpose.
+ // In this case, we need to set the originalURL to this document's opener's main document URL.
+ if (originalURL.isEmpty() && opener() && opener()->document())
+ originalURL = opener()->document()->firstPartyForCookies();
+ }
+ // If the originalURL is the same as the requested URL, we are processing a download
+ // initiated directly without a page and do not need to specify the originalURL.
+ if (originalURL == request.url())
+ request.setFirstPartyForCookies(URL());
+ else
+ request.setFirstPartyForCookies(originalURL);
}
-void FrameLoader::didLayout(LayoutMilestones milestones)
+void FrameLoader::didReachLayoutMilestone(LayoutMilestones milestones)
{
ASSERT(m_frame.isMainFrame());
- m_client.dispatchDidLayout(milestones);
+ m_client.dispatchDidReachLayoutMilestone(milestones);
}
void FrameLoader::didFirstLayout()
@@ -2428,23 +2471,34 @@ void FrameLoader::frameLoadCompleted()
void FrameLoader::detachChildren()
{
+ // detachChildren() will fire the unload event in each subframe and the
+ // HTML specification states that the parent document's ignore-opens-during-unload counter while
+ // this event is being fired in its subframes:
+ // https://html.spec.whatwg.org/multipage/browsers.html#unload-a-document
+ IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(m_frame.document());
+
+ // Any subframe inserted by unload event handlers executed in the loop below will not get unloaded
+ // because we create a copy of the subframes list before looping. Therefore, it would be unsafe to
+ // allow loading of subframes at this point.
+ SubframeLoadingDisabler subframeLoadingDisabler(m_frame.document());
+
Vector<Ref<Frame>, 16> childrenToDetach;
childrenToDetach.reserveInitialCapacity(m_frame.tree().childCount());
for (Frame* child = m_frame.tree().lastChild(); child; child = child->tree().previousSibling())
childrenToDetach.uncheckedAppend(*child);
- for (unsigned i = 0; i < childrenToDetach.size(); ++i)
- childrenToDetach[i]->loader().detachFromParent();
+ for (auto& child : childrenToDetach)
+ child->loader().detachFromParent();
}
-void FrameLoader::closeAndRemoveChild(Frame* child)
+void FrameLoader::closeAndRemoveChild(Frame& child)
{
- child->tree().detachFromParent();
+ child.tree().detachFromParent();
- child->setView(0);
- if (child->ownerElement() && child->page())
- child->page()->decrementSubframeCount();
- child->willDetachPage();
- child->detachFromPage();
+ child.setView(nullptr);
+ if (child.ownerElement() && child.page())
+ child.page()->decrementSubframeCount();
+ child.willDetachPage();
+ child.detachFromPage();
m_frame.tree().removeChild(child);
}
@@ -2466,18 +2520,20 @@ void FrameLoader::checkLoadComplete()
frames.append(*frame);
// To process children before their parents, iterate the vector backwards.
- for (unsigned i = frames.size(); i; --i)
- frames[i - 1]->loader().checkLoadCompleteForThisFrame();
+ for (auto frame = frames.rbegin(); frame != frames.rend(); ++frame) {
+ if ((*frame)->page())
+ (*frame)->loader().checkLoadCompleteForThisFrame();
+ }
}
int FrameLoader::numPendingOrLoadingRequests(bool recurse) const
{
if (!recurse)
- return m_frame.document()->cachedResourceLoader()->requestCount();
+ return m_frame.document()->cachedResourceLoader().requestCount();
int count = 0;
for (Frame* frame = &m_frame; frame; frame = frame->tree().traverseNext(&m_frame))
- count += frame->document()->cachedResourceLoader()->requestCount();
+ count += frame->document()->cachedResourceLoader().requestCount();
return count;
}
@@ -2486,44 +2542,53 @@ String FrameLoader::userAgent(const URL& url) const
return m_client.userAgent(url);
}
-void FrameLoader::handledOnloadEvents()
+void FrameLoader::dispatchOnloadEvents()
{
- m_client.dispatchDidHandleOnloadEvents();
+ m_client.dispatchDidDispatchOnloadEvents();
if (documentLoader())
- documentLoader()->handledOnloadEvents();
+ documentLoader()->dispatchOnloadEvents();
}
void FrameLoader::frameDetached()
{
- stopAllLoaders();
- m_frame.document()->stopActiveDOMObjects();
+ // Calling stopAllLoaders() can cause the frame to be deallocated, including the frame loader.
+ Ref<Frame> protectedFrame(m_frame);
+
+ if (m_frame.document()->pageCacheState() != Document::InPageCache) {
+ stopAllLoaders();
+ m_frame.document()->stopActiveDOMObjects();
+ }
+
detachFromParent();
}
void FrameLoader::detachFromParent()
{
+ // Calling stopAllLoaders() can cause the frame to be deallocated, including the frame loader.
Ref<Frame> protect(m_frame);
closeURL();
history().saveScrollPositionAndViewStateToItem(history().currentItem());
detachChildren();
- // stopAllLoaders() needs to be called after detachChildren(), because detachedChildren()
- // will trigger the unload event handlers of any child frames, and those event
- // handlers might start a new subresource load in this frame.
- stopAllLoaders();
+ if (m_frame.document()->pageCacheState() != Document::InPageCache) {
+ // stopAllLoaders() needs to be called after detachChildren() if the document is not in the page cache,
+ // because detachedChildren() will trigger the unload event handlers of any child frames, and those event
+ // handlers might start a new subresource load in this frame.
+ stopAllLoaders();
+ }
- InspectorInstrumentation::frameDetachedFromParent(&m_frame);
+ InspectorInstrumentation::frameDetachedFromParent(m_frame);
detachViewsAndDocumentLoader();
m_progressTracker = nullptr;
if (Frame* parent = m_frame.tree().parent()) {
- parent->loader().closeAndRemoveChild(&m_frame);
+ parent->loader().closeAndRemoveChild(m_frame);
parent->loader().scheduleCheckCompleted();
} else {
- m_frame.setView(0);
+ m_frame.setView(nullptr);
m_frame.willDetachPage();
m_frame.detachFromPage();
}
@@ -2532,7 +2597,7 @@ void FrameLoader::detachFromParent()
void FrameLoader::detachViewsAndDocumentLoader()
{
m_client.detachedFromParent2();
- setDocumentLoader(0);
+ setDocumentLoader(nullptr);
m_client.detachedFromParent3();
}
@@ -2546,29 +2611,19 @@ void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request)
// FIXME: Using m_loadType seems wrong for some callers.
// If we are only preparing to load the main resource, that is previous load's load type!
addExtraFieldsToRequest(request, m_loadType, true);
+
+ // Upgrade-Insecure-Requests should only be added to main resource requests
+ addHTTPUpgradeInsecureRequestsIfNeeded(request);
}
-void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool mainResource)
+ResourceRequestCachePolicy FrameLoader::defaultRequestCachingPolicy(const ResourceRequest& request, FrameLoadType loadType, bool isMainResource)
{
- // Don't set the cookie policy URL if it's already been set.
- // But make sure to set it on all requests regardless of protocol, as it has significance beyond the cookie policy (<rdar://problem/6616664>).
- if (request.firstPartyForCookies().isEmpty()) {
- if (mainResource && m_frame.isMainFrame())
- request.setFirstPartyForCookies(request.url());
- else if (Document* document = m_frame.document())
- request.setFirstPartyForCookies(document->firstPartyForCookies());
- }
-
- // The remaining modifications are only necessary for HTTP and HTTPS.
- if (!request.url().isEmpty() && !request.url().protocolIsInHTTPFamily())
- return;
-
- applyUserAgent(request);
-
- if (!mainResource) {
+ if (m_overrideCachePolicyForTesting)
+ return m_overrideCachePolicyForTesting.value();
+ if (!isMainResource) {
if (request.isConditional())
- request.setCachePolicy(ReloadIgnoringCacheData);
- else if (documentLoader()->isLoadingInAPISense()) {
+ return ReloadIgnoringCacheData;
+ if (documentLoader()->isLoadingInAPISense()) {
// If we inherit cache policy from a main resource, we use the DocumentLoader's
// original request cache policy for two reasons:
// 1. For POST requests, we mutate the cache policy for the main resource,
@@ -2579,26 +2634,55 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadTyp
ResourceRequestCachePolicy mainDocumentOriginalCachePolicy = documentLoader()->originalRequest().cachePolicy();
// 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.
// This policy is set on initial request too, but should not be inherited.
- ResourceRequestCachePolicy subresourceCachePolicy = (mainDocumentOriginalCachePolicy == ReturnCacheDataDontLoad) ? ReturnCacheDataElseLoad : mainDocumentOriginalCachePolicy;
- request.setCachePolicy(subresourceCachePolicy);
- } else
- request.setCachePolicy(UseProtocolCachePolicy);
-
+ return (mainDocumentOriginalCachePolicy == ReturnCacheDataDontLoad) ? ReturnCacheDataElseLoad : mainDocumentOriginalCachePolicy;
+ }
// FIXME: Other FrameLoader functions have duplicated code for setting cache policy of main request when reloading.
// It seems better to manage it explicitly than to hide the logic inside addExtraFieldsToRequest().
- } else if (loadType == FrameLoadTypeReload || loadType == FrameLoadTypeReloadFromOrigin || request.isConditional())
+ } else if (loadType == FrameLoadType::Reload || loadType == FrameLoadType::ReloadFromOrigin || request.isConditional())
+ return ReloadIgnoringCacheData;
+
+ return UseProtocolCachePolicy;
+}
+
+void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool isMainResource)
+{
+ // Don't set the cookie policy URL if it's already been set.
+ // But make sure to set it on all requests regardless of protocol, as it has significance beyond the cookie policy (<rdar://problem/6616664>).
+ if (request.firstPartyForCookies().isEmpty()) {
+ if (isMainResource && m_frame.isMainFrame())
+ request.setFirstPartyForCookies(request.url());
+ else if (Document* document = m_frame.document())
+ request.setFirstPartyForCookies(document->firstPartyForCookies());
+ }
+
+ Page* page = frame().page();
+ bool hasSpecificCachePolicy = request.cachePolicy() != UseProtocolCachePolicy;
+
+ if (page && page->isResourceCachingDisabled()) {
request.setCachePolicy(ReloadIgnoringCacheData);
+ loadType = FrameLoadType::ReloadFromOrigin;
+ } else if (!hasSpecificCachePolicy)
+ request.setCachePolicy(defaultRequestCachingPolicy(request, loadType, isMainResource));
+
+ // The remaining modifications are only necessary for HTTP and HTTPS.
+ if (!request.url().isEmpty() && !request.url().protocolIsInHTTPFamily())
+ return;
- if (request.cachePolicy() == ReloadIgnoringCacheData) {
- if (loadType == FrameLoadTypeReload)
- request.setHTTPHeaderField("Cache-Control", "max-age=0");
- else if (loadType == FrameLoadTypeReloadFromOrigin) {
- request.setHTTPHeaderField("Cache-Control", "no-cache");
- request.setHTTPHeaderField("Pragma", "no-cache");
+ if (!hasSpecificCachePolicy && request.cachePolicy() == ReloadIgnoringCacheData) {
+ if (loadType == FrameLoadType::Reload)
+ request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0");
+ else if (loadType == FrameLoadType::ReloadFromOrigin) {
+ request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "no-cache");
+ request.setHTTPHeaderField(HTTPHeaderName::Pragma, "no-cache");
}
}
-
- if (mainResource)
+
+ if (m_overrideResourceLoadPriorityForTesting)
+ request.setPriority(m_overrideResourceLoadPriorityForTesting.value());
+
+ applyUserAgent(request);
+
+ if (isMainResource)
request.setHTTPAccept(defaultAcceptHeader);
// Make sure we send the Origin header.
@@ -2638,16 +2722,25 @@ void FrameLoader::addHTTPOriginIfNeeded(ResourceRequest& request, const String&
request.setHTTPOrigin(origin);
}
-void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String& referrer, const String& frameName, bool lockHistory, FrameLoadType loadType, PassRefPtr<Event> event, PassRefPtr<FormState> prpFormState)
+void FrameLoader::addHTTPUpgradeInsecureRequestsIfNeeded(ResourceRequest& request)
+{
+ if (request.url().protocolIs("https")) {
+ // FIXME: Identify HSTS cases and avoid adding the header. <https://bugs.webkit.org/show_bug.cgi?id=157885>
+ return;
+ }
+
+ request.setHTTPHeaderField(HTTPHeaderName::UpgradeInsecureRequests, ASCIILiteral("1"));
+}
+
+void FrameLoader::loadPostRequest(const FrameLoadRequest& request, const String& referrer, FrameLoadType loadType, Event* event, FormState* formState)
{
- RefPtr<FormState> formState = prpFormState;
+ String frameName = request.frameName();
+ LockHistory lockHistory = request.lockHistory();
+ AllowNavigationToInvalidURL allowNavigationToInvalidURL = request.allowNavigationToInvalidURL();
+ NewFrameOpenerPolicy openerPolicy = request.newFrameOpenerPolicy();
- // Previously when this method was reached, the original FrameLoadRequest had been deconstructed to build a
- // bunch of parameters that would come in here and then be built back up to a ResourceRequest. In case
- // any caller depends on the immutability of the original ResourceRequest, I'm rebuilding a ResourceRequest
- // from scratch as it did all along.
+ const ResourceRequest& inRequest = request.resourceRequest();
const URL& url = inRequest.url();
- RefPtr<FormData> formData = inRequest.httpBody();
const String& contentType = inRequest.httpContentType();
String origin = inRequest.httpOrigin();
@@ -2657,28 +2750,31 @@ void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String
workingResourceRequest.setHTTPReferrer(referrer);
workingResourceRequest.setHTTPOrigin(origin);
workingResourceRequest.setHTTPMethod("POST");
- workingResourceRequest.setHTTPBody(formData);
+ workingResourceRequest.setHTTPBody(inRequest.httpBody());
workingResourceRequest.setHTTPContentType(contentType);
addExtraFieldsToRequest(workingResourceRequest, loadType, true);
- NavigationAction action(workingResourceRequest, loadType, true, event);
+ if (Document* document = m_frame.document())
+ document->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(workingResourceRequest, ContentSecurityPolicy::InsecureRequestType::Load);
+
+ NavigationAction action(workingResourceRequest, loadType, true, event, request.shouldOpenExternalURLsPolicy(), request.downloadAttribute());
if (!frameName.isEmpty()) {
// The search for a target frame is done earlier in the case of form submission.
if (Frame* targetFrame = formState ? 0 : findFrameForNavigation(frameName)) {
- targetFrame->loader().loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release());
+ targetFrame->loader().loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, WTFMove(formState), allowNavigationToInvalidURL);
return;
}
- policyChecker().checkNewWindowPolicy(action, workingResourceRequest, formState.release(), frameName, [this](const ResourceRequest& request, PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue) {
- continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue);
+ policyChecker().checkNewWindowPolicy(action, workingResourceRequest, WTFMove(formState), frameName, [this, allowNavigationToInvalidURL, openerPolicy] (const ResourceRequest& request, FormState* formState, const String& frameName, const NavigationAction& action, bool shouldContinue) {
+ continueLoadAfterNewWindowPolicy(request, formState, frameName, action, shouldContinue, allowNavigationToInvalidURL, openerPolicy);
});
return;
}
// must grab this now, since this load may stop the previous load and clear this flag
bool isRedirect = m_quickRedirectComing;
- loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, formState.release());
+ loadWithNavigationAction(workingResourceRequest, action, lockHistory, loadType, WTFMove(formState), allowNavigationToInvalidURL);
if (isRedirect) {
m_quickRedirectComing = false;
if (m_provisionalDocumentLoader)
@@ -2686,7 +2782,7 @@ void FrameLoader::loadPostRequest(const ResourceRequest& inRequest, const String
}
}
-unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ClientCredentialPolicy clientCredentialPolicy, ResourceError& error, ResourceResponse& response, Vector<char>& data)
+unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& request, StoredCredentials storedCredentials, ClientCredentialPolicy clientCredentialPolicy, ResourceError& error, ResourceResponse& response, RefPtr<SharedBuffer>& data)
{
ASSERT(m_frame.document());
String referrer = SecurityPolicy::generateReferrerHeader(m_frame.document()->referrerPolicy(), request.url(), outgoingReferrer());
@@ -2706,15 +2802,37 @@ unsigned long FrameLoader::loadResourceSynchronously(const ResourceRequest& requ
ResourceRequest newRequest(initialRequest);
requestFromDelegate(newRequest, identifier, error);
+#if ENABLE(CONTENT_EXTENSIONS)
+ if (error.isNull()) {
+ if (auto* page = m_frame.page()) {
+ if (m_documentLoader) {
+ auto blockedStatus = page->userContentProvider().processContentExtensionRulesForLoad(newRequest.url(), ResourceType::Raw, *m_documentLoader);
+ applyBlockedStatusToRequest(blockedStatus, newRequest);
+ if (blockedStatus.blockedLoad) {
+ newRequest = { };
+ error = ResourceError(errorDomainWebKitInternal, 0, initialRequest.url(), emptyString());
+ response = { };
+ data = nullptr;
+ }
+ }
+ }
+ }
+#endif
+
+ m_frame.document()->contentSecurityPolicy()->upgradeInsecureRequestIfNeeded(newRequest, ContentSecurityPolicy::InsecureRequestType::Load);
+
if (error.isNull()) {
ASSERT(!newRequest.isNull());
-
- if (!documentLoader()->applicationCacheHost()->maybeLoadSynchronously(newRequest, error, response, data)) {
- platformStrategies()->loaderStrategy()->loadResourceSynchronously(networkingContext(), identifier, newRequest, storedCredentials, clientCredentialPolicy, error, response, data);
- documentLoader()->applicationCacheHost()->maybeLoadFallbackSynchronously(newRequest, error, response, data);
+
+ if (!documentLoader()->applicationCacheHost().maybeLoadSynchronously(newRequest, error, response, data)) {
+ Vector<char> buffer;
+ platformStrategies()->loaderStrategy()->loadResourceSynchronously(networkingContext(), identifier, newRequest, storedCredentials, clientCredentialPolicy, error, response, buffer);
+ data = SharedBuffer::adoptVector(buffer);
+ documentLoader()->applicationCacheHost().maybeLoadFallbackSynchronously(newRequest, error, response, data);
+ ResourceLoadObserver::sharedObserver().logSubresourceLoading(&m_frame, newRequest, response);
}
}
- notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, response, data.data(), data.size(), -1, error);
+ notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, request, response, data ? data->data() : nullptr, data ? data->size() : 0, -1, error);
return identifier;
}
@@ -2765,13 +2883,17 @@ void FrameLoader::continueFragmentScrollAfterNavigationPolicy(const ResourceRequ
if (!shouldContinue)
return;
+ // Calling stopLoading() on the provisional document loader can cause the underlying
+ // frame to be deallocated.
+ Ref<Frame> protectedFrame(m_frame);
+
// If we have a provisional request for a different document, a fragment scroll should cancel it.
if (m_provisionalDocumentLoader && !equalIgnoringFragmentIdentifier(m_provisionalDocumentLoader->request().url(), request.url())) {
m_provisionalDocumentLoader->stopLoading();
- setProvisionalDocumentLoader(0);
+ setProvisionalDocumentLoader(nullptr);
}
- bool isRedirect = m_quickRedirectComing || policyChecker().loadType() == FrameLoadTypeRedirectWithLockedBackForwardList;
+ bool isRedirect = m_quickRedirectComing || policyChecker().loadType() == FrameLoadType::RedirectWithLockedBackForwardList;
loadInSameDocument(request.url(), 0, !isRedirect);
}
@@ -2783,10 +2905,10 @@ bool FrameLoader::shouldPerformFragmentNavigation(bool isFormSubmission, const S
// FIXME: What about load types other than Standard and Reload?
- return (!isFormSubmission || equalIgnoringCase(httpMethod, "GET"))
- && loadType != FrameLoadTypeReload
- && loadType != FrameLoadTypeReloadFromOrigin
- && loadType != FrameLoadTypeSame
+ return (!isFormSubmission || equalLettersIgnoringASCIICase(httpMethod, "get"))
+ && loadType != FrameLoadType::Reload
+ && loadType != FrameLoadType::ReloadFromOrigin
+ && loadType != FrameLoadType::Same
&& !shouldReload(m_frame.document()->url(), url)
// We don't want to just scroll if a link from within a
// frameset is trying to reload the frameset into _top.
@@ -2827,13 +2949,13 @@ bool FrameLoader::shouldClose()
bool shouldClose = false;
{
- NavigationDisablerForBeforeUnload navigationDisabler;
+ NavigationDisabler navigationDisabler;
size_t i;
for (i = 0; i < targetFrames.size(); i++) {
if (!targetFrames[i]->tree().isDescendantOf(&m_frame))
continue;
- if (!targetFrames[i]->loader().handleBeforeUnloadEvent(page->chrome(), this))
+ if (!targetFrames[i]->loader().dispatchBeforeUnloadEvent(page->chrome(), this))
break;
}
@@ -2848,36 +2970,105 @@ bool FrameLoader::shouldClose()
return shouldClose;
}
-bool FrameLoader::handleBeforeUnloadEvent(Chrome& chrome, FrameLoader* frameLoaderBeingNavigated)
+void FrameLoader::dispatchUnloadEvents(UnloadEventPolicy unloadEventPolicy)
+{
+ if (!m_frame.document())
+ return;
+
+ // We store the frame's page in a local variable because the frame might get detached inside dispatchEvent.
+ ForbidPromptsScope forbidPrompts(m_frame.page());
+ IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(m_frame.document());
+
+ if (m_didCallImplicitClose && !m_wasUnloadEventEmitted) {
+ auto* currentFocusedElement = m_frame.document()->focusedElement();
+ if (is<HTMLInputElement>(currentFocusedElement))
+ downcast<HTMLInputElement>(*currentFocusedElement).endEditing();
+ if (m_pageDismissalEventBeingDispatched == PageDismissalType::None) {
+ if (unloadEventPolicy == UnloadEventPolicyUnloadAndPageHide) {
+ m_pageDismissalEventBeingDispatched = PageDismissalType::PageHide;
+ m_frame.document()->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, m_frame.document()->pageCacheState() == Document::AboutToEnterPageCache), m_frame.document());
+ }
+
+ // FIXME: update Page Visibility state here.
+ // https://bugs.webkit.org/show_bug.cgi?id=116770
+
+ if (m_frame.document()->pageCacheState() == Document::NotInPageCache) {
+ Ref<Event> unloadEvent(Event::create(eventNames().unloadEvent, false, false));
+ // The DocumentLoader (and thus its LoadTiming) might get destroyed
+ // while dispatching the event, so protect it to prevent writing the end
+ // time into freed memory.
+ RefPtr<DocumentLoader> documentLoader = m_provisionalDocumentLoader;
+ m_pageDismissalEventBeingDispatched = PageDismissalType::Unload;
+ if (documentLoader && documentLoader->timing().startTime() && !documentLoader->timing().unloadEventStart() && !documentLoader->timing().unloadEventEnd()) {
+ auto& timing = documentLoader->timing();
+ timing.markUnloadEventStart();
+ m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document());
+ timing.markUnloadEventEnd();
+ } else
+ m_frame.document()->domWindow()->dispatchEvent(unloadEvent, m_frame.document());
+ }
+ }
+ m_pageDismissalEventBeingDispatched = PageDismissalType::None;
+ m_wasUnloadEventEmitted = true;
+ }
+
+ // Dispatching the unload event could have made m_frame.document() null.
+ if (!m_frame.document())
+ return;
+
+ if (m_frame.document()->pageCacheState() != Document::NotInPageCache)
+ return;
+
+ // Don't remove event listeners from a transitional empty document (see bug 28716 for more information).
+ bool keepEventListeners = m_stateMachine.isDisplayingInitialEmptyDocument() && m_provisionalDocumentLoader
+ && m_frame.document()->isSecureTransitionTo(m_provisionalDocumentLoader->url());
+
+ if (!keepEventListeners)
+ m_frame.document()->removeAllEventListeners();
+}
+
+static bool shouldAskForNavigationConfirmation(Document& document, const BeforeUnloadEvent& event)
+{
+ bool userDidInteractWithPage = document.topDocument().userDidInteractWithPage();
+ // Web pages can request we ask for confirmation before navigating by:
+ // - Cancelling the BeforeUnloadEvent (modern way)
+ // - Setting the returnValue attribute on the BeforeUnloadEvent to a non-empty string.
+ // - Returning a non-empty string from the event handler, which is then set as returnValue
+ // attribute on the BeforeUnloadEvent.
+ return userDidInteractWithPage && (event.defaultPrevented() || !event.returnValue().isEmpty());
+}
+
+bool FrameLoader::dispatchBeforeUnloadEvent(Chrome& chrome, FrameLoader* frameLoaderBeingNavigated)
{
DOMWindow* domWindow = m_frame.document()->domWindow();
if (!domWindow)
return true;
RefPtr<Document> document = m_frame.document();
- if (!document->body())
+ if (!document->bodyOrFrameset())
return true;
- RefPtr<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create();
- m_pageDismissalEventBeingDispatched = BeforeUnloadDismissal;
+ Ref<BeforeUnloadEvent> beforeUnloadEvent = BeforeUnloadEvent::create();
+ m_pageDismissalEventBeingDispatched = PageDismissalType::BeforeUnload;
- // We store the frame's page in a local variable because the frame might get detached inside dispatchEvent.
- Page* page = m_frame.page();
- page->incrementFrameHandlingBeforeUnloadEventCount();
- domWindow->dispatchEvent(beforeUnloadEvent.get(), domWindow->document());
- page->decrementFrameHandlingBeforeUnloadEventCount();
+ {
+ ForbidPromptsScope forbidPrompts(m_frame.page());
+ IgnoreOpensDuringUnloadCountIncrementer ignoreOpensDuringUnloadCountIncrementer(m_frame.document());
+ domWindow->dispatchEvent(beforeUnloadEvent, domWindow->document());
+ }
- m_pageDismissalEventBeingDispatched = NoDismissal;
+ m_pageDismissalEventBeingDispatched = PageDismissalType::None;
if (!beforeUnloadEvent->defaultPrevented())
document->defaultEventHandler(beforeUnloadEvent.get());
- if (beforeUnloadEvent->returnValue().isNull())
+
+ if (!shouldAskForNavigationConfirmation(*document, beforeUnloadEvent))
return true;
// If the navigating FrameLoader has already shown a beforeunload confirmation panel for the current navigation attempt,
// this frame is not allowed to cause another one to be shown.
if (frameLoaderBeingNavigated->m_currentNavigationHasShownBeforeUnloadConfirmPanel) {
- document->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Blocked attempt to show multiple beforeunload confirmation dialogs for the same navigation.");
+ document->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Blocked attempt to show multiple beforeunload confirmation dialogs for the same navigation."));
return true;
}
@@ -2889,8 +3080,8 @@ bool FrameLoader::handleBeforeUnloadEvent(Chrome& chrome, FrameLoader* frameLoad
Document* parentDocument = parentFrame->document();
if (!parentDocument)
return true;
- if (!m_frame.document() || !m_frame.document()->securityOrigin()->canAccess(parentDocument->securityOrigin())) {
- document->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Blocked attempt to show beforeunload confirmation dialog on behalf of a frame with different security origin. Protocols, domains, and ports must match.");
+ if (!m_frame.document() || !m_frame.document()->securityOrigin().canAccess(parentDocument->securityOrigin())) {
+ document->addConsoleMessage(MessageSource::JS, MessageLevel::Error, ASCIILiteral("Blocked attempt to show beforeunload confirmation dialog on behalf of a frame with different security origin. Protocols, domains, and ports must match."));
return true;
}
@@ -2908,10 +3099,10 @@ bool FrameLoader::handleBeforeUnloadEvent(Chrome& chrome, FrameLoader* frameLoad
frameLoaderBeingNavigated->m_currentNavigationHasShownBeforeUnloadConfirmPanel = true;
String text = document->displayStringModifiedByEncoding(beforeUnloadEvent->returnValue());
- return chrome.runBeforeUnloadConfirmPanel(text, &m_frame);
+ return chrome.runBeforeUnloadConfirmPanel(text, m_frame);
}
-void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, PassRefPtr<FormState> formState, bool shouldContinue)
+void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest& request, FormState* formState, bool shouldContinue, AllowNavigationToInvalidURL allowNavigationToInvalidURL)
{
// If we loaded an alternate page to replace an unreachableURL, we'll get in here with a
// nil policyDataSource because loading the alternate page will have passed
@@ -2920,19 +3111,23 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass
bool isTargetItem = history().provisionalItem() ? history().provisionalItem()->isTargetItem() : false;
- // Two reasons we can't continue:
+ bool urlIsDisallowed = allowNavigationToInvalidURL == AllowNavigationToInvalidURL::No && !request.url().isValid();
+
+ // Three reasons we can't continue:
// 1) Navigation policy delegate said we can't so request is nil. A primary case of this
// is the user responding Cancel to the form repost nag sheet.
// 2) User responded Cancel to an alert popped up by the before unload event handler.
- bool canContinue = shouldContinue && shouldClose();
+ // 3) The request's URL is invalid and navigation to invalid URLs is disallowed.
+ bool canContinue = shouldContinue && shouldClose() && !urlIsDisallowed;
if (!canContinue) {
// If we were waiting for a quick redirect, but the policy delegate decided to ignore it, then we
// need to report that the client redirect was cancelled.
+ // FIXME: The client should be told about ignored non-quick redirects, too.
if (m_quickRedirectComing)
clientRedirectCancelledOrFinished(false);
- setPolicyDocumentLoader(0);
+ setPolicyDocumentLoader(nullptr);
// If the navigation request came from the back/forward menu, and we punt on it, we have the
// problem that we have optimistically moved the b/f cursor already, so move it back. For sanity,
@@ -2957,22 +3152,20 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass
if (!m_frame.page())
return;
-#if ENABLE(INSPECTOR)
- if (Page* page = m_frame.page()) {
- if (m_frame.isMainFrame())
- page->inspectorController().resume();
- }
-#endif
-
setProvisionalDocumentLoader(m_policyDocumentLoader.get());
m_loadType = type;
setState(FrameStateProvisional);
- setPolicyDocumentLoader(0);
+ setPolicyDocumentLoader(nullptr);
- if (isBackForwardLoadType(type) && history().provisionalItem()->isInPageCache()) {
- loadProvisionalItemFromCachedPage();
- return;
+ if (isBackForwardLoadType(type)) {
+ auto& diagnosticLoggingClient = m_frame.page()->diagnosticLoggingClient();
+ if (history().provisionalItem()->isInPageCache()) {
+ diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::retrievalKey(), DiagnosticLoggingResultPass, ShouldSample::Yes);
+ loadProvisionalItemFromCachedPage();
+ return;
+ }
+ diagnosticLoggingClient.logDiagnosticMessageWithResult(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::retrievalKey(), DiagnosticLoggingResultFail, ShouldSample::Yes);
}
if (!formState) {
@@ -2980,13 +3173,13 @@ void FrameLoader::continueLoadAfterNavigationPolicy(const ResourceRequest&, Pass
return;
}
- m_client.dispatchWillSubmitForm(formState, [this](PolicyAction action) {
+ m_client.dispatchWillSubmitForm(*formState, [this] (PolicyAction action) {
policyChecker().continueLoadAfterWillSubmitForm(action);
});
}
void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& request,
- PassRefPtr<FormState> formState, const String& frameName, const NavigationAction& action, bool shouldContinue)
+ FormState* formState, const String& frameName, const NavigationAction& action, bool shouldContinue, AllowNavigationToInvalidURL allowNavigationToInvalidURL, NewFrameOpenerPolicy openerPolicy)
{
if (!shouldContinue)
return;
@@ -2996,16 +3189,20 @@ void FrameLoader::continueLoadAfterNewWindowPolicy(const ResourceRequest& reques
if (!mainFrame)
return;
+ mainFrame->loader().forceSandboxFlags(frame->loader().effectiveSandboxFlags());
+
if (frameName != "_blank")
mainFrame->tree().setName(frameName);
mainFrame->page()->setOpenedByDOM();
mainFrame->loader().m_client.dispatchShow();
- if (!m_suppressOpenerInNewFrame) {
- mainFrame->loader().setOpener(&frame.get());
+ if (openerPolicy == NewFrameOpenerPolicy::Allow) {
+ mainFrame->loader().setOpener(frame.ptr());
mainFrame->document()->setReferrerPolicy(frame->document()->referrerPolicy());
}
- mainFrame->loader().loadWithNavigationAction(request, NavigationAction(request), false, FrameLoadTypeStandard, formState);
+
+ NavigationAction newAction(request, action.shouldOpenExternalURLsPolicy());
+ mainFrame->loader().loadWithNavigationAction(request, newAction, LockHistory::No, FrameLoadType::Standard, formState, allowNavigationToInvalidURL);
}
void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& identifier, ResourceError& error)
@@ -3031,8 +3228,6 @@ void FrameLoader::requestFromDelegate(ResourceRequest& request, unsigned long& i
void FrameLoader::loadedResourceFromMemoryCache(CachedResource* resource, ResourceRequest& newRequest)
{
- newRequest = ResourceRequest(resource->url());
-
Page* page = m_frame.page();
if (!page)
return;
@@ -3045,14 +3240,14 @@ void FrameLoader::loadedResourceFromMemoryCache(CachedResource* resource, Resour
return;
if (!page->areMemoryCacheClientCallsEnabled()) {
- InspectorInstrumentation::didLoadResourceFromMemoryCache(page, m_documentLoader.get(), resource);
+ InspectorInstrumentation::didLoadResourceFromMemoryCache(*page, m_documentLoader.get(), resource);
m_documentLoader->recordMemoryCacheLoadForFutureClientNotification(resource->resourceRequest());
m_documentLoader->didTellClientAboutLoad(resource->url());
return;
}
if (m_client.dispatchDidLoadResourceFromMemoryCache(m_documentLoader.get(), newRequest, resource->response(), resource->encodedSize())) {
- InspectorInstrumentation::didLoadResourceFromMemoryCache(page, m_documentLoader.get(), resource);
+ InspectorInstrumentation::didLoadResourceFromMemoryCache(*page, m_documentLoader.get(), resource);
m_documentLoader->didTellClientAboutLoad(resource->url());
return;
}
@@ -3060,7 +3255,7 @@ void FrameLoader::loadedResourceFromMemoryCache(CachedResource* resource, Resour
unsigned long identifier;
ResourceError error;
requestFromDelegate(newRequest, identifier, error);
- InspectorInstrumentation::markResourceAsCached(page, identifier);
+ InspectorInstrumentation::markResourceAsCached(*page, identifier);
notifier().sendRemainingDelegateMessages(m_documentLoader.get(), identifier, newRequest, resource->response(), 0, resource->encodedSize(), 0, error);
}
@@ -3073,8 +3268,6 @@ void FrameLoader::applyUserAgent(ResourceRequest& request)
bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, const URL& url, unsigned long requestIdentifier)
{
- FeatureObserver::observe(m_frame.document(), FeatureObserver::XFrameOptions);
-
Frame& topFrame = m_frame.tree().top();
if (&m_frame == &topFrame)
return false;
@@ -3083,15 +3276,12 @@ bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, con
switch (disposition) {
case XFrameOptionsSameOrigin: {
- FeatureObserver::observe(m_frame.document(), FeatureObserver::XFrameOptionsSameOrigin);
RefPtr<SecurityOrigin> origin = SecurityOrigin::create(url);
if (!origin->isSameSchemeHostPort(topFrame.document()->securityOrigin()))
return true;
for (Frame* frame = m_frame.tree().parent(); frame; frame = frame->tree().parent()) {
- if (!origin->isSameSchemeHostPort(frame->document()->securityOrigin())) {
- FeatureObserver::observe(m_frame.document(), FeatureObserver::XFrameOptionsSameOriginWithBadAncestorChain);
+ if (!origin->isSameSchemeHostPort(frame->document()->securityOrigin()))
break;
- }
}
return false;
}
@@ -3100,15 +3290,16 @@ bool FrameLoader::shouldInterruptLoadForXFrameOptions(const String& content, con
case XFrameOptionsAllowAll:
return false;
case XFrameOptionsConflict:
- m_frame.document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Multiple 'X-Frame-Options' headers with conflicting values ('" + content + "') encountered when loading '" + url.stringCenterEllipsizedToLength() + "'. Falling back to 'DENY'.", requestIdentifier);
+ 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);
return true;
case XFrameOptionsInvalid:
- m_frame.document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Invalid 'X-Frame-Options' header encountered when loading '" + url.stringCenterEllipsizedToLength() + "': '" + content + "' is not a recognized directive. The header will be ignored.", requestIdentifier);
+ 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);
return false;
- default:
- ASSERT_NOT_REACHED();
+ case XFrameOptionsNone:
return false;
}
+ ASSERT_NOT_REACHED();
+ return false;
}
void FrameLoader::loadProvisionalItemFromCachedPage()
@@ -3121,9 +3312,9 @@ void FrameLoader::loadProvisionalItemFromCachedPage()
m_loadingFromCachedPage = true;
// Should have timing data from previous time(s) the page was shown.
- ASSERT(provisionalLoader->timing()->navigationStart());
+ ASSERT(provisionalLoader->timing().startTime());
provisionalLoader->resetTiming();
- provisionalLoader->timing()->markNavigationStart();
+ provisionalLoader->timing().markStartTime();
provisionalLoader->setCommitted(true);
commitProvisionalLoad();
@@ -3138,52 +3329,35 @@ bool FrameLoader::shouldTreatURLAsSameAsCurrent(const URL& url) const
bool FrameLoader::shouldTreatURLAsSrcdocDocument(const URL& url) const
{
- if (!equalIgnoringCase(url.string(), "about:srcdoc"))
+ if (!equalLettersIgnoringASCIICase(url.string(), "about:srcdoc"))
return false;
HTMLFrameOwnerElement* ownerElement = m_frame.ownerElement();
if (!ownerElement)
return false;
if (!ownerElement->hasTagName(iframeTag))
return false;
- return ownerElement->fastHasAttribute(srcdocAttr);
+ return ownerElement->hasAttributeWithoutSynchronization(srcdocAttr);
}
Frame* FrameLoader::findFrameForNavigation(const AtomicString& name, Document* activeDocument)
{
Frame* frame = m_frame.tree().find(name);
- // From http://www.whatwg.org/specs/web-apps/current-work/#seamlessLinks:
- //
- // If the source browsing context is the same as the browsing context
- // being navigated, and this browsing context has its seamless browsing
- // context flag set, and the browsing context being navigated was not
- // chosen using an explicit self-navigation override, then find the
- // nearest ancestor browsing context that does not have its seamless
- // browsing context flag set, and continue these steps as if that
- // browsing context was the one that was going to be navigated instead.
- if (frame == &m_frame && name != "_self" && m_frame.document()->shouldDisplaySeamlesslyWithParent()) {
- for (Frame* ancestor = &m_frame; ancestor; ancestor = ancestor->tree().parent()) {
- if (!ancestor->document()->shouldDisplaySeamlesslyWithParent()) {
- frame = ancestor;
- break;
- }
- }
- ASSERT(frame != &m_frame);
- }
-
// FIXME: Eventually all callers should supply the actual activeDocument so we can call canNavigate with the right document.
if (!activeDocument)
activeDocument = m_frame.document();
if (!activeDocument->canNavigate(frame))
- return 0;
+ return nullptr;
return frame;
}
-void FrameLoader::loadSameDocumentItem(HistoryItem* item)
+void FrameLoader::loadSameDocumentItem(HistoryItem& item)
{
- ASSERT(item->documentSequenceNumber() == history().currentItem()->documentSequenceNumber());
+ ASSERT(item.documentSequenceNumber() == history().currentItem()->documentSequenceNumber());
+
+ Ref<Frame> protect(m_frame);
// Save user view state to the current history item here since we don't do a normal load.
// FIXME: Does form state need to be saved here too?
@@ -3191,10 +3365,10 @@ void FrameLoader::loadSameDocumentItem(HistoryItem* item)
if (FrameView* view = m_frame.view())
view->setWasScrolledByUser(false);
- history().setCurrentItem(item);
+ history().setCurrentItem(&item);
// loadInSameDocument() actually changes the URL and notifies load delegates of a "fake" load
- loadInSameDocument(item->url(), item->stateObject(), false);
+ loadInSameDocument(item.url(), item.stateObject(), false);
// Restore user view state from the current history item here since we don't do a normal load.
history().restoreScrollPositionAndViewState();
@@ -3203,38 +3377,47 @@ void FrameLoader::loadSameDocumentItem(HistoryItem* item)
// FIXME: This function should really be split into a couple pieces, some of
// which should be methods of HistoryController and some of which should be
// methods of FrameLoader.
-void FrameLoader::loadDifferentDocumentItem(HistoryItem* item, FrameLoadType loadType, FormSubmissionCacheLoadPolicy cacheLoadPolicy)
+void FrameLoader::loadDifferentDocumentItem(HistoryItem& item, FrameLoadType loadType, FormSubmissionCacheLoadPolicy cacheLoadPolicy)
{
// Remember this item so we can traverse any child items as child frames load
- history().setProvisionalItem(item);
+ history().setProvisionalItem(&item);
- if (CachedPage* cachedPage = pageCache()->get(item)) {
- loadWithDocumentLoader(cachedPage->documentLoader(), loadType, 0);
+ if (CachedPage* cachedPage = PageCache::singleton().get(item, m_frame.page())) {
+ auto documentLoader = cachedPage->documentLoader();
+ m_client.updateCachedDocumentLoader(*documentLoader);
+ documentLoader->setTriggeringAction(NavigationAction(documentLoader->request(), loadType, false));
+ documentLoader->setLastCheckedRequest(ResourceRequest());
+ loadWithDocumentLoader(documentLoader, loadType, 0, AllowNavigationToInvalidURL::Yes);
return;
}
- URL itemURL = item->url();
- URL itemOriginalURL = item->originalURL();
+ URL itemURL = item.url();
+ URL itemOriginalURL = item.originalURL();
URL currentURL;
if (documentLoader())
currentURL = documentLoader()->url();
- RefPtr<FormData> formData = item->formData();
+ RefPtr<FormData> formData = item.formData();
ResourceRequest request(itemURL);
- if (!item->referrer().isNull())
- request.setHTTPReferrer(item->referrer());
-
+ if (!item.referrer().isNull())
+ request.setHTTPReferrer(item.referrer());
+
+ ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy = shouldOpenExternalURLsPolicyToApply(m_frame, item.shouldOpenExternalURLsPolicy());
+ bool isFormSubmission = false;
+ Event* event = nullptr;
+
// If this was a repost that failed the page cache, we might try to repost the form.
NavigationAction action;
if (formData) {
formData->generateFiles(m_frame.document());
request.setHTTPMethod("POST");
- request.setHTTPBody(formData);
- request.setHTTPContentType(item->formContentType());
- RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item->referrer());
+ request.setHTTPBody(WTFMove(formData));
+ request.setHTTPContentType(item.formContentType());
+ RefPtr<SecurityOrigin> securityOrigin = SecurityOrigin::createFromString(item.referrer());
addHTTPOriginIfNeeded(request, securityOrigin->toString());
+ addHTTPUpgradeInsecureRequestsIfNeeded(request);
// Make sure to add extra fields to the request after the Origin header is added for the FormData case.
// See https://bugs.webkit.org/show_bug.cgi?id=22194 for more discussion.
@@ -3250,49 +3433,54 @@ void FrameLoader::loadDifferentDocumentItem(HistoryItem* item, FrameLoadType loa
if (cacheLoadPolicy == MayAttemptCacheOnlyLoadForFormSubmissionItem) {
request.setCachePolicy(ReturnCacheDataDontLoad);
- action = NavigationAction(request, loadType, false);
+ action = NavigationAction(request, loadType, isFormSubmission, event, shouldOpenExternalURLsPolicy);
} else {
request.setCachePolicy(ReturnCacheDataElseLoad);
- action = NavigationAction(request, NavigationTypeFormResubmitted);
+ action = NavigationAction(request, NavigationType::FormResubmitted, event, shouldOpenExternalURLsPolicy);
}
} else {
switch (loadType) {
- case FrameLoadTypeReload:
- case FrameLoadTypeReloadFromOrigin:
- request.setCachePolicy(ReloadIgnoringCacheData);
- break;
- case FrameLoadTypeBack:
- case FrameLoadTypeForward:
- case FrameLoadTypeIndexedBackForward:
- // If the first load within a frame is a navigation within a back/forward list that was attached
- // without any of the items being loaded then we should use the default caching policy (<rdar://problem/8131355>).
- if (m_stateMachine.committedFirstRealDocumentLoad())
- request.setCachePolicy(ReturnCacheDataElseLoad);
- break;
- case FrameLoadTypeStandard:
- case FrameLoadTypeRedirectWithLockedBackForwardList:
- break;
- case FrameLoadTypeSame:
- default:
- ASSERT_NOT_REACHED();
+ case FrameLoadType::Reload:
+ case FrameLoadType::ReloadFromOrigin:
+ request.setCachePolicy(ReloadIgnoringCacheData);
+ break;
+ case FrameLoadType::Back:
+ case FrameLoadType::Forward:
+ case FrameLoadType::IndexedBackForward: {
+#if PLATFORM(COCOA)
+ bool allowStaleData = true;
+#else
+ bool allowStaleData = !item.wasRestoredFromSession();
+#endif
+ if (allowStaleData)
+ request.setCachePolicy(ReturnCacheDataElseLoad);
+ item.setWasRestoredFromSession(false);
+ break;
+ }
+ case FrameLoadType::Standard:
+ case FrameLoadType::RedirectWithLockedBackForwardList:
+ break;
+ case FrameLoadType::Same:
+ default:
+ ASSERT_NOT_REACHED();
}
addExtraFieldsToRequest(request, loadType, true);
ResourceRequest requestForOriginalURL(request);
requestForOriginalURL.setURL(itemOriginalURL);
- action = NavigationAction(requestForOriginalURL, loadType, false);
+ action = NavigationAction(requestForOriginalURL, loadType, isFormSubmission, event, shouldOpenExternalURLsPolicy);
}
- loadWithNavigationAction(request, action, false, loadType, 0);
+ loadWithNavigationAction(request, action, LockHistory::No, loadType, 0, AllowNavigationToInvalidURL::Yes);
}
// Loads content into this frame, as specified by history item
-void FrameLoader::loadItem(HistoryItem* item, FrameLoadType loadType)
+void FrameLoader::loadItem(HistoryItem& item, FrameLoadType loadType)
{
- m_requestedHistoryItem = item;
+ m_requestedHistoryItem = &item;
HistoryItem* currentItem = history().currentItem();
- bool sameDocumentNavigation = currentItem && item->shouldDoSameDocumentNavigationTo(currentItem);
+ bool sameDocumentNavigation = currentItem && item.shouldDoSameDocumentNavigationTo(*currentItem);
if (sameDocumentNavigation)
loadSameDocumentItem(item);
@@ -3304,13 +3492,12 @@ void FrameLoader::retryAfterFailedCacheOnlyMainResourceLoad()
{
ASSERT(m_state == FrameStateProvisional);
ASSERT(!m_loadingFromCachedPage);
- // We only use cache-only loads to avoid resubmitting forms.
- ASSERT(isBackForwardLoadType(m_loadType));
+ ASSERT(history().provisionalItem());
ASSERT(history().provisionalItem()->formData());
ASSERT(history().provisionalItem() == m_requestedHistoryItem.get());
FrameLoadType loadType = m_loadType;
- HistoryItem* item = history().provisionalItem();
+ HistoryItem& item = *history().provisionalItem();
stopAllLoaders(ShouldNotClearProvisionalItem);
loadDifferentDocumentItem(item, loadType, MayNotAttemptCacheOnlyLoadForFormSubmissionItem);
@@ -3319,10 +3506,31 @@ void FrameLoader::retryAfterFailedCacheOnlyMainResourceLoad()
ResourceError FrameLoader::cancelledError(const ResourceRequest& request) const
{
ResourceError error = m_client.cancelledError(request);
- error.setIsCancellation(true);
+ error.setType(ResourceError::Type::Cancellation);
+ return error;
+}
+
+ResourceError FrameLoader::blockedByContentBlockerError(const ResourceRequest& request) const
+{
+ return m_client.blockedByContentBlockerError(request);
+}
+
+ResourceError FrameLoader::blockedError(const ResourceRequest& request) const
+{
+ ResourceError error = m_client.blockedError(request);
+ error.setType(ResourceError::Type::Cancellation);
return error;
}
+#if ENABLE(CONTENT_FILTERING)
+ResourceError FrameLoader::blockedByContentFilterError(const ResourceRequest& request) const
+{
+ ResourceError error = m_client.blockedByContentFilterError(request);
+ error.setType(ResourceError::Type::General);
+ return error;
+}
+#endif
+
#if PLATFORM(IOS)
RetainPtr<CFDictionaryRef> FrameLoader::connectionProperties(ResourceLoader* loader)
{
@@ -3332,7 +3540,7 @@ RetainPtr<CFDictionaryRef> FrameLoader::connectionProperties(ResourceLoader* loa
String FrameLoader::referrer() const
{
- return m_documentLoader ? m_documentLoader->request().httpReferrer() : "";
+ return m_documentLoader ? m_documentLoader->request().httpReferrer() : emptyString();
}
void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds()
@@ -3342,8 +3550,8 @@ void FrameLoader::dispatchDidClearWindowObjectsInAllWorlds()
Vector<Ref<DOMWrapperWorld>> worlds;
ScriptController::getAllWorlds(worlds);
- for (size_t i = 0; i < worlds.size(); ++i)
- dispatchDidClearWindowObjectInWorld(worlds[i].get());
+ for (auto& world : worlds)
+ dispatchDidClearWindowObjectInWorld(world);
}
void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld& world)
@@ -3353,20 +3561,18 @@ void FrameLoader::dispatchDidClearWindowObjectInWorld(DOMWrapperWorld& world)
m_client.dispatchDidClearWindowObjectInWorld(world);
-#if ENABLE(INSPECTOR)
if (Page* page = m_frame.page())
- page->inspectorController().didClearWindowObjectInWorld(&m_frame, world);
-#endif
+ page->inspectorController().didClearWindowObjectInWorld(m_frame, world);
- InspectorInstrumentation::didClearWindowObjectInWorld(&m_frame, world);
+ InspectorInstrumentation::didClearWindowObjectInWorld(m_frame, world);
}
void FrameLoader::dispatchGlobalObjectAvailableInAllWorlds()
{
Vector<Ref<DOMWrapperWorld>> worlds;
ScriptController::getAllWorlds(worlds);
- for (size_t i = 0; i < worlds.size(); ++i)
- m_client.dispatchGlobalObjectAvailable(worlds[i].get());
+ for (auto& world : worlds)
+ m_client.dispatchGlobalObjectAvailable(world);
}
SandboxFlags FrameLoader::effectiveSandboxFlags() const
@@ -3398,31 +3604,24 @@ void FrameLoader::didChangeTitle(DocumentLoader* loader)
#endif
}
-void FrameLoader::didChangeIcons(IconType type)
-{
- m_client.dispatchDidChangeIcons(type);
-}
-
-void FrameLoader::dispatchDidCommitLoad()
+void FrameLoader::dispatchDidCommitLoad(std::optional<HasInsecureContent> initialHasInsecureContent)
{
if (m_stateMachine.creatingInitialEmptyDocument())
return;
- m_client.dispatchDidCommitLoad();
+ m_client.dispatchDidCommitLoad(initialHasInsecureContent);
if (m_frame.isMainFrame()) {
m_frame.page()->resetSeenPlugins();
m_frame.page()->resetSeenMediaEngines();
}
- InspectorInstrumentation::didCommitLoad(&m_frame, m_documentLoader.get());
+ InspectorInstrumentation::didCommitLoad(m_frame, m_documentLoader.get());
- if (m_frame.isMainFrame()) {
- m_frame.page()->featureObserver()->didCommitLoad();
#if ENABLE(REMOTE_INSPECTOR)
+ if (m_frame.isMainFrame())
m_frame.page()->remoteInspectorInformationDidChange();
#endif
- }
}
void FrameLoader::tellClientAboutPastMemoryCacheLoads()
@@ -3436,9 +3635,8 @@ void FrameLoader::tellClientAboutPastMemoryCacheLoads()
Vector<ResourceRequest> pastLoads;
m_documentLoader->takeMemoryCacheLoadsForClientNotification(pastLoads);
- size_t size = pastLoads.size();
- for (size_t i = 0; i < size; ++i) {
- CachedResource* resource = memoryCache()->resourceForRequest(pastLoads[i]);
+ for (auto& pastLoad : pastLoads) {
+ CachedResource* resource = MemoryCache::singleton().resourceForRequest(pastLoad, m_frame.page()->sessionID());
// FIXME: These loads, loaded from cache, but now gone from the cache by the time
// Page::setMemoryCacheClientCallsEnabled(true) is called, will not be seen by the client.
@@ -3459,12 +3657,8 @@ NetworkingContext* FrameLoader::networkingContext() const
void FrameLoader::loadProgressingStatusChanged()
{
- FrameView* view = m_frame.mainFrame().view();
- if (!view)
- return;
-
- view->updateLayerFlushThrottlingInAllFrames();
- view->adjustTiledBackingCoverage();
+ if (auto* view = m_frame.mainFrame().view())
+ view->loadProgressingStatusChanged();
}
void FrameLoader::forcePageTransitionIfNeeded()
@@ -3472,22 +3666,40 @@ void FrameLoader::forcePageTransitionIfNeeded()
m_client.forcePageTransitionIfNeeded();
}
+void FrameLoader::clearTestingOverrides()
+{
+ m_overrideCachePolicyForTesting = std::nullopt;
+ m_overrideResourceLoadPriorityForTesting = std::nullopt;
+ m_isStrictRawResourceValidationPolicyDisabledForTesting = false;
+}
+
+void FrameLoader::applyShouldOpenExternalURLsPolicyToNewDocumentLoader(DocumentLoader& documentLoader, ShouldOpenExternalURLsPolicy propagatedPolicy)
+{
+ documentLoader.setShouldOpenExternalURLsPolicy(shouldOpenExternalURLsPolicyToApply(m_frame, propagatedPolicy));
+}
+
+bool FrameLoader::isAlwaysOnLoggingAllowed() const
+{
+ return frame().isAlwaysOnLoggingAllowed();
+}
+
bool FrameLoaderClient::hasHTMLView() const
{
return true;
}
-PassRefPtr<Frame> createWindow(Frame* openerFrame, Frame* lookupFrame, const FrameLoadRequest& request, const WindowFeatures& features, bool& created)
+RefPtr<Frame> createWindow(Frame& openerFrame, Frame& lookupFrame, const FrameLoadRequest& request, const WindowFeatures& features, bool& created)
{
ASSERT(!features.dialog || request.frameName().isEmpty());
+ created = false;
+
if (!request.frameName().isEmpty() && request.frameName() != "_blank") {
- if (Frame* frame = lookupFrame->loader().findFrameForNavigation(request.frameName(), openerFrame->document())) {
+ if (RefPtr<Frame> frame = lookupFrame.loader().findFrameForNavigation(request.frameName(), openerFrame.document())) {
if (request.frameName() != "_self") {
if (Page* page = frame->page())
page->chrome().focus();
}
- created = false;
return frame;
}
}
@@ -3495,74 +3707,98 @@ PassRefPtr<Frame> createWindow(Frame* openerFrame, Frame* lookupFrame, const Fra
// Sandboxed frames cannot open new auxiliary browsing contexts.
if (isDocumentSandboxed(openerFrame, SandboxPopups)) {
// FIXME: This message should be moved off the console once a solution to https://bugs.webkit.org/show_bug.cgi?id=103274 exists.
- openerFrame->document()->addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, "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.");
- return 0;
+ 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.");
+ return nullptr;
}
// FIXME: Setting the referrer should be the caller's responsibility.
FrameLoadRequest requestWithReferrer = request;
- String referrer = SecurityPolicy::generateReferrerHeader(openerFrame->document()->referrerPolicy(), request.resourceRequest().url(), openerFrame->loader().outgoingReferrer());
+ String referrer = SecurityPolicy::generateReferrerHeader(openerFrame.document()->referrerPolicy(), request.resourceRequest().url(), openerFrame.loader().outgoingReferrer());
if (!referrer.isEmpty())
requestWithReferrer.resourceRequest().setHTTPReferrer(referrer);
- FrameLoader::addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), openerFrame->loader().outgoingOrigin());
+ FrameLoader::addHTTPOriginIfNeeded(requestWithReferrer.resourceRequest(), openerFrame.loader().outgoingOrigin());
+ FrameLoader::addHTTPUpgradeInsecureRequestsIfNeeded(requestWithReferrer.resourceRequest());
- Page* oldPage = openerFrame->page();
+ Page* oldPage = openerFrame.page();
if (!oldPage)
- return 0;
+ return nullptr;
- NavigationAction action(requestWithReferrer.resourceRequest());
- Page* page = oldPage->chrome().createWindow(openerFrame, requestWithReferrer, features, action);
+ ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy = shouldOpenExternalURLsPolicyToApply(openerFrame, request.shouldOpenExternalURLsPolicy());
+ Page* page = oldPage->chrome().createWindow(openerFrame, requestWithReferrer, features, NavigationAction(requestWithReferrer.resourceRequest(), shouldOpenExternalURLsPolicy));
if (!page)
- return 0;
+ return nullptr;
+
+ RefPtr<Frame> frame = &page->mainFrame();
- page->mainFrame().loader().forceSandboxFlags(openerFrame->document()->sandboxFlags());
+ frame->loader().forceSandboxFlags(openerFrame.document()->sandboxFlags());
if (request.frameName() != "_blank")
- page->mainFrame().tree().setName(request.frameName());
+ frame->tree().setName(request.frameName());
page->chrome().setToolbarsVisible(features.toolBarVisible || features.locationBarVisible);
+
+ if (!frame->page())
+ return nullptr;
page->chrome().setStatusbarVisible(features.statusBarVisible);
+
+ if (!frame->page())
+ return nullptr;
page->chrome().setScrollbarsVisible(features.scrollbarsVisible);
+
+ if (!frame->page())
+ return nullptr;
page->chrome().setMenubarVisible(features.menuBarVisible);
+
+ if (!frame->page())
+ return nullptr;
page->chrome().setResizable(features.resizable);
// 'x' and 'y' specify the location of the window, while 'width' and 'height'
// specify the size of the viewport. We can only resize the window, so adjust
// for the difference between the window size and the viewport size.
-// FIXME: We should reconcile the initialization of viewport arguments between iOS and OpenSource.
+ // FIXME: We should reconcile the initialization of viewport arguments between iOS and non-IOS.
#if !PLATFORM(IOS)
FloatSize viewportSize = page->chrome().pageRect().size();
FloatRect windowRect = page->chrome().windowRect();
- if (features.xSet)
- windowRect.setX(features.x);
- if (features.ySet)
- windowRect.setY(features.y);
+ if (features.x)
+ windowRect.setX(*features.x);
+ if (features.y)
+ windowRect.setY(*features.y);
// Zero width and height mean using default size, not minumum one.
- if (features.widthSet && features.width)
- windowRect.setWidth(features.width + (windowRect.width() - viewportSize.width()));
- if (features.heightSet && features.height)
- windowRect.setHeight(features.height + (windowRect.height() - viewportSize.height()));
+ if (features.width && *features.width)
+ windowRect.setWidth(*features.width + (windowRect.width() - viewportSize.width()));
+ if (features.height && *features.height)
+ windowRect.setHeight(*features.height + (windowRect.height() - viewportSize.height()));
// Ensure non-NaN values, minimum size as well as being within valid screen area.
- FloatRect newWindowRect = DOMWindow::adjustWindowRect(page, windowRect);
+ FloatRect newWindowRect = DOMWindow::adjustWindowRect(*page, windowRect);
+ if (!frame->page())
+ return nullptr;
page->chrome().setWindowRect(newWindowRect);
#else
// On iOS, width and height refer to the viewport dimensions.
ViewportArguments arguments;
// Zero width and height mean using default size, not minimum one.
- if (features.widthSet && features.width)
- arguments.width = features.width;
- if (features.heightSet && features.height)
- arguments.height = features.height;
- page->mainFrame().setViewportArguments(arguments);
+ if (features.width && *features.width)
+ arguments.width = *features.width;
+ if (features.height && *features.height)
+ arguments.height = *features.height;
+ frame->setViewportArguments(arguments);
#endif
+ if (!frame->page())
+ return nullptr;
page->chrome().show();
created = true;
- return &page->mainFrame();
+ return frame;
+}
+
+bool FrameLoader::shouldSuppressKeyboardInput() const
+{
+ return m_frame.settings().shouldSuppressKeyboardInputDuringProvisionalNavigation() && m_state == FrameStateProvisional;
}
} // namespace WebCore