Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/payments/PaymentRequest.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "BasicCardPayment.h"
8
#include "mozilla/dom/Element.h"
9
#include "mozilla/dom/PaymentRequest.h"
10
#include "mozilla/dom/PaymentRequestChild.h"
11
#include "mozilla/dom/PaymentResponse.h"
12
#include "mozilla/EventStateManager.h"
13
#include "mozilla/StaticPrefs.h"
14
#include "nsContentUtils.h"
15
#include "nsIScriptError.h"
16
#include "nsIURLParser.h"
17
#include "nsNetCID.h"
18
#include "PaymentRequestManager.h"
19
#include "mozilla/dom/MerchantValidationEvent.h"
20
21
namespace mozilla {
22
namespace dom {
23
24
NS_IMPL_CYCLE_COLLECTION_CLASS(PaymentRequest)
25
26
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PaymentRequest,
27
0
                                               DOMEventTargetHelper)
28
0
  // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
29
0
  // DOMEventTargetHelper does it for us.
30
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
31
32
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PaymentRequest,
33
0
                                                  DOMEventTargetHelper)
34
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResultPromise)
35
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAcceptPromise)
36
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAbortPromise)
37
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponse)
38
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mShippingAddress)
39
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFullShippingAddress)
40
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
41
42
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PaymentRequest,
43
0
                                                DOMEventTargetHelper)
44
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResultPromise)
45
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAcceptPromise)
46
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAbortPromise)
47
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponse)
48
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mShippingAddress)
49
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFullShippingAddress)
50
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
51
52
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PaymentRequest)
53
0
  NS_INTERFACE_MAP_ENTRY(nsIDocumentActivity)
54
0
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
55
56
NS_IMPL_ADDREF_INHERITED(PaymentRequest, DOMEventTargetHelper)
57
NS_IMPL_RELEASE_INHERITED(PaymentRequest, DOMEventTargetHelper)
58
59
bool
60
PaymentRequest::PrefEnabled(JSContext* aCx, JSObject* aObj)
61
0
{
62
0
#ifdef NIGHTLY_BUILD
63
0
  return XRE_IsContentProcess() &&
64
0
         Preferences::GetBool("dom.payments.request.enabled");
65
#else
66
  return false;
67
#endif
68
}
69
70
nsresult
71
PaymentRequest::IsValidStandardizedPMI(const nsAString& aIdentifier,
72
                                       nsAString& aErrorMsg)
73
0
{
74
0
  /*
75
0
   *   The syntax of a standardized payment method identifier is given by the
76
0
   *   following [ABNF]:
77
0
   *
78
0
   *       stdpmi = part *( "-" part )
79
0
   *       part = 1loweralpha *( DIGIT / loweralpha )
80
0
   *       loweralpha =  %x61-7A
81
0
   */
82
0
  nsString::const_iterator start, end;
83
0
  aIdentifier.BeginReading(start);
84
0
  aIdentifier.EndReading(end);
85
0
  while (start != end) {
86
0
    // the first char must be in the range %x61-7A
87
0
    if ((*start < 'a' || *start > 'z')) {
88
0
      aErrorMsg.AssignLiteral("'");
89
0
      aErrorMsg.Append(aIdentifier);
90
0
      aErrorMsg.AppendLiteral("' is not valid. The character '");
91
0
      aErrorMsg.Append(*start);
92
0
      aErrorMsg.AppendLiteral("' at the beginning or after the '-' must be in the range [a-z].");
93
0
      return NS_ERROR_RANGE_ERR;
94
0
    }
95
0
    ++start;
96
0
    // the rest can be in the range %x61-7A + DIGITs
97
0
    while (start != end && *start != '-' &&
98
0
           ((*start >= 'a' && *start <= 'z') || (*start >= '0' && *start <= '9'))) {
99
0
      ++start;
100
0
    }
101
0
    // if the char is not in the range %x61-7A + DIGITs, it must be '-'
102
0
    if (start != end && *start != '-') {
103
0
      aErrorMsg.AssignLiteral("'");
104
0
      aErrorMsg.Append(aIdentifier);
105
0
      aErrorMsg.AppendLiteral("' is not valid. The character '");
106
0
      aErrorMsg.Append(*start);
107
0
      aErrorMsg.AppendLiteral("' must be in the range [a-zA-z0-9-].");
108
0
      return NS_ERROR_RANGE_ERR;
109
0
    }
110
0
    if (*start == '-') {
111
0
      ++start;
112
0
      // the last char can not be '-'
113
0
      if (start == end) {
114
0
        aErrorMsg.AssignLiteral("'");
115
0
        aErrorMsg.Append(aIdentifier);
116
0
        aErrorMsg.AppendLiteral("' is not valid. The last character '");
117
0
        aErrorMsg.Append(*start);
118
0
        aErrorMsg.AppendLiteral("' must be in the range [a-z0-9].");
119
0
        return NS_ERROR_RANGE_ERR;
120
0
      }
121
0
    }
122
0
  }
123
0
  return NS_OK;
124
0
}
125
126
nsresult
127
PaymentRequest::IsValidPaymentMethodIdentifier(const nsAString& aIdentifier,
128
                                               nsAString& aErrorMsg)
129
0
{
130
0
  if (aIdentifier.IsEmpty()) {
131
0
    aErrorMsg.AssignLiteral("Payment method identifier is required.");
132
0
    return NS_ERROR_TYPE_ERR;
133
0
  }
134
0
  /*
135
0
   *  URL-based payment method identifier
136
0
   *
137
0
   *  1. If url's scheme is not "https", return false.
138
0
   *  2. If url's username or password is not the empty string, return false.
139
0
   *  3. Otherwise, return true.
140
0
   */
141
0
  nsCOMPtr<nsIURLParser> urlParser = do_GetService(NS_STDURLPARSER_CONTRACTID);
142
0
  MOZ_ASSERT(urlParser);
143
0
  uint32_t schemePos = 0;
144
0
  int32_t schemeLen = 0;
145
0
  uint32_t authorityPos = 0;
146
0
  int32_t authorityLen = 0;
147
0
  NS_ConvertUTF16toUTF8 url(aIdentifier);
148
0
  nsresult rv = urlParser->ParseURL(url.get(),
149
0
                                    url.Length(),
150
0
                                    &schemePos, &schemeLen,
151
0
                                    &authorityPos, &authorityLen,
152
0
                                    nullptr, nullptr);
153
0
  NS_ENSURE_SUCCESS(rv, NS_ERROR_RANGE_ERR);
154
0
  if (schemeLen == -1) {
155
0
    // The PMI is not a URL-based PMI, check if it is a standardized PMI
156
0
    return IsValidStandardizedPMI(aIdentifier, aErrorMsg);
157
0
  }
158
0
  if (!Substring(aIdentifier, schemePos, schemeLen).EqualsASCII("https")) {
159
0
    aErrorMsg.AssignLiteral("'");
160
0
    aErrorMsg.Append(aIdentifier);
161
0
    aErrorMsg.AppendLiteral("' is not valid. The scheme must be 'https'.");
162
0
    return NS_ERROR_RANGE_ERR;
163
0
  }
164
0
  if (Substring(aIdentifier, authorityPos, authorityLen).IsEmpty()) {
165
0
    aErrorMsg.AssignLiteral("'");
166
0
    aErrorMsg.Append(aIdentifier);
167
0
    aErrorMsg.AppendLiteral("' is not valid. hostname can not be empty.");
168
0
    return NS_ERROR_RANGE_ERR;
169
0
  }
170
0
171
0
  uint32_t usernamePos = 0;
172
0
  int32_t usernameLen = 0;
173
0
  uint32_t passwordPos = 0;
174
0
  int32_t passwordLen = 0;
175
0
  uint32_t hostnamePos = 0;
176
0
  int32_t hostnameLen = 0;
177
0
  int32_t port = 0;
178
0
179
0
  NS_ConvertUTF16toUTF8 authority(Substring(aIdentifier, authorityPos, authorityLen));
180
0
  rv = urlParser->ParseAuthority(authority.get(),
181
0
                                 authority.Length(),
182
0
                                 &usernamePos, &usernameLen,
183
0
                                 &passwordPos, &passwordLen,
184
0
                                 &hostnamePos, &hostnameLen,
185
0
                                 &port);
186
0
  if (NS_FAILED(rv)) {
187
0
    // Handle the special cases that URLParser treats it as an invalid URL, but
188
0
    // are used in web-platform-test
189
0
    // For exmaple:
190
0
    //     https://:@example.com             // should be considered as valid
191
0
    //     https://:password@example.com.    // should be considered as invalid
192
0
    int32_t atPos = authority.FindChar('@');
193
0
    if (atPos >= 0) {
194
0
      // only accept the case https://:@xxx
195
0
      if (atPos == 1 && authority.CharAt(0) == ':') {
196
0
        usernamePos = 0;
197
0
        usernameLen = 0;
198
0
        passwordPos = 0;
199
0
        passwordLen = 0;
200
0
      } else {
201
0
        // for the fail cases, don't care about what the actual length is.
202
0
        usernamePos = 0;
203
0
        usernameLen = INT32_MAX;
204
0
        passwordPos = 0;
205
0
        passwordLen = INT32_MAX;
206
0
      }
207
0
    } else {
208
0
      usernamePos = 0;
209
0
      usernameLen = -1;
210
0
      passwordPos = 0;
211
0
      passwordLen = -1;
212
0
    }
213
0
    // Parse server information when both username and password are empty or do not
214
0
    // exist.
215
0
    if ((usernameLen <= 0) && (passwordLen <= 0)) {
216
0
      if (authority.Length() - atPos - 1 == 0) {
217
0
        aErrorMsg.AssignLiteral("'");
218
0
        aErrorMsg.Append(aIdentifier);
219
0
        aErrorMsg.AppendLiteral("' is not valid. hostname can not be empty.");
220
0
        return NS_ERROR_RANGE_ERR;
221
0
      }
222
0
      // Re-using nsIURLParser::ParseServerInfo to extract the hostname and port
223
0
      // information. This can help us to handle complicated IPv6 cases.
224
0
      nsAutoCString serverInfo(Substring(authority,
225
0
                                         atPos + 1,
226
0
                                         authority.Length() - atPos - 1));
227
0
      rv = urlParser->ParseServerInfo(serverInfo.get(),
228
0
                                      serverInfo.Length(),
229
0
                                      &hostnamePos, &hostnameLen, &port);
230
0
      if (NS_FAILED(rv)) {
231
0
        // ParseServerInfo returns NS_ERROR_MALFORMED_URI in all fail cases, we
232
0
        // probably need a followup bug to figure out the fail reason.
233
0
        return NS_ERROR_RANGE_ERR;
234
0
      }
235
0
    }
236
0
  }
237
0
  // PMI is valid when usernameLen/passwordLen equals to -1 or 0.
238
0
  if (usernameLen > 0 || passwordLen > 0) {
239
0
    aErrorMsg.AssignLiteral("'");
240
0
    aErrorMsg.Append(aIdentifier);
241
0
    aErrorMsg.AssignLiteral("' is not valid. Username and password must be empty.");
242
0
    return NS_ERROR_RANGE_ERR;
243
0
  }
244
0
245
0
  // PMI is valid when hostnameLen is larger than 0
246
0
  if (hostnameLen <= 0) {
247
0
    aErrorMsg.AssignLiteral("'");
248
0
    aErrorMsg.Append(aIdentifier);
249
0
    aErrorMsg.AppendLiteral("' is not valid. hostname can not be empty.");
250
0
    return NS_ERROR_RANGE_ERR;
251
0
  }
252
0
  return NS_OK;
253
0
}
254
255
nsresult
256
PaymentRequest::IsValidMethodData(JSContext* aCx,
257
                                  const Sequence<PaymentMethodData>& aMethodData,
258
                                  nsAString& aErrorMsg)
259
0
{
260
0
  if (!aMethodData.Length()) {
261
0
    aErrorMsg.AssignLiteral("At least one payment method is required.");
262
0
    return NS_ERROR_TYPE_ERR;
263
0
  }
264
0
265
0
  for (const PaymentMethodData& methodData : aMethodData) {
266
0
    nsresult rv = IsValidPaymentMethodIdentifier(methodData.mSupportedMethods,
267
0
                                                 aErrorMsg);
268
0
    if (NS_FAILED(rv)) {
269
0
      return rv;
270
0
    }
271
0
272
0
    RefPtr<BasicCardService> service = BasicCardService::GetService();
273
0
    MOZ_ASSERT(service);
274
0
    if (service->IsBasicCardPayment(methodData.mSupportedMethods)) {
275
0
      if (!methodData.mData.WasPassed()) {
276
0
        continue;
277
0
      }
278
0
      MOZ_ASSERT(aCx);
279
0
      if (!service->IsValidBasicCardRequest(aCx,
280
0
                                            methodData.mData.Value(),
281
0
                                            aErrorMsg)) {
282
0
        return NS_ERROR_TYPE_ERR;
283
0
      }
284
0
    }
285
0
  }
286
0
287
0
  return NS_OK;
288
0
}
289
290
nsresult
291
PaymentRequest::IsValidNumber(const nsAString& aItem,
292
                              const nsAString& aStr,
293
                              nsAString& aErrorMsg)
294
0
{
295
0
  nsresult error = NS_ERROR_FAILURE;
296
0
297
0
  if (!aStr.IsEmpty()) {
298
0
    nsAutoString aValue(aStr);
299
0
300
0
    // If the beginning character is '-', we will check the second one.
301
0
    int beginningIndex = (aValue.First() == '-') ? 1 : 0;
302
0
303
0
    // Ensure
304
0
    // - the beginning character is a digit in [0-9], and
305
0
    // - the last character is not '.'
306
0
    // to follow spec:
307
0
    //   https://w3c.github.io/browser-payment-api/#dfn-valid-decimal-monetary-value
308
0
    //
309
0
    // For example, ".1" is not valid for '.' is not in [0-9],
310
0
    // and " 0.1" either for beginning with ' '
311
0
    if (aValue.Last() != '.' &&
312
0
        aValue.CharAt(beginningIndex) >= '0' &&
313
0
        aValue.CharAt(beginningIndex) <= '9') {
314
0
      aValue.ToFloat(&error);
315
0
    }
316
0
  }
317
0
318
0
  if (NS_FAILED(error)) {
319
0
    aErrorMsg.AssignLiteral("The amount.value of \"");
320
0
    aErrorMsg.Append(aItem);
321
0
    aErrorMsg.AppendLiteral("\"(");
322
0
    aErrorMsg.Append(aStr);
323
0
    aErrorMsg.AppendLiteral(") must be a valid decimal monetary value.");
324
0
    return NS_ERROR_TYPE_ERR;
325
0
  }
326
0
  return NS_OK;
327
0
}
328
329
nsresult
330
PaymentRequest::IsNonNegativeNumber(const nsAString& aItem,
331
                                    const nsAString& aStr,
332
                                    nsAString& aErrorMsg)
333
0
{
334
0
  nsresult error = NS_ERROR_FAILURE;
335
0
336
0
  if (!aStr.IsEmpty()) {
337
0
    nsAutoString aValue(aStr);
338
0
    // Ensure
339
0
    // - the beginning character is a digit in [0-9], and
340
0
    // - the last character is not '.'
341
0
    if (aValue.Last() != '.' &&
342
0
        aValue.First() >= '0' &&
343
0
        aValue.First() <= '9') {
344
0
      aValue.ToFloat(&error);
345
0
    }
346
0
  }
347
0
348
0
  if (NS_FAILED(error)) {
349
0
    aErrorMsg.AssignLiteral("The amount.value of \"");
350
0
    aErrorMsg.Append(aItem);
351
0
    aErrorMsg.AppendLiteral("\"(");
352
0
    aErrorMsg.Append(aStr);
353
0
    aErrorMsg.AppendLiteral(") must be a valid and non-negative decimal monetary value.");
354
0
    return NS_ERROR_TYPE_ERR;
355
0
  }
356
0
  return NS_OK;
357
0
}
358
359
nsresult
360
PaymentRequest::IsValidCurrency(const nsAString& aItem,
361
                                const nsAString& aCurrency,
362
                                nsAString& aErrorMsg)
363
0
{
364
0
   /*
365
0
    *  According to spec in https://w3c.github.io/payment-request/#validity-checkers,
366
0
    *  perform currency validation with following criteria
367
0
    *  1. The currency length must be 3.
368
0
    *  2. The currency contains any character that must be in the range "A" to "Z"
369
0
    *     (U+0041 to U+005A) or the range "a" to "z" (U+0061 to U+007A)
370
0
    */
371
0
   if (aCurrency.Length() != 3) {
372
0
     aErrorMsg.AssignLiteral("The length amount.currency of \"");
373
0
     aErrorMsg.Append(aItem);
374
0
     aErrorMsg.AppendLiteral("\"(");
375
0
     aErrorMsg.Append(aCurrency);
376
0
     aErrorMsg.AppendLiteral(") must be 3.");
377
0
     return NS_ERROR_RANGE_ERR;
378
0
   }
379
0
   // Don't use nsUnicharUtils::ToUpperCase, it converts the invalid "ınr" PMI to
380
0
   // to the valid one "INR".
381
0
   for (uint32_t idx = 0; idx < aCurrency.Length(); ++idx) {
382
0
     if ((aCurrency.CharAt(idx) >= 'A' && aCurrency.CharAt(idx) <= 'Z') ||
383
0
         (aCurrency.CharAt(idx) >= 'a' && aCurrency.CharAt(idx) <= 'z')) {
384
0
       continue;
385
0
     }
386
0
     aErrorMsg.AssignLiteral("The character amount.currency of \"");
387
0
     aErrorMsg.Append(aItem);
388
0
     aErrorMsg.AppendLiteral("\"(");
389
0
     aErrorMsg.Append(aCurrency);
390
0
     aErrorMsg.AppendLiteral(") must be in the range 'A' to 'Z'(U+0041 to U+005A) or 'a' to 'z'(U+0061 to U+007A).");
391
0
     return NS_ERROR_RANGE_ERR;
392
0
   }
393
0
   return NS_OK;
394
0
}
395
396
nsresult
397
PaymentRequest::IsValidCurrencyAmount(const nsAString& aItem,
398
                                      const PaymentCurrencyAmount& aAmount,
399
                                      const bool aIsTotalItem,
400
                                      nsAString& aErrorMsg)
401
0
{
402
0
  nsresult rv;
403
0
  // currencySystem must equal urn:iso:std:iso:4217
404
0
  if (!aAmount.mCurrencySystem.EqualsASCII("urn:iso:std:iso:4217")) {
405
0
    aErrorMsg.AssignLiteral("The amount.currencySystem of \"");
406
0
    aErrorMsg.Append(aItem);
407
0
    aErrorMsg.AppendLiteral("\"(");
408
0
    aErrorMsg.Append(aAmount.mCurrencySystem);
409
0
    aErrorMsg.AppendLiteral(") must equal urn:iso:std:iso:4217.");
410
0
    return NS_ERROR_RANGE_ERR;
411
0
  }
412
0
  rv = IsValidCurrency(aItem, aAmount.mCurrency, aErrorMsg);
413
0
  if (NS_FAILED(rv)) {
414
0
    return rv;
415
0
  }
416
0
  if (aIsTotalItem) {
417
0
    rv = IsNonNegativeNumber(aItem, aAmount.mValue, aErrorMsg);
418
0
    if (NS_FAILED(rv)) {
419
0
      return rv;
420
0
    }
421
0
  } else {
422
0
    rv = IsValidNumber(aItem, aAmount.mValue, aErrorMsg);
423
0
    if (NS_FAILED(rv)) {
424
0
      return rv;
425
0
    }
426
0
  }
427
0
  return NS_OK;
428
0
}
429
430
nsresult
431
PaymentRequest::IsValidDetailsInit(const PaymentDetailsInit& aDetails,
432
                                   const bool aRequestShipping,
433
                                   nsAString& aErrorMsg)
434
0
{
435
0
  // Check the amount.value and amount.currency of detail.total
436
0
  nsresult rv = IsValidCurrencyAmount(NS_LITERAL_STRING("details.total"),
437
0
                                      aDetails.mTotal.mAmount,
438
0
                                      true, // isTotalItem
439
0
                                      aErrorMsg);
440
0
  if (NS_FAILED(rv)) {
441
0
    return rv;
442
0
  }
443
0
  return IsValidDetailsBase(aDetails, aRequestShipping, aErrorMsg);
444
0
}
445
446
nsresult
447
PaymentRequest::IsValidDetailsUpdate(const PaymentDetailsUpdate& aDetails,
448
                                     const bool aRequestShipping)
449
0
{
450
0
  nsAutoString message;
451
0
  // Check the amount.value and amount.currency of detail.total
452
0
  nsresult rv = IsValidCurrencyAmount(NS_LITERAL_STRING("details.total"),
453
0
                                      aDetails.mTotal.mAmount,
454
0
                                      true, // isTotalItem
455
0
                                      message);
456
0
  if (NS_FAILED(rv)) {
457
0
    return rv;
458
0
  }
459
0
  return IsValidDetailsBase(aDetails, aRequestShipping, message);
460
0
}
461
462
nsresult
463
PaymentRequest::IsValidDetailsBase(const PaymentDetailsBase& aDetails,
464
                                   const bool aRequestShipping,
465
                                   nsAString& aErrorMsg)
466
0
{
467
0
  nsresult rv;
468
0
  // Check the amount.value of each item in the display items
469
0
  if (aDetails.mDisplayItems.WasPassed()) {
470
0
    const Sequence<PaymentItem>& displayItems = aDetails.mDisplayItems.Value();
471
0
    for (const PaymentItem& displayItem : displayItems) {
472
0
      rv = IsValidCurrencyAmount(displayItem.mLabel,
473
0
                                 displayItem.mAmount,
474
0
                                 false,  // isTotalItem
475
0
                                 aErrorMsg);
476
0
      if (NS_FAILED(rv)) {
477
0
        return rv;
478
0
      }
479
0
    }
480
0
  }
481
0
482
0
  // Check the shipping option
483
0
  if (aDetails.mShippingOptions.WasPassed() && aRequestShipping) {
484
0
    const Sequence<PaymentShippingOption>& shippingOptions = aDetails.mShippingOptions.Value();
485
0
    nsTArray<nsString> seenIDs;
486
0
    for (const PaymentShippingOption& shippingOption : shippingOptions) {
487
0
      rv = IsValidCurrencyAmount(NS_LITERAL_STRING("details.shippingOptions"),
488
0
                                 shippingOption.mAmount,
489
0
                                 false,  // isTotalItem
490
0
                                 aErrorMsg);
491
0
      if (NS_FAILED(rv)) {
492
0
        return rv;
493
0
      }
494
0
      if (seenIDs.Contains(shippingOption.mId)) {
495
0
        aErrorMsg.AssignLiteral("Duplicate shippingOption id '");
496
0
        aErrorMsg.Append(shippingOption.mId);
497
0
        aErrorMsg.AppendLiteral("'");
498
0
        return NS_ERROR_TYPE_ERR;
499
0
      }
500
0
      seenIDs.AppendElement(shippingOption.mId);
501
0
    }
502
0
  }
503
0
504
0
  // Check payment details modifiers
505
0
  if (aDetails.mModifiers.WasPassed()) {
506
0
    const Sequence<PaymentDetailsModifier>& modifiers = aDetails.mModifiers.Value();
507
0
    for (const PaymentDetailsModifier& modifier : modifiers) {
508
0
      rv = IsValidPaymentMethodIdentifier(modifier.mSupportedMethods, aErrorMsg);
509
0
      if (NS_FAILED(rv)) {
510
0
        return rv;
511
0
      }
512
0
      rv = IsValidCurrencyAmount(NS_LITERAL_STRING("details.modifiers.total"),
513
0
                                 modifier.mTotal.mAmount,
514
0
                                 true, // isTotalItem
515
0
                                 aErrorMsg);
516
0
      if (NS_FAILED(rv)) {
517
0
        return rv;
518
0
      }
519
0
      if (modifier.mAdditionalDisplayItems.WasPassed()) {
520
0
        const Sequence<PaymentItem>& displayItems = modifier.mAdditionalDisplayItems.Value();
521
0
        for (const PaymentItem& displayItem : displayItems) {
522
0
          rv = IsValidCurrencyAmount(displayItem.mLabel,
523
0
                                     displayItem.mAmount,
524
0
                                     false,  // isTotalItem
525
0
                                     aErrorMsg);
526
0
          if (NS_FAILED(rv)) {
527
0
            return rv;
528
0
          }
529
0
        }
530
0
      }
531
0
    }
532
0
  }
533
0
534
0
  return NS_OK;
535
0
}
536
537
already_AddRefed<PaymentRequest>
538
PaymentRequest::Constructor(const GlobalObject& aGlobal,
539
                            const Sequence<PaymentMethodData>& aMethodData,
540
                            const PaymentDetailsInit& aDetails,
541
                            const PaymentOptions& aOptions,
542
                            ErrorResult& aRv)
543
0
{
544
0
  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
545
0
  if (!window) {
546
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
547
0
    return nullptr;
548
0
  }
549
0
550
0
  // the feature can only be used in an active document
551
0
  if (!window->IsCurrentInnerWindow()) {
552
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
553
0
    return nullptr;
554
0
  }
555
0
556
0
557
0
  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
558
0
  if (!doc) {
559
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
560
0
    return nullptr;
561
0
  }
562
0
563
0
  // Check if AllowPaymentRequest on the owner document
564
0
  if (!doc->AllowPaymentRequest()) {
565
0
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
566
0
    return nullptr;
567
0
  }
568
0
569
0
  // Get the top level principal
570
0
  nsCOMPtr<nsIDocument> topLevelDoc = doc->GetTopLevelContentDocument();
571
0
  MOZ_ASSERT(topLevelDoc);
572
0
  nsCOMPtr<nsIPrincipal> topLevelPrincipal = topLevelDoc->NodePrincipal();
573
0
574
0
  // Check payment methods and details
575
0
  nsAutoString message;
576
0
  nsresult rv = IsValidMethodData(aGlobal.Context(),
577
0
                                  aMethodData,
578
0
                                  message);
579
0
  if (NS_FAILED(rv)) {
580
0
    if (rv == NS_ERROR_TYPE_ERR) {
581
0
      aRv.ThrowTypeError<MSG_ILLEGAL_TYPE_PR_CONSTRUCTOR>(message);
582
0
    } else if (rv == NS_ERROR_RANGE_ERR) {
583
0
      aRv.ThrowRangeError<MSG_ILLEGAL_RANGE_PR_CONSTRUCTOR>(message);
584
0
    }
585
0
    return nullptr;
586
0
  }
587
0
  rv = IsValidDetailsInit(aDetails, aOptions.mRequestShipping, message);
588
0
  if (NS_FAILED(rv)) {
589
0
    if (rv == NS_ERROR_TYPE_ERR) {
590
0
      aRv.ThrowTypeError<MSG_ILLEGAL_TYPE_PR_CONSTRUCTOR>(message);
591
0
    } else if (rv == NS_ERROR_RANGE_ERR) {
592
0
      aRv.ThrowRangeError<MSG_ILLEGAL_RANGE_PR_CONSTRUCTOR>(message);
593
0
    }
594
0
    return nullptr;
595
0
  }
596
0
597
0
  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
598
0
  if (NS_WARN_IF(!manager)) {
599
0
    return nullptr;
600
0
  }
601
0
602
0
  // Create PaymentRequest and set its |mId|
603
0
  RefPtr<PaymentRequest> request;
604
0
  rv = manager->CreatePayment(aGlobal.Context(), window, topLevelPrincipal, aMethodData,
605
0
                              aDetails, aOptions, getter_AddRefs(request));
606
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
607
0
    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
608
0
    return nullptr;
609
0
  }
610
0
  return request.forget();
611
0
}
612
613
already_AddRefed<PaymentRequest>
614
PaymentRequest::CreatePaymentRequest(nsPIDOMWindowInner* aWindow, nsresult& aRv)
615
0
{
616
0
  // Generate a unique id for identification
617
0
  nsID uuid;
618
0
  aRv = nsContentUtils::GenerateUUIDInPlace(uuid);
619
0
  if (NS_WARN_IF(NS_FAILED(aRv))) {
620
0
    return nullptr;
621
0
  }
622
0
623
0
  // Build a string in {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} format
624
0
  char buffer[NSID_LENGTH];
625
0
  uuid.ToProvidedString(buffer);
626
0
627
0
  // Remove {} and the null terminator
628
0
  nsAutoString id;
629
0
  id.AssignASCII(&buffer[1], NSID_LENGTH - 3);
630
0
631
0
  // Create payment request with generated id
632
0
  RefPtr<PaymentRequest> request = new PaymentRequest(aWindow, id);
633
0
  return request.forget();
634
0
}
635
636
PaymentRequest::PaymentRequest(nsPIDOMWindowInner* aWindow, const nsAString& aInternalId)
637
  : DOMEventTargetHelper(aWindow)
638
  , mInternalId(aInternalId)
639
  , mShippingAddress(nullptr)
640
  , mUpdating(false)
641
  , mRequestShipping(false)
642
  , mDeferredShow(false)
643
  , mUpdateError(NS_OK)
644
  , mState(eCreated)
645
  , mIPC(nullptr)
646
0
{
647
0
  MOZ_ASSERT(aWindow);
648
0
  RegisterActivityObserver();
649
0
}
650
651
already_AddRefed<Promise>
652
PaymentRequest::CanMakePayment(ErrorResult& aRv)
653
0
{
654
0
  if (mState != eCreated) {
655
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
656
0
    return nullptr;
657
0
  }
658
0
659
0
  if (mResultPromise) {
660
0
    // XXX This doesn't match the spec but does match Chromium.
661
0
    aRv.Throw(NS_ERROR_DOM_NOT_ALLOWED_ERR);
662
0
    return nullptr;
663
0
  }
664
0
665
0
  nsIGlobalObject* global = GetOwnerGlobal();
666
0
  ErrorResult result;
667
0
  RefPtr<Promise> promise = Promise::Create(global, result);
668
0
  if (result.Failed()) {
669
0
    aRv.Throw(NS_ERROR_FAILURE);
670
0
    return nullptr;
671
0
  }
672
0
673
0
  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
674
0
  MOZ_ASSERT(manager);
675
0
  nsresult rv = manager->CanMakePayment(this);
676
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
677
0
    promise->MaybeReject(NS_ERROR_FAILURE);
678
0
    return promise.forget();
679
0
  }
680
0
  mResultPromise = promise;
681
0
  return promise.forget();
682
0
}
683
684
void
685
PaymentRequest::RespondCanMakePayment(bool aResult)
686
0
{
687
0
  MOZ_ASSERT(mResultPromise);
688
0
  mResultPromise->MaybeResolve(aResult);
689
0
  mResultPromise = nullptr;
690
0
}
691
692
already_AddRefed<Promise>
693
PaymentRequest::Show(const Optional<OwningNonNull<Promise>>& aDetailsPromise,
694
                     ErrorResult& aRv)
695
0
{
696
0
  nsIGlobalObject* global = GetOwnerGlobal();
697
0
  nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
698
0
  MOZ_ASSERT(win);
699
0
  nsIDocument* doc = win->GetExtantDoc();
700
0
701
0
  if (!EventStateManager::IsHandlingUserInput()) {
702
0
    nsString msg = NS_LITERAL_STRING("User activation is now required to call PaymentRequest.show()");
703
0
    nsContentUtils::ReportToConsoleNonLocalized(msg,
704
0
                                                nsIScriptError::warningFlag,
705
0
                                                NS_LITERAL_CSTRING("Security"),
706
0
                                                doc);
707
0
    if (StaticPrefs::dom_payments_request_user_interaction_required()) {
708
0
      aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
709
0
      return nullptr;
710
0
    }
711
0
  }
712
0
713
0
  if (!doc || !doc->IsCurrentActiveDocument()) {
714
0
    aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
715
0
    return nullptr;
716
0
  }
717
0
718
0
  if (mState != eCreated) {
719
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
720
0
    return nullptr;
721
0
  }
722
0
723
0
  ErrorResult result;
724
0
  RefPtr<Promise> promise = Promise::Create(global, result);
725
0
  if (result.Failed()) {
726
0
    mState = eClosed;
727
0
    aRv.Throw(NS_ERROR_FAILURE);
728
0
    return nullptr;
729
0
  }
730
0
731
0
  if (aDetailsPromise.WasPassed()) {
732
0
    aDetailsPromise.Value().AppendNativeHandler(this);
733
0
    mUpdating = true;
734
0
    mDeferredShow = true;
735
0
  }
736
0
737
0
  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
738
0
  MOZ_ASSERT(manager);
739
0
  nsresult rv = manager->ShowPayment(this);
740
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
741
0
    if (rv == NS_ERROR_ABORT) {
742
0
      promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
743
0
    } else {
744
0
      promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
745
0
    }
746
0
    mState = eClosed;
747
0
    return promise.forget();
748
0
  }
749
0
750
0
  mAcceptPromise = promise;
751
0
  mState = eInteractive;
752
0
  return promise.forget();
753
0
}
754
755
void
756
PaymentRequest::RejectShowPayment(nsresult aRejectReason)
757
0
{
758
0
  MOZ_ASSERT(mAcceptPromise || mResponse);
759
0
  MOZ_ASSERT(mState == eInteractive);
760
0
761
0
  if (mResponse) {
762
0
    mResponse->RejectRetry(aRejectReason);
763
0
  } else {
764
0
    mAcceptPromise->MaybeReject(aRejectReason);
765
0
  }
766
0
  mState = eClosed;
767
0
  mAcceptPromise = nullptr;
768
0
}
769
770
void
771
PaymentRequest::RespondShowPayment(const nsAString& aMethodName,
772
                                   const nsAString& aDetails,
773
                                   const nsAString& aPayerName,
774
                                   const nsAString& aPayerEmail,
775
                                   const nsAString& aPayerPhone,
776
                                   nsresult aRv)
777
0
{
778
0
  MOZ_ASSERT(mAcceptPromise || mResponse);
779
0
  MOZ_ASSERT(mState == eInteractive);
780
0
781
0
  if (NS_FAILED(aRv)) {
782
0
    RejectShowPayment(aRv);
783
0
    return;
784
0
  }
785
0
786
0
  // https://github.com/w3c/payment-request/issues/692
787
0
  mShippingAddress.swap(mFullShippingAddress);
788
0
  mFullShippingAddress = nullptr;
789
0
790
0
  if (mResponse) {
791
0
    mResponse->RespondRetry(aMethodName, mShippingOption, mShippingAddress,
792
0
                            aDetails, aPayerName, aPayerEmail, aPayerPhone);
793
0
  } else {
794
0
    RefPtr<PaymentResponse> paymentResponse =
795
0
      new PaymentResponse(GetOwner(), this, mId, aMethodName,
796
0
                          mShippingOption, mShippingAddress, aDetails,
797
0
                          aPayerName, aPayerEmail, aPayerPhone);
798
0
    mResponse = paymentResponse;
799
0
    mAcceptPromise->MaybeResolve(paymentResponse);
800
0
  }
801
0
802
0
  mState = eClosed;
803
0
  mAcceptPromise = nullptr;
804
0
}
805
806
void
807
PaymentRequest::RespondComplete()
808
0
{
809
0
  MOZ_ASSERT(mResponse);
810
0
  mResponse->RespondComplete();
811
0
}
812
813
already_AddRefed<Promise>
814
PaymentRequest::Abort(ErrorResult& aRv)
815
0
{
816
0
  if (mState != eInteractive) {
817
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
818
0
    return nullptr;
819
0
  }
820
0
821
0
  if (mAbortPromise) {
822
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
823
0
    return nullptr;
824
0
  }
825
0
826
0
  nsIGlobalObject* global = GetOwnerGlobal();
827
0
  ErrorResult result;
828
0
  RefPtr<Promise> promise = Promise::Create(global, result);
829
0
  if (result.Failed()) {
830
0
    aRv.Throw(NS_ERROR_FAILURE);
831
0
    return nullptr;
832
0
  }
833
0
834
0
  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
835
0
  MOZ_ASSERT(manager);
836
0
  // It's possible to be called between show and its promise resolving.
837
0
  nsresult rv = manager->AbortPayment(this, mDeferredShow);
838
0
  mDeferredShow = false;
839
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
840
0
    aRv.Throw(NS_ERROR_FAILURE);
841
0
    return nullptr;
842
0
  }
843
0
844
0
  mAbortPromise = promise;
845
0
  return promise.forget();
846
0
}
847
848
void
849
PaymentRequest::RespondAbortPayment(bool aSuccess)
850
0
{
851
0
  // Check whether we are aborting the update:
852
0
  //
853
0
  // - If |mUpdateError| is not NS_OK, we are aborting the update as
854
0
  //   |mUpdateError| was set in method |AbortUpdate|.
855
0
  //   => Reject |mAcceptPromise| and reset |mUpdateError| to complete
856
0
  //      the action, regardless of |aSuccess|.
857
0
  //
858
0
  // - Otherwise, we are handling |Abort| method call from merchant.
859
0
  //   => Resolve/Reject |mAbortPromise| based on |aSuccess|.
860
0
  if (NS_FAILED(mUpdateError)) {
861
0
    // Respond show with mUpdateError, set mUpdating to false.
862
0
    mUpdating = false;
863
0
    RespondShowPayment(EmptyString(), EmptyString(), EmptyString(),
864
0
                       EmptyString(), EmptyString(), mUpdateError);
865
0
    mUpdateError = NS_OK;
866
0
    return;
867
0
  }
868
0
869
0
  MOZ_ASSERT(mAbortPromise);
870
0
  MOZ_ASSERT(mState == eInteractive);
871
0
872
0
  if (aSuccess) {
873
0
    mAbortPromise->MaybeResolve(JS::UndefinedHandleValue);
874
0
    mAbortPromise = nullptr;
875
0
    RejectShowPayment(NS_ERROR_DOM_ABORT_ERR);
876
0
  } else {
877
0
    mAbortPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
878
0
    mAbortPromise = nullptr;
879
0
  }
880
0
}
881
882
nsresult
883
PaymentRequest::UpdatePayment(JSContext* aCx, const PaymentDetailsUpdate& aDetails,
884
                              bool aDeferredShow)
885
0
{
886
0
  NS_ENSURE_ARG_POINTER(aCx);
887
0
  if (mState != eInteractive) {
888
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
889
0
  }
890
0
  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
891
0
  if (NS_WARN_IF(!manager)) {
892
0
    return NS_ERROR_FAILURE;
893
0
  }
894
0
  nsresult rv = manager->UpdatePayment(aCx, this, aDetails, mRequestShipping,
895
0
                                       aDeferredShow);
896
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
897
0
    return rv;
898
0
  }
899
0
  return NS_OK;
900
0
}
901
902
void
903
PaymentRequest::AbortUpdate(nsresult aRv, bool aDeferredShow)
904
0
{
905
0
  MOZ_ASSERT(NS_FAILED(aRv));
906
0
907
0
  if (mState != eInteractive) {
908
0
    return;
909
0
  }
910
0
  // Close down any remaining user interface.
911
0
  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
912
0
  MOZ_ASSERT(manager);
913
0
  nsresult rv = manager->AbortPayment(this, aDeferredShow);
914
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
915
0
    return;
916
0
  }
917
0
918
0
  // Remember update error |aRv| and do the following steps in RespondShowPayment.
919
0
  // 1. Set target.state to closed
920
0
  // 2. Reject the promise target.acceptPromise with exception "aRv"
921
0
  // 3. Abort the algorithm with update error
922
0
  mUpdateError = aRv;
923
0
}
924
925
nsresult
926
PaymentRequest::RetryPayment(JSContext* aCx, const PaymentValidationErrors& aErrors)
927
0
{
928
0
  if (mState == eInteractive) {
929
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
930
0
  }
931
0
  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
932
0
  MOZ_ASSERT(manager);
933
0
  nsresult rv = manager->RetryPayment(aCx, this, aErrors);
934
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
935
0
    return rv;
936
0
  }
937
0
  mState = eInteractive;
938
0
  return NS_OK;
939
0
}
940
941
void
942
PaymentRequest::GetId(nsAString& aRetVal) const
943
0
{
944
0
  aRetVal = mId;
945
0
}
946
947
void
948
PaymentRequest::GetInternalId(nsAString& aRetVal)
949
0
{
950
0
  aRetVal = mInternalId;
951
0
}
952
953
void
954
PaymentRequest::SetId(const nsAString& aId)
955
0
{
956
0
  mId = aId;
957
0
}
958
959
bool
960
PaymentRequest::Equals(const nsAString& aInternalId) const
961
0
{
962
0
  return mInternalId.Equals(aInternalId);
963
0
}
964
965
bool
966
PaymentRequest::ReadyForUpdate()
967
0
{
968
0
  return mState == eInteractive && !mUpdating;
969
0
}
970
971
void
972
PaymentRequest::SetUpdating(bool aUpdating)
973
0
{
974
0
  mUpdating = aUpdating;
975
0
}
976
977
already_AddRefed<PaymentResponse>
978
PaymentRequest::GetResponse() const
979
0
{
980
0
  RefPtr<PaymentResponse> response = mResponse;
981
0
  return response.forget();
982
0
}
983
984
nsresult
985
PaymentRequest::DispatchUpdateEvent(const nsAString& aType)
986
0
{
987
0
  MOZ_ASSERT(ReadyForUpdate());
988
0
989
0
  PaymentRequestUpdateEventInit init;
990
0
  init.mBubbles = false;
991
0
  init.mCancelable = false;
992
0
993
0
  RefPtr<PaymentRequestUpdateEvent> event =
994
0
    PaymentRequestUpdateEvent::Constructor(this, aType, init);
995
0
  event->SetTrusted(true);
996
0
  event->SetRequest(this);
997
0
998
0
  ErrorResult rv;
999
0
  DispatchEvent(*event, rv);
1000
0
  return rv.StealNSResult();
1001
0
}
1002
1003
nsresult
1004
PaymentRequest::DispatchMerchantValidationEvent(const nsAString& aType)
1005
0
{
1006
0
  MOZ_ASSERT(ReadyForUpdate());
1007
0
1008
0
  MerchantValidationEventInit init;
1009
0
  init.mBubbles = false;
1010
0
  init.mCancelable = false;
1011
0
  init.mValidationURL = EmptyString();
1012
0
1013
0
  ErrorResult rv;
1014
0
  RefPtr<MerchantValidationEvent> event =
1015
0
    MerchantValidationEvent::Constructor(this, aType, init, rv);
1016
0
  if (rv.Failed()) {
1017
0
    return rv.StealNSResult();
1018
0
  }
1019
0
  event->SetTrusted(true);
1020
0
  event->SetRequest(this);
1021
0
1022
0
  DispatchEvent(*event, rv);
1023
0
  return rv.StealNSResult();
1024
0
}
1025
1026
already_AddRefed<PaymentAddress>
1027
PaymentRequest::GetShippingAddress() const
1028
0
{
1029
0
  RefPtr<PaymentAddress> address = mShippingAddress;
1030
0
  return address.forget();
1031
0
}
1032
1033
nsresult
1034
PaymentRequest::UpdateShippingAddress(const nsAString& aCountry,
1035
                                      const nsTArray<nsString>& aAddressLine,
1036
                                      const nsAString& aRegion,
1037
                                      const nsAString& aCity,
1038
                                      const nsAString& aDependentLocality,
1039
                                      const nsAString& aPostalCode,
1040
                                      const nsAString& aSortingCode,
1041
                                      const nsAString& aOrganization,
1042
                                      const nsAString& aRecipient,
1043
                                      const nsAString& aPhone)
1044
0
{
1045
0
  nsTArray<nsString> emptyArray;
1046
0
  mShippingAddress = new PaymentAddress(GetOwner(), aCountry, emptyArray,
1047
0
                                        aRegion, aCity, aDependentLocality,
1048
0
                                        aPostalCode, aSortingCode,
1049
0
                                        EmptyString(), EmptyString(), EmptyString());
1050
0
  mFullShippingAddress = new PaymentAddress(GetOwner(), aCountry, aAddressLine,
1051
0
                                            aRegion, aCity, aDependentLocality,
1052
0
                                            aPostalCode, aSortingCode,
1053
0
                                            aOrganization, aRecipient, aPhone);
1054
0
  // Fire shippingaddresschange event
1055
0
  return DispatchUpdateEvent(NS_LITERAL_STRING("shippingaddresschange"));
1056
0
}
1057
1058
void
1059
PaymentRequest::SetShippingOption(const nsAString& aShippingOption)
1060
0
{
1061
0
  mShippingOption = aShippingOption;
1062
0
}
1063
1064
void
1065
PaymentRequest::GetShippingOption(nsAString& aRetVal) const
1066
0
{
1067
0
  aRetVal = mShippingOption;
1068
0
}
1069
1070
nsresult
1071
PaymentRequest::UpdateShippingOption(const nsAString& aShippingOption)
1072
0
{
1073
0
  mShippingOption = aShippingOption;
1074
0
1075
0
  // Fire shippingaddresschange event
1076
0
  return DispatchUpdateEvent(NS_LITERAL_STRING("shippingoptionchange"));
1077
0
}
1078
1079
void
1080
PaymentRequest::SetShippingType(const Nullable<PaymentShippingType>& aShippingType)
1081
0
{
1082
0
  mShippingType = aShippingType;
1083
0
}
1084
1085
Nullable<PaymentShippingType>
1086
PaymentRequest::GetShippingType() const
1087
0
{
1088
0
  return mShippingType;
1089
0
}
1090
1091
void PaymentRequest::GetOptions(PaymentOptions& aRetVal) const
1092
0
{
1093
0
  aRetVal = mOptions;
1094
0
}
1095
1096
void PaymentRequest::SetOptions(const PaymentOptions& aOptions)
1097
0
{
1098
0
  mOptions = aOptions;
1099
0
}
1100
1101
void
1102
PaymentRequest::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
1103
0
{
1104
0
  MOZ_ASSERT(aCx);
1105
0
  mUpdating = false;
1106
0
  if (NS_WARN_IF(!aValue.isObject())) {
1107
0
    return;
1108
0
  }
1109
0
1110
0
  // Converting value to a PaymentDetailsUpdate dictionary
1111
0
  PaymentDetailsUpdate details;
1112
0
  if (!details.Init(aCx, aValue)) {
1113
0
    AbortUpdate(NS_ERROR_DOM_TYPE_ERR, mDeferredShow);
1114
0
    JS_ClearPendingException(aCx);
1115
0
    return;
1116
0
  }
1117
0
1118
0
  nsresult rv = IsValidDetailsUpdate(details, mRequestShipping);
1119
0
  if (NS_FAILED(rv)) {
1120
0
    AbortUpdate(rv, mDeferredShow);
1121
0
    return;
1122
0
  }
1123
0
1124
0
  // Update the PaymentRequest with the new details
1125
0
  if (NS_FAILED(UpdatePayment(aCx, details, mDeferredShow))) {
1126
0
    AbortUpdate(NS_ERROR_DOM_ABORT_ERR, mDeferredShow);
1127
0
    return;
1128
0
  }
1129
0
1130
0
  mDeferredShow = false;
1131
0
}
1132
1133
void
1134
PaymentRequest::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
1135
0
{
1136
0
  MOZ_ASSERT(mDeferredShow);
1137
0
  mUpdating = false;
1138
0
  AbortUpdate(NS_ERROR_DOM_ABORT_ERR, mDeferredShow);
1139
0
  mDeferredShow = false;
1140
0
}
1141
1142
void
1143
PaymentRequest::RegisterActivityObserver()
1144
0
{
1145
0
  if (nsPIDOMWindowInner* window = GetOwner()) {
1146
0
    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
1147
0
    if (doc) {
1148
0
      doc->RegisterActivityObserver(
1149
0
        NS_ISUPPORTS_CAST(nsIDocumentActivity*, this));
1150
0
    }
1151
0
  }
1152
0
}
1153
1154
void
1155
PaymentRequest::UnregisterActivityObserver()
1156
0
{
1157
0
  if (nsPIDOMWindowInner* window = GetOwner()) {
1158
0
    nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
1159
0
    if (doc) {
1160
0
      doc->UnregisterActivityObserver(
1161
0
        NS_ISUPPORTS_CAST(nsIDocumentActivity*, this));
1162
0
    }
1163
0
  }
1164
0
}
1165
1166
void
1167
PaymentRequest::NotifyOwnerDocumentActivityChanged()
1168
0
{
1169
0
  nsPIDOMWindowInner* window = GetOwner();
1170
0
  NS_ENSURE_TRUE_VOID(window);
1171
0
  nsIDocument* doc = window->GetExtantDoc();
1172
0
  NS_ENSURE_TRUE_VOID(doc);
1173
0
1174
0
  if (!doc->IsCurrentActiveDocument()) {
1175
0
    RefPtr<PaymentRequestManager> mgr = PaymentRequestManager::GetSingleton();
1176
0
    mgr->ClosePayment(this);
1177
0
  }
1178
0
}
1179
1180
PaymentRequest::~PaymentRequest()
1181
0
{
1182
0
  if (mIPC) {
1183
0
    // If we're being destroyed, the PaymentRequestManager isn't holding any
1184
0
    // references to us and we can't be waiting for any replies.
1185
0
    mIPC->MaybeDelete(false);
1186
0
  }
1187
0
  UnregisterActivityObserver();
1188
0
}
1189
1190
JSObject*
1191
PaymentRequest::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
1192
0
{
1193
0
  return PaymentRequest_Binding::Wrap(aCx, this, aGivenProto);
1194
0
}
1195
1196
} // namespace dom
1197
} // namespace mozilla