Changeset 241606 in webkit


Ignore:
Timestamp:
Feb 15, 2019 1:03:53 PM (5 years ago)
Author:
Chris Dumez
Message:

Regression(PSON) Navigating quickly back and forth can lead to getting 'about:blank' in the backforward list
https://bugs.webkit.org/show_bug.cgi?id=194717
<rdar://problem/47884404>

Reviewed by Brady Eidson.

Source/WebKit:

When the client does a history navigation, the UIProcess sends a WebPage::GoToBackForwardItem IPC to the
WebProcess and the WebProcess sends a WebPageProxy::BackForwardGoToItem IPC back to the UIProcess to
update the current item in the BackForwardList. This means that there is a slight delay between the
point a client requests a history navigation and the point where the BackForwardList's current item gets
update. This delay is pre-existing behavior and not new to PSON.

However, with PSON enabled, if we decide to process-swap for the history navigation, we'll tell the
previous (committed) process to ignore the load and we ask a new (provisional) process to do the history
navigation. When the previous process receives the request to ignore the history navigation, it restores
the History's current item to the one previous the navigation, which sends a WebPageProxy::GoToBackForwardItem
IPC to the UIProcess to update the BackForwardList as well. In parallel, the new process starts the
history navigation and also sends a WebPageProxy::GoToBackForwardItem to update the BackForwardList's
current item as well. We end up with a race between the 2 GoToBackForwardItem IPC messages coming from
the old and new process. If the old process's message loses the race, we end up with the wrong current
history item getting set in the UIProcess. Later, when we commit the provisional load and try to suspend
the previous page, we would save the SuspendedPage on the *wrong* BackForwardList item. If one tries to
load this BackForwardList item later, we'll use its SuspendedPage and try to unsuspend it. However,
because the PageCache entry is saved on another HistoryItem than the one getting loaded in the WebProcess
side, we attempt to do a regular load instead of a PageCache restore. We end up failing the load because
pages cannot trigger new loads while in page cache. Because the load fails, we end up loading the
initial empty document and this is how we end up with 'about:blank' in the back forward list.

To address the issue, update WebPageProxy::backForwardGoToItem() to ignore messages from the old/committed
WebProcess when there is a pending provisional load. If the committed processes starts a legit new
load, it would clear any existing pending provisional load before attempting the call backForwardGoToItem().
As a result, ignoring such messages from the old processes when there is a pending provisional load is
safe.

In the future, we should probably move more of the history / backForwardList management to the UIProcess
to avoid this sort of issues. This would be a much larger refactoring though so I am going with this
simpler fix that is easily cherry-pickable for now.

  • UIProcess/WebPageProxy.cpp:

(WebKit::WebPageProxy::suspendCurrentPageIfPossible):
(WebKit::WebPageProxy::continueNavigationInNewProcess):
(WebKit::WebPageProxy::backForwardGoToItem):

Tools:

Add API test coverage.

  • TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:
Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r241604 r241606  
     12019-02-15  Chris Dumez  <cdumez@apple.com>
     2
     3        Regression(PSON) Navigating quickly back and forth can lead to getting 'about:blank' in the backforward list
     4        https://bugs.webkit.org/show_bug.cgi?id=194717
     5        <rdar://problem/47884404>
     6
     7        Reviewed by Brady Eidson.
     8
     9        When the client does a history navigation, the UIProcess sends a WebPage::GoToBackForwardItem IPC to the
     10        WebProcess and the WebProcess sends a WebPageProxy::BackForwardGoToItem IPC back to the UIProcess to
     11        update the current item in the BackForwardList. This means that there is a slight delay between the
     12        point a client requests a history navigation and the point where the BackForwardList's current item gets
     13        update. This delay is pre-existing behavior and not new to PSON.
     14
     15        However, with PSON enabled, if we decide to process-swap for the history navigation, we'll tell the
     16        previous (committed) process to ignore the load and we ask a new (provisional) process to do the history
     17        navigation. When the previous process receives the request to ignore the history navigation, it restores
     18        the History's current item to the one previous the navigation, which sends a WebPageProxy::GoToBackForwardItem
     19        IPC to the UIProcess to update the BackForwardList as well. In parallel, the new process starts the
     20        history navigation and also sends a WebPageProxy::GoToBackForwardItem to update the BackForwardList's
     21        current item as well. We end up with a race between the 2 GoToBackForwardItem IPC messages coming from
     22        the old and new process. If the old process's message loses the race, we end up with the wrong current
     23        history item getting set in the UIProcess. Later, when we commit the provisional load and try to suspend
     24        the previous page, we would save the SuspendedPage on the *wrong* BackForwardList item. If one tries to
     25        load this BackForwardList item later, we'll use its SuspendedPage and try to unsuspend it. However,
     26        because the PageCache entry is saved on another HistoryItem than the one getting loaded in the WebProcess
     27        side, we attempt to do a regular load instead of a PageCache restore. We end up failing the load because
     28        pages cannot trigger new loads while in page cache. Because the load fails, we end up loading the
     29        initial empty document and this is how we end up with 'about:blank' in the back forward list.
     30
     31        To address the issue, update WebPageProxy::backForwardGoToItem() to ignore messages from the old/committed
     32        WebProcess when there is a pending provisional load. If the committed processes starts a legit new
     33        load, it would clear any existing pending provisional load before attempting the call backForwardGoToItem().
     34        As a result, ignoring such messages from the old processes when there is a pending provisional load is
     35        safe.
     36
     37        In the future, we should probably move more of the history / backForwardList management to the UIProcess
     38        to avoid this sort of issues. This would be a much larger refactoring though so I am going with this
     39        simpler fix that is easily cherry-pickable for now.
     40
     41        * UIProcess/WebPageProxy.cpp:
     42        (WebKit::WebPageProxy::suspendCurrentPageIfPossible):
     43        (WebKit::WebPageProxy::continueNavigationInNewProcess):
     44        (WebKit::WebPageProxy::backForwardGoToItem):
     45
    1462019-02-15  Alex Christensen  <achristensen@webkit.org>
    247
  • trunk/Source/WebKit/UIProcess/WebPageProxy.cpp

    r241556 r241606  
    246246
    247247#define RELEASE_LOG_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_IF(isAlwaysOnLoggingAllowed(), channel, "%p - WebPageProxy::" fmt, this, ##__VA_ARGS__)
     248#define RELEASE_LOG_ERROR_IF_ALLOWED(channel, fmt, ...) RELEASE_LOG_ERROR_IF(isAlwaysOnLoggingAllowed(), channel, "%p - WebPageProxy::" fmt, this, ##__VA_ARGS__)
    248249
    249250// Represents the number of wheel events we can hold in the queue before we start pushing them preemptively.
     
    761762
    762763    // If the client forced a swap then it may not be Web-compatible to suspend the previous page because other windows may have an opener link to the page.
    763     if (processSwapRequestedByClient == ProcessSwapRequestedByClient::Yes)
     764    if (processSwapRequestedByClient == ProcessSwapRequestedByClient::Yes) {
     765        RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i because the swap was requested by the client", m_process->processIdentifier());
    764766        return false;
    765 
    766     if (isPageOpenedByDOMShowingInitialEmptyDocument())
     767    }
     768
     769    if (isPageOpenedByDOMShowingInitialEmptyDocument()) {
     770        RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i because it is showing the initial empty document", m_process->processIdentifier());
    767771        return false;
     772    }
    768773
    769774    auto* fromItem = navigation.fromItem();
    770775    if (!fromItem) {
    771         LOG(ProcessSwapping, "WebPageProxy %" PRIu64 " unable to create suspended page for process pid %i - No current back/forward item", pageID(), m_process->processIdentifier());
     776        RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i because the navigation does not have a fromItem", m_process->processIdentifier());
    772777        return false;
    773778    }
     
    775780    // If the source and the destination back / forward list items are the same, then this is a client-side redirect. In this case,
    776781    // there is no need to suspend the previous page as there will be no way to get back to it.
    777     if (fromItem == m_backForwardList->currentItem())
     782    if (fromItem == m_backForwardList->currentItem()) {
     783        RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i because this is a client-side redirect", m_process->processIdentifier());
    778784        return false;
    779 
     785    }
     786
     787    if (fromItem->url() != pageLoadState().url()) {
     788        RELEASE_LOG_ERROR_IF_ALLOWED(ProcessSwapping, "suspendCurrentPageIfPossible: Not suspending current page for process pid %i because fromItem's URL does not match the page URL.", m_process->processIdentifier());
     789        ASSERT_NOT_REACHED();
     790        return false;
     791    }
     792
     793    RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "suspendCurrentPageIfPossible: Suspending current page for process pid %i", m_process->processIdentifier());
    780794    auto suspendedPage = std::make_unique<SuspendedPageProxy>(*this, m_process.copyRef(), *fromItem, *mainFrameID);
    781795
     
    28372851
    28382852    if (m_provisionalPage) {
     2853        RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "continueNavigationInNewProcess: There is already a pending provisional load, cancelling it (provisonalNavigationID: %llu, navigationID: %llu)", m_provisionalPage->navigationID(), navigation.navigationID());
    28392854        if (m_provisionalPage->navigationID() != navigation.navigationID())
    28402855            m_provisionalPage->cancel();
     
    54765491void WebPageProxy::backForwardGoToItem(const BackForwardItemIdentifier& itemID, SandboxExtension::Handle& sandboxExtensionHandle)
    54775492{
     5493    // On process swap, we tell the previous process to ignore the load, which causes it so restore its current back forward item to its previous
     5494    // value. Since the load is really going on in a new provisional process, we want to ignore such requests from the committed process.
     5495    // Any real new load in the committed process would have cleared m_provisionalPage.
     5496    if (m_provisionalPage)
     5497        return;
     5498
    54785499    backForwardGoToItemShared(m_process.copyRef(), itemID, sandboxExtensionHandle);
    54795500}
  • trunk/Tools/ChangeLog

    r241602 r241606  
     12019-02-15  Chris Dumez  <cdumez@apple.com>
     2
     3        Regression(PSON) Navigating quickly back and forth can lead to getting 'about:blank' in the backforward list
     4        https://bugs.webkit.org/show_bug.cgi?id=194717
     5        <rdar://problem/47884404>
     6
     7        Reviewed by Brady Eidson.
     8
     9        Add API test coverage.
     10
     11        * TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm:
     12
    1132019-02-15  Youenn Fablet  <youenn@apple.com>
    214
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/ProcessSwapOnNavigation.mm

    r241556 r241606  
    20242024}
    20252025
     2026static void runQuickBackForwardNavigationTest(ShouldEnablePSON shouldEnablePSON)
     2027{
     2028    auto processPoolConfiguration = psonProcessPoolConfiguration();
     2029    processPoolConfiguration.get().processSwapsOnNavigation = shouldEnablePSON == ShouldEnablePSON::Yes ? YES : NO;
     2030    auto processPool = adoptNS([[WKProcessPool alloc] _initWithConfiguration:processPoolConfiguration.get()]);
     2031
     2032    auto webViewConfiguration = adoptNS([[WKWebViewConfiguration alloc] init]);
     2033    [webViewConfiguration setProcessPool:processPool.get()];
     2034    auto handler = adoptNS([[PSONScheme alloc] init]);
     2035    [webViewConfiguration setURLSchemeHandler:handler.get() forURLScheme:@"pson"];
     2036
     2037    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:webViewConfiguration.get()]);
     2038    auto delegate = adoptNS([[PSONNavigationDelegate alloc] init]);
     2039    [webView setNavigationDelegate:delegate.get()];
     2040
     2041    [webView configuration].preferences.safeBrowsingEnabled = NO;
     2042
     2043    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main1.html"]];
     2044    [webView loadRequest:request];
     2045
     2046    TestWebKitAPI::Util::run(&done);
     2047    done = false;
     2048
     2049    auto webkitPID = [webView _webProcessIdentifier];
     2050
     2051    request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.webkit.org/main2.html"]];
     2052    [webView loadRequest:request];
     2053
     2054    TestWebKitAPI::Util::run(&done);
     2055    done = false;
     2056
     2057    EXPECT_EQ(webkitPID, [webView _webProcessIdentifier]);
     2058
     2059    request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"pson://www.apple.com/main.html"]];
     2060    [webView loadRequest:request];
     2061
     2062    TestWebKitAPI::Util::run(&done);
     2063    done = false;
     2064
     2065    auto applePID = [webView _webProcessIdentifier];
     2066    if (shouldEnablePSON == ShouldEnablePSON::Yes)
     2067        EXPECT_NE(webkitPID, applePID);
     2068    else
     2069        EXPECT_EQ(webkitPID, applePID);
     2070
     2071    for (unsigned i = 0; i < 10; ++i) {
     2072        [webView goBack];
     2073        TestWebKitAPI::Util::sleep(0.1);
     2074        [webView goForward];
     2075        TestWebKitAPI::Util::spinRunLoop(0.1);
     2076    }
     2077
     2078    Vector<String> backForwardListURLs;
     2079    auto* backForwardList = [webView backForwardList];
     2080    for (unsigned i = 0; i < backForwardList.backList.count; ++i)
     2081        backForwardListURLs.append([backForwardList.backList[i].URL absoluteString]);
     2082    backForwardListURLs.append([backForwardList.currentItem.URL absoluteString]);
     2083    for (unsigned i = 0; i < backForwardList.forwardList.count; ++i)
     2084        backForwardListURLs.append([backForwardList.forwardList[i].URL absoluteString]);
     2085    EXPECT_EQ(3u, backForwardListURLs.size());
     2086    EXPECT_WK_STREQ("pson://www.webkit.org/main1.html", backForwardListURLs[0]);
     2087    EXPECT_WK_STREQ("pson://www.webkit.org/main2.html", backForwardListURLs[1]);
     2088    EXPECT_WK_STREQ("pson://www.apple.com/main.html", backForwardListURLs[2]);
     2089}
     2090
     2091TEST(ProcessSwap, QuickBackForwardNavigationWithoutPSON)
     2092{
     2093    runQuickBackForwardNavigationTest(ShouldEnablePSON::No);
     2094}
     2095
     2096TEST(ProcessSwap, QuickBackForwardNavigationWithPSON)
     2097{
     2098    runQuickBackForwardNavigationTest(ShouldEnablePSON::Yes);
     2099}
     2100
    20262101TEST(ProcessSwap, NavigationWithLockedHistoryWithoutPSON)
    20272102{
Note: See TracChangeset for help on using the changeset viewer.