Title: [235408] trunk
Revision
235408
Author
simon.fra...@apple.com
Date
2018-08-27 16:31:15 -0700 (Mon, 27 Aug 2018)

Log Message

Teach WebKitTestRunner and DumpRenderTree about detecting world leaks
https://bugs.webkit.org/show_bug.cgi?id=188994

Reviewed by Tim Horton.
Source/WebCore:

Export Document::postTask() for use by WTR's injected bundle.

* dom/Document.h:

Source/WebKit:

This patch adds the notion of a "control command" in the protocol between webkitpy and
WebKitTestRunner/DumpRenderTree. A command is simply an input string starting with a #
that is checked for before trying to parse the input as test URL. For now, just one
commmand is supported, which is "#CHECK FOR WORLD LEAKS".

In response to the command, the tool dumps an output block in the usual pseudo-MIME-style,
with a trailing "#EOF". Future patches will add support to webkitpy to parse this output.

DumpRenderTree stubs out the command, returning an empty block.

WebKitTestRunner responds to the command by dumping the list of live documents, if it was
run with the --check-for-world-leaks option.

When run with --check-for-world-leaks, WebKitTestRunner gets the list of live documents via
WKBundleGetLiveDocumentURLs() after every test (this allows it to detect the first test
that leaked a document), and keeps them in a map of document identifier to test and live document URL.
Then when it receives the "#CHECK FOR WORLD LEAKS" command, it calls into the bundle to
clear the page and memory caches, runs a GC, then posts a task (in the Document::postTaks() sense)
after which it requests the list of live documents for a final time, excluding any that are loaded
in live Frames (thus omitting the about:blank that will be loaded at this point). Documents in this
list are therefore leaked (or abandoned).

Future patches will hook up webkitpy reporting for leaked documents.

* WebProcess/InjectedBundle/API/c/WKBundle.cpp:
(WKBundleGetLiveDocumentURLs):
(WKBundleClearPageCache):
(WKBundleClearMemoryCache):
* WebProcess/InjectedBundle/API/c/WKBundlePage.cpp:
(WKBundlePagePostTask):
* WebProcess/InjectedBundle/API/c/WKBundlePage.h:
* WebProcess/InjectedBundle/API/c/WKBundlePrivate.h:
* WebProcess/InjectedBundle/InjectedBundle.cpp:
(WebKit::InjectedBundle::liveDocumentURLs):
* WebProcess/InjectedBundle/InjectedBundle.h:

Tools:

This patch adds the notion of a "control command" in the protocol between webkitpy and
WebKitTestRunner/DumpRenderTree. A command is simply an input string starting with a #
that is checked for before trying to parse the input as test URL. For now, just one
commmand is supported, which is "#CHECK FOR WORLD LEAKS".

In response to the command, the tool dumps an output block in the usual pseudo-MIME-style,
with a trailing "#EOF". Future patches will add support to webkitpy to parse this output.

DumpRenderTree stubs out the command, returning an empty block.

WebKitTestRunner responds to the command by dumping the list of live documents, if it was
run with the --check-for-world-leaks option.

When run with --check-for-world-leaks, WebKitTestRunner gets the list of live documents via
WKBundleGetLiveDocumentURLs() after every test (this allows it to detect the first test
that leaked a document), and keeps them in a map of document identifier to test and live document URL.
Then when it receives the "#CHECK FOR WORLD LEAKS" command, it calls into the bundle to
clear the page and memory caches, runs a GC, then posts a task (in the Document::postTaks() sense)
after which it requests the list of live documents for a final time, excluding any that are loaded
in live Frames (thus omitting the about:blank that will be loaded at this point). Documents in this
list are therefore leaked (or abandoned).

Future patches will hook up webkitpy reporting for leaked documents.

* DumpRenderTree/mac/DumpRenderTree.mm:
(initializeGlobalsFromCommandLineOptions):
(handleControlCommand):
(runTestingServerLoop):
* DumpRenderTree/win/DumpRenderTree.cpp:
(handleControlCommand):
(main):
* WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
(WTR::postGCTask):
(WTR::InjectedBundle::reportLiveDocuments):
(WTR::InjectedBundle::didReceiveMessageToPage):
* WebKitTestRunner/InjectedBundle/InjectedBundle.h:
* WebKitTestRunner/Options.cpp:
(WTR::handleOptionCheckForWorldLeaks):
(WTR::OptionsHandler::OptionsHandler):
* WebKitTestRunner/Options.h:
* WebKitTestRunner/TestController.cpp:
(WTR::AsyncTask::run):
(WTR::AsyncTask::currentTask):
(WTR::TestController::initialize):
(WTR::TestController::ensureViewSupportsOptionsForTest):
(WTR::TestController::resetStateToConsistentValues):
(WTR::TestController::updateLiveDocumentsAfterTest):
(WTR::TestController::checkForWorldLeaks):
(WTR::TestController::findAndDumpWorldLeaks):
(WTR::TestController::willDestroyWebView):
(WTR::parseInputLine):
(WTR::TestController::waitForCompletion):
(WTR::TestController::handleControlCommand):
(WTR::TestController::runTestingServerLoop):
(WTR::TestController::run):
(WTR::TestController::didReceiveLiveDocumentsList):
(WTR::TestController::didReceiveMessageFromInjectedBundle):
* WebKitTestRunner/TestController.h:
(WTR::AsyncTask::AsyncTask):
(WTR::AsyncTask::taskComplete):
(WTR::TestController::AbandonedDocumentInfo::AbandonedDocumentInfo):
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::invoke):
* WebKitTestRunner/TestOptions.h:
(WTR::TestOptions::hasSameInitializationOptions const):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (235407 => 235408)


--- trunk/Source/WebCore/ChangeLog	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Source/WebCore/ChangeLog	2018-08-27 23:31:15 UTC (rev 235408)
@@ -1,3 +1,14 @@
+2018-08-27  Simon Fraser  <simon.fra...@apple.com>
+
+        Teach WebKitTestRunner and DumpRenderTree about detecting world leaks
+        https://bugs.webkit.org/show_bug.cgi?id=188994
+
+        Reviewed by Tim Horton.
+
+        Export Document::postTask() for use by WTR's injected bundle.
+
+        * dom/Document.h:
+
 2018-08-27  Aditya Keerthi  <akeer...@apple.com>
 
         Consolidate ENABLE_INPUT_TYPE_COLOR and ENABLE_INPUT_TYPE_COLOR_POPOVER

Modified: trunk/Source/WebCore/dom/Document.h (235407 => 235408)


--- trunk/Source/WebCore/dom/Document.h	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Source/WebCore/dom/Document.h	2018-08-27 23:31:15 UTC (rev 235408)
@@ -1030,7 +1030,7 @@
     bool isDNSPrefetchEnabled() const { return m_isDNSPrefetchEnabled; }
     void parseDNSPrefetchControlHeader(const String&);
 
-    void postTask(Task&&) final; // Executes the task on context's thread asynchronously.
+    WEBCORE_EXPORT void postTask(Task&&) final; // Executes the task on context's thread asynchronously.
 
     ScriptedAnimationController* scriptedAnimationController() { return m_scriptedAnimationController.get(); }
     void suspendScriptedAnimationControllerCallbacks();

Modified: trunk/Source/WebKit/ChangeLog (235407 => 235408)


--- trunk/Source/WebKit/ChangeLog	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Source/WebKit/ChangeLog	2018-08-27 23:31:15 UTC (rev 235408)
@@ -1,3 +1,46 @@
+2018-08-27  Simon Fraser  <simon.fra...@apple.com>
+
+        Teach WebKitTestRunner and DumpRenderTree about detecting world leaks
+        https://bugs.webkit.org/show_bug.cgi?id=188994
+
+        Reviewed by Tim Horton.
+
+        This patch adds the notion of a "control command" in the protocol between webkitpy and 
+        WebKitTestRunner/DumpRenderTree. A command is simply an input string starting with a #
+        that is checked for before trying to parse the input as test URL. For now, just one
+        commmand is supported, which is "#CHECK FOR WORLD LEAKS".
+        
+        In response to the command, the tool dumps an output block in the usual pseudo-MIME-style,
+        with a trailing "#EOF". Future patches will add support to webkitpy to parse this output.
+        
+        DumpRenderTree stubs out the command, returning an empty block.
+        
+        WebKitTestRunner responds to the command by dumping the list of live documents, if it was
+        run with the --check-for-world-leaks option.
+        
+        When run with --check-for-world-leaks, WebKitTestRunner gets the list of live documents via
+        WKBundleGetLiveDocumentURLs() after every test (this allows it to detect the first test
+        that leaked a document), and keeps them in a map of document identifier to test and live document URL.
+        Then when it receives the "#CHECK FOR WORLD LEAKS" command, it calls into the bundle to
+        clear the page and memory caches, runs a GC, then posts a task (in the Document::postTaks() sense)
+        after which it requests the list of live documents for a final time, excluding any that are loaded
+        in live Frames (thus omitting the about:blank that will be loaded at this point). Documents in this
+        list are therefore leaked (or abandoned).
+        
+        Future patches will hook up webkitpy reporting for leaked documents.
+
+        * WebProcess/InjectedBundle/API/c/WKBundle.cpp:
+        (WKBundleGetLiveDocumentURLs):
+        (WKBundleClearPageCache):
+        (WKBundleClearMemoryCache):
+        * WebProcess/InjectedBundle/API/c/WKBundlePage.cpp:
+        (WKBundlePagePostTask):
+        * WebProcess/InjectedBundle/API/c/WKBundlePage.h:
+        * WebProcess/InjectedBundle/API/c/WKBundlePrivate.h:
+        * WebProcess/InjectedBundle/InjectedBundle.cpp:
+        (WebKit::InjectedBundle::liveDocumentURLs):
+        * WebProcess/InjectedBundle/InjectedBundle.h:
+
 2018-08-27  Alex Christensen  <achristen...@webkit.org>
 
         Fix plug-ins after r235398

Modified: trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundle.cpp (235407 => 235408)


--- trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundle.cpp	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundle.cpp	2018-08-27 23:31:15 UTC (rev 235408)
@@ -34,11 +34,18 @@
 #include "WKAPICast.h"
 #include "WKBundleAPICast.h"
 #include "WKBundlePrivate.h"
+#include "WKMutableArray.h"
+#include "WKMutableDictionary.h"
+#include "WKNumber.h"
+#include "WKRetainPtr.h"
+#include "WKString.h"
 #include "WebConnection.h"
 #include "WebFrame.h"
 #include "WebPage.h"
 #include "WebPageGroupProxy.h"
 #include <WebCore/DatabaseTracker.h>
+#include <WebCore/MemoryCache.h>
+#include <WebCore/PageCache.h>
 #include <WebCore/ResourceLoadObserver.h>
 #include <WebCore/ServiceWorkerThreadProxy.h>
 
@@ -213,6 +220,30 @@
     toImpl(bundleRef)->setAsynchronousSpellCheckingEnabled(toImpl(pageGroupRef), enabled);
 }
 
+WKArrayRef WKBundleGetLiveDocumentURLs(WKBundleRef bundleRef, WKBundlePageGroupRef pageGroupRef, bool excludeDocumentsInPageGroupPages)
+{
+    auto liveDocuments = toImpl(bundleRef)->liveDocumentURLs(toImpl(pageGroupRef), excludeDocumentsInPageGroupPages);
+
+    auto liveURLs = adoptWK(WKMutableArrayCreate());
+
+    for (const auto& it : liveDocuments) {
+        auto urlInfo = adoptWK(WKMutableDictionaryCreate());
+
+        auto documentIDKey = adoptWK(WKStringCreateWithUTF8CString("id"));
+        auto documentURLKey = adoptWK(WKStringCreateWithUTF8CString("url"));
+
+        auto documentIDValue = adoptWK(WKUInt64Create(it.key));
+        auto documentURLValue = adoptWK(toCopiedAPI(it.value));
+
+        WKDictionarySetItem(urlInfo.get(), documentIDKey.get(), documentIDValue.get());
+        WKDictionarySetItem(urlInfo.get(), documentURLKey.get(), documentURLValue.get());
+
+        WKArrayAppendItem(liveURLs.get(), urlInfo.get());
+    }
+    
+    return liveURLs.leakRef();
+}
+
 void WKBundleReportException(JSContextRef context, JSValueRef exception)
 {
     InjectedBundle::reportException(context, exception);
@@ -229,6 +260,16 @@
     DatabaseTracker::singleton().setQuota(*SecurityOriginData::fromDatabaseIdentifier("file__0"), quota);
 }
 
+void WKBundleClearPageCache(WKBundleRef bundle)
+{
+    PageCache::singleton().pruneToSizeNow(0, PruningReason::MemoryPressure);
+}
+
+void WKBundleClearMemoryCache(WKBundleRef bundle)
+{
+    MemoryCache::singleton().evictResources();
+}
+
 WKDataRef WKBundleCreateWKDataFromUInt8Array(WKBundleRef bundle, JSContextRef context, JSValueRef data)
 {
     return toAPI(&toImpl(bundle)->createWebDataFromUint8Array(context, data).leakRef());

Modified: trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp (235407 => 235408)


--- trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp	2018-08-27 23:31:15 UTC (rev 235408)
@@ -64,6 +64,7 @@
 #include <WebCore/PageOverlay.h>
 #include <WebCore/PageOverlayController.h>
 #include <WebCore/RenderLayerCompositor.h>
+#include <WebCore/ScriptExecutionContext.h>
 #include <WebCore/SecurityOriginData.h>
 #include <WebCore/URL.h>
 #include <WebCore/WheelEventTestTrigger.h>
@@ -619,6 +620,25 @@
     });
 }
 
+void WKBundlePagePostTask(WKBundlePageRef pageRef, WKBundlePageTestNotificationCallback callback, void* context)
+{
+    if (!callback)
+        return;
+    
+    WebKit::WebPage* webPage = toImpl(pageRef);
+    WebCore::Page* page = webPage ? webPage->corePage() : nullptr;
+    if (!page)
+        return;
+
+    WebCore::Document* document = page->mainFrame().document();
+    if (!document)
+        return;
+
+    document->postTask([=] (WebCore::ScriptExecutionContext&) {
+        callback(context);
+    });
+}
+
 void WKBundlePagePostMessage(WKBundlePageRef pageRef, WKStringRef messageNameRef, WKTypeRef messageBodyRef)
 {
     toImpl(pageRef)->postMessage(toWTFString(messageNameRef), toImpl(messageBodyRef));

Modified: trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.h (235407 => 235408)


--- trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.h	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.h	2018-08-27 23:31:15 UTC (rev 235408)
@@ -117,6 +117,9 @@
 typedef void (*WKBundlePageTestNotificationCallback)(void* context);
 WK_EXPORT void WKBundlePageRegisterScrollOperationCompletionCallback(WKBundlePageRef, WKBundlePageTestNotificationCallback, void* context);
 
+// Posts a task in the ScriptExecutionContext of the main frame. Used to do work after other tasks have completed.
+WK_EXPORT void WKBundlePagePostTask(WKBundlePageRef, WKBundlePageTestNotificationCallback, void* context);
+
 WK_EXPORT void WKBundlePagePostMessage(WKBundlePageRef page, WKStringRef messageName, WKTypeRef messageBody);
 
 // Switches a connection into a fully synchronous mode, so all messages become synchronous until we get a response.

Modified: trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePrivate.h (235407 => 235408)


--- trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePrivate.h	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePrivate.h	2018-08-27 23:31:15 UTC (rev 235408)
@@ -68,6 +68,8 @@
 WK_EXPORT uint64_t WKBundleGetWebNotificationID(WKBundleRef bundle, JSContextRef context, JSValueRef notification);
 WK_EXPORT WKDataRef WKBundleCreateWKDataFromUInt8Array(WKBundleRef bundle, JSContextRef context, JSValueRef data);
 WK_EXPORT void WKBundleSetAsynchronousSpellCheckingEnabled(WKBundleRef bundleRef, WKBundlePageGroupRef pageGroupRef, bool enabled);
+// Returns array of dictionaries. Dictionary keys are document identifiers, values are document URLs.
+WK_EXPORT WKArrayRef WKBundleGetLiveDocumentURLs(WKBundleRef bundle, WKBundlePageGroupRef pageGroup, bool excludeDocumentsInPageGroupPages);
 
 // UserContent API
 WK_EXPORT void WKBundleAddUserScript(WKBundleRef bundle, WKBundlePageGroupRef pageGroup, WKBundleScriptWorldRef scriptWorld, WKStringRef source, WKURLRef url, WKArrayRef whitelist, WKArrayRef blacklist, _WKUserScriptInjectionTime injectionTime, WKUserContentInjectedFrames injectedFrames);
@@ -96,6 +98,9 @@
 
 WK_EXPORT void WKBundleExtendClassesForParameterCoder(WKBundleRef bundle, WKArrayRef classes);
 
+WK_EXPORT void WKBundleClearPageCache(WKBundleRef bundle);
+WK_EXPORT void WKBundleClearMemoryCache(WKBundleRef bundle);
+
 #ifdef __cplusplus
 }
 #endif

Modified: trunk/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.cpp (235407 => 235408)


--- trunk/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.cpp	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.cpp	2018-08-27 23:31:15 UTC (rev 235408)
@@ -55,6 +55,7 @@
 #include <WebCore/ApplicationCache.h>
 #include <WebCore/ApplicationCacheStorage.h>
 #include <WebCore/CommonVM.h>
+#include <WebCore/Document.h>
 #include <WebCore/Frame.h>
 #include <WebCore/FrameLoader.h>
 #include <WebCore/FrameView.h>
@@ -604,6 +605,26 @@
     return API::Data::create(static_cast<unsigned char*>(arrayData->baseAddress()), arrayData->byteLength());
 }
 
+InjectedBundle::DocumentIDToURLMap InjectedBundle::liveDocumentURLs(WebPageGroupProxy* pageGroup, bool excludeDocumentsInPageGroupPages)
+{
+    DocumentIDToURLMap result;
+
+    for (const auto* document : Document::allDocuments())
+        result.add(document->identifier().toUInt64(), document->url().string());
+
+    if (excludeDocumentsInPageGroupPages) {
+        for (const auto* page : PageGroup::pageGroup(pageGroup->identifier())->pages()) {
+            for (const auto* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
+                if (!frame->document())
+                    continue;
+                result.remove(frame->document()->identifier().toUInt64());
+            }
+        }
+    }
+
+    return result;
+}
+
 void InjectedBundle::setTabKeyCyclesThroughElements(WebPage* page, bool enabled)
 {
     page->corePage()->setTabKeyCyclesThroughElements(enabled);

Modified: trunk/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.h (235407 => 235408)


--- trunk/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.h	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.h	2018-08-27 23:31:15 UTC (rev 235408)
@@ -122,6 +122,9 @@
     void removeAllWebNotificationPermissions(WebPage*);
     uint64_t webNotificationID(JSContextRef, JSValueRef);
     Ref<API::Data> createWebDataFromUint8Array(JSContextRef, JSValueRef);
+    
+    typedef HashMap<uint64_t, String> DocumentIDToURLMap;
+    DocumentIDToURLMap liveDocumentURLs(WebPageGroupProxy*, bool excludeDocumentsInPageGroupPages);
 
     // UserContent API
     void addUserScript(WebPageGroupProxy*, InjectedBundleScriptWorld*, String&& source, String&& url, API::Array* whitelist, API::Array* blacklist, WebCore::UserScriptInjectionTime, WebCore::UserContentInjectedFrames);

Modified: trunk/Tools/ChangeLog (235407 => 235408)


--- trunk/Tools/ChangeLog	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Tools/ChangeLog	2018-08-27 23:31:15 UTC (rev 235408)
@@ -1,3 +1,76 @@
+2018-08-27  Simon Fraser  <simon.fra...@apple.com>
+
+        Teach WebKitTestRunner and DumpRenderTree about detecting world leaks
+        https://bugs.webkit.org/show_bug.cgi?id=188994
+
+        Reviewed by Tim Horton.
+        
+        This patch adds the notion of a "control command" in the protocol between webkitpy and 
+        WebKitTestRunner/DumpRenderTree. A command is simply an input string starting with a #
+        that is checked for before trying to parse the input as test URL. For now, just one
+        commmand is supported, which is "#CHECK FOR WORLD LEAKS".
+        
+        In response to the command, the tool dumps an output block in the usual pseudo-MIME-style,
+        with a trailing "#EOF". Future patches will add support to webkitpy to parse this output.
+        
+        DumpRenderTree stubs out the command, returning an empty block.
+        
+        WebKitTestRunner responds to the command by dumping the list of live documents, if it was
+        run with the --check-for-world-leaks option.
+        
+        When run with --check-for-world-leaks, WebKitTestRunner gets the list of live documents via
+        WKBundleGetLiveDocumentURLs() after every test (this allows it to detect the first test
+        that leaked a document), and keeps them in a map of document identifier to test and live document URL.
+        Then when it receives the "#CHECK FOR WORLD LEAKS" command, it calls into the bundle to
+        clear the page and memory caches, runs a GC, then posts a task (in the Document::postTaks() sense)
+        after which it requests the list of live documents for a final time, excluding any that are loaded
+        in live Frames (thus omitting the about:blank that will be loaded at this point). Documents in this
+        list are therefore leaked (or abandoned).
+        
+        Future patches will hook up webkitpy reporting for leaked documents.
+
+        * DumpRenderTree/mac/DumpRenderTree.mm:
+        (initializeGlobalsFromCommandLineOptions):
+        (handleControlCommand):
+        (runTestingServerLoop):
+        * DumpRenderTree/win/DumpRenderTree.cpp:
+        (handleControlCommand):
+        (main):
+        * WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
+        (WTR::postGCTask):
+        (WTR::InjectedBundle::reportLiveDocuments):
+        (WTR::InjectedBundle::didReceiveMessageToPage):
+        * WebKitTestRunner/InjectedBundle/InjectedBundle.h:
+        * WebKitTestRunner/Options.cpp:
+        (WTR::handleOptionCheckForWorldLeaks):
+        (WTR::OptionsHandler::OptionsHandler):
+        * WebKitTestRunner/Options.h:
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::AsyncTask::run):
+        (WTR::AsyncTask::currentTask):
+        (WTR::TestController::initialize):
+        (WTR::TestController::ensureViewSupportsOptionsForTest):
+        (WTR::TestController::resetStateToConsistentValues):
+        (WTR::TestController::updateLiveDocumentsAfterTest):
+        (WTR::TestController::checkForWorldLeaks):
+        (WTR::TestController::findAndDumpWorldLeaks):
+        (WTR::TestController::willDestroyWebView):
+        (WTR::parseInputLine):
+        (WTR::TestController::waitForCompletion):
+        (WTR::TestController::handleControlCommand):
+        (WTR::TestController::runTestingServerLoop):
+        (WTR::TestController::run):
+        (WTR::TestController::didReceiveLiveDocumentsList):
+        (WTR::TestController::didReceiveMessageFromInjectedBundle):
+        * WebKitTestRunner/TestController.h:
+        (WTR::AsyncTask::AsyncTask):
+        (WTR::AsyncTask::taskComplete):
+        (WTR::TestController::AbandonedDocumentInfo::AbandonedDocumentInfo):
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::invoke):
+        * WebKitTestRunner/TestOptions.h:
+        (WTR::TestOptions::hasSameInitializationOptions const):
+
 2018-08-27  Alex Christensen  <achristen...@webkit.org>
 
         Fix API test after r235398

Modified: trunk/Tools/DumpRenderTree/mac/DumpRenderTree.mm (235407 => 235408)


--- trunk/Tools/DumpRenderTree/mac/DumpRenderTree.mm	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Tools/DumpRenderTree/mac/DumpRenderTree.mm	2018-08-27 23:31:15 UTC (rev 235408)
@@ -223,6 +223,7 @@
 static int allowAnyHTTPSCertificateForAllowedHosts;
 static int showWebView;
 static int printTestCount;
+static int checkForWorldLeaks;
 static BOOL printSeparators;
 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
 static std::set<std::string> allowedHosts;
@@ -1120,6 +1121,7 @@
         {"allow-any-certificate-for-allowed-hosts", no_argument, &allowAnyHTTPSCertificateForAllowedHosts, YES},
         {"show-webview", no_argument, &showWebView, YES},
         {"print-test-count", no_argument, &printTestCount, YES},
+        {"check-for-world-leaks", no_argument, &checkForWorldLeaks, NO},
         {nullptr, 0, nullptr, 0}
     };
 
@@ -1152,6 +1154,23 @@
     return (argc == optind+1 && strcmp(argv[optind], "-") == 0);
 }
 
+static bool handleControlCommand(const char* command)
+{
+    if (!strcmp("#CHECK FOR WORLD LEAKS", command)) {
+        // DumpRenderTree does not support checking for world leaks.
+        WTF::String result("\n");
+        printf("Content-Type: text/plain\n");
+        printf("Content-Length: %u\n", result.length());
+        fwrite(result.utf8().data(), 1, result.length(), stdout);
+        printf("#EOF\n");
+        fprintf(stderr, "#EOF\n");
+        fflush(stdout);
+        fflush(stderr);
+        return true;
+    }
+    return false;
+}
+
 static void runTestingServerLoop()
 {
     // When DumpRenderTree run in server mode, we just wait around for file names
@@ -1166,6 +1185,9 @@
         if (strlen(filenameBuffer) == 0)
             continue;
 
+        if (handleControlCommand(filenameBuffer))
+            continue;
+
         runTest(filenameBuffer);
 
         if (printTestCount) {

Modified: trunk/Tools/DumpRenderTree/win/DumpRenderTree.cpp (235407 => 235408)


--- trunk/Tools/DumpRenderTree/win/DumpRenderTree.cpp	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Tools/DumpRenderTree/win/DumpRenderTree.cpp	2018-08-27 23:31:15 UTC (rev 235408)
@@ -1112,6 +1112,22 @@
     ::setPersistentUserStyleSheetLocation(nullptr);
 }
 
+static bool handleControlCommand(const char* command)
+{
+    if (!strcmp("#CHECK FOR ABANDONED DOCUMENTS", command)) {
+        // DumpRenderTree does not support checking for abandonded documents.
+        String result("\n");
+        printf("Content-Type: text/plain\n");
+        printf("Content-Length: %u\n", result.length());
+        fwrite(result.utf8().data(), 1, result.length(), stdout);
+        printf("#EOF\n");
+        fprintf(stderr, "#EOF\n");
+        fflush(stdout);
+        fflush(stderr);
+        return true;
+    }
+    return false;
+}
 
 static void runTest(const string& inputLine)
 {
@@ -1616,6 +1632,9 @@
             if (strlen(filenameBuffer) == 0)
                 continue;
 
+            if (handleControlCommand(filenameBuffer))
+                continue;
+
             runTest(filenameBuffer);
         }
     } else {

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp (235407 => 235408)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp	2018-08-27 23:31:15 UTC (rev 235408)
@@ -180,6 +180,21 @@
     WKBundlePostMessage(m_bundle, errorMessageName.get(), errorMessageBody.get());
 }
 
+static void postGCTask(void* context)
+{
+    WKBundlePageRef page = reinterpret_cast<WKBundlePageRef>(context);
+    InjectedBundle::singleton().reportLiveDocuments(page);
+    WKRelease(page);
+}
+
+void InjectedBundle::reportLiveDocuments(WKBundlePageRef page)
+{
+    const bool excludeDocumentsInPageGroup = true;
+    auto documentURLs = adoptWK(WKBundleGetLiveDocumentURLs(m_bundle, m_pageGroup, excludeDocumentsInPageGroup));
+    auto ackMessageName = adoptWK(WKStringCreateWithUTF8CString("LiveDocuments"));
+    WKBundlePagePostMessage(page, ackMessageName.get(), documentURLs.get());
+}
+
 void InjectedBundle::didReceiveMessageToPage(WKBundlePageRef page, WKStringRef messageName, WKTypeRef messageBody)
 {
     if (WKStringIsEqualToUTF8CString(messageName, "BeginTest")) {
@@ -244,10 +259,27 @@
         TestRunner::removeAllWebNotificationPermissions();
 
         InjectedBundle::page()->resetAfterTest();
+        return;
+    }
 
+    if (WKStringIsEqualToUTF8CString(messageName, "GetLiveDocuments")) {
+        const bool excludeDocumentsInPageGroup = false;
+        auto documentURLs = adoptWK(WKBundleGetLiveDocumentURLs(m_bundle, m_pageGroup, excludeDocumentsInPageGroup));
+        auto ackMessageName = adoptWK(WKStringCreateWithUTF8CString("LiveDocuments"));
+        WKBundlePagePostMessage(page, ackMessageName.get(), documentURLs.get());
         return;
     }
 
+    if (WKStringIsEqualToUTF8CString(messageName, "CheckForWorldLeaks")) {
+        WKBundleClearPageCache(m_bundle);
+        WKBundleClearMemoryCache(m_bundle);
+        WKBundleGarbageCollectJavaScriptObjects(m_bundle);
+
+        WKRetain(page); // Balanced by the release in postGCTask.
+        WKBundlePagePostTask(page, postGCTask, (void*)page);
+        return;
+    }
+
     if (WKStringIsEqualToUTF8CString(messageName, "CallAddChromeInputFieldCallback")) {
         m_testRunner->callAddChromeInputFieldCallback();
         return;

Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h (235407 => 235408)


--- trunk/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h	2018-08-27 23:31:15 UTC (rev 235408)
@@ -140,6 +140,8 @@
     void textFieldDidBeginEditing();
     void textFieldDidEndEditing();
 
+    void reportLiveDocuments(WKBundlePageRef);
+
     void resetUserScriptInjectedCount() { m_userScriptInjectedCount = 0; }
     void increaseUserScriptInjectedCount() { ++m_userScriptInjectedCount; }
     size_t userScriptInjectedCount() const { return m_userScriptInjectedCount; }

Modified: trunk/Tools/WebKitTestRunner/Options.cpp (235407 => 235408)


--- trunk/Tools/WebKitTestRunner/Options.cpp	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Tools/WebKitTestRunner/Options.cpp	2018-08-27 23:31:15 UTC (rev 235408)
@@ -93,6 +93,12 @@
     return true;
 }
 
+static bool handleOptionCheckForWorldLeaks(Options& options, const char*, const char*)
+{
+    options.checkForWorldLeaks = true;
+    return true;
+}
+
 static bool handleOptionAllowAnyHTTPSCertificateForAllowedHosts(Options& options, const char*, const char*)
 {
     options.allowAnyHTTPSCertificateForAllowedHosts = true;
@@ -129,6 +135,7 @@
     optionList.append(Option("--allow-any-certificate-for-allowed-hosts", "Allows any HTTPS certificate for an allowed host.", handleOptionAllowAnyHTTPSCertificateForAllowedHosts));
     optionList.append(Option("--show-webview", "Show the WebView during test runs (for debugging)", handleOptionShowWebView));
     optionList.append(Option("--show-touches", "Show the touches during test runs (for debugging)", handleOptionShowTouches));
+    optionList.append(Option("--check-for-world-leaks", "Check for leaks of world objects (currently, documents)", handleOptionCheckForWorldLeaks));
 
     optionList.append(Option(0, 0, handleOptionUnmatched));
 }

Modified: trunk/Tools/WebKitTestRunner/Options.h (235407 => 235408)


--- trunk/Tools/WebKitTestRunner/Options.h	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Tools/WebKitTestRunner/Options.h	2018-08-27 23:31:15 UTC (rev 235408)
@@ -48,6 +48,7 @@
     bool shouldUseRemoteLayerTree { false };
     bool shouldShowWebView { false };
     bool shouldShowTouches { false };
+    bool checkForWorldLeaks { false };
     bool allowAnyHTTPSCertificateForAllowedHosts { false };
     std::vector<std::string> paths;
     std::set<std::string> allowedHosts;

Modified: trunk/Tools/WebKitTestRunner/TestController.cpp (235407 => 235408)


--- trunk/Tools/WebKitTestRunner/TestController.cpp	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Tools/WebKitTestRunner/TestController.cpp	2018-08-27 23:31:15 UTC (rev 235408)
@@ -115,6 +115,22 @@
     return WKStringCreateWithUTF8CString("MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue%2BPtwBRE6XfV%0AWtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID%0AAQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n%2FS%0Ar%2F7iJNroWlSzSMtTiQTEB%2BADWHGj9u1xrUrOilq%2Fo2cuQxIfZcNZkYAkWP4DubqW%0Ai0%2F%2FrgBvmco%3D");
 }
 
+AsyncTask* AsyncTask::m_currentTask;
+
+bool AsyncTask::run()
+{
+    m_currentTask = this;
+    m_task();
+    TestController::singleton().runUntil(m_taskDone, m_timeout);
+    m_currentTask = nullptr;
+    return m_taskDone;
+}
+
+AsyncTask* AsyncTask::currentTask()
+{
+    return m_currentTask;
+}
+
 static TestController* controller;
 
 TestController& TestController::singleton()
@@ -384,6 +400,7 @@
     m_allowedHosts = options.allowedHosts;
     m_shouldShowWebView = options.shouldShowWebView;
     m_shouldShowTouches = options.shouldShowTouches;
+    m_checkForWorldLeaks = options.checkForWorldLeaks;
     m_allowAnyHTTPSCertificateForAllowedHosts = options.allowAnyHTTPSCertificateForAllowedHosts;
 
     if (options.printSupportedFeatures) {
@@ -660,6 +677,8 @@
         if (m_mainWebView->viewSupportsOptions(options))
             return;
 
+        willDestroyWebView();
+
         WKPageSetPageUIClient(m_mainWebView->page(), nullptr);
         WKPageSetPageNavigationClient(m_mainWebView->page(), nullptr);
         WKPageClose(m_mainWebView->page());
@@ -669,7 +688,7 @@
 
     createWebViewWithOptions(options);
 
-    if (!resetStateToConsistentValues(options))
+    if (!resetStateToConsistentValues(options, ResetStage::BeforeTest))
         TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n");
 }
 
@@ -783,7 +802,7 @@
     platformResetPreferencesToConsistentValues();
 }
 
-bool TestController::resetStateToConsistentValues(const TestOptions& options)
+bool TestController::resetStateToConsistentValues(const TestOptions& options, ResetStage resetStage)
 {
     SetForScope<State> changeState(m_state, Resetting);
     m_beforeUnloadReturnValue = true;
@@ -911,9 +930,70 @@
     m_doneResetting = false;
     WKPageLoadURL(m_mainWebView->page(), blankURL());
     runUntil(m_doneResetting, m_currentInvocation->shortTimeout());
+    if (!m_doneResetting)
+        return false;
+    
+    if (resetStage == ResetStage::AfterTest && m_checkForWorldLeaks)
+        updateLiveDocumentsAfterTest();
+
     return m_doneResetting;
 }
 
+void TestController::updateLiveDocumentsAfterTest()
+{
+    AsyncTask([]() {
+        // After each test, we update the list of live documents so that we can detect when an abandoned document first showed up.
+        WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("GetLiveDocuments"));
+        WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
+    }, 5_s).run();
+}
+
+void TestController::checkForWorldLeaks()
+{
+    AsyncTask([]() {
+        // This runs at the end of a series of tests. It clears caches, runs a GC and then fetches the list of documents.
+        WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CheckForWorldLeaks"));
+        WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr);
+    }, 20_s).run();
+}
+
+void TestController::findAndDumpWorldLeaks()
+{
+    checkForWorldLeaks();
+
+    StringBuilder builder;
+    
+    if (m_abandonedDocumentInfo.size()) {
+        for (const auto& it : m_abandonedDocumentInfo) {
+            auto documentURL = it.value.abandonedDocumentURL;
+            if (documentURL.isEmpty())
+                documentURL = "(no url)";
+            builder.append("TEST: ");
+            builder.append(it.value.testURL);
+            builder.append('\n');
+            builder.append("ABANDONED DOCUMENT: ");
+            builder.append(documentURL);
+            builder.append('\n');
+        }
+    } else
+        builder.append("no abandoned documents");
+
+    String result = builder.toString();
+    printf("Content-Type: text/plain\n");
+    printf("Content-Length: %u\n", result.length());
+    fwrite(result.utf8().data(), 1, result.length(), stdout);
+    printf("#EOF\n");
+    fprintf(stderr, "#EOF\n");
+    fflush(stdout);
+    fflush(stderr);
+}
+
+void TestController::willDestroyWebView()
+{
+    // Before we kill the web view, look for abandoned documents before that web process goes away.
+    checkForWorldLeaks();
+}
+
 void TestController::terminateWebContentProcess()
 {
     WKPageTerminate(m_mainWebView->page());
@@ -1238,7 +1318,7 @@
     exit(1);
 }
 
-TestCommand parseInputLine(const std::string& inputLine)
+static TestCommand parseInputLine(const std::string& inputLine)
 {
     TestCommand result;
     CommandTokenizer tokenizer(inputLine);
@@ -1296,6 +1376,23 @@
     return true;
 }
 
+bool TestController::waitForCompletion(const WTF::Function<void ()>& function, WTF::Seconds timeout)
+{
+    m_doneResetting = false;
+    function();
+    runUntil(m_doneResetting, timeout);
+    return !m_doneResetting;
+}
+
+bool TestController::handleControlCommand(const char* command)
+{
+    if (!strcmp("#CHECK FOR WORLD LEAKS", command)) {
+        findAndDumpWorldLeaks();
+        return true;
+    }
+    return false;
+}
+
 void TestController::runTestingServerLoop()
 {
     char filenameBuffer[2048];
@@ -1307,6 +1404,9 @@
         if (strlen(filenameBuffer) == 0)
             continue;
 
+        if (handleControlCommand(filenameBuffer))
+            continue;
+
         if (!runTest(filenameBuffer))
             break;
     }
@@ -1321,6 +1421,7 @@
             if (!runTest(m_paths[i].c_str()))
                 break;
         }
+        findAndDumpWorldLeaks();
     }
 }
 
@@ -1385,8 +1486,51 @@
     m_eventSenderProxy->keyDown(key, modifiers, location);
 }
 
+void TestController::didReceiveLiveDocumentsList(WKArrayRef liveDocumentList)
+{
+    auto numDocuments = WKArrayGetSize(liveDocumentList);
+
+    HashMap<uint64_t, String> documentInfo;
+    for (size_t i = 0; i < numDocuments; ++i) {
+        WKTypeRef item = WKArrayGetItemAtIndex(liveDocumentList, i);
+        if (item && WKGetTypeID(item) == WKDictionaryGetTypeID()) {
+            WKDictionaryRef liveDocumentItem = static_cast<WKDictionaryRef>(item);
+
+            WKRetainPtr<WKStringRef> idKey(AdoptWK, WKStringCreateWithUTF8CString("id"));
+            WKUInt64Ref documentID = static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(liveDocumentItem, idKey.get()));
+
+            WKRetainPtr<WKStringRef> urlKey(AdoptWK, WKStringCreateWithUTF8CString("url"));
+            WKStringRef documentURL = static_cast<WKStringRef>(WKDictionaryGetItemForKey(liveDocumentItem, urlKey.get()));
+
+            documentInfo.add(WKUInt64GetValue(documentID), toWTFString(documentURL));
+        }
+    }
+
+    if (!documentInfo.size()) {
+        m_abandonedDocumentInfo.clear();
+        return;
+    }
+
+    // Remove any documents which are no longer live.
+    m_abandonedDocumentInfo.removeIf([&](auto& keyAndValue) {
+        return !documentInfo.contains(keyAndValue.key);
+    });
+    
+    // Add newly abandoned documents.
+    String currentTestURL = m_currentInvocation ? toWTFString(adoptWK(WKURLCopyString(m_currentInvocation->url()))) : "no test";
+    for (const auto& it : documentInfo)
+        m_abandonedDocumentInfo.add(it.key, AbandonedDocumentInfo(currentTestURL, it.value));
+}
+
 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody)
 {
+    if (WKStringIsEqualToUTF8CString(messageName, "LiveDocuments")) {
+        ASSERT(WKGetTypeID(messageBody) == WKArrayGetTypeID());
+        didReceiveLiveDocumentsList(static_cast<WKArrayRef>(messageBody));
+        AsyncTask::currentTask()->taskComplete();
+        return;
+    }
+
     if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) {
         if (m_state != RunningTest)
             return;

Modified: trunk/Tools/WebKitTestRunner/TestController.h (235407 => 235408)


--- trunk/Tools/WebKitTestRunner/TestController.h	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Tools/WebKitTestRunner/TestController.h	2018-08-27 23:31:15 UTC (rev 235408)
@@ -48,6 +48,33 @@
 struct TestCommand;
 struct TestOptions;
 
+class AsyncTask {
+public:
+    AsyncTask(WTF::Function<void ()>&& task, WTF::Seconds timeout)
+        : m_task(WTFMove(task))
+        , m_timeout(timeout)
+    {
+        ASSERT(!currentTask());
+    }
+
+    // Returns false on timeout.
+    bool run();
+
+    void taskComplete()
+    {
+        m_taskDone = true;
+    }
+
+    static AsyncTask* currentTask();
+
+private:
+    static AsyncTask* m_currentTask;
+
+    WTF::Function<void ()> m_task;
+    WTF::Seconds m_timeout;
+    bool m_taskDone { false };
+};
+
 // FIXME: Rename this TestRunner?
 class TestController {
 public:
@@ -121,9 +148,12 @@
 
     unsigned imageCountInGeneralPasteboard() const;
 
-    bool resetStateToConsistentValues(const TestOptions&);
+    enum class ResetStage { BeforeTest, AfterTest };
+    bool resetStateToConsistentValues(const TestOptions&, ResetStage);
     void resetPreferencesToConsistentValues(const TestOptions&);
 
+    void willDestroyWebView();
+
     void terminateWebContentProcess();
     void reattachPageToWebProcess();
 
@@ -231,7 +261,12 @@
 
     void runTestingServerLoop();
     bool runTest(const char* pathOrURL);
+    
+    // Returns false if timed out.
+    bool waitForCompletion(const WTF::Function<void ()>&, WTF::Seconds timeout);
 
+    bool handleControlCommand(const char* command);
+
     void platformInitialize();
     void platformDestroy();
     WKContextRef platformAdjustContext(WKContextRef, WKContextConfigurationRef);
@@ -261,6 +296,12 @@
     void updateWebViewSizeForTest(const TestInvocation&);
     void updateWindowScaleForTest(PlatformWebView*, const TestInvocation&);
 
+    void updateLiveDocumentsAfterTest();
+    void checkForWorldLeaks();
+
+    void didReceiveLiveDocumentsList(WKArrayRef);
+    void findAndDumpWorldLeaks();
+
     void decidePolicyForGeolocationPermissionRequestIfPossible();
     void decidePolicyForUserMediaPermissionRequestIfPossible();
 
@@ -431,6 +472,7 @@
     bool m_shouldShowWebView { false };
     
     bool m_shouldShowTouches { false };
+    bool m_checkForWorldLeaks { false };
 
     bool m_allowAnyHTTPSCertificateForAllowedHosts { false };
     
@@ -444,6 +486,18 @@
     std::unique_ptr<EventSenderProxy> m_eventSenderProxy;
 
     WorkQueueManager m_workQueueManager;
+
+    struct AbandonedDocumentInfo {
+        String testURL;
+        String abandonedDocumentURL;
+
+        AbandonedDocumentInfo() = default;
+        AbandonedDocumentInfo(String inTestURL, String inAbandonedDocumentURL)
+            : testURL(inTestURL)
+            , abandonedDocumentURL(inAbandonedDocumentURL)
+        { }
+    };
+    HashMap<uint64_t, AbandonedDocumentInfo> m_abandonedDocumentInfo;
 };
 
 struct TestCommand {

Modified: trunk/Tools/WebKitTestRunner/TestInvocation.cpp (235407 => 235408)


--- trunk/Tools/WebKitTestRunner/TestInvocation.cpp	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Tools/WebKitTestRunner/TestInvocation.cpp	2018-08-27 23:31:15 UTC (rev 235408)
@@ -187,7 +187,7 @@
         WKInspectorClose(WKPageGetInspector(TestController::singleton().mainWebView()->page()));
 #endif // !PLATFORM(IOS)
 
-    if (TestController::singleton().resetStateToConsistentValues(m_options))
+    if (TestController::singleton().resetStateToConsistentValues(m_options, TestController::ResetStage::AfterTest))
         return;
 
     // The process is unresponsive, so let's start a new one.

Modified: trunk/Tools/WebKitTestRunner/TestOptions.h (235407 => 235408)


--- trunk/Tools/WebKitTestRunner/TestOptions.h	2018-08-27 23:29:42 UTC (rev 235407)
+++ trunk/Tools/WebKitTestRunner/TestOptions.h	2018-08-27 23:31:15 UTC (rev 235408)
@@ -60,6 +60,7 @@
     bool enableColorFilter { false };
     bool punchOutWhiteBackgroundsInDarkMode { false };
     bool runSingly { false };
+    bool checkForWorldLeaks { false };
 
     float deviceScaleFactor { 1 };
     Vector<String> overrideLanguages;
@@ -97,7 +98,8 @@
             || enableColorFilter != options.enableColorFilter
             || punchOutWhiteBackgroundsInDarkMode != options.punchOutWhiteBackgroundsInDarkMode
             || jscOptions != options.jscOptions
-            || runSingly != options.runSingly)
+            || runSingly != options.runSingly
+            || checkForWorldLeaks != options.checkForWorldLeaks)
             return false;
 
         return true;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to