Changeset 244493 in webkit


Ignore:
Timestamp:
Apr 21, 2019 1:14:04 PM (5 years ago)
Author:
Chris Dumez
Message:

Regression(r243767) WebFrame::m_navigationIsContinuingInAnotherProcess flag is never reset
https://bugs.webkit.org/show_bug.cgi?id=197144

Reviewed by Darin Adler.

WebFrame::m_navigationIsContinuingInAnotherProcess flag is never reset since it was introduced in
r243767. This leads to leaking Navigation objects in the UIProcess when reusing a previously
suspended process because such process will no longer send the DidDestroyNavigation IPC.

It turns out that resetting the flags causes API tests such as ProcessSwap.QuickBackForwardNavigationWithPSON
to ASSERT. This is because when the UIProcess quickly navigate back and forth without waiting for policy
decisions, we may end up getting the policy decision for a particular navigation *after* we've sent the
DidDestroyNavigation.

As a result, this patch reverts r243767 and fixes in the assertion in http/tests/adClickAttribution/store-ad-click-attribution.html
another way. We initially assumed that the logic in WebPageProxy::didDestroyNavigation() was failing to
ignore the DidDestroyNavigation from the previous process after a swap due to a race, maybe because it was
sometimes received too late and m_provisionalPage was already cleared. However, this would not make sense
since the test is crashing consistently and the page would no longer be able to receive IPC from the
previous process *after* we've committed the provisional process/page.

The real issue was that the DidDestroyNavigation IPC was received *before* we could construct the
provisional page, which is why the logic in WebPageProxy::didDestroyNavigation() was failing to ignore
the bad IPC. In WebPageProxy::receivedNavigationPolicyDecision(), we were calling receivedPolicyDecision()
(which would send the DidReceivePolicyDecision to the previous WebProcess) and then continueNavigationInNewProcess()
in order to construct the provisional page. I personally did not expect we could receive IPC between the
calls to receivedNavigationPolicyDecision() and receivedPolicyDecision(), since we are not yielding and since
the DidReceivePolicyDecision IPC is asynchronous. However, this is exactly what was happening in the context
of this test. The reason is that the DidReceivePolicyDecision IPC was getting wrapped in a synchronous message
and sent as synchronous message due to the Connection::m_inDispatchMessageMarkedToUseFullySynchronousModeForTesting
flag which seems to get set in the test due to some EventSender IPC. I believe this is because the test uses
EventSender to do a click on a link which triggers the navigation.

To address the issue, I now call receivedNavigationPolicyDecision() *after* continueNavigationInNewProcess()
to make sure that we always start the provisional load in the new process before we tell the previous process
to stop loading. This way, there is no way we get IPC from the previous process about the current navigation
before we have a provisional page.

  • UIProcess/WebPageProxy.cpp:

(WebKit::WebPageProxy::receivedNavigationPolicyDecision):
(WebKit::WebPageProxy::didDestroyNavigation):

  • WebProcess/WebPage/WebFrame.cpp:

(WebKit::WebFrame::didReceivePolicyDecision):
(WebKit::WebFrame::documentLoaderDetached):

  • WebProcess/WebPage/WebFrame.h:
Location:
trunk/Source/WebKit
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r244484 r244493  
     12019-04-21  Chris Dumez  <cdumez@apple.com>
     2
     3        Regression(r243767) WebFrame::m_navigationIsContinuingInAnotherProcess flag is never reset
     4        https://bugs.webkit.org/show_bug.cgi?id=197144
     5
     6        Reviewed by Darin Adler.
     7
     8        WebFrame::m_navigationIsContinuingInAnotherProcess flag is never reset since it was introduced in
     9        r243767. This leads to leaking Navigation objects in the UIProcess when reusing a previously
     10        suspended process because such process will no longer send the DidDestroyNavigation IPC.
     11
     12        It turns out that resetting the flags causes API tests such as ProcessSwap.QuickBackForwardNavigationWithPSON
     13        to ASSERT. This is because when the UIProcess quickly navigate back and forth without waiting for policy
     14        decisions, we may end up getting the policy decision for a particular navigation *after* we've sent the
     15        DidDestroyNavigation.
     16
     17        As a result, this patch reverts r243767 and fixes in the assertion in http/tests/adClickAttribution/store-ad-click-attribution.html
     18        another way. We initially assumed that the logic in WebPageProxy::didDestroyNavigation() was failing to
     19        ignore the DidDestroyNavigation from the previous process after a swap due to a race, maybe because it was
     20        sometimes received too late and m_provisionalPage was already cleared. However, this would not make sense
     21        since the test is crashing consistently and the page would no longer be able to receive IPC from the
     22        previous process *after* we've committed the provisional process/page.
     23
     24        The real issue was that the DidDestroyNavigation IPC was received *before* we could construct the
     25        provisional page, which is why the logic in WebPageProxy::didDestroyNavigation() was failing to ignore
     26        the bad IPC. In WebPageProxy::receivedNavigationPolicyDecision(), we were calling receivedPolicyDecision()
     27        (which would send the DidReceivePolicyDecision to the previous WebProcess) and then continueNavigationInNewProcess()
     28        in order to construct the provisional page. I personally did not expect we could receive IPC between the
     29        calls to receivedNavigationPolicyDecision() and receivedPolicyDecision(), since we are not yielding and since
     30        the DidReceivePolicyDecision IPC is asynchronous. However, this is exactly what was happening in the context
     31        of this test. The reason is that the DidReceivePolicyDecision IPC was getting wrapped in a synchronous message
     32        and sent as synchronous message due to the Connection::m_inDispatchMessageMarkedToUseFullySynchronousModeForTesting
     33        flag which seems to get set in the test due to some EventSender IPC. I believe this is because the test uses
     34        EventSender to do a click on a link which triggers the navigation.
     35
     36        To address the issue, I now call receivedNavigationPolicyDecision() *after* continueNavigationInNewProcess()
     37        to make sure that we always start the provisional load in the new process before we tell the previous process
     38        to stop loading. This way, there is no way we get IPC from the previous process about the current navigation
     39        before we have a provisional page.
     40
     41        * UIProcess/WebPageProxy.cpp:
     42        (WebKit::WebPageProxy::receivedNavigationPolicyDecision):
     43        (WebKit::WebPageProxy::didDestroyNavigation):
     44        * WebProcess/WebPage/WebFrame.cpp:
     45        (WebKit::WebFrame::didReceivePolicyDecision):
     46        (WebKit::WebFrame::documentLoaderDetached):
     47        * WebProcess/WebPage/WebFrame.h:
     48
    1492019-04-20  Chris Dumez  <cdumez@apple.com>
    250
  • trunk/Source/WebKit/UIProcess/WebPageProxy.cpp

    r244464 r244493  
    27902790            RELEASE_LOG_IF_ALLOWED(ProcessSwapping, "decidePolicyForNavigationAction: keep using process %i for navigation, reason: %{public}s", processIdentifier(), reason.utf8().data());
    27912791
     2792        if (shouldProcessSwap) {
     2793            // FIXME: Architecturally we do not currently support multiple WebPage's with the same ID in a given WebProcess.
     2794            // In the case where the destination WebProcess has a SuspendedPageProxy for this WebPage, we should have thrown
     2795            // it away to support WebProcess re-use.
     2796            ASSERT(destinationSuspendedPage || !process().processPool().hasSuspendedPageFor(processForNavigation, *this));
     2797
     2798            auto suspendedPage = destinationSuspendedPage ? process().processPool().takeSuspendedPage(*destinationSuspendedPage) : nullptr;
     2799            if (suspendedPage && suspendedPage->failedToSuspend())
     2800                suspendedPage = nullptr;
     2801
     2802            continueNavigationInNewProcess(navigation, WTFMove(suspendedPage), WTFMove(processForNavigation), processSwapRequestedByClient, WTFMove(data));
     2803        }
     2804
    27922805        receivedPolicyDecision(policyAction, navigation.ptr(), shouldProcessSwap ? WTF::nullopt : WTFMove(data), WTFMove(sender), shouldProcessSwap ? WillContinueLoadInNewProcess::Yes : WillContinueLoadInNewProcess::No);
    2793 
    2794         if (!shouldProcessSwap)
    2795             return;
    2796 
    2797         // FIXME: Architecturally we do not currently support multiple WebPage's with the same ID in a given WebProcess.
    2798         // In the case where the destination WebProcess has a SuspendedPageProxy for this WebPage, we should have thrown
    2799         // it away to support WebProcess re-use.
    2800         ASSERT(destinationSuspendedPage || !process().processPool().hasSuspendedPageFor(processForNavigation, *this));
    2801 
    2802         auto suspendedPage = destinationSuspendedPage ? process().processPool().takeSuspendedPage(*destinationSuspendedPage) : nullptr;
    2803         if (suspendedPage && suspendedPage->failedToSuspend())
    2804             suspendedPage = nullptr;
    2805 
    2806         continueNavigationInNewProcess(navigation, WTFMove(suspendedPage), WTFMove(processForNavigation), processSwapRequestedByClient, WTFMove(data));
    28072806    });
    28082807}
     
    38683867{
    38693868    PageClientProtector protector(pageClient());
     3869
     3870    // On process-swap, the previous process tries to destroy the navigation but the provisional process is actually taking over the navigation.
     3871    if (m_provisionalPage && m_provisionalPage->navigationID() == navigationID)
     3872        return;
    38703873
    38713874    // FIXME: Message check the navigationID.
  • trunk/Source/WebKit/WebProcess/WebPage/WebFrame.cpp

    r243767 r244493  
    279279    }
    280280
    281     if (action == PolicyAction::StopAllLoads)
    282         m_navigationIsContinuingInAnotherProcess = true;
    283 
    284281    function(action, identifier);
    285282}
     
    816813void WebFrame::documentLoaderDetached(uint64_t navigationID)
    817814{
    818     if (m_navigationIsContinuingInAnotherProcess)
    819         return;
    820815    if (auto* page = this->page())
    821816        page->send(Messages::WebPageProxy::DidDestroyNavigation(navigationID));
  • trunk/Source/WebKit/WebProcess/WebPage/WebFrame.h

    r243767 r244493  
    189189   
    190190    uint64_t m_frameID { 0 };
    191     bool m_navigationIsContinuingInAnotherProcess { false };
    192191
    193192#if PLATFORM(IOS_FAMILY)
Note: See TracChangeset for help on using the changeset viewer.