Title: [233978] branches/safari-606-branch
Revision
233978
Author
[email protected]
Date
2018-07-18 22:40:20 -0700 (Wed, 18 Jul 2018)

Log Message

Cherry-pick r233915. rdar://problem/42345407

    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):

    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@233915 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Modified Paths

Added Paths

Diff

Modified: branches/safari-606-branch/Source/WebCore/ChangeLog (233977 => 233978)


--- branches/safari-606-branch/Source/WebCore/ChangeLog	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Source/WebCore/ChangeLog	2018-07-19 05:40:20 UTC (rev 233978)
@@ -1,5 +1,123 @@
 2018-07-18  Babak Shafiei  <[email protected]>
 
+        Cherry-pick r233915. rdar://problem/42345407
+
+    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):
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@233915 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2018-07-18  Wenson Hsieh  <[email protected]>
+
+            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.
+
+            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.
+
+2018-07-18  Babak Shafiei  <[email protected]>
+
         Cherry-pick r233891. rdar://problem/42345327
 
     Add an SPI hook to allow clients to yield document parsing and script execution

Modified: branches/safari-606-branch/Source/WebCore/dom/Document.cpp (233977 => 233978)


--- branches/safari-606-branch/Source/WebCore/dom/Document.cpp	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Source/WebCore/dom/Document.cpp	2018-07-19 05:40:20 UTC (rev 233978)
@@ -5193,6 +5193,11 @@
     m_currentScriptStack.removeLast();
 }
 
+bool Document::shouldDeferAsynchronousScriptsUntilParsingFinishes() const
+{
+    return parsing() && settings().shouldDeferAsynchronousScriptsUntilAfterDocumentLoad();
+}
+
 #if ENABLE(XSLT)
 
 void Document::scheduleToApplyXSLTransforms()
@@ -5429,6 +5434,8 @@
 
     Ref<Document> protectedThis(*this);
 
+    scriptRunner()->documentFinishedParsing();
+
     if (!m_documentTiming.domContentLoadedEventStart)
         m_documentTiming.domContentLoadedEventStart = MonotonicTime::now();
 

Modified: branches/safari-606-branch/Source/WebCore/dom/Document.h (233977 => 233978)


--- branches/safari-606-branch/Source/WebCore/dom/Document.h	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Source/WebCore/dom/Document.h	2018-07-19 05:40:20 UTC (rev 233978)
@@ -991,6 +991,8 @@
     void pushCurrentScript(HTMLScriptElement*);
     void popCurrentScript();
 
+    bool shouldDeferAsynchronousScriptsUntilParsingFinishes() const;
+
 #if ENABLE(XSLT)
     void scheduleToApplyXSLTransforms();
     void applyPendingXSLTransformsNowIfScheduled();

Modified: branches/safari-606-branch/Source/WebCore/dom/ScriptRunner.cpp (233977 => 233978)


--- branches/safari-606-branch/Source/WebCore/dom/ScriptRunner.cpp	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Source/WebCore/dom/ScriptRunner.cpp	2018-07-19 05:40:20 UTC (rev 233978)
@@ -86,6 +86,12 @@
         m_timer.startOneShot(0_s);
 }
 
+void ScriptRunner::documentFinishedParsing()
+{
+    if (!m_scriptsToExecuteSoon.isEmpty() && !m_timer.isActive())
+        resume();
+}
+
 void ScriptRunner::notifyFinished(PendingScript& pendingScript)
 {
     if (pendingScript.element().willExecuteInOrder())
@@ -105,8 +111,10 @@
     Ref<Document> protect(m_document);
 
     Vector<RefPtr<PendingScript>> scripts;
-    scripts.swap(m_scriptsToExecuteSoon);
 
+    if (!m_document.shouldDeferAsynchronousScriptsUntilParsingFinishes())
+        scripts.swap(m_scriptsToExecuteSoon);
+
     size_t numInOrderScriptsToExecute = 0;
     for (; numInOrderScriptsToExecute < m_scriptsToExecuteInOrder.size() && m_scriptsToExecuteInOrder[numInOrderScriptsToExecute]->isLoaded(); ++numInOrderScriptsToExecute)
         scripts.append(m_scriptsToExecuteInOrder[numInOrderScriptsToExecute].ptr());

Modified: branches/safari-606-branch/Source/WebCore/dom/ScriptRunner.h (233977 => 233978)


--- branches/safari-606-branch/Source/WebCore/dom/ScriptRunner.h	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Source/WebCore/dom/ScriptRunner.h	2018-07-19 05:40:20 UTC (rev 233978)
@@ -53,6 +53,8 @@
     void didBeginYieldingParser() { suspend(); }
     void didEndYieldingParser() { resume(); }
 
+    void documentFinishedParsing();
+
 private:
     void timerFired();
 

Modified: branches/safari-606-branch/Source/WebCore/page/Settings.yaml (233977 => 233978)


--- branches/safari-606-branch/Source/WebCore/page/Settings.yaml	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Source/WebCore/page/Settings.yaml	2018-07-19 05:40:20 UTC (rev 233978)
@@ -758,3 +758,6 @@
 
 incompleteImageBorderEnabled:
   initial: false
+
+shouldDeferAsynchronousScriptsUntilAfterDocumentLoad:
+  initial: false

Modified: branches/safari-606-branch/Source/WebKit/ChangeLog (233977 => 233978)


--- branches/safari-606-branch/Source/WebKit/ChangeLog	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Source/WebKit/ChangeLog	2018-07-19 05:40:20 UTC (rev 233978)
@@ -1,5 +1,102 @@
 2018-07-18  Babak Shafiei  <[email protected]>
 
+        Cherry-pick r233915. rdar://problem/42345407
+
+    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):
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@233915 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2018-07-18  Wenson Hsieh  <[email protected]>
+
+            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.
+
+            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:
+
+2018-07-18  Babak Shafiei  <[email protected]>
+
         Cherry-pick r233891. rdar://problem/42345327
 
     Add an SPI hook to allow clients to yield document parsing and script execution

Modified: branches/safari-606-branch/Source/WebKit/Shared/WebPreferences.yaml (233977 => 233978)


--- branches/safari-606-branch/Source/WebKit/Shared/WebPreferences.yaml	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Source/WebKit/Shared/WebPreferences.yaml	2018-07-19 05:40:20 UTC (rev 233978)
@@ -1092,6 +1092,10 @@
   type: bool
   defaultValue: false
 
+ShouldDeferAsynchronousScriptsUntilAfterDocumentLoad:
+  type: bool
+  defaultValue: false
+
 StorageAccessAPIEnabled:
   type: bool
   defaultValue: true

Modified: branches/safari-606-branch/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (233977 => 233978)


--- branches/safari-606-branch/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm	2018-07-19 05:40:20 UTC (rev 233978)
@@ -547,6 +547,7 @@
     pageConfiguration->setDrawsBackground([_configuration _drawsBackground]);
     pageConfiguration->setControlledByAutomation([_configuration _isControlledByAutomation]);
     pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::incompleteImageBorderEnabledKey(), WebKit::WebPreferencesStore::Value(!![_configuration _incompleteImageBorderEnabled]));
+    pageConfiguration->preferenceValues().set(WebKit::WebPreferencesKey::shouldDeferAsynchronousScriptsUntilAfterDocumentLoadKey(), WebKit::WebPreferencesStore::Value(!![_configuration _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad]));
 
 #if ENABLE(APPLICATION_MANIFEST)
     pageConfiguration->setApplicationManifest([_configuration _applicationManifest] ? [configuration _applicationManifest]->_applicationManifest.get() : nullptr);

Modified: branches/safari-606-branch/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfiguration.mm (233977 => 233978)


--- branches/safari-606-branch/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfiguration.mm	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfiguration.mm	2018-07-19 05:40:20 UTC (rev 233978)
@@ -164,6 +164,7 @@
     BOOL _allowMediaContentTypesRequiringHardwareSupportAsFallback;
     BOOL _colorFilterEnabled;
     BOOL _incompleteImageBorderEnabled;
+    BOOL _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad;
     BOOL _drawsBackground;
 
     RetainPtr<NSString> _overrideContentSecurityPolicy;
@@ -250,6 +251,7 @@
 
     _colorFilterEnabled = NO;
     _incompleteImageBorderEnabled = NO;
+    _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad = NO;
     _drawsBackground = YES;
 
     return self;
@@ -414,6 +416,7 @@
     configuration->_groupIdentifier = adoptNS([self->_groupIdentifier copyWithZone:zone]);
     configuration->_colorFilterEnabled = self->_colorFilterEnabled;
     configuration->_incompleteImageBorderEnabled = self->_incompleteImageBorderEnabled;
+    configuration->_shouldDeferAsynchronousScriptsUntilAfterDocumentLoad = self->_shouldDeferAsynchronousScriptsUntilAfterDocumentLoad;
     configuration->_drawsBackground = self->_drawsBackground;
 
     return configuration;
@@ -784,6 +787,16 @@
     _incompleteImageBorderEnabled = incompleteImageBorderEnabled;
 }
 
+- (BOOL)_shouldDeferAsynchronousScriptsUntilAfterDocumentLoad
+{
+    return _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad;
+}
+
+- (void)_setShouldDeferAsynchronousScriptsUntilAfterDocumentLoad:(BOOL)shouldDeferAsynchronousScriptsUntilAfterDocumentLoad
+{
+    _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad = shouldDeferAsynchronousScriptsUntilAfterDocumentLoad;
+}
+
 - (BOOL)_drawsBackground
 {
     return _drawsBackground;

Modified: branches/safari-606-branch/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfigurationPrivate.h (233977 => 233978)


--- branches/safari-606-branch/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfigurationPrivate.h	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfigurationPrivate.h	2018-07-19 05:40:20 UTC (rev 233978)
@@ -73,6 +73,7 @@
 @property (nonatomic, setter=_setColorFilterEnabled:) BOOL _colorFilterEnabled WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 @property (nonatomic, setter=_setIncompleteImageBorderEnabled:) BOOL _incompleteImageBorderEnabled WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 @property (nonatomic, setter=_setDrawsBackground:) BOOL _drawsBackground WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+@property (nonatomic, setter=_setShouldDeferAsynchronousScriptsUntilAfterDocumentLoad:) BOOL _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 #if TARGET_OS_IPHONE
 @property (nonatomic, setter=_setAlwaysRunsAtForegroundPriority:) BOOL _alwaysRunsAtForegroundPriority WK_API_AVAILABLE(ios(9_0));

Modified: branches/safari-606-branch/Tools/ChangeLog (233977 => 233978)


--- branches/safari-606-branch/Tools/ChangeLog	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Tools/ChangeLog	2018-07-19 05:40:20 UTC (rev 233978)
@@ -1,5 +1,95 @@
 2018-07-18  Babak Shafiei  <[email protected]>
 
+        Cherry-pick r233915. rdar://problem/42345407
+
+    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):
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@233915 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2018-07-18  Wenson Hsieh  <[email protected]>
+
+            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.
+
+            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):
+
+2018-07-18  Babak Shafiei  <[email protected]>
+
         Cherry-pick r233891. rdar://problem/42345327
 
     Add an SPI hook to allow clients to yield document parsing and script execution

Modified: branches/safari-606-branch/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (233977 => 233978)


--- branches/safari-606-branch/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2018-07-19 05:40:13 UTC (rev 233977)
+++ branches/safari-606-branch/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2018-07-19 05:40:20 UTC (rev 233978)
@@ -818,6 +818,7 @@
 		F4C8797F2059D8D3009CD00B /* ScrollViewInsetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4C8797E2059D8D3009CD00B /* ScrollViewInsetTests.mm */; };
 		F4CD74C620FDACFA00DE3794 /* text-with-async-script.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4CD74C520FDACF500DE3794 /* text-with-async-script.html */; };
 		F4CD74C920FDB49600DE3794 /* TestURLSchemeHandler.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4CD74C820FDB49600DE3794 /* TestURLSchemeHandler.mm */; };
+		F4D2986E20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D2986D20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm */; };
 		F4D4F3B61E4E2BCB00BB2767 /* DataInteractionSimulator.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D4F3B41E4E2BCB00BB2767 /* DataInteractionSimulator.mm */; };
 		F4D4F3B91E4E36E400BB2767 /* DataInteractionTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F4D4F3B71E4E36E400BB2767 /* DataInteractionTests.mm */; };
 		F4D5E4E81F0C5D38008C1A49 /* dragstart-clear-selection.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = F4D5E4E71F0C5D27008C1A49 /* dragstart-clear-selection.html */; };
@@ -2029,6 +2030,7 @@
 		F4CD74C520FDACF500DE3794 /* text-with-async-script.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "text-with-async-script.html"; sourceTree = "<group>"; };
 		F4CD74C720FDB49600DE3794 /* TestURLSchemeHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestURLSchemeHandler.h; sourceTree = "<group>"; };
 		F4CD74C820FDB49600DE3794 /* TestURLSchemeHandler.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TestURLSchemeHandler.mm; sourceTree = "<group>"; };
+		F4D2986D20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = RunScriptAfterDocumentLoad.mm; sourceTree = "<group>"; };
 		F4D4F3B41E4E2BCB00BB2767 /* DataInteractionSimulator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DataInteractionSimulator.mm; sourceTree = "<group>"; };
 		F4D4F3B51E4E2BCB00BB2767 /* DataInteractionSimulator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataInteractionSimulator.h; sourceTree = "<group>"; };
 		F4D4F3B71E4E36E400BB2767 /* DataInteractionTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DataInteractionTests.mm; sourceTree = "<group>"; };
@@ -2318,6 +2320,7 @@
 				CD9E292B1C90A71F000BB800 /* RequiresUserActionForPlayback.mm */,
 				51C8E1A41F26AC5400BF731B /* ResourceLoadStatistics.mm */,
 				A180C0F91EE67DF000468F47 /* RunOpenPanel.mm */,
+				F4D2986D20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm */,
 				CE0947362063223B003C9BA0 /* SchemeRegistry.mm */,
 				51EB12931FDF050500A5A1BD /* ServiceWorkerBasic.mm */,
 				37BCA61B1B596BA9002012CA /* ShouldOpenExternalURLsInNewWindowActions.mm */,
@@ -3832,6 +3835,7 @@
 				46E816F81E79E29C00375ADC /* RestoreStateAfterTermination.mm in Sources */,
 				F418BE151F71B7DC001970E6 /* RoundedRectTests.cpp in Sources */,
 				A180C0FA1EE67DF000468F47 /* RunOpenPanel.mm in Sources */,
+				F4D2986E20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm in Sources */,
 				CDCFA7AA1E45183200C2433D /* SampleMap.cpp in Sources */,
 				CE0947372063223B003C9BA0 /* SchemeRegistry.mm in Sources */,
 				7CCE7F121A411AE600447C4C /* ScrollPinningBehaviors.cpp in Sources */,

Added: branches/safari-606-branch/Tools/TestWebKitAPI/Tests/WebKitCocoa/RunScriptAfterDocumentLoad.mm (0 => 233978)


--- branches/safari-606-branch/Tools/TestWebKitAPI/Tests/WebKitCocoa/RunScriptAfterDocumentLoad.mm	                        (rev 0)
+++ branches/safari-606-branch/Tools/TestWebKitAPI/Tests/WebKitCocoa/RunScriptAfterDocumentLoad.mm	2018-07-19 05:40:20 UTC (rev 233978)
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#if WK_API_ENABLED
+
+#import "PlatformUtilities.h"
+#import "TestNavigationDelegate.h"
+#import "TestURLSchemeHandler.h"
+#import "TestWKWebView.h"
+#import <WebKit/WKWebViewConfigurationPrivate.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/Seconds.h>
+
+static const char* markup =
+    "<head>"
+    "<script>function scriptLoaded(element) { webkit.messageHandlers.testHandler.postMessage(`${element.getAttribute('src')} loaded`) }</script>"
+    "<script _onload_='scriptLoaded(this)' src='' async></script>"
+    "</head>"
+    "<body>"
+    "<script>document.addEventListener('DOMContentLoaded', () => webkit.messageHandlers.testHandler.postMessage('DOMContentLoaded'))</script>"
+    "<script _onload_='scriptLoaded(this)' src='' defer></script>"
+    "<script _onload_='scriptLoaded(this)' src=''></script>"
+    "</body>";
+
+TEST(RunScriptAfterDocumentLoad, ExecutionOrderOfScriptsInDocument)
+{
+    auto schemeHandler = adoptNS([[TestURLSchemeHandler alloc] init]);
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    [configuration setURLSchemeHandler:schemeHandler.get() forURLScheme:@"custom"];
+    [configuration _setShouldDeferAsynchronousScriptsUntilAfterDocumentLoad:YES];
+    [schemeHandler setStartURLSchemeTaskHandler:^(WKWebView *, id<WKURLSchemeTask> task) {
+        auto responseBlock = [task = retainPtr(task)] {
+            NSURL *requestURL = [task request].URL;
+            NSString *script = [NSString stringWithFormat:@"webkit.messageHandlers.testHandler.postMessage('Running %@')", requestURL.absoluteString.lastPathComponent];
+            auto response = adoptNS([[NSURLResponse alloc] initWithURL:requestURL MIMEType:@"text/_javascript_" expectedContentLength:[script length] textEncodingName:nil]);
+            [task didReceiveResponse:response.get()];
+            [task didReceiveData:[script dataUsingEncoding:NSUTF8StringEncoding]];
+            [task didFinish];
+        };
+
+        if ([[task request].URL.absoluteString containsString:@"async.js"]) {
+            responseBlock();
+            return;
+        }
+
+        // Delay request responses for the other two scripts for a short duration to ensure that in the absence
+        // of the deferred asynchronous scripts configuration flag, we will normally execute the asynchronous script
+        // before the other scripts.
+        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (0.25_s).nanoseconds()), dispatch_get_main_queue(), responseBlock);
+    }];
+
+    auto messages = adoptNS([NSMutableArray new]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 200, 200) configuration:configuration.get()]);
+    for (NSString *message in @[ @"Running async.js", @"Running defer.js", @"Running sync.js", @"DOMContentLoaded", @"async.js loaded", @"defer.js loaded", @"sync.js loaded" ]) {
+        [webView performAfterReceivingMessage:message action:[message = adoptNS(message.copy), messages] {
+            [messages addObject:message.get()];
+        }];
+    }
+
+    [webView loadHTMLString:@(markup) baseURL:[NSURL URLWithString:@"custom://"]];
+    [webView _test_waitForDidFinishNavigation];
+
+    // Verify that the asynchronous script is executed after document load, the synchronous script is executed before the
+    // deferred script, and the deferred script is executed prior to document load.
+    EXPECT_EQ(7U, [messages count]);
+    EXPECT_WK_STREQ("Running sync.js", [messages objectAtIndex:0]);
+    EXPECT_WK_STREQ("sync.js loaded", [messages objectAtIndex:1]);
+    EXPECT_WK_STREQ("Running defer.js", [messages objectAtIndex:2]);
+    EXPECT_WK_STREQ("defer.js loaded", [messages objectAtIndex:3]);
+    EXPECT_WK_STREQ("DOMContentLoaded", [messages objectAtIndex:4]);
+    EXPECT_WK_STREQ("Running async.js", [messages objectAtIndex:5]);
+    EXPECT_WK_STREQ("async.js loaded", [messages objectAtIndex:6]);
+}
+
+#endif // WK_API_ENABLED
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to