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>