Commit 00314989 authored by creis's avatar creis Committed by Commit bot

Notify the Blink client synchronously if the initial doc is accessed.

This avoids problems with the timer being delayed by nested message
loops and ScopedPageLoadDeferrers, where code in the nested message
loop might access the document and not generate a notification.

The timer was originally added to prevent re-entrant calls to V8,
but that can now be prevented with ScriptForbiddenScope.

BUG=629542
TEST=See bug for repro steps.

Review-Url: https://codereview.chromium.org/2171063002
Cr-Commit-Position: refs/heads/master@{#407269}
parent 8f4c8e6d
......@@ -2769,6 +2769,9 @@ RenderFrameImpl::createServiceWorkerProvider() {
}
void RenderFrameImpl::didAccessInitialDocument() {
// NOTE: Do not call back into JavaScript here, since this call is made from a
// V8 security check.
// If the request hasn't yet committed, notify the browser process that it is
// no longer safe to show the pending URL of the main frame, since a URL spoof
// is now possible. (If the request has committed, the browser already knows.)
......
......@@ -170,7 +170,6 @@ FrameLoader::FrameLoader(LocalFrame* frame)
, m_inStopAllLoaders(false)
, m_checkTimer(this, &FrameLoader::checkTimerFired)
, m_didAccessInitialDocument(false)
, m_didAccessInitialDocumentTimer(this, &FrameLoader::didAccessInitialDocumentTimerFired)
, m_forcedSandboxFlags(SandboxNone)
, m_dispatchingDidClearWindowObjectInMainWorld(false)
, m_protectProvisionalLoader(false)
......@@ -1066,22 +1065,12 @@ void FrameLoader::didAccessInitialDocument()
// We only need to notify the client once, and only for the main frame.
if (isLoadingMainFrame() && !m_didAccessInitialDocument) {
m_didAccessInitialDocument = true;
// Notify asynchronously, since this is called within a JavaScript security check.
m_didAccessInitialDocumentTimer.startOneShot(0, BLINK_FROM_HERE);
}
}
void FrameLoader::didAccessInitialDocumentTimerFired(Timer<FrameLoader>*)
{
if (client())
client()->didAccessInitialDocument();
}
void FrameLoader::notifyIfInitialDocumentAccessed()
{
if (m_didAccessInitialDocumentTimer.isActive()) {
m_didAccessInitialDocumentTimer.stop();
didAccessInitialDocumentTimerFired(0);
// Forbid script execution to prevent re-entering V8, since this is
// called from a binding security check.
ScriptForbiddenScope forbidScripts;
if (client())
client()->didAccessInitialDocument();
}
}
......
......@@ -100,16 +100,12 @@ public:
void replaceDocumentWhileExecutingJavaScriptURL(const String& source, Document* ownerDocument);
// Sets a timer to notify the client that the initial empty document has
// been accessed, and thus it is no longer safe to show a provisional URL
// above the document without risking a URL spoof.
// Notifies the client that the initial empty document has been accessed,
// and thus it is no longer safe to show a provisional URL above the
// document without risking a URL spoof. The client must not call back into
// JavaScript.
void didAccessInitialDocument();
// If the initial empty document is showing and has been accessed, this
// cancels the timer and immediately notifies the client in cases that
// waiting to notify would allow a URL spoof.
void notifyIfInitialDocumentAccessed();
DocumentLoader* documentLoader() const { return m_documentLoader.get(); }
DocumentLoader* provisionalDocumentLoader() const { return m_provisionalDocumentLoader.get(); }
......@@ -281,7 +277,6 @@ private:
Timer<FrameLoader> m_checkTimer;
bool m_didAccessInitialDocument;
Timer<FrameLoader> m_didAccessInitialDocumentTimer;
SandboxFlags m_forcedSandboxFlags;
......
......@@ -36,11 +36,6 @@ ScopedPageLoadDeferrer::ScopedPageLoadDeferrer(Page* exclusion)
if (page == exclusion || page->defersLoading())
continue;
m_deferredPages.append(page);
// Ensure that we notify the client if the initial empty document is accessed before
// showing anything modal, to prevent spoofs while the modal window or sheet is visible.
if (page->mainFrame()->isLocalFrame())
page->deprecatedLocalMainFrame()->loader().notifyIfInitialDocumentAccessed();
}
setDefersLoading(true);
......
......@@ -5545,13 +5545,13 @@ TEST_P(ParameterizedWebFrameTest, DidAccessInitialDocumentBodyBeforeModalDialog)
runPendingTasks();
EXPECT_FALSE(webFrameClient.m_didAccessInitialDocument);
// Access the initial document by modifying the body. We normally set a
// timer to notify the client.
// Access the initial document by modifying the body.
newView->mainFrame()->executeScript(
WebScriptSource("window.opener.document.body.innerHTML += 'Modified';"));
EXPECT_FALSE(webFrameClient.m_didAccessInitialDocument);
EXPECT_TRUE(webFrameClient.m_didAccessInitialDocument);
// Make sure that a modal dialog forces us to notify right away.
// Run a modal dialog, which used to run a nested message loop and require
// a special case for notifying about the access.
newView->mainFrame()->executeScript(
WebScriptSource("window.opener.confirm('Modal');"));
EXPECT_TRUE(webFrameClient.m_didAccessInitialDocument);
......@@ -5585,13 +5585,13 @@ TEST_P(ParameterizedWebFrameTest, DidWriteToInitialDocumentBeforeModalDialog)
EXPECT_FALSE(webFrameClient.m_didAccessInitialDocument);
// Access the initial document with document.write, which moves us past the
// initial empty document state of the state machine. We normally set a
// timer to notify the client.
// initial empty document state of the state machine.
newView->mainFrame()->executeScript(
WebScriptSource("window.opener.document.write('Modified'); window.opener.document.close();"));
EXPECT_FALSE(webFrameClient.m_didAccessInitialDocument);
EXPECT_TRUE(webFrameClient.m_didAccessInitialDocument);
// Make sure that a modal dialog forces us to notify right away.
// Run a modal dialog, which used to run a nested message loop and require
// a special case for notifying about the access.
newView->mainFrame()->executeScript(
WebScriptSource("window.opener.confirm('Modal');"));
EXPECT_TRUE(webFrameClient.m_didAccessInitialDocument);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment