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/ios_util.h" | 7 #include "base/ios/ios_util.h" |
8 #import "base/ios/weak_nsobject.h" | 8 #import "base/ios/weak_nsobject.h" |
9 #import "base/mac/bind_objc_block.h" | 9 #import "base/mac/bind_objc_block.h" |
10 #include "base/mac/foundation_util.h" | 10 #include "base/mac/foundation_util.h" |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
79 // True when close has been called and the PaymentRequest coordinator has | 79 // True when close has been called and the PaymentRequest coordinator has |
80 // been destroyed. | 80 // been destroyed. |
81 BOOL _closed; | 81 BOOL _closed; |
82 | 82 |
83 // Coordinator used to create and present the PaymentRequest view controller. | 83 // Coordinator used to create and present the PaymentRequest view controller. |
84 base::scoped_nsobject<PaymentRequestCoordinator> _paymentRequestCoordinator; | 84 base::scoped_nsobject<PaymentRequestCoordinator> _paymentRequestCoordinator; |
85 | 85 |
86 // Timer used to periodically unblock the webview's JS event queue. | 86 // Timer used to periodically unblock the webview's JS event queue. |
87 base::scoped_nsobject<NSTimer> _unblockEventQueueTimer; | 87 base::scoped_nsobject<NSTimer> _unblockEventQueueTimer; |
88 | 88 |
89 // 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 |
90 // PaymentResponse.complete() in a timely fashion. | 90 // page does not call PaymentResponse.complete() in a timely fashion. |
91 base::scoped_nsobject<NSTimer> _paymentResponseTimeoutTimer; | 91 base::scoped_nsobject<NSTimer> _paymentResponseTimeoutTimer; |
92 | |
93 // Timer used to cancel the Payment Request flow and close the UI if the | |
94 // page does not call settle the pending update promise in a timely fashion. | |
lpromero
2017/02/21 16:51:08
Should "call" be removed?
Moe
2017/02/21 22:56:15
Done.
| |
95 base::scoped_nsobject<NSTimer> _updateEventTimeoutTimer; | |
92 } | 96 } |
93 | 97 |
94 // Synchronous method executed by -asynchronouslyEnablePaymentRequest: | 98 // Synchronous method executed by -asynchronouslyEnablePaymentRequest: |
95 - (void)doEnablePaymentRequest:(BOOL)enabled; | 99 - (void)doEnablePaymentRequest:(BOOL)enabled; |
96 | 100 |
97 // Initialize the PaymentRequest JavaScript. | 101 // Initialize the PaymentRequest JavaScript. |
98 - (void)initializeWebViewForPaymentRequest; | 102 - (void)initializeWebViewForPaymentRequest; |
99 | 103 |
100 // Handler for injected JavaScript callbacks. | 104 // Handler for injected JavaScript callbacks. |
101 - (BOOL)handleScriptCommand:(const base::DictionaryValue&)JSONCommand; | 105 - (BOOL)handleScriptCommand:(const base::DictionaryValue&)JSONCommand; |
102 | 106 |
103 // Handles invocations of PaymentRequest.show(). The value of the JavaScript | 107 // Handles invocations of PaymentRequest.show(). The value of the JavaScript |
104 // PaymentRequest object should be provided in |message|. Returns YES if the | 108 // PaymentRequest object should be provided in |message|. Returns YES if the |
105 // invocation was successful. | 109 // invocation was successful. |
106 - (BOOL)handleRequestShow:(const base::DictionaryValue&)message; | 110 - (BOOL)handleRequestShow:(const base::DictionaryValue&)message; |
107 | 111 |
112 // Called by |_paymentResponseTimeoutTimer|, invokes handleResponseComplete: | |
113 // as if PaymentResponse.complete() was invoked with the default "unknown" | |
114 // argument. | |
115 - (BOOL)handleResponseComplete; | |
116 | |
108 // Handles invocations of PaymentResponse.complete(). Returns YES if the | 117 // Handles invocations of PaymentResponse.complete(). Returns YES if the |
109 // invocation was successful. | 118 // invocation was successful. |
110 - (BOOL)handleResponseComplete; | 119 - (BOOL)handleResponseComplete:(const base::DictionaryValue&)message; |
111 | 120 |
112 // Handles invocations of PaymentRequestUpdateEvent.updateWith(). Returns YES if | 121 // Handles invocations of PaymentRequestUpdateEvent.updateWith(). Returns YES if |
113 // the invocation was successful. | 122 // the invocation was successful. |
114 - (BOOL)handleUpdatePaymentDetails:(const base::DictionaryValue&)message; | 123 - (BOOL)handleUpdatePaymentDetails:(const base::DictionaryValue&)message; |
115 | 124 |
116 // Establishes a timer that periodically prompts the JS manager to execute a | 125 // Establishes a timer that periodically prompts the JS manager to execute a |
117 // noop. This works around an issue where the JS event queue is blocked while | 126 // noop. This works around an issue where the JS event queue is blocked while |
118 // presenting the Payment Request UI. | 127 // presenting the Payment Request UI. |
119 - (void)setUnblockEventQueueTimer; | 128 - (void)setUnblockEventQueueTimer; |
120 | 129 |
121 // Establishes a timer that calls handleResponseComplete when it times out. Per | 130 // Establishes a timer that calls handleResponseComplete when it times out. Per |
122 // the spec, if the page does not call PaymentResponse.complete() within some | 131 // the spec, if the page does not call PaymentResponse.complete() within some |
123 // timeout period, user agents may behave as if the complete() method was | 132 // timeout period, user agents may behave as if the complete() method was |
124 // called with no arguments. | 133 // called with no arguments. |
125 - (void)setPaymentResponseTimeoutTimer; | 134 - (void)setPaymentResponseTimeoutTimer; |
126 | 135 |
136 // Establishes a timer that dismisses the Payment Request UI when it times out. | |
137 // Per the spec, implementations may choose to consider a timeout for the | |
138 // promise provided with the PaymentRequestUpdateEvent.updateWith() call. If the | |
139 // promise doesn't get settled in a reasonable amount of time, it is as if it | |
140 // was rejected. | |
141 - (void)setUpdateEventTimeoutTimer; | |
142 | |
127 @end | 143 @end |
128 | 144 |
129 @implementation PaymentRequestManager | 145 @implementation PaymentRequestManager |
130 | 146 |
131 @synthesize enabled = _enabled; | 147 @synthesize enabled = _enabled; |
132 @synthesize webState = _webState; | 148 @synthesize webState = _webState; |
133 @synthesize browserState = _browserState; | 149 @synthesize browserState = _browserState; |
134 | 150 |
135 - (instancetype)initWithBaseViewController:(UIViewController*)viewController | 151 - (instancetype)initWithBaseViewController:(UIViewController*)viewController |
136 browserState: | 152 browserState: |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
268 return NO; | 284 return NO; |
269 } | 285 } |
270 | 286 |
271 if (command == "paymentRequest.requestShow") { | 287 if (command == "paymentRequest.requestShow") { |
272 return [self handleRequestShow:JSONCommand]; | 288 return [self handleRequestShow:JSONCommand]; |
273 } | 289 } |
274 if (command == "paymentRequest.requestCancel") { | 290 if (command == "paymentRequest.requestCancel") { |
275 return [self handleRequestCancel]; | 291 return [self handleRequestCancel]; |
276 } | 292 } |
277 if (command == "paymentRequest.responseComplete") { | 293 if (command == "paymentRequest.responseComplete") { |
278 return [self handleResponseComplete]; | 294 return [self handleResponseComplete:JSONCommand]; |
279 } | 295 } |
280 if (command == "paymentRequest.updatePaymentDetails") { | 296 if (command == "paymentRequest.updatePaymentDetails") { |
281 return [self handleUpdatePaymentDetails:JSONCommand]; | 297 return [self handleUpdatePaymentDetails:JSONCommand]; |
282 } | 298 } |
283 return NO; | 299 return NO; |
284 } | 300 } |
285 | 301 |
286 - (BOOL)handleRequestShow:(const base::DictionaryValue&)message { | 302 - (BOOL)handleRequestShow:(const base::DictionaryValue&)message { |
287 // TODO(crbug.com/602666): check that there's not already a pending request. | 303 // 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 | 304 // 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]; | 343 [_paymentRequestCoordinator start]; |
328 | 344 |
329 return YES; | 345 return YES; |
330 } | 346 } |
331 | 347 |
332 - (BOOL)handleRequestCancel { | 348 - (BOOL)handleRequestCancel { |
333 // TODO(crbug.com/602666): Check that there is already a pending request. | 349 // TODO(crbug.com/602666): Check that there is already a pending request. |
334 | 350 |
335 [_unblockEventQueueTimer invalidate]; | 351 [_unblockEventQueueTimer invalidate]; |
336 [_paymentResponseTimeoutTimer invalidate]; | 352 [_paymentResponseTimeoutTimer invalidate]; |
353 [_updateEventTimeoutTimer invalidate]; | |
337 | 354 |
338 [self cancelRequestWithErrorMessage:@"Request canceled by the page."]; | 355 base::WeakNSObject<PaymentRequestManager> weakSelf(self); |
356 void (^callback)() = ^{ | |
357 base::scoped_nsobject<PaymentRequestManager> strongSelf([weakSelf retain]); | |
358 [strongSelf cancelRequestWithErrorMessage:@"Request canceled."]; | |
359 }; | |
360 | |
361 [_paymentRequestCoordinator displayErrorWithCallback:callback]; | |
339 | 362 |
340 return YES; | 363 return YES; |
341 } | 364 } |
342 | 365 |
343 - (BOOL)handleResponseComplete { | 366 - (BOOL)handleResponseComplete { |
367 base::DictionaryValue command; | |
368 command.SetString("result", "unknown"); | |
369 return [self handleResponseComplete:command]; | |
370 } | |
371 | |
372 - (BOOL)handleResponseComplete:(const base::DictionaryValue&)message { | |
344 // TODO(crbug.com/602666): Check that there *is* a pending response here. | 373 // 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 | 374 |
347 [_unblockEventQueueTimer invalidate]; | 375 [_unblockEventQueueTimer invalidate]; |
348 [_paymentResponseTimeoutTimer invalidate]; | 376 [_paymentResponseTimeoutTimer invalidate]; |
377 [_updateEventTimeoutTimer invalidate]; | |
349 | 378 |
350 [self dismissUI]; | 379 [_unblockEventQueueTimer invalidate]; |
351 | 380 |
352 [_paymentRequestJsManager resolveResponsePromiseWithCompletionHandler:nil]; | 381 std::string result; |
382 if (!message.GetString("result", &result)) { | |
383 DLOG(ERROR) << "JS message parameter 'result' is missing"; | |
384 return NO; | |
385 } | |
386 | |
387 base::WeakNSObject<PaymentRequestManager> weakSelf(self); | |
388 void (^callback)() = ^{ | |
389 base::scoped_nsobject<PaymentRequestManager> strongSelf([weakSelf retain]); | |
390 [strongSelf dismissUI]; | |
391 [_paymentRequestJsManager resolveResponsePromiseWithCompletionHandler:nil]; | |
392 }; | |
393 | |
394 // Display UI indicating failure if the value of |result| is "fail". | |
395 if (result == "fail") { | |
396 [_paymentRequestCoordinator displayErrorWithCallback:callback]; | |
397 } else { | |
398 callback(); | |
399 } | |
353 | 400 |
354 return YES; | 401 return YES; |
355 } | 402 } |
356 | 403 |
357 - (BOOL)handleUpdatePaymentDetails:(const base::DictionaryValue&)message { | 404 - (BOOL)handleUpdatePaymentDetails:(const base::DictionaryValue&)message { |
358 // TODO(crbug.com/602666): Check that there is already a pending request. | 405 // TODO(crbug.com/602666): Check that there is already a pending request. |
359 | 406 |
360 [_unblockEventQueueTimer invalidate]; | 407 [_unblockEventQueueTimer invalidate]; |
361 | 408 |
362 const base::DictionaryValue* paymentDetailsData = nullptr; | 409 const base::DictionaryValue* paymentDetailsData = nullptr; |
(...skipping 23 matching lines...) Expand all Loading... | |
386 | 433 |
387 - (void)setPaymentResponseTimeoutTimer { | 434 - (void)setPaymentResponseTimeoutTimer { |
388 _paymentResponseTimeoutTimer.reset( | 435 _paymentResponseTimeoutTimer.reset( |
389 [[NSTimer scheduledTimerWithTimeInterval:kTimeoutInterval | 436 [[NSTimer scheduledTimerWithTimeInterval:kTimeoutInterval |
390 target:self | 437 target:self |
391 selector:@selector(handleResponseComplete) | 438 selector:@selector(handleResponseComplete) |
392 userInfo:nil | 439 userInfo:nil |
393 repeats:NO] retain]); | 440 repeats:NO] retain]); |
394 } | 441 } |
395 | 442 |
443 - (void)setUpdateEventTimeoutTimer { | |
444 _updateEventTimeoutTimer.reset( | |
445 [[NSTimer scheduledTimerWithTimeInterval:kTimeoutInterval | |
446 target:self | |
447 selector:@selector(handleRequestCancel) | |
448 userInfo:nil | |
449 repeats:NO] retain]); | |
450 } | |
451 | |
396 - (void)dismissUI { | 452 - (void)dismissUI { |
397 [_paymentRequestCoordinator stop]; | 453 [_paymentRequestCoordinator stop]; |
398 _paymentRequestCoordinator.reset(); | 454 _paymentRequestCoordinator.reset(); |
399 } | 455 } |
400 | 456 |
401 - (BOOL)webStateContentIsSecureHTML { | 457 - (BOOL)webStateContentIsSecureHTML { |
402 if (![self webState]) { | 458 if (![self webState]) { |
403 return NO; | 459 return NO; |
404 } | 460 } |
405 | 461 |
406 if (!web::UrlHasWebScheme([self webState]->GetLastCommittedURL()) || | 462 if (!web::UrlHasWebScheme([self webState]->GetLastCommittedURL()) || |
407 ![self webState]->ContentIsHTML()) { | 463 ![self webState]->ContentIsHTML()) { |
408 return NO; | 464 return NO; |
409 } | 465 } |
410 | 466 |
411 const web::NavigationItem* navigationItem = | 467 const web::NavigationItem* navigationItem = |
412 [self webState]->GetNavigationManager()->GetLastCommittedItem(); | 468 [self webState]->GetNavigationManager()->GetLastCommittedItem(); |
413 return navigationItem && | 469 return navigationItem && |
414 navigationItem->GetSSL().security_style == | 470 navigationItem->GetSSL().security_style == |
415 web::SECURITY_STYLE_AUTHENTICATED; | 471 web::SECURITY_STYLE_AUTHENTICATED; |
416 } | 472 } |
417 | 473 |
418 #pragma mark - PaymentRequestCoordinatorDelegate methods | 474 #pragma mark - PaymentRequestCoordinatorDelegate methods |
419 | 475 |
420 - (void)paymentRequestCoordinatorDidCancel: | 476 - (void)paymentRequestCoordinatorDidCancel: |
421 (PaymentRequestCoordinator*)coordinator { | 477 (PaymentRequestCoordinator*)coordinator { |
422 [self cancelRequestWithErrorMessage:@"Request canceled by user."]; | 478 [self cancelRequestWithErrorMessage:@"Request canceled."]; |
423 } | 479 } |
424 | 480 |
425 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator | 481 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator |
426 didConfirmWithPaymentResponse:(web::PaymentResponse)paymentResponse { | 482 didConfirmWithPaymentResponse:(web::PaymentResponse)paymentResponse { |
427 [_paymentRequestJsManager | 483 [_paymentRequestJsManager |
428 resolveRequestPromiseWithPaymentResponse:paymentResponse | 484 resolveRequestPromiseWithPaymentResponse:paymentResponse |
429 completionHandler:nil]; | 485 completionHandler:nil]; |
430 [self setUnblockEventQueueTimer]; | 486 [self setUnblockEventQueueTimer]; |
431 [self setPaymentResponseTimeoutTimer]; | 487 [self setPaymentResponseTimeoutTimer]; |
432 } | 488 } |
433 | 489 |
434 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator | 490 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator |
435 didSelectShippingAddress:(web::PaymentAddress)shippingAddress { | 491 didSelectShippingAddress:(web::PaymentAddress)shippingAddress { |
436 [_paymentRequestJsManager updateShippingAddress:shippingAddress | 492 [_paymentRequestJsManager updateShippingAddress:shippingAddress |
437 completionHandler:nil]; | 493 completionHandler:nil]; |
438 [self setUnblockEventQueueTimer]; | 494 [self setUnblockEventQueueTimer]; |
495 [self setUpdateEventTimeoutTimer]; | |
439 } | 496 } |
440 | 497 |
441 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator | 498 - (void)paymentRequestCoordinator:(PaymentRequestCoordinator*)coordinator |
442 didSelectShippingOption:(web::PaymentShippingOption)shippingOption { | 499 didSelectShippingOption:(web::PaymentShippingOption)shippingOption { |
443 [_paymentRequestJsManager updateShippingOption:shippingOption | 500 [_paymentRequestJsManager updateShippingOption:shippingOption |
444 completionHandler:nil]; | 501 completionHandler:nil]; |
445 [self setUnblockEventQueueTimer]; | 502 [self setUnblockEventQueueTimer]; |
503 [self setUpdateEventTimeoutTimer]; | |
446 } | 504 } |
447 | 505 |
448 #pragma mark - CRWWebStateObserver methods | 506 #pragma mark - CRWWebStateObserver methods |
449 | 507 |
450 - (void)webState:(web::WebState*)webState | 508 - (void)webState:(web::WebState*)webState |
451 didCommitNavigationWithDetails: | 509 didCommitNavigationWithDetails: |
452 (const web::LoadCommittedDetails&)load_details { | 510 (const web::LoadCommittedDetails&)load_details { |
453 [self dismissUI]; | 511 [self dismissUI]; |
454 _isScriptInjected = NO; | 512 _isScriptInjected = NO; |
455 [self enableCurrentWebState]; | 513 [self enableCurrentWebState]; |
456 [self initializeWebViewForPaymentRequest]; | 514 [self initializeWebViewForPaymentRequest]; |
457 } | 515 } |
458 | 516 |
459 @end | 517 @end |
OLD | NEW |