Changeset 286452 in webkit


Ignore:
Timestamp:
Dec 2, 2021 2:21:46 PM (8 months ago)
Author:
Devin Rousso
Message:

[Payment Request] Validate payment method data on construction
https://bugs.webkit.org/show_bug.cgi?id=233292
<rdar://problem/85736007>

Reviewed by Andy Estes.

Source/WebCore:

This will allow developers to replace any ApplePaySession.supportsVersion check(s) by
attempting to create a PaymentRequest object with data specific to Apple Pay. This is
actually an improvement, because it'll also allow developers to catch most errors in that
data earlier, as previously any errors would only be thrown when show() is called.

Spec: <https://github.com/w3c/payment-request/pull/976>

Test: http/tests/ssl/applepay/PaymentRequest.https.html

  • Modules/paymentrequest/PaymentRequest.cpp:

(WebCore::PaymentRequest::show):

  • Modules/paymentrequest/PaymentHandler.h:

Adjust PaymentHandler::convertData to take a Document so that additional validation
can be performed on the provided payment method(s) (e.g. check the Apple Pay version).

  • Modules/applepay/PaymentRequestValidator.h:
  • Modules/applepay/PaymentRequestValidator.mm:

(WebCore::PaymentRequestValidator::validate):
Allow callers to choose what fields to validate. This is needed because when the
ApplePaySessionPaymentRequest created/converted when validating the payment method data in
the PaymentRequest constructor isn't able to get fields from the PaymentRequest as it
hasn't been created yet.

  • Modules/applepay/ApplePaySession.cpp:

(WebCore::convertAndValidate):

  • Modules/applepay/paymentrequest/ApplePayPaymentHandler.h:
  • Modules/applepay/paymentrequest/ApplePayPaymentHandler.cpp:

(WebCore::convertAndValidateApplePayRequest):
(WebCore::ApplePayPaymentHandler::convertData):
(WebCore::ApplePayPaymentHandler::show):

  • Modules/applepay-ams-ui/ApplePayAMSUIPaymentHandler.h:
  • Modules/applepay-ams-ui/ApplePayAMSUIPaymentHandler.cpp:

(WebCore::convertAndValidateApplePayAMSUIRequest):
(WebCore::ApplePayAMSUIPaymentHandler::convertData):

LayoutTests:

  • http/tests/ssl/applepay/PaymentRequest.https.html:
  • http/tests/ssl/applepay/PaymentRequest.https-expected.txt:
Location:
trunk
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r286451 r286452  
     12021-12-02  Devin Rousso  <drousso@apple.com>
     2
     3        [Payment Request] Validate payment method data on construction
     4        https://bugs.webkit.org/show_bug.cgi?id=233292
     5        <rdar://problem/85736007>
     6
     7        Reviewed by Andy Estes.
     8
     9        * http/tests/ssl/applepay/PaymentRequest.https.html:
     10        * http/tests/ssl/applepay/PaymentRequest.https-expected.txt:
     11
    1122021-12-02  Fujii Hironori  <Hironori.Fujii@sony.com>
    213
  • trunk/LayoutTests/http/tests/ssl/applepay/PaymentRequest.https-expected.txt

    r274012 r286452  
    1111Testing ApplePayRequest.version
    1212
    13 SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.version = 0; request = new PaymentRequest([paymentMethod], validPaymentDetails())
    14 PASS request.show() rejected promise  with InvalidAccessError: "0" is not a supported version..
    15 SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.version = 1000; request = new PaymentRequest([paymentMethod], validPaymentDetails())
    16 PASS request.show() rejected promise  with InvalidAccessError: "1000" is not a supported version..
     13SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.version = 0;
     14PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception InvalidAccessError: "0" is not a supported version..
     15
     16SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.version = 1000;
     17PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception InvalidAccessError: "1000" is not a supported version..
     18
    1719
    1820Testing ApplePayRequest.countryCode
     
    2123PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Member ApplePayRequest.countryCode is required and must be an instance of DOMString.
    2224
    23 
    2425SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = undefined;
    2526PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Member ApplePayRequest.countryCode is required and must be an instance of DOMString.
    2627
    27 
    28 SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = 'invalid'; request = new PaymentRequest([paymentMethod], validPaymentDetails())
    29 PASS request.show() rejected promise  with TypeError: "invalid" is not a valid country code..
    30 
    31 SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = ''; request = new PaymentRequest([paymentMethod], validPaymentDetails())
    32 PASS request.show() rejected promise  with TypeError: "" is not a valid country code..
    33 
    34 SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = null; request = new PaymentRequest([paymentMethod], validPaymentDetails())
    35 PASS request.show() rejected promise  with TypeError: "null" is not a valid country code..
    36 
    37 SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = 7; request = new PaymentRequest([paymentMethod], validPaymentDetails())
    38 PASS request.show() rejected promise  with TypeError: "7" is not a valid country code..
     28SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = 'invalid';
     29PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: "invalid" is not a valid country code..
     30
     31SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = '';
     32PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: "" is not a valid country code..
     33
     34SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = null;
     35PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: "null" is not a valid country code..
     36
     37SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = 7;
     38PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: "7" is not a valid country code..
     39
    3940
    4041Testing ApplePayRequest.supportedNetworks
     
    4344PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Member ApplePayRequest.supportedNetworks is required and must be an instance of sequence.
    4445
    45 
    4646SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = '';
    4747PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    4848
    49 
    5049SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = null;
    5150PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    52 
    5351
    5452SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = undefined;
    5553PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Member ApplePayRequest.supportedNetworks is required and must be an instance of sequence.
    5654
    57 
    5855SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = 7;
    5956PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    6057
    61 
    62 SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = []; request = new PaymentRequest([paymentMethod], validPaymentDetails())
    63 PASS request.show() rejected promise  with TypeError: At least one supported network must be provided..
    64 
    65 SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = ['invalidNetwork']; request = new PaymentRequest([paymentMethod], validPaymentDetails())
    66 PASS request.show() rejected promise  with TypeError: "invalidNetwork" is not a valid payment network..
    67 
    68 SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = ['invalidNetwork', 'visa']; request = new PaymentRequest([paymentMethod], validPaymentDetails())
    69 PASS request.show() rejected promise  with TypeError: "invalidNetwork" is not a valid payment network..
     58SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = [];
     59PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: At least one supported network must be provided..
     60
     61SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = ['invalidNetwork'];
     62PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: "invalidNetwork" is not a valid payment network..
     63
     64SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = ['invalidNetwork', 'visa'];
     65PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: "invalidNetwork" is not a valid payment network..
     66
    7067
    7168Testing ApplePayRequest.merchantCapabilities
     
    7471PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Member ApplePayRequest.merchantCapabilities is required and must be an instance of sequence.
    7572
    76 
    7773SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = '';
    7874PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    7975
    80 
    8176SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = null;
    8277PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    83 
    8478
    8579SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = undefined;
    8680PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Member ApplePayRequest.merchantCapabilities is required and must be an instance of sequence.
    8781
    88 
    8982SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = 7;
    9083PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    9184
    92 
    9385SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = ['invalidCapability'];
    9486PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    9587
    96 
    9788SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = ['invalidCapability', 'supports3DS'];
    9889PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    9990
    100 
    101 SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = []; request = new PaymentRequest([paymentMethod], validPaymentDetails())
    102 PASS request.show() rejected promise  with TypeError: At least one merchant capability must be provided..
     91SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = [];
     92PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: At least one merchant capability must be provided..
     93
    10394
    10495Testing ApplePayRequest.requiredBillingContactFields
     
    10798PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    10899
    109 
    110100SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = null;
    111101PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    112102
    113 
    114103SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = 7;
    115104PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    116105
    117 
    118106SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = { };
    119107PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    120108
    121 
    122109SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = [''];
    123110PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    124111
    125 
    126112SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = [null];
    127113PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    128114
    129 
    130115SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = [undefined];
    131116PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    132117
    133 
    134118SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = [{}];
    135119PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    136120
    137 
    138121SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = ['invalid'];
    139122PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
     
    145128PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    146129
    147 
    148130SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.billingContact = 7;
    149131PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
     
    155137PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    156138
    157 
    158139SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = null;
    159140PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    160141
    161 
    162142SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = 7;
    163143PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Value is not a sequence.
    164144
    165 
    166145SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = { };
    167146PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    168147
    169 
    170148SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = [''];
    171149PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    172150
    173 
    174151SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = [null];
    175152PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    176153
    177 
    178154SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = [undefined];
    179155PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    180156
    181 
    182157SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = [{}];
    183158PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    184159
    185 
    186160SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = ['invalid'];
    187161PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
     
    192166SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.shippingContact = '';
    193167PASS new PaymentRequest([paymentMethod], validPaymentDetails()) threw exception TypeError: Type error.
    194 
    195168
    196169SETUP: paymentMethod = validPaymentMethod(); paymentMethod.data.shippingContact = 7;
     
    230203PASS new PaymentRequest([validPaymentMethod()], paymentDetails) threw exception RangeError: "" is not a valid currency code..
    231204
    232 
    233205SETUP: paymentDetails = validPaymentDetails(); paymentDetails.total = { label: 'label', amount: { currency: 'USD', value:'-10.00'} };
    234206PASS new PaymentRequest([validPaymentMethod()], paymentDetails) threw exception TypeError: Total currency values cannot be negative..
     
    237209PASS request.show() rejected promise  with TypeError: Total amount is too big..
    238210
     211
    239212Testing PaymentDetails.displayItems
    240213
     
    274247SETUP: paymentDetails = validPaymentDetails(); paymentDetails.displayItems = [{ label: 'label', amount: { currency: 'EUR', value: '10.00' } }]; request = new PaymentRequest([validPaymentMethod()], paymentDetails)
    275248PASS request.show() rejected promise  with TypeError: "EUR" does not match the expected currency of "USD". Apple Pay requires all PaymentCurrencyAmounts to use the same currency code..
     249
     250
    276251Testing PaymentDetails.shippingOptions
    277252
     
    311286SETUP: paymentDetails = validPaymentDetails(); paymentDetails.shippingOptions = [{ amount: { currency: 'EUR', value: '10.00' }, id: '', label: '' }]; request = new PaymentRequest([validPaymentMethod()], paymentDetails, {requestShipping: true})
    312287PASS request.show() rejected promise  with TypeError: "EUR" does not match the expected currency of "USD". Apple Pay requires all PaymentCurrencyAmounts to use the same currency code..
     288
     289
    313290Testing PaymentOptions
    314291
  • trunk/LayoutTests/http/tests/ssl/applepay/PaymentRequest.https.html

    r274012 r286452  
    5959    debug("Testing ApplePayRequest.version")
    6060    debug("")
    61     await logAndShouldReject("paymentMethod = validPaymentMethod(); paymentMethod.data.version = 0; request = new PaymentRequest([paymentMethod], validPaymentDetails())", "request.show()")
    62     await logAndShouldReject("paymentMethod = validPaymentMethod(); paymentMethod.data.version = 1000; request = new PaymentRequest([paymentMethod], validPaymentDetails())", "request.show()")
     61    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.version = 0;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
     62    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.version = 1000;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    6363    debug("")
    6464
     
    6666    debug("")
    6767    await logAndShouldThrow("paymentMethod = validPaymentMethod(); delete paymentMethod.data.countryCode;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    68     debug("")
    6968    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = undefined;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    70     debug("")
    71     await logAndShouldReject("paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = 'invalid'; request = new PaymentRequest([paymentMethod], validPaymentDetails())", "request.show()")
    72     debug("")
    73     await logAndShouldReject("paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = ''; request = new PaymentRequest([paymentMethod], validPaymentDetails())", "request.show()")
    74     debug("")
    75     await logAndShouldReject("paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = null; request = new PaymentRequest([paymentMethod], validPaymentDetails())", "request.show()")
    76     debug("")
    77     await logAndShouldReject("paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = 7; request = new PaymentRequest([paymentMethod], validPaymentDetails())", "request.show()")
     69    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = 'invalid';", "new PaymentRequest([paymentMethod], validPaymentDetails())")
     70    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = '';", "new PaymentRequest([paymentMethod], validPaymentDetails())")
     71    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = null;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
     72    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.countryCode = 7;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    7873    debug("")
    7974
     
    8176    debug("")
    8277    await logAndShouldThrow("paymentMethod = validPaymentMethod(); delete paymentMethod.data.supportedNetworks;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    83     debug("")
    8478    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = '';", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    85     debug("")
    8679    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = null;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    87     debug("")
    8880    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = undefined;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    89     debug("")
    9081    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = 7;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    91     debug("")
    92     await logAndShouldReject("paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = []; request = new PaymentRequest([paymentMethod], validPaymentDetails())", "request.show()")
    93     debug("")
    94     await logAndShouldReject("paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = ['invalidNetwork']; request = new PaymentRequest([paymentMethod], validPaymentDetails())", "request.show()")
    95     debug("")
    96     await logAndShouldReject("paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = ['invalidNetwork', 'visa']; request = new PaymentRequest([paymentMethod], validPaymentDetails())", "request.show()")
     82    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = [];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
     83    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = ['invalidNetwork'];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
     84    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.supportedNetworks = ['invalidNetwork', 'visa'];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    9785    debug("")
    9886
     
    10088    debug("");
    10189    await logAndShouldThrow("paymentMethod = validPaymentMethod(); delete paymentMethod.data.merchantCapabilities;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    102     debug("")
    10390    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = '';", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    104     debug("")
    10591    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = null;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    106     debug("")
    10792    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = undefined;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    108     debug("")
    10993    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = 7;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    110     debug("")
    11194    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = ['invalidCapability'];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    112     debug("")
    11395    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = ['invalidCapability', 'supports3DS'];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    114     debug("")
    115     await logAndShouldReject("paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = []; request = new PaymentRequest([paymentMethod], validPaymentDetails())", "request.show()")
     96    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.merchantCapabilities = [];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    11697    debug("")
    11798
     
    119100    debug("")
    120101    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = '';", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    121     debug("")
    122102    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = null;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    123     debug("")
    124103    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = 7;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    125     debug("")
    126104    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = { };", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    127     debug("")
    128105    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = [''];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    129     debug("")
    130106    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = [null];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    131     debug("")
    132107    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = [undefined];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    133     debug("")
    134108    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = [{}];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    135     debug("")
    136109    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredBillingContactFields = ['invalid'];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    137110    debug("")
     
    140113    debug("")
    141114    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.billingContact = '';", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    142     debug("")
    143115    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.billingContact = 7;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    144116    debug("")
     
    147119    debug("")
    148120    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = '';", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    149     debug("")
    150121    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = null;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    151     debug("")
    152122    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = 7;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    153     debug("")
    154123    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = { };", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    155     debug("")
    156124    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = [''];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    157     debug("")
    158125    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = [null];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    159     debug("")
    160126    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = [undefined];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    161     debug("")
    162127    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = [{}];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    163     debug("")
    164128    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.requiredShippingContactFields = ['invalid'];", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    165129    debug("")
     
    168132    debug("")
    169133    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.shippingContact = '';", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    170     debug("")
    171134    await logAndShouldThrow("paymentMethod = validPaymentMethod(); paymentMethod.data.shippingContact = 7;", "new PaymentRequest([paymentMethod], validPaymentDetails())")
    172135    debug("")
     
    184147    await logAndShouldThrow("paymentDetails = validPaymentDetails(); paymentDetails.total = { label: 'label', amount: 'amount' };", "new PaymentRequest([validPaymentMethod()], paymentDetails)")
    185148    await logAndShouldThrow("paymentDetails = validPaymentDetails(); paymentDetails.total = { label: 'label', amount: { currency: '', value: '0' } };", "new PaymentRequest([validPaymentMethod()], paymentDetails)")
    186     debug("")
    187149    await logAndShouldThrow("paymentDetails = validPaymentDetails(); paymentDetails.total = { label: 'label', amount: { currency: 'USD', value:'-10.00'} };", "new PaymentRequest([validPaymentMethod()], paymentDetails)")
    188150    await logAndShouldReject("paymentDetails = validPaymentDetails(); paymentDetails.total = { label: 'label', amount: { currency: 'USD', value: '10000000000.00' } }; request = new PaymentRequest([validPaymentMethod()], paymentDetails)", "request.show()")
     151    debug("")
    189152    debug("")
    190153
     
    203166    await logAndShouldThrow("paymentDetails = validPaymentDetails(); paymentDetails.displayItems = [{ label: 'label', amount: '10.00', type: 'invalid' }];", "new PaymentRequest([validPaymentMethod()], paymentDetails)")
    204167    await logAndShouldReject("paymentDetails = validPaymentDetails(); paymentDetails.displayItems = [{ label: 'label', amount: { currency: 'EUR', value: '10.00' } }]; request = new PaymentRequest([validPaymentMethod()], paymentDetails)", "request.show()")
     168    debug("")
     169    debug("")
    205170
    206171    debug("Testing PaymentDetails.shippingOptions")
     
    218183    await logAndShouldThrow("paymentDetails = validPaymentDetails(); paymentDetails.shippingOptions = [{ amount: '-1', detail: '', identifier: '', label: '' }];", "new PaymentRequest([validPaymentMethod()], paymentDetails, {requestShipping: true})")
    219184    await logAndShouldReject("paymentDetails = validPaymentDetails(); paymentDetails.shippingOptions = [{ amount: { currency: 'EUR', value: '10.00' }, id: '', label: '' }]; request = new PaymentRequest([validPaymentMethod()], paymentDetails, {requestShipping: true})", "request.show()")
     185    debug("")
     186    debug("")
    220187
    221188    debug("Testing PaymentOptions")
  • trunk/Source/WebCore/ChangeLog

    r286449 r286452  
     12021-12-02  Devin Rousso  <drousso@apple.com>
     2
     3        [Payment Request] Validate payment method data on construction
     4        https://bugs.webkit.org/show_bug.cgi?id=233292
     5        <rdar://problem/85736007>
     6
     7        Reviewed by Andy Estes.
     8
     9        This will allow developers to replace any `ApplePaySession.supportsVersion` check(s) by
     10        attempting to create a `PaymentRequest` object with data specific to Apple Pay. This is
     11        actually an improvement, because it'll also allow developers to catch most errors in that
     12        data earlier, as previously any errors would only be thrown when `show()` is called.
     13
     14        Spec: <https://github.com/w3c/payment-request/pull/976>
     15
     16        Test: http/tests/ssl/applepay/PaymentRequest.https.html
     17
     18        * Modules/paymentrequest/PaymentRequest.cpp:
     19        (WebCore::PaymentRequest::show):
     20        * Modules/paymentrequest/PaymentHandler.h:
     21        Adjust `PaymentHandler::convertData` to take a `Document` so that additional validation
     22        can be performed on the provided payment method(s) (e.g. check the Apple Pay version).
     23
     24        * Modules/applepay/PaymentRequestValidator.h:
     25        * Modules/applepay/PaymentRequestValidator.mm:
     26        (WebCore::PaymentRequestValidator::validate):
     27        Allow callers to choose what fields to validate. This is needed because when the
     28        `ApplePaySessionPaymentRequest` created/converted when validating the payment method data in
     29        the `PaymentRequest` constructor isn't able to get fields from the `PaymentRequest` as it
     30        hasn't been created yet.
     31
     32        * Modules/applepay/ApplePaySession.cpp:
     33        (WebCore::convertAndValidate):
     34        * Modules/applepay/paymentrequest/ApplePayPaymentHandler.h:
     35        * Modules/applepay/paymentrequest/ApplePayPaymentHandler.cpp:
     36        (WebCore::convertAndValidateApplePayRequest):
     37        (WebCore::ApplePayPaymentHandler::convertData):
     38        (WebCore::ApplePayPaymentHandler::show):
     39        * Modules/applepay-ams-ui/ApplePayAMSUIPaymentHandler.h:
     40        * Modules/applepay-ams-ui/ApplePayAMSUIPaymentHandler.cpp:
     41        (WebCore::convertAndValidateApplePayAMSUIRequest):
     42        (WebCore::ApplePayAMSUIPaymentHandler::convertData):
     43
    1442021-12-02  Brandon Stewart  <brandonstewart@apple.com>
    245
  • trunk/Source/WebCore/Modules/applepay-ams-ui/ApplePayAMSUIPaymentHandler.cpp

    r285444 r286452  
    3737namespace WebCore {
    3838
    39 static ExceptionOr<ApplePayAMSUIRequest> convertAndValidateApplePayAMSUIRequest(ScriptExecutionContext& context, JSC::JSValue data)
     39static ExceptionOr<ApplePayAMSUIRequest> convertAndValidateApplePayAMSUIRequest(Document& document, JSC::JSValue data)
    4040{
    4141    if (data.isEmpty())
    4242        return Exception { TypeError, "Missing payment method data." };
    4343
    44     auto throwScope = DECLARE_THROW_SCOPE(context.vm());
    45     auto applePayAMSUIRequest = convertDictionary<ApplePayAMSUIRequest>(*context.globalObject(), data);
     44    auto throwScope = DECLARE_THROW_SCOPE(document.vm());
     45    auto applePayAMSUIRequest = convertDictionary<ApplePayAMSUIRequest>(*document.globalObject(), data);
    4646    if (throwScope.exception())
    4747        return Exception { ExistingExceptionError };
     
    119119}
    120120
    121 ExceptionOr<void> ApplePayAMSUIPaymentHandler::convertData(JSC::JSValue data)
     121ExceptionOr<void> ApplePayAMSUIPaymentHandler::convertData(Document& document, JSC::JSValue data)
    122122{
    123     auto requestOrException = convertAndValidateApplePayAMSUIRequest(*scriptExecutionContext(), data);
     123    auto requestOrException = convertAndValidateApplePayAMSUIRequest(document, data);
    124124    if (requestOrException.hasException())
    125125        return requestOrException.releaseException();
  • trunk/Source/WebCore/Modules/applepay-ams-ui/ApplePayAMSUIPaymentHandler.h

    r285444 r286452  
    6969
    7070    // PaymentHandler
    71     ExceptionOr<void> convertData(JSC::JSValue) final;
     71    ExceptionOr<void> convertData(Document&, JSC::JSValue) final;
    7272    ExceptionOr<void> show(Document&) final;
    7373    bool canAbortSession() final { return false; }
  • trunk/Source/WebCore/Modules/applepay/ApplePaySession.cpp

    r282464 r286452  
    180180
    181181    // FIXME: Merge this validation into the validation we are doing above.
    182     auto validatedPaymentRequest = PaymentRequestValidator::validate(result);
     182    constexpr OptionSet fieldsToValidate = {
     183        PaymentRequestValidator::Field::MerchantCapabilities,
     184        PaymentRequestValidator::Field::SupportedNetworks,
     185        PaymentRequestValidator::Field::CountryCode,
     186        PaymentRequestValidator::Field::CurrencyCode,
     187        PaymentRequestValidator::Field::Total,
     188        PaymentRequestValidator::Field::ShippingMethods,
     189    };
     190    auto validatedPaymentRequest = PaymentRequestValidator::validate(result, fieldsToValidate);
    183191    if (validatedPaymentRequest.hasException())
    184192        return validatedPaymentRequest.releaseException();
  • trunk/Source/WebCore/Modules/applepay/PaymentRequestValidator.h

    r273113 r286452  
    3030#include "ApplePaySessionPaymentRequest.h"
    3131#include "ExceptionOr.h"
     32#include <wtf/OptionSet.h>
    3233
    3334namespace WebCore {
     
    3536class PaymentRequestValidator {
    3637public:
    37     static ExceptionOr<void> validate(const ApplePaySessionPaymentRequest&);
     38    enum class Field : uint8_t {
     39        MerchantCapabilities = 1 << 0,
     40        SupportedNetworks = 1 << 1,
     41        CountryCode = 1 << 2,
     42        CurrencyCode = 1 << 3,
     43        Total = 1 << 4,
     44        ShippingMethods = 1 << 5,
     45    };
     46
     47    static ExceptionOr<void> validate(const ApplePaySessionPaymentRequest&, OptionSet<PaymentRequestValidator::Field>);
    3848    static ExceptionOr<void> validateTotal(const ApplePayLineItem&);
    3949};
  • trunk/Source/WebCore/Modules/applepay/PaymentRequestValidator.mm

    r275084 r286452  
    4444static ExceptionOr<void> validateShippingMethod(const ApplePayShippingMethod&);
    4545
    46 ExceptionOr<void> PaymentRequestValidator::validate(const ApplePaySessionPaymentRequest& paymentRequest)
     46ExceptionOr<void> PaymentRequestValidator::validate(const ApplePaySessionPaymentRequest& paymentRequest, OptionSet<Field> fieldsToValidate)
    4747{
    48     auto validatedCountryCode = validateCountryCode(paymentRequest.countryCode());
    49     if (validatedCountryCode.hasException())
    50         return validatedCountryCode.releaseException();
    51 
    52     auto validatedCurrencyCode = validateCurrencyCode(paymentRequest.currencyCode());
    53     if (validatedCurrencyCode.hasException())
    54         return validatedCurrencyCode.releaseException();
    55 
    56     auto validatedSupportedNetworks = validateSupportedNetworks(paymentRequest.supportedNetworks());
    57     if (validatedSupportedNetworks.hasException())
    58         return validatedSupportedNetworks.releaseException();
    59 
    60     auto validatedMerchantCapabilities = validateMerchantCapabilities(paymentRequest.merchantCapabilities());
    61     if (validatedMerchantCapabilities.hasException())
    62         return validatedMerchantCapabilities.releaseException();
    63 
    64     auto validatedTotal = validateTotal(paymentRequest.total());
    65     if (validatedTotal.hasException())
    66         return validatedTotal.releaseException();
    67 
    68     auto validatedShippingMethods = validateShippingMethods(paymentRequest.shippingMethods());
    69     if (validatedShippingMethods.hasException())
    70         return validatedShippingMethods.releaseException();
    71 
    72     for (auto& countryCode : paymentRequest.supportedCountries()) {
    73         auto validatedCountryCode = validateCountryCode(countryCode);
     48    if (fieldsToValidate.contains(Field::CountryCode)) {
     49        auto validatedCountryCode = validateCountryCode(paymentRequest.countryCode());
    7450        if (validatedCountryCode.hasException())
    7551            return validatedCountryCode.releaseException();
     52    }
     53
     54    if (fieldsToValidate.contains(Field::CurrencyCode)) {
     55        auto validatedCurrencyCode = validateCurrencyCode(paymentRequest.currencyCode());
     56        if (validatedCurrencyCode.hasException())
     57            return validatedCurrencyCode.releaseException();
     58    }
     59
     60    if (fieldsToValidate.contains(Field::SupportedNetworks)) {
     61        auto validatedSupportedNetworks = validateSupportedNetworks(paymentRequest.supportedNetworks());
     62        if (validatedSupportedNetworks.hasException())
     63            return validatedSupportedNetworks.releaseException();
     64    }
     65
     66    if (fieldsToValidate.contains(Field::MerchantCapabilities)) {
     67        auto validatedMerchantCapabilities = validateMerchantCapabilities(paymentRequest.merchantCapabilities());
     68        if (validatedMerchantCapabilities.hasException())
     69            return validatedMerchantCapabilities.releaseException();
     70    }
     71
     72    if (fieldsToValidate.contains(Field::Total)) {
     73        auto validatedTotal = validateTotal(paymentRequest.total());
     74        if (validatedTotal.hasException())
     75            return validatedTotal.releaseException();
     76    }
     77
     78    if (fieldsToValidate.contains(Field::ShippingMethods)) {
     79        auto validatedShippingMethods = validateShippingMethods(paymentRequest.shippingMethods());
     80        if (validatedShippingMethods.hasException())
     81            return validatedShippingMethods.releaseException();
     82    }
     83
     84    if (fieldsToValidate.contains(Field::CountryCode)) {
     85        for (auto& countryCode : paymentRequest.supportedCountries()) {
     86            auto validatedCountryCode = validateCountryCode(countryCode);
     87            if (validatedCountryCode.hasException())
     88                return validatedCountryCode.releaseException();
     89        }
    7690    }
    7791
  • trunk/Source/WebCore/Modules/applepay/paymentrequest/ApplePayPaymentHandler.cpp

    r285521 r286452  
    7979namespace WebCore {
    8080
    81 static ExceptionOr<ApplePayRequest> convertAndValidateApplePayRequest(ScriptExecutionContext& context, JSC::JSValue data)
     81static inline PaymentCoordinator& paymentCoordinator(Document& document)
     82{
     83    ASSERT(document.page());
     84    return document.page()->paymentCoordinator();
     85}
     86
     87static ExceptionOr<ApplePayRequest> convertAndValidateApplePayRequest(Document& document, JSC::JSValue data)
    8288{
    8389    if (data.isEmpty())
    8490        return Exception { TypeError, "Missing payment method data." };
    8591
    86     auto throwScope = DECLARE_THROW_SCOPE(context.vm());
    87     auto applePayRequest = convertDictionary<ApplePayRequest>(*context.globalObject(), data);
     92    auto throwScope = DECLARE_THROW_SCOPE(document.vm());
     93    auto applePayRequest = convertDictionary<ApplePayRequest>(*document.globalObject(), data);
    8894    if (throwScope.exception())
    8995        return Exception { ExistingExceptionError };
     96
     97    auto validatedRequest = convertAndValidate(document, applePayRequest.version, applePayRequest, paymentCoordinator(document));
     98    if (validatedRequest.hasException())
     99        return validatedRequest.releaseException();
     100
     101    constexpr OptionSet fieldsToValidate = {
     102        PaymentRequestValidator::Field::MerchantCapabilities,
     103        PaymentRequestValidator::Field::SupportedNetworks,
     104        PaymentRequestValidator::Field::CountryCode,
     105    };
     106    auto exception = PaymentRequestValidator::validate(validatedRequest.releaseReturnValue(), fieldsToValidate);
     107    if (exception.hasException())
     108        return exception.releaseException();
    90109
    91110    return WTFMove(applePayRequest);
     
    108127    auto& url = std::get<URL>(identifier);
    109128    return url.host() == "apple.com" && url.path() == "/apple-pay";
    110 }
    111 
    112 static inline PaymentCoordinator& paymentCoordinator(Document& document)
    113 {
    114     ASSERT(document.page());
    115     return document.page()->paymentCoordinator();
    116129}
    117130
     
    201214}
    202215
    203 ExceptionOr<void> ApplePayPaymentHandler::convertData(JSC::JSValue data)
    204 {
    205     auto requestOrException = convertAndValidateApplePayRequest(*scriptExecutionContext(), data);
     216ExceptionOr<void> ApplePayPaymentHandler::convertData(Document& document, JSC::JSValue data)
     217{
     218    auto requestOrException = convertAndValidateApplePayRequest(document, data);
    206219    if (requestOrException.hasException())
    207220        return requestOrException.releaseException();
     
    270283        merge(request, WTFMove(std::get<1>(*modifierData)));
    271284
    272     auto exception = PaymentRequestValidator::validate(request);
     285    constexpr OptionSet fieldsToValidate = {
     286        PaymentRequestValidator::Field::CurrencyCode,
     287        PaymentRequestValidator::Field::Total,
     288        PaymentRequestValidator::Field::ShippingMethods,
     289    };
     290    auto exception = PaymentRequestValidator::validate(request, fieldsToValidate);
    273291    if (exception.hasException())
    274292        return exception.releaseException();
  • trunk/Source/WebCore/Modules/applepay/paymentrequest/ApplePayPaymentHandler.h

    r285444 r286452  
    7272
    7373    // PaymentHandler
    74     ExceptionOr<void> convertData(JSC::JSValue) final;
     74    ExceptionOr<void> convertData(Document&, JSC::JSValue) final;
    7575    ExceptionOr<void> show(Document&) final;
    7676    bool canAbortSession() final { return true; }
  • trunk/Source/WebCore/Modules/paymentrequest/PaymentHandler.h

    r285444 r286452  
    5151    static bool hasActiveSession(Document&);
    5252
    53     virtual ExceptionOr<void> convertData(JSC::JSValue) = 0;
     53    virtual ExceptionOr<void> convertData(Document&, JSC::JSValue) = 0;
    5454    virtual ExceptionOr<void> show(Document&) = 0;
    5555    virtual bool canAbortSession() = 0;
  • trunk/Source/WebCore/Modules/paymentrequest/PaymentRequest.cpp

    r285521 r286452  
    411411            continue;
    412412
    413         auto result = handler->convertData(data.releaseReturnValue());
     413        auto result = handler->convertData(document, data.releaseReturnValue());
    414414        if (result.hasException()) {
    415415            settleShowPromise(result.releaseException());
Note: See TracChangeset for help on using the changeset viewer.