Skip to content

Commit

Permalink
Merge r186049 - Crash: com.apple.WebKit.WebContent at com.apple.WebCo…
Browse files Browse the repository at this point in the history
…re: WebCore::CachedFrameBase::restore + 333

https://bugs.webkit.org/show_bug.cgi?id=146388
<rdar://problem/21567343>

Reviewed by Darin Adler.

Source/WebCore:

Pages that are currently loading are not supposed to go into the
PageCache. However, PageCache::canCache() only checks if the
FrameLoader's documentLoader is loading. If the subframe is in
provisional load stage, we would fail to detect that the frame is
actually loading because the FrameLoader active documentLoader would
be the provisional documentLoader, not the regular documentLoader.
Therefore, the page would get added to the PageCache and the frame
would keep loading while in the PageCache.

On http://www.audiusa.com/models, this is what was happening. It was
crashing because the subframe would finish loading while in the
PageCache, in which case we would fire the 'load' event and the
content 'load' event handler would then proceed to remove the iframe.
Upon restoring the PageCache entry, we would run into trouble as we
would have a CachedFrame whose Frame has been removed.

The solution proposed is to prevent page-caching if a subframe is in
provisional load stage.

Test: http/tests/navigation/page-cache-iframe-provisional-load.html

* history/PageCache.cpp:
(WebCore::logCanCacheFrameDecision):
(WebCore::PageCache::canCachePageContainingThisFrame):
* page/DiagnosticLoggingKeys.cpp:
(WebCore::DiagnosticLoggingKeys::provisionalLoadKey):
* page/DiagnosticLoggingKeys.h:

LayoutTests:

Add layout test to cover the case where a subframe is currently in
provisional load stage when checking if the page if page-cacheable.

The test also removes the iframe once loaded in order to cause a crash
if the frame were to finish loading while in the page cache.

* http/tests/navigation/page-cache-iframe-provisional-load-expected.txt: Added.
* http/tests/navigation/page-cache-iframe-provisional-load.html: Added.
* http/tests/navigation/resources/page-cache-helper-slow.html: Added.
  • Loading branch information
cdumez authored and carlosgcampos committed Jul 7, 2015
1 parent c55739b commit e2cf598
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 0 deletions.
18 changes: 18 additions & 0 deletions LayoutTests/ChangeLog
@@ -1,3 +1,21 @@
2015-06-28 Chris Dumez <cdumez@apple.com>

Crash: com.apple.WebKit.WebContent at com.apple.WebCore: WebCore::CachedFrameBase::restore + 333
https://bugs.webkit.org/show_bug.cgi?id=146388
<rdar://problem/21567343>

Reviewed by Darin Adler.

Add layout test to cover the case where a subframe is currently in
provisional load stage when checking if the page if page-cacheable.

The test also removes the iframe once loaded in order to cause a crash
if the frame were to finish loading while in the page cache.

* http/tests/navigation/page-cache-iframe-provisional-load-expected.txt: Added.
* http/tests/navigation/page-cache-iframe-provisional-load.html: Added.
* http/tests/navigation/resources/page-cache-helper-slow.html: Added.

2015-06-25 Zalan Bujtas <zalan@apple.com>

Do not send touch events to the slider's thumb when it does not have a renderer.
Expand Down
@@ -0,0 +1,11 @@
A frame in provisional load stage should prevent page caching.

On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".


pageshow - not from cache
PASS Page was not restored from PageCache
PASS successfullyParsed is true

TEST COMPLETE

@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html>
<body>
<script src="/resources/js-test-pre.js"></script>
<script>
description("A frame in provisional load stage should prevent page caching.");
window.jsTestIsAsync = true;

if (window.testRunner)
testRunner.overridePreference("WebKitUsesPageCachePreferenceKey", 1);

window.addEventListener("pageshow", function(event) {
debug("pageshow - " + (event.persisted ? "" : "not ") + "from cache");

if (!window.sessionStorage.page_cache_provisional_load_test_started)
return;

delete window.sessionStorage.page_cache_provisional_load_test_started;

if (event.persisted)
testFailed("Page was restored from PageCache");
else
testPassed("Page was not restored from PageCache");

finishJSTest();
}, false);

window.addEventListener("pagehide", function(event) {
debug("pagehide");
}, false);

function loadSubframeAndNavigateAway()
{
// Force a back navigation back to this page.
window.sessionStorage.page_cache_provisional_load_test_started = true;
window.location.href = "resources/page-cache-helper-slow.html";

var testFrame = document.getElementById("testFrame");
testFrame.src = "http://127.0.0.1:8000/navigation/resources/slow-resource.pl?delay=100";

// If the page goes into the page cache and the frame keeps loading while in the cache,
// the following will cause crashes.
testFrame.onload = function() { document.getElementById("testFrame").remove(); };
}

window.addEventListener('load', function() {
setTimeout(function() {
loadSubframeAndNavigateAway();
}, 0);
}, false);

</script>
<iframe id="testFrame" src="about:blank"></iframe>
<script src="/resources/js-test-post.js"></script>
</body>
</html>
@@ -0,0 +1,9 @@
This page should go back. If a test outputs the contents of this
page, then the test page failed to enter the page cache.
<script>
window.addEventListener("load", function() {
setTimeout(function() {
history.back();
}, 500);
}, false);
</script>
36 changes: 36 additions & 0 deletions Source/WebCore/ChangeLog
@@ -1,3 +1,39 @@
2015-06-28 Chris Dumez <cdumez@apple.com>

Crash: com.apple.WebKit.WebContent at com.apple.WebCore: WebCore::CachedFrameBase::restore + 333
https://bugs.webkit.org/show_bug.cgi?id=146388
<rdar://problem/21567343>

Reviewed by Darin Adler.

Pages that are currently loading are not supposed to go into the
PageCache. However, PageCache::canCache() only checks if the
FrameLoader's documentLoader is loading. If the subframe is in
provisional load stage, we would fail to detect that the frame is
actually loading because the FrameLoader active documentLoader would
be the provisional documentLoader, not the regular documentLoader.
Therefore, the page would get added to the PageCache and the frame
would keep loading while in the PageCache.

On http://www.audiusa.com/models, this is what was happening. It was
crashing because the subframe would finish loading while in the
PageCache, in which case we would fire the 'load' event and the
content 'load' event handler would then proceed to remove the iframe.
Upon restoring the PageCache entry, we would run into trouble as we
would have a CachedFrame whose Frame has been removed.

The solution proposed is to prevent page-caching if a subframe is in
provisional load stage.

Test: http/tests/navigation/page-cache-iframe-provisional-load.html

* history/PageCache.cpp:
(WebCore::logCanCacheFrameDecision):
(WebCore::PageCache::canCachePageContainingThisFrame):
* page/DiagnosticLoggingKeys.cpp:
(WebCore::DiagnosticLoggingKeys::provisionalLoadKey):
* page/DiagnosticLoggingKeys.h:

2015-06-25 Zalan Bujtas <zalan@apple.com>

Do not send touch events to the slider's thumb when it does not have a renderer.
Expand Down
12 changes: 12 additions & 0 deletions Source/WebCore/history/PageCache.cpp
Expand Up @@ -93,6 +93,7 @@ enum ReasonFrameCannotBeInPageCache {
DocumentLoaderUsesApplicationCache,
ClientDeniesCaching,
NumberOfReasonsFramesCannotBeInPageCache,
IsInProvisionalLoadStage,
};
COMPILE_ASSERT(NumberOfReasonsFramesCannotBeInPageCache <= sizeof(unsigned)*8, ReasonFrameCannotBeInPageCacheDoesNotFitInBitmap);

Expand All @@ -112,6 +113,11 @@ static inline void logPageCacheFailureDiagnosticMessage(Page* page, const String
static unsigned logCanCacheFrameDecision(Frame& frame, DiagnosticLoggingClient& diagnosticLoggingClient, unsigned indentLevel)
{
PCLOG("+---");
if (!frame.isMainFrame() && frame.loader().state() == FrameStateProvisional) {
PCLOG(" -Frame is in provisional load stage");
logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::provisionalLoadKey());
return 1 << IsInProvisionalLoadStage;
}
if (!frame.loader().documentLoader()) {
PCLOG(" -There is no DocumentLoader object");
logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::noDocumentLoaderKey());
Expand Down Expand Up @@ -305,6 +311,12 @@ bool PageCache::canCachePageContainingThisFrame(Frame& frame)
}

FrameLoader& frameLoader = frame.loader();

// Prevent page caching if a subframe is still in provisional load stage.
// We only do this check for subframes because the main frame is reused when navigating to a new page.
if (!frame.isMainFrame() && frameLoader.state() == FrameStateProvisional)
return false;

DocumentLoader* documentLoader = frameLoader.documentLoader();
Document* document = frame.document();

Expand Down
5 changes: 5 additions & 0 deletions Source/WebCore/page/DiagnosticLoggingKeys.cpp
Expand Up @@ -53,6 +53,11 @@ String DiagnosticLoggingKeys::pluginLoadingFailedKey()
return ASCIILiteral("pluginFailedLoading");
}

String DiagnosticLoggingKeys::provisionalLoadKey()
{
return ASCIILiteral("provisionalLoad");
}

String DiagnosticLoggingKeys::pageContainsPluginKey()
{
return ASCIILiteral("pageContainsPlugin");
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/page/DiagnosticLoggingKeys.h
Expand Up @@ -81,6 +81,7 @@ class DiagnosticLoggingKeys {
static String playedKey();
static String pluginLoadedKey();
static String pluginLoadingFailedKey();
static String provisionalLoadKey();
static String prunedDueToMaxSizeReached();
static String prunedDueToMemoryPressureKey();
static String prunedDueToProcessSuspended();
Expand Down

0 comments on commit e2cf598

Please sign in to comment.