Move SOAuthorization from WebKitAdditions to WebKit
authorjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 17 Jun 2019 20:40:08 +0000 (20:40 +0000)
committerjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 17 Jun 2019 20:40:08 +0000 (20:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=198874
<rdar://problem/47573431>

Reviewed by Brent Fulgham.

Source/WebCore/PAL:

This patch moves AppSSOSoftLink from WebKitAdditions to WebKit, and introduces
AuthKitSPI.h.

* PAL.xcodeproj/project.pbxproj:
* pal/cocoa/AppSSOSoftLink.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
* pal/cocoa/AppSSOSoftLink.mm: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
* pal/spi/cf/CFNetworkSPI.h:
* pal/spi/cocoa/AuthKitSPI.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.

Source/WebKit:

This patch basically moves everything from existing WebKitAdditions to WebKit.
It also replaces the LoadOptimizer nonsense with the actual SOAuthorization API.

* Configurations/WebKit.xcconfig:
* NetworkProcess/cocoa/NetworkSessionCocoa.mm:
(WebKit::NetworkSessionCocoa::NetworkSessionCocoa):
* PluginProcess/mac/PluginProcessMac.mm:
(WebKit::PluginProcess::platformInitializePluginProcess):
* SourcesCocoa.txt:
* UIProcess/API/APINavigationAction.h:
* UIProcess/Cocoa/NavigationState.mm:
(WebKit::trySOAuthorization):
(WebKit::tryInterceptNavigation):
(WebKit::NavigationState::NavigationClient::decidePolicyForNavigationAction):
(WebKit::tryOptimizingLoad): Deleted.
* UIProcess/Cocoa/SOAuthorization/NavigationSOAuthorizationSession.h: Added.
(WebKit::NavigationSOAuthorizationSession::callback):
* UIProcess/Cocoa/SOAuthorization/NavigationSOAuthorizationSession.mm: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
(WebKit::NavigationSOAuthorizationSession::NavigationSOAuthorizationSession):
(WebKit::NavigationSOAuthorizationSession::~NavigationSOAuthorizationSession):
(WebKit::NavigationSOAuthorizationSession::shouldStartInternal):
(WebKit::NavigationSOAuthorizationSession::webViewDidMoveToWindow):
* UIProcess/Cocoa/SOAuthorization/PopUpSOAuthorizationSession.h: Added.
* UIProcess/Cocoa/SOAuthorization/PopUpSOAuthorizationSession.mm: Added.
(-[WKSOSecretDelegate initWithSession:]):
(-[WKSOSecretDelegate webViewDidClose:]):
(-[WKSOSecretDelegate webView:decidePolicyForNavigationAction:decisionHandler:]):
(-[WKSOSecretDelegate webView:didFinishNavigation:]):
(WebKit::PopUpSOAuthorizationSession::PopUpSOAuthorizationSession):
(WebKit::PopUpSOAuthorizationSession::~PopUpSOAuthorizationSession):
(WebKit::PopUpSOAuthorizationSession::shouldStartInternal):
(WebKit::PopUpSOAuthorizationSession::fallBackToWebPathInternal):
(WebKit::PopUpSOAuthorizationSession::abortInternal):
(WebKit::PopUpSOAuthorizationSession::completeInternal):
(WebKit::PopUpSOAuthorizationSession::close):
(WebKit::PopUpSOAuthorizationSession::initSecretWebView):
* UIProcess/Cocoa/SOAuthorization/RedirectSOAuthorizationSession.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
* UIProcess/Cocoa/SOAuthorization/RedirectSOAuthorizationSession.mm: Added.
(WebKit::RedirectSOAuthorizationSession::RedirectSOAuthorizationSession):
(WebKit::RedirectSOAuthorizationSession::fallBackToWebPathInternal):
(WebKit::RedirectSOAuthorizationSession::abortInternal):
(WebKit::RedirectSOAuthorizationSession::completeInternal):
(WebKit::RedirectSOAuthorizationSession::beforeStart):
* UIProcess/Cocoa/SOAuthorization/SOAuthorizationCoordinator.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
* UIProcess/Cocoa/SOAuthorization/SOAuthorizationCoordinator.mm: Added.
(WebKit::SOAuthorizationCoordinator::SOAuthorizationCoordinator):
(WebKit::SOAuthorizationCoordinator::canAuthorize const):
(WebKit::SOAuthorizationCoordinator::tryAuthorize):
* UIProcess/Cocoa/SOAuthorization/SOAuthorizationNSURLExtras.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
* UIProcess/Cocoa/SOAuthorization/SOAuthorizationNSURLExtras.mm: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
(+[NSURL _web_canPerformAuthorizationWithURL:]):
* UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.h: Added.
(WebKit::SOAuthorizationSession::page const):
(WebKit::SOAuthorizationSession::state const):
(WebKit::SOAuthorizationSession::setState):
(WebKit::SOAuthorizationSession::navigationAction):
* UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.mm: Added.
(WebKit::SOAuthorizationSession::SOAuthorizationSession):
(WebKit::SOAuthorizationSession::~SOAuthorizationSession):
(WebKit::SOAuthorizationSession::releaseNavigationAction):
(WebKit::SOAuthorizationSession::becomeCompleted):
(WebKit::SOAuthorizationSession::shouldStart):
(WebKit::SOAuthorizationSession::start):
(WebKit::SOAuthorizationSession::fallBackToWebPath):
(WebKit::SOAuthorizationSession::abort):
(WebKit::SOAuthorizationSession::complete):
(WebKit::SOAuthorizationSession::presentViewController):
(WebKit::SOAuthorizationSession::dismissViewController):
* UIProcess/Cocoa/SOAuthorization/SubFrameSOAuthorizationSession.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
* UIProcess/Cocoa/SOAuthorization/SubFrameSOAuthorizationSession.mm: Added.
(WebKit::SubFrameSOAuthorizationSession::SubFrameSOAuthorizationSession):
(WebKit::SubFrameSOAuthorizationSession::fallBackToWebPathInternal):
(WebKit::SubFrameSOAuthorizationSession::abortInternal):
(WebKit::SubFrameSOAuthorizationSession::completeInternal):
(WebKit::SubFrameSOAuthorizationSession::beforeStart):
(WebKit::SubFrameSOAuthorizationSession::loadDataToFrame):
(WebKit::SubFrameSOAuthorizationSession::postDidCancelMessageToParent):
* UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.h: Renamed from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
* UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.mm: Added.
(-[WKSOAuthorizationDelegate authorization:presentViewController:withCompletion:]):
(-[WKSOAuthorizationDelegate authorizationDidNotHandle:]):
(-[WKSOAuthorizationDelegate authorizationDidCancel:]):
(-[WKSOAuthorizationDelegate authorizationDidComplete:]):
(-[WKSOAuthorizationDelegate authorization:didCompleteWithHTTPAuthorizationHeaders:]):
(-[WKSOAuthorizationDelegate authorization:didCompleteWithHTTPResponse:httpBody:]):
(-[WKSOAuthorizationDelegate authorization:didCompleteWithError:]):
(-[WKSOAuthorizationDelegate setSession:]):
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::decidePolicyForNavigationAction):
(WebKit::trySOAuthorization):
(WebKit::WebPageProxy::createNewPage):
(WebKit::tryOptimizingLoad): Deleted.
* UIProcess/WebPageProxy.h:
(WebKit::WebPageProxy::setShouldSuppressSOAuthorizationInAllNavigationPolicyDecision):
(WebKit::WebPageProxy::setShouldSuppressSOAuthorizationInNextNavigationPolicyDecision):
* UIProcess/WebsiteData/WebsiteDataStore.cpp:
(WebKit::WebsiteDataStore::WebsiteDataStore):
* UIProcess/WebsiteData/WebsiteDataStore.h:
(WebKit::WebsiteDataStore::soAuthorizationCoordinator):
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/cocoa/WebProcessCocoa.mm:
(WebKit::WebProcess::platformInitializeProcess):

Source/WTF:

* wtf/Platform.h:
Adds a feature flag to detect AppSSO framework.

Tools:

This patch moves all SOAuthorization tests from WebKitAdditions to WebKit.

* TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig:
* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/TestSOAuthorization.mm: Added.
(-[TestSOAuthorizationNavigationDelegate init]):
(-[TestSOAuthorizationNavigationDelegate webView:didFinishNavigation:]):
(-[TestSOAuthorizationNavigationDelegate webView:decidePolicyForNavigationAction:decisionHandler:]):
(-[TestSOAuthorizationNavigationDelegate webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:]):
(-[TestSOAuthorizationViewController viewDidAppear]):
(-[TestSOAuthorizationViewController viewDidDisappear]):
(overrideCanPerformAuthorizationWithURL):
(overrideSetDelegate):
(overrideBeginAuthorizationWithURL):
(overrideCancelAuthorization):
(overrideAddObserverForName):
(overrideIsURLFromAppleOwnedDomain):
(resetState):
(configureSOAuthorizationWebView):
(generateHtml):
(checkAuthorizationOptions):
(TestWebKitAPI::TEST):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@246514 268f45cc-cd09-0410-ab3c-d52691b4dbfc

41 files changed:
Source/WTF/ChangeLog
Source/WTF/wtf/Platform.h
Source/WebCore/PAL/ChangeLog
Source/WebCore/PAL/PAL.xcodeproj/project.pbxproj
Source/WebCore/PAL/pal/cocoa/AppSSOSoftLink.h [new file with mode: 0644]
Source/WebCore/PAL/pal/cocoa/AppSSOSoftLink.mm [new file with mode: 0644]
Source/WebCore/PAL/pal/spi/cf/CFNetworkSPI.h
Source/WebCore/PAL/pal/spi/cocoa/AuthKitSPI.h [new file with mode: 0644]
Source/WebKit/ChangeLog
Source/WebKit/Configurations/WebKit.xcconfig
Source/WebKit/NetworkProcess/cocoa/NetworkSessionCocoa.mm
Source/WebKit/PluginProcess/mac/PluginProcessMac.mm
Source/WebKit/SourcesCocoa.txt
Source/WebKit/UIProcess/API/APINavigationAction.h
Source/WebKit/UIProcess/Cocoa/NavigationState.mm
Source/WebKit/UIProcess/Cocoa/SOAuthorization/NavigationSOAuthorizationSession.h [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/NavigationSOAuthorizationSession.mm [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/PopUpSOAuthorizationSession.h [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/PopUpSOAuthorizationSession.mm [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/RedirectSOAuthorizationSession.h [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/RedirectSOAuthorizationSession.mm [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationCoordinator.h [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationCoordinator.mm [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationNSURLExtras.h [moved from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm with 86% similarity]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationNSURLExtras.mm [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.h [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.mm [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/SubFrameSOAuthorizationSession.h [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/SubFrameSOAuthorizationSession.mm [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.h [new file with mode: 0644]
Source/WebKit/UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.mm [new file with mode: 0644]
Source/WebKit/UIProcess/WebPageProxy.cpp
Source/WebKit/UIProcess/WebPageProxy.h
Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.cpp
Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm
Tools/ChangeLog
Tools/TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/TestSOAuthorization.mm [new file with mode: 0644]

index 9c9f5ca..2604936 100644 (file)
@@ -1,3 +1,14 @@
+2019-06-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        Move SOAuthorization from WebKitAdditions to WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=198874
+        <rdar://problem/47573431>
+
+        Reviewed by Brent Fulgham.
+
+        * wtf/Platform.h:
+        Adds a feature flag to detect AppSSO framework.
+
 2019-06-17  Ryan Haddad  <ryanhaddad@apple.com>
 
         Unreviewed, rolling out r246501.
index d56b0b2..1eb7997 100644 (file)
 #define HAVE_CORETEXT_AUTO_OPTICAL_SIZING 1
 #endif
 
+#if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 130000 || PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
+#define HAVE_APP_SSO 1
+#endif
index ee05678..15028d9 100644 (file)
@@ -1,3 +1,20 @@
+2019-06-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        Move SOAuthorization from WebKitAdditions to WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=198874
+        <rdar://problem/47573431>
+
+        Reviewed by Brent Fulgham.
+
+        This patch moves AppSSOSoftLink from WebKitAdditions to WebKit, and introduces
+        AuthKitSPI.h.
+
+        * PAL.xcodeproj/project.pbxproj:
+        * pal/cocoa/AppSSOSoftLink.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
+        * pal/cocoa/AppSSOSoftLink.mm: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
+        * pal/spi/cf/CFNetworkSPI.h:
+        * pal/spi/cocoa/AuthKitSPI.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
+
 2019-06-14  Jiewen Tan  <jiewen_tan@apple.com>
 
         Disable AppSSO for web processes and plugin processes
index 3656378..ab85e48 100644 (file)
                44E1A8B021FA54EB00C3048E /* LookupSoftLink.mm in Sources */ = {isa = PBXBuildFile; fileRef = 44E1A8AE21FA54DA00C3048E /* LookupSoftLink.mm */; };
                570AB8F120AE2E8D00B8BE87 /* SecKeyProxySPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 570AB8F020AE2E8D00B8BE87 /* SecKeyProxySPI.h */; };
                570AB8F920AF6E3D00B8BE87 /* NSXPCConnectionSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 570AB8F820AF6E3D00B8BE87 /* NSXPCConnectionSPI.h */; };
+               572A107822B456F500F410C8 /* AuthKitSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 572A107722B456F500F410C8 /* AuthKitSPI.h */; };
+               57FD318A22B3593E008D0E8B /* AppSSOSoftLink.mm in Sources */ = {isa = PBXBuildFile; fileRef = 57FD318922B3593E008D0E8B /* AppSSOSoftLink.mm */; };
+               57FD318B22B35989008D0E8B /* AppSSOSoftLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 57FD318822B3592F008D0E8B /* AppSSOSoftLink.h */; };
                63C7EDC721AFAE04006A7B99 /* NSProgressSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 63E369F921AFA83F001C14BC /* NSProgressSPI.h */; };
                7A1656441F97B2B900BA3CE4 /* NSKeyedArchiverSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A1656431F97B2B800BA3CE4 /* NSKeyedArchiverSPI.h */; };
                7A36D0F9223AD9AB00B0522E /* CommonCryptoSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A36D0F8223AD9AB00B0522E /* CommonCryptoSPI.h */; };
                44E1A8AE21FA54DA00C3048E /* LookupSoftLink.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LookupSoftLink.mm; sourceTree = "<group>"; };
                570AB8F020AE2E8D00B8BE87 /* SecKeyProxySPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecKeyProxySPI.h; sourceTree = "<group>"; };
                570AB8F820AF6E3D00B8BE87 /* NSXPCConnectionSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSXPCConnectionSPI.h; sourceTree = "<group>"; };
+               572A107722B456F500F410C8 /* AuthKitSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AuthKitSPI.h; sourceTree = "<group>"; };
+               57FD318822B3592F008D0E8B /* AppSSOSoftLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppSSOSoftLink.h; sourceTree = "<group>"; };
+               57FD318922B3593E008D0E8B /* AppSSOSoftLink.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AppSSOSoftLink.mm; sourceTree = "<group>"; };
                63E369F921AFA83F001C14BC /* NSProgressSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSProgressSPI.h; sourceTree = "<group>"; };
                7A1656431F97B2B800BA3CE4 /* NSKeyedArchiverSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSKeyedArchiverSPI.h; sourceTree = "<group>"; };
                7A36D0F8223AD9AB00B0522E /* CommonCryptoSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonCryptoSPI.h; sourceTree = "<group>"; };
                        isa = PBXGroup;
                        children = (
                                2D02E93B2056FAA700A13797 /* AudioToolboxSPI.h */,
+                               572A107722B456F500F410C8 /* AuthKitSPI.h */,
                                0C2DA1221F3BEB4900DBC317 /* AVKitSPI.h */,
                                0C2DA1231F3BEB4900DBC317 /* CFNSURLConnectionSPI.h */,
                                7A36D0F8223AD9AB00B0522E /* CommonCryptoSPI.h */,
                1C4876DE1F8D831300CCEEBD /* cocoa */ = {
                        isa = PBXGroup;
                        children = (
+                               57FD318822B3592F008D0E8B /* AppSSOSoftLink.h */,
+                               57FD318922B3593E008D0E8B /* AppSSOSoftLink.mm */,
                                077E87B0226A460200A2AFF0 /* AVFoundationSoftLink.h */,
                                077E87AF226A460200A2AFF0 /* AVFoundationSoftLink.mm */,
                                F44291661FA52705002CC93E /* FileSizeFormatterCocoa.mm */,
                        isa = PBXHeadersBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               57FD318B22B35989008D0E8B /* AppSSOSoftLink.h in Headers */,
                                2D02E93C2056FAA700A13797 /* AudioToolboxSPI.h in Headers */,
+                               572A107822B456F500F410C8 /* AuthKitSPI.h in Headers */,
                                077E87B2226A460300A2AFF0 /* AVFoundationSoftLink.h in Headers */,
                                0C7785891F45130F00F4EBB6 /* AVFoundationSPI.h in Headers */,
                                0C2DA13E1F3BEB4900DBC317 /* AVKitSPI.h in Headers */,
                        developmentRegion = en;
                        hasScannedForEncodings = 1;
                        knownRegions = (
+                               en,
                                English,
                                Japanese,
                                French,
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               57FD318A22B3593E008D0E8B /* AppSSOSoftLink.mm in Sources */,
                                077E87B1226A460200A2AFF0 /* AVFoundationSoftLink.mm in Sources */,
                                0C5FFF0F1F78D9DA009EFF1A /* ClockCM.mm in Sources */,
                                0CF99CA81F738437007EE793 /* CoreMediaSoftLink.cpp in Sources */,
diff --git a/Source/WebCore/PAL/pal/cocoa/AppSSOSoftLink.h b/Source/WebCore/PAL/pal/cocoa/AppSSOSoftLink.h
new file mode 100644 (file)
index 0000000..614c537
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#if HAVE(APP_SSO)
+
+#import <AppSSO/AppSSO.h>
+#import <wtf/SoftLinking.h>
+
+SOFT_LINK_FRAMEWORK_FOR_HEADER(PAL, AppSSO);
+
+SOFT_LINK_CLASS_FOR_HEADER(PAL, SOAuthorization);
+
+SOFT_LINK_CONSTANT_FOR_HEADER(PAL, AppSSO, SOAuthorizationOptionUserActionInitiated, NSString*);
+#define SOAuthorizationOptionUserActionInitiated PAL::get_AppSSO_SOAuthorizationOptionUserActionInitiated()
+
+SOFT_LINK_CONSTANT_FOR_HEADER(PAL, AppSSO, SOErrorDomain, NSErrorDomain);
+#define SOErrorDomain PAL::get_AppSSO_SOErrorDomain()
+
+#endif
diff --git a/Source/WebCore/PAL/pal/cocoa/AppSSOSoftLink.mm b/Source/WebCore/PAL/pal/cocoa/AppSSOSoftLink.mm
new file mode 100644 (file)
index 0000000..ef1cd20
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "config.h"
+
+#if HAVE(APP_SSO)
+
+#import <wtf/SoftLinking.h>
+
+SOFT_LINK_FRAMEWORK_FOR_SOURCE_WITH_EXPORT(PAL, AppSSO, PAL_EXPORT);
+
+#if PLATFORM(MAC)
+SOFT_LINK_CLASS_FOR_SOURCE_OPTIONAL_WITH_EXPORT(PAL, AppSSO, SOAuthorization, PAL_EXPORT);
+#else
+SOFT_LINK_CLASS_FOR_SOURCE_WITH_EXPORT(PAL, AppSSO, SOAuthorization, PAL_EXPORT);
+#endif
+
+SOFT_LINK_CONSTANT_FOR_SOURCE_WITH_EXPORT(PAL, AppSSO, SOAuthorizationOptionUserActionInitiated, NSString*, PAL_EXPORT);
+
+SOFT_LINK_CONSTANT_FOR_SOURCE_WITH_EXPORT(PAL, AppSSO, SOErrorDomain, NSErrorDomain, PAL_EXPORT);
+
+#endif
index 182ff8b..4557085 100644 (file)
@@ -236,7 +236,7 @@ typedef NS_ENUM(NSInteger, NSURLSessionCompanionProxyPreference) {
 #if HAVE(CFNETWORK_NSURLSESSION_STRICTRUSTEVALUATE)
 + (void)_strictTrustEvaluate:(NSURLAuthenticationChallenge *)challenge queue:(dispatch_queue_t)queue completionHandler:(void (^)(NSURLAuthenticationChallenge *challenge, OSStatus trustResult))cb;
 #endif
-#if HAVE(LOAD_OPTIMIZER)
+#if HAVE(APP_SSO)
 + (void)_disableAppSSO;
 #endif
 @end
diff --git a/Source/WebCore/PAL/pal/spi/cocoa/AuthKitSPI.h b/Source/WebCore/PAL/pal/spi/cocoa/AuthKitSPI.h
new file mode 100644 (file)
index 0000000..6c82ec9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#if HAVE(APP_SSO)
+
+#if USE(APPLE_INTERNAL_SDK)
+
+#import <AuthKit/AKAuthorizationController.h>
+
+#else
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface AKAuthorizationController : NSObject
+
++ (BOOL)isURLFromAppleOwnedDomain:(NSURL *)url;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif // USE(APPLE_INTERNAL_SDK)
+
+#endif // HAVE(APP_SSO)
index 6eda6bc..3f86b4e 100644 (file)
@@ -1,3 +1,114 @@
+2019-06-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        Move SOAuthorization from WebKitAdditions to WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=198874
+        <rdar://problem/47573431>
+
+        Reviewed by Brent Fulgham.
+
+        This patch basically moves everything from existing WebKitAdditions to WebKit.
+        It also replaces the LoadOptimizer nonsense with the actual SOAuthorization API.
+
+        * Configurations/WebKit.xcconfig:
+        * NetworkProcess/cocoa/NetworkSessionCocoa.mm:
+        (WebKit::NetworkSessionCocoa::NetworkSessionCocoa):
+        * PluginProcess/mac/PluginProcessMac.mm:
+        (WebKit::PluginProcess::platformInitializePluginProcess):
+        * SourcesCocoa.txt:
+        * UIProcess/API/APINavigationAction.h:
+        * UIProcess/Cocoa/NavigationState.mm:
+        (WebKit::trySOAuthorization):
+        (WebKit::tryInterceptNavigation):
+        (WebKit::NavigationState::NavigationClient::decidePolicyForNavigationAction):
+        (WebKit::tryOptimizingLoad): Deleted.
+        * UIProcess/Cocoa/SOAuthorization/NavigationSOAuthorizationSession.h: Added.
+        (WebKit::NavigationSOAuthorizationSession::callback):
+        * UIProcess/Cocoa/SOAuthorization/NavigationSOAuthorizationSession.mm: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
+        (WebKit::NavigationSOAuthorizationSession::NavigationSOAuthorizationSession):
+        (WebKit::NavigationSOAuthorizationSession::~NavigationSOAuthorizationSession):
+        (WebKit::NavigationSOAuthorizationSession::shouldStartInternal):
+        (WebKit::NavigationSOAuthorizationSession::webViewDidMoveToWindow):
+        * UIProcess/Cocoa/SOAuthorization/PopUpSOAuthorizationSession.h: Added.
+        * UIProcess/Cocoa/SOAuthorization/PopUpSOAuthorizationSession.mm: Added.
+        (-[WKSOSecretDelegate initWithSession:]):
+        (-[WKSOSecretDelegate webViewDidClose:]):
+        (-[WKSOSecretDelegate webView:decidePolicyForNavigationAction:decisionHandler:]):
+        (-[WKSOSecretDelegate webView:didFinishNavigation:]):
+        (WebKit::PopUpSOAuthorizationSession::PopUpSOAuthorizationSession):
+        (WebKit::PopUpSOAuthorizationSession::~PopUpSOAuthorizationSession):
+        (WebKit::PopUpSOAuthorizationSession::shouldStartInternal):
+        (WebKit::PopUpSOAuthorizationSession::fallBackToWebPathInternal):
+        (WebKit::PopUpSOAuthorizationSession::abortInternal):
+        (WebKit::PopUpSOAuthorizationSession::completeInternal):
+        (WebKit::PopUpSOAuthorizationSession::close):
+        (WebKit::PopUpSOAuthorizationSession::initSecretWebView):
+        * UIProcess/Cocoa/SOAuthorization/RedirectSOAuthorizationSession.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
+        * UIProcess/Cocoa/SOAuthorization/RedirectSOAuthorizationSession.mm: Added.
+        (WebKit::RedirectSOAuthorizationSession::RedirectSOAuthorizationSession):
+        (WebKit::RedirectSOAuthorizationSession::fallBackToWebPathInternal):
+        (WebKit::RedirectSOAuthorizationSession::abortInternal):
+        (WebKit::RedirectSOAuthorizationSession::completeInternal):
+        (WebKit::RedirectSOAuthorizationSession::beforeStart):
+        * UIProcess/Cocoa/SOAuthorization/SOAuthorizationCoordinator.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
+        * UIProcess/Cocoa/SOAuthorization/SOAuthorizationCoordinator.mm: Added.
+        (WebKit::SOAuthorizationCoordinator::SOAuthorizationCoordinator):
+        (WebKit::SOAuthorizationCoordinator::canAuthorize const):
+        (WebKit::SOAuthorizationCoordinator::tryAuthorize):
+        * UIProcess/Cocoa/SOAuthorization/SOAuthorizationNSURLExtras.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
+        * UIProcess/Cocoa/SOAuthorization/SOAuthorizationNSURLExtras.mm: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
+        (+[NSURL _web_canPerformAuthorizationWithURL:]):
+        * UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.h: Added.
+        (WebKit::SOAuthorizationSession::page const):
+        (WebKit::SOAuthorizationSession::state const):
+        (WebKit::SOAuthorizationSession::setState):
+        (WebKit::SOAuthorizationSession::navigationAction):
+        * UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.mm: Added.
+        (WebKit::SOAuthorizationSession::SOAuthorizationSession):
+        (WebKit::SOAuthorizationSession::~SOAuthorizationSession):
+        (WebKit::SOAuthorizationSession::releaseNavigationAction):
+        (WebKit::SOAuthorizationSession::becomeCompleted):
+        (WebKit::SOAuthorizationSession::shouldStart):
+        (WebKit::SOAuthorizationSession::start):
+        (WebKit::SOAuthorizationSession::fallBackToWebPath):
+        (WebKit::SOAuthorizationSession::abort):
+        (WebKit::SOAuthorizationSession::complete):
+        (WebKit::SOAuthorizationSession::presentViewController):
+        (WebKit::SOAuthorizationSession::dismissViewController):
+        * UIProcess/Cocoa/SOAuthorization/SubFrameSOAuthorizationSession.h: Copied from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
+        * UIProcess/Cocoa/SOAuthorization/SubFrameSOAuthorizationSession.mm: Added.
+        (WebKit::SubFrameSOAuthorizationSession::SubFrameSOAuthorizationSession):
+        (WebKit::SubFrameSOAuthorizationSession::fallBackToWebPathInternal):
+        (WebKit::SubFrameSOAuthorizationSession::abortInternal):
+        (WebKit::SubFrameSOAuthorizationSession::completeInternal):
+        (WebKit::SubFrameSOAuthorizationSession::beforeStart):
+        (WebKit::SubFrameSOAuthorizationSession::loadDataToFrame):
+        (WebKit::SubFrameSOAuthorizationSession::postDidCancelMessageToParent):
+        * UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.h: Renamed from Tools/TestWebKitAPI/Tests/WebKitCocoa/TestLoadOptimizer.mm.
+        * UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.mm: Added.
+        (-[WKSOAuthorizationDelegate authorization:presentViewController:withCompletion:]):
+        (-[WKSOAuthorizationDelegate authorizationDidNotHandle:]):
+        (-[WKSOAuthorizationDelegate authorizationDidCancel:]):
+        (-[WKSOAuthorizationDelegate authorizationDidComplete:]):
+        (-[WKSOAuthorizationDelegate authorization:didCompleteWithHTTPAuthorizationHeaders:]):
+        (-[WKSOAuthorizationDelegate authorization:didCompleteWithHTTPResponse:httpBody:]):
+        (-[WKSOAuthorizationDelegate authorization:didCompleteWithError:]):
+        (-[WKSOAuthorizationDelegate setSession:]):
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::decidePolicyForNavigationAction):
+        (WebKit::trySOAuthorization):
+        (WebKit::WebPageProxy::createNewPage):
+        (WebKit::tryOptimizingLoad): Deleted.
+        * UIProcess/WebPageProxy.h:
+        (WebKit::WebPageProxy::setShouldSuppressSOAuthorizationInAllNavigationPolicyDecision):
+        (WebKit::WebPageProxy::setShouldSuppressSOAuthorizationInNextNavigationPolicyDecision):
+        * UIProcess/WebsiteData/WebsiteDataStore.cpp:
+        (WebKit::WebsiteDataStore::WebsiteDataStore):
+        * UIProcess/WebsiteData/WebsiteDataStore.h:
+        (WebKit::WebsiteDataStore::soAuthorizationCoordinator):
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/cocoa/WebProcessCocoa.mm:
+        (WebKit::WebProcess::platformInitializeProcess):
+
 2019-06-17  Tim Horton  <timothy_horton@apple.com>
 
         Fix the build.
index d9dda2c..430f7df 100644 (file)
@@ -127,7 +127,14 @@ WK_URL_FORMATTING_LDFLAGS_YES = -framework URLFormatting;
 WK_WEBINSPECTORUI_LDFLAGS = $(WK_WEBINSPECTORUI_LDFLAGS_$(WK_PLATFORM_NAME));
 WK_WEBINSPECTORUI_LDFLAGS_macosx = -weak_framework WebInspectorUI;
 
-FRAMEWORK_AND_LIBRARY_LDFLAGS = -lobjc -framework CFNetwork -framework CoreAudio -framework CoreFoundation -framework CoreGraphics -framework CoreText -framework Foundation -framework ImageIO -framework IOKit -framework WebKitLegacy -lnetwork $(WK_ACCESSIBILITY_LDFLAGS) $(WK_APPKIT_LDFLAGS) $(WK_ASSERTION_SERVICES_LDFLAGS) $(WK_CARBON_LDFLAGS) $(WK_CORE_PDF_LDFLAGS) $(WK_CORE_PREDICTION_LDFLAGS) $(WK_CORE_SERVICES_LDFLAGS) $(WK_DEVICE_IDENTITY_LDFLAGS) $(WK_GRAPHICS_SERVICES_LDFLAGS) $(WK_IOSURFACE_LDFLAGS) $(WK_LIBSANDBOX_LDFLAGS) $(WK_LIBWEBRTC_LDFLAGS) $(WK_MOBILE_CORE_SERVICES_LDFLAGS) $(WK_MOBILE_GESTALT_LDFLAGS) $(WK_OPENGL_LDFLAGS) $(WK_PDFKIT_LDFLAGS) $(WK_SAFE_BROWSING_LDFLAGS) $(WK_SECURITY_INTERFACE_LDFLAGS) $(WK_UIKIT_LDFLAGS) $(WK_URL_FORMATTING_LDFLAGS) $(WK_WEBINSPECTORUI_LDFLAGS);
+WK_AUTHKIT_LDFLAGS = $(WK_AUTHKIT_LDFLAGS_$(WK_PLATFORM_NAME));
+WK_AUTHKIT_LDFLAGS_iphoneos = $(WK_AUTHKIT_LDFLAGS$(WK_IOS_13));
+WK_AUTHKIT_LDFLAGS_iphonesimulator = $(WK_AUTHKIT_LDFLAGS$(WK_IOS_13));
+WK_AUTHKIT_LDFLAGS_IOS_SINCE_13 = -framework AuthKit;
+WK_AUTHKIT_LDFLAGS_macosx = $(WK_AUTHKIT_LDFLAGS$(WK_MACOS_1015));
+WK_AUTHKIT_LDFLAGS_MACOS_SINCE_1015 = -framework AuthKit;
+
+FRAMEWORK_AND_LIBRARY_LDFLAGS = -lobjc -framework CFNetwork -framework CoreAudio -framework CoreFoundation -framework CoreGraphics -framework CoreText -framework Foundation -framework ImageIO -framework IOKit -framework WebKitLegacy -lnetwork $(WK_ACCESSIBILITY_LDFLAGS) $(WK_APPKIT_LDFLAGS) $(WK_ASSERTION_SERVICES_LDFLAGS) $(WK_AUTHKIT_LDFLAGS) $(WK_CARBON_LDFLAGS) $(WK_CORE_PDF_LDFLAGS) $(WK_CORE_PREDICTION_LDFLAGS) $(WK_CORE_SERVICES_LDFLAGS) $(WK_DEVICE_IDENTITY_LDFLAGS) $(WK_GRAPHICS_SERVICES_LDFLAGS) $(WK_IOSURFACE_LDFLAGS) $(WK_LIBSANDBOX_LDFLAGS) $(WK_LIBWEBRTC_LDFLAGS) $(WK_MOBILE_CORE_SERVICES_LDFLAGS) $(WK_MOBILE_GESTALT_LDFLAGS) $(WK_OPENGL_LDFLAGS) $(WK_PDFKIT_LDFLAGS) $(WK_SAFE_BROWSING_LDFLAGS) $(WK_SECURITY_INTERFACE_LDFLAGS) $(WK_UIKIT_LDFLAGS) $(WK_URL_FORMATTING_LDFLAGS) $(WK_WEBINSPECTORUI_LDFLAGS);
 
 // Prevent C++ standard library basic_stringstream, operator new, delete and their related exception types from being exported as weak symbols.
 UNEXPORTED_SYMBOL_LDFLAGS = -Wl,-unexported_symbol -Wl,__ZTISt9bad_alloc -Wl,-unexported_symbol -Wl,__ZTISt9exception -Wl,-unexported_symbol -Wl,__ZTSSt9bad_alloc -Wl,-unexported_symbol -Wl,__ZTSSt9exception -Wl,-unexported_symbol -Wl,__ZdlPvS_ -Wl,-unexported_symbol -Wl,__ZnwmPv -Wl,-unexported_symbol -Wl,__Znwm -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEC2EOS4_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEC1EOS4_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEaSEDn -Wl,-unexported_symbol -Wl,__ZNKSt3__18functionIFvN7WebCore12PolicyActionEEEclES2_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEE4swapERS4_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEC1ERKS4_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEC2ERKS4_ -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEED1Ev -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEED2Ev -Wl,-unexported_symbol -Wl,__ZNSt3__18functionIFvN7WebCore12PolicyActionEEEaSERKS4_ -Wl,-unexported_symbol -Wl,__ZTVNSt3__117bad_function_callE -Wl,-unexported_symbol -Wl,__ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_13basic_istreamIcS2_EE -Wl,-unexported_symbol -Wl,__ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE0_NS_14basic_iostreamIcS2_EE -Wl,-unexported_symbol -Wl,__ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE16_NS_13basic_ostreamIcS2_EE -Wl,-unexported_symbol -Wl,__ZTTNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE -Wl,-unexported_symbol -Wl,__ZTVNSt3__115basic_stringbufIcNS_11char_traitsIcEENS_9allocatorIcEEEE -Wl,-unexported_symbol -Wl,__ZTVNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE -Wl,-unexported_symbol -Wl,__ZTCNSt3__118basic_stringstreamIcNS_11char_traitsIcEENS_9allocatorIcEEEE8_NS_13basic_ostreamIcS2_EE;
index 26250d6..bdbf9b4 100644 (file)
@@ -942,8 +942,8 @@ NetworkSessionCocoa::NetworkSessionCocoa(NetworkProcess& networkProcess, Network
 
     NSURLSessionConfiguration *configuration = configurationForSessionID(m_sessionID);
 
-#if HAVE(LOAD_OPTIMIZER)
-    NETWORKSESSIONCOCOA_LOADOPTIMIZER_ADDITIONS
+#if HAVE(APP_SSO)
+    configuration._preventsAppSSO = true;
 #endif
 
 #if USE(CFNETWORK_AUTO_ADDED_HTTP_HEADER_SUPPRESSION)
index 787bcba..d60037f 100644 (file)
@@ -405,7 +405,7 @@ void PluginProcess::platformInitializePluginProcess(PluginProcessCreationParamet
     // Disable Dark Mode in the plugin process to avoid rendering issues.
     [NSApp setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameAqua]];
 #endif
-#if HAVE(LOAD_OPTIMIZER)
+#if HAVE(APP_SSO)
     [NSURLSession _disableAppSSO];
 #endif
 }
index 46617be..27dcf09 100644 (file)
@@ -328,6 +328,15 @@ UIProcess/Automation/ios/WebAutomationSessionIOS.mm
 
 UIProcess/Automation/mac/WebAutomationSessionMac.mm
 
+UIProcess/Cocoa/SOAuthorization/NavigationSOAuthorizationSession.mm
+UIProcess/Cocoa/SOAuthorization/PopUpSOAuthorizationSession.mm
+UIProcess/Cocoa/SOAuthorization/RedirectSOAuthorizationSession.mm
+UIProcess/Cocoa/SOAuthorization/SOAuthorizationCoordinator.mm
+UIProcess/Cocoa/SOAuthorization/SOAuthorizationNSURLExtras.mm
+UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.mm
+UIProcess/Cocoa/SOAuthorization/SubFrameSOAuthorizationSession.mm
+UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.mm
+
 UIProcess/Cocoa/AutomationClient.mm
 UIProcess/Cocoa/AutomationSessionClient.mm
 UIProcess/Cocoa/DiagnosticLoggingClient.mm
index c1528f8..4f81fdf 100644 (file)
@@ -69,8 +69,9 @@ public:
 
     Navigation* mainFrameNavigation() const { return m_mainFrameNavigation.get(); }
 
-#if HAVE(LOAD_OPTIMIZER)
-APINAVIGATIONACTION_LOADOPTIMIZER_ADDITIONS_1
+#if HAVE(APP_SSO)
+    bool shouldPerformSOAuthorization() { return m_shouldPerformSOAuthorization; }
+    void unsetShouldPerformSOAuthorization() { m_shouldPerformSOAuthorization = false; }
 #endif
 
 private:
@@ -100,8 +101,8 @@ private:
     WTF::URL m_originalURL;
 
     bool m_shouldOpenAppLinks;
-#if HAVE(LOAD_OPTIMIZER)
-APINAVIGATIONACTION_LOADOPTIMIZER_ADDITIONS_2
+#if HAVE(APP_SSO)
+    bool m_shouldPerformSOAuthorization { true };
 #endif
 
     RefPtr<UserInitiatedAction> m_userInitiatedAction;
index a4ea6b7..325ff81 100644 (file)
@@ -40,6 +40,7 @@
 #import "Logging.h"
 #import "NavigationActionData.h"
 #import "PageLoadState.h"
+#import "SOAuthorizationCoordinator.h"
 #import "WKBackForwardListInternal.h"
 #import "WKBackForwardListItemInternal.h"
 #import "WKFrameInfoInternal.h"
 #import <pal/spi/cocoa/LaunchServicesSPI.h>
 #endif
 
-#if USE(APPLE_INTERNAL_SDK)
-#import <WebKitAdditions/NavigationStateAdditions.mm>
-#endif
-
 #if USE(QUICK_LOOK)
 #import "QuickLookDocumentData.h"
 #endif
@@ -474,12 +471,18 @@ bool NavigationState::NavigationClient::willGoToBackForwardListItem(WebPageProxy
 }
 #endif
 
-#if !USE(APPLE_INTERNAL_SDK)
-static void tryOptimizingLoad(Ref<API::NavigationAction>&&, WebPageProxy&, Function<void(bool)>&& completionHandler)
+static void trySOAuthorization(Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, Function<void(bool)>&& completionHandler)
 {
+#if HAVE(APP_SSO)
+    if (!navigationAction->shouldPerformSOAuthorization()) {
+        completionHandler(false);
+        return;
+    }
+    page.websiteDataStore().soAuthorizationCoordinator().tryAuthorize(WTFMove(navigationAction), page, WTFMove(completionHandler));
+#else
     completionHandler(false);
-}
 #endif
+}
 
 static void tryInterceptNavigation(Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, WTF::Function<void(bool)>&& completionHandler)
 {
@@ -489,7 +492,7 @@ static void tryInterceptNavigation(Ref<API::NavigationAction>&& navigationAction
         auto* localCompletionHandler = new WTF::Function<void (bool)>([navigationAction = WTFMove(navigationAction), weakPage = makeWeakPtr(page), completionHandler = WTFMove(completionHandler)] (bool success) mutable {
             ASSERT(RunLoop::isMain());
             if (!success && weakPage) {
-                tryOptimizingLoad(WTFMove(navigationAction), *weakPage, WTFMove(completionHandler));
+                trySOAuthorization(WTFMove(navigationAction), *weakPage, WTFMove(completionHandler));
                 return;
             }
             completionHandler(success);
@@ -504,7 +507,7 @@ static void tryInterceptNavigation(Ref<API::NavigationAction>&& navigationAction
     }
 #endif
 
-    tryOptimizingLoad(WTFMove(navigationAction), page, WTFMove(completionHandler));
+    trySOAuthorization(WTFMove(navigationAction), page, WTFMove(completionHandler));
 }
 
 void NavigationState::NavigationClient::decidePolicyForNavigationAction(WebPageProxy& webPageProxy, Ref<API::NavigationAction>&& navigationAction, Ref<WebFramePolicyListenerProxy>&& listener, API::Object* userInfo)
@@ -618,7 +621,7 @@ void NavigationState::NavigationClient::decidePolicyForNavigationAction(WebPageP
             localListener->download();
             break;
         case _WKNavigationActionPolicyAllowWithoutTryingAppLink:
-            tryOptimizingLoad(WTFMove(navigationAction), webPageProxy, [localListener = WTFMove(localListener), websitePolicies = WTFMove(apiWebsitePolicies)] (bool optimizedLoad) {
+            trySOAuthorization(WTFMove(navigationAction), webPageProxy, [localListener = WTFMove(localListener), websitePolicies = WTFMove(apiWebsitePolicies)] (bool optimizedLoad) {
                 if (optimizedLoad) {
                     localListener->ignore();
                     return;
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/NavigationSOAuthorizationSession.h b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/NavigationSOAuthorizationSession.h
new file mode 100644 (file)
index 0000000..9a02f05
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#if HAVE(APP_SSO)
+
+#include "SOAuthorizationSession.h"
+#include "WebViewDidMoveToWindowObserver.h"
+#include <wtf/CompletionHandler.h>
+
+namespace WebKit {
+
+// When the WebView, owner of the page, is not in the window, the session will then pause
+// and later resume after the WebView being moved into the window.
+// The reason to apply the above rule to the whole session instead of UI session only is UI
+// can be shown out of process, in which case WebKit will not even get notified.
+// FSM: Idle => isInWindow => Active => Completed
+//      Idle => !isInWindow => Waiting => become isInWindow => Active => Completed
+class NavigationSOAuthorizationSession : public SOAuthorizationSession, private WebViewDidMoveToWindowObserver {
+public:
+    using SOAuthorizationSession::weakPtrFactory;
+    using WeakValueType = SOAuthorizationSession::WeakValueType;
+
+    ~NavigationSOAuthorizationSession();
+
+protected:
+    using Callback = CompletionHandler<void(bool)>;
+
+    NavigationSOAuthorizationSession(SOAuthorization *, Ref<API::NavigationAction>&&, WebPageProxy&, InitiatingAction, Callback&&);
+
+    void invokeCallback(bool intercepted) { m_callback(intercepted); }
+
+private:
+    // SOAuthorizationSession
+    void shouldStartInternal() final;
+
+    // WebViewDidMoveToWindowObserver
+    void webViewDidMoveToWindow() final;
+
+    virtual void beforeStart() = 0;
+
+    Callback m_callback;
+};
+
+} // namespace WebKit
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/NavigationSOAuthorizationSession.mm b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/NavigationSOAuthorizationSession.mm
new file mode 100644 (file)
index 0000000..4226bbe
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "config.h"
+#include "NavigationSOAuthorizationSession.h"
+
+#if HAVE(APP_SSO)
+
+#import "WebPageProxy.h"
+#import <WebCore/ResourceResponse.h>
+
+namespace WebKit {
+
+NavigationSOAuthorizationSession::NavigationSOAuthorizationSession(SOAuthorization *soAuthorization, Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, InitiatingAction action, Callback&& completionHandler)
+    : SOAuthorizationSession(soAuthorization, WTFMove(navigationAction), page, action)
+    , m_callback(WTFMove(completionHandler))
+{
+}
+
+NavigationSOAuthorizationSession::~NavigationSOAuthorizationSession()
+{
+    if (m_callback)
+        m_callback(true);
+    if (state() == State::Waiting && page())
+        page()->removeObserver(*this);
+}
+
+void NavigationSOAuthorizationSession::shouldStartInternal()
+{
+    auto* pagePtr = page();
+    ASSERT(pagePtr);
+    beforeStart();
+    if (!pagePtr->isInWindow()) {
+        setState(State::Waiting);
+        pagePtr->addObserver(*this);
+        return;
+    }
+    start();
+}
+
+void NavigationSOAuthorizationSession::webViewDidMoveToWindow()
+{
+    auto* pagePtr = page();
+    if (state() != State::Waiting || !pagePtr || !pagePtr->isInWindow())
+        return;
+    start();
+    pagePtr->removeObserver(*this);
+}
+
+} // namespace WebKit
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/PopUpSOAuthorizationSession.h b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/PopUpSOAuthorizationSession.h
new file mode 100644 (file)
index 0000000..8d4bb72
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#if HAVE(APP_SSO)
+
+#include "SOAuthorizationSession.h"
+
+OBJC_CLASS WKSOSecretDelegate;
+OBJC_CLASS WKWebView;
+
+namespace API {
+class NavigationAction;
+}
+
+namespace WebKit {
+
+// FSM: Idle => Active => Completed
+class PopUpSOAuthorizationSession final : public SOAuthorizationSession {
+public:
+    using NewPageCallback = CompletionHandler<void(RefPtr<WebPageProxy>&&)>;
+    using UIClientCallback = Function<void(Ref<API::NavigationAction>&&, NewPageCallback&&)>;
+
+    static Ref<SOAuthorizationSession> create(SOAuthorization *soAuthorization, WebPageProxy& page, Ref<API::NavigationAction>&& navigationAction, NewPageCallback&& newPageCallback, UIClientCallback&& uiClientCallback);
+    ~PopUpSOAuthorizationSession();
+
+    void close(WKWebView *);
+
+private:
+    PopUpSOAuthorizationSession(SOAuthorization *, WebPageProxy&, Ref<API::NavigationAction>&&, NewPageCallback&&, UIClientCallback&&);
+
+    void shouldStartInternal() final;
+    void fallBackToWebPathInternal() final;
+    void abortInternal() final;
+    void completeInternal(WebCore::ResourceResponse&&, NSData *) final;
+
+    void initSecretWebView();
+
+    NewPageCallback m_newPageCallback;
+    UIClientCallback m_uiClientCallback;
+
+    RetainPtr<WKSOSecretDelegate> m_secretDelegate;
+    RetainPtr<WKWebView> m_secretWebView;
+};
+
+} // namespace WebKit
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/PopUpSOAuthorizationSession.mm b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/PopUpSOAuthorizationSession.mm
new file mode 100644 (file)
index 0000000..ef3ee39
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2019 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 "PopUpSOAuthorizationSession.h"
+
+#if HAVE(APP_SSO)
+
+#import "WKNavigationDelegatePrivate.h"
+#import "WKUIDelegate.h"
+#import "WKWebViewConfigurationPrivate.h"
+#import "WKWebViewInternal.h"
+#import <WebCore/ResourceResponse.h>
+#import <WebKit/APINavigationAction.h>
+#import <wtf/BlockPtr.h>
+
+@interface WKSOSecretDelegate : NSObject <WKNavigationDelegate, WKUIDelegate> {
+@private
+    WeakPtr<WebKit::PopUpSOAuthorizationSession> _session;
+    BOOL _isFirstNavigation;
+}
+
+- (instancetype)initWithSession:(WebKit::PopUpSOAuthorizationSession *)session;
+
+@end
+
+@implementation WKSOSecretDelegate
+
+- (instancetype)initWithSession:(WebKit::PopUpSOAuthorizationSession *)session
+{
+    if ((self = [super init])) {
+        _session = makeWeakPtr(session);
+        _isFirstNavigation = YES;
+    }
+    return self;
+}
+
+// WKUIDelegate
+- (void)webViewDidClose:(WKWebView *)webView
+{
+    if (!_session)
+        return;
+    _session->close(webView);
+}
+
+// WKNavigationDelegate
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
+{
+    // FIXME<rdar://problem/48787839>: We should restrict the load to only substitute data.
+    // Use the following heuristic as a workaround right now.
+    // Ignore the first load in the secret window, which navigates to the authentication URL.
+    if (_isFirstNavigation) {
+        _isFirstNavigation = NO;
+        decisionHandler(WKNavigationActionPolicyCancel);
+        return;
+    }
+    decisionHandler(_WKNavigationActionPolicyAllowWithoutTryingAppLink);
+}
+
+- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
+{
+    if (!_session)
+        return;
+    _session->close(webView);
+}
+
+@end
+
+namespace WebKit {
+
+Ref<SOAuthorizationSession> PopUpSOAuthorizationSession::create(SOAuthorization *soAuthorization, WebPageProxy& page, Ref<API::NavigationAction>&& navigationAction, NewPageCallback&& newPageCallback, UIClientCallback&& uiClientCallback)
+{
+    return adoptRef(*new PopUpSOAuthorizationSession(soAuthorization, page, WTFMove(navigationAction), WTFMove(newPageCallback), WTFMove(uiClientCallback)));
+}
+
+PopUpSOAuthorizationSession::PopUpSOAuthorizationSession(SOAuthorization *soAuthorization, WebPageProxy& page, Ref<API::NavigationAction>&& navigationAction, NewPageCallback&& newPageCallback, UIClientCallback&& uiClientCallback)
+    : SOAuthorizationSession(soAuthorization, WTFMove(navigationAction), page, InitiatingAction::PopUp)
+    , m_newPageCallback(WTFMove(newPageCallback))
+    , m_uiClientCallback(WTFMove(uiClientCallback))
+{
+}
+
+PopUpSOAuthorizationSession::~PopUpSOAuthorizationSession()
+{
+    ASSERT(state() != State::Waiting);
+    if (m_newPageCallback)
+        m_newPageCallback(nullptr);
+}
+
+void PopUpSOAuthorizationSession::shouldStartInternal()
+{
+    ASSERT(page() && page()->isInWindow());
+    start();
+}
+
+void PopUpSOAuthorizationSession::fallBackToWebPathInternal()
+{
+    m_uiClientCallback(releaseNavigationAction(), WTFMove(m_newPageCallback));
+}
+
+void PopUpSOAuthorizationSession::abortInternal()
+{
+    if (!page()) {
+        m_newPageCallback(nullptr);
+        return;
+    }
+
+    initSecretWebView();
+    m_newPageCallback(m_secretWebView->_page.get());
+    [m_secretWebView evaluateJavaScript: @"window.close()" completionHandler:nil];
+}
+
+void PopUpSOAuthorizationSession::completeInternal(WebCore::ResourceResponse&& response, NSData *data)
+{
+    if (response.httpStatusCode() != 200 || !page()) {
+        fallBackToWebPathInternal();
+        return;
+    }
+
+    initSecretWebView();
+    m_newPageCallback(m_secretWebView->_page.get());
+    [m_secretWebView loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:response.url()];
+}
+
+void PopUpSOAuthorizationSession::close(WKWebView *webView)
+{
+    if (!m_secretWebView)
+        return;
+    if (state() != State::Completed || webView != m_secretWebView.get()) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+    m_secretWebView = nullptr;
+    WTFLogAlways("SecretWebView is cleaned.");
+}
+
+void PopUpSOAuthorizationSession::initSecretWebView()
+{
+    ASSERT(page());
+    auto initiatorWebView = fromWebPageProxy(*page());
+    auto configuration = adoptNS([initiatorWebView.configuration copy]);
+    [configuration _setRelatedWebView:initiatorWebView];
+    m_secretWebView = adoptNS([[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
+
+    m_secretDelegate = adoptNS([[WKSOSecretDelegate alloc] initWithSession:this]);
+    [m_secretWebView setUIDelegate:m_secretDelegate.get()];
+    [m_secretWebView setNavigationDelegate:m_secretDelegate.get()];
+
+    m_secretWebView->_page->setShouldSuppressSOAuthorizationInAllNavigationPolicyDecision();
+    WTFLogAlways("SecretWebView is created.");
+}
+
+} // namespace WebKit
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/RedirectSOAuthorizationSession.h b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/RedirectSOAuthorizationSession.h
new file mode 100644 (file)
index 0000000..7f84197
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#if HAVE(APP_SSO)
+
+#include "NavigationSOAuthorizationSession.h"
+
+namespace WebKit {
+
+class RedirectSOAuthorizationSession final : public NavigationSOAuthorizationSession {
+public:
+    using Callback = CompletionHandler<void(bool)>;
+
+    static Ref<SOAuthorizationSession> create(SOAuthorization *soAuthorization, Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, Callback&& completionHandler);
+
+private:
+    RedirectSOAuthorizationSession(SOAuthorization *, Ref<API::NavigationAction>&&, WebPageProxy&, Callback&&);
+
+    // SOAuthorizationSession
+    void fallBackToWebPathInternal() final;
+    void abortInternal() final;
+    void completeInternal(WebCore::ResourceResponse&&, NSData *) final;
+
+    // NavigationSOAuthorizationSession
+    void beforeStart() final;
+};
+
+} // namespace WebKit
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/RedirectSOAuthorizationSession.mm b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/RedirectSOAuthorizationSession.mm
new file mode 100644 (file)
index 0000000..ee9c029
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 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 "RedirectSOAuthorizationSession.h"
+
+#if HAVE(APP_SSO)
+
+#import "DataReference.h"
+#import <WebCore/ResourceResponse.h>
+
+namespace WebKit {
+
+Ref<SOAuthorizationSession> RedirectSOAuthorizationSession::create(SOAuthorization *soAuthorization, Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, Callback&& completionHandler)
+{
+    return adoptRef(*new RedirectSOAuthorizationSession(soAuthorization, WTFMove(navigationAction), page, WTFMove(completionHandler)));
+}
+
+RedirectSOAuthorizationSession::RedirectSOAuthorizationSession(SOAuthorization *soAuthorization, Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, Callback&& completionHandler)
+    : NavigationSOAuthorizationSession(soAuthorization, WTFMove(navigationAction), page, InitiatingAction::Redirect, WTFMove(completionHandler))
+{
+}
+
+void RedirectSOAuthorizationSession::fallBackToWebPathInternal()
+{
+    invokeCallback(false);
+}
+
+void RedirectSOAuthorizationSession::abortInternal()
+{
+    invokeCallback(true);
+}
+
+void RedirectSOAuthorizationSession::completeInternal(WebCore::ResourceResponse&& response, NSData *data)
+{
+    auto* pagePtr = page();
+    if ((response.httpStatusCode() != 302 && response.httpStatusCode() != 200) || !pagePtr) {
+        fallBackToWebPathInternal();
+        return;
+    }
+    invokeCallback(true);
+    if (response.httpStatusCode() == 302) {
+#if PLATFORM(IOS)
+        auto* navigationActionPtr = navigationAction();
+        ASSERT(navigationActionPtr);
+        // MobileSafari has a WBSURLSpoofingMitigator, which will not display the provisional URL for navigations without user gestures.
+        // For slow loads that are initiated from the MobileSafari Favorites screen, the aforementioned behavior will create a period
+        // after authentication completion where the new request to the application site loads with a blank URL and blank page. To
+        // workaround this issue, we load an html page that does a client side redirection to the application site on behalf of the
+        // request URL, instead of directly loading a new request. The html page should be super fast to load and therefore will not
+        // show an empty URL or a blank page. These changes ensure a relevant URL bar and useful page content during the load.
+        if (!navigationActionPtr->isProcessingUserGesture()) {
+            pagePtr->setShouldSuppressSOAuthorizationInNextNavigationPolicyDecision();
+            auto html = makeString("<script>location = '", response.httpHeaderFields().get(WebCore::HTTPHeaderName::Location), "'</script>").utf8();
+            auto data = IPC::DataReference(reinterpret_cast<const uint8_t*>(html.data()), html.length());
+            pagePtr->loadData(data, "text/html"_s, "UTF-8"_s, navigationActionPtr->request().url());
+            return;
+        }
+#endif
+        pagePtr->loadRequest(WebCore::ResourceRequest(response.httpHeaderFields().get(WebCore::HTTPHeaderName::Location)));
+    }
+    if (response.httpStatusCode() == 200) {
+        pagePtr->setShouldSuppressSOAuthorizationInNextNavigationPolicyDecision();
+        pagePtr->loadData(IPC::DataReference(static_cast<const uint8_t*>(data.bytes), data.length), "text/html"_s, "UTF-8"_s, response.url().string());
+    }
+}
+
+void RedirectSOAuthorizationSession::beforeStart()
+{
+}
+
+} // namespace WebKit
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationCoordinator.h b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationCoordinator.h
new file mode 100644 (file)
index 0000000..47f67d3
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#if HAVE(APP_SSO)
+
+#include <wtf/Forward.h>
+#include <wtf/Noncopyable.h>
+#include <wtf/RetainPtr.h>
+
+OBJC_CLASS SOAuthorization;
+OBJC_CLASS WKSOAuthorizationDelegate;
+
+namespace API {
+class NavigationAction;
+}
+
+namespace WebCore {
+class ResourceRequest;
+}
+
+namespace WebKit {
+
+class WebPageProxy;
+
+class SOAuthorizationCoordinator {
+    WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(SOAuthorizationCoordinator);
+public:
+    SOAuthorizationCoordinator();
+
+    // For Navigation interception.
+    void tryAuthorize(Ref<API::NavigationAction>&&, WebPageProxy&, Function<void(bool)>&&);
+
+    // For PopUp interception.
+    using NewPageCallback = CompletionHandler<void(RefPtr<WebPageProxy>&&)>;
+    using UIClientCallback = Function<void(Ref<API::NavigationAction>&&, NewPageCallback&&)>;
+    void tryAuthorize(Ref<API::NavigationAction>&&, WebPageProxy&, NewPageCallback&&, UIClientCallback&&);
+
+private:
+    bool canAuthorize(const URL&) const;
+
+    RetainPtr<SOAuthorization> m_soAuthorization;
+    RetainPtr<WKSOAuthorizationDelegate> m_soAuthorizationDelegate;
+};
+
+} // namespace WebKit
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationCoordinator.mm b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationCoordinator.mm
new file mode 100644 (file)
index 0000000..db44de2
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 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 "SOAuthorizationCoordinator.h"
+
+#if HAVE(APP_SSO)
+
+#import "PopUpSOAuthorizationSession.h"
+#import "RedirectSOAuthorizationSession.h"
+#import "SubFrameSOAuthorizationSession.h"
+#import "WKSOAuthorizationDelegate.h"
+#import <WebCore/ResourceRequest.h>
+#import <pal/cocoa/AppSSOSoftLink.h>
+#import <pal/spi/cf/CFNetworkSPI.h>
+#import <pal/spi/cocoa/AuthKitSPI.h>
+#import <wtf/Function.h>
+
+namespace WebKit {
+
+SOAuthorizationCoordinator::SOAuthorizationCoordinator()
+{
+#if PLATFORM(MAC)
+    // In the case of base system, which doesn't have AppSSO.framework.
+    if (!PAL::getSOAuthorizationClass())
+        return;
+#endif
+    m_soAuthorization = adoptNS([PAL::allocSOAuthorizationInstance() init]);
+    m_soAuthorizationDelegate = adoptNS([[WKSOAuthorizationDelegate alloc] init]);
+    m_soAuthorization.get().delegate = m_soAuthorizationDelegate.get();
+    [NSURLSession _disableAppSSO];
+}
+
+bool SOAuthorizationCoordinator::canAuthorize(const URL& url) const
+{
+    return m_soAuthorization && [PAL::getSOAuthorizationClass() canPerformAuthorizationWithURL:url responseCode:0];
+}
+
+void SOAuthorizationCoordinator::tryAuthorize(Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, Function<void(bool)>&& completionHandler)
+{
+    if (!canAuthorize(navigationAction->request().url())) {
+        completionHandler(false);
+        return;
+    }
+
+    // SubFrameSOAuthorizationSession should only be allowed for Apple first parties.
+    bool subframeNavigation = navigationAction->targetFrame() && !navigationAction->targetFrame()->isMainFrame();
+    if (subframeNavigation && (!page.mainFrame() || ![AKAuthorizationController isURLFromAppleOwnedDomain:page.mainFrame()->url()])) {
+        completionHandler(false);
+        return;
+    }
+
+    auto session = subframeNavigation ? SubFrameSOAuthorizationSession::create(m_soAuthorization.get(), WTFMove(navigationAction), page, WTFMove(completionHandler)) : RedirectSOAuthorizationSession::create(m_soAuthorization.get(), WTFMove(navigationAction), page, WTFMove(completionHandler));
+    [m_soAuthorizationDelegate setSession:WTFMove(session)];
+}
+
+void SOAuthorizationCoordinator::tryAuthorize(Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, NewPageCallback&& newPageCallback, UIClientCallback&& uiClientCallback)
+{
+    bool subframeNavigation = navigationAction->sourceFrame() && !navigationAction->sourceFrame()->isMainFrame();
+    if (subframeNavigation || !navigationAction->isProcessingUserGesture() || !canAuthorize(navigationAction->request().url())) {
+        uiClientCallback(WTFMove(navigationAction), WTFMove(newPageCallback));
+        return;
+    }
+
+    auto session = PopUpSOAuthorizationSession::create(m_soAuthorization.get(), page, WTFMove(navigationAction), WTFMove(newPageCallback), WTFMove(uiClientCallback));
+    [m_soAuthorizationDelegate setSession:WTFMove(session)];
+}
+
+} // namespace WebKit
+
+#endif
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#import "config.h"
+#pragma once
+
+#if HAVE(APP_SSO)
+
+// FIXME rdar://problem/50028246 Remove the following once the radar is fixed.
+@interface NSURL (SOAuthorizationExtras)
+
++ (BOOL)_web_canPerformAuthorizationWithURL:(NSURL *)url;
+
+@end
 
-#if HAVE(LOAD_OPTIMIZER)
-#import <WebKitAdditions/TestLoadOptimizerAdditions.mm>
 #endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationNSURLExtras.mm b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationNSURLExtras.mm
new file mode 100644 (file)
index 0000000..25a0edb
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 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 "SOAuthorizationNSURLExtras.h"
+
+#if HAVE(APP_SSO)
+
+#import <pal/cocoa/AppSSOSoftLink.h>
+
+@implementation NSURL (SOAuthorizationExtras)
+
++ (BOOL)_web_canPerformAuthorizationWithURL:(NSURL *)url
+{
+    return [PAL::getSOAuthorizationClass() canPerformAuthorizationWithURL:url responseCode:0];
+}
+
+@end
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.h b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.h
new file mode 100644 (file)
index 0000000..1d1db23
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#if HAVE(APP_SSO)
+
+#include <AppSSO/SOBase.h>
+#include <wtf/Forward.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RetainPtr.h>
+#include <wtf/WeakObjCPtr.h>
+#include <wtf/WeakPtr.h>
+
+OBJC_CLASS SOAuthorization;
+
+namespace API {
+class NavigationAction;
+}
+
+namespace WebCore {
+class ResourceResponse;
+}
+
+namespace WebKit {
+
+class WebPageProxy;
+
+// A session will only be executed once.
+class SOAuthorizationSession : public RefCounted<SOAuthorizationSession>, public CanMakeWeakPtr<SOAuthorizationSession> {
+public:
+    enum class InitiatingAction : uint8_t {
+        Redirect,
+        PopUp,
+        SubFrame
+    };
+
+    using UICallback = void (^)(BOOL, NSError *);
+
+    virtual ~SOAuthorizationSession();
+
+    // Probably not start immediately.
+    void shouldStart();
+
+    // The following should only be called by SOAuthorizationDelegate methods.
+    void fallBackToWebPath();
+    void abort();
+    // Only responses that meet all of the following requirements will be processed:
+    // 1) it has the same origin as the request;
+    // 2) it has a status code of 302 or 200.
+    // Otherwise, it falls back to the web path.
+    // Only the following HTTP headers will be processed:
+    // { Set-Cookie, Location }.
+    void complete(NSHTTPURLResponse *, NSData *);
+    void presentViewController(SOAuthorizationViewController, UICallback);
+
+protected:
+    // FSM depends on derived classes.
+    enum class State : uint8_t {
+        Idle,
+        Active,
+        Waiting,
+        Completed
+    };
+
+    SOAuthorizationSession(SOAuthorization *, Ref<API::NavigationAction>&&, WebPageProxy&, InitiatingAction);
+
+    void start();
+    WebPageProxy* page() const { return m_page.get(); }
+    State state() const { return m_state; }
+    void setState(State state) { m_state = state; }
+    const API::NavigationAction* navigationAction() { return m_navigationAction.get(); }
+    Ref<API::NavigationAction> releaseNavigationAction();
+
+private:
+    virtual void shouldStartInternal() = 0;
+    virtual void fallBackToWebPathInternal() = 0;
+    virtual void abortInternal() = 0;
+    virtual void completeInternal(WebCore::ResourceResponse&&, NSData *) = 0;
+
+    void becomeCompleted();
+    void dismissViewController();
+
+    State m_state  { State::Idle };
+    WeakObjCPtr<SOAuthorization *> m_soAuthorization;
+    RefPtr<API::NavigationAction> m_navigationAction;
+    WeakPtr<WebPageProxy> m_page;
+    InitiatingAction m_action;
+
+    RetainPtr<SOAuthorizationViewController> m_viewController;
+#if PLATFORM(MAC)
+    RetainPtr<NSWindow> m_sheetWindow;
+    RetainPtr<NSObject> m_sheetWindowWillCloseObserver;
+#endif
+};
+
+} // namespace WebKit
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.mm b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SOAuthorizationSession.mm
new file mode 100644 (file)
index 0000000..83bb5c6
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#include "config.h"
+#include "SOAuthorizationSession.h"
+
+#if HAVE(APP_SSO)
+
+#import "APIHTTPCookieStore.h"
+#import "APINavigation.h"
+#import "APINavigationAction.h"
+#import "APIUIClient.h"
+#import "WKUIDelegate.h"
+#import "WebPageProxy.h"
+#import "WebSiteDataStore.h"
+#import <WebCore/ResourceResponse.h>
+#import <WebCore/SecurityOrigin.h>
+#import <pal/cocoa/AppSSOSoftLink.h>
+#import <wtf/Vector.h>
+
+namespace WebKit {
+
+namespace {
+
+static Vector<WebCore::Cookie> toCookieVector(NSArray<NSHTTPCookie *> *cookies)
+{
+    Vector<WebCore::Cookie> result;
+    result.reserveInitialCapacity(cookies.count);
+    for (id cookie in cookies)
+        result.uncheckedAppend(cookie);
+    return result;
+}
+
+static bool isSameOrigin(const WebCore::ResourceRequest& request, const WebCore::ResourceResponse& response)
+{
+    auto requestOrigin = WebCore::SecurityOrigin::create(request.url());
+    return requestOrigin->isSameOriginAs(WebCore::SecurityOrigin::create(response.url()).get());
+}
+
+} // namespace
+
+SOAuthorizationSession::SOAuthorizationSession(SOAuthorization *soAuthorization, Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, InitiatingAction action)
+    : m_soAuthorization(soAuthorization)
+    , m_navigationAction(WTFMove(navigationAction))
+    , m_page(makeWeakPtr(page))
+    , m_action(action)
+{
+}
+
+SOAuthorizationSession::~SOAuthorizationSession()
+{
+    if (m_state == State::Active && !!m_soAuthorization)
+        [m_soAuthorization cancelAuthorization];
+    if (m_state != State::Idle && m_state != State::Completed)
+        becomeCompleted();
+}
+
+Ref<API::NavigationAction> SOAuthorizationSession::releaseNavigationAction()
+{
+    return m_navigationAction.releaseNonNull();
+}
+
+void SOAuthorizationSession::becomeCompleted()
+{
+    ASSERT(m_state == State::Active || m_state == State::Waiting);
+    m_state = State::Completed;
+    if (m_viewController)
+        dismissViewController();
+}
+
+void SOAuthorizationSession::shouldStart()
+{
+    ASSERT(m_state == State::Idle);
+    if (!m_page)
+        return;
+    shouldStartInternal();
+}
+
+void SOAuthorizationSession::start()
+{
+    ASSERT((m_state == State::Idle || m_state == State::Waiting) && m_page && m_navigationAction);
+    m_state = State::Active;
+    if (!m_soAuthorization)
+        return;
+
+    // FIXME<rdar://problem/48909336>: Replace the below with AppSSO constants.
+    auto initiatorOrigin = emptyString();
+    if (m_navigationAction->sourceFrame())
+        initiatorOrigin = m_navigationAction->sourceFrame()->securityOrigin().securityOrigin().toString();
+    if (m_action == InitiatingAction::SubFrame && m_page->mainFrame())
+        initiatorOrigin = WebCore::SecurityOrigin::create(m_page->mainFrame()->url())->toString();
+    NSDictionary *authorizationOptions = @{
+        SOAuthorizationOptionUserActionInitiated: @(m_navigationAction->isProcessingUserGesture()),
+        @"initiatorOrigin": (NSString *)initiatorOrigin,
+        @"initiatingAction": @(static_cast<NSInteger>(m_action))
+    };
+    [m_soAuthorization setAuthorizationOptions:authorizationOptions];
+
+    auto *nsRequest = m_navigationAction->request().nsURLRequest(WebCore::HTTPBodyUpdatePolicy::UpdateHTTPBody);
+    [m_soAuthorization beginAuthorizationWithURL:nsRequest.URL httpHeaders:nsRequest.allHTTPHeaderFields httpBody:nsRequest.HTTPBody];
+}
+
+void SOAuthorizationSession::fallBackToWebPath()
+{
+    if (m_state != State::Active)
+        return;
+    becomeCompleted();
+    fallBackToWebPathInternal();
+}
+
+void SOAuthorizationSession::abort()
+{
+    if (m_state == State::Idle || m_state == State::Completed)
+        return;
+    becomeCompleted();
+    abortInternal();
+}
+
+void SOAuthorizationSession::complete(NSHTTPURLResponse *httpResponse, NSData *data)
+{
+    if (m_state != State::Active)
+        return;
+    ASSERT(m_navigationAction);
+    becomeCompleted();
+
+    auto response = WebCore::ResourceResponse(httpResponse);
+    if (!isSameOrigin(m_navigationAction->request(), response)) {
+        fallBackToWebPathInternal();
+        return;
+    }
+
+    // Set cookies.
+    auto cookies = toCookieVector([NSHTTPCookie cookiesWithResponseHeaderFields:httpResponse.allHeaderFields forURL:response.url()]);
+    if (cookies.isEmpty()) {
+        completeInternal(WTFMove(response), data);
+        return;
+    }
+
+    if (!m_page)
+        return;
+    m_page->websiteDataStore().cookieStore().setCookies(cookies, [weakThis = makeWeakPtr(*this), response = WTFMove(response), data = adoptNS([[NSData alloc] initWithData:data])] () mutable {
+        if (!weakThis)
+            return;
+        weakThis->completeInternal(WTFMove(response), data.get());
+    });
+}
+
+void SOAuthorizationSession::presentViewController(SOAuthorizationViewController viewController, UICallback uiCallback)
+{
+    ASSERT(m_state == State::Active);
+    // Only expect at most one UI session for the whole authorization session.
+    if (!m_page || m_viewController) {
+        uiCallback(NO, adoptNS([[NSError alloc] initWithDomain:SOErrorDomain code:kSOErrorAuthorizationPresentationFailed userInfo:nil]).get());
+        return;
+    }
+
+    m_viewController = viewController;
+#if PLATFORM(MAC)
+    ASSERT(!m_sheetWindow);
+    m_sheetWindow = [NSWindow windowWithContentViewController:m_viewController.get()];
+
+    ASSERT(!m_sheetWindowWillCloseObserver);
+    m_sheetWindowWillCloseObserver = [[NSNotificationCenter defaultCenter] addObserverForName:NSWindowWillCloseNotification object:m_sheetWindow.get() queue:nil usingBlock:[weakThis = makeWeakPtr(*this)] (NSNotification *) {
+        if (!weakThis)
+            return;
+        weakThis->dismissViewController();
+    }];
+
+    NSWindow *presentingWindow = m_page->platformWindow();
+    if (!presentingWindow) {
+        uiCallback(NO, adoptNS([[NSError alloc] initWithDomain:SOErrorDomain code:kSOErrorAuthorizationPresentationFailed userInfo:nil]).get());
+        return;
+    }
+    [presentingWindow beginSheet:m_sheetWindow.get() completionHandler:nil];
+#elif PLATFORM(IOS)
+    UIViewController *presentingViewController = m_page->uiClient().presentingViewController();
+    if (!presentingViewController) {
+        uiCallback(NO, adoptNS([[NSError alloc] initWithDomain:SOErrorDomain code:kSOErrorAuthorizationPresentationFailed userInfo:nil]).get());
+        return;
+    }
+
+    [presentingViewController presentViewController:m_viewController.get() animated:YES completion:nil];
+#endif
+
+    uiCallback(YES, nil);
+}
+
+void SOAuthorizationSession::dismissViewController()
+{
+    ASSERT(m_viewController);
+#if PLATFORM(MAC)
+    ASSERT(m_sheetWindow && m_sheetWindowWillCloseObserver);
+
+    [[NSNotificationCenter defaultCenter] removeObserver:m_sheetWindowWillCloseObserver.get()];
+    m_sheetWindowWillCloseObserver = nullptr;
+
+    [[m_sheetWindow sheetParent] endSheet:m_sheetWindow.get()];
+    m_sheetWindow = nullptr;
+#elif PLATFORM(IOS)
+    [[m_viewController presentingViewController] dismissViewControllerAnimated:YES completion:nil];
+#endif
+
+    m_viewController = nullptr;
+}
+
+} // namespace WebKit
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SubFrameSOAuthorizationSession.h b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SubFrameSOAuthorizationSession.h
new file mode 100644 (file)
index 0000000..681f53a
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#if HAVE(APP_SSO)
+
+#include "NavigationSOAuthorizationSession.h"
+
+namespace IPC {
+class DataReference;
+}
+
+namespace WebKit {
+
+class SubFrameSOAuthorizationSession final : public NavigationSOAuthorizationSession {
+public:
+    using Callback = CompletionHandler<void(bool)>;
+
+    static Ref<SOAuthorizationSession> create(SOAuthorization *soAuthorization, Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, Callback&& completionHandler);
+
+private:
+    SubFrameSOAuthorizationSession(SOAuthorization *, Ref<API::NavigationAction>&&, WebPageProxy&, Callback&&);
+
+    // SOAuthorizationSession
+    void fallBackToWebPathInternal() final;
+    void abortInternal() final;
+    void completeInternal(WebCore::ResourceResponse&&, NSData *) final;
+
+    // NavigationSOAuthorizationSession
+    void beforeStart() final;
+
+    void loadDataToFrame(const IPC::DataReference&, const URL&);
+    void postDidCancelMessageToParent(Function<void()>&&);
+};
+
+} // namespace WebKit
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SubFrameSOAuthorizationSession.mm b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/SubFrameSOAuthorizationSession.mm
new file mode 100644 (file)
index 0000000..beabc53
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2019 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 "SubFrameSOAuthorizationSession.h"
+
+#if HAVE(APP_SSO)
+
+#import "APIFrameHandle.h"
+#import "APINavigationAction.h"
+#import "DataReference.h"
+#import "WebPageProxy.h"
+#import "WebProcessProxy.h"
+#import <WebCore/ResourceResponse.h>
+#import <wtf/RunLoop.h>
+
+namespace WebKit {
+using namespace WebCore;
+
+const char* soAuthorizationPostDidStartMessageToParent = "<script>parent.postMessage('SOAuthorizationDidStart', '*');</script>";
+const char* soAuthorizationPostDidCancelMessageToParent = "parent.postMessage('SOAuthorizationDidCancel', '*');";
+
+Ref<SOAuthorizationSession> SubFrameSOAuthorizationSession::create(SOAuthorization *soAuthorization, Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, Callback&& completionHandler)
+{
+    return adoptRef(*new SubFrameSOAuthorizationSession(soAuthorization, WTFMove(navigationAction), page, WTFMove(completionHandler)));
+}
+
+SubFrameSOAuthorizationSession::SubFrameSOAuthorizationSession(SOAuthorization *soAuthorization, Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, Callback&& completionHandler)
+    : NavigationSOAuthorizationSession(soAuthorization, WTFMove(navigationAction), page, InitiatingAction::SubFrame, WTFMove(completionHandler))
+{
+}
+
+void SubFrameSOAuthorizationSession::fallBackToWebPathInternal()
+{
+    // Instead of issuing a load, we execute the Javascript directly. This provides us a callback
+    // to ensure the final load is issued after the message is posted.
+    postDidCancelMessageToParent([weakThis = makeWeakPtr(*this)] {
+        if (!weakThis)
+            return;
+        auto* pagePtr = weakThis->page();
+        auto* navigationActionPtr = weakThis->navigationAction();
+        if (!pagePtr || !navigationActionPtr)
+            return;
+
+        if (auto* targetFrame = navigationActionPtr->targetFrame()) {
+            if (auto* frame = pagePtr->process().webFrame(targetFrame->handle().frameID())) {
+                pagePtr->setShouldSuppressSOAuthorizationInNextNavigationPolicyDecision();
+                // Issue a new load to the original URL as the original load is aborted before start.
+                frame->loadURL(navigationActionPtr->request().url());
+            }
+        }
+    });
+}
+
+void SubFrameSOAuthorizationSession::abortInternal()
+{
+    ASSERT_NOT_REACHED();
+}
+
+void SubFrameSOAuthorizationSession::completeInternal(WebCore::ResourceResponse&& response, NSData *data)
+{
+    if (response.httpStatusCode() != 200) {
+        fallBackToWebPathInternal();
+        return;
+    }
+    loadDataToFrame(IPC::DataReference(reinterpret_cast<const uint8_t*>(data.bytes), data.length), response.url());
+}
+
+void SubFrameSOAuthorizationSession::beforeStart()
+{
+    // Cancelled the current load before loading the data to post SOAuthorizationDidStart to the parent frame.
+    invokeCallback(true);
+    // Currently in the middle of decidePolicyForNavigationAction, should start a new load after.
+    RunLoop::main().dispatch([weakThis = makeWeakPtr(*this)] {
+        if (!weakThis || !weakThis->page() || !weakThis->navigationAction())
+            return;
+        // Instead of executing the Javascript directly, issuing a load. This will set the origin properly.
+        weakThis->loadDataToFrame(IPC::DataReference(reinterpret_cast<const uint8_t*>(soAuthorizationPostDidStartMessageToParent), strlen(soAuthorizationPostDidStartMessageToParent)), weakThis->navigationAction()->request().url());
+    });
+}
+
+void SubFrameSOAuthorizationSession::loadDataToFrame(const IPC::DataReference& data, const URL& baseURL)
+{
+    auto* pagePtr = page();
+    auto* navigationActionPtr = navigationAction();
+    if (!pagePtr || !navigationActionPtr)
+        return;
+
+    if (auto* targetFrame = navigationActionPtr->targetFrame()) {
+        if (auto* frame = pagePtr->process().webFrame(targetFrame->handle().frameID())) {
+            pagePtr->setShouldSuppressSOAuthorizationInNextNavigationPolicyDecision();
+            frame->loadData(data, "text/html", "UTF-8", baseURL);
+        }
+    }
+}
+
+void SubFrameSOAuthorizationSession::postDidCancelMessageToParent(Function<void()>&& callback)
+{
+    auto* pagePtr = page();
+    auto* navigationActionPtr = navigationAction();
+    if (!pagePtr || !navigationActionPtr)
+        return;
+
+    if (auto* targetFrame = navigationActionPtr->targetFrame()) {
+        pagePtr->runJavaScriptInFrame(targetFrame->handle().frameID(), soAuthorizationPostDidCancelMessageToParent, false, [callback = WTFMove(callback)] (API::SerializedScriptValue*, bool, const ExceptionDetails&, ScriptValueCallback::Error) {
+            callback();
+        });
+    }
+}
+
+} // namespace WebKit
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.h b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.h
new file mode 100644 (file)
index 0000000..231dcc9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#import <pal/cocoa/AppSSOSoftLink.h>
+#import <wtf/RefCounted.h>
+
+#if HAVE(APP_SSO)
+
+namespace WebKit {
+class SOAuthorizationSession;
+class WebPageProxy;
+}
+
+@interface WKSOAuthorizationDelegate : NSObject <SOAuthorizationDelegate> {
+@package
+RefPtr<WebKit::SOAuthorizationSession> _session;
+}
+
+- (void)setSession:(RefPtr<WebKit::SOAuthorizationSession>&&)session;
+
+@end
+
+#endif
diff --git a/Source/WebKit/UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.mm b/Source/WebKit/UIProcess/Cocoa/SOAuthorization/WKSOAuthorizationDelegate.mm
new file mode 100644 (file)
index 0000000..e0514f0
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2019 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 "WKSOAuthorizationDelegate.h"
+
+#if HAVE(APP_SSO)
+
+#import "SOAuthorizationSession.h"
+#import <wtf/RunLoop.h>
+
+@implementation WKSOAuthorizationDelegate
+
+- (void)authorization:(SOAuthorization *)authorization presentViewController:(SOAuthorizationViewController)viewController withCompletion:(void (^)(BOOL success, NSError *error))completion
+{
+    ASSERT(RunLoop::isMain() && completion);
+    if (!_session) {
+        ASSERT_NOT_REACHED();
+        completion(NO, nil);
+        return;
+    }
+    _session->presentViewController(viewController, completion);
+}
+
+- (void)authorizationDidNotHandle:(SOAuthorization *)authorization
+{
+    ASSERT(RunLoop::isMain());
+    LOG_ERROR("Could not handle AppSSO.");
+    if (!_session) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+    _session->fallBackToWebPath();
+}
+
+- (void)authorizationDidCancel:(SOAuthorization *)authorization
+{
+    ASSERT(RunLoop::isMain());
+    if (!_session) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+    _session->abort();
+}
+
+- (void)authorizationDidComplete:(SOAuthorization *)authorization
+{
+    ASSERT(RunLoop::isMain());
+    LOG_ERROR("Complete AppSSO without any data.");
+    if (!_session) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+    _session->fallBackToWebPath();
+}
+
+- (void)authorization:(SOAuthorization *)authorization didCompleteWithHTTPAuthorizationHeaders:(NSDictionary *)httpAuthorizationHeaders
+{
+    ASSERT(RunLoop::isMain());
+    LOG_ERROR("Complete AppSSO with unexpected callback.");
+    if (!_session) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+    _session->fallBackToWebPath();
+}
+
+- (void)authorization:(SOAuthorization *)authorization didCompleteWithHTTPResponse:(NSHTTPURLResponse *)httpResponse httpBody:(NSData *)httpBody
+{
+    ASSERT(RunLoop::isMain());
+    if (!_session) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+    _session->complete(httpResponse, httpBody);
+}
+
+- (void)authorization:(SOAuthorization *)authorization didCompleteWithError:(NSError *)error
+{
+    ASSERT(RunLoop::isMain());
+    LOG_ERROR("Could not complete AppSSO: %d", error.code);
+    if (!_session) {
+        ASSERT_NOT_REACHED();
+        return;
+    }
+    _session->fallBackToWebPath();
+}
+
+- (void)setSession:(RefPtr<WebKit::SOAuthorizationSession>&&)session
+{
+    _session = WTFMove(session);
+    if (_session)
+        _session->shouldStart();
+}
+
+@end
+
+#endif
index 38470e5..1302a84 100644 (file)
 #include "EditableImageController.h"
 #endif
 
+#if HAVE(APP_SSO)
+#include "SOAuthorizationCoordinator.h"
+#endif
+
 // This controls what strategy we use for mouse wheel coalescing.
 #define MERGE_WHEEL_EVENTS 1
 
@@ -4730,8 +4734,9 @@ void WebPageProxy::decidePolicyForNavigationAction(Ref<WebProcessProxy>&& proces
 
         auto navigationAction = API::NavigationAction::create(WTFMove(navigationActionData), sourceFrameInfo.get(), destinationFrameInfo.ptr(), WTF::nullopt, WTFMove(request), originalRequest.url(), shouldOpenAppLinks, WTFMove(userInitiatedActivity), mainFrameNavigation);
 
-#if HAVE(LOAD_OPTIMIZER)
-WEBPAGEPROXY_LOADOPTIMIZER_ADDITIONS_3
+#if HAVE(APP_SSO)
+        if (m_shouldSuppressSOAuthorizationInNextNavigationPolicyDecision || m_shouldSuppressSOAuthorizationInAllNavigationPolicyDecision)
+            navigationAction->unsetShouldPerformSOAuthorization();
 #endif
 
         m_navigationClient->decidePolicyForNavigationAction(*this, WTFMove(navigationAction), WTFMove(listener), process->transformHandlesToObjects(userData.object()).get());
@@ -4739,8 +4744,8 @@ WEBPAGEPROXY_LOADOPTIMIZER_ADDITIONS_3
 
     m_shouldSuppressAppLinksInNextNavigationPolicyDecision = false;
 
-#if HAVE(LOAD_OPTIMIZER)
-WEBPAGEPROXY_LOADOPTIMIZER_ADDITIONS_4
+#if HAVE(APP_SSO)
+    m_shouldSuppressSOAuthorizationInNextNavigationPolicyDecision = false;
 #endif
 }
 
@@ -5029,10 +5034,10 @@ void WebPageProxy::didUpdateHistoryTitle(const String& title, const String& url,
 
 using NewPageCallback = CompletionHandler<void(RefPtr<WebPageProxy>&&)>;
 using UIClientCallback = Function<void(Ref<API::NavigationAction>&&, NewPageCallback&&)>;
-static void tryOptimizingLoad(Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, NewPageCallback&& newPageCallback, UIClientCallback&& uiClientCallback)
+static void trySOAuthorization(Ref<API::NavigationAction>&& navigationAction, WebPageProxy& page, NewPageCallback&& newPageCallback, UIClientCallback&& uiClientCallback)
 {
-#if HAVE(LOAD_OPTIMIZER)
-WEBPAGEPROXY_LOADOPTIMIZER_ADDITIONS_6
+#if HAVE(APP_SSO)
+    page.websiteDataStore().soAuthorizationCoordinator().tryAuthorize(WTFMove(navigationAction), page, WTFMove(newPageCallback), WTFMove(uiClientCallback));
 #else
     ASSERT_UNUSED(page, page.pageID());
     uiClientCallback(WTFMove(navigationAction), WTFMove(newPageCallback));
@@ -5056,8 +5061,8 @@ void WebPageProxy::createNewPage(const FrameInfoData& originatingFrameInfoData,
 
         newPage->m_shouldSuppressAppLinksInNextNavigationPolicyDecision = hostsAreEqual(URL({ }, mainFrameURL), request.url());
 
-#if HAVE(LOAD_OPTIMIZER)
-WEBPAGEPROXY_LOADOPTIMIZER_ADDITIONS_5
+#if HAVE(APP_SSO)
+        newPage->m_shouldSuppressSOAuthorizationInNextNavigationPolicyDecision = true;
 #endif
     };
 
@@ -5065,7 +5070,7 @@ WEBPAGEPROXY_LOADOPTIMIZER_ADDITIONS_5
     bool shouldOpenAppLinks = !hostsAreEqual(originatingFrameInfo->request().url(), request.url());
     auto navigationAction = API::NavigationAction::create(WTFMove(navigationActionData), originatingFrameInfo.ptr(), nullptr, WTF::nullopt, WTFMove(request), URL(), shouldOpenAppLinks, WTFMove(userInitiatedActivity));
 
-    tryOptimizingLoad(WTFMove(navigationAction), *this, WTFMove(completionHandler), [this, protectedThis = makeRef(*this), windowFeatures = WTFMove(windowFeatures)] (Ref<API::NavigationAction>&& navigationAction, CompletionHandler<void(RefPtr<WebPageProxy>&&)>&& completionHandler) mutable {
+    trySOAuthorization(WTFMove(navigationAction), *this, WTFMove(completionHandler), [this, protectedThis = makeRef(*this), windowFeatures = WTFMove(windowFeatures)] (Ref<API::NavigationAction>&& navigationAction, CompletionHandler<void(RefPtr<WebPageProxy>&&)>&& completionHandler) mutable {
         m_uiClient->createNewPage(*this, WTFMove(windowFeatures), WTFMove(navigationAction), WTFMove(completionHandler));
     });
 }
index 93374ab..b7736b6 100644 (file)
@@ -141,10 +141,6 @@ OBJC_CLASS _WKRemoteObjectRegistry;
 #include <WebCore/WebMediaSessionManagerClient.h>
 #endif
 
-#if USE(APPLE_INTERNAL_SDK)
-#include <WebKitAdditions/WebPageProxyAdditions.h>
-#endif
-
 #if ENABLE(MEDIA_SESSION)
 namespace WebCore {
 class MediaSessionMetadata;
@@ -1549,8 +1545,9 @@ public:
     void removeObserver(WebViewDidMoveToWindowObserver&);
     void webViewDidMoveToWindow();
 
-#if HAVE(LOAD_OPTIMIZER)
-WEBPAGEPROXY_LOADOPTIMIZER_ADDITIONS_1
+#if HAVE(APP_SSO)
+    void setShouldSuppressSOAuthorizationInAllNavigationPolicyDecision() { m_shouldSuppressSOAuthorizationInAllNavigationPolicyDecision = true; }
+    void setShouldSuppressSOAuthorizationInNextNavigationPolicyDecision() { m_shouldSuppressSOAuthorizationInNextNavigationPolicyDecision = true; }
 #endif
 
     Logger& logger();
@@ -2288,8 +2285,9 @@ private:
     WebCore::ResourceRequest m_decidePolicyForResponseRequest;
     bool m_shouldSuppressAppLinksInNextNavigationPolicyDecision { false };
 
-#if HAVE(LOAD_OPTIMIZER)
-WEBPAGEPROXY_LOADOPTIMIZER_ADDITIONS_2
+#if HAVE(APP_SSO)
+    bool m_shouldSuppressSOAuthorizationInNextNavigationPolicyDecision { false };
+    bool m_shouldSuppressSOAuthorizationInAllNavigationPolicyDecision { false };
 #endif
 
     Deque<NativeWebMouseEvent> m_mouseEventQueue;
index 83e3a1f..8bc0805 100644 (file)
 #include "SecKeyProxyStore.h"
 #endif
 
+#if HAVE(APP_SSO)
+#include "SOAuthorizationCoordinator.h"
+#endif
+
 namespace WebKit {
 
 static bool allowsWebsiteDataRecordsForAllOrigins;
@@ -103,10 +107,10 @@ WebsiteDataStore::WebsiteDataStore(Ref<WebsiteDataStoreConfiguration>&& configur
     , m_authenticatorManager(makeUniqueRef<AuthenticatorManager>())
 #endif
     , m_client(makeUniqueRef<WebsiteDataStoreClient>())
-{
-#if HAVE(LOAD_OPTIMIZER)
-WEBSITEDATASTORE_LOADOPTIMIZER_ADDITIONS_2
+#if HAVE(APP_SSO)
+    , m_soAuthorizationCoordinator(makeUniqueRef<SOAuthorizationCoordinator>())
 #endif
+{
     WTF::setProcessPrivileges(allPrivileges());
     maybeRegisterWithSessionIDMap();
     platformInitialize();
@@ -124,10 +128,10 @@ WebsiteDataStore::WebsiteDataStore(PAL::SessionID sessionID)
     , m_authenticatorManager(makeUniqueRef<AuthenticatorManager>())
 #endif
     , m_client(makeUniqueRef<WebsiteDataStoreClient>())
-{
-#if HAVE(LOAD_OPTIMIZER)
-WEBSITEDATASTORE_LOADOPTIMIZER_ADDITIONS_2
+#if HAVE(APP_SSO)
+    , m_soAuthorizationCoordinator(makeUniqueRef<SOAuthorizationCoordinator>())
 #endif
+{
     maybeRegisterWithSessionIDMap();
     platformInitialize();
 
index 822c3fc..0b23501 100644 (file)
 #include <WebCore/CurlProxySettings.h>
 #endif
 
-#if USE(APPLE_INTERNAL_SDK)
-#include <WebKitAdditions/WebsiteDataStoreAdditions.h>
-#endif
-
 namespace API {
 class HTTPCookieStore;
 }
@@ -74,6 +70,7 @@ namespace WebKit {
 class AuthenticatorManager;
 class SecKeyProxyStore;
 class DeviceIdHashSaltStorage;
+class SOAuthorizationCoordinator;
 class WebPageProxy;
 class WebProcessPool;
 class WebResourceLoadStatisticsStore;
@@ -255,8 +252,8 @@ public:
     WebDeviceOrientationAndMotionAccessController& deviceOrientationAndMotionAccessController() { return m_deviceOrientationAndMotionAccessController; }
 #endif
 
-#if HAVE(LOAD_OPTIMIZER)
-WEBSITEDATASTORE_LOADOPTIMIZER_ADDITIONS_1
+#if HAVE(APP_SSO)
+    SOAuthorizationCoordinator& soAuthorizationCoordinator() { return m_soAuthorizationCoordinator.get(); }
 #endif
 
 private:
@@ -336,6 +333,10 @@ private:
     UniqueRef<WebsiteDataStoreClient> m_client;
 
     RefPtr<API::HTTPCookieStore> m_cookieStore;
+
+#if HAVE(APP_SSO)
+    UniqueRef<SOAuthorizationCoordinator> m_soAuthorizationCoordinator;
+#endif
 };
 
 }
index 025ae2a..320f708 100644 (file)
                57DCEDC7214F18300016B847 /* MockLocalConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCEDC5214F18300016B847 /* MockLocalConnection.h */; };
                57DCEDCB214F4E420016B847 /* MockAuthenticatorManager.h in Headers */ = {isa = PBXBuildFile; fileRef = 57DCEDC9214F4E420016B847 /* MockAuthenticatorManager.h */; };
                57EB2E3A21E1983E00B89CDF /* U2fHidAuthenticator.h in Headers */ = {isa = PBXBuildFile; fileRef = 57EB2E3821E1983E00B89CDF /* U2fHidAuthenticator.h */; };
+               57FD318022B35158008D0E8B /* NavigationSOAuthorizationSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 57FD317822B35149008D0E8B /* NavigationSOAuthorizationSession.h */; };
+               57FD318122B3515B008D0E8B /* PopUpSOAuthorizationSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 57FD317022B35148008D0E8B /* PopUpSOAuthorizationSession.h */; };
+               57FD318222B3515E008D0E8B /* RedirectSOAuthorizationSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 57FD317422B35149008D0E8B /* RedirectSOAuthorizationSession.h */; };
+               57FD318322B35162008D0E8B /* SOAuthorizationCoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = 57FD317222B35148008D0E8B /* SOAuthorizationCoordinator.h */; };
+               57FD318422B35165008D0E8B /* SOAuthorizationNSURLExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 57FD317322B35148008D0E8B /* SOAuthorizationNSURLExtras.h */; };
+               57FD318522B35169008D0E8B /* SOAuthorizationSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 57FD317C22B3514A008D0E8B /* SOAuthorizationSession.h */; };
+               57FD318622B3516C008D0E8B /* SubFrameSOAuthorizationSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 57FD317D22B3514A008D0E8B /* SubFrameSOAuthorizationSession.h */; };
+               57FD318722B35170008D0E8B /* WKSOAuthorizationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 57FD317122B35148008D0E8B /* WKSOAuthorizationDelegate.h */; };
                587743A621C30BBE00AE9084 /* HTTPSUpgradeList.db in Resources */ = {isa = PBXBuildFile; fileRef = 587743A421C30AD800AE9084 /* HTTPSUpgradeList.db */; };
                58E977DF21C49A00005D92A6 /* NetworkHTTPSUpgradeChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = 58E977DD21C49A00005D92A6 /* NetworkHTTPSUpgradeChecker.h */; };
                5C0B17781E7C880E00E9123C /* NetworkSocketStreamMessageReceiver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5C0B17741E7C879C00E9123C /* NetworkSocketStreamMessageReceiver.cpp */; };
                57DCEDCD214F51680016B847 /* MockAuthenticatorManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MockAuthenticatorManager.cpp; sourceTree = "<group>"; };
                57EB2E3821E1983E00B89CDF /* U2fHidAuthenticator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = U2fHidAuthenticator.h; sourceTree = "<group>"; };
                57EB2E3921E1983E00B89CDF /* U2fHidAuthenticator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = U2fHidAuthenticator.cpp; sourceTree = "<group>"; };
+               57FD317022B35148008D0E8B /* PopUpSOAuthorizationSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PopUpSOAuthorizationSession.h; sourceTree = "<group>"; };
+               57FD317122B35148008D0E8B /* WKSOAuthorizationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKSOAuthorizationDelegate.h; sourceTree = "<group>"; };
+               57FD317222B35148008D0E8B /* SOAuthorizationCoordinator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOAuthorizationCoordinator.h; sourceTree = "<group>"; };
+               57FD317322B35148008D0E8B /* SOAuthorizationNSURLExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOAuthorizationNSURLExtras.h; sourceTree = "<group>"; };
+               57FD317422B35149008D0E8B /* RedirectSOAuthorizationSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RedirectSOAuthorizationSession.h; sourceTree = "<group>"; };
+               57FD317522B35149008D0E8B /* PopUpSOAuthorizationSession.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PopUpSOAuthorizationSession.mm; sourceTree = "<group>"; };
+               57FD317622B35149008D0E8B /* SOAuthorizationNSURLExtras.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SOAuthorizationNSURLExtras.mm; sourceTree = "<group>"; };
+               57FD317722B35149008D0E8B /* SubFrameSOAuthorizationSession.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SubFrameSOAuthorizationSession.mm; sourceTree = "<group>"; };
+               57FD317822B35149008D0E8B /* NavigationSOAuthorizationSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NavigationSOAuthorizationSession.h; sourceTree = "<group>"; };
+               57FD317922B35149008D0E8B /* SOAuthorizationCoordinator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SOAuthorizationCoordinator.mm; sourceTree = "<group>"; };
+               57FD317A22B3514A008D0E8B /* RedirectSOAuthorizationSession.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RedirectSOAuthorizationSession.mm; sourceTree = "<group>"; };
+               57FD317B22B3514A008D0E8B /* NavigationSOAuthorizationSession.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = NavigationSOAuthorizationSession.mm; sourceTree = "<group>"; };
+               57FD317C22B3514A008D0E8B /* SOAuthorizationSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SOAuthorizationSession.h; sourceTree = "<group>"; };
+               57FD317D22B3514A008D0E8B /* SubFrameSOAuthorizationSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SubFrameSOAuthorizationSession.h; sourceTree = "<group>"; };
+               57FD317E22B3514A008D0E8B /* WKSOAuthorizationDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKSOAuthorizationDelegate.mm; sourceTree = "<group>"; };
+               57FD317F22B3514A008D0E8B /* SOAuthorizationSession.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SOAuthorizationSession.mm; sourceTree = "<group>"; };
                587743A421C30AD800AE9084 /* HTTPSUpgradeList.db */ = {isa = PBXFileReference; lastKnownFileType = file; name = HTTPSUpgradeList.db; path = DerivedSources/WebKit2/HTTPSUpgradeList.db; sourceTree = BUILT_PRODUCTS_DIR; };
                58E977DC21C499FE005D92A6 /* NetworkHTTPSUpgradeChecker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkHTTPSUpgradeChecker.cpp; sourceTree = "<group>"; };
                58E977DD21C49A00005D92A6 /* NetworkHTTPSUpgradeChecker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NetworkHTTPSUpgradeChecker.h; sourceTree = "<group>"; };
                1ABC3DF21899E415004F0626 /* Cocoa */ = {
                        isa = PBXGroup;
                        children = (
+                               57FD316B22B3367E008D0E8B /* SOAuthorization */,
                                99C81D551C20DFBE005C4C82 /* AutomationClient.h */,
                                99C81D561C20DFBE005C4C82 /* AutomationClient.mm */,
                                990D28B71C6539A000986977 /* AutomationSessionClient.h */,
                        path = Mock;
                        sourceTree = "<group>";
                };
+               57FD316B22B3367E008D0E8B /* SOAuthorization */ = {
+                       isa = PBXGroup;
+                       children = (
+                               57FD317822B35149008D0E8B /* NavigationSOAuthorizationSession.h */,
+                               57FD317B22B3514A008D0E8B /* NavigationSOAuthorizationSession.mm */,
+                               57FD317022B35148008D0E8B /* PopUpSOAuthorizationSession.h */,
+                               57FD317522B35149008D0E8B /* PopUpSOAuthorizationSession.mm */,
+                               57FD317422B35149008D0E8B /* RedirectSOAuthorizationSession.h */,
+                               57FD317A22B3514A008D0E8B /* RedirectSOAuthorizationSession.mm */,
+                               57FD317222B35148008D0E8B /* SOAuthorizationCoordinator.h */,
+                               57FD317922B35149008D0E8B /* SOAuthorizationCoordinator.mm */,
+                               57FD317322B35148008D0E8B /* SOAuthorizationNSURLExtras.h */,
+                               57FD317622B35149008D0E8B /* SOAuthorizationNSURLExtras.mm */,
+                               57FD317C22B3514A008D0E8B /* SOAuthorizationSession.h */,
+                               57FD317F22B3514A008D0E8B /* SOAuthorizationSession.mm */,
+                               57FD317D22B3514A008D0E8B /* SubFrameSOAuthorizationSession.h */,
+                               57FD317722B35149008D0E8B /* SubFrameSOAuthorizationSession.mm */,
+                               57FD317122B35148008D0E8B /* WKSOAuthorizationDelegate.h */,
+                               57FD317E22B3514A008D0E8B /* WKSOAuthorizationDelegate.mm */,
+                       );
+                       path = SOAuthorization;
+                       sourceTree = "<group>";
+               };
                5C1426F11C23F81700D41183 /* Downloads */ = {
                        isa = PBXGroup;
                        children = (
                                2D50366B1BCDE17900E20BB3 /* NativeWebGestureEvent.h in Headers */,
                                263172CF18B469490065B9C3 /* NativeWebTouchEvent.h in Headers */,
                                1ADCB86B189831B30022EE5A /* NavigationActionData.h in Headers */,
+                               57FD318022B35158008D0E8B /* NavigationSOAuthorizationSession.h in Headers */,
                                1ABC3DF61899E437004F0626 /* NavigationState.h in Headers */,
                                1A6FBA2A11E6862700DB1371 /* NetscapeBrowserFuncs.h in Headers */,
                                1A6FBD2811E69BC200DB1371 /* NetscapePlugin.h in Headers */,
                                1A4A9F3312B844E2008FE984 /* PluginQuirks.h in Headers */,
                                7CD622781739D863005BD7FF /* PluginSandboxProfile.h in Headers */,
                                1A6FB7AF11E64B6800DB1371 /* PluginView.h in Headers */,
+                               57FD318122B3515B008D0E8B /* PopUpSOAuthorizationSession.h in Headers */,
                                83A0ED351F747CCF003299EB /* PreconnectTask.h in Headers */,
                                AAB145E6223F931200E489D8 /* PrefetchCache.h in Headers */,
                                E1CC1B9012D7EADF00625838 /* PrintInfo.h in Headers */,
                                83048AE61ACA45DC0082C832 /* ProcessThrottlerClient.h in Headers */,
                                A1E688701F6E2BAB007006A6 /* QuarantineSPI.h in Headers */,
                                A118A9EF1907AD6F00F7C92B /* QuickLookDocumentData.h in Headers */,
+                               57FD318222B3515E008D0E8B /* RedirectSOAuthorizationSession.h in Headers */,
                                2D47B56D1810714E003A3AEE /* RemoteLayerBackingStore.h in Headers */,
                                2DDF731518E95060004F5A66 /* RemoteLayerBackingStoreCollection.h in Headers */,
                                1AB16AEA164B3A8800290D62 /* RemoteLayerTreeContext.h in Headers */,
                                995226D6207D184600F78420 /* SimulatedInputDispatcher.h in Headers */,
                                2DAF06D618BD1A470081CEB1 /* SmartMagnificationController.h in Headers */,
                                2DE6943E18BD2A68005C15E5 /* SmartMagnificationControllerMessages.h in Headers */,
+                               57FD318322B35162008D0E8B /* SOAuthorizationCoordinator.h in Headers */,
+                               57FD318422B35165008D0E8B /* SOAuthorizationNSURLExtras.h in Headers */,
+                               57FD318522B35169008D0E8B /* SOAuthorizationSession.h in Headers */,
                                5272B28B1406985D0096A5D0 /* StatisticsData.h in Headers */,
                                514BDED316C98EDD00E4E25E /* StatisticsRequest.h in Headers */,
                                7A3FECA221F7C09700F267CD /* StorageAccessStatus.h in Headers */,
                                1AB31A9716BC688100F6DBC9 /* StorageManagerMessages.h in Headers */,
                                1AE00D6C18327C1200087DD7 /* StringReference.h in Headers */,
                                296BD85D15019BC30071F424 /* StringUtilities.h in Headers */,
+                               57FD318622B3516C008D0E8B /* SubFrameSOAuthorizationSession.h in Headers */,
                                4459984222833E8700E61373 /* SyntheticEditingCommandType.h in Headers */,
                                3157135F2040A9B20084F9CF /* SystemPreviewController.h in Headers */,
                                CE1A0BD61A48E6C60054EF74 /* TCCSPI.h in Headers */,
                                1DB01943211CF002009FB3E8 /* WKShareSheet.h in Headers */,
                                513E462D1AD837560016234A /* WKSharingServicePickerDelegate.h in Headers */,
                                93F549B41E3174B7000E7239 /* WKSnapshotConfiguration.h in Headers */,
+                               57FD318722B35170008D0E8B /* WKSOAuthorizationDelegate.h in Headers */,
                                7A78FF32224191960096483E /* WKStorageAccessAlert.h in Headers */,
                                BC407606124FF0270068F20A /* WKString.h in Headers */,
                                BC40761A124FF0370068F20A /* WKStringCF.h in Headers */,
index 37eb4af..bb16f8a 100644 (file)
@@ -457,7 +457,7 @@ void WebProcess::platformInitializeProcess(const AuxiliaryProcessInitializationP
     registerWithStateDumper();
 #endif
 
-#if HAVE(LOAD_OPTIMIZER)
+#if HAVE(APP_SSO)
     [NSURLSession _disableAppSSO];
 #endif
 }
index c13be67..0437400 100644 (file)
@@ -1,3 +1,34 @@
+2019-06-17  Jiewen Tan  <jiewen_tan@apple.com>
+
+        Move SOAuthorization from WebKitAdditions to WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=198874
+        <rdar://problem/47573431>
+
+        Reviewed by Brent Fulgham.
+
+        This patch moves all SOAuthorization tests from WebKitAdditions to WebKit.
+
+        * TestWebKitAPI/Configurations/TestWebKitAPI.xcconfig:
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/TestSOAuthorization.mm: Added.
+        (-[TestSOAuthorizationNavigationDelegate init]):
+        (-[TestSOAuthorizationNavigationDelegate webView:didFinishNavigation:]):
+        (-[TestSOAuthorizationNavigationDelegate webView:decidePolicyForNavigationAction:decisionHandler:]):
+        (-[TestSOAuthorizationNavigationDelegate webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:]):
+        (-[TestSOAuthorizationViewController viewDidAppear]):
+        (-[TestSOAuthorizationViewController viewDidDisappear]):
+        (overrideCanPerformAuthorizationWithURL):
+        (overrideSetDelegate):
+        (overrideBeginAuthorizationWithURL):
+        (overrideCancelAuthorization):
+        (overrideAddObserverForName):
+        (overrideIsURLFromAppleOwnedDomain):
+        (resetState):
+        (configureSOAuthorizationWebView):
+        (generateHtml):
+        (checkAuthorizationOptions):
+        (TestWebKitAPI::TEST):
+
 2019-06-17  Brent Fulgham  <bfulgham@apple.com>
 
         Ensure ITP state is relayed to Network Process on restart
index c6dfb33..891a6f3 100644 (file)
@@ -28,7 +28,14 @@ GCC_ENABLE_OBJC_EXCEPTIONS = YES;
 
 UNEXPORTED_SYMBOL_LDFLAGS = -Wl,-unexported_symbol -Wl,__ZN7testing4Test16TearDownTestCaseEv -Wl,-unexported_symbol -Wl,__ZN7testing4Test13SetUpTestCaseEv
 
-OTHER_LDFLAGS = $(inherited) $(UNEXPORTED_SYMBOL_LDFLAGS) -lgtest -force_load $(BUILT_PRODUCTS_DIR)/libTestWebKitAPI.a -framework JavaScriptCore -framework WebKit -lWebCoreTestSupport $(OTHER_LDFLAGS_PLATFORM);
+WK_AUTHKIT_LDFLAGS = $(WK_AUTHKIT_LDFLAGS_$(WK_PLATFORM_NAME));
+WK_AUTHKIT_LDFLAGS_iphoneos = $(WK_AUTHKIT_LDFLAGS$(WK_IOS_13));
+WK_AUTHKIT_LDFLAGS_iphonesimulator = $(WK_AUTHKIT_LDFLAGS$(WK_IOS_13));
+WK_AUTHKIT_LDFLAGS_IOS_SINCE_13 = -framework AuthKit;
+WK_AUTHKIT_LDFLAGS_macosx = $(WK_AUTHKIT_LDFLAGS$(WK_MACOS_1015));
+WK_AUTHKIT_LDFLAGS_MACOS_SINCE_1015 = -framework AuthKit;
+
+OTHER_LDFLAGS = $(inherited) $(UNEXPORTED_SYMBOL_LDFLAGS) -lgtest -force_load $(BUILT_PRODUCTS_DIR)/libTestWebKitAPI.a -framework JavaScriptCore -framework WebKit -lWebCoreTestSupport $(WK_AUTHKIT_LDFLAGS) $(OTHER_LDFLAGS_PLATFORM);
 OTHER_LDFLAGS_PLATFORM[sdk=macosx*] = -framework Cocoa -framework Carbon;
 
 // FIXME: This should not be built on iOS. Instead we should create and use a TestWebKitAPI application.
index fdbcd96..864729a 100644 (file)
                57599E2A1F071AA000A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityRead.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57599E251F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityRead.html */; };
                57599E2B1F071AA000A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityWrite.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57599E231F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityWrite.html */; };
                5769C50B1D9B0002000847FB /* SerializedCryptoKeyWrap.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5769C50A1D9B0001000847FB /* SerializedCryptoKeyWrap.mm */; };
-               5774AA6821FBBF7800AF2A1B /* TestLoadOptimizer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5774AA6721FBBF7800AF2A1B /* TestLoadOptimizer.mm */; };
+               5774AA6821FBBF7800AF2A1B /* TestSOAuthorization.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5774AA6721FBBF7800AF2A1B /* TestSOAuthorization.mm */; };
                5778D05622110A2600899E3B /* LoadWebArchive.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5778D05522110A2600899E3B /* LoadWebArchive.mm */; };
                578CBD67204FB2C80083B9F2 /* LocalAuthentication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 578CBD66204FB2C70083B9F2 /* LocalAuthentication.framework */; };
                57901FB11CAF142D00ED64F9 /* LoadInvalidURLRequest.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 57901FB01CAF141C00ED64F9 /* LoadInvalidURLRequest.html */; };
                57599E251F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityRead.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = IndexedDBStructuredCloneBackwardCompatibilityRead.html; sourceTree = "<group>"; };
                57599E261F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-shm */ = {isa = PBXFileReference; lastKnownFileType = file; path = "IndexedDBStructuredCloneBackwardCompatibility.sqlite3-shm"; sourceTree = "<group>"; };
                5769C50A1D9B0001000847FB /* SerializedCryptoKeyWrap.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SerializedCryptoKeyWrap.mm; sourceTree = "<group>"; };
-               5774AA6721FBBF7800AF2A1B /* TestLoadOptimizer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TestLoadOptimizer.mm; sourceTree = "<group>"; };
+               5774AA6721FBBF7800AF2A1B /* TestSOAuthorization.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TestSOAuthorization.mm; sourceTree = "<group>"; };
                5778D05522110A2600899E3B /* LoadWebArchive.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = LoadWebArchive.mm; sourceTree = "<group>"; };
                578CBD66204FB2C70083B9F2 /* LocalAuthentication.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LocalAuthentication.framework; path = System/Library/Frameworks/LocalAuthentication.framework; sourceTree = SDKROOT; };
                57901FAC1CAF12C200ED64F9 /* LoadInvalidURLRequest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LoadInvalidURLRequest.mm; sourceTree = "<group>"; };
                                515BE1701D428BD100DD7C68 /* StoreBlobThenDelete.mm */,
                                1C734B5220788C4800F430EA /* SystemColors.mm */,
                                2D70059521EDA0C6003463CB /* TabOutOfWebView.mm */,
-                               5774AA6721FBBF7800AF2A1B /* TestLoadOptimizer.mm */,
+                               5774AA6721FBBF7800AF2A1B /* TestSOAuthorization.mm */,
                                F4CD74C720FDB49600DE3794 /* TestURLSchemeHandler.h */,
                                F4CD74C820FDB49600DE3794 /* TestURLSchemeHandler.mm */,
                                C22FA32A228F8708009D7988 /* TextWidth.mm */,
                                F4F5BB5221667BAA002D06B9 /* TestFontOptions.mm in Sources */,
                                F45E15762112CE6200307E82 /* TestInputDelegate.mm in Sources */,
                                F45D3891215A7B4B002A2979 /* TestInspectorBar.mm in Sources */,
-                               5774AA6821FBBF7800AF2A1B /* TestLoadOptimizer.mm in Sources */,
                                2D1C04A71D76298B000A6816 /* TestNavigationDelegate.mm in Sources */,
                                A14FC5901B8AE36F00D107EB /* TestProtocol.mm in Sources */,
                                7CCE7EAE1A411A3400447C4C /* TestsController.cpp in Sources */,
+                               5774AA6821FBBF7800AF2A1B /* TestSOAuthorization.mm in Sources */,
                                F4CD74C920FDB49600DE3794 /* TestURLSchemeHandler.mm in Sources */,
                                2EFF06D41D8AEDBB0004BB30 /* TestWKWebView.mm in Sources */,
                                F4517B672054C49500C26721 /* TestWKWebViewController.mm in Sources */,
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/TestSOAuthorization.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/TestSOAuthorization.mm
new file mode 100644 (file)
index 0000000..8c947a5
--- /dev/null
@@ -0,0 +1,1852 @@
+/*
+ * Copyright (C) 2019 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 "Test.h"
+
+#if HAVE(APP_SSO)
+
+#import "ClassMethodSwizzler.h"
+#import "InstanceMethodSwizzler.h"
+#import "PlatformUtilities.h"
+#import "TestWKWebView.h"
+#import <WebKit/WKNavigationDelegatePrivate.h>
+#import <WebKit/WKNavigationPrivate.h>
+#import <pal/cocoa/AppSSOSoftLink.h>
+#import <pal/spi/cocoa/AuthKitSPI.h>
+#import <wtf/BlockPtr.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/StringPrintStream.h>
+#import <wtf/URL.h>
+#import <wtf/text/WTFString.h>
+
+static bool navigationCompleted = false;
+static bool authorizationPerformed = false;
+static bool authorizationCancelled = false;
+static bool uiShowed = false;
+static bool newWindowCreated = false;
+static bool haveHttpBody = false;
+static String finalURL;
+static SOAuthorization* gAuthorization;
+static id<SOAuthorizationDelegate> gDelegate;
+#if PLATFORM(MAC)
+static BlockPtr<void(NSNotification *)> gNotificationCallback;
+#endif
+static RetainPtr<WKWebView> gNewWindow;
+
+static const char* openerTemplate =
+"<html>"
+"<button onclick='clickMe()' style='width:400px;height:400px'>window.open</button>"
+"<script>"
+"function clickMe()"
+"{"
+"    window.addEventListener('message', receiveMessage, false);"
+"    newWindow = window.open('%s');"
+"    %s"
+"    if (!newWindow)"
+"        return;"
+"    const pollTimer = window.setInterval(function() {"
+"        if (newWindow.closed) {"
+"            window.clearInterval(pollTimer);"
+"            window.webkit.messageHandlers.testHandler.postMessage('WindowClosed.');"
+"        }"
+"    }, 200);"
+"}"
+"function receiveMessage(event)"
+"{"
+"    if (event.origin == 'http://www.example.com') {"
+"        window.webkit.messageHandlers.testHandler.postMessage(event.data);"
+"        %s"
+"    }"
+"}"
+"</script>"
+"</html>";
+
+static const char* newWindowResponseTemplate =
+"<html>"
+"<script>"
+"window.opener.postMessage('Hello.', '*');"
+"%s"
+"</script>"
+"</html>";
+
+static const char* parentTemplate =
+"<html>"
+"<iframe src='%s'></iframe>"
+"<script>"
+"function receiveMessage(event)"
+"{"
+"    window.webkit.messageHandlers.testHandler.postMessage(event.origin);"
+"    window.webkit.messageHandlers.testHandler.postMessage(event.data);"
+"}"
+"window.addEventListener('message', receiveMessage, false);"
+"</script>"
+"</html>";
+
+static const char* iframeTemplate =
+"<html>"
+"<script>"
+"parent.postMessage('Hello.', '*');"
+"</script>"
+"</html>";
+
+static const char* samlResponse =
+"<html>"
+"<script>"
+"window.webkit.messageHandlers.testHandler.postMessage('SAML');"
+"</script>"
+"</html>";
+
+@interface TestSOAuthorizationNavigationDelegate : NSObject <WKNavigationDelegate, WKUIDelegate>
+@property bool isDefaultPolicy;
+- (instancetype)init;
+@end
+
+@implementation TestSOAuthorizationNavigationDelegate
+
+- (instancetype)init
+{
+    if (self = [super init])
+        self.isDefaultPolicy = true;
+    return self;
+}
+
+- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
+{
+    EXPECT_FALSE(navigationCompleted);
+    navigationCompleted = true;
+    finalURL = navigation._request.URL.absoluteString;
+}
+
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
+{
+    if (self.isDefaultPolicy) {
+        decisionHandler(WKNavigationActionPolicyAllow);
+        return;
+    }
+    decisionHandler(_WKNavigationActionPolicyAllowWithoutTryingAppLink);
+}
+
+- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
+{
+    EXPECT_FALSE(newWindowCreated);
+    newWindowCreated = true;
+    gNewWindow = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration];
+    [gNewWindow setNavigationDelegate:self];
+    return gNewWindow.get();
+}
+
+@end
+
+#if PLATFORM(MAC)
+@interface TestSOAuthorizationViewController : NSViewController
+#elif PLATFORM(IOS)
+@interface TestSOAuthorizationViewController : UIViewController
+#endif
+@end
+
+@implementation TestSOAuthorizationViewController
+
+- (void)viewDidAppear
+{
+    EXPECT_FALSE(uiShowed);
+    uiShowed = true;
+}
+
+- (void)viewDidDisappear
+{
+    EXPECT_TRUE(uiShowed);
+    uiShowed = false;
+}
+
+@end
+
+static bool overrideCanPerformAuthorizationWithURL(id, SEL, NSURL *url, NSInteger)
+{
+    if (![url.lastPathComponent isEqual:@"simple.html"] && ![url.host isEqual:@"www.example.com"] && ![url.lastPathComponent isEqual:@"GetSessionCookie.html"])
+        return false;
+    return true;
+}
+
+static void overrideSetDelegate(id self, SEL, id<SOAuthorizationDelegate> delegate)
+{
+    gAuthorization = self;
+    gDelegate = delegate;
+}
+
+static void overrideBeginAuthorizationWithURL(id, SEL, NSURL *url, NSDictionary *headers, NSData *body)
+{
+    EXPECT_TRUE(headers != nil);
+    EXPECT_TRUE((body == nil) ^ haveHttpBody);
+    EXPECT_FALSE(authorizationPerformed);
+    authorizationPerformed = true;
+}
+
+static void overrideCancelAuthorization(id, SEL)
+{
+    EXPECT_FALSE(authorizationCancelled);
+    authorizationCancelled = true;
+}
+
+#if PLATFORM(MAC)
+using NotificationCallback = void (^)(NSNotification *note);
+static id overrideAddObserverForName(id, SEL, NSNotificationName name, id, NSOperationQueue *, NotificationCallback block)
+{
+    if ([name isEqual:NSWindowWillCloseNotification])
+        gNotificationCallback = makeBlockPtr(block);
+    return [NSNumber numberWithBool:YES];
+}
+#endif
+
+static bool overrideIsURLFromAppleOwnedDomain(id, SEL, NSURL *)
+{
+    return true;
+}
+
+static void resetState()
+{
+    navigationCompleted = false;
+    authorizationPerformed = false;
+    authorizationCancelled = false;
+    uiShowed = false;
+    newWindowCreated = false;
+    haveHttpBody = false;
+    finalURL = emptyString();
+    gAuthorization = nullptr;
+    gDelegate = nullptr;
+#if PLATFORM(MAC)
+    gNotificationCallback = nullptr;
+#endif
+    gNewWindow = nullptr;
+}
+
+static void configureSOAuthorizationWebView(TestWKWebView *webView, TestSOAuthorizationNavigationDelegate *delegate)
+{
+    [webView setNavigationDelegate:delegate];
+    [webView setUIDelegate:delegate];
+}
+
+static String generateHtml(const char* templateHtml, const String& substitute, const String& optionalSubstitute1 = emptyString(), const String& optionalSubstitute2 = emptyString())
+{
+    StringPrintStream stream;
+    stream.printf(templateHtml, substitute.utf8().data(), optionalSubstitute1.utf8().data(), optionalSubstitute2.utf8().data());
+    return stream.toString();
+}
+
+// FIXME<rdar://problem/48909336>: Replace the below with AppSSO constants.
+static void checkAuthorizationOptions(bool userActionInitiated, String initiatorOrigin, int initiatingAction)
+{
+    EXPECT_TRUE(gAuthorization);
+    // FIXME: Remove the selector once the iOS bots has been updated to a recent SDK.
+    auto selector = NSSelectorFromString(@"authorizationOptions");
+    if ([gAuthorization respondsToSelector:selector]) {
+        EXPECT_EQ(((NSNumber *)[gAuthorization performSelector:selector][SOAuthorizationOptionUserActionInitiated]).boolValue, userActionInitiated);
+        EXPECT_WK_STREQ([gAuthorization performSelector:selector][@"initiatorOrigin"], initiatorOrigin);
+        EXPECT_EQ(((NSNumber *)[gAuthorization performSelector:selector][@"initiatingAction"]).intValue, initiatingAction);
+    }
+}
+
+namespace TestWebKitAPI {
+
+TEST(SOAuthorizationRedirect, NoInterceptions)
+{
+    resetState();
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&navigationCompleted);
+
+    EXPECT_WK_STREQ(testURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionError)
+{
+    resetState();
+    ClassMethodSwizzler swizzler(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&navigationCompleted);
+
+    EXPECT_WK_STREQ(testURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionDoNotHandle)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    [gDelegate authorizationDidNotHandle:gAuthorization];
+    Util::run(&navigationCompleted);
+
+    EXPECT_WK_STREQ(testURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionCancel)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    [gDelegate authorizationDidCancel:gAuthorization];
+    // FIXME: Find a delegate method that can detect load cancels.
+    Util::sleep(0.5);
+    EXPECT_WK_STREQ("", webView.get()._committedURL.absoluteString);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionCompleteWithoutData)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    [gDelegate authorizationDidComplete:gAuthorization];
+    Util::run(&navigationCompleted);
+
+    EXPECT_WK_STREQ(testURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionUnexpectedCompletion)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPAuthorizationHeaders:adoptNS([[NSDictionary alloc] init]).get()];
+    Util::run(&navigationCompleted);
+
+    EXPECT_WK_STREQ(testURL.get().absoluteString, finalURL);
+}
+
+// { Default delegate method, No App Links }
+TEST(SOAuthorizationRedirect, InterceptionSucceed1)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+#if PLATFORM(IOS)
+    navigationCompleted = false;
+    Util::run(&navigationCompleted);
+#endif
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+}
+
+// { Default delegate method, With App Links }
+TEST(SOAuthorizationRedirect, InterceptionSucceed2)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    // Force App Links with a request.URL that has a different host than the current one (empty host) and ShouldOpenExternalURLsPolicy::ShouldAllow.
+    auto testURL = URL(URL(), "https://www.example.com");
+#if PLATFORM(MAC)
+    [webView _loadRequest:[NSURLRequest requestWithURL:testURL] shouldOpenExternalURLs:YES];
+#elif PLATFORM(IOS)
+    // Here we force RedirectSOAuthorizationSession to go to the with user gestures route.
+    [webView evaluateJavaScript: [NSString stringWithFormat:@"location = '%@'", (id)testURL.string()] completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+#if PLATFORM(MAC)
+    checkAuthorizationOptions(false, "", 0);
+#elif PLATFORM(IOS)
+    checkAuthorizationOptions(true, "null", 0);
+#endif
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+}
+
+// { Custom delegate method, With App Links }
+TEST(SOAuthorizationRedirect, InterceptionSucceed3)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    // Force App Links with a request.URL that has a different host than the current one (empty host) and ShouldOpenExternalURLsPolicy::ShouldAllow.
+    auto testURL = URL(URL(), "https://www.example.com");
+#if PLATFORM(MAC)
+    [webView _loadRequest:[NSURLRequest requestWithURL:testURL] shouldOpenExternalURLs:YES];
+#elif PLATFORM(IOS)
+    // Here we force RedirectSOAuthorizationSession to go to the with user gestures route.
+    [webView evaluateJavaScript: [NSString stringWithFormat:@"location = '%@'", (id)testURL.string()] completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+#if PLATFORM(MAC)
+    checkAuthorizationOptions(false, "", 0);
+#elif PLATFORM(IOS)
+    checkAuthorizationOptions(true, "null", 0);
+#endif
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+}
+
+// { _WKNavigationActionPolicyAllowWithoutTryingAppLink, No App Links }
+TEST(SOAuthorizationRedirect, InterceptionSucceed4)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    // A separate delegate that implements decidePolicyForNavigationAction.
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+    [delegate setIsDefaultPolicy:false];
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+#if PLATFORM(IOS)
+    navigationCompleted = false;
+    Util::run(&navigationCompleted);
+#endif
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionSucceedWithOtherHttpStatusCode)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:400 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+    EXPECT_WK_STREQ(testURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionSucceedWithCookie)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Set-Cookie" : @"sessionid=38afes7a8;", @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+#if PLATFORM(IOS)
+    navigationCompleted = false;
+    Util::run(&navigationCompleted);
+#endif
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+
+    navigationCompleted = false;
+    [webView evaluateJavaScript: @"document.cookie" completionHandler:^(NSString *result, NSError *) {
+        EXPECT_WK_STREQ("sessionid=38afes7a8", [result UTF8String]);
+        navigationCompleted = true;
+    }];
+    Util::run(&navigationCompleted);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionSucceedWithCookies)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Set-Cookie" : @"sessionid=38afes7a8, qwerty=219ffwef9w0f, id=a3fWa;", @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+#if PLATFORM(IOS)
+    navigationCompleted = false;
+    Util::run(&navigationCompleted);
+#endif
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+
+    navigationCompleted = false;
+    [webView evaluateJavaScript: @"document.cookie" completionHandler:^(NSString *result, NSError *) {
+        EXPECT_WK_STREQ("id=a3fWa; qwerty=219ffwef9w0f; sessionid=38afes7a8", [result UTF8String]);
+        navigationCompleted = true;
+    }];
+    Util::run(&navigationCompleted);
+}
+
+// The redirected URL has a different domain than the test URL.
+TEST(SOAuthorizationRedirect, InterceptionSucceedWithRedirectionAndCookie)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    auto testURL = URL(URL(), "https://www.example.com");
+#if PLATFORM(MAC)
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL]];
+#elif PLATFORM(IOS)
+    // Here we force RedirectSOAuthorizationSession to go to the with user gestures route.
+    [webView evaluateJavaScript: [NSString stringWithFormat:@"location = '%@'", (id)testURL.string()] completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+#if PLATFORM(MAC)
+    checkAuthorizationOptions(false, "", 0);
+#elif PLATFORM(IOS)
+    checkAuthorizationOptions(true, "null", 0);
+#endif
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Set-Cookie" : @"sessionid=38afes7a8;", @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+
+    navigationCompleted = false;
+    [webView evaluateJavaScript: @"document.cookie" completionHandler:^(NSString *result, NSError *) {
+        EXPECT_WK_STREQ("", [result UTF8String]);
+        navigationCompleted = true;
+    }];
+    Util::run(&navigationCompleted);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionSucceedWithDifferentOrigin)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    auto redirectURL = URL(URL(), "https://www.example.com");
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:redirectURL statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : redirectURL.string() }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+    EXPECT_WK_STREQ(testURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionSucceedWithWaitingSession)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    [webView setNavigationDelegate:delegate.get()];
+    [webView setUIDelegate:delegate.get()];
+
+    // The session will be waiting since the web view is is not int the window.
+    [webView removeFromSuperview];
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::sleep(0.5);
+    EXPECT_FALSE(authorizationPerformed);
+
+    // Should activate the session.
+    [webView addToTestWindow];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+#if PLATFORM(IOS)
+    navigationCompleted = false;
+    Util::run(&navigationCompleted);
+#endif
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionSucceedWithActiveSessionDidMoveWindow)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    // Should be a no op.
+    [webView addToTestWindow];
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+#if PLATFORM(IOS)
+    navigationCompleted = false;
+    Util::run(&navigationCompleted);
+#endif
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionSucceedTwice)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    for (int i = 0; i < 2; i++) {
+        authorizationPerformed = false;
+        [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+        Util::run(&authorizationPerformed);
+        checkAuthorizationOptions(false, "", 0);
+
+        navigationCompleted = false;
+        RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+        auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+        [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+        Util::run(&navigationCompleted);
+#if PLATFORM(IOS)
+        navigationCompleted = false;
+        Util::run(&navigationCompleted);
+#endif
+        EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+    }
+}
+
+TEST(SOAuthorizationRedirect, InterceptionSucceedSuppressActiveSession)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler4(PAL::getSOAuthorizationClass(), @selector(cancelAuthorization), reinterpret_cast<IMP>(overrideCancelAuthorization));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    // Suppress the last active session.
+    authorizationPerformed = false;
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationCancelled);
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+#if PLATFORM(IOS)
+    navigationCompleted = false;
+    Util::run(&navigationCompleted);
+#endif
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionSucceedSuppressWaitingSession)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    [webView setNavigationDelegate:delegate.get()];
+    [webView setUIDelegate:delegate.get()];
+
+    // The session will be waiting since the web view is is not int the window.
+    [webView removeFromSuperview];
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::sleep(0.5);
+    EXPECT_FALSE(authorizationPerformed);
+
+    // Suppress the last waiting session.
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::sleep(0.5);
+    EXPECT_FALSE(authorizationPerformed);
+
+    // Activate the last session.
+    [webView addToTestWindow];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+#if PLATFORM(IOS)
+    navigationCompleted = false;
+    Util::run(&navigationCompleted);
+#endif
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionSucceedSAML)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    // Add a http body to the request to mimic a SAML request.
+    auto request = adoptNS([NSMutableURLRequest requestWithURL:testURL.get()]);
+    [request setHTTPBody:adoptNS([[NSData alloc] init]).get()];
+    haveHttpBody = true;
+
+    [webView loadRequest:request.get()];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(false, "", 0);
+
+    // Pass a HTTP 200 response with a html to mimic a SAML response.
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:samlResponse length:strlen(samlResponse)]).get()];
+    [webView waitForMessage:@"SAML"];
+}
+
+TEST(SOAuthorizationRedirect, AuthorizationOptions)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:@"" baseURL:(NSURL *)URL(URL(), "http://www.webkit.org")];
+    Util::run(&navigationCompleted);
+    [webView evaluateJavaScript: @"location = 'http://www.example.com'" completionHandler:nil];
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "http://www.webkit.org", 0);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionDidNotHandleTwice)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+
+    // Test passes if no crashes.
+    [gDelegate authorizationDidNotHandle:gAuthorization];
+    [gDelegate authorizationDidNotHandle:gAuthorization];
+}
+
+TEST(SOAuthorizationRedirect, InterceptionCompleteTwice)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+
+    // Test passes if no crashes.
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+}
+
+// FIXME(175204): Enable the iOS tests once the bug is fixed.
+#if PLATFORM(MAC)
+TEST(SOAuthorizationRedirect, InterceptionSucceedWithUI)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+
+    auto viewController = adoptNS([[TestSOAuthorizationViewController alloc] init]);
+    auto view = adoptNS([[NSView alloc] initWithFrame:NSZeroRect]);
+    [viewController setView:view.get()];
+
+    [gDelegate authorization:gAuthorization presentViewController:viewController.get() withCompletion:^(BOOL success, NSError *) {
+        EXPECT_TRUE(success);
+    }];
+    Util::run(&uiShowed);
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+    EXPECT_FALSE(uiShowed);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionCancelWithUI)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+
+    auto viewController = adoptNS([[TestSOAuthorizationViewController alloc] init]);
+    auto view = adoptNS([[NSView alloc] initWithFrame:NSZeroRect]);
+    [viewController setView:view.get()];
+
+    [gDelegate authorization:gAuthorization presentViewController:viewController.get() withCompletion:^(BOOL success, NSError *) {
+        EXPECT_TRUE(success);
+    }];
+    Util::run(&uiShowed);
+
+    [gDelegate authorizationDidCancel:gAuthorization];
+    // FIXME: Find a delegate method that can detect load cancels.
+    Util::sleep(0.5);
+    EXPECT_WK_STREQ("", webView.get()._committedURL.absoluteString);
+    EXPECT_FALSE(uiShowed);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionErrorWithUI)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+
+    auto viewController = adoptNS([[TestSOAuthorizationViewController alloc] init]);
+    auto view = adoptNS([[NSView alloc] initWithFrame:NSZeroRect]);
+    [viewController setView:view.get()];
+
+    [gDelegate authorization:gAuthorization presentViewController:viewController.get() withCompletion:^(BOOL success, NSError *) {
+        EXPECT_TRUE(success);
+    }];
+    Util::run(&uiShowed);
+
+    [gDelegate authorization:gAuthorization didCompleteWithError:adoptNS([[NSError alloc] initWithDomain:NSCocoaErrorDomain code:0 userInfo:nil]).get()];
+    Util::run(&navigationCompleted);
+    EXPECT_WK_STREQ(testURL.get().absoluteString, finalURL);
+    EXPECT_FALSE(uiShowed);
+}
+
+TEST(SOAuthorizationRedirect, InterceptionSucceedSuppressActiveSessionWithUI)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler4(PAL::getSOAuthorizationClass(), @selector(cancelAuthorization), reinterpret_cast<IMP>(overrideCancelAuthorization));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+
+    auto viewController = adoptNS([[TestSOAuthorizationViewController alloc] init]);
+    auto view = adoptNS([[NSView alloc] initWithFrame:NSZeroRect]);
+    [viewController setView:view.get()];
+
+    [gDelegate authorization:gAuthorization presentViewController:viewController.get() withCompletion:^(BOOL success, NSError *) {
+        EXPECT_TRUE(success);
+    }];
+    Util::run(&uiShowed);
+
+    // Suppress the last active session.
+    authorizationPerformed = false;
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationCancelled);
+    Util::run(&authorizationPerformed);
+    EXPECT_FALSE(uiShowed);
+
+    RetainPtr<NSURL> redirectURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:302 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Location" : [redirectURL absoluteString] }]);
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] init]).get()];
+    Util::run(&navigationCompleted);
+    EXPECT_WK_STREQ(redirectURL.get().absoluteString, finalURL);
+}
+
+TEST(SOAuthorizationRedirect, ShowUITwice)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+
+    auto viewController = adoptNS([[TestSOAuthorizationViewController alloc] init]);
+    auto view = adoptNS([[NSView alloc] initWithFrame:NSZeroRect]);
+    [viewController setView:view.get()];
+
+    [gDelegate authorization:gAuthorization presentViewController:viewController.get() withCompletion:^(BOOL success, NSError *) {
+        EXPECT_TRUE(success);
+    }];
+    Util::run(&uiShowed);
+
+    uiShowed = false;
+    [gDelegate authorization:gAuthorization presentViewController:viewController.get() withCompletion:^(BOOL success, NSError *error) {
+        EXPECT_FALSE(success);
+        EXPECT_EQ(error.code, kSOErrorAuthorizationPresentationFailed);
+        EXPECT_WK_STREQ(error.domain, "com.apple.AppSSO.AuthorizationError");
+        uiShowed = true;
+    }];
+    Util::run(&uiShowed);
+}
+#endif
+
+#if PLATFORM(MAC)
+TEST(SOAuthorizationRedirect, NSNotificationCenter)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
+    Util::run(&authorizationPerformed);
+
+    auto viewController = adoptNS([[TestSOAuthorizationViewController alloc] init]);
+    auto view = adoptNS([[NSView alloc] initWithFrame:NSZeroRect]);
+    {
+        InstanceMethodSwizzler swizzler4([NSNotificationCenter class], @selector(addObserverForName:object:queue:usingBlock:), reinterpret_cast<IMP>(overrideAddObserverForName));
+        [viewController setView:view.get()];
+        [gDelegate authorization:gAuthorization presentViewController:viewController.get() withCompletion:^(BOOL success, NSError *) {
+            EXPECT_TRUE(success);
+        }];
+        Util::run(&uiShowed);
+    }
+
+    gNotificationCallback(nullptr);
+    EXPECT_FALSE(uiShowed);
+}
+#endif
+
+TEST(SOAuthorizationPopUp, NoInterceptions)
+{
+    resetState();
+
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testHtml = generateHtml(openerTemplate, testURL.get().absoluteString);
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:testURL.get()];
+    Util::run(&navigationCompleted);
+
+    navigationCompleted = false;
+#if PLATFORM(MAC)
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&newWindowCreated);
+    Util::run(&navigationCompleted);
+    EXPECT_WK_STREQ(testURL.get().absoluteString, finalURL);
+}
+
+// FIXME(172614): Enable the following test for iOS once the bug is fixed.
+#if PLATFORM(MAC)
+TEST(SOAuthorizationPopUp, NoInterceptionsSubFrame)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto iframeTestHtml = generateHtml(openerTemplate, testURL.get().absoluteString);
+    auto testHtml = makeString("<iframe style='width:400px;height:400px' srcdoc=\"", iframeTestHtml, "\" />");
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+    // The new window will not navigate to the testURL as the iframe has unique origin.
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+    Util::run(&newWindowCreated);
+    EXPECT_FALSE(authorizationPerformed);
+}
+#endif
+
+TEST(SOAuthorizationPopUp, NoInterceptionsWithoutUserGesture)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    // The default value of javaScriptCanOpenWindowsAutomatically is NO on iOS, and YES on macOS.
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    configuration.get().preferences.javaScriptCanOpenWindowsAutomatically = YES;
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400) configuration:configuration.get()]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView _evaluateJavaScriptWithoutUserGesture: @"window.open('http://www.example.com')" completionHandler:nil];
+    Util::run(&newWindowCreated);
+    EXPECT_FALSE(authorizationPerformed);
+}
+
+TEST(SOAuthorizationPopUp, InterceptionError)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testHtml = generateHtml(openerTemplate, testURL.get().absoluteString);
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+#if PLATFORM(MAC)
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "file://", 1);
+
+    authorizationPerformed = false;
+    navigationCompleted = false;
+    [gDelegate authorization:gAuthorization didCompleteWithError:adoptNS([[NSError alloc] initWithDomain:NSCocoaErrorDomain code:0 userInfo:nil]).get()];
+    Util::run(&newWindowCreated);
+    Util::run(&navigationCompleted);
+    EXPECT_WK_STREQ(testURL.get().absoluteString, finalURL);
+    EXPECT_FALSE(authorizationPerformed); // Don't intercept the first navigation in the new window.
+}
+
+TEST(SOAuthorizationPopUp, InterceptionCancel)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testHtml = generateHtml(openerTemplate, testURL.get().absoluteString);
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+#if PLATFORM(MAC)
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "file://", 1);
+
+    // The secret WKWebView needs to be destroyed right the way.
+    @autoreleasepool {
+        [gDelegate authorizationDidCancel:gAuthorization];
+    }
+    [webView waitForMessage:@"WindowClosed."];
+}
+
+TEST(SOAuthorizationPopUp, InterceptionSucceedCloseByItself)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(openerTemplate, testURL.string());
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+#if PLATFORM(MAC)
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "file://", 1);
+
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+    auto resonseHtmlCString = generateHtml(newWindowResponseTemplate, "window.close();").utf8(); // The pop up closes itself.
+    // The secret WKWebView needs to be destroyed right the way.
+    @autoreleasepool {
+        [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:resonseHtmlCString.data() length:resonseHtmlCString.length()]).get()];
+    }
+    [webView waitForMessage:@"Hello."];
+    [webView waitForMessage:@"WindowClosed."];
+}
+
+TEST(SOAuthorizationPopUp, InterceptionSucceedCloseByParent)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(openerTemplate, testURL.string(), "", "event.source.close();"); // The parent closes the pop up.
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+#if PLATFORM(MAC)
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "file://", 1);
+
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+    auto resonseHtmlCString = generateHtml(newWindowResponseTemplate, "").utf8();
+    // The secret WKWebView needs to be destroyed right the way.
+    @autoreleasepool {
+        [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:resonseHtmlCString.data() length:resonseHtmlCString.length()]).get()];
+    }
+    [webView waitForMessage:@"Hello."];
+    [webView waitForMessage:@"WindowClosed."];
+}
+
+TEST(SOAuthorizationPopUp, InterceptionSucceedCloseByWebKit)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(openerTemplate, testURL.string());
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+#if PLATFORM(MAC)
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "file://", 1);
+
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+    auto resonseHtmlCString = generateHtml(newWindowResponseTemplate, "").utf8();
+    // The secret WKWebView needs to be destroyed right the way.
+    @autoreleasepool {
+        [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:resonseHtmlCString.data() length:resonseHtmlCString.length()]).get()];
+    }
+    [webView waitForMessage:@"Hello."];
+    [webView waitForMessage:@"WindowClosed."];
+}
+
+TEST(SOAuthorizationPopUp, InterceptionSucceedWithOtherHttpStatusCode)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"simple" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testHtml = generateHtml(openerTemplate, testURL.get().absoluteString);
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+#if PLATFORM(MAC)
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "file://", 1);
+
+    // Will fallback to web path.
+    navigationCompleted = false;
+    authorizationPerformed = false;
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:400 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+    auto resonseHtmlCString = generateHtml(newWindowResponseTemplate, "").utf8();
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:resonseHtmlCString.data() length:resonseHtmlCString.length()]).get()];
+    Util::run(&newWindowCreated);
+    Util::run(&navigationCompleted);
+    EXPECT_WK_STREQ(testURL.get().absoluteString, finalURL);
+    EXPECT_FALSE(authorizationPerformed);
+}
+
+// Setting cookie is ensured by other tests. Here is to cover if the whole authentication handshake can be completed.
+TEST(SOAuthorizationPopUp, InterceptionSucceedWithCookie)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(openerTemplate, testURL.string());
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+#if PLATFORM(MAC)
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "file://", 1);
+
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Set-Cookie" : @"sessionid=38afes7a8;"}]);
+    auto resonseHtmlCString = generateHtml(newWindowResponseTemplate, "").utf8();
+    // The secret WKWebView needs to be destroyed right the way.
+    @autoreleasepool {
+        [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:resonseHtmlCString.data() length:resonseHtmlCString.length()]).get()];
+    }
+    [webView waitForMessage:@"Hello."];
+    [webView waitForMessage:@"WindowClosed."];
+}
+
+TEST(SOAuthorizationPopUp, InterceptionSucceedTwice)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(openerTemplate, testURL.string());
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+    for (int i = 0; i < 2; i++) {
+        authorizationPerformed = false;
+#if PLATFORM(MAC)
+        [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+        [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+        Util::run(&authorizationPerformed);
+        checkAuthorizationOptions(true, "file://", 1);
+
+        auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+        auto resonseHtmlCString = generateHtml(newWindowResponseTemplate, "").utf8();
+        // The secret WKWebView needs to be destroyed right the way.
+        @autoreleasepool {
+            [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:resonseHtmlCString.data() length:resonseHtmlCString.length()]).get()];
+        }
+        [webView waitForMessage:@"Hello."];
+        [webView waitForMessage:@"WindowClosed."];
+    }
+}
+
+TEST(SOAuthorizationPopUp, InterceptionSucceedSuppressActiveSession)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler4(PAL::getSOAuthorizationClass(), @selector(cancelAuthorization), reinterpret_cast<IMP>(overrideCancelAuthorization));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(openerTemplate, testURL.string());
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+#if PLATFORM(MAC)
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "file://", 1);
+
+    // Suppress the last active session.
+    auto newWebView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400) configuration:webView.get().configuration]);
+    configureSOAuthorizationWebView(newWebView.get(), delegate.get());
+
+    navigationCompleted = false;
+    [newWebView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+    authorizationPerformed = false;
+#if PLATFORM(MAC)
+    [newWebView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [newWebView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&authorizationCancelled);
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "file://", 1);
+
+    navigationCompleted = false;
+    [webView evaluateJavaScript: @"newWindow" completionHandler:^(id result, NSError *) {
+        EXPECT_TRUE(result == adoptNS([NSNull null]).get());
+        navigationCompleted = true;
+    }];
+    Util::run(&navigationCompleted);
+
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+    auto resonseHtmlCString = generateHtml(newWindowResponseTemplate, "").utf8();
+    // The secret WKWebView needs to be destroyed right the way.
+    @autoreleasepool {
+        [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:resonseHtmlCString.data() length:resonseHtmlCString.length()]).get()];
+    }
+    [newWebView waitForMessage:@"Hello."];
+    [newWebView waitForMessage:@"WindowClosed."];
+}
+
+TEST(SOAuthorizationPopUp, InterceptionSucceedNewWindowNavigation)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(openerTemplate, testURL.string(), makeString("newWindow.location = '", baseURL.get().absoluteString.UTF8String, "';")); // Starts a new navigation on the new window.
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    Util::run(&navigationCompleted);
+
+#if PLATFORM(MAC)
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "file://", 1);
+
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+    auto resonseHtmlCString = generateHtml(newWindowResponseTemplate, "").utf8();
+    // The secret WKWebView needs to be destroyed right the way.
+    @autoreleasepool {
+        [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:resonseHtmlCString.data() length:resonseHtmlCString.length()]).get()];
+    }
+    [webView waitForMessage:@"Hello."];
+    [webView waitForMessage:@"WindowClosed."];
+}
+
+TEST(SOAuthorizationPopUp, AuthorizationOptions)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(openerTemplate, testURL.string());
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:(NSURL *)URL(URL(), "http://www.webkit.org")];
+    Util::run(&navigationCompleted);
+
+#if PLATFORM(MAC)
+    [webView sendClicksAtPoint:NSMakePoint(200, 200) numberOfClicks:1];
+#elif PLATFORM(IOS)
+    [webView evaluateJavaScript: @"clickMe()" completionHandler:nil];
+#endif
+    Util::run(&authorizationPerformed);
+    checkAuthorizationOptions(true, "http://www.webkit.org", 1);
+}
+
+TEST(SOAuthorizationSubFrame, NoInterceptions)
+{
+    resetState();
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"GetSessionCookie" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testHtml = generateHtml(parentTemplate, testURL.get().absoluteString);
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    [webView waitForMessage:@""];
+}
+
+TEST(SOAuthorizationSubFrame, NoInterceptionsNonAppleFirstPartyMainFrame)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    auto testHtml = generateHtml(parentTemplate, URL(URL(), "http://www.example.com").string());
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:(NSURL *)URL(URL(), "http://www.webkit.org")];
+    // Try to wait until the iframe load is finished.
+    Util::sleep(0.5);
+    // Make sure we don't intercept the iframe.
+    EXPECT_FALSE(authorizationPerformed);
+}
+
+TEST(SOAuthorizationSubFrame, InterceptionError)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+    ClassMethodSwizzler swizzler4([AKAuthorizationController class], @selector(isURLFromAppleOwnedDomain:), reinterpret_cast<IMP>(overrideIsURLFromAppleOwnedDomain));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"GetSessionCookie" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testHtml = generateHtml(parentTemplate, testURL.get().absoluteString);
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    [webView waitForMessage:@"null"];
+    [webView waitForMessage:@"SOAuthorizationDidStart"];
+    checkAuthorizationOptions(false, "file://", 2);
+
+    [gDelegate authorization:gAuthorization didCompleteWithError:adoptNS([[NSError alloc] initWithDomain:NSCocoaErrorDomain code:0 userInfo:nil]).get()];
+    [webView waitForMessage:@"null"];
+    [webView waitForMessage:@"SOAuthorizationDidCancel"];
+    [webView waitForMessage:@""];
+    // Trys to wait until the iframe load is finished.
+    Util::sleep(0.5);
+    // Make sure we don't load the request of the iframe to the main frame.
+    EXPECT_WK_STREQ("", finalURL);
+}
+
+TEST(SOAuthorizationSubFrame, InterceptionSuccess)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+    ClassMethodSwizzler swizzler4([AKAuthorizationController class], @selector(isURLFromAppleOwnedDomain:), reinterpret_cast<IMP>(overrideIsURLFromAppleOwnedDomain));
+
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(parentTemplate, testURL.string());
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:nil];
+    [webView waitForMessage:@"http://www.example.com"];
+    [webView waitForMessage:@"SOAuthorizationDidStart"];
+    checkAuthorizationOptions(false, "null", 2);
+
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+    auto iframeHtmlCString = generateHtml(iframeTemplate, "").utf8();
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:iframeHtmlCString.data() length:iframeHtmlCString.length()]).get()];
+    [webView waitForMessage:@"http://www.example.com"];
+    [webView waitForMessage:@"Hello."];
+}
+
+TEST(SOAuthorizationSubFrame, InterceptionSucceedWithOtherHttpStatusCode)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+    ClassMethodSwizzler swizzler4([AKAuthorizationController class], @selector(isURLFromAppleOwnedDomain:), reinterpret_cast<IMP>(overrideIsURLFromAppleOwnedDomain));
+
+    RetainPtr<NSURL> baseURL = [[NSBundle mainBundle] URLForResource:@"simple2" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"GetSessionCookie" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    auto testHtml = generateHtml(parentTemplate, testURL.get().absoluteString);
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:baseURL.get()];
+    [webView waitForMessage:@"null"];
+    [webView waitForMessage:@"SOAuthorizationDidStart"];
+    checkAuthorizationOptions(false, "file://", 2);
+
+    // Will fallback to web path.
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL.get() statusCode:400 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+    auto iframeHtmlCString = generateHtml(iframeTemplate, "").utf8();
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:iframeHtmlCString.data() length:iframeHtmlCString.length()]).get()];
+    [webView waitForMessage:@"null"];
+    [webView waitForMessage:@"SOAuthorizationDidCancel"];
+    [webView waitForMessage:@""];
+    // Trys to wait until the iframe load is finished.
+    Util::sleep(0.5);
+    // Make sure we don't load the request of the iframe to the main frame.
+    EXPECT_WK_STREQ("", finalURL);
+}
+
+// Setting cookie is ensured by other tests. Here is to cover if the whole authentication handshake can be completed.
+TEST(SOAuthorizationSubFrame, InterceptionSucceedWithCookie)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+    ClassMethodSwizzler swizzler4([AKAuthorizationController class], @selector(isURLFromAppleOwnedDomain:), reinterpret_cast<IMP>(overrideIsURLFromAppleOwnedDomain));
+
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(parentTemplate, testURL.string());
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:nil];
+    [webView waitForMessage:@"http://www.example.com"];
+    [webView waitForMessage:@"SOAuthorizationDidStart"];
+    checkAuthorizationOptions(false, "null", 2);
+
+    auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:@{ @"Set-Cookie" : @"sessionid=38afes7a8;"}]);
+    auto iframeHtmlCString = generateHtml(iframeTemplate, "").utf8();
+    [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:iframeHtmlCString.data() length:iframeHtmlCString.length()]).get()];
+    [webView waitForMessage:@"http://www.example.com"];
+    [webView waitForMessage:@"Hello."];
+}
+
+TEST(SOAuthorizationSubFrame, InterceptionSuccessTwice)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+    ClassMethodSwizzler swizzler4([AKAuthorizationController class], @selector(isURLFromAppleOwnedDomain:), reinterpret_cast<IMP>(overrideIsURLFromAppleOwnedDomain));
+
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(parentTemplate, testURL.string());
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    for (int i = 0; i < 2; i++) {
+        authorizationPerformed = false;
+        navigationCompleted = false;
+
+        [webView loadHTMLString:testHtml baseURL:nil];
+        [webView waitForMessage:@"http://www.example.com"];
+        [webView waitForMessage:@"SOAuthorizationDidStart"];
+        checkAuthorizationOptions(false, "null", 2);
+
+        auto response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:testURL statusCode:200 HTTPVersion:@"HTTP/1.1" headerFields:nil]);
+        auto iframeHtmlCString = generateHtml(iframeTemplate, "").utf8();
+        [gDelegate authorization:gAuthorization didCompleteWithHTTPResponse:response.get() httpBody:adoptNS([[NSData alloc] initWithBytes:iframeHtmlCString.data() length:iframeHtmlCString.length()]).get()];
+        [webView waitForMessage:@"http://www.example.com"];
+        [webView waitForMessage:@"Hello."];
+    }
+}
+
+TEST(SOAuthorizationSubFrame, AuthorizationOptions)
+{
+    resetState();
+    ClassMethodSwizzler swizzler1(PAL::getSOAuthorizationClass(), @selector(canPerformAuthorizationWithURL:responseCode:), reinterpret_cast<IMP>(overrideCanPerformAuthorizationWithURL));
+    InstanceMethodSwizzler swizzler2(PAL::getSOAuthorizationClass(), @selector(setDelegate:), reinterpret_cast<IMP>(overrideSetDelegate));
+    InstanceMethodSwizzler swizzler3(PAL::getSOAuthorizationClass(), @selector(beginAuthorizationWithURL:httpHeaders:httpBody:), reinterpret_cast<IMP>(overrideBeginAuthorizationWithURL));
+
+    auto testURL = URL(URL(), "http://www.example.com");
+    auto testHtml = generateHtml(parentTemplate, testURL.string());
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+    auto delegate = adoptNS([[TestSOAuthorizationNavigationDelegate alloc] init]);
+    configureSOAuthorizationWebView(webView.get(), delegate.get());
+
+    [webView loadHTMLString:testHtml baseURL:(NSURL *)URL(URL(), "http://www.apple.com")];
+    [webView waitForMessage:@"http://www.example.com"];
+    [webView waitForMessage:@"SOAuthorizationDidStart"];
+    checkAuthorizationOptions(false, "http://www.apple.com", 2);
+}
+
+} // namespace TestWebKitAPI
+
+#endif