Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / ios / chrome / browser / signin / gaia_auth_fetcher_ios.mm
blob607b5b58b020bb810932024ff596c33720fd6d37
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ios/chrome/browser/signin/gaia_auth_fetcher_ios.h"
7 #import <WebKit/WebKit.h>
9 #include "base/logging.h"
10 #import "base/mac/foundation_util.h"
11 #include "base/mac/scoped_block.h"
12 #import "base/mac/scoped_nsobject.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "ios/chrome/browser/experimental_flags.h"
15 #include "ios/chrome/browser/signin/gaia_auth_fetcher_ios_private.h"
16 #include "ios/web/public/browser_state.h"
17 #import "ios/web/public/web_view_creation_util.h"
18 #include "net/base/load_flags.h"
19 #import "net/base/mac/url_conversions.h"
20 #include "net/base/net_errors.h"
21 #include "net/http/http_request_headers.h"
22 #include "net/url_request/url_request_status.h"
24 namespace {
26 // Whether the iOS specialization of the GaiaAuthFetcher should be used.
27 // TODO(bzanotti): Having this be false is a temporary solution to
28 // crbug.com/521521.
29 bool g_should_use_gaia_auth_fetcher_ios = false;
31 // Creates an NSURLRequest to |url| that can be loaded by a WebView from |body|
32 // and |headers|.
33 // The request is a GET if |body| is empty and a POST otherwise.
34 NSURLRequest* GetRequest(const std::string& body,
35                          const std::string& headers,
36                          const GURL& url) {
37   base::scoped_nsobject<NSMutableURLRequest> request(
38       [[NSMutableURLRequest alloc] initWithURL:net::NSURLWithGURL(url)]);
39   net::HttpRequestHeaders request_headers;
40   request_headers.AddHeadersFromString(headers);
41   for (net::HttpRequestHeaders::Iterator it(request_headers); it.GetNext();) {
42     [request setValue:base::SysUTF8ToNSString(it.value())
43         forHTTPHeaderField:base::SysUTF8ToNSString(it.name())];
44   }
45   if (!body.empty()) {
46     // TODO(bzanotti): HTTPBody is currently ignored in POST Request on
47     // WKWebView. Add a workaround here.
48     NSData* post_data =
49         [base::SysUTF8ToNSString(body) dataUsingEncoding:NSUTF8StringEncoding];
50     [request setHTTPBody:post_data];
51     [request setHTTPMethod:@"POST"];
52     DCHECK(![[request allHTTPHeaderFields] objectForKey:@"Content-Type"]);
53     [request setValue:@"application/x-www-form-urlencoded"
54         forHTTPHeaderField:@"Content-Type"];
55   }
56   return request.autorelease();
60 #pragma mark - GaiaAuthFetcherNavigationDelegate
62 @implementation GaiaAuthFetcherNavigationDelegate {
63   GaiaAuthFetcherIOSBridge* bridge_;  // weak
66 - (instancetype)initWithBridge:(GaiaAuthFetcherIOSBridge*)bridge {
67   self = [super init];
68   if (self) {
69     bridge_ = bridge;
70   }
71   return self;
74 #pragma mark WKNavigationDelegate
76 - (void)webView:(WKWebView*)webView
77     didFailNavigation:(WKNavigation*)navigation
78             withError:(NSError*)error {
79   DVLOG(1) << "Gaia fetcher navigation failed: "
80            << base::SysNSStringToUTF8(error.localizedDescription);
81   bridge_->URLFetchFailure(false /* is_cancelled */);
84 - (void)webView:(WKWebView*)webView
85     didFailProvisionalNavigation:(WKNavigation*)navigation
86                        withError:(NSError*)error {
87   DVLOG(1) << "Gaia fetcher provisional navigation failed: "
88            << base::SysNSStringToUTF8(error.localizedDescription);
89   bridge_->URLFetchFailure(false /* is_cancelled */);
92 - (void)webView:(WKWebView*)webView
93     didFinishNavigation:(WKNavigation*)navigation {
94   // A WKNavigation is an opaque object. The only way to access the body of the
95   // response is via Javascript.
96   [webView evaluateJavaScript:@"document.body.innerText"
97             completionHandler:^(id result, NSError* error) {
98               NSString* data = base::mac::ObjCCast<NSString>(result);
99               if (error || !data) {
100                 DVLOG(1) << "Gaia fetcher extract body failed:"
101                          << base::SysNSStringToUTF8(error.localizedDescription);
102                 bridge_->URLFetchFailure(false /* is_cancelled */);
103               } else {
104                 bridge_->URLFetchSuccess(base::SysNSStringToUTF8(data));
105               }
106             }];
109 @end
111 #pragma mark - GaiaAuthFetcherIOSBridge
113 GaiaAuthFetcherIOSBridge::GaiaAuthFetcherIOSBridge(
114     GaiaAuthFetcherIOS* fetcher,
115     web::BrowserState* browser_state)
116     : fetch_pending_(false),
117       fetcher_(fetcher),
118       url_(),
119       navigation_delegate_(
120           [[GaiaAuthFetcherNavigationDelegate alloc] initWithBridge:this]),
121       web_view_(web::CreateWKWebView(CGRectZero, browser_state)) {
122   [web_view_ setNavigationDelegate:navigation_delegate_];
125 GaiaAuthFetcherIOSBridge::~GaiaAuthFetcherIOSBridge() {
126   [web_view_ setNavigationDelegate:nil];
127   [web_view_ stopLoading];
128   web_view_.reset();
129   navigation_delegate_.reset();
132 void GaiaAuthFetcherIOSBridge::Fetch(const GURL& url,
133                                      const std::string& headers,
134                                      const std::string& body) {
135   fetch_pending_ = true;
136   url_ = url;
137   [web_view_ loadRequest:GetRequest(body, headers, url)];
140 void GaiaAuthFetcherIOSBridge::Cancel() {
141   if (!fetch_pending_) {
142     return;
143   }
144   [web_view_ stopLoading];
145   URLFetchFailure(true /* is_cancelled */);
148 void GaiaAuthFetcherIOSBridge::URLFetchSuccess(const std::string& data) {
149   if (!fetch_pending_) {
150     return;
151   }
152   fetch_pending_ = false;
153   // WKWebViewNavigationDelegate API doesn't give any way to get the HTTP
154   // response code of a navigation. Default to 200 for success.
155   fetcher_->FetchComplete(url_, data, net::ResponseCookies(),
156                           net::URLRequestStatus(), 200);
159 void GaiaAuthFetcherIOSBridge::URLFetchFailure(bool is_cancelled) {
160   if (!fetch_pending_) {
161     return;
162   }
163   fetch_pending_ = false;
164   // WKWebViewNavigationDelegate API doesn't give any way to get the HTTP
165   // response code of a navigation. Default to 500 for error.
166   int error = is_cancelled ? net::ERR_ABORTED : net::ERR_FAILED;
167   fetcher_->FetchComplete(url_, std::string(), net::ResponseCookies(),
168                           net::URLRequestStatus::FromError(error), 500);
171 #pragma mark - GaiaAuthFetcherIOS definition
173 GaiaAuthFetcherIOS::GaiaAuthFetcherIOS(GaiaAuthConsumer* consumer,
174                                        const std::string& source,
175                                        net::URLRequestContextGetter* getter,
176                                        web::BrowserState* browser_state)
177     : GaiaAuthFetcher(consumer, source, getter),
178       bridge_(),
179       browser_state_(browser_state) {
182 GaiaAuthFetcherIOS::~GaiaAuthFetcherIOS() {
185 void GaiaAuthFetcherIOS::CreateAndStartGaiaFetcher(const std::string& body,
186                                                    const std::string& headers,
187                                                    const GURL& gaia_gurl,
188                                                    int load_flags) {
189   DCHECK(!HasPendingFetch()) << "Tried to fetch two things at once!";
191   bool cookies_required = !(load_flags & (net::LOAD_DO_NOT_SEND_COOKIES |
192                                           net::LOAD_DO_NOT_SAVE_COOKIES));
193   if (!ShouldUseGaiaAuthFetcherIOS() || !cookies_required) {
194     GaiaAuthFetcher::CreateAndStartGaiaFetcher(body, headers, gaia_gurl,
195                                                load_flags);
196     return;
197   }
199   DVLOG(2) << "Gaia fetcher URL: " << gaia_gurl.spec();
200   DVLOG(2) << "Gaia fetcher headers: " << headers;
201   DVLOG(2) << "Gaia fetcher body: " << body;
203   // The fetch requires cookies and WKWebView is being used. The only way to do
204   // a network request with cookies sent and saved is by making it through a
205   // WKWebView.
206   SetPendingFetch(true);
207   if (!bridge_) {
208     bridge_.reset(new GaiaAuthFetcherIOSBridge(this, browser_state_));
209   }
210   bridge_->Fetch(gaia_gurl, headers, body);
213 void GaiaAuthFetcherIOS::CancelRequest() {
214   if (!HasPendingFetch()) {
215     return;
216   }
217   if (bridge_) {
218     bridge_->Cancel();
219   }
220   GaiaAuthFetcher::CancelRequest();
223 void GaiaAuthFetcherIOS::FetchComplete(const GURL& url,
224                                        const std::string& data,
225                                        const net::ResponseCookies& cookies,
226                                        const net::URLRequestStatus& status,
227                                        int response_code) {
228   DVLOG(2) << "Response " << url.spec() << ", code = " << response_code << "\n";
229   DVLOG(2) << "data: " << data << "\n";
230   SetPendingFetch(false);
231   DispatchFetchedRequest(url, data, cookies, status, response_code);
234 void GaiaAuthFetcherIOS::SetShouldUseGaiaAuthFetcherIOSForTesting(
235     bool use_gaia_fetcher_ios) {
236   // TODO(bzanotti): Commenting the below line is a temporary solution to
237   // crbug.com/521521.
238   // g_should_use_gaia_auth_fetcher_ios = use_gaia_fetcher_ios;
241 bool GaiaAuthFetcherIOS::ShouldUseGaiaAuthFetcherIOS() {
242   return experimental_flags::IsWKWebViewEnabled() &&
243          g_should_use_gaia_auth_fetcher_ios;