Changeset 201171 in webkit


Ignore:
Timestamp:
May 19, 2016 10:58:16 AM (8 years ago)
Author:
BJ Burg
Message:

Web Inspector: timelines should not update via requestAnimationFrame unless Web Inspector is visible
https://bugs.webkit.org/show_bug.cgi?id=157897
<rdar://problem/26330802>

Reviewed by Timothy Hatcher.

Source/WebInspectorUI:

The timelines overview tries to animate using requestAnimationFrame, even if the
inspector frontend is not really visible. When it does this, requestAnimationFrame
simply stalls out until the inspector becomes visible. If a recording is started
while the inspector is not visible, then when it is shown again, the timeline will
start to animate from 0s instead of the current time. This happens because the
requestAnimationFrame was requested when the current time actually was 0, and it
finally executes some time later, when the current time is no longer accurate.
Since the timelines animate by calculating time elapsed since the previous frame
rather than using event timestamps, there is no way for the timelines to skip forward
in their animations in scenarios where the current time becomes arbitrarily skewed.

To fix this, consider the visibility state of the frontend as reported by the UIProcess.
Fire a global notification when visibility state changes, and start or stop updating
the current time as the frontend becomes visible or not shown.

This does not affect most other uses of requestAnimationFrame, which are used as
timers to call updateLayout at an appropriate time. The timelines case is different
because the current time is fixed prior to requesting an animation frame, and
later animation frames are only triggered by earlier requests, so there's nothing to
coalesce.

  • UserInterface/Base/Main.js:

(WebInspector.loaded): Initialize WebInspector.visible.

  • UserInterface/Base/Object.js: Add new event.
  • UserInterface/Protocol/InspectorFrontendAPI.js:

(InspectorFrontendAPI.setIsVisible): Added.

  • UserInterface/Test/Test.js:

(WebInspector.updateVisibilityState): Add a stub.

  • UserInterface/Views/TimelineRecordingContentView.js:

(WebInspector.TimelineRecordingContentView):
(WebInspector.TimelineRecordingContentView.prototype._inspectorVisibilityStateChanged):
If visibility state changes while capturing, then start or stop updating the
current time as appropriate. Otherwise, refresh the timelines with updated
times so that they know about the recording's updated start/current/end time.

(WebInspector.TimelineRecordingContentView.prototype._startUpdatingCurrentTime):
Bail out if the Web Inspector frontend is not visible to the user and won't be
able to service requestAnimationFrames immediately.

Source/WebKit2:

The UIProcess needs to notify the Inspector frontend when it is truly visible.
The frontend can't use document.visibilityState because it doesn't seem to work
if the inspector frontend's WKWebView is created but not attached to a window.

  • UIProcess/WebInspectorProxy.cpp:

(WebKit::WebInspectorProxy::open):
(WebKit::WebInspectorProxy::didClose):
Send visibility updates to the inspector process when the inspector becomes
"visible" or "not visible". It becomes visible if it is attached to the
inspected page's window, or gets its own native window.

  • WebProcess/WebPage/WebInspectorUI.cpp:

(WebKit::WebInspectorUI::frontendLoaded):
(WebKit::WebInspectorUI::setDockingUnavailable):
(WebKit::WebInspectorUI::setIsVisible):
Call InspectorFrontendAPI.updateVisibilityState to let the frontend know.

  • WebProcess/WebPage/WebInspectorUI.h:
  • WebProcess/WebPage/WebInspectorUI.messages.in:

Add new message.

Location:
trunk/Source
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebInspectorUI/ChangeLog

    r201126 r201171  
     12016-05-19  Brian Burg  <bburg@apple.com>
     2
     3        Web Inspector: timelines should not update via requestAnimationFrame unless Web Inspector is visible
     4        https://bugs.webkit.org/show_bug.cgi?id=157897
     5        <rdar://problem/26330802>
     6
     7        Reviewed by Timothy Hatcher.
     8
     9        The timelines overview tries to animate using requestAnimationFrame, even if the
     10        inspector frontend is not really visible. When it does this, requestAnimationFrame
     11        simply stalls out until the inspector becomes visible. If a recording is started
     12        while the inspector is not visible, then when it is shown again, the timeline will
     13        start to animate from 0s instead of the current time. This happens because the
     14        requestAnimationFrame was requested when the current time actually was 0, and it
     15        finally executes some time later, when the current time is no longer accurate.
     16        Since the timelines animate by calculating time elapsed since the previous frame
     17        rather than using event timestamps, there is no way for the timelines to skip forward
     18        in their animations in scenarios where the current time becomes arbitrarily skewed.
     19
     20        To fix this, consider the visibility state of the frontend as reported by the UIProcess.
     21        Fire a global notification when visibility state changes, and start or stop updating
     22        the current time as the frontend becomes visible or not shown.
     23
     24        This does not affect most other uses of requestAnimationFrame, which are used as
     25        timers to call updateLayout at an appropriate time. The timelines case is different
     26        because the current time is fixed prior to requesting an animation frame, and
     27        later animation frames are only triggered by earlier requests, so there's nothing to
     28        coalesce.
     29
     30        * UserInterface/Base/Main.js:
     31        (WebInspector.loaded): Initialize WebInspector.visible.
     32
     33        * UserInterface/Base/Object.js: Add new event.
     34
     35        * UserInterface/Protocol/InspectorFrontendAPI.js:
     36        (InspectorFrontendAPI.setIsVisible): Added.
     37
     38        * UserInterface/Test/Test.js:
     39        (WebInspector.updateVisibilityState): Add a stub.
     40
     41        * UserInterface/Views/TimelineRecordingContentView.js:
     42        (WebInspector.TimelineRecordingContentView):
     43        (WebInspector.TimelineRecordingContentView.prototype._inspectorVisibilityStateChanged):
     44        If visibility state changes while capturing, then start or stop updating the
     45        current time as appropriate. Otherwise, refresh the timelines with updated
     46        times so that they know about the recording's updated start/current/end time.
     47
     48        (WebInspector.TimelineRecordingContentView.prototype._startUpdatingCurrentTime):
     49        Bail out if the Web Inspector frontend is not visible to the user and won't be
     50        able to service requestAnimationFrames immediately.
     51
    1522016-05-18  Timothy Hatcher  <timothy@apple.com>
    253
  • trunk/Source/WebInspectorUI/UserInterface/Base/Main.js

    r200332 r201171  
    177177    };
    178178
     179    this.visible = false;
     180
    179181    this._windowKeydownListeners = [];
    180182};
     
    712714};
    713715
     716WebInspector.updateVisibilityState = function(visible)
     717{
     718    this.visible = visible;
     719    this.notifications.dispatchEventToListeners(WebInspector.Notification.VisibilityStateDidChange);
     720}
     721
    714722WebInspector.handlePossibleLinkClick = function(event, frame, alwaysOpenExternally)
    715723{
  • trunk/Source/WebInspectorUI/UserInterface/Base/Object.js

    r195581 r201171  
    217217    TabTypesChanged: "tab-types-changed",
    218218    DebugUIEnabledDidChange: "debug-ui-enabled-did-change",
     219    VisibilityStateDidChange: "visibility-state-did-change",
    219220};
  • trunk/Source/WebInspectorUI/UserInterface/Protocol/InspectorFrontendAPI.js

    r201101 r201171  
    6767    {
    6868        WebInspector.updateDockedState(side);
     69    },
     70
     71    setIsVisible: function(visible)
     72    {
     73        WebInspector.updateVisibilityState(visible);
    6974    },
    7075
  • trunk/Source/WebInspectorUI/UserInterface/Test/Test.js

    r197827 r201171  
    9696WebInspector.updateDockedState = () => {};
    9797WebInspector.updateDockingAvailability = () => {};
     98WebInspector.updateVisibilityState = () => {};
    9899
    99100window.InspectorTest = new FrontendTestHarness();
  • trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRecordingContentView.js

    r201082 r201171  
    9292        WebInspector.TimelineView.addEventListener(WebInspector.TimelineView.Event.RecordWasFiltered, this._recordWasFiltered, this);
    9393
     94        WebInspector.notifications.addEventListener(WebInspector.Notification.VisibilityStateDidChange, this._inspectorVisibilityStateChanged, this);
     95
    9496        for (let instrument of this._recording.instruments)
    9597            this._instrumentAdded(instrument);
     
    342344    }
    343345
     346    _inspectorVisibilityStateChanged()
     347    {
     348        if (WebInspector.timelineManager.activeRecording !== this._recording)
     349            return;
     350
     351        // Stop updating since the results won't be rendered anyway.
     352        if (!WebInspector.visible && this._updating) {
     353            this._stopUpdatingCurrentTime();
     354            return;
     355        }
     356
     357        // Nothing else to do if the current time was not being updated.
     358        if (!WebInspector.visible)
     359            return;
     360
     361        let {startTime, endTime} = this.representedObject;
     362        if (!WebInspector.timelineManager.isCapturing()) {
     363            // Force the overview to render data from the entire recording.
     364            // This is necessary if the recording was started when the inspector was not
     365            // visible because the views were never updated with currentTime/endTime.
     366            this._updateTimes(startTime, endTime, endTime);
     367            return;
     368        }
     369
     370        this._startUpdatingCurrentTime(endTime);
     371    }
     372
    344373    _update(timestamp)
    345374    {
     
    402431            return;
    403432
    404         if (typeof startTime === "number")
     433        // Don't update the current time if the Inspector is not visible, as the requestAnimationFrames won't work.
     434        if (!WebInspector.visible)
     435            return;
     436
     437        if (typeof startTime === "number" && !isNaN(this._currentTime))
    405438            this._currentTime = startTime;
    406         else if (!isNaN(this._currentTime)) {
     439        else {
    407440            // This happens when you stop and later restart recording.
    408441            // COMPATIBILITY (iOS 9): Timeline.recordingStarted events did not include a timestamp.
  • trunk/Source/WebKit2/ChangeLog

    r201168 r201171  
     12016-05-19  Brian Burg  <bburg@apple.com>
     2
     3        Web Inspector: timelines should not update via requestAnimationFrame unless Web Inspector is visible
     4        https://bugs.webkit.org/show_bug.cgi?id=157897
     5        <rdar://problem/26330802>
     6
     7        Reviewed by Timothy Hatcher.
     8
     9        The UIProcess needs to notify the Inspector frontend when it is truly visible.
     10        The frontend can't use document.visibilityState because it doesn't seem to work
     11        if the inspector frontend's WKWebView is created but not attached to a window.
     12
     13        * UIProcess/WebInspectorProxy.cpp:
     14        (WebKit::WebInspectorProxy::open):
     15        (WebKit::WebInspectorProxy::didClose):
     16        Send visibility updates to the inspector process when the inspector becomes
     17        "visible" or "not visible". It becomes visible if it is attached to the
     18        inspected page's window, or gets its own native window.
     19
     20        * WebProcess/WebPage/WebInspectorUI.cpp:
     21        (WebKit::WebInspectorUI::frontendLoaded):
     22        (WebKit::WebInspectorUI::setDockingUnavailable):
     23        (WebKit::WebInspectorUI::setIsVisible):
     24        Call InspectorFrontendAPI.updateVisibilityState to let the frontend know.
     25
     26        * WebProcess/WebPage/WebInspectorUI.h:
     27        * WebProcess/WebPage/WebInspectorUI.messages.in:
     28        Add new message.
     29
    1302016-05-19  Brian Burg  <bburg@apple.com>
    231
  • trunk/Source/WebKit2/UIProcess/WebInspectorProxy.cpp

    r201101 r201171  
    582582
    583583    m_isVisible = true;
     584    m_inspectorPage->process().send(Messages::WebInspectorUI::SetIsVisible(m_isVisible), m_inspectorPage->pageID());
    584585
    585586    platformOpen();
     
    590591    if (!m_inspectorPage)
    591592        return;
    592 
    593     m_inspectorPage->process().removeMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID());
    594593
    595594    m_isVisible = false;
     
    597596    m_showMessageSent = false;
    598597    m_ignoreFirstBringToFront = false;
     598
     599    m_inspectorPage->process().send(Messages::WebInspectorUI::SetIsVisible(m_isVisible), m_inspectorPage->pageID());
     600    m_inspectorPage->process().removeMessageReceiver(Messages::WebInspectorProxy::messageReceiverName(), m_inspectedPage->pageID());
    599601
    600602    if (m_isAttached)
  • trunk/Source/WebKit2/WebProcess/WebPage/WebInspectorUI.cpp

    r201101 r201171  
    9696    setDockingUnavailable(m_dockingUnavailable);
    9797    setDockSide(m_dockSide);
     98    setIsVisible(m_isVisible);
    9899
    99100    WebProcess::singleton().parentProcessConnection()->send(Messages::WebInspectorProxy::FrontendLoaded(), m_inspectedPageIdentifier);
     
    179180void WebInspectorUI::setDockingUnavailable(bool unavailable)
    180181{
     182    m_dockingUnavailable = unavailable;
     183
    181184    m_frontendAPIDispatcher.dispatchCommand(ASCIILiteral("setDockingUnavailable"), unavailable);
    182     m_dockingUnavailable = unavailable;
     185}
     186
     187void WebInspectorUI::setIsVisible(bool visible)
     188{
     189    m_isVisible = visible;
     190
     191    m_frontendAPIDispatcher.dispatchCommand(ASCIILiteral("setIsVisible"), visible);
    183192}
    184193
  • trunk/Source/WebKit2/WebProcess/WebPage/WebInspectorUI.h

    r201101 r201171  
    7575    void setDockingUnavailable(bool);
    7676
     77    void setIsVisible(bool);
     78
    7779    void didSave(const String& url);
    7880    void didAppend(const String& url);
     
    127129    bool m_underTest { false };
    128130    bool m_dockingUnavailable { false };
     131    bool m_isVisible { false };
    129132    DockSide m_dockSide { DockSide::Undocked };
    130133    unsigned m_inspectionLevel { 1 };
  • trunk/Source/WebKit2/WebProcess/WebPage/WebInspectorUI.messages.in

    r201101 r201171  
    2828    Detached()
    2929    SetDockingUnavailable(bool unavailable)
     30    SetIsVisible(bool visible)
    3031
    3132    ShowConsole()
Note: See TracChangeset for help on using the changeset viewer.