Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(161)

Side by Side Diff: ios/chrome/browser/payments/payment_request_manager.mm

Issue 2585233003: Upstream Chrome on iOS source code [2/11]. (Closed)
Patch Set: Created 3 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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.
4
5 #import "ios/chrome/browser/payments/payment_request_manager.h"
6
7 #include "base/ios/ios_util.h"
8 #import "base/ios/weak_nsobject.h"
9 #import "base/mac/bind_objc_block.h"
10 #include "base/mac/foundation_util.h"
11 #include "base/mac/scoped_nsobject.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/strings/sys_string_conversions.h"
14 #import "base/values.h"
15 #include "components/autofill/core/browser/personal_data_manager.h"
16 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h"
17 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
18 #import "ios/chrome/browser/payments/js_payment_request_manager.h"
19 #import "ios/chrome/browser/payments/payment_request_coordinator.h"
20 #import "ios/chrome/browser/payments/payment_request_web_state_observer.h"
21 #include "ios/web/public/favicon_status.h"
22 #include "ios/web/public/navigation_item.h"
23 #include "ios/web/public/navigation_manager.h"
24 #include "ios/web/public/payments/payment_request.h"
25 #include "ios/web/public/ssl_status.h"
26 #import "ios/web/public/url_scheme_util.h"
27 #import "ios/web/public/web_state/crw_web_view_proxy.h"
28 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
29 #include "ios/web/public/web_state/url_verification_constants.h"
30 #include "ios/web/public/web_state/web_state.h"
31
32 namespace {
33 // Command prefix for injected JavaScript.
34 const std::string kCommandPrefix = "paymentRequest";
35 } // namespace
36
37 @interface PaymentRequestManager ()<PaymentRequestCoordinatorDelegate,
38 PaymentRequestWebStateDelegate> {
39 // View controller used to present the PaymentRequest view controller.
40 base::WeakNSObject<UIViewController> _baseViewController;
41
42 // PersonalDataManager used to manage user credit cards and addresses.
43 autofill::PersonalDataManager* _personalDataManager;
44
45 // WebState for the tab this object is attached to.
46 web::WebState* _webState;
47
48 // Observer for |_webState|.
49 std::unique_ptr<PaymentRequestWebStateObserver> _webStateObserver;
50
51 // Object that manages JavaScript injection into the web view.
52 base::WeakNSObject<JSPaymentRequestManager> _paymentRequestJsManager;
53
54 // Boolean to track if the current WebState is enabled (JS callback is set
55 // up).
56 BOOL _webStateEnabled;
57
58 // Boolean to track if the script has been injected in the current page. This
59 // is a faster check than asking the JS controller.
60 BOOL _isScriptInjected;
61
62 // True when close has been called and the PaymentRequest coordinator has
63 // been destroyed.
64 BOOL _closed;
65
66 // Coordinator used to create and present the PaymentRequest view controller.
67 base::scoped_nsobject<PaymentRequestCoordinator> _paymentRequestCoordinator;
68 }
69
70 // Synchronous method executed by -asynchronouslyEnablePaymentRequest:
71 - (void)doEnablePaymentRequest:(BOOL)enabled;
72
73 // Initialize the PaymentRequest JavaScript.
74 - (void)initializeWebViewForPaymentRequest;
75
76 // Handler for injected JavaScript callbacks.
77 - (BOOL)handleScriptCommand:(const base::DictionaryValue&)JSONCommand;
78
79 // Handles invocations of PaymentRequest.show(). The value of the JavaScript
80 // PaymentRequest object should be provided in |message|. Returns YES if the
81 // invocation was successful.
82 - (BOOL)handleRequestShow:(const base::DictionaryValue&)message;
83
84 // Handles invocations of PaymentResponse.complete(). Returns YES if the
85 // invocation was successful.
86 - (BOOL)handleResponseComplete;
87
88 @end
89
90 @implementation PaymentRequestManager
91
92 @synthesize enabled = _enabled;
93 @synthesize webState = _webState;
94
95 - (instancetype)initWithBaseViewController:(UIViewController*)viewController
96 browserState:
97 (ios::ChromeBrowserState*)browserState {
98 if ((self = [super init])) {
99 _baseViewController.reset(viewController);
100
101 _personalDataManager =
102 autofill::PersonalDataManagerFactory::GetForBrowserState(
103 browserState->GetOriginalChromeBrowserState());
104
105 // Set up the web state observer. This lasts as long as this object does,
106 // but it will observe and un-observe the web tabs as it changes over time.
107 _webStateObserver.reset(new PaymentRequestWebStateObserver(self));
108 }
109 return self;
110 }
111
112 - (instancetype)init {
113 NOTREACHED();
114 return nil;
115 }
116
117 - (void)setWebState:(web::WebState*)webState {
118 [self disconnectWebState];
119 if (webState) {
120 _paymentRequestJsManager.reset(
121 base::mac::ObjCCastStrict<JSPaymentRequestManager>(
122 [webState->GetJSInjectionReceiver()
123 instanceOfClass:[JSPaymentRequestManager class]]));
124 _webState = webState;
125 _webStateObserver->ObserveWebState(webState);
126 [self enableCurrentWebState];
127 } else {
128 _webState = nullptr;
129 }
130 }
131
132 - (void)enablePaymentRequest:(BOOL)enabled {
133 // Asynchronously enables PaymentRequest, so that some preferences
134 // (UIAccessibilityIsVoiceOverRunning(), for example) have time to synchronize
135 // with their own notifications.
136 base::WeakNSObject<PaymentRequestManager> weakSelf(self);
137 dispatch_async(dispatch_get_main_queue(), ^{
138 [weakSelf doEnablePaymentRequest:enabled];
139 });
140 }
141
142 - (void)doEnablePaymentRequest:(BOOL)enabled {
143 BOOL changing = _enabled != enabled;
144 if (changing) {
145 if (!enabled) {
146 [self dismissUI];
147 }
148 _enabled = enabled;
149 [self enableCurrentWebState];
150 }
151 }
152
153 - (void)cancelRequest {
154 [self dismissUI];
155 [_paymentRequestJsManager rejectRequestPromise:@"Request cancelled by user."
156 completionHandler:nil];
157 }
158
159 - (void)close {
160 if (_closed)
161 return;
162
163 _closed = YES;
164 [self disableCurrentWebState];
165 [self setWebState:nil];
166 [self dismissUI];
167 }
168
169 - (void)enableCurrentWebState {
170 if (![self webState]) {
171 return;
172 }
173
174 if (_enabled && [self webStateContentIsSecureHTML]) {
175 if (!_webStateEnabled) {
176 base::WeakNSObject<PaymentRequestManager> weakSelf(self);
177 auto callback =
178 base::BindBlock(^bool(const base::DictionaryValue& JSON,
179 const GURL& originURL, bool userIsInteracting) {
180 base::scoped_nsobject<PaymentRequestManager> strongSelf(
181 [weakSelf retain]);
182 // |originURL| and |userIsInteracting| aren't used.
183 return [strongSelf handleScriptCommand:JSON];
184 });
185 [self webState]->AddScriptCommandCallback(callback, kCommandPrefix);
186
187 _webStateEnabled = YES;
188 }
189
190 [self initializeWebViewForPaymentRequest];
191 } else {
192 [self disableCurrentWebState];
193 }
194 }
195
196 - (void)disableCurrentWebState {
197 if (_webStateEnabled) {
198 _webState->RemoveScriptCommandCallback(kCommandPrefix);
199 _webStateEnabled = NO;
200 }
201 }
202
203 - (void)disconnectWebState {
204 if (_webState) {
205 _paymentRequestJsManager.reset();
206 _webStateObserver->ObserveWebState(nullptr);
207 [self disableCurrentWebState];
208 }
209 }
210
211 - (void)initializeWebViewForPaymentRequest {
212 DCHECK(_webStateEnabled);
213
214 if (![self webStateContentIsSecureHTML]) {
215 return;
216 }
217
218 [_paymentRequestJsManager inject];
219 _isScriptInjected = YES;
220 }
221
222 - (BOOL)handleScriptCommand:(const base::DictionaryValue&)JSONCommand {
223 if (![self webStateContentIsSecureHTML]) {
224 return NO;
225 }
226
227 std::string command;
228 if (!JSONCommand.GetString("command", &command)) {
229 DLOG(ERROR) << "RECEIVED BAD JSON - NO 'command' field";
230 return NO;
231 }
232
233 if (command == "paymentRequest.requestShow") {
234 return [self handleRequestShow:JSONCommand];
235 }
236 if (command == "paymentRequest.responseComplete") {
237 return [self handleResponseComplete];
238 }
239 return NO;
240 }
241
242 - (BOOL)handleRequestShow:(const base::DictionaryValue&)message {
243 // TODO(crbug.com/602666): check that there's not already a pending request.
244 // TODO(crbug.com/602666): compare our supported payment types (i.e. autofill
245 // credit card types) against the merchant supported types and return NO
246 // if the intersection is empty.
247
248 const base::DictionaryValue* paymentRequestData;
249 web::PaymentRequest paymentRequest;
250 if (!message.GetDictionary("payment_request", &paymentRequestData)) {
251 DLOG(ERROR) << "JS message parameter 'payment_request' is missing";
252 return NO;
253 }
254 if (!paymentRequest.FromDictionaryValue(*paymentRequestData)) {
255 DLOG(ERROR) << "JS message parameter 'payment_request' is invalid";
256 return NO;
257 }
258
259 UIImage* pageFavicon = nil;
260 web::NavigationItem* navigationItem =
261 [self webState]->GetNavigationManager()->GetVisibleItem();
262 if (navigationItem && !navigationItem->GetFavicon().image.IsEmpty())
263 pageFavicon = navigationItem->GetFavicon().image.ToUIImage();
264 NSString* pageTitle = base::SysUTF16ToNSString([self webState]->GetTitle());
265 NSString* pageHost =
266 base::SysUTF8ToNSString([self webState]->GetLastCommittedURL().host());
267 _paymentRequestCoordinator.reset([[PaymentRequestCoordinator alloc]
268 initWithBaseViewController:_baseViewController
269 personalDataManager:_personalDataManager]);
270 [_paymentRequestCoordinator setPaymentRequest:paymentRequest];
271 [_paymentRequestCoordinator setPageFavicon:pageFavicon];
272 [_paymentRequestCoordinator setPageTitle:pageTitle];
273 [_paymentRequestCoordinator setPageHost:pageHost];
274 [_paymentRequestCoordinator setDelegate:self];
275
276 [_paymentRequestCoordinator start];
277
278 return YES;
279 }
280
281 - (BOOL)handleResponseComplete {
282 // TODO(crbug.com/602666): Check that there *is* a pending response here.
283 // TODO(crbug.com/602666): Indicate success or failure in the UI.
284
285 [self dismissUI];
286
287 // TODO(crbug.com/602666): Reject the promise on failure.
288 [_paymentRequestJsManager resolveResponsePromise:nil];
289
290 return YES;
291 }
292
293 - (void)dismissUI {
294 [_paymentRequestCoordinator stop];
295 _paymentRequestCoordinator.reset();
296 }
297
298 - (BOOL)webStateContentIsSecureHTML {
299 if (![self webState]) {
300 return NO;
301 }
302
303 if (!web::UrlHasWebScheme([self webState]->GetLastCommittedURL()) ||
304 ![self webState]->ContentIsHTML()) {
305 return NO;
306 }
307
308 const web::NavigationItem* navigationItem =
309 [self webState]->GetNavigationManager()->GetLastCommittedItem();
310 return navigationItem &&
311 navigationItem->GetSSL().security_style ==
312 web::SECURITY_STYLE_AUTHENTICATED;
313 }
314
315 #pragma mark - PaymentRequestCoordinatorDelegate methods
316
317 - (void)paymentRequestCoordinatorDidCancel {
318 [self cancelRequest];
319 }
320
321 - (void)paymentRequestCoordinatorDidConfirm:
322 (web::PaymentResponse)paymentResponse {
323 [_paymentRequestJsManager resolveRequestPromise:paymentResponse
324 completionHandler:nil];
325 }
326
327 #pragma mark - PaymentRequestWebStateDelegate methods
328
329 - (void)pageLoadedWithStatus:(web::PageLoadCompletionStatus)loadStatus {
330 if (loadStatus != web::PageLoadCompletionStatus::SUCCESS)
331 return;
332
333 [self dismissUI];
334 _isScriptInjected = NO;
335 [self enableCurrentWebState];
336 }
337
338 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/payments/payment_request_manager.h ('k') | ios/chrome/browser/payments/payment_request_utils.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698