Changeset 233915 in webkit


Ignore:
Timestamp:
Jul 18, 2018 10:08:46 AM (6 years ago)
Author:
Wenson Hsieh
Message:

Add SPI to defer running async script until after document load
https://bugs.webkit.org/show_bug.cgi?id=187748
<rdar://problem/42317378>

Reviewed by Ryosuke Niwa and Tim Horton.

Source/WebCore:

On watchOS, we currently observe that time-consuming async scripts can block the first paint of Reader, leaving
the user with a blank screen for tens of seconds. One way to mitigate this is to defer async script execution
until after document load (i.e. the same timing as DOMContentLoaded).

This patch introduces an SPI configuration allowing internal clients to defer execution of asynchronous script
until after document load; this, in combination with the parser yielding token introduced in r233891, allows
Safari on watchOS to avoid being blocked on slow script execution before the first paint of the Reader page on
most article-like pages. See below for more details.

Test: RunScriptAfterDocumentLoad.ExecutionOrderOfScriptsInDocument

  • dom/Document.cpp:

(WebCore::Document::shouldDeferAsynchronousScriptsUntilParsingFinishes const):
(WebCore::Document::finishedParsing):

Notify ScriptRunner when the Document has finished parsing, and is about to fire DOMContentLoaded.

  • dom/Document.h:
  • dom/ScriptRunner.cpp:

(WebCore::ScriptRunner::documentFinishedParsing):

When the document is finished parsing, kick off the script execution timer if needed to run any async script
that has been deferred.

(WebCore::ScriptRunner::notifyFinished):
(WebCore::ScriptRunner::timerFired):

Instead of always taking from the list of async scripts to execute, check our document to see whether we should
defer this until after document load. If so, ignore m_scriptsToExecuteSoon.

  • dom/ScriptRunner.h:
  • page/Settings.yaml:

Add a WebCore setting for this behavior.

Source/WebKit:

Add plumbing for a new ShouldDeferAsynchronousScriptsUntilAfterDocumentLoad configuration that determines
whether async script execution should be deferred until document load (i.e. DOMContentLoaded). This
configuration defaults to NO on all platforms. See WebCore ChangeLog for more detail.

  • Shared/WebPreferences.yaml:
  • UIProcess/API/Cocoa/WKWebView.mm:

(-[WKWebView _initializeWithConfiguration:]):

  • UIProcess/API/Cocoa/WKWebViewConfiguration.mm:

(-[WKWebViewConfiguration init]):
(-[WKWebViewConfiguration copyWithZone:]):
(-[WKWebViewConfiguration _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad]):
(-[WKWebViewConfiguration _setShouldDeferAsynchronousScriptsUntilAfterDocumentLoad:]):

  • UIProcess/API/Cocoa/WKWebViewConfigurationPrivate.h:

Tools:

Add an API test to verify that when the deferred async script configuration is set, async scripts will be
executed after the DOMContentLoaded event.

  • TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
  • TestWebKitAPI/Tests/WebKitCocoa/RunScriptAfterDocumentLoad.mm: Added.

(TEST):

Location:
trunk
Files:
1 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r233910 r233915  
     12018-07-18  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Add SPI to defer running async script until after document load
     4        https://bugs.webkit.org/show_bug.cgi?id=187748
     5        <rdar://problem/42317378>
     6
     7        Reviewed by Ryosuke Niwa and Tim Horton.
     8
     9        On watchOS, we currently observe that time-consuming async scripts can block the first paint of Reader, leaving
     10        the user with a blank screen for tens of seconds. One way to mitigate this is to defer async script execution
     11        until after document load (i.e. the same timing as DOMContentLoaded).
     12
     13        This patch introduces an SPI configuration allowing internal clients to defer execution of asynchronous script
     14        until after document load; this, in combination with the parser yielding token introduced in r233891, allows
     15        Safari on watchOS to avoid being blocked on slow script execution before the first paint of the Reader page on
     16        most article-like pages. See below for more details.
     17
     18        Test: RunScriptAfterDocumentLoad.ExecutionOrderOfScriptsInDocument
     19
     20        * dom/Document.cpp:
     21        (WebCore::Document::shouldDeferAsynchronousScriptsUntilParsingFinishes const):
     22        (WebCore::Document::finishedParsing):
     23
     24        Notify ScriptRunner when the Document has finished parsing, and is about to fire DOMContentLoaded.
     25
     26        * dom/Document.h:
     27        * dom/ScriptRunner.cpp:
     28        (WebCore::ScriptRunner::documentFinishedParsing):
     29
     30        When the document is finished parsing, kick off the script execution timer if needed to run any async script
     31        that has been deferred.
     32
     33        (WebCore::ScriptRunner::notifyFinished):
     34        (WebCore::ScriptRunner::timerFired):
     35
     36        Instead of always taking from the list of async scripts to execute, check our document to see whether we should
     37        defer this until after document load. If so, ignore `m_scriptsToExecuteSoon`.
     38
     39        * dom/ScriptRunner.h:
     40        * page/Settings.yaml:
     41
     42        Add a WebCore setting for this behavior.
     43
    1442018-07-18  Zan Dobersek  <zdobersek@igalia.com>
    245
  • trunk/Source/WebCore/dom/Document.cpp

    r233891 r233915  
    51945194}
    51955195
     5196bool Document::shouldDeferAsynchronousScriptsUntilParsingFinishes() const
     5197{
     5198    return parsing() && settings().shouldDeferAsynchronousScriptsUntilAfterDocumentLoad();
     5199}
     5200
    51965201#if ENABLE(XSLT)
    51975202
     
    54295434
    54305435    Ref<Document> protectedThis(*this);
     5436
     5437    scriptRunner()->documentFinishedParsing();
    54315438
    54325439    if (!m_documentTiming.domContentLoadedEventStart)
  • trunk/Source/WebCore/dom/Document.h

    r233891 r233915  
    992992    void popCurrentScript();
    993993
     994    bool shouldDeferAsynchronousScriptsUntilParsingFinishes() const;
     995
    994996#if ENABLE(XSLT)
    995997    void scheduleToApplyXSLTransforms();
  • trunk/Source/WebCore/dom/ScriptRunner.cpp

    r233891 r233915  
    8787}
    8888
     89void ScriptRunner::documentFinishedParsing()
     90{
     91    if (!m_scriptsToExecuteSoon.isEmpty() && !m_timer.isActive())
     92        resume();
     93}
     94
    8995void ScriptRunner::notifyFinished(PendingScript& pendingScript)
    9096{
     
    106112
    107113    Vector<RefPtr<PendingScript>> scripts;
    108     scripts.swap(m_scriptsToExecuteSoon);
     114
     115    if (!m_document.shouldDeferAsynchronousScriptsUntilParsingFinishes())
     116        scripts.swap(m_scriptsToExecuteSoon);
    109117
    110118    size_t numInOrderScriptsToExecute = 0;
  • trunk/Source/WebCore/dom/ScriptRunner.h

    r233891 r233915  
    5454    void didEndYieldingParser() { resume(); }
    5555
     56    void documentFinishedParsing();
     57
    5658private:
    5759    void timerFired();
  • trunk/Source/WebCore/page/Settings.yaml

    r233869 r233915  
    759759incompleteImageBorderEnabled:
    760760  initial: false
     761
     762shouldDeferAsynchronousScriptsUntilAfterDocumentLoad:
     763  initial: false
  • trunk/Source/WebKit/ChangeLog

    r233909 r233915  
     12018-07-18  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Add SPI to defer running async script until after document load
     4        https://bugs.webkit.org/show_bug.cgi?id=187748
     5        <rdar://problem/42317378>
     6
     7        Reviewed by Ryosuke Niwa and Tim Horton.
     8
     9        Add plumbing for a new ShouldDeferAsynchronousScriptsUntilAfterDocumentLoad configuration that determines
     10        whether async script execution should be deferred until document load (i.e. DOMContentLoaded). This
     11        configuration defaults to NO on all platforms. See WebCore ChangeLog for more detail.
     12
     13        * Shared/WebPreferences.yaml:
     14        * UIProcess/API/Cocoa/WKWebView.mm:
     15        (-[WKWebView _initializeWithConfiguration:]):
     16        * UIProcess/API/Cocoa/WKWebViewConfiguration.mm:
     17        (-[WKWebViewConfiguration init]):
     18        (-[WKWebViewConfiguration copyWithZone:]):
     19        (-[WKWebViewConfiguration _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad]):
     20        (-[WKWebViewConfiguration _setShouldDeferAsynchronousScriptsUntilAfterDocumentLoad:]):
     21        * UIProcess/API/Cocoa/WKWebViewConfigurationPrivate.h:
     22
    1232018-07-18  Zan Dobersek  <zdobersek@igalia.com>
    224
  • trunk/Source/WebKit/Shared/WebPreferences.yaml

    r233869 r233915  
    10901090
    10911091IncompleteImageBorderEnabled:
     1092  type: bool
     1093  defaultValue: false
     1094
     1095ShouldDeferAsynchronousScriptsUntilAfterDocumentLoad:
    10921096  type: bool
    10931097  defaultValue: false
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm

    r233905 r233915  
    548548    pageConfiguration->setControlledByAutomation([_configuration _isControlledByAutomation]);
    549549    pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::incompleteImageBorderEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _incompleteImageBorderEnabled]));
     550    pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldDeferAsynchronousScriptsUntilAfterDocumentLoadKey(), WebKit::WebPreferencesStore::Value(!![_configuration _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad]));
    550551
    551552#if ENABLE(APPLICATION_MANIFEST)
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfiguration.mm

    r233828 r233915  
    165165    BOOL _colorFilterEnabled;
    166166    BOOL _incompleteImageBorderEnabled;
     167    BOOL _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad;
    167168    BOOL _drawsBackground;
    168169
     
    251252    _colorFilterEnabled = NO;
    252253    _incompleteImageBorderEnabled = NO;
     254    _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad = NO;
    253255    _drawsBackground = YES;
    254256
     
    415417    configuration->_colorFilterEnabled = self->_colorFilterEnabled;
    416418    configuration->_incompleteImageBorderEnabled = self->_incompleteImageBorderEnabled;
     419    configuration->_shouldDeferAsynchronousScriptsUntilAfterDocumentLoad = self->_shouldDeferAsynchronousScriptsUntilAfterDocumentLoad;
    417420    configuration->_drawsBackground = self->_drawsBackground;
    418421
     
    783786{
    784787    _incompleteImageBorderEnabled = incompleteImageBorderEnabled;
     788}
     789
     790- (BOOL)_shouldDeferAsynchronousScriptsUntilAfterDocumentLoad
     791{
     792    return _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad;
     793}
     794
     795- (void)_setShouldDeferAsynchronousScriptsUntilAfterDocumentLoad:(BOOL)shouldDeferAsynchronousScriptsUntilAfterDocumentLoad
     796{
     797    _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad = shouldDeferAsynchronousScriptsUntilAfterDocumentLoad;
    785798}
    786799
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfigurationPrivate.h

    r233828 r233915  
    7474@property (nonatomic, setter=_setIncompleteImageBorderEnabled:) BOOL _incompleteImageBorderEnabled WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
    7575@property (nonatomic, setter=_setDrawsBackground:) BOOL _drawsBackground WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
     76@property (nonatomic, setter=_setShouldDeferAsynchronousScriptsUntilAfterDocumentLoad:) BOOL _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
    7677
    7778#if TARGET_OS_IPHONE
  • trunk/Tools/ChangeLog

    r233914 r233915  
     12018-07-18  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Add SPI to defer running async script until after document load
     4        https://bugs.webkit.org/show_bug.cgi?id=187748
     5        <rdar://problem/42317378>
     6
     7        Reviewed by Ryosuke Niwa and Tim Horton.
     8
     9        Add an API test to verify that when the deferred async script configuration is set, async scripts will be
     10        executed after the DOMContentLoaded event.
     11
     12        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
     13        * TestWebKitAPI/Tests/WebKitCocoa/RunScriptAfterDocumentLoad.mm: Added.
     14        (TEST):
     15
    1162018-07-18  Charlie Turner  <cturner@igalia.com>
    217
  • trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj

    r233891 r233915  
    817817                F4CD74C620FDACFA00DE3794 /* text-with-async-script.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4CD74C520FDACF500DE3794 /* text-with-async-script.html */; };
    818818                F4CD74C920FDB49600DE3794 /* TestURLSchemeHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4CD74C820FDB49600DE3794 /* TestURLSchemeHandler.mm */; };
     819                F4D2986E20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D2986D20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm */; };
    819820                F4D4F3B61E4E2BCB00BB2767 /* DataInteractionSimulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D4F3B41E4E2BCB00BB2767 /* DataInteractionSimulator.mm */; };
    820821                F4D4F3B91E4E36E400BB2767 /* DataInteractionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D4F3B71E4E36E400BB2767 /* DataInteractionTests.mm */; };
     
    20252026                F4CD74C720FDB49600DE3794 /* TestURLSchemeHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestURLSchemeHandler.h; sourceTree = "<group>"; };
    20262027                F4CD74C820FDB49600DE3794 /* TestURLSchemeHandler.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TestURLSchemeHandler.mm; sourceTree = "<group>"; };
     2028                F4D2986D20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RunScriptAfterDocumentLoad.mm; sourceTree = "<group>"; };
    20272029                F4D4F3B41E4E2BCB00BB2767 /* DataInteractionSimulator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DataInteractionSimulator.mm; sourceTree = "<group>"; };
    20282030                F4D4F3B51E4E2BCB00BB2767 /* DataInteractionSimulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataInteractionSimulator.h; sourceTree = "<group>"; };
     
    23132315                                51C8E1A41F26AC5400BF731B /* ResourceLoadStatistics.mm */,
    23142316                                A180C0F91EE67DF000468F47 /* RunOpenPanel.mm */,
     2317                                F4D2986D20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm */,
    23152318                                CE0947362063223B003C9BA0 /* SchemeRegistry.mm */,
    23162319                                51EB12931FDF050500A5A1BD /* ServiceWorkerBasic.mm */,
     
    38253828                                F418BE151F71B7DC001970E6 /* RoundedRectTests.cpp in Sources */,
    38263829                                A180C0FA1EE67DF000468F47 /* RunOpenPanel.mm in Sources */,
     3830                                F4D2986E20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm in Sources */,
    38273831                                CDCFA7AA1E45183200C2433D /* SampleMap.cpp in Sources */,
    38283832                                CE0947372063223B003C9BA0 /* SchemeRegistry.mm in Sources */,
Note: See TracChangeset for help on using the changeset viewer.