REGRESSION (r219013): OAuth flows are broken when redirecting back to application...
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Aug 2017 16:30:41 +0000 (16:30 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Aug 2017 16:30:41 +0000 (16:30 +0000)
https://bugs.webkit.org/show_bug.cgi?id=175247
<rdar://problem/33679804>

Reviewed by Brady Eidson.

Source/WebCore:

Add SPI so that Safari can differentiate between a form submission and a redirected form submission
and have PolicyCheck notify the frame loader client if the navigation was in response to receiving a
redirect response. This is the WebKit portion to fix an issue when a native app makes use of an OAuth
OAuth flow that bounces to Safari for user login and then bounce back to the app. Microsoft Graph's
OAuth flow is one example.

Safari was differentiating between a form submission and a redirected form submission based on the
nullity of WKNavigationAction.sourceFrame because in both cases the navigation type was WKNavigationTypeFormSubmitted.
The navigation type is the same for both navigations because WebKit always used the navigation
action from the original request for the redirect request when the original request redirected.
Prior to r219013, WKNavigationAction.sourceFrame would be nil for a form submission that redirects.
Following r219013, WKNavigationAction.sourceFrame is non-nil unless the navigation was initiated by
API. In particular, WKNavigationAction.sourceFrame is non-nil for the redirect navigation corresponding
to a form submission that redirects.

* loader/EmptyClients.cpp:
(WebCore::EmptyFrameLoaderClient::dispatchDecidePolicyForNavigationAction):
* loader/FrameLoaderClient.h:
Have dispatchDecidePolicyForNavigationAction() take a boolean as to whether the navigation was in
response to receiving a redirect response.
* loader/PolicyChecker.cpp:
(WebCore::PolicyChecker::checkNavigationPolicy): Notify the frame loader client whether the navigation
is in response to receiving a redirect response.

Source/WebKit:

Add SPI WKNavigationAction._isRedirect to query whether the navigation was in response to receiving
a redirect response. The majority of the WebKit change is plumbing this knowledge through to connect
it with the SPI.

* Shared/NavigationActionData.cpp:
(WebKit::NavigationActionData::encode const):
(WebKit::NavigationActionData::decode):
Encode and decode the boolean NavigationActionData::isRedirect.
* Shared/NavigationActionData.h:
* UIProcess/API/APINavigationAction.h:
* UIProcess/API/Cocoa/WKNavigationAction.mm:
(-[WKNavigationAction _isRedirect]): Added.
* UIProcess/API/Cocoa/WKNavigationActionPrivate.h:
* WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction): Set NavigationActionData::isRedirect
depending on whether the navigation is in response to receiving a redirect response.
* WebProcess/WebCoreSupport/WebFrameLoaderClient.h:

Source/WebKitLegacy/mac:

Plumb knowledge of whether a navigation was in response to receiving a redirect response.
We do not actually make use of this knowledge in WebKitLegacy because we do not know of any
clients that need to make use of this information at this time. If such a needs comes up
then we can expose API/SPI similar to what we do for WebKit.

* WebCoreSupport/WebFrameLoaderClient.h:
* WebCoreSupport/WebFrameLoaderClient.mm:
(WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction):

Source/WebKitLegacy/win:

Plumb knowledge of whether a navigation was in response to receiving a redirect response.
We do not actually make use of this knowledge in WebKitLegacy because we do not know of any
clients that need to make use of this information at this time. If such a needs comes up
then we can expose API/SPI similar to what we do for WebKit.

* WebCoreSupport/WebFrameLoaderClient.cpp:
(WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction):
* WebCoreSupport/WebFrameLoaderClient.h:

Tools:

Add tests for redirects.

* TestWebKitAPI/Tests/WebKit2Cocoa/DecidePolicyForNavigationAction.mm:
(TEST):
* TestWebKitAPI/cocoa/TestProtocol.mm:
(createRedirectURL):
(-[TestProtocol startLoading]):

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

21 files changed:
Source/WebCore/ChangeLog
Source/WebCore/loader/EmptyClients.cpp
Source/WebCore/loader/FrameLoaderClient.h
Source/WebCore/loader/PolicyChecker.cpp
Source/WebKit/ChangeLog
Source/WebKit/Shared/NavigationActionData.cpp
Source/WebKit/Shared/NavigationActionData.h
Source/WebKit/UIProcess/API/APINavigationAction.h
Source/WebKit/UIProcess/API/Cocoa/WKNavigationAction.mm
Source/WebKit/UIProcess/API/Cocoa/WKNavigationActionPrivate.h
Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp
Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.h
Source/WebKitLegacy/mac/ChangeLog
Source/WebKitLegacy/mac/WebCoreSupport/WebFrameLoaderClient.h
Source/WebKitLegacy/mac/WebCoreSupport/WebFrameLoaderClient.mm
Source/WebKitLegacy/win/ChangeLog
Source/WebKitLegacy/win/WebCoreSupport/WebFrameLoaderClient.cpp
Source/WebKitLegacy/win/WebCoreSupport/WebFrameLoaderClient.h
Tools/ChangeLog
Tools/TestWebKitAPI/Tests/WebKit2Cocoa/DecidePolicyForNavigationAction.mm
Tools/TestWebKitAPI/cocoa/TestProtocol.mm

index 2dd60d3..501800f 100644 (file)
@@ -1,3 +1,35 @@
+2017-08-09  Daniel Bates  <dabates@apple.com>
+
+        REGRESSION (r219013): OAuth flows are broken when redirecting back to application after authentication
+        https://bugs.webkit.org/show_bug.cgi?id=175247
+        <rdar://problem/33679804>
+
+        Reviewed by Brady Eidson.
+
+        Add SPI so that Safari can differentiate between a form submission and a redirected form submission
+        and have PolicyCheck notify the frame loader client if the navigation was in response to receiving a
+        redirect response. This is the WebKit portion to fix an issue when a native app makes use of an OAuth
+        OAuth flow that bounces to Safari for user login and then bounce back to the app. Microsoft Graph's
+        OAuth flow is one example.
+
+        Safari was differentiating between a form submission and a redirected form submission based on the
+        nullity of WKNavigationAction.sourceFrame because in both cases the navigation type was WKNavigationTypeFormSubmitted.
+        The navigation type is the same for both navigations because WebKit always used the navigation
+        action from the original request for the redirect request when the original request redirected.
+        Prior to r219013, WKNavigationAction.sourceFrame would be nil for a form submission that redirects.
+        Following r219013, WKNavigationAction.sourceFrame is non-nil unless the navigation was initiated by
+        API. In particular, WKNavigationAction.sourceFrame is non-nil for the redirect navigation corresponding
+        to a form submission that redirects.
+
+        * loader/EmptyClients.cpp:
+        (WebCore::EmptyFrameLoaderClient::dispatchDecidePolicyForNavigationAction):
+        * loader/FrameLoaderClient.h:
+        Have dispatchDecidePolicyForNavigationAction() take a boolean as to whether the navigation was in
+        response to receiving a redirect response.
+        * loader/PolicyChecker.cpp:
+        (WebCore::PolicyChecker::checkNavigationPolicy): Notify the frame loader client whether the navigation
+        is in response to receiving a redirect response.
+
 2017-08-09  Sam Weinig  <sam@webkit.org>
 
         WTF::Function does not allow for reference / non-default constructible return types
index f864258..cd1d9cb 100644 (file)
@@ -339,7 +339,7 @@ class EmptyFrameLoaderClient final : public FrameLoaderClient {
 
     void dispatchDecidePolicyForResponse(const ResourceResponse&, const ResourceRequest&, FramePolicyFunction&&) final { }
     void dispatchDecidePolicyForNewWindowAction(const NavigationAction&, const ResourceRequest&, FormState*, const String&, FramePolicyFunction&&) final;
-    void dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, FormState*, FramePolicyFunction&&) final;
+    void dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, bool didReceiveRedirectResponse, FormState*, FramePolicyFunction&&) final;
     void cancelPolicyCheck() final { }
 
     void dispatchUnableToImplementPolicy(const ResourceError&) final { }
@@ -608,7 +608,7 @@ void EmptyFrameLoaderClient::dispatchDecidePolicyForNewWindowAction(const Naviga
 {
 }
 
-void EmptyFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, FormState*, FramePolicyFunction&&)
+void EmptyFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, bool, FormState*, FramePolicyFunction&&)
 {
 }
 
index f03128b..394d09a 100644 (file)
@@ -179,7 +179,7 @@ public:
 
     virtual void dispatchDecidePolicyForResponse(const ResourceResponse&, const ResourceRequest&, FramePolicyFunction&&) = 0;
     virtual void dispatchDecidePolicyForNewWindowAction(const NavigationAction&, const ResourceRequest&, FormState*, const String& frameName, FramePolicyFunction&&) = 0;
-    virtual void dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, FormState*, FramePolicyFunction&&) = 0;
+    virtual void dispatchDecidePolicyForNavigationAction(const NavigationAction&, const ResourceRequest&, bool didReceiveRedirectResponse, FormState*, FramePolicyFunction&&) = 0;
     virtual void cancelPolicyCheck() = 0;
 
     virtual void dispatchUnableToImplementPolicy(const ResourceError&) = 0;
index 5c68975..14babaa 100644 (file)
@@ -147,7 +147,7 @@ void PolicyChecker::checkNavigationPolicy(const ResourceRequest& request, bool d
 
     m_delegateIsDecidingNavigationPolicy = true;
     m_suggestedFilename = action.downloadAttribute().isEmpty() ? nullAtom() : action.downloadAttribute();
-    m_frame.loader().client().dispatchDecidePolicyForNavigationAction(action, request, formState, [this](PolicyAction action) {
+    m_frame.loader().client().dispatchDecidePolicyForNavigationAction(action, request, didReceiveRedirectResponse, formState, [this](PolicyAction action) {
         continueAfterNavigationPolicy(action);
     });
     m_delegateIsDecidingNavigationPolicy = false;
index b833652..080b885 100644 (file)
@@ -1,3 +1,29 @@
+2017-08-09  Daniel Bates  <dabates@apple.com>
+
+        REGRESSION (r219013): OAuth flows are broken when redirecting back to application after authentication
+        https://bugs.webkit.org/show_bug.cgi?id=175247
+        <rdar://problem/33679804>
+
+        Reviewed by Brady Eidson.
+
+        Add SPI WKNavigationAction._isRedirect to query whether the navigation was in response to receiving
+        a redirect response. The majority of the WebKit change is plumbing this knowledge through to connect
+        it with the SPI.
+
+        * Shared/NavigationActionData.cpp:
+        (WebKit::NavigationActionData::encode const):
+        (WebKit::NavigationActionData::decode):
+        Encode and decode the boolean NavigationActionData::isRedirect.
+        * Shared/NavigationActionData.h:
+        * UIProcess/API/APINavigationAction.h:
+        * UIProcess/API/Cocoa/WKNavigationAction.mm:
+        (-[WKNavigationAction _isRedirect]): Added.
+        * UIProcess/API/Cocoa/WKNavigationActionPrivate.h:
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
+        (WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction): Set NavigationActionData::isRedirect
+        depending on whether the navigation is in response to receiving a redirect response.
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.h:
+
 2017-08-09  Sam Weinig  <sam@webkit.org>
 
         WTF::Function does not allow for reference / non-default constructible return types
index f8cd178..52fad7d 100644 (file)
@@ -46,6 +46,7 @@ void NavigationActionData::encode(IPC::Encoder& encoder) const
     encoder.encodeEnum(shouldOpenExternalURLsPolicy);
     encoder << downloadAttribute;
     encoder << clickLocationInRootViewCoordinates;
+    encoder << isRedirect;
 }
 
 bool NavigationActionData::decode(IPC::Decoder& decoder, NavigationActionData& result)
@@ -68,6 +69,8 @@ bool NavigationActionData::decode(IPC::Decoder& decoder, NavigationActionData& r
         return false;
     if (!decoder.decode(result.clickLocationInRootViewCoordinates))
         return false;
+    if (!decoder.decode(result.isRedirect))
+        return false;
 
     return true;
 }
index 3e1c5e7..6637941 100644 (file)
@@ -49,6 +49,7 @@ struct NavigationActionData {
     WebCore::ShouldOpenExternalURLsPolicy shouldOpenExternalURLsPolicy { WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow };
     WTF::String downloadAttribute;
     WebCore::FloatPoint clickLocationInRootViewCoordinates;
+    bool isRedirect { false };
 };
 
 }
index acd28b8..97ddac2 100644 (file)
@@ -56,6 +56,7 @@ public:
     bool shouldOpenExternalSchemes() const { return m_navigationActionData.shouldOpenExternalURLsPolicy == WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow || m_navigationActionData.shouldOpenExternalURLsPolicy == WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemes; }
     bool shouldOpenAppLinks() const { return m_shouldOpenAppLinks && m_navigationActionData.shouldOpenExternalURLsPolicy == WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow; }
     bool shouldPerformDownload() const { return !m_navigationActionData.downloadAttribute.isNull(); }
+    bool isRedirect() const { return m_navigationActionData.isRedirect; }
 
     bool isProcessingUserGesture() const { return m_userInitiatedAction; }
     UserInitiatedAction* userInitiatedAction() const { return m_userInitiatedAction.get(); }
index 749822f..ad5e6ee 100644 (file)
@@ -231,6 +231,11 @@ static NSInteger toNSButtonNumber(WebKit::WebMouseEvent::Button mouseButton)
     return nil;
 }
 
+- (BOOL)_isRedirect
+{
+    return _navigationAction->isRedirect();
+}
+
 @end
 
 #endif
index fbd7880..1a7d817 100644 (file)
@@ -54,6 +54,8 @@ typedef NS_ENUM(NSInteger, WKSyntheticClickType) {
 @property (nonatomic, readonly) CGPoint _clickLocationInRootViewCoordinates WK_API_AVAILABLE(ios(11.0));
 #endif
 
+@property (nonatomic, readonly) BOOL _isRedirect WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+
 @end
 
 #endif
index 8eb97a5..c0a9680 100644 (file)
@@ -757,7 +757,7 @@ void WebFrameLoaderClient::dispatchDecidePolicyForNewWindowAction(const Navigati
     webPage->send(Messages::WebPageProxy::DecidePolicyForNewWindowAction(m_frame->frameID(), SecurityOriginData::fromFrame(coreFrame), navigationActionData, request, frameName, listenerID, UserData(WebProcess::singleton().transformObjectsToHandles(userData.get()).get())));
 }
 
-void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const NavigationAction& navigationAction, const ResourceRequest& request, FormState* formState, FramePolicyFunction&& function)
+void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const NavigationAction& navigationAction, const ResourceRequest& request, bool didReceiveRedirectResponse, FormState* formState, FramePolicyFunction&& function)
 {
     WebPage* webPage = m_frame->page();
     if (!webPage) {
@@ -808,6 +808,7 @@ void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const Navigat
     navigationActionData.canHandleRequest = webPage->canHandleRequest(request);
     navigationActionData.shouldOpenExternalURLsPolicy = navigationAction.shouldOpenExternalURLsPolicy();
     navigationActionData.downloadAttribute = navigationAction.downloadAttribute();
+    navigationActionData.isRedirect = didReceiveRedirectResponse;
 
     WebCore::Frame* coreFrame = m_frame->coreFrame();
     WebDocumentLoader* documentLoader = static_cast<WebDocumentLoader*>(coreFrame->loader().policyDocumentLoader());
index 4d7c5de..999fab8 100644 (file)
@@ -113,7 +113,7 @@ private:
     
     void dispatchDecidePolicyForResponse(const WebCore::ResourceResponse&, const WebCore::ResourceRequest&, WebCore::FramePolicyFunction&&) final;
     void dispatchDecidePolicyForNewWindowAction(const WebCore::NavigationAction&, const WebCore::ResourceRequest&, WebCore::FormState*, const String& frameName, WebCore::FramePolicyFunction&&) final;
-    void dispatchDecidePolicyForNavigationAction(const WebCore::NavigationAction&, const WebCore::ResourceRequest&, WebCore::FormState*, WebCore::FramePolicyFunction&&) final;
+    void dispatchDecidePolicyForNavigationAction(const WebCore::NavigationAction&, const WebCore::ResourceRequest&, bool didReceiveRedirectResponse, WebCore::FormState*, WebCore::FramePolicyFunction&&) final;
     void cancelPolicyCheck() final;
     
     void dispatchUnableToImplementPolicy(const WebCore::ResourceError&) final;
index 1e89aee..addb728 100644 (file)
@@ -1,3 +1,20 @@
+2017-08-09  Daniel Bates  <dabates@apple.com>
+
+        REGRESSION (r219013): OAuth flows are broken when redirecting back to application after authentication
+        https://bugs.webkit.org/show_bug.cgi?id=175247
+        <rdar://problem/33679804>
+
+        Reviewed by Brady Eidson.
+
+        Plumb knowledge of whether a navigation was in response to receiving a redirect response.
+        We do not actually make use of this knowledge in WebKitLegacy because we do not know of any
+        clients that need to make use of this information at this time. If such a needs comes up
+        then we can expose API/SPI similar to what we do for WebKit.
+
+        * WebCoreSupport/WebFrameLoaderClient.h:
+        * WebCoreSupport/WebFrameLoaderClient.mm:
+        (WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction):
+
 2017-08-08  Brady Eidson  <beidson@apple.com>
 
         Don't enable default icon loading in WK1 for apps linked against old SDKs.
index aad4d6f..10e5bbc 100644 (file)
@@ -120,7 +120,7 @@ private:
 
     void dispatchDecidePolicyForResponse(const WebCore::ResourceResponse&, const WebCore::ResourceRequest&, WebCore::FramePolicyFunction&&) final;
     void dispatchDecidePolicyForNewWindowAction(const WebCore::NavigationAction&, const WebCore::ResourceRequest&, WebCore::FormState*, const WTF::String& frameName, WebCore::FramePolicyFunction&&) final;
-    void dispatchDecidePolicyForNavigationAction(const WebCore::NavigationAction&, const WebCore::ResourceRequest&, WebCore::FormState*, WebCore::FramePolicyFunction&&) final;
+    void dispatchDecidePolicyForNavigationAction(const WebCore::NavigationAction&, const WebCore::ResourceRequest&, bool didReceiveRedirectResponse, WebCore::FormState*, WebCore::FramePolicyFunction&&) final;
     void cancelPolicyCheck() final;
 
     void dispatchUnableToImplementPolicy(const WebCore::ResourceError&) final;
index 6fb059e..e6ea4d6 100644 (file)
@@ -892,7 +892,7 @@ void WebFrameLoaderClient::dispatchDecidePolicyForNewWindowAction(const Navigati
                           decisionListener:setUpPolicyListener(WTFMove(function), tryAppLink ? (NSURL *)request.url() : nil).get()];
 }
 
-void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const NavigationAction& action, const ResourceRequest& request, FormState* formState, FramePolicyFunction&& function)
+void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const NavigationAction& action, const ResourceRequest& request, bool, FormState* formState, FramePolicyFunction&& function)
 {
     WebView *webView = getWebView(m_webFrame.get());
     BOOL tryAppLink = shouldTryAppLink(webView, action, core(m_webFrame.get()));
index 2105998..5ecfca1 100644 (file)
@@ -1,3 +1,20 @@
+2017-08-09  Daniel Bates  <dabates@apple.com>
+
+        REGRESSION (r219013): OAuth flows are broken when redirecting back to application after authentication
+        https://bugs.webkit.org/show_bug.cgi?id=175247
+        <rdar://problem/33679804>
+
+        Reviewed by Brady Eidson.
+
+        Plumb knowledge of whether a navigation was in response to receiving a redirect response.
+        We do not actually make use of this knowledge in WebKitLegacy because we do not know of any
+        clients that need to make use of this information at this time. If such a needs comes up
+        then we can expose API/SPI similar to what we do for WebKit.
+
+        * WebCoreSupport/WebFrameLoaderClient.cpp:
+        (WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction):
+        * WebCoreSupport/WebFrameLoaderClient.h:
+
 2017-07-25  Said Abou-Hallawa  <sabouhallawa@apple.com>
 
         Async image decoding for large images should be disabled after the first time a tile is painted
index 17f0a73..32ab82e 100644 (file)
@@ -549,7 +549,7 @@ void WebFrameLoaderClient::dispatchDecidePolicyForNewWindowAction(const Navigati
     function(PolicyUse);
 }
 
-void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const NavigationAction& action, const ResourceRequest& request, FormState* formState, FramePolicyFunction&& function)
+void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const NavigationAction& action, const ResourceRequest& request, bool, FormState* formState, FramePolicyFunction&& function)
 {
     WebView* webView = m_webFrame->webView();
     Frame* coreFrame = core(m_webFrame);
index 4bd4bbd..ff0d94e 100644 (file)
@@ -98,7 +98,7 @@ public:
 
     void dispatchDecidePolicyForResponse(const WebCore::ResourceResponse&, const WebCore::ResourceRequest&, WebCore::FramePolicyFunction&&) override;
     void dispatchDecidePolicyForNewWindowAction(const WebCore::NavigationAction&, const WebCore::ResourceRequest&, WebCore::FormState*, const WTF::String& frameName, WebCore::FramePolicyFunction&&) override;
-    void dispatchDecidePolicyForNavigationAction(const WebCore::NavigationAction&, const WebCore::ResourceRequest&, WebCore::FormState*, WebCore::FramePolicyFunction&&) override;
+    void dispatchDecidePolicyForNavigationAction(const WebCore::NavigationAction&, const WebCore::ResourceRequest&, bool didReceiveRedirectResponse, WebCore::FormState*, WebCore::FramePolicyFunction&&) override;
     void cancelPolicyCheck() override;
 
     void dispatchUnableToImplementPolicy(const WebCore::ResourceError&) override;
index 4a53209..256f398 100644 (file)
@@ -1,3 +1,19 @@
+2017-08-09  Daniel Bates  <dabates@apple.com>
+
+        REGRESSION (r219013): OAuth flows are broken when redirecting back to application after authentication
+        https://bugs.webkit.org/show_bug.cgi?id=175247
+        <rdar://problem/33679804>
+
+        Reviewed by Brady Eidson.
+
+        Add tests for redirects.
+
+        * TestWebKitAPI/Tests/WebKit2Cocoa/DecidePolicyForNavigationAction.mm:
+        (TEST):
+        * TestWebKitAPI/cocoa/TestProtocol.mm:
+        (createRedirectURL):
+        (-[TestProtocol startLoading]):
+
 2017-08-08  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Unreviewed, rolling out r220393.
index 3c14284..7e37c05 100644 (file)
@@ -28,6 +28,8 @@
 #if PLATFORM(MAC)
 
 #import "PlatformUtilities.h"
+#import "TestProtocol.h"
+#import <WebKit/WKNavigationActionPrivate.h>
 #import <wtf/RetainPtr.h>
 #import <wtf/mac/AppKitCompatibilityDeclarations.h>
 
@@ -380,6 +382,150 @@ TEST(WebKit2, DecidePolicyForNavigationActionForTargetedFormSubmission)
     action = nullptr;
 }
 
+TEST(WebKit2, DecidePolicyForNavigationActionForHyperlinkThatRedirects)
+{
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+
+    auto window = adoptNS([[NSWindow alloc] initWithContentRect:[webView frame] styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES]);
+    [[window contentView] addSubview:webView.get()];
+
+    auto controller = adoptNS([[DecidePolicyForNavigationActionController alloc] init]);
+    [webView setNavigationDelegate:controller.get()];
+    [webView setUIDelegate:controller.get()];
+
+    [TestProtocol registerWithScheme:@"http"];
+    finishedNavigation = false;
+    [webView loadHTMLString:@"<a style=\"display: block; height: 100%\" href=\"http://redirect/?result\">" baseURL:[NSURL URLWithString:@"http://webkit.org"]];
+    TestWebKitAPI::Util::run(&finishedNavigation);
+
+    decidedPolicy = false;
+    [newWebView setNavigationDelegate:controller.get()];
+    NSPoint clickPoint = NSMakePoint(100, 100);
+    [[webView hitTest:clickPoint] mouseDown:[NSEvent mouseEventWithType:NSEventTypeLeftMouseDown location:clickPoint modifierFlags:0 timestamp:0 windowNumber:[window windowNumber] context:nil eventNumber:0 clickCount:1 pressure:1]];
+    [[webView hitTest:clickPoint] mouseUp:[NSEvent mouseEventWithType:NSEventTypeLeftMouseUp location:clickPoint modifierFlags:0 timestamp:0 windowNumber:[window windowNumber] context:nil eventNumber:0 clickCount:1 pressure:1]];
+    TestWebKitAPI::Util::run(&decidedPolicy);
+
+    EXPECT_EQ(WKNavigationTypeLinkActivated, [action navigationType]);
+    EXPECT_TRUE([action sourceFrame] == [action targetFrame]);
+    EXPECT_WK_STREQ("GET", [[action request] HTTPMethod]);
+    EXPECT_WK_STREQ("http://redirect/?result", [[[action request] URL] absoluteString]);
+    EXPECT_EQ(webView.get(), [[action sourceFrame] webView]);
+    EXPECT_WK_STREQ("http", [[[action sourceFrame] securityOrigin] protocol]);
+    EXPECT_WK_STREQ("webkit.org", [[[action sourceFrame] securityOrigin] host]);
+    EXPECT_FALSE([action _isRedirect]);
+
+    // Wait to decide policy for redirect.
+    decidedPolicy = false;
+    TestWebKitAPI::Util::run(&decidedPolicy);
+
+    EXPECT_EQ(WKNavigationTypeLinkActivated, [action navigationType]);
+    EXPECT_TRUE([action sourceFrame] == [action targetFrame]);
+    EXPECT_WK_STREQ("GET", [[action request] HTTPMethod]);
+    EXPECT_WK_STREQ("http://result/", [[[action request] URL] absoluteString]);
+    EXPECT_EQ(webView.get(), [[action sourceFrame] webView]);
+    EXPECT_WK_STREQ("http", [[[action sourceFrame] securityOrigin] protocol]);
+    EXPECT_WK_STREQ("webkit.org", [[[action sourceFrame] securityOrigin] host]);
+    EXPECT_TRUE([action _isRedirect]);
+
+    [TestProtocol unregister];
+    newWebView = nullptr;
+    action = nullptr;
+}
+
+TEST(WebKit2, DecidePolicyForNavigationActionForPOSTFormSubmissionThatRedirectsToGET)
+{
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+
+    auto window = adoptNS([[NSWindow alloc] initWithContentRect:[webView frame] styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES]);
+    [[window contentView] addSubview:webView.get()];
+
+    auto controller = adoptNS([[DecidePolicyForNavigationActionController alloc] init]);
+    [webView setNavigationDelegate:controller.get()];
+    [webView setUIDelegate:controller.get()];
+
+    finishedNavigation = false;
+    [webView loadHTMLString:@"<form action=\"http://redirect/?result\" method=\"POST\"><input type=\"submit\" name=\"submitButton\" value=\"Submit\"></form>" baseURL:[NSURL URLWithString:@"http://webkit.org"]];
+    TestWebKitAPI::Util::run(&finishedNavigation);
+
+    [TestProtocol registerWithScheme:@"http"];
+    decidedPolicy = false;
+    [webView evaluateJavaScript:@"document.forms[0].submit()" completionHandler:nil];
+    TestWebKitAPI::Util::run(&decidedPolicy);
+
+    EXPECT_EQ(WKNavigationTypeFormSubmitted, [action navigationType]);
+    EXPECT_TRUE([action sourceFrame] == [action targetFrame]);
+    EXPECT_WK_STREQ("POST", [[action request] HTTPMethod]);
+    EXPECT_WK_STREQ("http://redirect/?result", [[[action request] URL] absoluteString]);
+    EXPECT_EQ(webView.get(), [[action sourceFrame] webView]);
+    EXPECT_WK_STREQ("http", [[[action sourceFrame] securityOrigin] protocol]);
+    EXPECT_WK_STREQ("webkit.org", [[[action sourceFrame] securityOrigin] host]);
+    EXPECT_FALSE([action _isRedirect]);
+
+    // Wait to decide policy for redirect.
+    decidedPolicy = false;
+    TestWebKitAPI::Util::run(&decidedPolicy);
+
+    EXPECT_EQ(WKNavigationTypeFormSubmitted, [action navigationType]);
+    EXPECT_TRUE([action sourceFrame] == [action targetFrame]);
+    EXPECT_WK_STREQ("GET", [[action request] HTTPMethod]);
+    EXPECT_WK_STREQ("http://result/", [[[action request] URL] absoluteString]);
+    EXPECT_EQ(webView.get(), [[action sourceFrame] webView]);
+    EXPECT_WK_STREQ("http", [[[action sourceFrame] securityOrigin] protocol]);
+    EXPECT_WK_STREQ("webkit.org", [[[action sourceFrame] securityOrigin] host]);
+    EXPECT_TRUE([action _isRedirect]);
+
+    [TestProtocol unregister];
+    newWebView = nullptr;
+    action = nullptr;
+}
+
+TEST(WebKit2, DecidePolicyForNavigationActionForPOSTFormSubmissionThatRedirectsToPOST)
+{
+    auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+
+    auto window = adoptNS([[NSWindow alloc] initWithContentRect:[webView frame] styleMask:NSWindowStyleMaskBorderless backing:NSBackingStoreBuffered defer:YES]);
+    [[window contentView] addSubview:webView.get()];
+
+    auto controller = adoptNS([[DecidePolicyForNavigationActionController alloc] init]);
+    [webView setNavigationDelegate:controller.get()];
+    [webView setUIDelegate:controller.get()];
+
+    finishedNavigation = false;
+    [webView loadHTMLString:@"<form action=\"http://307-redirect/?result\" method=\"POST\"><input type=\"submit\" name=\"submitButton\" value=\"Submit\"></form>" baseURL:[NSURL URLWithString:@"http://webkit.org"]];
+    TestWebKitAPI::Util::run(&finishedNavigation);
+
+    [TestProtocol registerWithScheme:@"http"];
+    decidedPolicy = false;
+    [webView evaluateJavaScript:@"document.forms[0].submit()" completionHandler:nil];
+    TestWebKitAPI::Util::run(&decidedPolicy);
+
+    EXPECT_EQ(WKNavigationTypeFormSubmitted, [action navigationType]);
+    EXPECT_TRUE([action sourceFrame] == [action targetFrame]);
+    EXPECT_WK_STREQ("POST", [[action request] HTTPMethod]);
+    EXPECT_WK_STREQ("http://307-redirect/?result", [[[action request] URL] absoluteString]);
+    EXPECT_EQ(webView.get(), [[action sourceFrame] webView]);
+    EXPECT_WK_STREQ("http", [[[action sourceFrame] securityOrigin] protocol]);
+    EXPECT_WK_STREQ("webkit.org", [[[action sourceFrame] securityOrigin] host]);
+    EXPECT_FALSE([action _isRedirect]);
+
+    // Wait to decide policy for redirect.
+    decidedPolicy = false;
+    TestWebKitAPI::Util::run(&decidedPolicy);
+
+    EXPECT_EQ(WKNavigationTypeFormSubmitted, [action navigationType]);
+    EXPECT_TRUE([action sourceFrame] == [action targetFrame]);
+    EXPECT_WK_STREQ("POST", [[action request] HTTPMethod]);
+    EXPECT_WK_STREQ("http://result/", [[[action request] URL] absoluteString]);
+    EXPECT_EQ(webView.get(), [[action sourceFrame] webView]);
+    EXPECT_WK_STREQ("http", [[[action sourceFrame] securityOrigin] protocol]);
+    EXPECT_WK_STREQ("webkit.org", [[[action sourceFrame] securityOrigin] host]);
+    EXPECT_TRUE([action _isRedirect]);
+
+    [TestProtocol unregister];
+    newWebView = nullptr;
+    action = nullptr;
+}
+
 #endif
 
 #endif
index 30dd2b6..be18e07 100644 (file)
@@ -72,17 +72,29 @@ static NSString *testScheme;
     testScheme = nil;
 }
 
+static NSURL *createRedirectURL(NSString *query)
+{
+    return [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", testScheme, query]];
+}
+
 - (void)startLoading
 {
     NSURL *requestURL = self.request.URL;
     EXPECT_TRUE([requestURL.scheme isEqualToString:testScheme]);
 
+    if ([requestURL.host isEqualToString:@"307-redirect"]) {
+        RetainPtr<NSHTTPURLResponse> response = adoptNS([[NSHTTPURLResponse alloc] initWithURL:requestURL statusCode:307 HTTPVersion:@"HTTP/1.1" headerFields:@{@"Content-Type" : @"text/html"}]);
+        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:createRedirectURL(requestURL.query)];
+        request.HTTPMethod = self.request.HTTPMethod;
+        [self.client URLProtocol:self wasRedirectedToRequest:request redirectResponse:response.get()];
+        return;
+    }
+
     NSData *data = [@"PASS" dataUsingEncoding:NSASCIIStringEncoding];
     RetainPtr<NSURLResponse> response = adoptNS([[NSURLResponse alloc] initWithURL:requestURL MIMEType:@"text/html" expectedContentLength:data.length textEncodingName:nil]);
 
     if ([requestURL.host isEqualToString:@"redirect"]) {
-        NSURL *redirectURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", testScheme, requestURL.query]];
-        [self.client URLProtocol:self wasRedirectedToRequest:[NSURLRequest requestWithURL:redirectURL] redirectResponse:response.get()];
+        [self.client URLProtocol:self wasRedirectedToRequest:[NSURLRequest requestWithURL:createRedirectURL(requestURL.query)] redirectResponse:response.get()];
         return;
     }