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

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

Issue 2701923003: [Payment Request] Error message screen (Closed)
Patch Set: rebase Created 3 years, 10 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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698