Title: [210031] trunk
Revision
210031
Author
aes...@apple.com
Date
2016-12-20 13:18:30 -0800 (Tue, 20 Dec 2016)

Log Message

[Cocoa] REGRESSION (r209558): Calling decisionHandler multiple times in webView:decidePolicyForNavigationAction:decisionHandler: leads to a crash
https://bugs.webkit.org/show_bug.cgi?id=165992
Source/WebKit2:

<rdar://problem/29693817>

Reviewed by Brady Eidson.

r209558 added additional move semantics to the decisionHandler block passed to
-webView:decidePolicyForNavigationAction:decisionHandler:, resulting in a null pointer
dereference in clients that call the decisionHandler block more than once.

None of the completion handlers we expose in the WebKit API are intended to be called more
than once, and we ASSERT in CompletionHandlerCallChecker::didCallCompletionHandler() that
this doesn't happen.

This change strenghtens that protection by returning immediately in completion handlers
called more than once. And in programs linked on or after WebKit 603.1.17, an NSException is
thrown if a completion handler is called a second time, like we do for completion handlers
that are never called.

New API test: WebKit2.DuplicateCompletionHandlerCalls

* Shared/Cocoa/CompletionHandlerCallChecker.h: Declared completionHandlerHasBeenCalled().
* Shared/Cocoa/CompletionHandlerCallChecker.mm:
(WebKit::shouldThrowExceptionForDuplicateCompletionHandlerCall): Added. Returns true if the
program is linked on or after
LibraryVersion::FirstWithExceptionsForDuplicateCompletionHandlerCalls.
(WebKit::CompletionHandlerCallChecker::completionHandlerHasBeenCalled): Added. Returns
whether or not the completion handler has been called. If it has, and
shouldThrowExceptionForDuplicateCompletionHandlerCall() returns true, throws an
NSInternalInconsistencyException.
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _setInputDelegate:]): Added an early return if the completion handler has
already been called.
* UIProcess/Cocoa/NavigationState.mm:
(WebKit::NavigationState::NavigationClient::decidePolicyForNavigationAction): Ditto.
(WebKit::NavigationState::NavigationClient::decidePolicyForNavigationResponse): Ditto.
(WebKit::NavigationState::NavigationClient::didReceiveAuthenticationChallenge): Ditto.
* UIProcess/Cocoa/UIDelegate.mm:
(WebKit::UIDelegate::UIClient::runJavaScriptAlert): Ditto.
(WebKit::UIDelegate::UIClient::runJavaScriptConfirm): Ditto.
(WebKit::UIDelegate::UIClient::runJavaScriptPrompt): Ditto.
(WebKit::UIDelegate::UIClient::exceededDatabaseQuota): Ditto.
(WebKit::UIDelegate::UIClient::runOpenPanel): Ditto.
(WebKit::UIDelegate::UIClient::reachedApplicationCacheOriginQuota): Ditto.
* UIProcess/Cocoa/VersionChecks.h: Added
FirstWithExceptionsForDuplicateCompletionHandlerCalls with the version number for 603.1.17.

Tools:

Reviewed by Brady Eidson.

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKit2Cocoa/DuplicateCompletionHandlerCalls.mm: Added.
(expectException):
(-[DuplicateCompletionHandlerCallsDelegate webView:decidePolicyForNavigationAction:decisionHandler:]):
(-[DuplicateCompletionHandlerCallsDelegate webView:decidePolicyForNavigationResponse:decisionHandler:]):
(-[DuplicateCompletionHandlerCallsDelegate webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:]):
(-[DuplicateCompletionHandlerCallsDelegate webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:]):
(-[DuplicateCompletionHandlerCallsDelegate webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:]):
(-[DuplicateCompletionHandlerCallsDelegate _webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:]):
(-[DuplicateCompletionHandlerCallsDelegate _webView:willSubmitFormValues:userObject:submissionHandler:]):
(TEST):
* TestWebKitAPI/Tests/WebKit2Cocoa/duplicate-completion-handler-calls.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/WebKit2/ChangeLog (210030 => 210031)


--- trunk/Source/WebKit2/ChangeLog	2016-12-20 21:14:00 UTC (rev 210030)
+++ trunk/Source/WebKit2/ChangeLog	2016-12-20 21:18:30 UTC (rev 210031)
@@ -1,3 +1,52 @@
+2016-12-20  Andy Estes  <aes...@apple.com>
+
+        [Cocoa] REGRESSION (r209558): Calling decisionHandler multiple times in webView:decidePolicyForNavigationAction:decisionHandler: leads to a crash
+        https://bugs.webkit.org/show_bug.cgi?id=165992
+        <rdar://problem/29693817>
+
+        Reviewed by Brady Eidson.
+
+        r209558 added additional move semantics to the decisionHandler block passed to
+        -webView:decidePolicyForNavigationAction:decisionHandler:, resulting in a null pointer
+        dereference in clients that call the decisionHandler block more than once.
+
+        None of the completion handlers we expose in the WebKit API are intended to be called more
+        than once, and we ASSERT in CompletionHandlerCallChecker::didCallCompletionHandler() that
+        this doesn't happen.
+
+        This change strenghtens that protection by returning immediately in completion handlers
+        called more than once. And in programs linked on or after WebKit 603.1.17, an NSException is
+        thrown if a completion handler is called a second time, like we do for completion handlers
+        that are never called.
+
+        New API test: WebKit2.DuplicateCompletionHandlerCalls
+
+        * Shared/Cocoa/CompletionHandlerCallChecker.h: Declared completionHandlerHasBeenCalled().
+        * Shared/Cocoa/CompletionHandlerCallChecker.mm:
+        (WebKit::shouldThrowExceptionForDuplicateCompletionHandlerCall): Added. Returns true if the
+        program is linked on or after
+        LibraryVersion::FirstWithExceptionsForDuplicateCompletionHandlerCalls.
+        (WebKit::CompletionHandlerCallChecker::completionHandlerHasBeenCalled): Added. Returns
+        whether or not the completion handler has been called. If it has, and
+        shouldThrowExceptionForDuplicateCompletionHandlerCall() returns true, throws an
+        NSInternalInconsistencyException.
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _setInputDelegate:]): Added an early return if the completion handler has
+        already been called.
+        * UIProcess/Cocoa/NavigationState.mm:
+        (WebKit::NavigationState::NavigationClient::decidePolicyForNavigationAction): Ditto.
+        (WebKit::NavigationState::NavigationClient::decidePolicyForNavigationResponse): Ditto.
+        (WebKit::NavigationState::NavigationClient::didReceiveAuthenticationChallenge): Ditto.
+        * UIProcess/Cocoa/UIDelegate.mm:
+        (WebKit::UIDelegate::UIClient::runJavaScriptAlert): Ditto.
+        (WebKit::UIDelegate::UIClient::runJavaScriptConfirm): Ditto.
+        (WebKit::UIDelegate::UIClient::runJavaScriptPrompt): Ditto.
+        (WebKit::UIDelegate::UIClient::exceededDatabaseQuota): Ditto.
+        (WebKit::UIDelegate::UIClient::runOpenPanel): Ditto.
+        (WebKit::UIDelegate::UIClient::reachedApplicationCacheOriginQuota): Ditto.
+        * UIProcess/Cocoa/VersionChecks.h: Added
+        FirstWithExceptionsForDuplicateCompletionHandlerCalls with the version number for 603.1.17.
+
 2016-12-20  Dean Jackson  <d...@apple.com>
 
         Remove INDIE_UI

Modified: trunk/Source/WebKit2/Shared/Cocoa/CompletionHandlerCallChecker.h (210030 => 210031)


--- trunk/Source/WebKit2/Shared/Cocoa/CompletionHandlerCallChecker.h	2016-12-20 21:14:00 UTC (rev 210030)
+++ trunk/Source/WebKit2/Shared/Cocoa/CompletionHandlerCallChecker.h	2016-12-20 21:18:30 UTC (rev 210031)
@@ -39,6 +39,7 @@
     ~CompletionHandlerCallChecker();
 
     void didCallCompletionHandler();
+    bool completionHandlerHasBeenCalled() const;
 
 private:
     CompletionHandlerCallChecker(Class delegateClass, SEL delegateMethodSelector);

Modified: trunk/Source/WebKit2/Shared/Cocoa/CompletionHandlerCallChecker.mm (210030 => 210031)


--- trunk/Source/WebKit2/Shared/Cocoa/CompletionHandlerCallChecker.mm	2016-12-20 21:14:00 UTC (rev 210030)
+++ trunk/Source/WebKit2/Shared/Cocoa/CompletionHandlerCallChecker.mm	2016-12-20 21:18:30 UTC (rev 210031)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -28,8 +28,10 @@
 
 #if WK_API_ENABLED
 
+#import <mutex>
 #import <objc/runtime.h>
 #import <wtf/Ref.h>
+#import "VersionChecks.h"
 
 namespace WebKit {
 
@@ -60,6 +62,29 @@
     m_didCallCompletionHandler = true;
 }
 
+static bool shouldThrowExceptionForDuplicateCompletionHandlerCall()
+{
+    static bool shouldThrowException;
+    static std::once_flag once;
+    std::call_once(once, [] {
+        shouldThrowException = linkedOnOrAfter(LibraryVersion::FirstWithExceptionsForDuplicateCompletionHandlerCalls);
+    });
+    return shouldThrowException;
+}
+
+bool CompletionHandlerCallChecker::completionHandlerHasBeenCalled() const
+{
+    if (!m_didCallCompletionHandler)
+        return false;
+
+    if (shouldThrowExceptionForDuplicateCompletionHandlerCall()) {
+        Class delegateClass = classImplementingDelegateMethod();
+        [NSException raise:NSInternalInconsistencyException format:@"Completion handler passed to %c[%@ %@] was called more than once", class_isMetaClass(delegateClass) ? '+' : '-', NSStringFromClass(delegateClass), NSStringFromSelector(m_delegateMethodSelector)];
+    }
+
+    return true;
+}
+
 Class CompletionHandlerCallChecker::classImplementingDelegateMethod() const
 {
     Class delegateClass = m_delegateClass;

Modified: trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm (210030 => 210031)


--- trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm	2016-12-20 21:14:00 UTC (rev 210030)
+++ trunk/Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm	2016-12-20 21:18:30 UTC (rev 210031)
@@ -3886,6 +3886,8 @@
             RefPtr<WebKit::WebFormSubmissionListenerProxy> localListener = WTFMove(listener);
             RefPtr<WebKit::CompletionHandlerCallChecker> checker = WebKit::CompletionHandlerCallChecker::create(inputDelegate.get(), @selector(_webView:willSubmitFormValues:userObject:submissionHandler:));
             [inputDelegate _webView:m_webView willSubmitFormValues:valueMap.get() userObject:userObject submissionHandler:[localListener, checker] {
+                if (checker->completionHandlerHasBeenCalled())
+                    return;
                 checker->didCallCompletionHandler();
                 localListener->continueSubmission();
             }];

Modified: trunk/Source/WebKit2/UIProcess/Cocoa/NavigationState.mm (210030 => 210031)


--- trunk/Source/WebKit2/UIProcess/Cocoa/NavigationState.mm	2016-12-20 21:14:00 UTC (rev 210030)
+++ trunk/Source/WebKit2/UIProcess/Cocoa/NavigationState.mm	2016-12-20 21:18:30 UTC (rev 210031)
@@ -337,6 +337,8 @@
     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(navigationDelegate.get(), delegateHasWebsitePolicies ? @selector(_webView:decidePolicyForNavigationAction:decisionHandler:) : @selector(webView:decidePolicyForNavigationAction:decisionHandler:));
     
     auto decisionHandlerWithPolicies = [localListener = RefPtr<WebFramePolicyListenerProxy>(WTFMove(listener)), localNavigationAction = RefPtr<API::NavigationAction>(&navigationAction), checker = WTFMove(checker), mainFrameURLString](WKNavigationActionPolicy actionPolicy, _WKWebsitePolicies *websitePolicies) mutable {
+        if (checker->completionHandlerHasBeenCalled())
+            return;
         checker->didCallCompletionHandler();
 
         WebsitePolicies policies;
@@ -412,6 +414,8 @@
     RefPtr<WebFramePolicyListenerProxy> localListener = WTFMove(listener);
     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(navigationDelegate.get(), @selector(webView:decidePolicyForNavigationResponse:decisionHandler:));
     [navigationDelegate webView:m_navigationState.m_webView decidePolicyForNavigationResponse:wrapper(navigationResponse) decisionHandler:[localListener, checker](WKNavigationResponsePolicy responsePolicy) {
+        if (checker->completionHandlerHasBeenCalled())
+            return;
         checker->didCallCompletionHandler();
 
         switch (responsePolicy) {
@@ -647,6 +651,8 @@
         RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(navigationDelegate.get(), @selector(webView:didReceiveAuthenticationChallenge:completionHandler:));
         [static_cast<id <WKNavigationDelegatePrivate>>(navigationDelegate.get()) webView:m_navigationState.m_webView didReceiveAuthenticationChallenge:wrapper(*authenticationChallenge)
             completionHandler:[challenge, checker](NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential) {
+                if (checker->completionHandlerHasBeenCalled())
+                    return;
                 checker->didCallCompletionHandler();
 
                 switch (disposition) {

Modified: trunk/Source/WebKit2/UIProcess/Cocoa/UIDelegate.mm (210030 => 210031)


--- trunk/Source/WebKit2/UIProcess/Cocoa/UIDelegate.mm	2016-12-20 21:14:00 UTC (rev 210030)
+++ trunk/Source/WebKit2/UIProcess/Cocoa/UIDelegate.mm	2016-12-20 21:18:30 UTC (rev 210031)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -216,6 +216,8 @@
 
     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:));
     [delegate webView:m_uiDelegate.m_webView runJavaScriptAlertPanelWithMessage:message initiatedByFrame:wrapper(API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin())) completionHandler:BlockPtr<void ()>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] {
+        if (checker->completionHandlerHasBeenCalled())
+            return;
         completionHandler();
         checker->didCallCompletionHandler();
     }).get()];
@@ -236,6 +238,8 @@
 
     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:));
     [delegate webView:m_uiDelegate.m_webView runJavaScriptConfirmPanelWithMessage:message initiatedByFrame:wrapper(API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin())) completionHandler:BlockPtr<void (BOOL)>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](BOOL result) {
+        if (checker->completionHandlerHasBeenCalled())
+            return;
         completionHandler(result);
         checker->didCallCompletionHandler();
     }).get()];
@@ -256,6 +260,8 @@
 
     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:));
     [delegate webView:m_uiDelegate.m_webView runJavaScriptTextInputPanelWithPrompt:message defaultText:defaultValue initiatedByFrame:wrapper(API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin())) completionHandler:BlockPtr<void (NSString *)>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](NSString *result) {
+        if (checker->completionHandlerHasBeenCalled())
+            return;
         completionHandler(result);
         checker->didCallCompletionHandler();
     }).get()];
@@ -281,6 +287,8 @@
     ASSERT(securityOrigin);
     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:));
     [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView decideDatabaseQuotaForSecurityOrigin:wrapper(*securityOrigin) currentQuota:currentQuota currentOriginUsage:currentOriginUsage currentDatabaseUsage:currentUsage expectedUsage:expectedUsage decisionHandler:BlockPtr<void (unsigned long long newQuota)>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](unsigned long long newQuota) {
+        if (checker->completionHandlerHasBeenCalled())
+            return;
         checker->didCallCompletionHandler();
         completionHandler(newQuota);
     }).get()];
@@ -302,6 +310,8 @@
     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:));
 
     [delegate webView:m_uiDelegate.m_webView runOpenPanelWithParameters:wrapper(*openPanelParameters) initiatedByFrame:wrapper(frame) completionHandler:[checker, resultListener](NSArray *URLs) {
+        if (checker->completionHandlerHasBeenCalled())
+            return;
         checker->didCallCompletionHandler();
 
         if (!URLs) {
@@ -460,6 +470,8 @@
     RefPtr<API::SecurityOrigin> apiOrigin = API::SecurityOrigin::create(securityOrigin);
     
     [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView decideWebApplicationCacheQuotaForSecurityOrigin:wrapper(*apiOrigin) currentQuota:currentQuota totalBytesNeeded:totalBytesNeeded decisionHandler:BlockPtr<void (unsigned long long)>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](unsigned long long newQuota) {
+        if (checker->completionHandlerHasBeenCalled())
+            return;
         checker->didCallCompletionHandler();
         completionHandler(newQuota);
     }).get()];

Modified: trunk/Source/WebKit2/UIProcess/Cocoa/VersionChecks.h (210030 => 210031)


--- trunk/Source/WebKit2/UIProcess/Cocoa/VersionChecks.h	2016-12-20 21:14:00 UTC (rev 210030)
+++ trunk/Source/WebKit2/UIProcess/Cocoa/VersionChecks.h	2016-12-20 21:18:30 UTC (rev 210031)
@@ -23,8 +23,7 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef VersionChecks_h
-#define VersionChecks_h
+#pragma once
 
 namespace WebKit {
 
@@ -40,10 +39,9 @@
 enum class LibraryVersion {
     FirstWithNetworkCache = 0x02590116, // 601.1.22
     FirstWithMediaTypesRequiringUserActionForPlayback = 0x025A0121, // 602.1.33
+    FirstWithExceptionsForDuplicateCompletionHandlerCalls = 0x025B0111, // 603.1.17
 };
 
 bool linkedOnOrAfter(LibraryVersion);
 
 }
-
-#endif

Modified: trunk/Tools/ChangeLog (210030 => 210031)


--- trunk/Tools/ChangeLog	2016-12-20 21:14:00 UTC (rev 210030)
+++ trunk/Tools/ChangeLog	2016-12-20 21:18:30 UTC (rev 210031)
@@ -1,3 +1,23 @@
+2016-12-20  Andy Estes  <aes...@apple.com>
+
+        [Cocoa] REGRESSION (r209558): Calling decisionHandler multiple times in webView:decidePolicyForNavigationAction:decisionHandler: leads to a crash
+        https://bugs.webkit.org/show_bug.cgi?id=165992
+
+        Reviewed by Brady Eidson.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKit2Cocoa/DuplicateCompletionHandlerCalls.mm: Added.
+        (expectException):
+        (-[DuplicateCompletionHandlerCallsDelegate webView:decidePolicyForNavigationAction:decisionHandler:]):
+        (-[DuplicateCompletionHandlerCallsDelegate webView:decidePolicyForNavigationResponse:decisionHandler:]):
+        (-[DuplicateCompletionHandlerCallsDelegate webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:]):
+        (-[DuplicateCompletionHandlerCallsDelegate webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:]):
+        (-[DuplicateCompletionHandlerCallsDelegate webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:]):
+        (-[DuplicateCompletionHandlerCallsDelegate _webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:]):
+        (-[DuplicateCompletionHandlerCallsDelegate _webView:willSubmitFormValues:userObject:submissionHandler:]):
+        (TEST):
+        * TestWebKitAPI/Tests/WebKit2Cocoa/duplicate-completion-handler-calls.html: Added.
+
 2016-12-20  Dean Jackson  <d...@apple.com>
 
         Remove INDIE_UI

Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (210030 => 210031)


--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2016-12-20 21:14:00 UTC (rev 210030)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj	2016-12-20 21:18:30 UTC (rev 210031)
@@ -462,6 +462,8 @@
 		A14FC5881B8991BF00D107EB /* ContentFiltering.mm in Sources */ = {isa = PBXBuildFile; fileRef = A14FC5861B8991B600D107EB /* ContentFiltering.mm */; };
 		A14FC58B1B89927100D107EB /* ContentFilteringPlugIn.mm in Sources */ = {isa = PBXBuildFile; fileRef = A14FC5891B89927100D107EB /* ContentFilteringPlugIn.mm */; };
 		A14FC5901B8AE36F00D107EB /* TestProtocol.mm in Sources */ = {isa = PBXBuildFile; fileRef = A14FC58E1B8AE36500D107EB /* TestProtocol.mm */; };
+		A155022A1E05020B00A24C57 /* DuplicateCompletionHandlerCalls.mm in Sources */ = {isa = PBXBuildFile; fileRef = A15502281E05020B00A24C57 /* DuplicateCompletionHandlerCalls.mm */; };
+		A155022C1E050D0300A24C57 /* duplicate-completion-handler-calls.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = A155022B1E050BC500A24C57 /* duplicate-completion-handler-calls.html */; };
 		A16F66BA1C40EB4F00BD4D24 /* ContentFiltering.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = A16F66B91C40EA2000BD4D24 /* ContentFiltering.html */; };
 		A1C4FB731BACD1CA003742D0 /* pages.pages in Copy Resources */ = {isa = PBXBuildFile; fileRef = A1C4FB721BACD1B7003742D0 /* pages.pages */; };
 		A1DF74321C41B65800A2F4D0 /* AlwaysRevalidatedURLSchemes.mm in Sources */ = {isa = PBXBuildFile; fileRef = A1DF74301C41B65800A2F4D0 /* AlwaysRevalidatedURLSchemes.mm */; };
@@ -595,6 +597,7 @@
 			dstSubfolderSpec = 7;
 			files = (
 				9BD4239C1E04C01C00200395 /* chinese-character-with-image.html in Copy Resources */,
+				A155022C1E050D0300A24C57 /* duplicate-completion-handler-calls.html in Copy Resources */,
 				5110FCF91E01CD8A006F8D0B /* IndexUpgrade.blob in Copy Resources */,
 				5110FCF61E01CD83006F8D0B /* IndexUpgrade.sqlite3 in Copy Resources */,
 				5110FCF11E01CD64006F8D0B /* IDBIndexUpgradeToV2.html in Copy Resources */,
@@ -1136,6 +1139,8 @@
 		A14FC5891B89927100D107EB /* ContentFilteringPlugIn.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ContentFilteringPlugIn.mm; sourceTree = "<group>"; };
 		A14FC58D1B8AE36500D107EB /* TestProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestProtocol.h; path = cocoa/TestProtocol.h; sourceTree = "<group>"; };
 		A14FC58E1B8AE36500D107EB /* TestProtocol.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TestProtocol.mm; path = cocoa/TestProtocol.mm; sourceTree = "<group>"; };
+		A15502281E05020B00A24C57 /* DuplicateCompletionHandlerCalls.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DuplicateCompletionHandlerCalls.mm; sourceTree = "<group>"; };
+		A155022B1E050BC500A24C57 /* duplicate-completion-handler-calls.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "duplicate-completion-handler-calls.html"; sourceTree = "<group>"; };
 		A16F66B91C40EA2000BD4D24 /* ContentFiltering.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = ContentFiltering.html; sourceTree = "<group>"; };
 		A18AA8CC1C3FA218009B2B97 /* ContentFiltering.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentFiltering.h; sourceTree = "<group>"; };
 		A1A4FE5D18DD3DB700B5EA8A /* Download.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Download.mm; sourceTree = "<group>"; };
@@ -1461,6 +1466,7 @@
 				5C2936911D5BF63E00DEAB1E /* CookieAcceptPolicy.mm */,
 				2DC4CF761D2D9DD800ECCC94 /* DataDetection.mm */,
 				A1A4FE5D18DD3DB700B5EA8A /* Download.mm */,
+				A15502281E05020B00A24C57 /* DuplicateCompletionHandlerCalls.mm */,
 				2D8104CB1BEC13E70020DA46 /* FindInPage.mm */,
 				2D1FE0AF1AD465C1006CD9E6 /* FixedLayoutSize.mm */,
 				CD78E11A1DB7EA360014A2DE /* FullscreenDelegate.mm */,
@@ -1625,6 +1631,7 @@
 				5714ECB81CA8B58800051AC8 /* DownloadRequestOriginalURL.html */,
 				5714ECBC1CA8C21800051AC8 /* DownloadRequestOriginalURL2.html */,
 				5714ECBA1CA8BFD100051AC8 /* DownloadRequestOriginalURLFrame.html */,
+				A155022B1E050BC500A24C57 /* duplicate-completion-handler-calls.html */,
 				9984FACD1CFFB038008D198C /* editable-body.html */,
 				93575C551D30366E000D604D /* focus-inputs.html */,
 				F4F405BA1D4C0CF8007A9707 /* full-size-autoplaying-video-with-audio.html */,
@@ -2479,6 +2486,7 @@
 				7CCE7EEA1A411AE600447C4C /* DidNotHandleKeyDown.cpp in Sources */,
 				7CCE7EEB1A411AE600447C4C /* DocumentStartUserScriptAlertCrash.cpp in Sources */,
 				37A22AA71DCAA27200AFBFC4 /* ObservedRenderingProgressEventsAfterCrash.mm in Sources */,
+				A155022A1E05020B00A24C57 /* DuplicateCompletionHandlerCalls.mm in Sources */,
 				7CCE7EBB1A411A7E00447C4C /* DOMHTMLTableCellCellAbove.mm in Sources */,
 				5C9E56851DF9145400C9EE33 /* WebsitePolicies.mm in Sources */,
 				2D51A0C71C8BF00C00765C45 /* DOMHTMLVideoElementWrapper.mm in Sources */,

Added: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/DuplicateCompletionHandlerCalls.mm (0 => 210031)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/DuplicateCompletionHandlerCalls.mm	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/DuplicateCompletionHandlerCalls.mm	2016-12-20 21:18:30 UTC (rev 210031)
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 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"
+#import <WebKit/WebKit.h>
+
+#if WK_API_ENABLED
+
+#import "PlatformUtilities.h"
+#import "Utilities.h"
+#import <WebKit/WKUIDelegatePrivate.h>
+#import <WebKit/WKWebViewPrivate.h>
+#import <WebKit/_WKInputDelegate.h>
+#import <wtf/RetainPtr.h>
+
+using namespace TestWebKitAPI;
+
+static bool testFinished;
+
+@interface DuplicateCompletionHandlerCallsDelegate : NSObject <WKNavigationDelegate, WKUIDelegate, _WKInputDelegate>
+@end
+
+@implementation DuplicateCompletionHandlerCallsDelegate
+
+static void expectException(void (^completionHandler)())
+{
+    bool exceptionThrown = false;
+    @try {
+        completionHandler();
+    } @catch (NSException *exception) {
+        EXPECT_WK_STREQ(NSInternalInconsistencyException, exception.name);
+        exceptionThrown = true;
+    }
+    EXPECT_TRUE(exceptionThrown);
+}
+
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
+{
+    decisionHandler(WKNavigationActionPolicyAllow);
+
+    expectException(^ {
+        decisionHandler(WKNavigationActionPolicyAllow);
+    });
+}
+
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
+{
+    decisionHandler(WKNavigationResponsePolicyAllow);
+
+    expectException(^ {
+        decisionHandler(WKNavigationResponsePolicyAllow);
+    });
+}
+
+- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)())completionHandler
+{
+    completionHandler();
+
+    expectException(^ {
+        completionHandler();
+    });
+}
+
+- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler
+{
+    completionHandler(YES);
+
+    expectException(^ {
+        completionHandler(YES);
+    });
+}
+
+- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler
+{
+    completionHandler(nil);
+
+    expectException(^ {
+        completionHandler(nil);
+    });
+}
+
+- (void)_webView:(WKWebView *)webView decideDatabaseQuotaForSecurityOrigin:(WKSecurityOrigin *)securityOrigin currentQuota:(unsigned long long)currentQuota currentOriginUsage:(unsigned long long)currentOriginUsage currentDatabaseUsage:(unsigned long long)currentUsage expectedUsage:(unsigned long long)expectedUsage decisionHandler:(void (^)(unsigned long long newQuota))decisionHandler
+{
+    decisionHandler(expectedUsage);
+
+    expectException(^ {
+        decisionHandler(expectedUsage);
+    });
+}
+
+- (void)_webView:(WKWebView *)webView willSubmitFormValues:(NSDictionary *)values userObject:(NSObject<NSSecureCoding> *)userObject submissionHandler:(void (^)())submissionHandler
+{
+    submissionHandler();
+
+    expectException(^ {
+        submissionHandler();
+    });
+
+    testFinished = true;
+}
+
+@end
+
+TEST(WebKit2, DuplicateCompletionHandlerCalls)
+{
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectMake(0, 0, 800, 600)]);
+    auto delegate = adoptNS([[DuplicateCompletionHandlerCallsDelegate alloc] init]);
+    [webView setNavigationDelegate:delegate.get()];
+    [webView setUIDelegate:delegate.get()];
+    [webView _setInputDelegate:delegate.get()];
+    [webView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"duplicate-completion-handler-calls" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]]];
+
+    Util::run(&testFinished);
+}
+
+#endif // WK_API_ENABLED

Added: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/duplicate-completion-handler-calls.html (0 => 210031)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/duplicate-completion-handler-calls.html	                        (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/duplicate-completion-handler-calls.html	2016-12-20 21:18:30 UTC (rev 210031)
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<form action=""
+<script>
+    alert("alert");
+    confirm("confirm");
+    prompt("prompt");
+    openDatabase("testDatabase", "1", "test database", 1024);
+    document.forms[0].submit();
+</script>
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to