Diff
Modified: trunk/Source/WebCore/ChangeLog (233914 => 233915)
--- trunk/Source/WebCore/ChangeLog 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Source/WebCore/ChangeLog 2018-07-18 17:08:46 UTC (rev 233915)
@@ -1,3 +1,46 @@
+2018-07-18 Wenson Hsieh <wenson_hs...@apple.com>
+
+ 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 Zan Dobersek <zdober...@igalia.com>
[Nicosia] Add debug border, repaint counter state tracking to Nicosia::CompositionLayer
Modified: trunk/Source/WebCore/dom/Document.cpp (233914 => 233915)
--- trunk/Source/WebCore/dom/Document.cpp 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Source/WebCore/dom/Document.cpp 2018-07-18 17:08:46 UTC (rev 233915)
@@ -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: trunk/Source/WebCore/dom/Document.h (233914 => 233915)
--- trunk/Source/WebCore/dom/Document.h 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Source/WebCore/dom/Document.h 2018-07-18 17:08:46 UTC (rev 233915)
@@ -991,6 +991,8 @@
void pushCurrentScript(HTMLScriptElement*);
void popCurrentScript();
+ bool shouldDeferAsynchronousScriptsUntilParsingFinishes() const;
+
#if ENABLE(XSLT)
void scheduleToApplyXSLTransforms();
void applyPendingXSLTransformsNowIfScheduled();
Modified: trunk/Source/WebCore/dom/ScriptRunner.cpp (233914 => 233915)
--- trunk/Source/WebCore/dom/ScriptRunner.cpp 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Source/WebCore/dom/ScriptRunner.cpp 2018-07-18 17:08:46 UTC (rev 233915)
@@ -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: trunk/Source/WebCore/dom/ScriptRunner.h (233914 => 233915)
--- trunk/Source/WebCore/dom/ScriptRunner.h 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Source/WebCore/dom/ScriptRunner.h 2018-07-18 17:08:46 UTC (rev 233915)
@@ -53,6 +53,8 @@
void didBeginYieldingParser() { suspend(); }
void didEndYieldingParser() { resume(); }
+ void documentFinishedParsing();
+
private:
void timerFired();
Modified: trunk/Source/WebCore/page/Settings.yaml (233914 => 233915)
--- trunk/Source/WebCore/page/Settings.yaml 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Source/WebCore/page/Settings.yaml 2018-07-18 17:08:46 UTC (rev 233915)
@@ -758,3 +758,6 @@
incompleteImageBorderEnabled:
initial: false
+
+shouldDeferAsynchronousScriptsUntilAfterDocumentLoad:
+ initial: false
Modified: trunk/Source/WebKit/ChangeLog (233914 => 233915)
--- trunk/Source/WebKit/ChangeLog 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Source/WebKit/ChangeLog 2018-07-18 17:08:46 UTC (rev 233915)
@@ -1,3 +1,25 @@
+2018-07-18 Wenson Hsieh <wenson_hs...@apple.com>
+
+ 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 Zan Dobersek <zdober...@igalia.com>
[CoordGraphics] Start tracking Nicosia layers in CoordinatedGraphicsState
Modified: trunk/Source/WebKit/Shared/WebPreferences.yaml (233914 => 233915)
--- trunk/Source/WebKit/Shared/WebPreferences.yaml 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Source/WebKit/Shared/WebPreferences.yaml 2018-07-18 17:08:46 UTC (rev 233915)
@@ -1092,6 +1092,10 @@
type: bool
defaultValue: false
+ShouldDeferAsynchronousScriptsUntilAfterDocumentLoad:
+ type: bool
+ defaultValue: false
+
StorageAccessAPIEnabled:
type: bool
defaultValue: true
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (233914 => 233915)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2018-07-18 17:08:46 UTC (rev 233915)
@@ -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: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfiguration.mm (233914 => 233915)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfiguration.mm 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfiguration.mm 2018-07-18 17:08:46 UTC (rev 233915)
@@ -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: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfigurationPrivate.h (233914 => 233915)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfigurationPrivate.h 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewConfigurationPrivate.h 2018-07-18 17:08:46 UTC (rev 233915)
@@ -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: trunk/Tools/ChangeLog (233914 => 233915)
--- trunk/Tools/ChangeLog 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Tools/ChangeLog 2018-07-18 17:08:46 UTC (rev 233915)
@@ -1,3 +1,18 @@
+2018-07-18 Wenson Hsieh <wenson_hs...@apple.com>
+
+ 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 Charlie Turner <ctur...@igalia.com>
[WPE] Update WPEBackend in flatpak
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (233914 => 233915)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2018-07-18 16:04:33 UTC (rev 233914)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2018-07-18 17:08:46 UTC (rev 233915)
@@ -816,6 +816,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 */; };
@@ -2024,6 +2025,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>"; };
@@ -2312,6 +2314,7 @@
CD9E292B1C90A71F000BB800 /* RequiresUserActionForPlayback.mm */,
51C8E1A41F26AC5400BF731B /* ResourceLoadStatistics.mm */,
A180C0F91EE67DF000468F47 /* RunOpenPanel.mm */,
+ F4D2986D20FEE7370092D636 /* RunScriptAfterDocumentLoad.mm */,
CE0947362063223B003C9BA0 /* SchemeRegistry.mm */,
51EB12931FDF050500A5A1BD /* ServiceWorkerBasic.mm */,
37BCA61B1B596BA9002012CA /* ShouldOpenExternalURLsInNewWindowActions.mm */,
@@ -3824,6 +3827,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: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RunScriptAfterDocumentLoad.mm (0 => 233915)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RunScriptAfterDocumentLoad.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RunScriptAfterDocumentLoad.mm 2018-07-18 17:08:46 UTC (rev 233915)
@@ -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