Changeset 271703 in webkit


Ignore:
Timestamp:
Jan 21, 2021 11:37:52 AM (18 months ago)
Author:
Devin Rousso
Message:

Payment Request API - response.shippingOption is always null
https://bugs.webkit.org/show_bug.cgi?id=220566
<rdar://problem/73204387>

Reviewed by Andy Estes.

Source/WebCore:

The Payment Request API spec indicates that when updating a PaymentRequest's details [1]
we should be checking for the existence of displayItems, shippingOptions (but only if
requestShipping was specified), and modifiers before overriding any corresponding data
on the PaymentRequest itself. This means that calls to updateWith only need to provide
the data that needs to be changed (e.g. the total) rather than data for the whole payment.

[1]: https://www.w3.org/TR/payment-request/#dfn-update-a-paymentrequest-s-details-algorithm

Tests: http/tests/inspector/paymentrequest/payment-request-internal-properties.https.html

http/tests/paymentrequest/updateWith-displayItems.https.html
http/tests/paymentrequest/updateWith-modifiers.https.html
http/tests/paymentrequest/updateWith-shippingOptions.https.html
http/tests/paymentrequest/updateWith-total.https.html

  • Modules/paymentrequest/PaymentDetailsBase.h:

Make every member Optional so we can distinguish between "provided an empty array" and
"did not provide the property at all".

  • Modules/paymentrequest/PaymentRequest.cpp:

(WebCore::checkAndCanonicalizeDetails):
(WebCore::PaymentRequest::create):
(WebCore::PaymentRequest::settleDetailsPromise):

  • Modules/applepay/paymentrequest/ApplePayPaymentHandler.cpp:

(WebCore::ApplePayPaymentHandler::show):
(WebCore::ApplePayPaymentHandler::computeShippingMethods):
(WebCore::ApplePayPaymentHandler::computeTotalAndLineItems const):
(WebCore::ApplePayPaymentHandler::computeErrors const):
Add checks before accessing displayItems, shippingOptions, and modifiers.
Match the described behavior of the spec where shippingOptions (but only if
requestShipping was specified) and modifiers are set to an empty array if not provided
when constructing the PaymentRequest.

  • inspector/WebInjectedScriptHost.cpp:

(WebCore::objectForPaymentDetails):
Don't create internal properties for displayItems, shippingOptions, and modifiers
unless they are actually provided for the same reason as above.

LayoutTests:

  • http/tests/inspector/paymentrequest/payment-request-internal-properties.https-expected.txt:
  • http/tests/paymentrequest/updateWith-displayItems.https.html: Added.
  • http/tests/paymentrequest/updateWith-displayItems.https-expected.txt: Added.
  • http/tests/paymentrequest/updateWith-modifiers.https.html: Added.
  • http/tests/paymentrequest/updateWith-modifiers.https-expected.txt: Added.
  • http/tests/paymentrequest/updateWith-shippingOptions.https.html: Added.
  • http/tests/paymentrequest/updateWith-shippingOptions.https-expected.txt: Added.
  • http/tests/paymentrequest/updateWith-total.https.html: Added.
  • http/tests/paymentrequest/updateWith-total.https-expected.txt: Added.
Location:
trunk
Files:
8 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r271702 r271703  
     12021-01-21  Devin Rousso  <drousso@apple.com>
     2
     3        Payment Request API - response.shippingOption is always null
     4        https://bugs.webkit.org/show_bug.cgi?id=220566
     5        <rdar://problem/73204387>
     6
     7        Reviewed by Andy Estes.
     8
     9        * http/tests/inspector/paymentrequest/payment-request-internal-properties.https-expected.txt:
     10        * http/tests/paymentrequest/updateWith-displayItems.https.html: Added.
     11        * http/tests/paymentrequest/updateWith-displayItems.https-expected.txt: Added.
     12        * http/tests/paymentrequest/updateWith-modifiers.https.html: Added.
     13        * http/tests/paymentrequest/updateWith-modifiers.https-expected.txt: Added.
     14        * http/tests/paymentrequest/updateWith-shippingOptions.https.html: Added.
     15        * http/tests/paymentrequest/updateWith-shippingOptions.https-expected.txt: Added.
     16        * http/tests/paymentrequest/updateWith-total.https.html: Added.
     17        * http/tests/paymentrequest/updateWith-total.https-expected.txt: Added.
     18
    1192021-01-21  Rini Patel  <rini_patel@apple.com>
    220
  • trunk/LayoutTests/http/tests/inspector/paymentrequest/payment-request-internal-properties.https-expected.txt

    r232155 r271703  
    9797    "pending": false
    9898  },
    99   "displayItems": [],
    100   "shippingOptions": [],
    10199  "modifiers": []
    102100}
     
    121119    "pending": false
    122120  },
    123   "displayItems": [],
    124   "shippingOptions": [],
    125121  "modifiers": []
    126122}
     
    145141    "pending": false
    146142  },
    147   "displayItems": [],
    148   "shippingOptions": [],
    149143  "modifiers": []
    150144}
  • trunk/Source/WebCore/ChangeLog

    r271701 r271703  
     12021-01-21  Devin Rousso  <drousso@apple.com>
     2
     3        Payment Request API - response.shippingOption is always null
     4        https://bugs.webkit.org/show_bug.cgi?id=220566
     5        <rdar://problem/73204387>
     6
     7        Reviewed by Andy Estes.
     8
     9        The Payment Request API spec indicates that when updating a `PaymentRequest`'s details [1]
     10        we should be checking for the existence of `displayItems`, `shippingOptions` (but only if
     11        `requestShipping` was specified), and `modifiers` before overriding any corresponding data
     12        on the `PaymentRequest` itself. This means that calls to `updateWith` only need to provide
     13        the data that needs to be changed (e.g. the `total`) rather than data for the whole payment.
     14
     15        [1]: https://www.w3.org/TR/payment-request/#dfn-update-a-paymentrequest-s-details-algorithm
     16
     17        Tests: http/tests/inspector/paymentrequest/payment-request-internal-properties.https.html
     18               http/tests/paymentrequest/updateWith-displayItems.https.html
     19               http/tests/paymentrequest/updateWith-modifiers.https.html
     20               http/tests/paymentrequest/updateWith-shippingOptions.https.html
     21               http/tests/paymentrequest/updateWith-total.https.html
     22
     23        * Modules/paymentrequest/PaymentDetailsBase.h:
     24        Make every member `Optional` so we can distinguish between "provided an empty array" and
     25        "did not provide the property at all".
     26
     27        * Modules/paymentrequest/PaymentRequest.cpp:
     28        (WebCore::checkAndCanonicalizeDetails):
     29        (WebCore::PaymentRequest::create):
     30        (WebCore::PaymentRequest::settleDetailsPromise):
     31        * Modules/applepay/paymentrequest/ApplePayPaymentHandler.cpp:
     32        (WebCore::ApplePayPaymentHandler::show):
     33        (WebCore::ApplePayPaymentHandler::computeShippingMethods):
     34        (WebCore::ApplePayPaymentHandler::computeTotalAndLineItems const):
     35        (WebCore::ApplePayPaymentHandler::computeErrors const):
     36        Add checks before accessing `displayItems`, `shippingOptions`, and `modifiers`.
     37        Match the described behavior of the spec where `shippingOptions` (but only if
     38        `requestShipping` was specified) and `modifiers` are set to an empty array if not provided
     39        when constructing the `PaymentRequest`.
     40
     41        * inspector/WebInjectedScriptHost.cpp:
     42        (WebCore::objectForPaymentDetails):
     43        Don't create internal properties for `displayItems`, `shippingOptions`, and `modifiers`
     44        unless they are actually provided for the same reason as above.
     45
    1462021-01-21  Simon Fraser  <simon.fraser@apple.com>
    247
  • trunk/Source/WebCore/Modules/applepay/paymentrequest/ApplePayPaymentHandler.cpp

    r268700 r271703  
    219219    request.setRequester(ApplePaySessionPaymentRequest::Requester::PaymentRequest);
    220220
    221     String expectedCurrency = m_paymentRequest->paymentDetails().total.amount.currency;
     221    auto& details = m_paymentRequest->paymentDetails();
     222
     223    String expectedCurrency = details.total.amount.currency;
    222224    request.setCurrencyCode(expectedCurrency);
    223225
    224     auto total = convertAndValidate(m_paymentRequest->paymentDetails().total, expectedCurrency);
     226    auto total = convertAndValidate(details.total, expectedCurrency);
    225227    ASSERT(!total.hasException());
    226228    request.setTotal(total.releaseReturnValue());
    227229
    228     auto convertedLineItems = convertAndValidate(m_paymentRequest->paymentDetails().displayItems, expectedCurrency);
    229     if (convertedLineItems.hasException())
    230         return convertedLineItems.releaseException();
    231     request.setLineItems(convertedLineItems.releaseReturnValue());
     230    if (details.displayItems) {
     231        auto convertedLineItems = convertAndValidate(*details.displayItems, expectedCurrency);
     232        if (convertedLineItems.hasException())
     233            return convertedLineItems.releaseException();
     234        request.setLineItems(convertedLineItems.releaseReturnValue());
     235    }
    232236
    233237    mergePaymentOptions(m_paymentRequest->paymentOptions(), request);
     
    261265{
    262266    auto& details = m_paymentRequest->paymentDetails();
    263     auto& currency = details.total.amount.currency;
    264     auto& shippingOptions = details.shippingOptions;
    265 
    266     Vector<ApplePaySessionPaymentRequest::ShippingMethod> shippingMethods;
    267     shippingMethods.reserveInitialCapacity(shippingOptions.size());
    268 
    269     for (auto& shippingOption : shippingOptions) {
    270         auto shippingMethod = convertAndValidate(shippingOption, currency);
    271         if (shippingMethod.hasException())
    272             return shippingMethod.releaseException();
    273         shippingMethods.uncheckedAppend(shippingMethod.releaseReturnValue());
    274     }
    275 
    276     return WTFMove(shippingMethods);
     267
     268    Vector<ApplePaySessionPaymentRequest::ShippingMethod> shippingOptions;
     269    if (details.shippingOptions) {
     270        auto& currency = details.total.amount.currency;
     271
     272        shippingOptions.reserveInitialCapacity(details.shippingOptions->size());
     273        for (auto& shippingOption : *details.shippingOptions) {
     274            auto shippingMethod = convertAndValidate(shippingOption, currency);
     275            if (shippingMethod.hasException())
     276                return shippingMethod.releaseException();
     277            shippingOptions.uncheckedAppend(shippingMethod.releaseReturnValue());
     278        }
     279    }
     280
     281    return WTFMove(shippingOptions);
    277282}
    278283
     
    287292    auto total = convertedTotal.releaseReturnValue();
    288293
    289     auto convertedLineItems = convertAndValidate(details.displayItems, currency);
    290     if (convertedLineItems.hasException())
    291         return convertedLineItems.releaseException();
    292     auto lineItems = convertedLineItems.releaseReturnValue();
     294    Vector<ApplePaySessionPaymentRequest::LineItem> lineItems;
     295    if (details.displayItems) {
     296        auto convertedLineItems = convertAndValidate(*details.displayItems, currency);
     297        if (convertedLineItems.hasException())
     298            return convertedLineItems.releaseException();
     299        lineItems = convertedLineItems.releaseReturnValue();
     300    }
    293301
    294302    if (!m_selectedPaymentMethodType)
    295303        return ApplePaySessionPaymentRequest::TotalAndLineItems { WTFMove(total), WTFMove(lineItems) };
    296304
    297     auto& modifiers = details.modifiers;
    298     auto& serializedModifierData = m_paymentRequest->serializedModifierData();
    299     ASSERT(modifiers.size() == serializedModifierData.size());
    300     for (size_t i = 0; i < modifiers.size(); ++i) {
    301         auto convertedIdentifier = convertAndValidatePaymentMethodIdentifier(modifiers[i].supportedMethods);
    302         if (!convertedIdentifier || !handlesIdentifier(*convertedIdentifier))
    303             continue;
    304 
    305         if (serializedModifierData[i].isEmpty())
    306             continue;
    307 
    308         auto& lexicalGlobalObject = *document().globalObject();
    309         auto scope = DECLARE_THROW_SCOPE(lexicalGlobalObject.vm());
    310         JSC::JSValue data;
    311         {
    312             auto lock = JSC::JSLockHolder { &lexicalGlobalObject };
    313             data = JSONParse(&lexicalGlobalObject, serializedModifierData[i]);
     305    if (details.modifiers) {
     306        auto& serializedModifierData = m_paymentRequest->serializedModifierData();
     307        ASSERT(details.modifiers->size() == serializedModifierData.size());
     308        for (size_t i = 0; i < details.modifiers->size(); ++i) {
     309            auto& modifier = details.modifiers->at(i);
     310
     311            auto convertedIdentifier = convertAndValidatePaymentMethodIdentifier(modifier.supportedMethods);
     312            if (!convertedIdentifier || !handlesIdentifier(*convertedIdentifier))
     313                continue;
     314
     315            if (serializedModifierData[i].isEmpty())
     316                continue;
     317
     318            auto& lexicalGlobalObject = *document().globalObject();
     319            auto scope = DECLARE_THROW_SCOPE(lexicalGlobalObject.vm());
     320            JSC::JSValue data;
     321            {
     322                auto lock = JSC::JSLockHolder { &lexicalGlobalObject };
     323                data = JSONParse(&lexicalGlobalObject, serializedModifierData[i]);
     324                if (scope.exception())
     325                    return Exception { ExistingExceptionError };
     326            }
     327
     328            auto applePayModifier = convertDictionary<ApplePayModifier>(lexicalGlobalObject, WTFMove(data));
    314329            if (scope.exception())
    315330                return Exception { ExistingExceptionError };
     331
     332            if (applePayModifier.paymentMethodType != *m_selectedPaymentMethodType)
     333                continue;
     334
     335            if (modifier.total) {
     336                auto totalOverride = convertAndValidate(*modifier.total, currency);
     337                if (totalOverride.hasException())
     338                    return totalOverride.releaseException();
     339                total = totalOverride.releaseReturnValue();
     340            }
     341
     342            auto additionalDisplayItems = convertAndValidate(modifier.additionalDisplayItems, currency);
     343            if (additionalDisplayItems.hasException())
     344                return additionalDisplayItems.releaseException();
     345            lineItems.appendVector(additionalDisplayItems.releaseReturnValue());
     346            break;
    316347        }
    317 
    318         auto applePayModifier = convertDictionary<ApplePayModifier>(lexicalGlobalObject, WTFMove(data));
    319         if (scope.exception())
    320             return Exception { ExistingExceptionError };
    321 
    322         if (applePayModifier.paymentMethodType != *m_selectedPaymentMethodType)
    323             continue;
    324 
    325         if (modifiers[i].total) {
    326             auto totalOverride = convertAndValidate(*modifiers[i].total, currency);
    327             if (totalOverride.hasException())
    328                 return totalOverride.releaseException();
    329             total = totalOverride.releaseReturnValue();
    330         }
    331 
    332         auto additionalDisplayItems = convertAndValidate(modifiers[i].additionalDisplayItems, currency);
    333         if (additionalDisplayItems.hasException())
    334             return additionalDisplayItems.releaseException();
    335         lineItems.appendVector(additionalDisplayItems.releaseReturnValue());
    336         break;
    337348    }
    338349
     
    350361    Vector<PaymentError> errors;
    351362
    352     if (m_paymentRequest->paymentDetails().shippingOptions.isEmpty())
     363    auto& details = m_paymentRequest->paymentDetails();
     364
     365    if (!details.shippingOptions || details.shippingOptions->isEmpty())
    353366        computeAddressErrors(WTFMove(error), WTFMove(addressErrors), errors);
    354367
  • trunk/Source/WebCore/Modules/paymentrequest/PaymentDetailsBase.h

    r220955 r271703  
    3636
    3737struct PaymentDetailsBase {
    38     Vector<PaymentItem> displayItems;
    39     Vector<PaymentShippingOption> shippingOptions;
    40     Vector<PaymentDetailsModifier> modifiers;
     38    Optional<Vector<PaymentItem>> displayItems;
     39    Optional<Vector<PaymentShippingOption>> shippingOptions;
     40    Optional<Vector<PaymentDetailsModifier>> modifiers;
    4141};
    4242
  • trunk/Source/WebCore/Modules/paymentrequest/PaymentRequest.cpp

    r271615 r271703  
    241241}
    242242
    243 enum class ShouldValidatePaymentMethodIdentifier {
     243enum class IsUpdate {
    244244    No,
    245245    Yes,
    246246};
    247247
    248 static ExceptionOr<std::tuple<String, Vector<String>>> checkAndCanonicalizeDetails(JSC::JSGlobalObject& execState, PaymentDetailsBase& details, bool requestShipping, ShouldValidatePaymentMethodIdentifier shouldValidatePaymentMethodIdentifier)
    249 {
    250     for (auto& item : details.displayItems) {
    251         auto exception = checkAndCanonicalizeAmount(item.amount);
    252         if (exception.hasException())
    253             return exception.releaseException();
    254     }
    255 
    256     String selectedShippingOption;
    257     if (requestShipping) {
    258         HashSet<String> seenShippingOptionIDs;
    259         for (auto& shippingOption : details.shippingOptions) {
    260             auto exception = checkAndCanonicalizeAmount(shippingOption.amount);
    261             if (exception.hasException())
    262                 return exception.releaseException();
    263 
    264             auto addResult = seenShippingOptionIDs.add(shippingOption.id);
    265             if (!addResult.isNewEntry)
    266                 return Exception { TypeError, "Shipping option IDs must be unique." };
    267 
    268             if (shippingOption.selected)
    269                 selectedShippingOption = shippingOption.id;
    270         }
    271     }
    272 
    273     Vector<String> serializedModifierData;
    274     serializedModifierData.reserveInitialCapacity(details.modifiers.size());
    275     for (auto& modifier : details.modifiers) {
    276         if (shouldValidatePaymentMethodIdentifier == ShouldValidatePaymentMethodIdentifier::Yes) {
    277             auto paymentMethodIdentifier = convertAndValidatePaymentMethodIdentifier(modifier.supportedMethods);
    278             if (!paymentMethodIdentifier)
    279                 return Exception { RangeError, makeString('"', modifier.supportedMethods, "\" is an invalid payment method identifier.") };
    280         }
    281 
    282         if (modifier.total) {
    283             auto exception = checkAndCanonicalizeTotal(modifier.total->amount);
    284             if (exception.hasException())
    285                 return exception.releaseException();
    286         }
    287 
    288         for (auto& item : modifier.additionalDisplayItems) {
     248static ExceptionOr<std::tuple<String, Vector<String>>> checkAndCanonicalizeDetails(JSC::JSGlobalObject& execState, PaymentDetailsBase& details, bool requestShipping, IsUpdate isUpdate)
     249{
     250    if (details.displayItems) {
     251        for (auto& item : *details.displayItems) {
    289252            auto exception = checkAndCanonicalizeAmount(item.amount);
    290253            if (exception.hasException())
    291254                return exception.releaseException();
    292255        }
    293 
    294         String serializedData;
    295         if (modifier.data) {
    296             auto scope = DECLARE_THROW_SCOPE(execState.vm());
    297             serializedData = JSONStringify(&execState, modifier.data.get(), 0);
    298             if (scope.exception())
    299                 return Exception { ExistingExceptionError };
    300             modifier.data.clear();
    301         }
    302         serializedModifierData.uncheckedAppend(WTFMove(serializedData));
    303     }
     256    }
     257
     258    String selectedShippingOption;
     259    if (requestShipping) {
     260        if (details.shippingOptions) {
     261            HashSet<String> seenShippingOptionIDs;
     262            for (auto& shippingOption : *details.shippingOptions) {
     263                auto exception = checkAndCanonicalizeAmount(shippingOption.amount);
     264                if (exception.hasException())
     265                    return exception.releaseException();
     266
     267                auto addResult = seenShippingOptionIDs.add(shippingOption.id);
     268                if (!addResult.isNewEntry)
     269                    return Exception { TypeError, "Shipping option IDs must be unique." };
     270
     271                if (shippingOption.selected)
     272                    selectedShippingOption = shippingOption.id;
     273            }
     274        } else if (isUpdate == IsUpdate::No)
     275            details.shippingOptions = { { } };
     276    }
     277
     278    Vector<String> serializedModifierData;
     279    if (details.modifiers) {
     280        serializedModifierData.reserveInitialCapacity(details.modifiers->size());
     281        for (auto& modifier : *details.modifiers) {
     282            if (isUpdate == IsUpdate::Yes) {
     283                auto paymentMethodIdentifier = convertAndValidatePaymentMethodIdentifier(modifier.supportedMethods);
     284                if (!paymentMethodIdentifier)
     285                    return Exception { RangeError, makeString('"', modifier.supportedMethods, "\" is an invalid payment method identifier.") };
     286            }
     287
     288            if (modifier.total) {
     289                auto exception = checkAndCanonicalizeTotal(modifier.total->amount);
     290                if (exception.hasException())
     291                    return exception.releaseException();
     292            }
     293
     294            for (auto& item : modifier.additionalDisplayItems) {
     295                auto exception = checkAndCanonicalizeAmount(item.amount);
     296                if (exception.hasException())
     297                    return exception.releaseException();
     298            }
     299
     300            String serializedData;
     301            if (modifier.data) {
     302                auto scope = DECLARE_THROW_SCOPE(execState.vm());
     303                serializedData = JSONStringify(&execState, modifier.data.get(), 0);
     304                if (scope.exception())
     305                    return Exception { ExistingExceptionError };
     306                modifier.data.clear();
     307            }
     308            serializedModifierData.uncheckedAppend(WTFMove(serializedData));
     309        }
     310    } else if (isUpdate == IsUpdate::No)
     311        details.modifiers = { { } };
    304312
    305313    return std::make_tuple(WTFMove(selectedShippingOption), WTFMove(serializedModifierData));
     
    358366        return totalResult.releaseException();
    359367
    360     auto detailsResult = checkAndCanonicalizeDetails(*document.globalObject(), details, options.requestShipping, ShouldValidatePaymentMethodIdentifier::No);
     368    auto detailsResult = checkAndCanonicalizeDetails(*document.globalObject(), details, options.requestShipping, IsUpdate::No);
    361369    if (detailsResult.hasException())
    362370        return detailsResult.releaseException();
     
    662670    }
    663671
    664     auto detailsResult = checkAndCanonicalizeDetails(*context.globalObject(), detailsUpdate, m_options.requestShipping, ShouldValidatePaymentMethodIdentifier::Yes);
     672    auto detailsResult = checkAndCanonicalizeDetails(*context.globalObject(), detailsUpdate, m_options.requestShipping, IsUpdate::Yes);
    665673    if (detailsResult.hasException()) {
    666674        abortWithException(detailsResult.releaseException());
     
    670678    auto shippingOptionAndModifierData = detailsResult.releaseReturnValue();
    671679
    672     m_details.total = detailsUpdate.total ? WTFMove(*detailsUpdate.total) : PaymentItem();
    673     m_details.displayItems = WTFMove(detailsUpdate.displayItems);
    674     if (m_options.requestShipping) {
     680    if (detailsUpdate.total)
     681        m_details.total = WTFMove(*detailsUpdate.total);
     682    if (detailsUpdate.displayItems)
     683        m_details.displayItems = WTFMove(*detailsUpdate.displayItems);
     684    if (detailsUpdate.shippingOptions && m_options.requestShipping) {
    675685        m_details.shippingOptions = WTFMove(detailsUpdate.shippingOptions);
    676686        m_shippingOption = WTFMove(std::get<0>(shippingOptionAndModifierData));
    677687    }
    678 
    679     m_details.modifiers = WTFMove(detailsUpdate.modifiers);
    680     m_serializedModifierData = WTFMove(std::get<1>(shippingOptionAndModifierData));
     688    if (detailsUpdate.modifiers) {
     689        m_details.modifiers = WTFMove(*detailsUpdate.modifiers);
     690        m_serializedModifierData = WTFMove(std::get<1>(shippingOptionAndModifierData));
     691    }
    681692
    682693    auto result = activePaymentHandler()->detailsUpdated(reason, WTFMove(detailsUpdate.error), WTFMove(detailsUpdate.shippingAddressErrors), WTFMove(detailsUpdate.payerErrors), detailsUpdate.paymentMethodErrors.get());
  • trunk/Source/WebCore/inspector/WebInjectedScriptHost.cpp

    r261670 r271703  
    127127static JSObject* objectForPaymentDetails(VM& vm, JSGlobalObject* exec, const PaymentDetailsInit& paymentDetails)
    128128{
    129     auto* displayItems = constructEmptyArray(exec, nullptr);
    130     for (unsigned i = 0; i < paymentDetails.displayItems.size(); ++i)
    131         displayItems->putDirectIndex(exec, i, objectForPaymentItem(vm, exec, paymentDetails.displayItems[i]));
    132 
    133     auto* shippingOptions = constructEmptyArray(exec, nullptr);
    134     for (unsigned i = 0; i < paymentDetails.shippingOptions.size(); ++i)
    135         shippingOptions->putDirectIndex(exec, i, objectForPaymentShippingOption(vm, exec, paymentDetails.shippingOptions[i]));
    136 
    137     auto* modifiers = constructEmptyArray(exec, nullptr);
    138     for (unsigned i = 0; i < paymentDetails.modifiers.size(); ++i)
    139         modifiers->putDirectIndex(exec, i, objectForPaymentDetailsModifier(vm, exec, paymentDetails.modifiers[i]));
    140 
    141129    auto* object = constructEmptyObject(exec);
    142130    object->putDirect(vm, Identifier::fromString(vm, "id"), jsString(vm, paymentDetails.id));
    143131    object->putDirect(vm, Identifier::fromString(vm, "total"), objectForPaymentItem(vm, exec, paymentDetails.total));
    144     object->putDirect(vm, Identifier::fromString(vm, "displayItems"), displayItems);
    145     object->putDirect(vm, Identifier::fromString(vm, "shippingOptions"), shippingOptions);
    146     object->putDirect(vm, Identifier::fromString(vm, "modifiers"), modifiers);
     132    if (paymentDetails.displayItems) {
     133        auto* displayItems = constructEmptyArray(exec, nullptr);
     134        for (unsigned i = 0; i < paymentDetails.displayItems->size(); ++i)
     135            displayItems->putDirectIndex(exec, i, objectForPaymentItem(vm, exec, paymentDetails.displayItems->at(i)));
     136        object->putDirect(vm, Identifier::fromString(vm, "displayItems"), displayItems);
     137    }
     138    if (paymentDetails.shippingOptions) {
     139        auto* shippingOptions = constructEmptyArray(exec, nullptr);
     140        for (unsigned i = 0; i < paymentDetails.shippingOptions->size(); ++i)
     141            shippingOptions->putDirectIndex(exec, i, objectForPaymentShippingOption(vm, exec, paymentDetails.shippingOptions->at(i)));
     142        object->putDirect(vm, Identifier::fromString(vm, "shippingOptions"), shippingOptions);
     143    }
     144    if (paymentDetails.modifiers) {
     145        auto* modifiers = constructEmptyArray(exec, nullptr);
     146        for (unsigned i = 0; i < paymentDetails.modifiers->size(); ++i)
     147            modifiers->putDirectIndex(exec, i, objectForPaymentDetailsModifier(vm, exec, paymentDetails.modifiers->at(i)));
     148        object->putDirect(vm, Identifier::fromString(vm, "modifiers"), modifiers);
     149    }
    147150    return object;
    148151}
Note: See TracChangeset for help on using the changeset viewer.