Changeset 235408 in webkit
- Timestamp:
- Aug 27, 2018 4:31:15 PM (6 years ago)
- Location:
- trunk
- Files:
-
- 20 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r235403 r235408 1 2018-08-27 Simon Fraser <simon.fraser@apple.com> 2 3 Teach WebKitTestRunner and DumpRenderTree about detecting world leaks 4 https://bugs.webkit.org/show_bug.cgi?id=188994 5 6 Reviewed by Tim Horton. 7 8 Export Document::postTask() for use by WTR's injected bundle. 9 10 * dom/Document.h: 11 1 12 2018-08-27 Aditya Keerthi <akeerthi@apple.com> 2 13 -
trunk/Source/WebCore/dom/Document.h
r235014 r235408 1031 1031 void parseDNSPrefetchControlHeader(const String&); 1032 1032 1033 void postTask(Task&&) final; // Executes the task on context's thread asynchronously.1033 WEBCORE_EXPORT void postTask(Task&&) final; // Executes the task on context's thread asynchronously. 1034 1034 1035 1035 ScriptedAnimationController* scriptedAnimationController() { return m_scriptedAnimationController.get(); } -
trunk/Source/WebKit/ChangeLog
r235407 r235408 1 2018-08-27 Simon Fraser <simon.fraser@apple.com> 2 3 Teach WebKitTestRunner and DumpRenderTree about detecting world leaks 4 https://bugs.webkit.org/show_bug.cgi?id=188994 5 6 Reviewed by Tim Horton. 7 8 This patch adds the notion of a "control command" in the protocol between webkitpy and 9 WebKitTestRunner/DumpRenderTree. A command is simply an input string starting with a # 10 that is checked for before trying to parse the input as test URL. For now, just one 11 commmand is supported, which is "#CHECK FOR WORLD LEAKS". 12 13 In response to the command, the tool dumps an output block in the usual pseudo-MIME-style, 14 with a trailing "#EOF". Future patches will add support to webkitpy to parse this output. 15 16 DumpRenderTree stubs out the command, returning an empty block. 17 18 WebKitTestRunner responds to the command by dumping the list of live documents, if it was 19 run with the --check-for-world-leaks option. 20 21 When run with --check-for-world-leaks, WebKitTestRunner gets the list of live documents via 22 WKBundleGetLiveDocumentURLs() after every test (this allows it to detect the first test 23 that leaked a document), and keeps them in a map of document identifier to test and live document URL. 24 Then when it receives the "#CHECK FOR WORLD LEAKS" command, it calls into the bundle to 25 clear the page and memory caches, runs a GC, then posts a task (in the Document::postTaks() sense) 26 after which it requests the list of live documents for a final time, excluding any that are loaded 27 in live Frames (thus omitting the about:blank that will be loaded at this point). Documents in this 28 list are therefore leaked (or abandoned). 29 30 Future patches will hook up webkitpy reporting for leaked documents. 31 32 * WebProcess/InjectedBundle/API/c/WKBundle.cpp: 33 (WKBundleGetLiveDocumentURLs): 34 (WKBundleClearPageCache): 35 (WKBundleClearMemoryCache): 36 * WebProcess/InjectedBundle/API/c/WKBundlePage.cpp: 37 (WKBundlePagePostTask): 38 * WebProcess/InjectedBundle/API/c/WKBundlePage.h: 39 * WebProcess/InjectedBundle/API/c/WKBundlePrivate.h: 40 * WebProcess/InjectedBundle/InjectedBundle.cpp: 41 (WebKit::InjectedBundle::liveDocumentURLs): 42 * WebProcess/InjectedBundle/InjectedBundle.h: 43 1 44 2018-08-27 Alex Christensen <achristensen@webkit.org> 2 45 -
trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundle.cpp
r233400 r235408 35 35 #include "WKBundleAPICast.h" 36 36 #include "WKBundlePrivate.h" 37 #include "WKMutableArray.h" 38 #include "WKMutableDictionary.h" 39 #include "WKNumber.h" 40 #include "WKRetainPtr.h" 41 #include "WKString.h" 37 42 #include "WebConnection.h" 38 43 #include "WebFrame.h" … … 40 45 #include "WebPageGroupProxy.h" 41 46 #include <WebCore/DatabaseTracker.h> 47 #include <WebCore/MemoryCache.h> 48 #include <WebCore/PageCache.h> 42 49 #include <WebCore/ResourceLoadObserver.h> 43 50 #include <WebCore/ServiceWorkerThreadProxy.h> … … 214 221 } 215 222 223 WKArrayRef WKBundleGetLiveDocumentURLs(WKBundleRef bundleRef, WKBundlePageGroupRef pageGroupRef, bool excludeDocumentsInPageGroupPages) 224 { 225 auto liveDocuments = toImpl(bundleRef)->liveDocumentURLs(toImpl(pageGroupRef), excludeDocumentsInPageGroupPages); 226 227 auto liveURLs = adoptWK(WKMutableArrayCreate()); 228 229 for (const auto& it : liveDocuments) { 230 auto urlInfo = adoptWK(WKMutableDictionaryCreate()); 231 232 auto documentIDKey = adoptWK(WKStringCreateWithUTF8CString("id")); 233 auto documentURLKey = adoptWK(WKStringCreateWithUTF8CString("url")); 234 235 auto documentIDValue = adoptWK(WKUInt64Create(it.key)); 236 auto documentURLValue = adoptWK(toCopiedAPI(it.value)); 237 238 WKDictionarySetItem(urlInfo.get(), documentIDKey.get(), documentIDValue.get()); 239 WKDictionarySetItem(urlInfo.get(), documentURLKey.get(), documentURLValue.get()); 240 241 WKArrayAppendItem(liveURLs.get(), urlInfo.get()); 242 } 243 244 return liveURLs.leakRef(); 245 } 246 216 247 void WKBundleReportException(JSContextRef context, JSValueRef exception) 217 248 { … … 228 259 // Historically, we've used the following (somewhat nonsensical) string for the databaseIdentifier of local files. 229 260 DatabaseTracker::singleton().setQuota(*SecurityOriginData::fromDatabaseIdentifier("file__0"), quota); 261 } 262 263 void WKBundleClearPageCache(WKBundleRef bundle) 264 { 265 PageCache::singleton().pruneToSizeNow(0, PruningReason::MemoryPressure); 266 } 267 268 void WKBundleClearMemoryCache(WKBundleRef bundle) 269 { 270 MemoryCache::singleton().evictResources(); 230 271 } 231 272 -
trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.cpp
r234330 r235408 65 65 #include <WebCore/PageOverlayController.h> 66 66 #include <WebCore/RenderLayerCompositor.h> 67 #include <WebCore/ScriptExecutionContext.h> 67 68 #include <WebCore/SecurityOriginData.h> 68 69 #include <WebCore/URL.h> … … 616 617 617 618 page->ensureTestTrigger().setTestCallbackAndStartNotificationTimer([=]() { 619 callback(context); 620 }); 621 } 622 623 void WKBundlePagePostTask(WKBundlePageRef pageRef, WKBundlePageTestNotificationCallback callback, void* context) 624 { 625 if (!callback) 626 return; 627 628 WebKit::WebPage* webPage = toImpl(pageRef); 629 WebCore::Page* page = webPage ? webPage->corePage() : nullptr; 630 if (!page) 631 return; 632 633 WebCore::Document* document = page->mainFrame().document(); 634 if (!document) 635 return; 636 637 document->postTask([=] (WebCore::ScriptExecutionContext&) { 618 638 callback(context); 619 639 }); -
trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePage.h
r226471 r235408 118 118 WK_EXPORT void WKBundlePageRegisterScrollOperationCompletionCallback(WKBundlePageRef, WKBundlePageTestNotificationCallback, void* context); 119 119 120 // Posts a task in the ScriptExecutionContext of the main frame. Used to do work after other tasks have completed. 121 WK_EXPORT void WKBundlePagePostTask(WKBundlePageRef, WKBundlePageTestNotificationCallback, void* context); 122 120 123 WK_EXPORT void WKBundlePagePostMessage(WKBundlePageRef page, WKStringRef messageName, WKTypeRef messageBody); 121 124 -
trunk/Source/WebKit/WebProcess/InjectedBundle/API/c/WKBundlePrivate.h
r233207 r235408 69 69 WK_EXPORT WKDataRef WKBundleCreateWKDataFromUInt8Array(WKBundleRef bundle, JSContextRef context, JSValueRef data); 70 70 WK_EXPORT void WKBundleSetAsynchronousSpellCheckingEnabled(WKBundleRef bundleRef, WKBundlePageGroupRef pageGroupRef, bool enabled); 71 // Returns array of dictionaries. Dictionary keys are document identifiers, values are document URLs. 72 WK_EXPORT WKArrayRef WKBundleGetLiveDocumentURLs(WKBundleRef bundle, WKBundlePageGroupRef pageGroup, bool excludeDocumentsInPageGroupPages); 71 73 72 74 // UserContent API … … 97 99 WK_EXPORT void WKBundleExtendClassesForParameterCoder(WKBundleRef bundle, WKArrayRef classes); 98 100 101 WK_EXPORT void WKBundleClearPageCache(WKBundleRef bundle); 102 WK_EXPORT void WKBundleClearMemoryCache(WKBundleRef bundle); 103 99 104 #ifdef __cplusplus 100 105 } -
trunk/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.cpp
r235205 r235408 56 56 #include <WebCore/ApplicationCacheStorage.h> 57 57 #include <WebCore/CommonVM.h> 58 #include <WebCore/Document.h> 58 59 #include <WebCore/Frame.h> 59 60 #include <WebCore/FrameLoader.h> … … 605 606 } 606 607 608 InjectedBundle::DocumentIDToURLMap InjectedBundle::liveDocumentURLs(WebPageGroupProxy* pageGroup, bool excludeDocumentsInPageGroupPages) 609 { 610 DocumentIDToURLMap result; 611 612 for (const auto* document : Document::allDocuments()) 613 result.add(document->identifier().toUInt64(), document->url().string()); 614 615 if (excludeDocumentsInPageGroupPages) { 616 for (const auto* page : PageGroup::pageGroup(pageGroup->identifier())->pages()) { 617 for (const auto* frame = &page->mainFrame(); frame; frame = frame->tree().traverseNext()) { 618 if (!frame->document()) 619 continue; 620 result.remove(frame->document()->identifier().toUInt64()); 621 } 622 } 623 } 624 625 return result; 626 } 627 607 628 void InjectedBundle::setTabKeyCyclesThroughElements(WebPage* page, bool enabled) 608 629 { -
trunk/Source/WebKit/WebProcess/InjectedBundle/InjectedBundle.h
r233207 r235408 123 123 uint64_t webNotificationID(JSContextRef, JSValueRef); 124 124 Ref<API::Data> createWebDataFromUint8Array(JSContextRef, JSValueRef); 125 126 typedef HashMap<uint64_t, String> DocumentIDToURLMap; 127 DocumentIDToURLMap liveDocumentURLs(WebPageGroupProxy*, bool excludeDocumentsInPageGroupPages); 125 128 126 129 // UserContent API -
trunk/Tools/ChangeLog
r235405 r235408 1 2018-08-27 Simon Fraser <simon.fraser@apple.com> 2 3 Teach WebKitTestRunner and DumpRenderTree about detecting world leaks 4 https://bugs.webkit.org/show_bug.cgi?id=188994 5 6 Reviewed by Tim Horton. 7 8 This patch adds the notion of a "control command" in the protocol between webkitpy and 9 WebKitTestRunner/DumpRenderTree. A command is simply an input string starting with a # 10 that is checked for before trying to parse the input as test URL. For now, just one 11 commmand is supported, which is "#CHECK FOR WORLD LEAKS". 12 13 In response to the command, the tool dumps an output block in the usual pseudo-MIME-style, 14 with a trailing "#EOF". Future patches will add support to webkitpy to parse this output. 15 16 DumpRenderTree stubs out the command, returning an empty block. 17 18 WebKitTestRunner responds to the command by dumping the list of live documents, if it was 19 run with the --check-for-world-leaks option. 20 21 When run with --check-for-world-leaks, WebKitTestRunner gets the list of live documents via 22 WKBundleGetLiveDocumentURLs() after every test (this allows it to detect the first test 23 that leaked a document), and keeps them in a map of document identifier to test and live document URL. 24 Then when it receives the "#CHECK FOR WORLD LEAKS" command, it calls into the bundle to 25 clear the page and memory caches, runs a GC, then posts a task (in the Document::postTaks() sense) 26 after which it requests the list of live documents for a final time, excluding any that are loaded 27 in live Frames (thus omitting the about:blank that will be loaded at this point). Documents in this 28 list are therefore leaked (or abandoned). 29 30 Future patches will hook up webkitpy reporting for leaked documents. 31 32 * DumpRenderTree/mac/DumpRenderTree.mm: 33 (initializeGlobalsFromCommandLineOptions): 34 (handleControlCommand): 35 (runTestingServerLoop): 36 * DumpRenderTree/win/DumpRenderTree.cpp: 37 (handleControlCommand): 38 (main): 39 * WebKitTestRunner/InjectedBundle/InjectedBundle.cpp: 40 (WTR::postGCTask): 41 (WTR::InjectedBundle::reportLiveDocuments): 42 (WTR::InjectedBundle::didReceiveMessageToPage): 43 * WebKitTestRunner/InjectedBundle/InjectedBundle.h: 44 * WebKitTestRunner/Options.cpp: 45 (WTR::handleOptionCheckForWorldLeaks): 46 (WTR::OptionsHandler::OptionsHandler): 47 * WebKitTestRunner/Options.h: 48 * WebKitTestRunner/TestController.cpp: 49 (WTR::AsyncTask::run): 50 (WTR::AsyncTask::currentTask): 51 (WTR::TestController::initialize): 52 (WTR::TestController::ensureViewSupportsOptionsForTest): 53 (WTR::TestController::resetStateToConsistentValues): 54 (WTR::TestController::updateLiveDocumentsAfterTest): 55 (WTR::TestController::checkForWorldLeaks): 56 (WTR::TestController::findAndDumpWorldLeaks): 57 (WTR::TestController::willDestroyWebView): 58 (WTR::parseInputLine): 59 (WTR::TestController::waitForCompletion): 60 (WTR::TestController::handleControlCommand): 61 (WTR::TestController::runTestingServerLoop): 62 (WTR::TestController::run): 63 (WTR::TestController::didReceiveLiveDocumentsList): 64 (WTR::TestController::didReceiveMessageFromInjectedBundle): 65 * WebKitTestRunner/TestController.h: 66 (WTR::AsyncTask::AsyncTask): 67 (WTR::AsyncTask::taskComplete): 68 (WTR::TestController::AbandonedDocumentInfo::AbandonedDocumentInfo): 69 * WebKitTestRunner/TestInvocation.cpp: 70 (WTR::TestInvocation::invoke): 71 * WebKitTestRunner/TestOptions.h: 72 (WTR::TestOptions::hasSameInitializationOptions const): 73 1 74 2018-08-27 Alex Christensen <achristensen@webkit.org> 2 75 -
trunk/Tools/DumpRenderTree/mac/DumpRenderTree.mm
r234940 r235408 224 224 static int showWebView; 225 225 static int printTestCount; 226 static int checkForWorldLeaks; 226 227 static BOOL printSeparators; 227 228 static RetainPtr<CFStringRef> persistentUserStyleSheetLocation; … … 1121 1122 {"show-webview", no_argument, &showWebView, YES}, 1122 1123 {"print-test-count", no_argument, &printTestCount, YES}, 1124 {"check-for-world-leaks", no_argument, &checkForWorldLeaks, NO}, 1123 1125 {nullptr, 0, nullptr, 0} 1124 1126 }; … … 1153 1155 } 1154 1156 1157 static bool handleControlCommand(const char* command) 1158 { 1159 if (!strcmp("#CHECK FOR WORLD LEAKS", command)) { 1160 // DumpRenderTree does not support checking for world leaks. 1161 WTF::String result("\n"); 1162 printf("Content-Type: text/plain\n"); 1163 printf("Content-Length: %u\n", result.length()); 1164 fwrite(result.utf8().data(), 1, result.length(), stdout); 1165 printf("#EOF\n"); 1166 fprintf(stderr, "#EOF\n"); 1167 fflush(stdout); 1168 fflush(stderr); 1169 return true; 1170 } 1171 return false; 1172 } 1173 1155 1174 static void runTestingServerLoop() 1156 1175 { … … 1165 1184 1166 1185 if (strlen(filenameBuffer) == 0) 1186 continue; 1187 1188 if (handleControlCommand(filenameBuffer)) 1167 1189 continue; 1168 1190 -
trunk/Tools/DumpRenderTree/win/DumpRenderTree.cpp
r234948 r235408 1113 1113 } 1114 1114 1115 static bool handleControlCommand(const char* command) 1116 { 1117 if (!strcmp("#CHECK FOR ABANDONED DOCUMENTS", command)) { 1118 // DumpRenderTree does not support checking for abandonded documents. 1119 String result("\n"); 1120 printf("Content-Type: text/plain\n"); 1121 printf("Content-Length: %u\n", result.length()); 1122 fwrite(result.utf8().data(), 1, result.length(), stdout); 1123 printf("#EOF\n"); 1124 fprintf(stderr, "#EOF\n"); 1125 fflush(stdout); 1126 fflush(stderr); 1127 return true; 1128 } 1129 return false; 1130 } 1115 1131 1116 1132 static void runTest(const string& inputLine) … … 1617 1633 continue; 1618 1634 1635 if (handleControlCommand(filenameBuffer)) 1636 continue; 1637 1619 1638 runTest(filenameBuffer); 1620 1639 } -
trunk/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp
r235399 r235408 181 181 } 182 182 183 static void postGCTask(void* context) 184 { 185 WKBundlePageRef page = reinterpret_cast<WKBundlePageRef>(context); 186 InjectedBundle::singleton().reportLiveDocuments(page); 187 WKRelease(page); 188 } 189 190 void InjectedBundle::reportLiveDocuments(WKBundlePageRef page) 191 { 192 const bool excludeDocumentsInPageGroup = true; 193 auto documentURLs = adoptWK(WKBundleGetLiveDocumentURLs(m_bundle, m_pageGroup, excludeDocumentsInPageGroup)); 194 auto ackMessageName = adoptWK(WKStringCreateWithUTF8CString("LiveDocuments")); 195 WKBundlePagePostMessage(page, ackMessageName.get(), documentURLs.get()); 196 } 197 183 198 void InjectedBundle::didReceiveMessageToPage(WKBundlePageRef page, WKStringRef messageName, WKTypeRef messageBody) 184 199 { … … 245 260 246 261 InjectedBundle::page()->resetAfterTest(); 247 262 return; 263 } 264 265 if (WKStringIsEqualToUTF8CString(messageName, "GetLiveDocuments")) { 266 const bool excludeDocumentsInPageGroup = false; 267 auto documentURLs = adoptWK(WKBundleGetLiveDocumentURLs(m_bundle, m_pageGroup, excludeDocumentsInPageGroup)); 268 auto ackMessageName = adoptWK(WKStringCreateWithUTF8CString("LiveDocuments")); 269 WKBundlePagePostMessage(page, ackMessageName.get(), documentURLs.get()); 270 return; 271 } 272 273 if (WKStringIsEqualToUTF8CString(messageName, "CheckForWorldLeaks")) { 274 WKBundleClearPageCache(m_bundle); 275 WKBundleClearMemoryCache(m_bundle); 276 WKBundleGarbageCollectJavaScriptObjects(m_bundle); 277 278 WKRetain(page); // Balanced by the release in postGCTask. 279 WKBundlePagePostTask(page, postGCTask, (void*)page); 248 280 return; 249 281 } -
trunk/Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h
r235399 r235408 141 141 void textFieldDidEndEditing(); 142 142 143 void reportLiveDocuments(WKBundlePageRef); 144 143 145 void resetUserScriptInjectedCount() { m_userScriptInjectedCount = 0; } 144 146 void increaseUserScriptInjectedCount() { ++m_userScriptInjectedCount; } -
trunk/Tools/WebKitTestRunner/Options.cpp
r230192 r235408 94 94 } 95 95 96 static bool handleOptionCheckForWorldLeaks(Options& options, const char*, const char*) 97 { 98 options.checkForWorldLeaks = true; 99 return true; 100 } 101 96 102 static bool handleOptionAllowAnyHTTPSCertificateForAllowedHosts(Options& options, const char*, const char*) 97 103 { … … 130 136 optionList.append(Option("--show-webview", "Show the WebView during test runs (for debugging)", handleOptionShowWebView)); 131 137 optionList.append(Option("--show-touches", "Show the touches during test runs (for debugging)", handleOptionShowTouches)); 138 optionList.append(Option("--check-for-world-leaks", "Check for leaks of world objects (currently, documents)", handleOptionCheckForWorldLeaks)); 132 139 133 140 optionList.append(Option(0, 0, handleOptionUnmatched)); -
trunk/Tools/WebKitTestRunner/Options.h
r230192 r235408 49 49 bool shouldShowWebView { false }; 50 50 bool shouldShowTouches { false }; 51 bool checkForWorldLeaks { false }; 51 52 bool allowAnyHTTPSCertificateForAllowedHosts { false }; 52 53 std::vector<std::string> paths; -
trunk/Tools/WebKitTestRunner/TestController.cpp
r235399 r235408 114 114 // Any fake response would do, all we need for testing is to implement the callback. 115 115 return WKStringCreateWithUTF8CString("MIHFMHEwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAnX0TILJrOMUue%2BPtwBRE6XfV%0AWtKQbsshxk5ZhcUwcwyvcnIq9b82QhJdoACdD34rqfCAIND46fXKQUnb0mvKzQID%0AAQABFhFNb3ppbGxhSXNNeUZyaWVuZDANBgkqhkiG9w0BAQQFAANBAAKv2Eex2n%2FS%0Ar%2F7iJNroWlSzSMtTiQTEB%2BADWHGj9u1xrUrOilq%2Fo2cuQxIfZcNZkYAkWP4DubqW%0Ai0%2F%2FrgBvmco%3D"); 116 } 117 118 AsyncTask* AsyncTask::m_currentTask; 119 120 bool AsyncTask::run() 121 { 122 m_currentTask = this; 123 m_task(); 124 TestController::singleton().runUntil(m_taskDone, m_timeout); 125 m_currentTask = nullptr; 126 return m_taskDone; 127 } 128 129 AsyncTask* AsyncTask::currentTask() 130 { 131 return m_currentTask; 116 132 } 117 133 … … 385 401 m_shouldShowWebView = options.shouldShowWebView; 386 402 m_shouldShowTouches = options.shouldShowTouches; 403 m_checkForWorldLeaks = options.checkForWorldLeaks; 387 404 m_allowAnyHTTPSCertificateForAllowedHosts = options.allowAnyHTTPSCertificateForAllowedHosts; 388 405 … … 661 678 return; 662 679 680 willDestroyWebView(); 681 663 682 WKPageSetPageUIClient(m_mainWebView->page(), nullptr); 664 683 WKPageSetPageNavigationClient(m_mainWebView->page(), nullptr); … … 670 689 createWebViewWithOptions(options); 671 690 672 if (!resetStateToConsistentValues(options ))691 if (!resetStateToConsistentValues(options, ResetStage::BeforeTest)) 673 692 TestInvocation::dumpWebProcessUnresponsiveness("<unknown> - TestController::run - Failed to reset state to consistent values\n"); 674 693 } … … 784 803 } 785 804 786 bool TestController::resetStateToConsistentValues(const TestOptions& options )805 bool TestController::resetStateToConsistentValues(const TestOptions& options, ResetStage resetStage) 787 806 { 788 807 SetForScope<State> changeState(m_state, Resetting); … … 912 931 WKPageLoadURL(m_mainWebView->page(), blankURL()); 913 932 runUntil(m_doneResetting, m_currentInvocation->shortTimeout()); 933 if (!m_doneResetting) 934 return false; 935 936 if (resetStage == ResetStage::AfterTest && m_checkForWorldLeaks) 937 updateLiveDocumentsAfterTest(); 938 914 939 return m_doneResetting; 940 } 941 942 void TestController::updateLiveDocumentsAfterTest() 943 { 944 AsyncTask([]() { 945 // After each test, we update the list of live documents so that we can detect when an abandoned document first showed up. 946 WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("GetLiveDocuments")); 947 WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr); 948 }, 5_s).run(); 949 } 950 951 void TestController::checkForWorldLeaks() 952 { 953 AsyncTask([]() { 954 // This runs at the end of a series of tests. It clears caches, runs a GC and then fetches the list of documents. 955 WKRetainPtr<WKStringRef> messageName = adoptWK(WKStringCreateWithUTF8CString("CheckForWorldLeaks")); 956 WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), nullptr); 957 }, 20_s).run(); 958 } 959 960 void TestController::findAndDumpWorldLeaks() 961 { 962 checkForWorldLeaks(); 963 964 StringBuilder builder; 965 966 if (m_abandonedDocumentInfo.size()) { 967 for (const auto& it : m_abandonedDocumentInfo) { 968 auto documentURL = it.value.abandonedDocumentURL; 969 if (documentURL.isEmpty()) 970 documentURL = "(no url)"; 971 builder.append("TEST: "); 972 builder.append(it.value.testURL); 973 builder.append('\n'); 974 builder.append("ABANDONED DOCUMENT: "); 975 builder.append(documentURL); 976 builder.append('\n'); 977 } 978 } else 979 builder.append("no abandoned documents"); 980 981 String result = builder.toString(); 982 printf("Content-Type: text/plain\n"); 983 printf("Content-Length: %u\n", result.length()); 984 fwrite(result.utf8().data(), 1, result.length(), stdout); 985 printf("#EOF\n"); 986 fprintf(stderr, "#EOF\n"); 987 fflush(stdout); 988 fflush(stderr); 989 } 990 991 void TestController::willDestroyWebView() 992 { 993 // Before we kill the web view, look for abandoned documents before that web process goes away. 994 checkForWorldLeaks(); 915 995 } 916 996 … … 1239 1319 } 1240 1320 1241 TestCommand parseInputLine(const std::string& inputLine)1321 static TestCommand parseInputLine(const std::string& inputLine) 1242 1322 { 1243 1323 TestCommand result; … … 1297 1377 } 1298 1378 1379 bool TestController::waitForCompletion(const WTF::Function<void ()>& function, WTF::Seconds timeout) 1380 { 1381 m_doneResetting = false; 1382 function(); 1383 runUntil(m_doneResetting, timeout); 1384 return !m_doneResetting; 1385 } 1386 1387 bool TestController::handleControlCommand(const char* command) 1388 { 1389 if (!strcmp("#CHECK FOR WORLD LEAKS", command)) { 1390 findAndDumpWorldLeaks(); 1391 return true; 1392 } 1393 return false; 1394 } 1395 1299 1396 void TestController::runTestingServerLoop() 1300 1397 { … … 1306 1403 1307 1404 if (strlen(filenameBuffer) == 0) 1405 continue; 1406 1407 if (handleControlCommand(filenameBuffer)) 1308 1408 continue; 1309 1409 … … 1322 1422 break; 1323 1423 } 1424 findAndDumpWorldLeaks(); 1324 1425 } 1325 1426 } … … 1386 1487 } 1387 1488 1489 void TestController::didReceiveLiveDocumentsList(WKArrayRef liveDocumentList) 1490 { 1491 auto numDocuments = WKArrayGetSize(liveDocumentList); 1492 1493 HashMap<uint64_t, String> documentInfo; 1494 for (size_t i = 0; i < numDocuments; ++i) { 1495 WKTypeRef item = WKArrayGetItemAtIndex(liveDocumentList, i); 1496 if (item && WKGetTypeID(item) == WKDictionaryGetTypeID()) { 1497 WKDictionaryRef liveDocumentItem = static_cast<WKDictionaryRef>(item); 1498 1499 WKRetainPtr<WKStringRef> idKey(AdoptWK, WKStringCreateWithUTF8CString("id")); 1500 WKUInt64Ref documentID = static_cast<WKUInt64Ref>(WKDictionaryGetItemForKey(liveDocumentItem, idKey.get())); 1501 1502 WKRetainPtr<WKStringRef> urlKey(AdoptWK, WKStringCreateWithUTF8CString("url")); 1503 WKStringRef documentURL = static_cast<WKStringRef>(WKDictionaryGetItemForKey(liveDocumentItem, urlKey.get())); 1504 1505 documentInfo.add(WKUInt64GetValue(documentID), toWTFString(documentURL)); 1506 } 1507 } 1508 1509 if (!documentInfo.size()) { 1510 m_abandonedDocumentInfo.clear(); 1511 return; 1512 } 1513 1514 // Remove any documents which are no longer live. 1515 m_abandonedDocumentInfo.removeIf([&](auto& keyAndValue) { 1516 return !documentInfo.contains(keyAndValue.key); 1517 }); 1518 1519 // Add newly abandoned documents. 1520 String currentTestURL = m_currentInvocation ? toWTFString(adoptWK(WKURLCopyString(m_currentInvocation->url()))) : "no test"; 1521 for (const auto& it : documentInfo) 1522 m_abandonedDocumentInfo.add(it.key, AbandonedDocumentInfo(currentTestURL, it.value)); 1523 } 1524 1388 1525 void TestController::didReceiveMessageFromInjectedBundle(WKStringRef messageName, WKTypeRef messageBody) 1389 1526 { 1527 if (WKStringIsEqualToUTF8CString(messageName, "LiveDocuments")) { 1528 ASSERT(WKGetTypeID(messageBody) == WKArrayGetTypeID()); 1529 didReceiveLiveDocumentsList(static_cast<WKArrayRef>(messageBody)); 1530 AsyncTask::currentTask()->taskComplete(); 1531 return; 1532 } 1533 1390 1534 if (WKStringIsEqualToUTF8CString(messageName, "EventSender")) { 1391 1535 if (m_state != RunningTest) -
trunk/Tools/WebKitTestRunner/TestController.h
r235399 r235408 49 49 struct TestOptions; 50 50 51 class AsyncTask { 52 public: 53 AsyncTask(WTF::Function<void ()>&& task, WTF::Seconds timeout) 54 : m_task(WTFMove(task)) 55 , m_timeout(timeout) 56 { 57 ASSERT(!currentTask()); 58 } 59 60 // Returns false on timeout. 61 bool run(); 62 63 void taskComplete() 64 { 65 m_taskDone = true; 66 } 67 68 static AsyncTask* currentTask(); 69 70 private: 71 static AsyncTask* m_currentTask; 72 73 WTF::Function<void ()> m_task; 74 WTF::Seconds m_timeout; 75 bool m_taskDone { false }; 76 }; 77 51 78 // FIXME: Rename this TestRunner? 52 79 class TestController { … … 122 149 unsigned imageCountInGeneralPasteboard() const; 123 150 124 bool resetStateToConsistentValues(const TestOptions&); 151 enum class ResetStage { BeforeTest, AfterTest }; 152 bool resetStateToConsistentValues(const TestOptions&, ResetStage); 125 153 void resetPreferencesToConsistentValues(const TestOptions&); 154 155 void willDestroyWebView(); 126 156 127 157 void terminateWebContentProcess(); … … 232 262 void runTestingServerLoop(); 233 263 bool runTest(const char* pathOrURL); 264 265 // Returns false if timed out. 266 bool waitForCompletion(const WTF::Function<void ()>&, WTF::Seconds timeout); 267 268 bool handleControlCommand(const char* command); 234 269 235 270 void platformInitialize(); … … 262 297 void updateWindowScaleForTest(PlatformWebView*, const TestInvocation&); 263 298 299 void updateLiveDocumentsAfterTest(); 300 void checkForWorldLeaks(); 301 302 void didReceiveLiveDocumentsList(WKArrayRef); 303 void findAndDumpWorldLeaks(); 304 264 305 void decidePolicyForGeolocationPermissionRequestIfPossible(); 265 306 void decidePolicyForUserMediaPermissionRequestIfPossible(); … … 432 473 433 474 bool m_shouldShowTouches { false }; 475 bool m_checkForWorldLeaks { false }; 434 476 435 477 bool m_allowAnyHTTPSCertificateForAllowedHosts { false }; … … 445 487 446 488 WorkQueueManager m_workQueueManager; 489 490 struct AbandonedDocumentInfo { 491 String testURL; 492 String abandonedDocumentURL; 493 494 AbandonedDocumentInfo() = default; 495 AbandonedDocumentInfo(String inTestURL, String inAbandonedDocumentURL) 496 : testURL(inTestURL) 497 , abandonedDocumentURL(inAbandonedDocumentURL) 498 { } 499 }; 500 HashMap<uint64_t, AbandonedDocumentInfo> m_abandonedDocumentInfo; 447 501 }; 448 502 -
trunk/Tools/WebKitTestRunner/TestInvocation.cpp
r235399 r235408 188 188 #endif // !PLATFORM(IOS) 189 189 190 if (TestController::singleton().resetStateToConsistentValues(m_options ))190 if (TestController::singleton().resetStateToConsistentValues(m_options, TestController::ResetStage::AfterTest)) 191 191 return; 192 192 -
trunk/Tools/WebKitTestRunner/TestOptions.h
r235399 r235408 61 61 bool punchOutWhiteBackgroundsInDarkMode { false }; 62 62 bool runSingly { false }; 63 bool checkForWorldLeaks { false }; 63 64 64 65 float deviceScaleFactor { 1 }; … … 98 99 || punchOutWhiteBackgroundsInDarkMode != options.punchOutWhiteBackgroundsInDarkMode 99 100 || jscOptions != options.jscOptions 100 || runSingly != options.runSingly) 101 || runSingly != options.runSingly 102 || checkForWorldLeaks != options.checkForWorldLeaks) 101 103 return false; 102 104
Note: See TracChangeset
for help on using the changeset viewer.