Commit d31a9a16 authored by Mihai Sardarescu's avatar Mihai Sardarescu Committed by Commit Bot

Remove kUseNSURLSessionForGaiaSigninRequests flag.

This CL is the first CL that removes the feature flag
kUseNSURLSessionForGaiaSigninRequests as the feature is enabled by default.
A follow-up CL will also merge GaiaAuthFetcherIOSNSURLSessionBridge in
GaiaAuthFetcherIOSBridge.

Bug: 1101748
Change-Id: Id8ca73ca198bf3771aefabf157edf8229a84eb27
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2366802Reviewed-by: default avatarNohemi Fernandez <fernandex@chromium.org>
Reviewed-by: default avatarDavid Roger <droger@chromium.org>
Commit-Queue: David Roger <droger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800964}
parent a4df6cf8
......@@ -101,7 +101,6 @@ source_set("browser") {
"//ios/chrome/browser/complex_tasks",
"//ios/chrome/browser/download",
"//ios/chrome/browser/itunes_urls",
"//ios/chrome/browser/signin:feature_flags",
"//ios/chrome/browser/ssl:feature_flags",
"//ios/chrome/browser/sync/glue",
"//ios/chrome/browser/ui:feature_flags",
......
......@@ -28,8 +28,6 @@ source_set("signin") {
"gaia_auth_fetcher_ios_bridge.mm",
"gaia_auth_fetcher_ios_ns_url_session_bridge.h",
"gaia_auth_fetcher_ios_ns_url_session_bridge.mm",
"gaia_auth_fetcher_ios_wk_webview_bridge.h",
"gaia_auth_fetcher_ios_wk_webview_bridge.mm",
"identity_manager_factory.cc",
"identity_manager_factory.h",
"identity_manager_factory_observer.h",
......@@ -49,7 +47,6 @@ source_set("signin") {
"signin_util.mm",
]
deps = [
":feature_flags",
"//base",
"//components/browser_sync",
"//components/content_settings/core/browser",
......@@ -86,15 +83,6 @@ source_set("signin") {
]
}
source_set("feature_flags") {
configs += [ "//build/config/compiler:enable_arc" ]
sources = [
"feature_flags.h",
"feature_flags.mm",
]
deps = [ "//base" ]
}
source_set("test_support") {
configs += [ "//build/config/compiler:enable_arc" ]
testonly = true
......@@ -132,7 +120,6 @@ source_set("unit_tests") {
"signin_browser_state_info_updater_unittest.mm",
]
deps = [
":feature_flags",
":signin",
":test_support",
"//base",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_SIGNIN_FEATURE_FLAGS_H_
#define IOS_CHROME_BROWSER_SIGNIN_FEATURE_FLAGS_H_
#include "base/feature_list.h"
// Feature flag to enable NSURLSession for GAIAAuthFetcherIOS.
extern const base::Feature kUseNSURLSessionForGaiaSigninRequests;
#endif // IOS_CHROME_BROWSER_SIGNIN_FEATURE_FLAGS_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ios/chrome/browser/signin/feature_flags.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
const base::Feature kUseNSURLSessionForGaiaSigninRequests{
"UseNSURLSessionForGaiaSigninRequests", base::FEATURE_ENABLED_BY_DEFAULT};
......@@ -72,8 +72,8 @@ class GaiaAuthFetcherIOS
net::Error net_error,
int response_code) override;
std::unique_ptr<GaiaAuthFetcherIOSBridge> bridge_;
web::BrowserState* browser_state_;
std::unique_ptr<GaiaAuthFetcherIOSBridge> bridge_;
DISALLOW_COPY_AND_ASSIGN(GaiaAuthFetcherIOS);
};
......
......@@ -8,10 +8,7 @@
#include "base/logging.h"
#import "base/mac/foundation_util.h"
#include "ios/chrome/browser/signin/feature_flags.h"
#import "ios/chrome/browser/signin/gaia_auth_fetcher_ios_ns_url_session_bridge.h"
#include "ios/chrome/browser/signin/gaia_auth_fetcher_ios_wk_webview_bridge.h"
#include "ios/web/common/features.h"
#include "ios/web/public/browser_state.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
......@@ -32,14 +29,8 @@ GaiaAuthFetcherIOS::GaiaAuthFetcherIOS(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
web::BrowserState* browser_state)
: GaiaAuthFetcher(consumer, source, url_loader_factory),
browser_state_(browser_state) {
if (base::FeatureList::IsEnabled(kUseNSURLSessionForGaiaSigninRequests)) {
bridge_.reset(
new GaiaAuthFetcherIOSNSURLSessionBridge(this, browser_state));
} else {
bridge_.reset(new GaiaAuthFetcherIOSWKWebViewBridge(this, browser_state));
}
}
browser_state_(browser_state),
bridge_(new GaiaAuthFetcherIOSNSURLSessionBridge(this, browser_state_)) {}
GaiaAuthFetcherIOS::~GaiaAuthFetcherIOS() {}
......
......@@ -18,9 +18,7 @@ class GaiaAuthFetcherIOSNSURLSessionBridge;
@class NSURLSession;
// Specialization of GaiaAuthFetcher on iOS, using NSURLSession to send
// requests. This class can only be used when those 2 flags are enabbled:
// + kUseNSURLSessionForGaiaSigninRequests
// + web::features::kWKHTTPSystemCookieStore
// requests.
class GaiaAuthFetcherIOSNSURLSessionBridge : public GaiaAuthFetcherIOSBridge {
public:
GaiaAuthFetcherIOSNSURLSessionBridge(
......
......@@ -11,9 +11,7 @@
#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"
#include "components/signin/ios/browser/account_consistency_service.h"
#include "ios/chrome/browser/signin/feature_flags.h"
#include "ios/net/cookies/system_cookie_util.h"
#include "ios/web/common/features.h"
#include "ios/web/public/browser_state.h"
#import "net/base/mac/url_conversions.h"
......@@ -83,7 +81,6 @@ GaiaAuthFetcherIOSNSURLSessionBridge::GaiaAuthFetcherIOSNSURLSessionBridge(
GaiaAuthFetcherIOSBridge::GaiaAuthFetcherIOSBridgeDelegate* delegate,
web::BrowserState* browser_state)
: GaiaAuthFetcherIOSBridge(delegate, browser_state) {
DCHECK(base::FeatureList::IsEnabled(kUseNSURLSessionForGaiaSigninRequests));
url_session_delegate_ = [[GaiaAuthFetcherIOSURLSessionDelegate alloc] init];
url_session_delegate_.bridge = this;
}
......
......@@ -12,9 +12,7 @@
#include "base/strings/sys_string_conversions.h"
#include "base/test/bind_test_util.h"
#import "base/test/ios/wait_util.h"
#include "base/test/scoped_feature_list.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#include "ios/chrome/browser/signin/feature_flags.h"
#include "ios/chrome/browser/signin/gaia_auth_fetcher_ios_bridge.h"
#import "ios/chrome/browser/web/chrome_web_test.h"
#include "ios/net/cookies/system_cookie_util.h"
......@@ -161,9 +159,6 @@ class GaiaAuthFetcherIOSNSURLSessionBridgeTest : public ChromeWebTest {
friend TestGaiaAuthFetcherIOSNSURLSessionBridge;
// kWKHTTPSystemCookieStore and kUseNSURLSessionForGaiaSigninRequests should
// be enabled.
base::test::ScopedFeatureList scoped_feature_list;
// Browser state for the tests.
std::unique_ptr<ChromeBrowserState> browser_state_;
// Instance used for the tests.
......@@ -201,10 +196,6 @@ NSURLSession* TestGaiaAuthFetcherIOSNSURLSessionBridge::CreateNSURLSession(
#pragma mark - GaiaAuthFetcherIOSNSURLSessionBridgeTest
void GaiaAuthFetcherIOSNSURLSessionBridgeTest::SetUp() {
std::vector<base::Feature> enabled_features;
std::vector<base::Feature> disabled_features;
enabled_features.push_back(kUseNSURLSessionForGaiaSigninRequests);
scoped_feature_list.InitWithFeatures(enabled_features, disabled_features);
delegate_.reset(new FakeGaiaAuthFetcherIOSBridgeDelegate());
browser_state_ = TestChromeBrowserState::Builder().Build();
ns_url_session_bridge_.reset(new TestGaiaAuthFetcherIOSNSURLSessionBridge(
......
......@@ -4,16 +4,12 @@
#include "ios/chrome/browser/signin/gaia_auth_fetcher_ios.h"
#import <WebKit/WebKit.h>
#include <memory>
#include "base/ios/ios_util.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
#include "ios/chrome/browser/browser_state/test_chrome_browser_state.h"
#import "ios/chrome/browser/signin/gaia_auth_fetcher_ios_wk_webview_bridge.h"
#include "ios/web/public/test/web_task_environment.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
......@@ -21,8 +17,6 @@
#include "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/gtest_support.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
......@@ -30,23 +24,15 @@
namespace {
class FakeGaiaAuthFetcherIOSWKWebViewBridge
: public GaiaAuthFetcherIOSWKWebViewBridge {
class MockGaiaAuthFetcherIOSBridge : public GaiaAuthFetcherIOSBridge {
public:
FakeGaiaAuthFetcherIOSWKWebViewBridge(
MockGaiaAuthFetcherIOSBridge(
GaiaAuthFetcherIOSBridge::GaiaAuthFetcherIOSBridgeDelegate* delegate,
web::BrowserState* browser_state)
: GaiaAuthFetcherIOSWKWebViewBridge(delegate, browser_state),
mock_web_view_(nil) {}
private:
WKWebView* BuildWKWebView() override {
if (!mock_web_view_) {
mock_web_view_ = [OCMockObject niceMockForClass:[WKWebView class]];
}
return mock_web_view_;
}
id mock_web_view_;
: GaiaAuthFetcherIOSBridge(delegate, browser_state) {}
MOCK_METHOD0(Cancel, void());
MOCK_METHOD0(FetchPendingRequest, void());
};
class MockGaiaConsumer : public GaiaAuthConsumer {
......@@ -67,34 +53,23 @@ class GaiaAuthFetcherIOSTest : public PlatformTest {
GaiaAuthFetcherIOSTest() {
browser_state_ = TestChromeBrowserState::Builder().Build();
ActiveStateManager::FromBrowserState(browser_state())->SetActive(true);
gaia_auth_fetcher_.reset(new GaiaAuthFetcherIOS(
&consumer_, gaia::GaiaSource::kChrome,
test_url_loader_factory_.GetSafeWeakWrapper(), browser_state()));
gaia_auth_fetcher_->bridge_.reset(new FakeGaiaAuthFetcherIOSWKWebViewBridge(
gaia_auth_fetcher_.get(), browser_state()));
test_url_loader_factory_.GetSafeWeakWrapper(), browser_state_.get()));
gaia_auth_fetcher_->bridge_.reset(new MockGaiaAuthFetcherIOSBridge(
gaia_auth_fetcher_.get(), browser_state_.get()));
}
~GaiaAuthFetcherIOSTest() override {
gaia_auth_fetcher_.reset();
ActiveStateManager::FromBrowserState(browser_state())->SetActive(false);
}
GaiaAuthFetcherIOSBridge* GetBridge() {
return gaia_auth_fetcher_->bridge_.get();
}
ChromeBrowserState* browser_state() { return browser_state_.get(); }
id GetMockWKWebView() {
GaiaAuthFetcherIOSWKWebViewBridge* wkWebviewBridge =
reinterpret_cast<GaiaAuthFetcherIOSWKWebViewBridge*>(
gaia_auth_fetcher_->bridge_.get());
return wkWebviewBridge->GetWKWebView();
MockGaiaAuthFetcherIOSBridge* GetBridge() {
return static_cast<MockGaiaAuthFetcherIOSBridge*>(
gaia_auth_fetcher_->bridge_.get());
}
web::WebTaskEnvironment task_environment_;
// BrowserState, required for WKWebView creation.
std::unique_ptr<ChromeBrowserState> browser_state_;
MockGaiaConsumer consumer_;
network::TestURLLoaderFactory test_url_loader_factory_;
......@@ -104,46 +79,45 @@ class GaiaAuthFetcherIOSTest : public PlatformTest {
// Tests that the cancel mechanism works properly by cancelling an OAuthLogin
// request and controlling that the consumer is properly called.
TEST_F(GaiaAuthFetcherIOSTest, StartOAuthLoginCancelled) {
GoogleServiceAuthError expected_error =
GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
EXPECT_CALL(consumer_, OnClientLoginFailure(expected_error)).Times(1);
[static_cast<WKWebView*>([GetMockWKWebView() expect])
loadRequest:[OCMArg any]];
[[GetMockWKWebView() expect] stopLoading];
MockGaiaAuthFetcherIOSBridge* bridge = GetBridge();
EXPECT_CALL(*bridge, FetchPendingRequest());
gaia_auth_fetcher_->StartOAuthLogin("fake_token", "gaia");
GoogleServiceAuthError expected_error =
GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
EXPECT_CALL(consumer_, OnClientLoginFailure(expected_error));
EXPECT_CALL(*bridge, Cancel()).WillOnce([&bridge]() {
bridge->OnURLFetchFailure(net::ERR_ABORTED, 0);
});
gaia_auth_fetcher_->CancelRequest();
EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
}
// Tests that the successful case works properly by starting a MergeSession
// request, making it succeed and controlling that the consumer is properly
// called.
TEST_F(GaiaAuthFetcherIOSTest, StartMergeSession) {
EXPECT_CALL(consumer_, OnMergeSessionSuccess("data")).Times(1);
[static_cast<WKWebView*>([[GetMockWKWebView() expect] andDo:^(NSInvocation*) {
GetBridge()->OnURLFetchSuccess("data", 200);
}]) loadRequest:[OCMArg any]];
MockGaiaAuthFetcherIOSBridge* bridge = GetBridge();
EXPECT_CALL(*bridge, FetchPendingRequest()).WillOnce([&bridge]() {
bridge->OnURLFetchSuccess("data", 200);
});
EXPECT_CALL(consumer_, OnMergeSessionSuccess("data"));
gaia_auth_fetcher_->StartMergeSession("uber_token", "");
EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
}
// Tests that the failure case works properly by starting a LogOut request,
// making it fail, and controlling that the consumer is properly called.
TEST_F(GaiaAuthFetcherIOSTest, StartLogOutError) {
MockGaiaAuthFetcherIOSBridge* bridge = GetBridge();
GoogleServiceAuthError expected_error =
GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED);
EXPECT_CALL(consumer_, OnLogOutFailure(expected_error)).Times(1);
[static_cast<WKWebView*>([[GetMockWKWebView() expect] andDo:^(NSInvocation*) {
GetBridge()->OnURLFetchFailure(net::ERR_FAILED, 500);
}]) loadRequest:[OCMArg any]];
EXPECT_CALL(consumer_, OnLogOutFailure(expected_error));
EXPECT_CALL(*bridge, FetchPendingRequest()).WillOnce([&bridge]() {
bridge->OnURLFetchFailure(net::ERR_FAILED, 500);
});
gaia_auth_fetcher_->StartLogOut();
EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
}
// Tests that requests that do not require cookies are using the original
......@@ -163,48 +137,3 @@ TEST_F(GaiaAuthFetcherIOSTest, StartGetCheckConnectionInfo) {
gaia_auth_fetcher_->StartGetCheckConnectionInfo();
base::RunLoop().RunUntilIdle();
}
// Tests whether the WKWebView is actually stopped when the browser state is
// inactive.
TEST_F(GaiaAuthFetcherIOSTest, OnInactive) {
[[GetMockWKWebView() expect] stopLoading];
ActiveStateManager::FromBrowserState(browser_state())->SetActive(false);
EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
}
// Tests that the pending request is processed when the browser state becomes
// active.
TEST_F(GaiaAuthFetcherIOSTest, FetchOnActive) {
EXPECT_CALL(consumer_, OnMergeSessionSuccess("data")).Times(1);
// No action is made until the browser state is active, then a WKWebView and
// its navigation delegate are created, and the request is processed.
[[GetMockWKWebView() expect] setNavigationDelegate:[OCMArg isNotNil]];
[static_cast<WKWebView*>([[GetMockWKWebView() expect] andDo:^(NSInvocation*) {
GetBridge()->OnURLFetchSuccess("data", 200);
}]) loadRequest:[OCMArg any]];
ActiveStateManager::FromBrowserState(browser_state())->SetActive(false);
gaia_auth_fetcher_->StartMergeSession("uber_token", "");
ActiveStateManager::FromBrowserState(browser_state())->SetActive(true);
EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
}
// Tests that the pending request is stopped when the browser state becomes
// inactive and restarted when it becomes active again.
TEST_F(GaiaAuthFetcherIOSTest, StopOnInactiveReFetchOnActive) {
EXPECT_CALL(consumer_, OnMergeSessionSuccess("data")).Times(1);
[static_cast<WKWebView*>([GetMockWKWebView() expect])
loadRequest:[OCMArg any]];
[[GetMockWKWebView() expect] setNavigationDelegate:[OCMArg isNil]];
[[GetMockWKWebView() expect] setNavigationDelegate:[OCMArg isNotNil]];
[static_cast<WKWebView*>([[GetMockWKWebView() expect] andDo:^(NSInvocation*) {
GetBridge()->OnURLFetchSuccess("data", 200);
}]) loadRequest:[OCMArg any]];
gaia_auth_fetcher_->StartMergeSession("uber_token", "");
ActiveStateManager::FromBrowserState(browser_state())->SetActive(false);
ActiveStateManager::FromBrowserState(browser_state())->SetActive(true);
EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_CHROME_BROWSER_SIGNIN_GAIA_AUTH_FETCHER_IOS_WK_WEBVIEW_BRIDGE_H_
#define IOS_CHROME_BROWSER_SIGNIN_GAIA_AUTH_FETCHER_IOS_WK_WEBVIEW_BRIDGE_H_
#import <Foundation/Foundation.h>
#include "components/signin/ios/browser/active_state_manager.h"
#include "ios/chrome/browser/signin/gaia_auth_fetcher_ios_bridge.h"
@class GaiaAuthFetcherNavigationDelegate;
@class NSHTTPCookie;
@class WKWebView;
// Specialization of GaiaAuthFetcher on iOS, using WKWebView to send requests.
class GaiaAuthFetcherIOSWKWebViewBridge : public GaiaAuthFetcherIOSBridge,
ActiveStateManager::Observer {
public:
GaiaAuthFetcherIOSWKWebViewBridge(
GaiaAuthFetcherIOSBridge::GaiaAuthFetcherIOSBridgeDelegate* delegate,
web::BrowserState* browser_state);
~GaiaAuthFetcherIOSWKWebViewBridge() override;
// GaiaAuthFetcherIOSBridge.
void Cancel() override;
protected:
friend class GaiaAuthFetcherIOSTest;
// GaiaAuthFetcherIOSBridge.
void FetchPendingRequest() override;
// Returns the cached WKWebView if it exists, or creates one if necessary.
// Can return nil if the browser state is not active.
WKWebView* GetWKWebView();
// Actually creates a WKWebView. Virtual for testing.
virtual WKWebView* BuildWKWebView();
// Stops any page loading in the WKWebView currently in use and releases it.
void ResetWKWebView();
// ActiveStateManager::Observer implementation.
void OnActive() override;
void OnInactive() override;
private:
// Navigation delegate of |web_view_| that informs the bridge of relevant
// navigation events.
GaiaAuthFetcherNavigationDelegate* navigation_delegate_;
// Web view used to do the network requests.
WKWebView* web_view_;
DISALLOW_COPY_AND_ASSIGN(GaiaAuthFetcherIOSWKWebViewBridge);
};
#endif // IOS_CHROME_BROWSER_SIGNIN_GAIA_AUTH_FETCHER_IOS_WK_WEBVIEW_BRIDGE_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/browser/signin/gaia_auth_fetcher_ios_wk_webview_bridge.h"
#import <Foundation/Foundation.h>
#import <WebKit/WebKit.h>
#include "base/json/string_escape.h"
#include "base/strings/sys_string_conversions.h"
#import "ios/web/common/web_view_creation_util.h"
#import "net/base/mac/url_conversions.h"
#include "net/http/http_request_headers.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// JavaScript template to do a POST request using an XMLHttpRequest.
// The request is retried once on failure, as it can be marked as failing to
// load the resource because of 302s on POST request (the cookies of the first
// response are correctly set).
//
// The template takes three arguments (in order):
// * The quoted and escaped URL to send a POST request to.
// * The HTTP headers of the request. They should be written as valid JavaScript
// statements, adding headers to the XMLHttpRequest variable named 'req'
// (e.g. 'req.setRequestHeader("Foo", "Bar");').
// * The quoted and escaped body of the POST request.
NSString* const kPostRequestTemplate =
@"<html><script>"
"function __gCrWebDoPostRequest() {"
" function createAndSendPostRequest() {"
" var req = new XMLHttpRequest();"
" req.open(\"POST\", %@, false);"
" req.setRequestHeader(\"Content-Type\","
"\"application/x-www-form-urlencoded\");"
"%@"
" req.send(%@);"
" if (req.status != 200) {"
" throw req.status;"
" }"
" return req.responseText;"
" }"
" try {"
" return createAndSendPostRequest();"
" } catch(err) {"
" return createAndSendPostRequest();"
" }"
"}"
"</script></html>";
// JavaScript template to read the response to a GET or POST request. There is
// two different cases:
// * GET request, which was made by simply loading a request to the correct
// URL. The response is the inner text (to avoid formatting in case of JSON
// answers) of the body.
// * POST request, in case the "__gCrWebDoPostRequest" function is defined.
// Running the function will do a POST request via a XMLHttpRequest and
// return the response. See DoPostRequest below to know why this is necessary.
NSString* const kReadResponseTemplate =
@"if (typeof __gCrWebDoPostRequest === 'function') {"
" __gCrWebDoPostRequest();"
"} else {"
" document.body.innerText;"
"}";
// Escapes and quotes |value| and converts the result to an NSString.
NSString* EscapeAndQuoteToNSString(const std::string& value) {
return base::SysUTF8ToNSString(base::GetQuotedJSONString(value));
}
// Simulates a POST request on |web_view| using a XMLHttpRequest in
// JavaScript.
// This is needed because WKWebView ignores the HTTPBody in a POST request
// before iOS11 and because WKWebView cannot read response body if
// content-disposition header is set. See
// https://bugs.webkit.org/show_bug.cgi?id=145410
// TODO(crbug.com/889471) Remove this once requests are done using
// NSUrlSession in iOS.
void DoPostRequest(WKWebView* web_view,
const std::string& body,
const std::string& headers,
const GURL& url) {
NSMutableString* header_data = [NSMutableString string];
net::HttpRequestHeaders request_headers;
request_headers.AddHeadersFromString(headers);
for (net::HttpRequestHeaders::Iterator it(request_headers); it.GetNext();) {
if (it.name() == "Origin") {
// The Origin request header cannot be set on an XMLHttpRequest.
continue;
}
// net::HttpRequestHeaders escapes the name and value for a header. Some
// escaping might still be necessary for the JavaScript layer.
[header_data appendFormat:@"req.setRequestHeader(%@, %@);",
EscapeAndQuoteToNSString(it.name()),
EscapeAndQuoteToNSString(it.value())];
}
NSString* html_string =
[NSString stringWithFormat:kPostRequestTemplate,
EscapeAndQuoteToNSString(url.spec()),
header_data, EscapeAndQuoteToNSString(body)];
// |url| is used as the baseURL to avoid CORS issues.
[web_view loadHTMLString:html_string baseURL:net::NSURLWithGURL(url)];
}
} // namespace
#pragma mark - GaiaAuthFetcherNavigationDelegate
// Navigation delegate attached to a WKWebView used for URL fetches.
@interface GaiaAuthFetcherNavigationDelegate : NSObject <WKNavigationDelegate>
@property(nonatomic, assign) GaiaAuthFetcherIOSWKWebViewBridge* bridge;
@end
@implementation GaiaAuthFetcherNavigationDelegate
- (instancetype)initWithBridge:(GaiaAuthFetcherIOSWKWebViewBridge*)bridge {
self = [super init];
if (self) {
_bridge = bridge;
}
return self;
}
- (void)javascriptCompletionWithResult:(NSString*)result error:(NSError*)error {
if (error || !result) {
VLOG(1) << "Gaia fetcher extract body failed:"
<< base::SysNSStringToUTF8(error.localizedDescription);
// WKWebViewNavigationDelegate API doesn't give any way to get the HTTP
// response code of a navigation. Default to 500 for error.
self.bridge->OnURLFetchFailure(net::ERR_FAILED, 500);
} else {
DCHECK([result isKindOfClass:[NSString class]]);
// WKWebViewNavigationDelegate API doesn't give any way to get
// the HTTP response code of a navigation. Default to 200 for
// success.
self.bridge->OnURLFetchSuccess(base::SysNSStringToUTF8(result), 200);
}
}
#pragma mark WKNavigationDelegate
- (void)webView:(WKWebView*)webView
didFailNavigation:(WKNavigation*)navigation
withError:(NSError*)error {
VLOG(1) << "Gaia fetcher navigation failed: "
<< base::SysNSStringToUTF8(error.localizedDescription);
self.bridge->OnURLFetchFailure(net::ERR_FAILED, 500);
}
- (void)webView:(WKWebView*)webView
didFailProvisionalNavigation:(WKNavigation*)navigation
withError:(NSError*)error {
VLOG(1) << "Gaia fetcher provisional navigation failed: "
<< base::SysNSStringToUTF8(error.localizedDescription);
self.bridge->OnURLFetchFailure(net::ERR_FAILED, 500);
}
- (void)webView:(WKWebView*)webView
didFinishNavigation:(WKNavigation*)navigation {
// A WKNavigation is an opaque object. The only way to access the body of the
// response is via Javascript.
DVLOG(2) << "WKWebView loaded:" << net::GURLWithNSURL(webView.URL);
__weak __typeof(self) weakSelf = self;
[webView evaluateJavaScript:kReadResponseTemplate
completionHandler:^(NSString* result, NSError* error) {
[weakSelf javascriptCompletionWithResult:result error:error];
}];
}
@end
#pragma mark - GaiaAuthFetcherIOSWKWebViewBridge
GaiaAuthFetcherIOSWKWebViewBridge::GaiaAuthFetcherIOSWKWebViewBridge(
GaiaAuthFetcherIOSBridge::GaiaAuthFetcherIOSBridgeDelegate* delegate,
web::BrowserState* browser_state)
: GaiaAuthFetcherIOSBridge(delegate, browser_state) {
ActiveStateManager::FromBrowserState(GetBrowserState())->AddObserver(this);
}
GaiaAuthFetcherIOSWKWebViewBridge::~GaiaAuthFetcherIOSWKWebViewBridge() {
ActiveStateManager::FromBrowserState(GetBrowserState())->RemoveObserver(this);
ResetWKWebView();
}
void GaiaAuthFetcherIOSWKWebViewBridge::Cancel() {
if (!GetRequest().pending)
return;
[GetWKWebView() stopLoading];
VLOG(1) << "Fetch was cancelled";
OnURLFetchFailure(net::ERR_ABORTED, 500);
}
void GaiaAuthFetcherIOSWKWebViewBridge::FetchPendingRequest() {
if (!GetRequest().pending)
return;
if (!GetRequest().body.empty() && GetRequest().should_use_xml_http_request) {
DoPostRequest(GetWKWebView(), GetRequest().body, GetRequest().headers,
GetRequest().url);
} else {
[GetWKWebView() loadRequest:GetNSURLRequest()];
}
}
WKWebView* GaiaAuthFetcherIOSWKWebViewBridge::GetWKWebView() {
if (!ActiveStateManager::FromBrowserState(GetBrowserState())->IsActive()) {
// |GetBrowserState()| is not active, WKWebView linked to this browser state
// should not exist or be created.
return nil;
}
if (!web_view_) {
web_view_ = BuildWKWebView();
navigation_delegate_ =
[[GaiaAuthFetcherNavigationDelegate alloc] initWithBridge:this];
[web_view_ setNavigationDelegate:navigation_delegate_];
}
return web_view_;
}
void GaiaAuthFetcherIOSWKWebViewBridge::ResetWKWebView() {
[web_view_ setNavigationDelegate:nil];
[web_view_ stopLoading];
web_view_ = nil;
navigation_delegate_ = nil;
}
WKWebView* GaiaAuthFetcherIOSWKWebViewBridge::BuildWKWebView() {
return web::BuildWKWebViewForQueries(GetBrowserState());
}
void GaiaAuthFetcherIOSWKWebViewBridge::OnActive() {
// |GetBrowserState()| is now active. If there is a pending request, restart
// it.
FetchPendingRequest();
}
void GaiaAuthFetcherIOSWKWebViewBridge::OnInactive() {
// |GetBrowserState()| is now inactive. Stop using |web_view_| and don't
// create a new one until it is active.
ResetWKWebView();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment