Changeset 266634 in webkit


Ignore:
Timestamp:
Sep 4, 2020 1:17:21 PM (4 years ago)
Author:
sihui_liu@apple.com
Message:

Webpages flash when switching between windows
https://bugs.webkit.org/show_bug.cgi?id=216012

Reviewed by Tim Horton.

Source/WebCore/PAL:

  • pal/spi/cocoa/QuartzCoreSPI.h:

Source/WebKit:

Based on patches from Tim Horton. To avoid flash, we need to make sure view to be selected updates its content
before UI process commits its transaction. We did this by making UI process block until receiving a reply
DidUpdateActivityState from web process. However, web process did not make sure the reply would be sent after
normal rendering update and corresponding transasction commit. Instead, it flushed transactions in progress and
replied. To fix this, now we make web process reply in transaction commit handler after rendering update, if
there is an active transaction.

In the switching case, view to be unselected will detach from root layer in its web process, which makes its
content empty. This change is independent from the UI process commit, so we want this to happen after UI process
commits (which submits the view hierachy change). Otherwise, empty content(white flash) on the unselected view
will be displayed. To fix this, we let UI process send the activity state change message in transaction commit
handler, if there is an active transaction.

  • UIProcess/Cocoa/WebPageProxyCocoa.mm:

(WebKit::WebPageProxy::scheduleActivityStateUpdate):

  • UIProcess/WebPageProxy.cpp:

(WebKit::WebPageProxy::setSuppressVisibilityUpdates):
(WebKit::WebPageProxy::activityStateDidChange):
(WebKit::WebPageProxy::dispatchActivityStateChange):

  • UIProcess/WebPageProxy.h:
  • WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.h:
  • WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm:

(WebKit::TiledCoreAnimationDrawingArea::TiledCoreAnimationDrawingArea):
(WebKit::TiledCoreAnimationDrawingArea::updateRendering):
(WebKit::TiledCoreAnimationDrawingArea::handleActivityStateChangeCallbacks):
(WebKit::TiledCoreAnimationDrawingArea::handleActivityStateChangeCallbacksIfNeeded):
(WebKit::TiledCoreAnimationDrawingArea::activityStateDidChange):
(WebKit::TiledCoreAnimationDrawingArea::didUpdateActivityStateTimerFired): Deleted.

Location:
trunk/Source
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/PAL/ChangeLog

    r266133 r266634  
     12020-09-04  Sihui Liu  <sihui_liu@apple.com>
     2
     3        Webpages flash when switching between windows
     4        https://bugs.webkit.org/show_bug.cgi?id=216012
     5
     6        Reviewed by Tim Horton.
     7
     8        * pal/spi/cocoa/QuartzCoreSPI.h:
     9
    1102020-08-25  Alex Christensen  <achristensen@webkit.org>
    211
  • trunk/Source/WebCore/PAL/pal/spi/cocoa/QuartzCoreSPI.h

    r261153 r266634  
    144144+ (CATransactionPhase)currentPhase;
    145145+ (void)synchronize;
     146+ (uint32_t)currentState;
    146147@end
    147148
  • trunk/Source/WebKit/ChangeLog

    r266613 r266634  
     12020-09-04  Sihui Liu  <sihui_liu@apple.com>
     2
     3        Webpages flash when switching between windows
     4        https://bugs.webkit.org/show_bug.cgi?id=216012
     5
     6        Reviewed by Tim Horton.
     7
     8        Based on patches from Tim Horton. To avoid flash, we need to make sure view to be selected updates its content
     9        before UI process commits its transaction. We did this by making UI process block until receiving a reply
     10        DidUpdateActivityState from web process. However, web process did not make sure the reply would be sent after
     11        normal rendering update and corresponding transasction commit. Instead, it flushed transactions in progress and
     12        replied. To fix this, now we make web process reply in transaction commit handler after rendering update, if
     13        there is an active transaction.
     14
     15        In the switching case, view to be unselected will detach from root layer in its web process, which makes its
     16        content empty. This change is independent from the UI process commit, so we want this to happen after UI process
     17        commits (which submits the view hierachy change). Otherwise, empty content(white flash) on the unselected view
     18        will be displayed. To fix this, we let UI process send the activity state change message in transaction commit
     19        handler, if there is an active transaction.
     20
     21        * UIProcess/Cocoa/WebPageProxyCocoa.mm:
     22        (WebKit::WebPageProxy::scheduleActivityStateUpdate):
     23        * UIProcess/WebPageProxy.cpp:
     24        (WebKit::WebPageProxy::setSuppressVisibilityUpdates):
     25        (WebKit::WebPageProxy::activityStateDidChange):
     26        (WebKit::WebPageProxy::dispatchActivityStateChange):
     27        * UIProcess/WebPageProxy.h:
     28        * WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.h:
     29        * WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm:
     30        (WebKit::TiledCoreAnimationDrawingArea::TiledCoreAnimationDrawingArea):
     31        (WebKit::TiledCoreAnimationDrawingArea::updateRendering):
     32        (WebKit::TiledCoreAnimationDrawingArea::handleActivityStateChangeCallbacks):
     33        (WebKit::TiledCoreAnimationDrawingArea::handleActivityStateChangeCallbacksIfNeeded):
     34        (WebKit::TiledCoreAnimationDrawingArea::activityStateDidChange):
     35        (WebKit::TiledCoreAnimationDrawingArea::didUpdateActivityStateTimerFired): Deleted.
     36
    1372020-09-04  Per Arne Vollan  <pvollan@apple.com>
    238
  • trunk/Source/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm

    r266412 r266634  
    4646#import <WebCore/LocalCurrentGraphicsContext.h>
    4747#import <WebCore/NotImplemented.h>
     48#import <WebCore/RunLoopObserver.h>
    4849#import <WebCore/SearchPopupMenuCocoa.h>
    4950#import <WebCore/TextAlternativeWithRange.h>
    5051#import <WebCore/ValidationBubble.h>
    5152#import <pal/spi/cocoa/NEFilterSourceSPI.h>
     53#import <pal/spi/cocoa/QuartzCoreSPI.h>
    5254#import <wtf/BlockPtr.h>
    5355#import <wtf/SoftLinking.h>
     
    488490#endif // HAVE(QUICKLOOK_THUMBNAILING)
    489491
     492void WebPageProxy::scheduleActivityStateUpdate()
     493{
     494    bool hasScheduledObserver = m_activityStateChangeDispatcher->isScheduled();
     495    bool hasActiveCATransaction = [CATransaction currentState];
     496
     497    if (hasScheduledObserver && hasActiveCATransaction) {
     498        ASSERT(m_hasScheduledActivityStateUpdate);
     499        m_hasScheduledActivityStateUpdate = false;
     500        m_activityStateChangeDispatcher->invalidate();
     501    }
     502
     503    if (m_hasScheduledActivityStateUpdate)
     504        return;
     505    m_hasScheduledActivityStateUpdate = true;
     506
     507    // If there is an active transaction, we need to dispatch the update after the transaction is committed,
     508    // to avoid flash caused by web process setting root layer too early.
     509    // If there is no active transaction, likely there is no root layer change or change is committed,
     510    // then schedule dispatch on runloop observer to collect changes in the same runloop cycle before dispatching.
     511    if (hasActiveCATransaction) {
     512        [CATransaction addCommitHandler:[weakThis = makeWeakPtr(*this)] {
     513            auto protectedThis = makeRefPtr(weakThis.get());
     514            if (!protectedThis)
     515                return;
     516
     517            protectedThis->dispatchActivityStateChange();
     518        } forPhase:kCATransactionPhasePostCommit];
     519        return;
     520    }
     521
     522    m_activityStateChangeDispatcher->schedule();
     523}
     524
    490525} // namespace WebKit
    491526
  • trunk/Source/WebKit/UIProcess/WebPageProxy.cpp

    r266412 r266634  
    18571857    if (!m_suppressVisibilityUpdates) {
    18581858#if PLATFORM(COCOA)
    1859         m_activityStateChangeDispatcher->schedule();
     1859        scheduleActivityStateUpdate();
    18601860#else
    18611861        dispatchActivityStateChange();
     
    19091909        return;
    19101910    }
    1911     m_activityStateChangeDispatcher->schedule();
     1911    scheduleActivityStateUpdate();
    19121912#else
    19131913    UNUSED_PARAM(dispatchMode);
     
    19381938{
    19391939#if PLATFORM(COCOA)
    1940     m_activityStateChangeDispatcher->invalidate();
     1940    if (m_activityStateChangeDispatcher->isScheduled())
     1941        m_activityStateChangeDispatcher->invalidate();
     1942    m_hasScheduledActivityStateUpdate = false;
    19411943#endif
    19421944
  • trunk/Source/WebKit/UIProcess/WebPageProxy.h

    r266576 r266634  
    899899    bool scrollPerformanceDataCollectionEnabled() const { return m_scrollPerformanceDataCollectionEnabled; }
    900900    RemoteLayerTreeScrollingPerformanceData* scrollingPerformanceData() { return m_scrollingPerformanceData.get(); }
     901
     902    void scheduleActivityStateUpdate();
    901903#endif // PLATFORM(COCOA)
    902904
     
    27212723    std::unique_ptr<RemoteLayerTreeScrollingPerformanceData> m_scrollingPerformanceData;
    27222724    bool m_scrollPerformanceDataCollectionEnabled { false };
     2725
     2726    bool m_hasScheduledActivityStateUpdate { false };
    27232727#endif
    27242728    UserObservablePageCounter::Token m_pageIsUserObservableCount;
  • trunk/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.h

    r266412 r266634  
    8585
    8686    void activityStateDidChange(OptionSet<WebCore::ActivityState::Flag> changed, ActivityStateChangeID, const Vector<CallbackID>&) override;
    87     void didUpdateActivityStateTimerFired();
    8887
    8988    void attachViewOverlayGraphicsLayer(WebCore::GraphicsLayer*) override;
     
    109108    void sendEnterAcceleratedCompositingModeIfNeeded() override;
    110109    void sendDidFirstLayerFlushIfNeeded();
     110    void handleActivityStateChangeCallbacksIfNeeded();
     111    void handleActivityStateChangeCallbacks();
    111112
    112113    void adjustTransientZoom(double scale, WebCore::FloatPoint origin) override;
     
    152153    WebCore::FloatPoint m_transientZoomOrigin;
    153154
    154     RunLoop::Timer<TiledCoreAnimationDrawingArea> m_sendDidUpdateActivityStateTimer;
    155155    Vector<CallbackID> m_nextActivityStateChangeCallbackIDs;
    156156    ActivityStateChangeID m_activityStateChangeID { ActivityStateChangeAsynchronous };
     
    170170    bool m_needsSendEnterAcceleratedCompositingMode { true };
    171171    bool m_needsSendDidFirstLayerFlush { true };
     172    bool m_shouldHandleActivityStateChangeCallbacks { false };
    172173};
    173174
  • trunk/Source/WebKit/WebProcess/WebPage/mac/TiledCoreAnimationDrawingArea.mm

    r266412 r266634  
    7474TiledCoreAnimationDrawingArea::TiledCoreAnimationDrawingArea(WebPage& webPage, const WebPageCreationParameters& parameters)
    7575    : DrawingArea(DrawingAreaTypeTiledCoreAnimation, parameters.drawingAreaIdentifier, webPage)
    76     , m_sendDidUpdateActivityStateTimer(RunLoop::main(), this, &TiledCoreAnimationDrawingArea::didUpdateActivityStateTimerFired)
    7776    , m_isPaintingSuspended(!(parameters.activityState & ActivityState::IsVisible))
    7877{
     
    493492
    494493        sendDidFirstLayerFlushIfNeeded();
     494        handleActivityStateChangeCallbacksIfNeeded();
    495495        invalidateRenderingUpdateRunLoopObserver();
    496496    }
     497}
     498
     499void TiledCoreAnimationDrawingArea::handleActivityStateChangeCallbacks()
     500{
     501    if (!m_shouldHandleActivityStateChangeCallbacks)
     502        return;
     503    m_shouldHandleActivityStateChangeCallbacks = false;
     504
     505    if (m_activityStateChangeID != ActivityStateChangeAsynchronous)
     506        m_webPage.send(Messages::WebPageProxy::DidUpdateActivityState());
     507
     508    for (auto& callbackID : m_nextActivityStateChangeCallbackIDs)
     509        m_webPage.send(Messages::WebPageProxy::VoidCallback(callbackID));
     510    m_nextActivityStateChangeCallbackIDs.clear();
     511
     512    m_activityStateChangeID = ActivityStateChangeAsynchronous;
     513}
     514
     515void TiledCoreAnimationDrawingArea::handleActivityStateChangeCallbacksIfNeeded()
     516{
     517    if (!m_shouldHandleActivityStateChangeCallbacks)
     518        return;
     519
     520    // If there is no active transaction, likely there is no layer change or change is committed,
     521    // perform the callbacks immediately, which may unblock UI process.
     522    if (![CATransaction currentState]) {
     523        handleActivityStateChangeCallbacks();
     524        return;
     525    }
     526
     527    [CATransaction addCommitHandler:[weakThis = makeWeakPtr(*this)] {
     528        if (!weakThis)
     529            return;
     530
     531        auto protectedPage = makeRef(weakThis->m_webPage);
     532        auto* drawingArea = static_cast<TiledCoreAnimationDrawingArea*>(protectedPage->drawingArea());
     533        ASSERT(weakThis.get() == drawingArea);
     534        if (drawingArea != weakThis.get())
     535            return;
     536
     537        drawingArea->handleActivityStateChangeCallbacks();
     538    } forPhase:kCATransactionPhasePostCommit];
    497539}
    498540
     
    510552
    511553    if (m_activityStateChangeID != ActivityStateChangeAsynchronous || !m_nextActivityStateChangeCallbackIDs.isEmpty())
    512         m_sendDidUpdateActivityStateTimer.startOneShot(0_s);
    513 }
    514 
    515 void TiledCoreAnimationDrawingArea::didUpdateActivityStateTimerFired()
    516 {
    517     [CATransaction flush];
    518 
    519     if (m_activityStateChangeID != ActivityStateChangeAsynchronous)
    520         m_webPage.send(Messages::WebPageProxy::DidUpdateActivityState());
    521 
    522     for (const auto& callbackID : m_nextActivityStateChangeCallbackIDs)
    523         m_webPage.send(Messages::WebPageProxy::VoidCallback(callbackID));
    524 
    525     m_nextActivityStateChangeCallbackIDs.clear();
    526     m_activityStateChangeID = ActivityStateChangeAsynchronous;
     554        m_shouldHandleActivityStateChangeCallbacks = true;
     555
     556    scheduleRenderingUpdate();
    527557}
    528558
Note: See TracChangeset for help on using the changeset viewer.