[Payment Request] Resolve PaymentRequest.show()'s accept promise when a payment is...
authoraestes@apple.com <aestes@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Oct 2017 21:00:45 +0000 (21:00 +0000)
committeraestes@apple.com <aestes@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 23 Oct 2017 21:00:45 +0000 (21:00 +0000)
https://bugs.webkit.org/show_bug.cgi?id=178609
<rdar://problem/33542813>

Reviewed by Alex Christensen.

Source/WebCore:

This patch implements the logic for resolving PaymentRequest.show()'s accept promise when
the user authorizes a payment, and implements PaymentResponse.complete().

Tests: http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https.html
       http/tests/paymentrequest/payment-response-complete-method.https.html
       http/tests/paymentrequest/payment-response-methodName-attribute.https.html
       http/tests/paymentrequest/payment-response-payerEmail-attribute.https.html
       http/tests/paymentrequest/payment-response-payerName-attribute.https.html
       http/tests/paymentrequest/payment-response-payerPhone-attribute.https.html

* DerivedSources.make:
* Modules/applepay/ApplePayPaymentContact.h:
* Modules/applepay/Payment.h:
(WebCore::Payment::Payment): Deleted.
(WebCore::Payment::pkPayment const): Deleted.
* Modules/applepay/PaymentContact.h:
(WebCore::PaymentContact::PaymentContact): Deleted.
(WebCore::PaymentContact::pkContact const): Deleted.
* Modules/applepay/cocoa/PaymentContactCocoa.mm:
(WebCore::convert):
* Modules/applepay/paymentrequest/ApplePayPaymentHandler.cpp:
(WebCore::ApplePayPaymentHandler::hasActiveSession):
(WebCore::ApplePayPaymentHandler::ApplePayPaymentHandler):
(WebCore::ApplePayPaymentHandler::document):
(WebCore::ApplePayPaymentHandler::paymentCoordinator):
(WebCore::ApplePayPaymentHandler::convertData):
(WebCore::ApplePayPaymentHandler::show):
(WebCore::ApplePayPaymentHandler::hide):
(WebCore::ApplePayPaymentHandler::canMakePayment):
(WebCore::ApplePayPaymentHandler::complete):
(WebCore::convert):
(WebCore::ApplePayPaymentHandler::didAuthorizePayment):
(WebCore::ApplePayPaymentHandler::didSelectShippingMethod):
(WebCore::ApplePayPaymentHandler::didSelectShippingContact):
* Modules/applepay/paymentrequest/ApplePayPaymentHandler.h:
* Modules/paymentrequest/PaymentAddress.h:
* Modules/paymentrequest/PaymentAddress.idl:
* Modules/paymentrequest/PaymentHandler.cpp:
(WebCore::PaymentHandler::create):
* Modules/paymentrequest/PaymentHandler.h:
* Modules/paymentrequest/PaymentRequest.cpp:
(WebCore::PaymentRequest::show):
(WebCore::PaymentRequest::stop):
(WebCore::PaymentRequest::canMakePayment):
(WebCore::PaymentRequest::canSuspendForDocumentSuspension const):
(WebCore::PaymentRequest::shippingAddressChanged):
(WebCore::PaymentRequest::shippingOptionChanged):
(WebCore::PaymentRequest::accept):
(WebCore::PaymentRequest::complete):
* Modules/paymentrequest/PaymentRequest.h:
* Modules/paymentrequest/PaymentResponse.cpp:
(WebCore::PaymentResponse::PaymentResponse):
(WebCore::PaymentResponse::complete):
* Modules/paymentrequest/PaymentResponse.h:
* WebCore.xcodeproj/project.pbxproj:
* testing/Internals.cpp:
(WebCore::Internals::Internals):
(WebCore::Internals::mockPaymentCoordinator const):
* testing/Internals.h:
* testing/Internals.idl:
* testing/MockPayment.h: Added.
* testing/MockPaymentAddress.h: Added.
* testing/MockPaymentAddress.idl: Added.
* testing/MockPaymentContact.h: Added.
* testing/MockPaymentCoordinator.cpp:
(WebCore::MockPaymentCoordinator::canMakePaymentsWithActiveCard):
(WebCore::MockPaymentCoordinator::openPaymentSetup):
(WebCore::dispatchIfShowing):
(WebCore::MockPaymentCoordinator::showPaymentUI):
(WebCore::MockPaymentCoordinator::completeMerchantValidation):
(WebCore::MockPaymentCoordinator::completePaymentSession):
(WebCore::MockPaymentCoordinator::abortPaymentSession):
(WebCore::MockPaymentCoordinator::cancelPaymentSession):
(WebCore::MockPaymentCoordinator::paymentCoordinatorDestroyed):
* testing/MockPaymentCoordinator.h:
* testing/MockPaymentCoordinator.idl: Added.

LayoutTests:

* http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https-expected.txt: Added.
* http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https.html: Copied from imported/w3c/web-platform-tests/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html.
* http/tests/paymentrequest/payment-response-complete-method.https-expected.txt: Added.
* http/tests/paymentrequest/payment-response-complete-method.https.html: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/complete-method-manual.https.html.
* http/tests/paymentrequest/payment-response-methodName-attribute.https-expected.txt: Added.
* http/tests/paymentrequest/payment-response-methodName-attribute.https.html: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/methodName-attribute-manual.https.html.
* http/tests/paymentrequest/payment-response-payerEmail-attribute.https-expected.txt: Added.
* http/tests/paymentrequest/payment-response-payerEmail-attribute.https.html: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/payerEmail-attribute-manual.https.html.
* http/tests/paymentrequest/payment-response-payerName-attribute.https-expected.txt: Added.
* http/tests/paymentrequest/payment-response-payerName-attribute.https.html: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/payerName-attribute-manual.https.html.
* http/tests/paymentrequest/payment-response-payerPhone-attribute.https-expected.txt: Added.
* http/tests/paymentrequest/payment-response-payerPhone-attribute.https.html: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/payerPhone-attribute-manual.https.html.
* http/tests/paymentrequest/resources/helpers.js: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/helpers.js.
(test):
(async.getPaymentResponse):
(async.getPaymentRequestResponse):
(async.runTest):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@223855 268f45cc-cd09-0410-ab3c-d52691b4dbfc

42 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https.html [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/payment-response-complete-method.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/payment-response-complete-method.https.html [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/payment-response-methodName-attribute.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/payment-response-methodName-attribute.https.html [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/payment-response-payerEmail-attribute.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/payment-response-payerEmail-attribute.https.html [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/payment-response-payerName-attribute.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/payment-response-payerName-attribute.https.html [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/payment-response-payerPhone-attribute.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/payment-response-payerPhone-attribute.https.html [new file with mode: 0644]
LayoutTests/http/tests/paymentrequest/resources/helpers.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/DerivedSources.make
Source/WebCore/Modules/applepay/ApplePayPaymentContact.h
Source/WebCore/Modules/applepay/Payment.h
Source/WebCore/Modules/applepay/PaymentContact.h
Source/WebCore/Modules/applepay/cocoa/PaymentContactCocoa.mm
Source/WebCore/Modules/applepay/paymentrequest/ApplePayPaymentHandler.cpp
Source/WebCore/Modules/applepay/paymentrequest/ApplePayPaymentHandler.h
Source/WebCore/Modules/paymentrequest/PaymentAddress.cpp [new file with mode: 0644]
Source/WebCore/Modules/paymentrequest/PaymentAddress.h
Source/WebCore/Modules/paymentrequest/PaymentAddress.idl
Source/WebCore/Modules/paymentrequest/PaymentHandler.cpp
Source/WebCore/Modules/paymentrequest/PaymentHandler.h
Source/WebCore/Modules/paymentrequest/PaymentRequest.cpp
Source/WebCore/Modules/paymentrequest/PaymentRequest.h
Source/WebCore/Modules/paymentrequest/PaymentResponse.cpp
Source/WebCore/Modules/paymentrequest/PaymentResponse.h
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl
Source/WebCore/testing/MockPayment.h [new file with mode: 0644]
Source/WebCore/testing/MockPaymentAddress.h [new file with mode: 0644]
Source/WebCore/testing/MockPaymentAddress.idl [new file with mode: 0644]
Source/WebCore/testing/MockPaymentContact.h [new file with mode: 0644]
Source/WebCore/testing/MockPaymentCoordinator.cpp
Source/WebCore/testing/MockPaymentCoordinator.h
Source/WebCore/testing/MockPaymentCoordinator.idl [new file with mode: 0644]

index 30f2c63..67bfdf7 100644 (file)
@@ -1,3 +1,29 @@
+2017-10-23  Andy Estes  <aestes@apple.com>
+
+        [Payment Request] Resolve PaymentRequest.show()'s accept promise when a payment is authorized
+        https://bugs.webkit.org/show_bug.cgi?id=178609
+        <rdar://problem/33542813>
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https-expected.txt: Added.
+        * http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https.html: Copied from imported/w3c/web-platform-tests/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html.
+        * http/tests/paymentrequest/payment-response-complete-method.https-expected.txt: Added.
+        * http/tests/paymentrequest/payment-response-complete-method.https.html: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/complete-method-manual.https.html.
+        * http/tests/paymentrequest/payment-response-methodName-attribute.https-expected.txt: Added.
+        * http/tests/paymentrequest/payment-response-methodName-attribute.https.html: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/methodName-attribute-manual.https.html.
+        * http/tests/paymentrequest/payment-response-payerEmail-attribute.https-expected.txt: Added.
+        * http/tests/paymentrequest/payment-response-payerEmail-attribute.https.html: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/payerEmail-attribute-manual.https.html.
+        * http/tests/paymentrequest/payment-response-payerName-attribute.https-expected.txt: Added.
+        * http/tests/paymentrequest/payment-response-payerName-attribute.https.html: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/payerName-attribute-manual.https.html.
+        * http/tests/paymentrequest/payment-response-payerPhone-attribute.https-expected.txt: Added.
+        * http/tests/paymentrequest/payment-response-payerPhone-attribute.https.html: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/payerPhone-attribute-manual.https.html.
+        * http/tests/paymentrequest/resources/helpers.js: Copied from imported/w3c/web-platform-tests/payment-request/payment-response/helpers.js.
+        (test):
+        (async.getPaymentResponse):
+        (async.getPaymentRequestResponse):
+        (async.runTest):
+
 2017-10-23  Chris Dumez  <cdumez@apple.com>
 
         Drop confusing Event::dispatched() method
diff --git a/LayoutTests/http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https-expected.txt b/LayoutTests/http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https-expected.txt
new file mode 100644 (file)
index 0000000..3287a08
--- /dev/null
@@ -0,0 +1,5 @@
+If the requestShipping member is true, then shippingAddress's PaymentAddress must match the expected values.
+
+PASS Can construct a payment request (smoke test). 
+PASS If the requestShipping member is true, then shippingAddress's PaymentAddress must match the expected values. 
+
diff --git a/LayoutTests/http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https.html b/LayoutTests/http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https.html
new file mode 100644 (file)
index 0000000..a5f080b
--- /dev/null
@@ -0,0 +1,90 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://www.w3.org/TR/payment-request/#paymentaddress-interface">
+<title>
+  PaymentResponse.prototype.shippingAddress
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script>
+const options = { requestShipping: true };
+function runTest(button, expected = {}) {
+  button.disabled = true;
+  promise_test(async () => {
+    const { response } = await getPaymentRequestResponse(options);
+    await response.complete();
+    assert_idl_attribute(response, "shippingAddress");
+    const { shippingAddress: addr } = response;
+    assert_true(
+      addr instanceof PaymentAddress,
+      "Expect instance of PaymentAddress"
+    );
+    // An [ISO3166] alpha-2 code. The canonical form is upper case.
+    const { country } = addr;
+    assert_equals(country.length, 2, "Expected length is 2");
+    assert_true(/^[A-Z]{2}$/.test(country), "Canonical form is upper case");
+    assert_true(
+      addr.addressLine instanceof Array,
+      "Expected addressLine to be an array"
+    );
+    assert_throws(
+      new TypeError(),
+      () => {
+        addr.addressLine.push("this must throw");
+      },
+      "Array must be frozen"
+    );
+    for (let [attr, expectedValue] of Object.entries(expected)) {
+      assert_idl_attribute(addr, attr);
+      const msg = `Expected paymentAddress.${attr} to equal ${expectedValue}.`;
+      //.toString() flattens array addressLine,
+      //.toLowerCase() because case can't be enforced for some attributes
+      const actualValue = addr[attr].toString().toLowerCase();
+      expectedValue = expectedValue.toString().toLowerCase();
+      assert_equals(actualValue, expectedValue, msg);
+    }
+    // Check toJSON result
+    for (let [prop, jsonValue] of Object.entries(addr.toJSON())) {
+      const actualValue = jsonValue.toString().toLowerCase();
+      const expectedValue = expected[prop].toString().toLowerCase();
+      const msg = `Expected JSON ${prop} to be ${expectedValue}`;
+      assert_equals(actualValue, expectedValue, msg);
+    }
+  }, button.textContent.trim());
+  done();
+}
+</script>
+<ol>
+  <li>
+    <button id="button">
+      If the requestShipping member is true, then shippingAddress's PaymentAddress must match the expected values.
+    </button>
+  </li>
+</ol>
+<script>
+    const address = {
+        countryCode: 'AF',
+        addressLines: ['1 wpt street'],
+        locality: 'Kabul',
+        postalCode: '1001',
+        localizedName: 'web platform test',
+        phoneNumber: '+93555555555',
+    };
+    internals.mockPaymentCoordinator.setShippingAddress(address);
+
+    const expectedAddress = {
+        country: address.countryCode,
+        addressLine: address.addressLines,
+        region: '',
+        city: address.locality,
+        dependentLocality: '',
+        postalCode: address.postalCode,
+        sortingCode: '',
+        languageCode: '',
+        organization: '',
+        recipient: address.localizedName,
+        phone: address.phoneNumber,
+    };
+    runTest(document.getElementById("button"), expectedAddress);
+</script>
diff --git a/LayoutTests/http/tests/paymentrequest/payment-response-complete-method.https-expected.txt b/LayoutTests/http/tests/paymentrequest/payment-response-complete-method.https-expected.txt
new file mode 100644 (file)
index 0000000..790691e
--- /dev/null
@@ -0,0 +1,13 @@
+✅ If the value of the internal slot [[completeCalled]] is true, reject promise with an "InvalidStateError" DOMException.
+✅ Passing no argument defaults to "unknown", eventually closing the sheet and doesn't throw.
+✅ Passing "success" eventually closes the sheet and doesn't throw.
+✅ Passing "fail" eventually closes the sheet and doesn't throw.
+
+PASS Can construct a payment request (smoke test). 
+PASS If the value of the internal slot [[completeCalled]] is true,
+      reject promise with an "InvalidStateError" DOMException. 
+PASS Passing no argument defaults to "unknown",
+      eventually closing the sheet and doesn't throw. 
+PASS Passing "success" eventually closes the sheet and doesn't throw. 
+PASS Passing "fail" eventually closes the sheet and doesn't throw. 
+
diff --git a/LayoutTests/http/tests/paymentrequest/payment-response-complete-method.https.html b/LayoutTests/http/tests/paymentrequest/payment-response-complete-method.https.html
new file mode 100644 (file)
index 0000000..5fd6827
--- /dev/null
@@ -0,0 +1,79 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-complete()">
+<title>
+  PaymentResponse.prototype.complete() method
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<script>
+async function runTest({ completeWith: result }, button) {
+  button.disabled = true;
+  const { response, request } = await getPaymentRequestResponse();
+  promise_test(async () => {
+    try {
+      // We .complete() as normal, using the passed test value
+      const promise = response.complete(result);
+      assert_true(promise instanceof Promise, "returns a promise");
+      const returnedValue = await promise;
+      assert_equals(
+        returnedValue,
+        undefined,
+        "Returned value must always be undefined"
+      );
+      // We now call .complete() again, to force an exception
+      // because [[completeCalled]] is true.
+      try {
+        await response.complete(result);
+        assert_unreached("Expected InvalidStateError to be thrown");
+      } catch (err) {
+        assert_equals(
+          err.code,
+          DOMException.INVALID_STATE_ERR,
+          "Must throw an InvalidStateError"
+        );
+      }
+      button.innerHTML = `✅  ${button.textContent}`;
+    } catch (err) {
+      button.innerHTML = `❌  ${button.textContent}`;
+      assert_unreached("Unexpected exception: " + err.message);
+    }
+  }, button.textContent.trim());
+}
+</script>
+<ol>
+  <li>
+    <button id="button1">
+      If the value of the internal slot [[completeCalled]] is true,
+      reject promise with an "InvalidStateError" DOMException.
+    </button>
+  </li>
+  <li>
+    <button id="button2">
+      Passing no argument defaults to "unknown",
+      eventually closing the sheet and doesn't throw.
+    </button>
+  </li>
+  <li>
+    <button id="button3">
+      Passing "success" eventually closes the sheet and doesn't throw.
+    </button>
+  </li>
+  <li>
+    <button id="button4">
+      Passing "fail" eventually closes the sheet and doesn't throw.
+    </button>
+  </li>
+</ol>
+<script>
+    async function runTests()
+    {
+        await runTest({completeWith: "success"}, document.getElementById("button1"));
+        await runTest({completeWith: "unknown"}, document.getElementById("button2"));
+        await runTest({completeWith: "success"}, document.getElementById("button3"));
+        await runTest({completeWith: "fail"}, document.getElementById("button4"));
+        done();
+    }
+    runTests();
+</script>
diff --git a/LayoutTests/http/tests/paymentrequest/payment-response-methodName-attribute.https-expected.txt b/LayoutTests/http/tests/paymentrequest/payment-response-methodName-attribute.https-expected.txt
new file mode 100644 (file)
index 0000000..b35c848
--- /dev/null
@@ -0,0 +1,5 @@
+Expect the payment method identifier to be 'https://apple.com/apple-pay'.
+
+PASS Can construct a payment request (smoke test). 
+PASS Expect the payment method identifier to be 'https://apple.com/apple-pay'. 
+
diff --git a/LayoutTests/http/tests/paymentrequest/payment-response-methodName-attribute.https.html b/LayoutTests/http/tests/paymentrequest/payment-response-methodName-attribute.https.html
new file mode 100644 (file)
index 0000000..97e89a6
--- /dev/null
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-methodname">
+<title>
+  PaymentResponse.prototype.methodName attribute
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<ol>
+  <li>
+    <button id="button">
+      Expect the payment method identifier to be 'https://apple.com/apple-pay'.
+    </button>
+  </li>
+</ol>
+<script>
+    runTest(document.getElementById("button"), {}, { methodName: 'https://apple.com/apple-pay' }).then(done);
+</script>
diff --git a/LayoutTests/http/tests/paymentrequest/payment-response-payerEmail-attribute.https-expected.txt b/LayoutTests/http/tests/paymentrequest/payment-response-payerEmail-attribute.https-expected.txt
new file mode 100644 (file)
index 0000000..f539645
--- /dev/null
@@ -0,0 +1,13 @@
+payerEmail attribute is null when options undefined.
+payerEmail attribute is null when requestPayerEmail is undefined.
+payerEmail attribute is null when requestPayerEmail is false.
+payerEmail attribute is 'wpt@w3.org' when requestPayerEmail is true.
+payerEmail attribute is 'wpt@w3.org' when requestPayerEmail is truthy.
+
+PASS Can construct a payment request (smoke test). 
+PASS payerEmail attribute is null when options undefined. 
+PASS payerEmail attribute is null when requestPayerEmail is undefined. 
+PASS payerEmail attribute is null when requestPayerEmail is false. 
+PASS payerEmail attribute is 'wpt@w3.org' when requestPayerEmail is true. 
+PASS payerEmail attribute is 'wpt@w3.org' when requestPayerEmail is truthy. 
+
diff --git a/LayoutTests/http/tests/paymentrequest/payment-response-payerEmail-attribute.https.html b/LayoutTests/http/tests/paymentrequest/payment-response-payerEmail-attribute.https.html
new file mode 100644 (file)
index 0000000..83fb935
--- /dev/null
@@ -0,0 +1,50 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-payeremail">
+<title>
+  PaymentResponse.prototype.payerEmail attribute
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<ol>
+  <li>
+    <button id="button1">
+      payerEmail attribute is null when options undefined.
+    </button>
+  </li>
+  <li>
+    <button id="button2">
+      payerEmail attribute is null when requestPayerEmail is undefined.
+    </button>
+  </li>
+  <li>
+    <button id="button3">
+      payerEmail attribute is null when requestPayerEmail is false.
+    </button>
+  </li>
+  <li>
+    <button id="button4">
+      payerEmail attribute is 'wpt@w3.org' when requestPayerEmail is true.
+    </button>
+  </li>
+  <li>
+    <button id="button5">
+      payerEmail attribute is 'wpt@w3.org' when requestPayerEmail is truthy.
+    </button>
+  </li>
+</ol>
+<script>
+    internals.mockPaymentCoordinator.setShippingAddress({ emailAddress: 'wpt@w3.org' });
+
+    async function runTests()
+    {
+        await runTest(document.getElementById("button1"), undefined, { payerEmail: null });
+        await runTest(document.getElementById("button2"), { requestPayerEmail: undefined }, { payerEmail: null });
+        await runTest(document.getElementById("button3"), { requestPayerEmail: false }, { payerEmail: null });
+        await runTest(document.getElementById("button4"), { requestPayerEmail: true }, { payerEmail: 'wpt@w3.org' });
+        await runTest(document.getElementById("button5"), { requestPayerEmail: 'yep' }, { payerEmail: 'wpt@w3.org' });
+        done();
+    }
+    runTests();
+</script>
\ No newline at end of file
diff --git a/LayoutTests/http/tests/paymentrequest/payment-response-payerName-attribute.https-expected.txt b/LayoutTests/http/tests/paymentrequest/payment-response-payerName-attribute.https-expected.txt
new file mode 100644 (file)
index 0000000..972fa6d
--- /dev/null
@@ -0,0 +1,13 @@
+payerName attribute is null when option is undefined.
+payerName attribute is null when requestPayerName is undefined.
+payerName attribute is null when requestPayerName is false.
+payerName attribute is 'web platform test' when requestPayerName is true.
+payerName attribute is 'web platform test' when requestPayerName is truthy.
+
+PASS Can construct a payment request (smoke test). 
+PASS payerName attribute is null when option is undefined. 
+PASS payerName attribute is null when requestPayerName is undefined. 
+PASS payerName attribute is null when requestPayerName is false. 
+PASS payerName attribute is 'web platform test' when requestPayerName is true. 
+PASS payerName attribute is 'web platform test' when requestPayerName is truthy. 
+
diff --git a/LayoutTests/http/tests/paymentrequest/payment-response-payerName-attribute.https.html b/LayoutTests/http/tests/paymentrequest/payment-response-payerName-attribute.https.html
new file mode 100644 (file)
index 0000000..a723b2d
--- /dev/null
@@ -0,0 +1,50 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-payername">
+<title>
+  PaymentResponse.prototype.payerName attribute
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<ol>
+  <li>
+    <button id="button1">
+      payerName attribute is null when option is undefined.
+    </button>
+  </li>
+  <li>
+    <button id="button2">
+      payerName attribute is null when requestPayerName is undefined.
+    </button>
+  </li>
+  <li>
+    <button id="button3">
+      payerName attribute is null when requestPayerName is false.
+    </button>
+  </li>
+  <li>
+    <button id="button4">
+      payerName attribute is 'web platform test' when requestPayerName is true.
+    </button>
+  </li>
+  <li>
+    <button id="button5">
+      payerName attribute is 'web platform test' when requestPayerName is truthy.
+    </button>
+  </li>
+</ol>
+<script>
+    internals.mockPaymentCoordinator.setShippingAddress({ localizedName: 'web platform test' });
+    
+    async function runTests()
+    {
+        await runTest(document.getElementById("button1"), undefined, { payerName: null });
+        await runTest(document.getElementById("button2"), { requestPayerName: undefined }, { payerName: null });
+        await runTest(document.getElementById("button3"), { requestPayerName: false }, { payerName: null });
+        await runTest(document.getElementById("button4"), { requestPayerName: true }, { payerName: 'web platform test' });
+        await runTest(document.getElementById("button5"), { requestPayerName: 'yep' }, { payerName: 'web platform test' });
+        done();
+    }
+    runTests();
+</script>
\ No newline at end of file
diff --git a/LayoutTests/http/tests/paymentrequest/payment-response-payerPhone-attribute.https-expected.txt b/LayoutTests/http/tests/paymentrequest/payment-response-payerPhone-attribute.https-expected.txt
new file mode 100644 (file)
index 0000000..c87f72c
--- /dev/null
@@ -0,0 +1,13 @@
+payerPhone attribute is null when options is undefined.
+payerPhone attribute is null when requestPayerPhone is undefined.
+payerPhone attribute is null when requestPayerPhone is false.
+payerPhone attribute is '+12345678910' when requestPayerPhone is true.
+payerPhone attribute is '+12345678910' when requestPayerPhone is truthy.
+
+PASS Can construct a payment request (smoke test). 
+PASS payerPhone attribute is null when options is undefined. 
+PASS payerPhone attribute is null when requestPayerPhone is undefined. 
+PASS payerPhone attribute is null when requestPayerPhone is false. 
+PASS payerPhone attribute is '+12345678910' when requestPayerPhone is true. 
+PASS payerPhone attribute is '+12345678910' when requestPayerPhone is truthy. 
+
diff --git a/LayoutTests/http/tests/paymentrequest/payment-response-payerPhone-attribute.https.html b/LayoutTests/http/tests/paymentrequest/payment-response-payerPhone-attribute.https.html
new file mode 100644 (file)
index 0000000..0a40c48
--- /dev/null
@@ -0,0 +1,50 @@
+<!doctype html>
+<meta charset="utf8">
+<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-payerphone">
+<title>
+  PaymentResponse.prototype.payerPhone attribute
+</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/helpers.js"></script>
+<ol>
+  <li>
+    <button id="button1">
+      payerPhone attribute is null when options is undefined.
+    </button>
+  </li>
+  <li>
+    <button id="button2">
+      payerPhone attribute is null when requestPayerPhone is undefined.
+    </button>
+  </li>
+  <li>
+    <button id="button3">
+      payerPhone attribute is null when requestPayerPhone is false.
+    </button>
+  </li>
+  <li>
+    <button id="button4">
+      payerPhone attribute is '+12345678910' when requestPayerPhone is true.
+    </button>
+  </li>
+  <li>
+    <button id="button5">
+      payerPhone attribute is '+12345678910' when requestPayerPhone is truthy.
+    </button>
+  </li>
+</ol>
+<script>
+    internals.mockPaymentCoordinator.setShippingAddress({ phoneNumber: '+12345678910' });
+    
+    async function runTests()
+    {
+        await runTest(document.getElementById("button1"), undefined, { payerPhone: null });
+        await runTest(document.getElementById("button2"), { requestPayerPhone: undefined }, { payerPhone: null });
+        await runTest(document.getElementById("button3"), { requestPayerPhone: false }, { payerPhone: null });
+        await runTest(document.getElementById("button4"), { requestPayerPhone: true }, { payerPhone: '+12345678910' });
+        await runTest(document.getElementById("button5"), { requestPayerPhone: 'yep' }, { payerPhone: '+12345678910' });
+        done();
+    }
+    runTests();
+</script>
diff --git a/LayoutTests/http/tests/paymentrequest/resources/helpers.js b/LayoutTests/http/tests/paymentrequest/resources/helpers.js
new file mode 100644 (file)
index 0000000..0122c29
--- /dev/null
@@ -0,0 +1,156 @@
+setup({ explicit_done: true, explicit_timeout: true });
+
+const validMethod = Object.freeze({
+  supportedMethods: "https://apple.com/apple-pay",
+  data: {
+      version: 2,
+      merchantIdentifier: '',
+      merchantCapabilities: ['supports3DS'],
+      supportedNetworks: ['visa', 'masterCard'],
+      countryCode: 'US',
+  },
+});
+
+const validMethods = Object.freeze([validMethod]);
+
+const validAmount = Object.freeze({
+  currency: "USD",
+  value: "1.00",
+});
+
+const validTotal = Object.freeze({
+  label: "Valid total",
+  amount: validAmount,
+});
+const validDetails = {
+  total: validTotal,
+};
+
+test(() => {
+  try {
+    new PaymentRequest(validMethods, validDetails);
+  } catch (err) {
+    done();
+    throw err;
+  }
+}, "Can construct a payment request (smoke test).");
+
+/**
+ * Pops up a payment sheet, allowing options to be
+ * passed in if particular values are needed.
+ *
+ * @param PaymentOptions options
+ */
+async function getPaymentResponse(options, id) {
+  const { response } = getPaymentRequestResponse(options, id);
+  return response;
+}
+
+/**
+ * Creates a payment request and response pair.
+ * It also shows the payment sheet.
+ *
+ * @param {PaymentOptions?} options
+ * @param {String?} id
+ */
+async function getPaymentRequestResponse(options, id) {
+  const methods = [{
+      supportedMethods: "https://apple.com/apple-pay",
+      data: {
+          version: 2,
+          merchantIdentifier: '',
+          merchantCapabilities: ['supports3DS'],
+          supportedNetworks: ['visa', 'masterCard'],
+          countryCode: 'US',
+      },
+  }];
+  const details = {
+    id,
+    total: {
+      label: "Total due",
+      amount: { currency: "USD", value: "1.0" },
+    },
+    shippingOptions: [
+      {
+        id: "fail1",
+        label: "Fail option 1",
+        amount: { currency: "USD", value: "5.00" },
+        selected: false,
+      },
+      {
+        id: "pass",
+        label: "Pass option",
+        amount: { currency: "USD", value: "5.00" },
+        selected: true,
+      },
+      {
+        id: "fail2",
+        label: "Fail option 2",
+        amount: { currency: "USD", value: "5.00" },
+        selected: false,
+      },
+    ],
+  };
+  const request = new PaymentRequest(methods, details, options);
+  request.onshippingaddresschange = ev => ev.updateWith(details);
+  request.onshippingoptionchange = ev => ev.updateWith(details);
+  request.onapplepayvalidatemerchant = ev => ev.complete({});
+  const response = await request.show();
+  return { request, response };
+}
+
+/**
+ * Runs a manual test for payment
+ *
+ * @param {HTMLButtonElement} button The HTML button.
+ * @param {PaymentOptions?} options.
+ * @param {Object} expected What property values are expected to pass the test.
+ * @param {String?} id And id for the request/response pair.
+ */
+async function runTest(button, options, expected = {}, id = undefined) {
+  button.disabled = true;
+  const { request, response } = await getPaymentRequestResponse(options, id);
+  await response.complete();
+  test(() => {
+    assert_idl_attribute(
+      response,
+      "requestId",
+      "Expected requestId to be an IDL attribute."
+    );
+    assert_equals(response.requestId, request.id, `Expected ids to match`);
+    for (const [attribute, value] of Object.entries(expected)) {
+      assert_idl_attribute(
+        response,
+        attribute,
+        `Expected ${attribute} to be an IDL attribute.`
+      );
+      assert_equals(
+        response[attribute],
+        value,
+        `Expected response ${attribute} attribute to be ${value}`
+      );
+    }
+    assert_idl_attribute(response, "details");
+    assert_equals(typeof response.details, "object", "Expected an object");
+    // Testing that this does not throw:
+    response.toJSON();
+    if (options && options.requestShipping) {
+      assert_equals(
+        response.shippingOption,
+        "pass",
+        "request.shippingOption must be 'pass'"
+      );
+    } else {
+      assert_equals(
+        request.shippingOption,
+        null,
+        "If requestShipping is falsy, request.shippingOption must be null"
+      );
+      assert_equals(
+        response.shippingOption,
+        null,
+        "request.shippingOption must be null"
+      );
+    }
+  }, button.textContent.trim());
+}
index f6fb925..5dd5d0f 100644 (file)
@@ -1,3 +1,88 @@
+2017-10-23  Andy Estes  <aestes@apple.com>
+
+        [Payment Request] Resolve PaymentRequest.show()'s accept promise when a payment is authorized
+        https://bugs.webkit.org/show_bug.cgi?id=178609
+        <rdar://problem/33542813>
+
+        Reviewed by Alex Christensen.
+
+        This patch implements the logic for resolving PaymentRequest.show()'s accept promise when
+        the user authorizes a payment, and implements PaymentResponse.complete().
+
+        Tests: http/tests/paymentrequest/payment-address-attributes-and-toJSON-method.https.html
+               http/tests/paymentrequest/payment-response-complete-method.https.html
+               http/tests/paymentrequest/payment-response-methodName-attribute.https.html
+               http/tests/paymentrequest/payment-response-payerEmail-attribute.https.html
+               http/tests/paymentrequest/payment-response-payerName-attribute.https.html
+               http/tests/paymentrequest/payment-response-payerPhone-attribute.https.html
+
+        * DerivedSources.make:
+        * Modules/applepay/ApplePayPaymentContact.h:
+        * Modules/applepay/Payment.h:
+        (WebCore::Payment::Payment): Deleted.
+        (WebCore::Payment::pkPayment const): Deleted.
+        * Modules/applepay/PaymentContact.h:
+        (WebCore::PaymentContact::PaymentContact): Deleted.
+        (WebCore::PaymentContact::pkContact const): Deleted.
+        * Modules/applepay/cocoa/PaymentContactCocoa.mm:
+        (WebCore::convert):
+        * Modules/applepay/paymentrequest/ApplePayPaymentHandler.cpp:
+        (WebCore::ApplePayPaymentHandler::hasActiveSession):
+        (WebCore::ApplePayPaymentHandler::ApplePayPaymentHandler):
+        (WebCore::ApplePayPaymentHandler::document):
+        (WebCore::ApplePayPaymentHandler::paymentCoordinator):
+        (WebCore::ApplePayPaymentHandler::convertData):
+        (WebCore::ApplePayPaymentHandler::show):
+        (WebCore::ApplePayPaymentHandler::hide):
+        (WebCore::ApplePayPaymentHandler::canMakePayment):
+        (WebCore::ApplePayPaymentHandler::complete):
+        (WebCore::convert):
+        (WebCore::ApplePayPaymentHandler::didAuthorizePayment):
+        (WebCore::ApplePayPaymentHandler::didSelectShippingMethod):
+        (WebCore::ApplePayPaymentHandler::didSelectShippingContact):
+        * Modules/applepay/paymentrequest/ApplePayPaymentHandler.h:
+        * Modules/paymentrequest/PaymentAddress.h:
+        * Modules/paymentrequest/PaymentAddress.idl:
+        * Modules/paymentrequest/PaymentHandler.cpp:
+        (WebCore::PaymentHandler::create):
+        * Modules/paymentrequest/PaymentHandler.h:
+        * Modules/paymentrequest/PaymentRequest.cpp:
+        (WebCore::PaymentRequest::show):
+        (WebCore::PaymentRequest::stop):
+        (WebCore::PaymentRequest::canMakePayment):
+        (WebCore::PaymentRequest::canSuspendForDocumentSuspension const):
+        (WebCore::PaymentRequest::shippingAddressChanged):
+        (WebCore::PaymentRequest::shippingOptionChanged):
+        (WebCore::PaymentRequest::accept):
+        (WebCore::PaymentRequest::complete):
+        * Modules/paymentrequest/PaymentRequest.h:
+        * Modules/paymentrequest/PaymentResponse.cpp:
+        (WebCore::PaymentResponse::PaymentResponse):
+        (WebCore::PaymentResponse::complete):
+        * Modules/paymentrequest/PaymentResponse.h:
+        * WebCore.xcodeproj/project.pbxproj:
+        * testing/Internals.cpp:
+        (WebCore::Internals::Internals):
+        (WebCore::Internals::mockPaymentCoordinator const):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+        * testing/MockPayment.h: Added.
+        * testing/MockPaymentAddress.h: Added.
+        * testing/MockPaymentAddress.idl: Added.
+        * testing/MockPaymentContact.h: Added.
+        * testing/MockPaymentCoordinator.cpp:
+        (WebCore::MockPaymentCoordinator::canMakePaymentsWithActiveCard):
+        (WebCore::MockPaymentCoordinator::openPaymentSetup):
+        (WebCore::dispatchIfShowing):
+        (WebCore::MockPaymentCoordinator::showPaymentUI):
+        (WebCore::MockPaymentCoordinator::completeMerchantValidation):
+        (WebCore::MockPaymentCoordinator::completePaymentSession):
+        (WebCore::MockPaymentCoordinator::abortPaymentSession):
+        (WebCore::MockPaymentCoordinator::cancelPaymentSession):
+        (WebCore::MockPaymentCoordinator::paymentCoordinatorDestroyed):
+        * testing/MockPaymentCoordinator.h:
+        * testing/MockPaymentCoordinator.idl: Added.
+
 2017-10-23  Dean Jackson  <dino@apple.com>
 
         Attempt to stop iOS Simulator tests from failing because
index 655b5c8..e5302cc 100644 (file)
@@ -908,6 +908,8 @@ JS_BINDING_IDLS = \
     $(WebCore)/testing/MockCDMFactory.idl \
     $(WebCore)/testing/MockContentFilterSettings.idl \
     $(WebCore)/testing/MockPageOverlay.idl \
+    $(WebCore)/testing/MockPaymentAddress.idl \
+    $(WebCore)/testing/MockPaymentCoordinator.idl \
     $(WebCore)/testing/TypeConversions.idl \
     $(WebCore)/workers/AbstractWorker.idl \
     $(WebCore)/workers/DedicatedWorkerGlobalScope.idl \
index eb6e28f..dba99ff 100644 (file)
@@ -38,8 +38,10 @@ struct ApplePayPaymentContact {
     String emailAddress;
     String givenName;
     String familyName;
+    String localizedName;
     String phoneticGivenName;
     String phoneticFamilyName;
+    String localizedPhoneticName;
     std::optional<Vector<String>> addressLines;
     String subLocality;
     String locality;
index 73ff2b9..c9054d7 100644 (file)
@@ -35,7 +35,7 @@ namespace WebCore {
 
 struct ApplePayPayment;
 
-class Payment {
+class WEBCORE_EXPORT Payment {
 public:
     Payment() = default;
 
@@ -44,7 +44,9 @@ public:
     {
     }
 
-    ApplePayPayment toApplePayPayment() const;
+    virtual ~Payment() = default;
+
+    virtual ApplePayPayment toApplePayPayment() const;
 
     PKPayment *pkPayment() const { return m_pkPayment.get(); }
 
index 0e7d612..ec010ac 100644 (file)
@@ -36,16 +36,17 @@ namespace WebCore {
 
 struct ApplePayPaymentContact;
 
-class PaymentContact {
+class WEBCORE_EXPORT PaymentContact {
 public:
     PaymentContact() = default;
     explicit PaymentContact(PKContact *pkContact)
         : m_pkContact(pkContact)
     {
     }
+    virtual ~PaymentContact() = default;
 
     static PaymentContact fromApplePayPaymentContact(unsigned version, const ApplePayPaymentContact&);
-    ApplePayPaymentContact toApplePayPaymentContact() const;
+    virtual ApplePayPaymentContact toApplePayPaymentContact() const;
 
     PKContact *pkContact() const { return m_pkContact.get(); }
 
index 07bbece..0d82249 100644 (file)
@@ -189,10 +189,12 @@ static ApplePayPaymentContact convert(PKContact *contact)
     NSPersonNameComponents *name = contact.name;
     result.givenName = name.givenName;
     result.familyName = name.familyName;
+    result.localizedName = [NSPersonNameComponentsFormatter localizedStringFromPersonNameComponents:name style:NSPersonNameComponentsFormatterStyleDefault options:0];
 
     NSPersonNameComponents *phoneticName = name.phoneticRepresentation;
     result.phoneticGivenName = phoneticName.givenName;
     result.phoneticFamilyName = phoneticName.familyName;
+    result.localizedPhoneticName = [NSPersonNameComponentsFormatter localizedStringFromPersonNameComponents:name style:NSPersonNameComponentsFormatterStyleDefault options:NSPersonNameComponentsFormatterPhonetic];
 
     CNPostalAddress *postalAddress = contact.postalAddress;
     if (postalAddress.street.length) {
index 1b89de0..8a605ac 100644 (file)
 #include "ApplePayContactField.h"
 #include "ApplePayMerchantCapability.h"
 #include "ApplePayMerchantValidationEvent.h"
+#include "ApplePayPayment.h"
 #include "ApplePaySessionPaymentRequest.h"
 #include "Document.h"
 #include "EventNames.h"
+#include "JSApplePayPayment.h"
 #include "JSApplePayRequest.h"
 #include "LinkIconCollector.h"
 #include "MainFrame.h"
 #include "Page.h"
+#include "Payment.h"
+#include "PaymentAuthorizationStatus.h"
 #include "PaymentContact.h"
 #include "PaymentCoordinator.h"
 #include "PaymentRequestValidator.h"
+#include "PaymentResponse.h"
 #include "Settings.h"
 
 namespace WebCore {
@@ -61,12 +66,25 @@ static inline PaymentCoordinator& paymentCoordinator(Document& document)
 
 bool ApplePayPaymentHandler::hasActiveSession(Document& document)
 {
-    return paymentCoordinator(document).hasActiveSession();
+    return WebCore::paymentCoordinator(document).hasActiveSession();
 }
 
-ApplePayPaymentHandler::ApplePayPaymentHandler(PaymentRequest& paymentRequest)
-    : m_paymentRequest { paymentRequest }
+ApplePayPaymentHandler::ApplePayPaymentHandler(Document& document, const PaymentRequest::MethodIdentifier& identifier, PaymentRequest& paymentRequest)
+    : ContextDestructionObserver { &document }
+    , m_identifier { identifier }
+    , m_paymentRequest { paymentRequest }
 {
+    ASSERT(handlesIdentifier(m_identifier));
+}
+
+Document& ApplePayPaymentHandler::document()
+{
+    return downcast<Document>(*scriptExecutionContext());
+}
+
+PaymentCoordinator& ApplePayPaymentHandler::paymentCoordinator()
+{
+    return WebCore::paymentCoordinator(document());
 }
 
 static ExceptionOr<void> validate(const PaymentCurrencyAmount& amount, const String& expectedCurrency)
@@ -140,10 +158,11 @@ static ExceptionOr<ApplePaySessionPaymentRequest::ShippingMethod> convertAndVali
     return { WTFMove(result) };
 }
 
-ExceptionOr<void> ApplePayPaymentHandler::convertData(JSC::ExecState& execState, JSC::JSValue&& data)
+ExceptionOr<void> ApplePayPaymentHandler::convertData(JSC::JSValue&& data)
 {
-    auto throwScope = DECLARE_THROW_SCOPE(execState.vm());
-    auto applePayRequest = convertDictionary<ApplePayRequest>(execState, WTFMove(data));
+    auto& context = *scriptExecutionContext();
+    auto throwScope = DECLARE_THROW_SCOPE(context.vm());
+    auto applePayRequest = convertDictionary<ApplePayRequest>(*context.execState(), WTFMove(data));
     if (throwScope.exception())
         return Exception { ExistingExceptionError };
 
@@ -151,7 +170,7 @@ ExceptionOr<void> ApplePayPaymentHandler::convertData(JSC::ExecState& execState,
     return { };
 }
 
-ExceptionOr<void> ApplePayPaymentHandler::show(Document& document)
+ExceptionOr<void> ApplePayPaymentHandler::show()
 {
     auto validatedRequest = convertAndValidate(m_applePayRequest->version, *m_applePayRequest);
     if (validatedRequest.hasException())
@@ -203,16 +222,16 @@ ExceptionOr<void> ApplePayPaymentHandler::show(Document& document)
         return exception.releaseException();
 
     Vector<URL> linkIconURLs;
-    for (auto& icon : LinkIconCollector { document }.iconsOfTypes({ LinkIconType::TouchIcon, LinkIconType::TouchPrecomposedIcon }))
+    for (auto& icon : LinkIconCollector { document() }.iconsOfTypes({ LinkIconType::TouchIcon, LinkIconType::TouchPrecomposedIcon }))
         linkIconURLs.append(icon.url);
 
-    paymentCoordinator(document).beginPaymentSession(*this, document.url(), linkIconURLs, request);
+    paymentCoordinator().beginPaymentSession(*this, document().url(), linkIconURLs, request);
     return { };
 }
 
-void ApplePayPaymentHandler::hide(Document& document)
+void ApplePayPaymentHandler::hide()
 {
-    paymentCoordinator(document).abortPaymentSession();
+    paymentCoordinator().abortPaymentSession();
 }
 
 static bool shouldDiscloseApplePayCapability(Document& document)
@@ -224,14 +243,36 @@ static bool shouldDiscloseApplePayCapability(Document& document)
     return document.settings().applePayCapabilityDisclosureAllowed();
 }
 
-void ApplePayPaymentHandler::canMakePayment(Document& document, WTF::Function<void(bool)>&& completionHandler)
+void ApplePayPaymentHandler::canMakePayment(Function<void(bool)>&& completionHandler)
 {
-    if (!shouldDiscloseApplePayCapability(document)) {
-        completionHandler(paymentCoordinator(document).canMakePayments());
+    if (!shouldDiscloseApplePayCapability(document())) {
+        completionHandler(paymentCoordinator().canMakePayments());
         return;
     }
 
-    paymentCoordinator(document).canMakePaymentsWithActiveCard(m_applePayRequest->merchantIdentifier, document.domain(), WTFMove(completionHandler));
+    paymentCoordinator().canMakePaymentsWithActiveCard(m_applePayRequest->merchantIdentifier, document().domain(), WTFMove(completionHandler));
+}
+
+void ApplePayPaymentHandler::complete(std::optional<PaymentComplete>&& result)
+{
+    if (!result) {
+        paymentCoordinator().completePaymentSession(std::nullopt);
+        return;
+    }
+
+    PaymentAuthorizationResult authorizationResult;
+    switch (*result) {
+    case PaymentComplete::Fail:
+    case PaymentComplete::Unknown:
+        authorizationResult.status = PaymentAuthorizationStatus::Failure;
+        authorizationResult.errors.append({ PaymentError::Code::Unknown, { }, std::nullopt });
+        break;
+    case PaymentComplete::Success:
+        authorizationResult.status = PaymentAuthorizationStatus::Success;
+        break;
+    }
+
+    paymentCoordinator().completePaymentSession(WTFMove(authorizationResult));
 }
 
 void ApplePayPaymentHandler::validateMerchant(const URL& validationURL)
@@ -240,6 +281,30 @@ void ApplePayPaymentHandler::validateMerchant(const URL& validationURL)
         m_paymentRequest->dispatchEvent(ApplePayMerchantValidationEvent::create(eventNames().applepayvalidatemerchantEvent, validationURL).get());
 }
 
+static Ref<PaymentAddress> convert(const ApplePayPaymentContact& contact)
+{
+    return PaymentAddress::create(contact.countryCode, contact.addressLines.value_or(Vector<String>()), contact.administrativeArea, contact.locality, contact.subLocality, contact.postalCode, String(), String(), String(), contact.localizedName, contact.phoneNumber);
+}
+
+void ApplePayPaymentHandler::didAuthorizePayment(const Payment& payment)
+{
+    auto applePayPayment = payment.toApplePayPayment();
+    auto& execState = *document().execState();
+    auto details = JSC::Strong<JSC::JSObject> { execState.vm(), asObject(toJS<IDLDictionary<ApplePayPayment>>(execState, *JSC::jsCast<JSDOMGlobalObject*>(execState.lexicalGlobalObject()), applePayPayment)) };
+    const auto& shippingContact = applePayPayment.shippingContact.value_or(ApplePayPaymentContact());
+    m_paymentRequest->accept(WTF::get<URL>(m_identifier).string(), WTFMove(details), convert(shippingContact), shippingContact.localizedName, shippingContact.emailAddress, shippingContact.phoneNumber);
+}
+
+void ApplePayPaymentHandler::didSelectShippingMethod(const ApplePaySessionPaymentRequest::ShippingMethod& shippingMethod)
+{
+    m_paymentRequest->shippingOptionChanged(shippingMethod.identifier);
+}
+
+void ApplePayPaymentHandler::didSelectShippingContact(const PaymentContact& shippingContact)
+{
+    m_paymentRequest->shippingAddressChanged(convert(shippingContact.toApplePayPaymentContact()));
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(APPLE_PAY) && ENABLE(PAYMENT_REQUEST)
index 7f719eb..55e67e6 100644 (file)
 #if ENABLE(APPLE_PAY) && ENABLE(PAYMENT_REQUEST)
 
 #include "ApplePayRequest.h"
+#include "ContextDestructionObserver.h"
 #include "PaymentHandler.h"
+#include "PaymentRequest.h"
 #include "PaymentSession.h"
 #include <wtf/Noncopyable.h>
 #include <wtf/Ref.h>
 
 namespace WebCore {
 
-class PaymentRequest;
+class PaymentCoordinator;
 
-class ApplePayPaymentHandler final : public PaymentHandler, public PaymentSession {
+class ApplePayPaymentHandler final : public PaymentHandler, public PaymentSession, private ContextDestructionObserver {
 public:
     static bool handlesIdentifier(const PaymentRequest::MethodIdentifier&);
     static bool hasActiveSession(Document&);
 
 private:
     friend class PaymentHandler;
-    explicit ApplePayPaymentHandler(PaymentRequest&);
+    explicit ApplePayPaymentHandler(Document&, const PaymentRequest::MethodIdentifier&, PaymentRequest&);
+
+    Document& document();
+    PaymentCoordinator& paymentCoordinator();
 
     // PaymentHandler
-    ExceptionOr<void> convertData(JSC::ExecState&, JSC::JSValue&&) final;
-    ExceptionOr<void> show(Document&) final;
-    void hide(Document&) final;
-    void canMakePayment(Document&, WTF::Function<void(bool)>&& completionHandler) final;
+    ExceptionOr<void> convertData(JSC::JSValue&&) final;
+    ExceptionOr<void> show() final;
+    void hide() final;
+    void canMakePayment(WTF::Function<void(bool)>&& completionHandler) final;
+    void complete(std::optional<PaymentComplete>&&) final;
 
     // PaymentSession
     void validateMerchant(const URL&) final;
-    void didAuthorizePayment(const Payment&) final { }
-    void didSelectShippingMethod(const ApplePaySessionPaymentRequest::ShippingMethod&) final { }
-    void didSelectShippingContact(const PaymentContact&) final { }
+    void didAuthorizePayment(const Payment&) final;
+    void didSelectShippingMethod(const ApplePaySessionPaymentRequest::ShippingMethod&) final;
+    void didSelectShippingContact(const PaymentContact&) final;
     void didSelectPaymentMethod(const PaymentMethod&) final { }
     void didCancelPaymentSession() final { }
 
+    PaymentRequest::MethodIdentifier m_identifier;
     Ref<PaymentRequest> m_paymentRequest;
     std::optional<ApplePayRequest> m_applePayRequest;
 };
diff --git a/Source/WebCore/Modules/paymentrequest/PaymentAddress.cpp b/Source/WebCore/Modules/paymentrequest/PaymentAddress.cpp
new file mode 100644 (file)
index 0000000..dbe6817
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "PaymentAddress.h"
+
+#if ENABLE(PAYMENT_REQUEST)
+
+namespace WebCore {
+
+PaymentAddress::PaymentAddress(const String& country, const Vector<String>& addressLine, const String& region, const String& city, const String& dependentLocality, const String& postalCode, const String& sortingCode, const String& languageCode, const String& organization, const String& recipient, const String& phone)
+    : m_country { country }
+    , m_addressLine { addressLine }
+    , m_region { region }
+    , m_city { city }
+    , m_dependentLocality { dependentLocality }
+    , m_postalCode { postalCode }
+    , m_sortingCode { sortingCode }
+    , m_languageCode { languageCode }
+    , m_organization { organization }
+    , m_recipient { recipient }
+    , m_phone { phone }
+{
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(PAYMENT_REQUEST)
index 42c7745..c13e203 100644 (file)
@@ -35,6 +35,11 @@ namespace WebCore {
 
 class PaymentAddress final : public RefCounted<PaymentAddress> {
 public:
+    template <typename... Args> static Ref<PaymentAddress> create(Args&&... args)
+    {
+        return adoptRef(*new PaymentAddress(std::forward<Args>(args)...));
+    }
+
     const String& country() const { return m_country; }
     const Vector<String>& addressLine() const { return m_addressLine; }
     const String& region() const { return m_region; }
@@ -48,6 +53,8 @@ public:
     const String& phone() const { return m_phone; }
 
 private:
+    PaymentAddress(const String& country, const Vector<String>& addressLine, const String& region, const String& city, const String& dependentLocality, const String& postalCode, const String& sortingCode, const String& languageCode, const String& organization, const String& recipient, const String& phone);
+
     String m_country;
     Vector<String> m_addressLine;
     String m_region;
index 72116fc..e26b757 100644 (file)
@@ -26,6 +26,7 @@
 [
     Conditional=PAYMENT_REQUEST,
     EnabledBySetting=PaymentRequest,
+    Exposed=Window,
     ImplementationLacksVTable,
     SecureContext
 ] interface PaymentAddress {
index e7ecd14..f365575 100644 (file)
 
 namespace WebCore {
 
-RefPtr<PaymentHandler> PaymentHandler::create(PaymentRequest& paymentRequest, const PaymentRequest::MethodIdentifier& identifier)
+RefPtr<PaymentHandler> PaymentHandler::create(Document& document, PaymentRequest& paymentRequest, const PaymentRequest::MethodIdentifier& identifier)
 {
 #if ENABLE(APPLE_PAY)
     if (ApplePayPaymentHandler::handlesIdentifier(identifier))
-        return adoptRef(new ApplePayPaymentHandler(paymentRequest));
+        return adoptRef(new ApplePayPaymentHandler(document, identifier, paymentRequest));
 #else
+    UNUSED_PARAM(document);
     UNUSED_PARAM(paymentRequest);
     UNUSED_PARAM(identifier);
 #endif
index 4487607..8272026 100644 (file)
@@ -32,7 +32,6 @@
 #include <wtf/Function.h>
 
 namespace JSC {
-class ExecState;
 class JSValue;
 }
 
@@ -42,13 +41,14 @@ class Document;
 
 class PaymentHandler : public virtual PaymentSessionBase {
 public:
-    static RefPtr<PaymentHandler> create(PaymentRequest&, const PaymentRequest::MethodIdentifier&);
+    static RefPtr<PaymentHandler> create(Document&, PaymentRequest&, const PaymentRequest::MethodIdentifier&);
     static bool hasActiveSession(Document&);
 
-    virtual ExceptionOr<void> convertData(JSC::ExecState&, JSC::JSValue&&) = 0;
-    virtual ExceptionOr<void> show(Document&) = 0;
-    virtual void hide(Document&) = 0;
-    virtual void canMakePayment(Document&, WTF::Function<void(bool)>&& completionHandler) = 0;
+    virtual ExceptionOr<void> convertData(JSC::JSValue&&) = 0;
+    virtual ExceptionOr<void> show() = 0;
+    virtual void hide() = 0;
+    virtual void canMakePayment(WTF::Function<void(bool)>&& completionHandler) = 0;
+    virtual void complete(std::optional<PaymentComplete>&&) = 0;
 };
 
 } // namespace WebCore
index bfecf5f..8a5babd 100644 (file)
 
 #include "ApplePayPaymentHandler.h"
 #include "Document.h"
+#include "JSPaymentResponse.h"
 #include "PaymentAddress.h"
 #include "PaymentCurrencyAmount.h"
 #include "PaymentDetailsInit.h"
 #include "PaymentHandler.h"
 #include "PaymentMethodData.h"
 #include "PaymentOptions.h"
+#include "PaymentResponse.h"
 #include "ScriptController.h"
 #include <JavaScriptCore/JSONObject.h>
 #include <JavaScriptCore/ThrowScope.h>
@@ -376,11 +378,11 @@ void PaymentRequest::show(Document& document, ShowPromise&& promise)
             return;
         }
 
-        auto handler = PaymentHandler::create(*this, paymentMethod.identifier);
+        auto handler = PaymentHandler::create(document, *this, paymentMethod.identifier);
         if (!handler)
             continue;
 
-        auto result = handler->convertData(*document.execState(), data.releaseReturnValue());
+        auto result = handler->convertData(data.releaseReturnValue());
         if (result.hasException()) {
             m_showPromise->reject(result.releaseException());
             return;
@@ -395,7 +397,7 @@ void PaymentRequest::show(Document& document, ShowPromise&& promise)
         return;
     }
 
-    auto exception = selectedPaymentHandler->show(document);
+    auto exception = selectedPaymentHandler->show();
     if (exception.hasException()) {
         m_showPromise->reject(exception.releaseException());
         return;
@@ -413,7 +415,7 @@ void PaymentRequest::stop()
 
     if (auto paymentHandler = std::exchange(m_activePaymentHandler, nullptr)) {
         unsetPendingActivity(this);
-        paymentHandler->hide(downcast<Document>(*scriptExecutionContext()));
+        paymentHandler->hide();
     }
 
     ASSERT(m_state == State::Interactive);
@@ -445,15 +447,15 @@ void PaymentRequest::canMakePayment(Document& document, CanMakePaymentPromise&&
         if (data.hasException())
             continue;
 
-        auto handler = PaymentHandler::create(*this, paymentMethod.identifier);
+        auto handler = PaymentHandler::create(document, *this, paymentMethod.identifier);
         if (!handler)
             continue;
 
-        auto exception = handler->convertData(*document.execState(), data.releaseReturnValue());
+        auto exception = handler->convertData(data.releaseReturnValue());
         if (exception.hasException())
             continue;
 
-        handler->canMakePayment(document, [promise = WTFMove(promise)](bool canMakePayment) mutable {
+        handler->canMakePayment([promise = WTFMove(promise)](bool canMakePayment) mutable {
             promise.resolve(canMakePayment);
         });
         return;
@@ -478,14 +480,61 @@ bool PaymentRequest::canSuspendForDocumentSuspension() const
 {
     switch (m_state) {
     case State::Created:
-    case State::Closed:
         ASSERT(!m_activePaymentHandler);
         return true;
     case State::Interactive:
+    case State::Closed:
         return !m_activePaymentHandler;
     }
 }
 
+void PaymentRequest::shippingAddressChanged(Ref<PaymentAddress>&& shippingAddress)
+{
+    ASSERT(m_state == State::Interactive);
+    m_shippingAddress = WTFMove(shippingAddress);
+    // FIXME: run the PaymentRequest updated algorithm.
+}
+
+void PaymentRequest::shippingOptionChanged(const String& shippingOption)
+{
+    ASSERT(m_state == State::Interactive);
+    m_shippingOption = shippingOption;
+    // FIXME: run the PaymentRequest updated algorithm.
+}
+
+void PaymentRequest::accept(const String& methodName, JSC::Strong<JSC::JSObject>&& details, Ref<PaymentAddress>&& shippingAddress, const String& payerName, const String& payerEmail, const String& payerPhone)
+{
+    ASSERT(m_state == State::Interactive);
+
+    auto response = PaymentResponse::create(*this);
+    response->setRequestId(m_details.id);
+    response->setMethodName(methodName);
+    response->setDetails(WTFMove(details));
+
+    if (m_options.requestShipping) {
+        response->setShippingAddress(shippingAddress.ptr());
+        response->setShippingOption(m_shippingOption);
+    }
+
+    if (m_options.requestPayerName)
+        response->setPayerName(payerName);
+
+    if (m_options.requestPayerEmail)
+        response->setPayerEmail(payerEmail);
+
+    if (m_options.requestPayerPhone)
+        response->setPayerPhone(payerPhone);
+
+    m_showPromise->resolve(response.get());
+    m_state = State::Closed;
+}
+
+void PaymentRequest::complete(std::optional<PaymentComplete>&& result)
+{
+    ASSERT(m_state == State::Closed);
+    std::exchange(m_activePaymentHandler, nullptr)->complete(WTFMove(result));
+}
+
 } // namespace WebCore
 
 #endif // ENABLE(PAYMENT_REQUEST)
index a89185b..a8da4d1 100644 (file)
@@ -42,6 +42,7 @@ class Document;
 class PaymentAddress;
 class PaymentHandler;
 class PaymentResponse;
+enum class PaymentComplete;
 enum class PaymentShippingType;
 struct PaymentMethodData;
 
@@ -66,6 +67,11 @@ public:
     const PaymentOptions& paymentOptions() const { return m_options; }
     const PaymentDetailsInit& paymentDetails() const { return m_details; }
 
+    void shippingAddressChanged(Ref<PaymentAddress>&&);
+    void shippingOptionChanged(const String& shippingOption);
+    void accept(const String& methodName, JSC::Strong<JSC::JSObject>&& details, Ref<PaymentAddress>&& shippingAddress, const String& payerName, const String& payerEmail, const String& payerPhone);
+    void complete(std::optional<PaymentComplete>&&);
+
     using MethodIdentifier = Variant<String, URL>;
     using RefCounted<PaymentRequest>::ref;
     using RefCounted<PaymentRequest>::deref;
index 702abc4..2aeaedf 100644 (file)
 
 #if ENABLE(PAYMENT_REQUEST)
 
+#include "PaymentRequest.h"
 #include <wtf/RunLoop.h>
 
 namespace WebCore {
 
-void PaymentResponse::complete(std::optional<PaymentComplete>&&, DOMPromiseDeferred<void>&& promise)
+PaymentResponse::PaymentResponse(PaymentRequest& request)
+    : m_request { request }
 {
-    promise.reject(Exception { NotSupportedError, ASCIILiteral("Not implemented") });
+}
+
+PaymentResponse::~PaymentResponse() = default;
+
+void PaymentResponse::complete(std::optional<PaymentComplete>&& result, DOMPromiseDeferred<void>&& promise)
+{
+    if (m_completeCalled) {
+        promise.reject(Exception { InvalidStateError });
+        return;
+    }
+
+    m_completeCalled = true;
+    m_request->complete(WTFMove(result));
+    promise.resolve();
 }
 
 } // namespace WebCore
index 2f029e1..d033a76 100644 (file)
 #include "PaymentComplete.h"
 
 namespace WebCore {
+    
+class Document;
+class PaymentRequest;
 
 class PaymentResponse final : public RefCounted<PaymentResponse> {
 public:
+    static Ref<PaymentResponse> create(PaymentRequest& request)
+    {
+        return adoptRef(*new PaymentResponse(request));
+    }
+
+    ~PaymentResponse();
+
     const String& requestId() const { return m_requestId; }
+    void setRequestId(const String& requestId) { m_requestId = requestId; }
+
     const String& methodName() const { return m_methodName; }
+    void setMethodName(const String& methodName) { m_methodName = methodName; }
+
     const JSC::Strong<JSC::JSObject>& details() const { return m_details; }
+    void setDetails(JSC::Strong<JSC::JSObject>&& details) { m_details = WTFMove(details); }
+
     PaymentAddress* shippingAddress() const { return m_shippingAddress.get(); }
+    void setShippingAddress(PaymentAddress* shippingAddress) { m_shippingAddress = shippingAddress; }
+
     const String& shippingOption() const { return m_shippingOption; }
+    void setShippingOption(const String& shippingOption) { m_shippingOption = shippingOption; }
+
     const String& payerName() const { return m_payerName; }
+    void setPayerName(const String& payerName) { m_payerName = payerName; }
+
     const String& payerEmail() const { return m_payerEmail; }
+    void setPayerEmail(const String& payerEmail) { m_payerEmail = payerEmail; }
+
     const String& payerPhone() const { return m_payerPhone; }
+    void setPayerPhone(const String& payerPhone) { m_payerPhone = payerPhone; }
 
     void complete(std::optional<PaymentComplete>&&, DOMPromiseDeferred<void>&&);
 
 private:
+    explicit PaymentResponse(PaymentRequest&);
+
+    Ref<PaymentRequest> m_request;
     String m_requestId;
     String m_methodName;
     JSC::Strong<JSC::JSObject> m_details;
@@ -55,6 +83,7 @@ private:
     String m_payerName;
     String m_payerEmail;
     String m_payerPhone;
+    bool m_completeCalled { false };
 };
 
 } // namespace WebCore
index 7401aec..71cb7a7 100644 (file)
                A140618C1E2ECA0A0032B34E /* MockPreviewLoaderClient.h in Headers */ = {isa = PBXBuildFile; fileRef = A140618A1E2ECA0A0032B34E /* MockPreviewLoaderClient.h */; };
                A14090FB1AA51E1D0091191A /* ContentFilterUnblockHandlerCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = A14090FA1AA51E1D0091191A /* ContentFilterUnblockHandlerCocoa.mm */; };
                A14090FD1AA51E480091191A /* ContentFilterUnblockHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = A14090FC1AA51E480091191A /* ContentFilterUnblockHandler.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               A146D31A1F99BCF800D29196 /* JSMockPaymentCoordinator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A146D3191F99BCBB00D29196 /* JSMockPaymentCoordinator.cpp */; };
+               A146D31B1F99BCFB00D29196 /* JSMockPaymentCoordinator.h in Headers */ = {isa = PBXBuildFile; fileRef = A146D3181F99BCBA00D29196 /* JSMockPaymentCoordinator.h */; };
+               A146D3211F99CB1A00D29196 /* MockPaymentAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = A146D31C1F99C9C200D29196 /* MockPaymentAddress.h */; };
+               A146D3221F99D0EC00D29196 /* JSMockPaymentAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A146D3201F99CA3E00D29196 /* JSMockPaymentAddress.cpp */; };
+               A146D3231F99D0EF00D29196 /* JSMockPaymentAddress.h in Headers */ = {isa = PBXBuildFile; fileRef = A146D31F1F99CA3D00D29196 /* JSMockPaymentAddress.h */; };
+               A146D3251F99D69800D29196 /* MockPaymentContact.h in Headers */ = {isa = PBXBuildFile; fileRef = A146D3241F99D69800D29196 /* MockPaymentContact.h */; };
                A14832B0187F618D00DA63A6 /* WAKAppKitStubs.h in Headers */ = {isa = PBXBuildFile; fileRef = A148328C187F508700DA63A6 /* WAKAppKitStubs.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A14832B1187F61E100DA63A6 /* WAKAppKitStubs.m in Sources */ = {isa = PBXBuildFile; fileRef = A148328D187F508700DA63A6 /* WAKAppKitStubs.m */; };
                A14832B2187F61ED00DA63A6 /* WAKClipView.h in Headers */ = {isa = PBXBuildFile; fileRef = A148328E187F508700DA63A6 /* WAKClipView.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A149786E1ABAF33800CEF7E4 /* ContentFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A149786C1ABAF33800CEF7E4 /* ContentFilter.cpp */; };
                A149786F1ABAF33800CEF7E4 /* ContentFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = A149786D1ABAF33800CEF7E4 /* ContentFilter.h */; };
                A14978711ABAF3A500CEF7E4 /* PlatformContentFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = A14978701ABAF3A500CEF7E4 /* PlatformContentFilter.h */; };
+               A14BB0A01F9813B800605A35 /* MockPayment.h in Headers */ = {isa = PBXBuildFile; fileRef = A14BB09E1F9813B800605A35 /* MockPayment.h */; };
                A15D75151E68F7C400A35FBC /* BlobCallback.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A15D75121E68F7B100A35FBC /* BlobCallback.cpp */; };
                A15D75161E68F7C800A35FBC /* BlobCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = A15D75131E68F7B100A35FBC /* BlobCallback.h */; };
                A15D751B1E68F8A300A35FBC /* JSBlobCallback.h in Headers */ = {isa = PBXBuildFile; fileRef = A15D75181E68F83600A35FBC /* JSBlobCallback.h */; };
                A1CC567B1F4614AD00A4555B /* JSPaymentResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = A1CC56641F46146700A4555B /* JSPaymentResponse.h */; };
                A1CC567D1F4614B200A4555B /* JSPaymentShippingOption.h in Headers */ = {isa = PBXBuildFile; fileRef = A1CC56591F46145C00A4555B /* JSPaymentShippingOption.h */; };
                A1CC567F1F4614B700A4555B /* JSPaymentShippingType.h in Headers */ = {isa = PBXBuildFile; fileRef = A1CC56501F46145300A4555B /* JSPaymentShippingType.h */; };
+               A1CFE0321F9E71290065C345 /* PaymentAddress.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A1CFE0311F9E71290065C345 /* PaymentAddress.cpp */; };
                A1DE712D18612AC100734192 /* TouchEvents.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A1DE712B18612AC100734192 /* TouchEvents.cpp */; };
                A1DF5A7F1F7EBD0B0058A477 /* ApplePayRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = A1DF5A7C1F7EBD0B0058A477 /* ApplePayRequest.h */; };
                A1DF5A861F7EBDF20058A477 /* ApplePayMerchantCapability.h in Headers */ = {isa = PBXBuildFile; fileRef = A1DF5A831F7EBDF20058A477 /* ApplePayMerchantCapability.h */; };
                A140618A1E2ECA0A0032B34E /* MockPreviewLoaderClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MockPreviewLoaderClient.h; sourceTree = "<group>"; };
                A14090FA1AA51E1D0091191A /* ContentFilterUnblockHandlerCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ContentFilterUnblockHandlerCocoa.mm; sourceTree = "<group>"; };
                A14090FC1AA51E480091191A /* ContentFilterUnblockHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentFilterUnblockHandler.h; sourceTree = "<group>"; };
+               A146D3161F99B53D00D29196 /* MockPaymentCoordinator.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = MockPaymentCoordinator.idl; sourceTree = "<group>"; };
+               A146D3181F99BCBA00D29196 /* JSMockPaymentCoordinator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMockPaymentCoordinator.h; sourceTree = "<group>"; };
+               A146D3191F99BCBB00D29196 /* JSMockPaymentCoordinator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSMockPaymentCoordinator.cpp; sourceTree = "<group>"; };
+               A146D31C1F99C9C200D29196 /* MockPaymentAddress.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockPaymentAddress.h; sourceTree = "<group>"; };
+               A146D31E1F99C9C200D29196 /* MockPaymentAddress.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = MockPaymentAddress.idl; sourceTree = "<group>"; };
+               A146D31F1F99CA3D00D29196 /* JSMockPaymentAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSMockPaymentAddress.h; sourceTree = "<group>"; };
+               A146D3201F99CA3E00D29196 /* JSMockPaymentAddress.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSMockPaymentAddress.cpp; sourceTree = "<group>"; };
+               A146D3241F99D69800D29196 /* MockPaymentContact.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockPaymentContact.h; sourceTree = "<group>"; };
                A148328C187F508700DA63A6 /* WAKAppKitStubs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WAKAppKitStubs.h; sourceTree = "<group>"; };
                A148328D187F508700DA63A6 /* WAKAppKitStubs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WAKAppKitStubs.m; sourceTree = "<group>"; };
                A148328E187F508700DA63A6 /* WAKClipView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WAKClipView.h; sourceTree = "<group>"; };
                A149786C1ABAF33800CEF7E4 /* ContentFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ContentFilter.cpp; sourceTree = "<group>"; };
                A149786D1ABAF33800CEF7E4 /* ContentFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentFilter.h; sourceTree = "<group>"; };
                A14978701ABAF3A500CEF7E4 /* PlatformContentFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlatformContentFilter.h; sourceTree = "<group>"; };
+               A14BB09E1F9813B800605A35 /* MockPayment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MockPayment.h; sourceTree = "<group>"; };
                A15D75121E68F7B100A35FBC /* BlobCallback.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = BlobCallback.cpp; sourceTree = "<group>"; };
                A15D75131E68F7B100A35FBC /* BlobCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BlobCallback.h; sourceTree = "<group>"; };
                A15D75141E68F7B100A35FBC /* BlobCallback.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = BlobCallback.idl; sourceTree = "<group>"; };
                A1CC56631F46146600A4555B /* JSPaymentCurrencyAmount.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPaymentCurrencyAmount.h; sourceTree = "<group>"; };
                A1CC56641F46146700A4555B /* JSPaymentResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPaymentResponse.h; sourceTree = "<group>"; };
                A1CC56651F46146800A4555B /* JSPaymentAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPaymentAddress.h; sourceTree = "<group>"; };
+               A1CFE0311F9E71290065C345 /* PaymentAddress.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = PaymentAddress.cpp; sourceTree = "<group>"; };
                A1DE712B18612AC100734192 /* TouchEvents.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TouchEvents.cpp; sourceTree = "<group>"; };
                A1DF5A7C1F7EBD0B0058A477 /* ApplePayRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ApplePayRequest.h; sourceTree = "<group>"; };
                A1DF5A7E1F7EBD0B0058A477 /* ApplePayRequest.idl */ = {isa = PBXFileReference; lastKnownFileType = text; path = ApplePayRequest.idl; sourceTree = "<group>"; };
                                2D6F3E8C1C1ECB1C0061DBD4 /* MockPageOverlay.idl */,
                                2DAAE32C19DCAF6000E002D2 /* MockPageOverlayClient.cpp */,
                                2DAAE32D19DCAF6000E002D2 /* MockPageOverlayClient.h */,
+                               A14BB09E1F9813B800605A35 /* MockPayment.h */,
+                               A146D31C1F99C9C200D29196 /* MockPaymentAddress.h */,
+                               A146D31E1F99C9C200D29196 /* MockPaymentAddress.idl */,
+                               A146D3241F99D69800D29196 /* MockPaymentContact.h */,
                                A1AFEDE51F8BFF6D0087013F /* MockPaymentCoordinator.cpp */,
                                A1AFEDE41F8BFF6D0087013F /* MockPaymentCoordinator.h */,
+                               A146D3161F99B53D00D29196 /* MockPaymentCoordinator.idl */,
                                A14061891E2ECA0A0032B34E /* MockPreviewLoaderClient.cpp */,
                                A140618A1E2ECA0A0032B34E /* MockPreviewLoaderClient.h */,
                                EB081CD81696084400553730 /* TypeConversions.h */,
                                A19AEA1E1AAA806E00B52B25 /* JSMockContentFilterSettings.h */,
                                2D6F3E921C1F85550061DBD4 /* JSMockPageOverlay.cpp */,
                                2D6F3E931C1F85550061DBD4 /* JSMockPageOverlay.h */,
+                               A146D3201F99CA3E00D29196 /* JSMockPaymentAddress.cpp */,
+                               A146D31F1F99CA3D00D29196 /* JSMockPaymentAddress.h */,
+                               A146D3191F99BCBB00D29196 /* JSMockPaymentCoordinator.cpp */,
+                               A146D3181F99BCBA00D29196 /* JSMockPaymentCoordinator.h */,
                                EBF5121A1696496C0056BD25 /* JSTypeConversions.cpp */,
                                EBF5121B1696496C0056BD25 /* JSTypeConversions.h */,
                        );
                A1F76B0E1F44C0CF0014C318 /* paymentrequest */ = {
                        isa = PBXGroup;
                        children = (
+                               A1CFE0311F9E71290065C345 /* PaymentAddress.cpp */,
                                A1F76B401F44CF7F0014C318 /* PaymentAddress.h */,
                                A1F76B421F44CF7F0014C318 /* PaymentAddress.idl */,
                                A1F76B581F44D3B20014C318 /* PaymentComplete.h */,
                                538EC9331F99B9F7004D22A8 /* JSMockCDMFactory.h in Headers */,
                                A19AEA211AAA808600B52B25 /* JSMockContentFilterSettings.h in Headers */,
                                538EC9341F99B9F7004D22A8 /* JSMockPageOverlay.h in Headers */,
+                               A146D3231F99D0EF00D29196 /* JSMockPaymentAddress.h in Headers */,
+                               A146D31B1F99BCFB00D29196 /* JSMockPaymentCoordinator.h in Headers */,
                                EBF5121D1696496C0056BD25 /* JSTypeConversions.h in Headers */,
                                CDC26B41160A8CCE0026757B /* LegacyMockCDM.h in Headers */,
                                A1BF6B831AA96C7D00AF4A8A /* MockContentFilter.h in Headers */,
                                4157EBFB1E3AB67F00AC9FE9 /* MockLibWebRTCPeerConnection.h in Headers */,
                                2D6F3E911C1ECB2F0061DBD4 /* MockPageOverlay.h in Headers */,
                                2D97F04819DD4140001EE9C3 /* MockPageOverlayClient.h in Headers */,
+                               A14BB0A01F9813B800605A35 /* MockPayment.h in Headers */,
+                               A146D3211F99CB1A00D29196 /* MockPaymentAddress.h in Headers */,
+                               A146D3251F99D69800D29196 /* MockPaymentContact.h in Headers */,
                                A1AFEDE61F8BFF6D0087013F /* MockPaymentCoordinator.h in Headers */,
                                A140618C1E2ECA0A0032B34E /* MockPreviewLoaderClient.h in Headers */,
                                AA5F3B8D16CC33D100455EB0 /* PlatformSpeechSynthesizerMock.h in Headers */,
                                CDF4B7321E03D06000E235A2 /* JSMockCDMFactory.cpp in Sources */,
                                A19AEA221AAA808A00B52B25 /* JSMockContentFilterSettings.cpp in Sources */,
                                2D4150DE1C1F868C000A3BA2 /* JSMockPageOverlay.cpp in Sources */,
+                               A146D3221F99D0EC00D29196 /* JSMockPaymentAddress.cpp in Sources */,
+                               A146D31A1F99BCF800D29196 /* JSMockPaymentCoordinator.cpp in Sources */,
                                EBF5121C1696496C0056BD25 /* JSTypeConversions.cpp in Sources */,
                                CDC26B40160A8CC60026757B /* LegacyMockCDM.cpp in Sources */,
                                CDF4B7311E03D00700E235A2 /* MockCDMFactory.cpp in Sources */,
                                2D5002FB1B56D7990020AAF7 /* PathUtilities.cpp in Sources */,
                                A8FA6E5E0E4CFDED00D5CF49 /* Pattern.cpp in Sources */,
                                A80A38FE0E50CC8200A25EBC /* PatternCG.cpp in Sources */,
+                               A1CFE0321F9E71290065C345 /* PaymentAddress.cpp in Sources */,
                                1A8A646C1D19FF8700D0E00F /* PaymentCocoa.mm in Sources */,
                                1A8A646D1D19FF8700D0E00F /* PaymentContactCocoa.mm in Sources */,
                                1A58E86D1D19E42D00C0EA73 /* PaymentCoordinator.cpp in Sources */,
index 9eb7b38..5a1181e 100644 (file)
@@ -513,8 +513,11 @@ Internals::Internals(Document& document)
     setConsoleMessageListener(nullptr);
 
 #if ENABLE(APPLE_PAY)
-    if (auto frame = document.frame())
-        frame->mainFrame().setPaymentCoordinator(std::make_unique<PaymentCoordinator>(*new MockPaymentCoordinator(frame->mainFrame())));
+    auto* frame = document.frame();
+    if (frame && frame->isMainFrame()) {
+        m_mockPaymentCoordinator = new MockPaymentCoordinator(frame->mainFrame());
+        frame->mainFrame().setPaymentCoordinator(std::make_unique<PaymentCoordinator>(*m_mockPaymentCoordinator));
+    }
 #endif
 }
 
@@ -4258,4 +4261,11 @@ String Internals::timelineDescription(AnimationTimeline& timeline)
     return timeline.description();
 }
 
+#if ENABLE(APPLE_PAY)
+MockPaymentCoordinator& Internals::mockPaymentCoordinator() const
+{
+    return *m_mockPaymentCoordinator;
+}
+#endif
+
 } // namespace WebCore
index 782a2bf..b572769 100644 (file)
@@ -73,6 +73,7 @@ class MemoryInfo;
 class MockCDMFactory;
 class MockContentFilterSettings;
 class MockPageOverlay;
+class MockPaymentCoordinator;
 class NodeList;
 class Page;
 class Range;
@@ -618,6 +619,10 @@ public:
 #endif
 
     bool hasServiceWorkerRegisteredForOrigin(const String&);
+        
+#if ENABLE(APPLE_PAY)
+    MockPaymentCoordinator& mockPaymentCoordinator() const;
+#endif
 
     String timelineDescription(AnimationTimeline&);
 
@@ -642,6 +647,10 @@ private:
 
     std::unique_ptr<InspectorStubFrontend> m_inspectorFrontend;
     RefPtr<CacheStorageConnection> m_cacheStorageConnection;
+
+#if ENABLE(APPLE_PAY)
+    MockPaymentCoordinator* m_mockPaymentCoordinator { nullptr };
+#endif
 };
 
 } // namespace WebCore
index bb72dc5..fd107eb 100644 (file)
@@ -563,4 +563,5 @@ enum EventThrottlingBehavior {
     boolean hasServiceWorkerRegisteredForOrigin(DOMString origin);
 
     [EnabledAtRuntime=WebAnimations] DOMString timelineDescription(AnimationTimeline timeline);
+    [Conditional=APPLE_PAY] readonly attribute MockPaymentCoordinator mockPaymentCoordinator;
 };
diff --git a/Source/WebCore/testing/MockPayment.h b/Source/WebCore/testing/MockPayment.h
new file mode 100644 (file)
index 0000000..e9539fb
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(APPLE_PAY)
+
+#include "ApplePayPayment.h"
+#include "Payment.h"
+
+namespace WebCore {
+
+class MockPayment final : public Payment {
+public:
+    explicit MockPayment(ApplePayPayment&& applePayPayment)
+        : m_applePayPayment { WTFMove(applePayPayment) }
+    {
+    }
+
+    ApplePayPayment toApplePayPayment() const final { return m_applePayPayment; }
+
+private:
+    ApplePayPayment m_applePayPayment;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(APPLE_PAY)
diff --git a/Source/WebCore/testing/MockPaymentAddress.h b/Source/WebCore/testing/MockPaymentAddress.h
new file mode 100644 (file)
index 0000000..b123cf5
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(APPLE_PAY)
+
+#include "ApplePayPaymentContact.h"
+
+namespace WebCore {
+
+struct MockPaymentAddress : ApplePayPaymentContact {
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(APPLE_PAY)
diff --git a/Source/WebCore/testing/MockPaymentAddress.idl b/Source/WebCore/testing/MockPaymentAddress.idl
new file mode 100644 (file)
index 0000000..30b2e1b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+    Conditional=APPLE_PAY
+] dictionary MockPaymentAddress {
+    DOMString countryCode;
+    FrozenArray<DOMString> addressLines;
+    DOMString administrativeArea;
+    DOMString locality;
+    DOMString subLocality;
+    DOMString postalCode;
+    DOMString localizedName;
+    DOMString phoneNumber;
+    DOMString emailAddress;
+};
diff --git a/Source/WebCore/testing/MockPaymentContact.h b/Source/WebCore/testing/MockPaymentContact.h
new file mode 100644 (file)
index 0000000..b529d44
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(APPLE_PAY)
+
+#include "ApplePayPaymentContact.h"
+#include "PaymentContact.h"
+
+namespace WebCore {
+
+class MockPaymentContact final : public PaymentContact {
+public:
+    explicit MockPaymentContact(ApplePayPaymentContact&& applePayPaymentContact)
+        : m_applePayPaymentContact { WTFMove(applePayPaymentContact) }
+    {
+    }
+
+    ApplePayPaymentContact toApplePayPaymentContact() const final { return m_applePayPaymentContact; }
+
+private:
+    ApplePayPaymentContact m_applePayPaymentContact;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(APPLE_PAY)
index 9794b28..5f518f0 100644 (file)
@@ -29,6 +29,8 @@
 #if ENABLE(APPLE_PAY)
 
 #include "MainFrame.h"
+#include "MockPayment.h"
+#include "MockPaymentContact.h"
 #include "PaymentCoordinator.h"
 #include "URL.h"
 #include <wtf/RunLoop.h>
@@ -58,30 +60,75 @@ bool MockPaymentCoordinator::canMakePayments()
     return true;
 }
 
-void MockPaymentCoordinator::canMakePaymentsWithActiveCard(const String&, const String&, WTF::Function<void(bool)>&& completionHandler)
+void MockPaymentCoordinator::canMakePaymentsWithActiveCard(const String&, const String&, Function<void(bool)>&& completionHandler)
 {
     RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)]() {
         completionHandler(true);
     });
 }
 
-void MockPaymentCoordinator::openPaymentSetup(const String&, const String&, WTF::Function<void(bool)>&& completionHandler)
+void MockPaymentCoordinator::openPaymentSetup(const String&, const String&, Function<void(bool)>&& completionHandler)
 {
     RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler)]() {
         completionHandler(true);
     });
 }
 
+static uint64_t showCount;
+static uint64_t hideCount;
+
+static void dispatchIfShowing(Function<void()>&& function)
+{
+    ASSERT(showCount > hideCount);
+    RunLoop::main().dispatch([currentShowCount = showCount, function = WTFMove(function)]() {
+        if (showCount > hideCount && showCount == currentShowCount)
+            function();
+    });
+}
+
 bool MockPaymentCoordinator::showPaymentUI(const URL&, const Vector<URL>&, const ApplePaySessionPaymentRequest&)
 {
-    RunLoop::main().dispatch([mainFrame = makeRef(m_mainFrame)]() {
+    ASSERT(showCount == hideCount);
+    ++showCount;
+    dispatchIfShowing([mainFrame = makeRef(m_mainFrame)]() {
         mainFrame->paymentCoordinator().validateMerchant({ URL(), ASCIILiteral("https://webkit.org/") });
     });
     return true;
 }
 
+void MockPaymentCoordinator::completeMerchantValidation(const PaymentMerchantSession&)
+{
+    dispatchIfShowing([mainFrame = makeRef(m_mainFrame), shippingAddress = m_shippingAddress]() {
+        ApplePayPaymentContact contact = shippingAddress;
+        mainFrame->paymentCoordinator().didSelectShippingContact(MockPaymentContact { WTFMove(contact) });
+
+        ApplePayPayment payment;
+        payment.shippingContact = shippingAddress;
+        mainFrame->paymentCoordinator().didAuthorizePayment(MockPayment { WTFMove(payment) });
+    });
+}
+
+void MockPaymentCoordinator::completePaymentSession(std::optional<PaymentAuthorizationResult>&&)
+{
+    ++hideCount;
+    ASSERT(showCount == hideCount);
+}
+
+void MockPaymentCoordinator::abortPaymentSession()
+{
+    ++hideCount;
+    ASSERT(showCount == hideCount);
+}
+
+void MockPaymentCoordinator::cancelPaymentSession()
+{
+    ++hideCount;
+    ASSERT(showCount == hideCount);
+}
+
 void MockPaymentCoordinator::paymentCoordinatorDestroyed()
 {
+    ASSERT(showCount == hideCount);
     delete this;
 }
 
index 22be9b3..69e8cd9 100644 (file)
@@ -27,6 +27,7 @@
 
 #if ENABLE(APPLE_PAY)
 
+#include "MockPaymentAddress.h"
 #include "PaymentCoordinatorClient.h"
 
 namespace WebCore {
@@ -37,22 +38,28 @@ class MockPaymentCoordinator final : public PaymentCoordinatorClient {
 public:
     explicit MockPaymentCoordinator(MainFrame&);
 
+    void setShippingAddress(MockPaymentAddress&& shippingAddress) { m_shippingAddress = WTFMove(shippingAddress); }
+
+    void ref() const { }
+    void deref() const { }
+
 private:
     bool supportsVersion(unsigned) final;
     bool canMakePayments() final;
     void canMakePaymentsWithActiveCard(const String&, const String&, WTF::Function<void(bool)>&&);
     void openPaymentSetup(const String&, const String&, WTF::Function<void(bool)>&&);
     bool showPaymentUI(const URL&, const Vector<URL>&, const ApplePaySessionPaymentRequest&) final;
-    void completeMerchantValidation(const PaymentMerchantSession&) final { }
+    void completeMerchantValidation(const PaymentMerchantSession&) final;
     void completeShippingMethodSelection(std::optional<ShippingMethodUpdate>&&) final { }
     void completeShippingContactSelection(std::optional<ShippingContactUpdate>&&) final { }
     void completePaymentMethodSelection(std::optional<PaymentMethodUpdate>&&) final { }
-    void completePaymentSession(std::optional<PaymentAuthorizationResult>&&) final { }
-    void abortPaymentSession() final { }
-    void cancelPaymentSession() final { }
+    void completePaymentSession(std::optional<PaymentAuthorizationResult>&&) final;
+    void abortPaymentSession() final;
+    void cancelPaymentSession() final;
     void paymentCoordinatorDestroyed() final;
 
     MainFrame& m_mainFrame;
+    MockPaymentAddress m_shippingAddress;
 };
 
 } // namespace WebCore
diff --git a/Source/WebCore/testing/MockPaymentCoordinator.idl b/Source/WebCore/testing/MockPaymentCoordinator.idl
new file mode 100644 (file)
index 0000000..e5ba309
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+[
+    Conditional=APPLE_PAY,
+    NoInterfaceObject,
+] interface MockPaymentCoordinator {
+    void setShippingAddress(MockPaymentAddress shippingAddress);
+};