OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ios/chrome/browser/payments/payment_request_manager.h" | 5 #import "ios/chrome/browser/payments/payment_request_manager.h" |
6 | 6 |
| 7 #include "base/ios/block_types.h" |
7 #include "base/ios/ios_util.h" | 8 #include "base/ios/ios_util.h" |
8 #import "base/mac/bind_objc_block.h" | 9 #import "base/mac/bind_objc_block.h" |
9 #include "base/mac/foundation_util.h" | 10 #include "base/mac/foundation_util.h" |
10 #include "base/memory/ptr_util.h" | 11 #include "base/memory/ptr_util.h" |
11 #include "base/strings/sys_string_conversions.h" | 12 #include "base/strings/sys_string_conversions.h" |
12 #import "base/values.h" | 13 #import "base/values.h" |
13 #include "components/autofill/core/browser/autofill_manager.h" | 14 #include "components/autofill/core/browser/autofill_manager.h" |
14 #include "components/autofill/core/browser/personal_data_manager.h" | 15 #include "components/autofill/core/browser/personal_data_manager.h" |
15 #include "components/autofill/ios/browser/autofill_driver_ios.h" | 16 #include "components/autofill/ios/browser/autofill_driver_ios.h" |
16 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h" | 17 #include "ios/chrome/browser/autofill/personal_data_manager_factory.h" |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 // provided by the _personalDataManager and manages selected ones for the | 61 // provided by the _personalDataManager and manages selected ones for the |
61 // current PaymentRequest flow. | 62 // current PaymentRequest flow. |
62 std::unique_ptr<PaymentRequest> _paymentRequest; | 63 std::unique_ptr<PaymentRequest> _paymentRequest; |
63 | 64 |
64 // WebState for the tab this object is attached to. | 65 // WebState for the tab this object is attached to. |
65 web::WebState* _webState; | 66 web::WebState* _webState; |
66 | 67 |
67 // Observer for |_webState|. | 68 // Observer for |_webState|. |
68 std::unique_ptr<web::WebStateObserverBridge> _webStateObserver; | 69 std::unique_ptr<web::WebStateObserverBridge> _webStateObserver; |
69 | 70 |
70 // Object that manages JavaScript injection into the web view. | |
71 __weak JSPaymentRequestManager* _paymentRequestJsManager; | |
72 | |
73 // Boolean to track if the current WebState is enabled (JS callback is set | 71 // Boolean to track if the current WebState is enabled (JS callback is set |
74 // up). | 72 // up). |
75 BOOL _webStateEnabled; | 73 BOOL _webStateEnabled; |
76 | 74 |
77 // Boolean to track if the script has been injected in the current page. This | 75 // Boolean to track if the script has been injected in the current page. This |
78 // is a faster check than asking the JS controller. | 76 // is a faster check than asking the JS controller. |
79 BOOL _isScriptInjected; | 77 BOOL _isScriptInjected; |
80 | 78 |
81 // True when close has been called and the PaymentRequest coordinator has | 79 // True when close has been called and the PaymentRequest coordinator has |
82 // been destroyed. | 80 // been destroyed. |
83 BOOL _closed; | 81 BOOL _closed; |
84 | 82 |
85 // Coordinator used to create and present the PaymentRequest view controller. | 83 // Coordinator used to create and present the PaymentRequest view controller. |
86 PaymentRequestCoordinator* _paymentRequestCoordinator; | 84 PaymentRequestCoordinator* _paymentRequestCoordinator; |
87 | 85 |
88 // Timer used to periodically unblock the webview's JS event queue. | 86 // Timer used to periodically unblock the webview's JS event queue. |
89 NSTimer* _unblockEventQueueTimer; | 87 NSTimer* _unblockEventQueueTimer; |
90 | 88 |
91 // Timer used to close the UI if the page does not call | 89 // Timer used to complete the Payment Request flow and close the UI if the |
92 // PaymentResponse.complete() in a timely fashion. | 90 // page does not call PaymentResponse.complete() in a timely fashion. |
93 NSTimer* _paymentResponseTimeoutTimer; | 91 NSTimer* _paymentResponseTimeoutTimer; |
| 92 |
| 93 // Timer used to cancel the Payment Request flow and close the UI if the |
| 94 // page does not settle the pending update promise in a timely fashion. |
| 95 NSTimer* _updateEventTimeoutTimer; |
94 } | 96 } |
95 | 97 |
| 98 // Object that manages JavaScript injection into the web view. |
| 99 @property(nonatomic, weak) JSPaymentRequestManager* paymentRequestJsManager; |
| 100 |
96 // Synchronous method executed by -asynchronouslyEnablePaymentRequest: | 101 // Synchronous method executed by -asynchronouslyEnablePaymentRequest: |
97 - (void)doEnablePaymentRequest:(BOOL)enabled; | 102 - (void)doEnablePaymentRequest:(BOOL)enabled; |
98 | 103 |
99 // Initialize the PaymentRequest JavaScript. | 104 // Initialize the PaymentRequest JavaScript. |
100 - (void)initializeWebViewForPaymentRequest; | 105 - (void)initializeWebViewForPaymentRequest; |
101 | 106 |
102 // Handler for injected JavaScript callbacks. | 107 // Handler for injected JavaScript callbacks. |
103 - (BOOL)handleScriptCommand:(const base::DictionaryValue&)JSONCommand; | 108 - (BOOL)handleScriptCommand:(const base::DictionaryValue&)JSONCommand; |
104 | 109 |
105 // Handles invocations of PaymentRequest.show(). The value of the JavaScript | 110 // Handles invocations of PaymentRequest.show(). The value of the JavaScript |
106 // PaymentRequest object should be provided in |message|. Returns YES if the | 111 // PaymentRequest object should be provided in |message|. Returns YES if the |
107 // invocation was successful. | 112 // invocation was successful. |
108 - (BOOL)handleRequestShow:(const base::DictionaryValue&)message; | 113 - (BOOL)handleRequestShow:(const base::DictionaryValue&)message; |
109 | 114 |
| 115 // Called by |_paymentResponseTimeoutTimer|, invokes handleResponseComplete: |
| 116 // as if PaymentResponse.complete() was invoked with the default "unknown" |
| 117 // argument. |
| 118 - (BOOL)handleResponseComplete; |
| 119 |
110 // Handles invocations of PaymentResponse.complete(). Returns YES if the | 120 // Handles invocations of PaymentResponse.complete(). Returns YES if the |
111 // invocation was successful. | 121 // invocation was successful. |
112 - (BOOL)handleResponseComplete; | 122 - (BOOL)handleResponseComplete:(const base::DictionaryValue&)message; |
113 | 123 |
114 // Handles invocations of PaymentRequestUpdateEvent.updateWith(). Returns YES if | 124 // Handles invocations of PaymentRequestUpdateEvent.updateWith(). Returns YES if |
115 // the invocation was successful. | 125 // the invocation was successful. |
116 - (BOOL)handleUpdatePaymentDetails:(const base::DictionaryValue&)message; | 126 - (BOOL)handleUpdatePaymentDetails:(const base::DictionaryValue&)message; |
117 | 127 |
118 // Establishes a timer that periodically prompts the JS manager to execute a | 128 // Establishes a timer that periodically prompts the JS manager to execute a |
119 // noop. This works around an issue where the JS event queue is blocked while | 129 // noop. This works around an issue where the JS event queue is blocked while |
120 // presenting the Payment Request UI. | 130 // presenting the Payment Request UI. |
121 - (void)setUnblockEventQueueTimer; | 131 - (void)setUnblockEventQueueTimer; |
122 | 132 |
123 // Establishes a timer that calls handleResponseComplete when it times out. Per | 133 // Establishes a timer that calls handleResponseComplete when it times out. Per |
124 // the spec, if the page does not call PaymentResponse.complete() within some | 134 // the spec, if the page does not call PaymentResponse.complete() within some |
125 // timeout period, user agents may behave as if the complete() method was | 135 // timeout period, user agents may behave as if the complete() method was |
126 // called with no arguments. | 136 // called with no arguments. |
127 - (void)setPaymentResponseTimeoutTimer; | 137 - (void)setPaymentResponseTimeoutTimer; |
128 | 138 |
| 139 // Establishes a timer that dismisses the Payment Request UI when it times out. |
| 140 // Per the spec, implementations may choose to consider a timeout for the |
| 141 // promise provided with the PaymentRequestUpdateEvent.updateWith() call. If the |
| 142 // promise doesn't get settled in a reasonable amount of time, it is as if it |
| 143 // was rejected. |
| 144 - (void)setUpdateEventTimeoutTimer; |
| 145 |
129 @end | 146 @end |
130 | 147 |
131 @implementation PaymentRequestManager | 148 @implementation PaymentRequestManager |
132 | 149 |
133 @synthesize enabled = _enabled; | 150 @synthesize enabled = _enabled; |
134 @synthesize webState = _webState; | 151 @synthesize webState = _webState; |
135 @synthesize browserState = _browserState; | 152 @synthesize browserState = _browserState; |
| 153 @synthesize paymentRequestJsManager = _paymentRequestJsManager; |
136 | 154 |
137 - (instancetype)initWithBaseViewController:(UIViewController*)viewController | 155 - (instancetype)initWithBaseViewController:(UIViewController*)viewController |
138 browserState: | 156 browserState: |
139 (ios::ChromeBrowserState*)browserState { | 157 (ios::ChromeBrowserState*)browserState { |
140 if ((self = [super init])) { | 158 if ((self = [super init])) { |
141 _baseViewController = viewController; | 159 _baseViewController = viewController; |
142 | 160 |
143 _browserState = browserState; | 161 _browserState = browserState; |
144 | 162 |
145 _personalDataManager = | 163 _personalDataManager = |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
268 return NO; | 286 return NO; |
269 } | 287 } |
270 | 288 |
271 if (command == "paymentRequest.requestShow") { | 289 if (command == "paymentRequest.requestShow") { |
272 return [self handleRequestShow:JSONCommand]; | 290 return [self handleRequestShow:JSONCommand]; |
273 } | 291 } |
274 if (command == "paymentRequest.requestCancel") { | 292 if (command == "paymentRequest.requestCancel") { |
275 return [self handleRequestCancel]; | 293 return [self handleRequestCancel]; |
276 } | 294 } |
277 if (command == "paymentRequest.responseComplete") { | 295 if (command == "paymentRequest.responseComplete") { |
278 return [self handleResponseComplete]; | 296 return [self handleResponseComplete:JSONCommand]; |
279 } | 297 } |
280 if (command == "paymentRequest.updatePaymentDetails") { | 298 if (command == "paymentRequest.updatePaymentDetails") { |
281 return [self handleUpdatePaymentDetails:JSONCommand]; | 299 return [self handleUpdatePaymentDetails:JSONCommand]; |
282 } | 300 } |
283 return NO; | 301 return NO; |
284 } | 302 } |
285 | 303 |
286 - (BOOL)handleRequestShow:(const base::DictionaryValue&)message { | 304 - (BOOL)handleRequestShow:(const base::DictionaryValue&)message { |
287 // TODO(crbug.com/602666): check that there's not already a pending request. | 305 // TODO(crbug.com/602666): check that there's not already a pending request. |
288 // TODO(crbug.com/602666): compare our supported payment types (i.e. autofill | 306 // TODO(crbug.com/602666): compare our supported payment types (i.e. autofill |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 [_paymentRequestCoordinator start]; | 345 [_paymentRequestCoordinator start]; |
328 | 346 |
329 return YES; | 347 return YES; |
330 } | 348 } |
331 | 349 |
332 - (BOOL)handleRequestCancel { | 350 - (BOOL)handleRequestCancel { |
333 // TODO(crbug.com/602666): Check that there is already a pending request. | 351 // TODO(crbug.com/602666): Check that there is already a pending request. |
334 | 352 |
335 [_unblockEventQueueTimer invalidate]; | 353 [_unblockEventQueueTimer invalidate]; |
336 [_paymentResponseTimeoutTimer invalidate]; | 354 [_paymentResponseTimeoutTimer invalidate]; |
| 355 [_updateEventTimeoutTimer invalidate]; |
337 | 356 |
338 [self cancelRequestWithErrorMessage:@"Request canceled by the page."]; | 357 __weak PaymentRequestManager* weakSelf = self; |
| 358 ProceduralBlock callback = ^{ |
| 359 PaymentRequestManager* strongSelf = weakSelf; |
| 360 // Early return if the manager has been deallocated. |
| 361 if (!strongSelf) |
| 362 return; |
| 363 [strongSelf cancelRequestWithErrorMessage:@"Request canceled."]; |
| 364 }; |
| 365 |
| 366 [_paymentRequestCoordinator displayErrorWithCallback:callback]; |
339 | 367 |
340 return YES; | 368 return YES; |
341 } | 369 } |
342 | 370 |
343 - (BOOL)handleResponseComplete { | 371 - (BOOL)handleResponseComplete { |
| 372 base::DictionaryValue command; |
| 373 command.SetString("result", "unknown"); |
| 374 return [self handleResponseComplete:command]; |
| 375 } |
| 376 |
| 377 - (BOOL)handleResponseComplete:(const base::DictionaryValue&)message { |
344 // TODO(crbug.com/602666): Check that there *is* a pending response here. | 378 // TODO(crbug.com/602666): Check that there *is* a pending response here. |
345 // TODO(crbug.com/602666): Indicate success or failure in the UI. | |
346 | 379 |
347 [_unblockEventQueueTimer invalidate]; | 380 [_unblockEventQueueTimer invalidate]; |
348 [_paymentResponseTimeoutTimer invalidate]; | 381 [_paymentResponseTimeoutTimer invalidate]; |
| 382 [_updateEventTimeoutTimer invalidate]; |
349 | 383 |
350 [self dismissUI]; | 384 std::string result; |
| 385 if (!message.GetString("result", &result)) { |
| 386 DLOG(ERROR) << "JS message parameter 'result' is missing"; |
| 387 return NO; |
| 388 } |
351 | 389 |
352 [_paymentRequestJsManager resolveResponsePromiseWithCompletionHandler:nil]; | 390 __weak PaymentRequestManager* weakSelf = self; |
| 391 ProceduralBlock callback = ^{ |
| 392 PaymentRequestManager* strongSelf = weakSelf; |
| 393 // Early return if the manager has been deallocated. |
| 394 if (!strongSelf) |
| 395 return; |
| 396 [strongSelf dismissUI]; |
| 397 [strongSelf.paymentRequestJsManager |
| 398 resolveResponsePromiseWithCompletionHandler:nil]; |
| 399 }; |
| 400 |
| 401 // Display UI indicating failure if the value of |result| is "fail". |
| 402 if (result == "fail") { |
| 403 [_paymentRequestCoordinator displayErrorWithCallback:callback]; |
| 404 } else { |
| 405 callback(); |
| 406 } |
353 | 407 |
354 return YES; | 408 return YES; |
355 } | 409 } |
356 | 410 |
357 - (BOOL)handleUpdatePaymentDetails:(const base::DictionaryValue&)message { | 411 - (BOOL)handleUpdatePaymentDetails:(const base::DictionaryValue&)message { |
358 // TODO(crbug.com/602666): Check that there is already a pending request. | 412 // TODO(crbug.com/602666): Check that there is already a pending request. |
359 | 413 |
360 [_unblockEventQueueTimer invalidate]; | 414 [_unblockEventQueueTimer invalidate]; |
| 415 [_updateEventTimeoutTimer invalidate]; |
361 | 416 |
362 const base::DictionaryValue* paymentDetailsData = nullptr; | 417 const base::DictionaryValue* paymentDetailsData = nullptr; |
363 web::PaymentDetails paymentDetails; | 418 web::PaymentDetails paymentDetails; |
364 if (!message.GetDictionary("payment_details", &paymentDetailsData)) { | 419 if (!message.GetDictionary("payment_details", &paymentDetailsData)) { |
365 DLOG(ERROR) << "JS message parameter 'payment_details' is missing"; | 420 DLOG(ERROR) << "JS message parameter 'payment_details' is missing"; |
366 return NO; | 421 return NO; |
367 } | 422 } |
368 if (!paymentDetails.FromDictionaryValue(*paymentDetailsData)) { | 423 if (!paymentDetails.FromDictionaryValue(*paymentDetailsData)) { |
369 DLOG(ERROR) << "JS message parameter 'payment_details' is invalid"; | 424 DLOG(ERROR) << "JS message parameter 'payment_details' is invalid"; |
370 return NO; | 425 return NO; |
(...skipping 15 matching lines...) Expand all Loading... |
386 | 441 |
387 - (void)setPaymentResponseTimeoutTimer { | 442 - (void)setPaymentResponseTimeoutTimer { |
388 _paymentResponseTimeoutTimer = | 443 _paymentResponseTimeoutTimer = |
389 [NSTimer scheduledTimerWithTimeInterval:kTimeoutInterval | 444 [NSTimer scheduledTimerWithTimeInterval:kTimeoutInterval |
390 target:self | 445 target:self |
391 selector:@selector(handleResponseComplete) | 446 selector:@selector(handleResponseComplete) |
392 userInfo:nil | 447 userInfo:nil |
393 repeats:NO]; | 448 repeats:NO]; |
394 } | 449 } |
395 | 450 |
| 451 - (void)setUpdateEventTimeoutTimer { |
| 452 _updateEventTimeoutTimer = |
| 453 [NSTimer scheduledTimerWithTimeInterval:kTimeoutInterval |
| 454 target:self |
| 455 selector:@selector(handleRequestCancel) |
| 456 userInfo:nil |
| 457 repeats:NO]; |
| 458 } |
| 459 |
396 - (void)dismissUI { | 460 - (void)dismissUI { |
397 [_paymentRequestCoordinator stop]; | 461 [_paymentRequestCoordinator stop]; |
398 _paymentRequestCoordinator = nil; | 462 _paymentRequestCoordinator = nil; |
399 } | 463 } |
400 | 464 |
401 - (BOOL)webStateContentIsSecureHTML { | 465 - (BOOL)webStateContentIsSecureHTML { |
402 if (![self webState]) { | 466 if (![self webState]) { |
403 return NO; | 467 return NO; |
404 } | 468 } |
405 | 469 |
406 if (!web::UrlHasWebScheme([self webState]->GetLastCommittedURL()) || | 470 if (!web::UrlHasWebScheme([self webState]->GetLastCommittedURL()) || |
407 ![self webState]->ContentIsHTML()) { | 471 ![self webState]->ContentIsHTML()) { |
408 return NO; | 472 return NO; |
409 } | 473 } |
410 | 474 |
411 const web::NavigationItem* navigationItem = | 475 const web::NavigationItem* navigationItem = |
412 [self webState]->GetNavigationManager()->GetLastCommittedItem(); | 476 [self webState]->GetNavigationManager()->GetLastCommittedItem(); |
413 return navigationItem && | 477 return navigationItem && |
414 navigationItem->GetSSL().security_style == | 478 navigationItem->GetSSL().security_style == |
415 web::SECURITY_STYLE_AUTHENTICATED; | 479 web::SECURITY_STYLE_AUTHENTICATED; |
416 } | 480 } |
417 | 481 |
418 #pragma mark - PaymentRequestCoordinatorDelegate methods | 482 #pragma mark - PaymentRequestCoordinatorDelegate methods |
419 | 483 |
420 - (void)paymentRequestCoordinatorDidCancel: | 484 - (void)paymentRequestCoordinatorDidCancel: |
421 (PaymentRequestCoordinator*)coordinator { | 485 (PaymentRequestCoordinator*)coordinator { |
422 [self cancelRequestWithErrorMessage:@"Request canceled by user."]; | 486 [self cancelRequestWithErrorMessage:@"Request canceled."]; |
423 } | 487 } |
424 | 488 |
425 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator | 489 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator |
426 didConfirmWithPaymentResponse:(web::PaymentResponse)paymentResponse { | 490 didConfirmWithPaymentResponse:(web::PaymentResponse)paymentResponse { |
427 [_paymentRequestJsManager | 491 [_paymentRequestJsManager |
428 resolveRequestPromiseWithPaymentResponse:paymentResponse | 492 resolveRequestPromiseWithPaymentResponse:paymentResponse |
429 completionHandler:nil]; | 493 completionHandler:nil]; |
430 [self setUnblockEventQueueTimer]; | 494 [self setUnblockEventQueueTimer]; |
431 [self setPaymentResponseTimeoutTimer]; | 495 [self setPaymentResponseTimeoutTimer]; |
432 } | 496 } |
433 | 497 |
434 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator | 498 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator |
435 didSelectShippingAddress:(web::PaymentAddress)shippingAddress { | 499 didSelectShippingAddress:(web::PaymentAddress)shippingAddress { |
436 [_paymentRequestJsManager updateShippingAddress:shippingAddress | 500 [_paymentRequestJsManager updateShippingAddress:shippingAddress |
437 completionHandler:nil]; | 501 completionHandler:nil]; |
438 [self setUnblockEventQueueTimer]; | 502 [self setUnblockEventQueueTimer]; |
| 503 [self setUpdateEventTimeoutTimer]; |
439 } | 504 } |
440 | 505 |
441 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator | 506 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator |
442 didSelectShippingOption:(web::PaymentShippingOption)shippingOption { | 507 didSelectShippingOption:(web::PaymentShippingOption)shippingOption { |
443 [_paymentRequestJsManager updateShippingOption:shippingOption | 508 [_paymentRequestJsManager updateShippingOption:shippingOption |
444 completionHandler:nil]; | 509 completionHandler:nil]; |
445 [self setUnblockEventQueueTimer]; | 510 [self setUnblockEventQueueTimer]; |
| 511 [self setUpdateEventTimeoutTimer]; |
446 } | 512 } |
447 | 513 |
448 #pragma mark - CRWWebStateObserver methods | 514 #pragma mark - CRWWebStateObserver methods |
449 | 515 |
450 - (void)webState:(web::WebState*)webState | 516 - (void)webState:(web::WebState*)webState |
451 didCommitNavigationWithDetails: | 517 didCommitNavigationWithDetails: |
452 (const web::LoadCommittedDetails&)load_details { | 518 (const web::LoadCommittedDetails&)load_details { |
453 [self dismissUI]; | 519 [self dismissUI]; |
454 _isScriptInjected = NO; | 520 _isScriptInjected = NO; |
455 [self enableCurrentWebState]; | 521 [self enableCurrentWebState]; |
456 [self initializeWebViewForPaymentRequest]; | 522 [self initializeWebViewForPaymentRequest]; |
457 } | 523 } |
458 | 524 |
459 @end | 525 @end |
OLD | NEW |