Diff
Modified: trunk/LayoutTests/ChangeLog (281829 => 281830)
--- trunk/LayoutTests/ChangeLog 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/LayoutTests/ChangeLog 2021-08-31 23:07:44 UTC (rev 281830)
@@ -1,3 +1,14 @@
+2021-08-31 Marcos Caceres <[email protected]>
+
+ [Payment Request] Calling PaymentRequest's show() should consume user activation
+ https://bugs.webkit.org/show_bug.cgi?id=217365
+
+ Reviewed by Youenn Fablet and Devin Rousso.
+
+ * http/tests/paymentrequest/payment-is-showing.https.html:
+ * http/tests/paymentrequest/payment-request-show-method.https.html:
+ * platform/ios-wk2/imported/w3c/web-platform-tests/payment-request/show-method-optional-promise-rejects.https-expected.txt: Removed.
+
2021-08-31 Eric Hutchison <[email protected]>
Update test expectations for iOS14 fast/ tests.
Modified: trunk/LayoutTests/http/tests/paymentrequest/payment-is-showing.https.html (281829 => 281830)
--- trunk/LayoutTests/http/tests/paymentrequest/payment-is-showing.https.html 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/LayoutTests/http/tests/paymentrequest/payment-is-showing.https.html 2021-08-31 23:07:44 UTC (rev 281830)
@@ -71,11 +71,16 @@
// Sets the "payment-relevant browsing context's payment request is
// showing boolean" to true and then try to show a second payment sheet in
// the same window. The second show() should reject.
- const [showPromise1, showPromise2] = await activateThen(
- async () => {
- return [request1.show(), request2.show()];
+ const [showPromise1] = await activateThen(
+ () => {
+ return [request1.show()]
}
);
+ const [showPromise2] = await activateThen(
+ () => {
+ return [request2.show()]
+ }
+ );
await promise_rejects(
t,
'AbortError',
@@ -117,12 +122,18 @@
const iframeRequest = new iframeWindow.PaymentRequest(methods, details);
const windowRequest = new window.PaymentRequest(methods, details);
- // Let's get some blessed showPromises
- const [iframeShowPromise, windowShowPromise] = await activateThen(
- async () => {
- // iframe sets "is showing boolean", ignore the returned promise.
+ // Let's get blessed promise for the iframe
+ const [iframeShowPromise] = await activateThen(
+ () => {
+ // iframe sets "is showing boolean"
+ return [iframeRequest.show()];
+ }
+ );
+
+ const [windowShowPromise] = await activateThen(
+ () => {
// The top level window now tries to show() the payment request.
- return [iframeRequest.show(), windowRequest.show()];
+ return [windowRequest.show()];
}
);
@@ -130,7 +141,7 @@
t,
'AbortError',
windowShowPromise,
- 'iframe is already showing a payment request.',
+ 'iframe is already showing a payment request',
);
// Cleanup
@@ -154,12 +165,18 @@
// We first show a payment request via the the top level browsing context,
// windowRequest.show() sets "is showing boolean" to true. Then we try to
// show a payment request in the iframe, which should reject.
- const [windowShowPromise, iframeShowPromise] = await activateThen(
- async () => {
- return [windowRequest.show(), iframeRequest.show()];
+ const [windowShowPromise] = await activateThen(
+ () => {
+ return [windowRequest.show()];
}
);
+ const [iframeShowPromise] = await activateThen(
+ () => {
+ return [iframeRequest.show()];
+ }
+ );
+
await promise_rejects(
t,
'AbortError',
@@ -188,15 +205,15 @@
// Get the showPromise for each browsing context. Doing this in a separate
// test_driver.bless() is important because the user gesture brings
// |window| to the foreground, so that the payment sheet can show.
- const [
- windowShowPromise,
- iframeShowPromise,
- ] = await activateThen(
+ const [windowShowPromise] = await activateThen(
+ () => {
+ return [windowRequest.show()];
+ }
+ );
+
+ const [ iframeShowPromise ] = await activateThen(
async () => {
- return [
- windowRequest.show(),
- iframeRequest.show(),
- ];
+ return [iframeRequest.show()];
}
);
@@ -227,17 +244,16 @@
// Get the showPromise for each browsing context. Doing this in a separate
// test_driver.bless() is important because the user gesture brings
// |window| to the foreground, so that the payment sheet can show.
- const [
- iframeShowPromise,
- windowShowPromise,
- ] = await activateThen(
- async () => {
- return [
- iframeRequest.show(),
- windowRequest.show(),
- ];
+ const [iframeShowPromise] = await activateThen(
+ () => {
+ return [iframeRequest.show()];
}
);
+ const [windowShowPromise] = await activateThen(
+ () => {
+ return [windowRequest.show()];
+ }
+ );
// windowShowPromise and iframeRequest will both reject
await promise_rejects(
Modified: trunk/LayoutTests/http/tests/paymentrequest/payment-request-show-method.https.html (281829 => 281830)
--- trunk/LayoutTests/http/tests/paymentrequest/payment-request-show-method.https.html 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/LayoutTests/http/tests/paymentrequest/payment-request-show-method.https.html 2021-08-31 23:07:44 UTC (rev 281830)
@@ -46,7 +46,7 @@
user_activation_test(async t => {
const request = new PaymentRequest(defaultMethods, defaultDetails);
const acceptPromise = request.show(); // Sets state to "interactive"
- await promise_rejects(t, "InvalidStateError", request.show());
+ await promise_rejects(t, "SecurityError", request.show());
await request.abort();
await promise_rejects(t, "AbortError", acceptPromise);
}, `Throws if the promise [[state]] is not "created"`);
@@ -56,7 +56,7 @@
const request2 = new PaymentRequest(defaultMethods, defaultDetails);
const acceptPromise1 = request1.show();
const acceptPromise2 = request2.show();
- await promise_rejects(t, "AbortError", acceptPromise2);
+ await promise_rejects(t, "SecurityError", acceptPromise2);
await request1.abort();
await promise_rejects(t, "AbortError", acceptPromise1);
}, `If the user agent's "payment request is showing" boolean is true, then return a promise rejected with an "AbortError" DOMException.`);
Modified: trunk/LayoutTests/imported/w3c/ChangeLog (281829 => 281830)
--- trunk/LayoutTests/imported/w3c/ChangeLog 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/LayoutTests/imported/w3c/ChangeLog 2021-08-31 23:07:44 UTC (rev 281830)
@@ -1,3 +1,20 @@
+2021-08-31 Marcos Caceres <[email protected]>
+
+ [Payment Request] Calling PaymentRequest's show() should consume user activation
+ https://bugs.webkit.org/show_bug.cgi?id=217365
+
+ Reviewed by Youenn Fablet and Devin Rousso.
+
+ Re-import payment-request tests from WPT c0453ea15be63fc697bdbc141aa6837a1020b114.
+
+ * web-platform-tests/payment-request/payment-is-showing.https.html:
+ * web-platform-tests/payment-request/payment-request-canmakepayment-method.https-expected.txt:
+ * web-platform-tests/payment-request/payment-request-hasenrolledinstrument-method.tentative.https-expected.txt:
+ * web-platform-tests/payment-request/payment-request-show-method.https-expected.txt: Added.
+ * web-platform-tests/payment-request/payment-request-show-method.https.html:
+ * web-platform-tests/payment-request/rejects_if_not_active.https.html:
+ * web-platform-tests/payment-request/show-method-optional-promise-rejects.https-expected.txt:
+
2021-08-31 Ryosuke Niwa <[email protected]>
Resync web-platform-tests/shadow-dom
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-is-showing.https.html (281829 => 281830)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-is-showing.https.html 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-is-showing.https.html 2021-08-31 23:07:44 UTC (rev 281830)
@@ -4,7 +4,7 @@
rel="help"
href=""
/>
-<meta name="timeout" content="long">
+<meta name="timeout" content="long" />
<script src=""
<script src=""
<script src=""
@@ -22,7 +22,7 @@
supportedNetworks: ["visa"],
},
};
- const methods = [{supportedMethods: "basic-card"}, applePayMethod];
+ const methods = [{ supportedMethods: "basic-card" }, applePayMethod];
const details = {
total: {
label: "Total",
@@ -44,28 +44,23 @@
iframe.allow = "payment";
iframe.src = ""
document.body.appendChild(iframe);
- await new Promise(resolve => {
- iframe.addEventListener("load", resolve, {once: true});
+ await new Promise((resolve) => {
+ iframe.addEventListener("load", resolve, { once: true });
});
return iframe;
}
- /**
- * Creates a popup window. The caller must be triggered with a user gesture.
- *
- * @param {String} src Optional resource URL to load.
- * @returns {Promise} Resolves when the src loads.
- */
- async function loadPopupInsideUserGesture(src = "" {
- const popupWindow = window.open(src, "", "width=400,height=400");
- await new Promise(resolve => {
- popupWindow.addEventListener("load", resolve, {once: true});
- });
- popupWindow.focus();
- return popupWindow;
+ function getShowPromiseFromContext(paymentRequest, context = this) {
+ return test_driver.bless(
+ "payment request show()",
+ () => {
+ return [paymentRequest.show()];
+ },
+ context
+ );
}
- promise_test(async t => {
+ promise_test(async (t) => {
const request1 = new PaymentRequest(methods, details);
const request2 = new PaymentRequest(methods, details);
@@ -72,17 +67,17 @@
// Sets the "payment-relevant browsing context's payment request is
// showing boolean" to true and then try to show a second payment sheet in
// the same window. The second show() should reject.
- const [showPromise1, showPromise2] = await test_driver.bless(
- "testing one payment sheet per window",
- () => {
- return [request1.show(), request2.show()];
- },
- );
+ await test_driver.bless("payment request show()");
+ const showPromise1 = request1.show();
+
+ await test_driver.bless("payment request show()");
+ const showPromise2 = request2.show();
+
await promise_rejects_dom(
t,
"AbortError",
showPromise2,
- "Attempting to show a second payment request must reject.",
+ "Attempting to show a second payment request must reject."
);
await request1.abort();
@@ -90,7 +85,7 @@
t,
"AbortError",
showPromise1,
- "request1 was aborted via .abort()",
+ "request1 was aborted via .abort()"
);
// Finally, request2 should have been "closed", so trying to show
@@ -101,40 +96,40 @@
t,
"InvalidStateError",
rejectedPromise,
- "Attempting to show a second payment request must reject.",
+ "Attempting to show a second payment request must reject."
);
// Finally, we confirm that request2's returned promises are unique.
assert_not_equals(
showPromise2,
rejectedPromise,
- "Returned Promises be unique",
+ "Returned Promises be unique"
);
}, "The top browsing context can only show one payment sheet at a time.");
- promise_test(async t => {
+ promise_test(async (t) => {
const iframe = await attachIframe();
const iframeWindow = iframe.contentWindow;
// Payment requests
+ const windowRequest = new window.PaymentRequest(methods, details);
const iframeRequest = new iframeWindow.PaymentRequest(methods, details);
- const windowRequest = new window.PaymentRequest(methods, details);
// Let's get some blessed showPromises
- const [showPromise] = await test_driver.bless(
- "testing top window show() blocked by payment sheet in iframe",
- () => {
- // iframe sets "is showing boolean", ignore the returned promise.
- iframeRequest.show();
- // The top level window now tries to show() the payment request.
- return [windowRequest.show()];
- },
+ // iframe sets "is showing boolean", ignore the returned promise.
+ const [iframePromise] = await getShowPromiseFromContext(
+ iframeRequest,
+ iframeWindow
);
+ // The top level window now tries to show() the payment request.
+ await test_driver.bless("payment request show()");
+ const showPromise = windowRequest.show();
+
await promise_rejects_dom(
t,
"AbortError",
showPromise,
- "iframe is already showing a payment request.",
+ "iframe is already showing a payment request."
);
// Cleanup
@@ -142,361 +137,34 @@
iframe.remove();
}, "If an iframe shows a payment request, the top-level browsing context can't also show one.");
- promise_test(async t => {
+ promise_test(async (t) => {
const iframe = await attachIframe();
const iframeWindow = iframe.contentWindow;
-
- // Payment requests
const iframeRequest = new iframeWindow.PaymentRequest(methods, details);
- const windowRequest = new window.PaymentRequest(methods, details);
-
- // We first show a payment request via the the top level browsing context,
- // windowRequest.show() sets "is showing boolean" to true. Then we try to
- // show a payment request in the iframe, which should reject.
- const [windowShowPromise, iframeShowPromise] = await test_driver.bless(
- "testing iframe show() blocked by payment sheet in top window",
- () => {
- return [windowRequest.show(), iframeRequest.show()];
- },
+ const [iframeShowPromise] = await getShowPromiseFromContext(
+ iframeRequest,
+ iframeWindow
);
- await promise_rejects_dom(
- t,
- "AbortError",
- iframeShowPromise,
- "The top window is already showing a payment request.",
- );
-
- // Cleanup
- await windowRequest.abort();
- await promise_rejects_dom(
- t,
- "AbortError",
- windowShowPromise,
- "The window payment request should be aborted by test.",
- );
- iframe.remove();
- }, "An iframe cannot show a payment request if the top-level window is already showing one.");
-
- promise_test(async t => {
- // Create a PaymentReuqest in top-level window.
- const windowRequest = new window.PaymentRequest(methods, details);
-
- // Use a single user gesture to open a popup window with a PaymentRequest.
- // Then trigger show() first on |popupRequest| then on |windowRequest|.
- // The latter should reject.
- const [
- popupWindow,
- popupRequest,
- popupShowPromise,
- windowShowPromise,
- ] = await test_driver.bless(
- "testing top-level show() blocked by payment sheet in popup",
- async () => {
- const popupWindow = await loadPopupInsideUserGesture();
- const popupRequest = new popupWindow.PaymentRequest(methods, details);
- const popupShowPromise = popupRequest.show();
- const windowShowPromise = windowRequest.show();
- return [
- popupWindow,
- popupRequest,
- popupShowPromise,
- windowShowPromise,
- ];
- },
- );
-
- await promise_rejects_dom(
- t,
- "AbortError",
- windowShowPromise,
- "Expected window's showPromise to reject, request is already showing",
- );
-
- await popupRequest.abort();
- await promise_rejects_dom(
- t,
- "AbortError",
- popupShowPromise,
- "Expected popupShowPromise to be aborted by test.",
- );
- popupWindow.close();
- }, "Using a popup window prevents the top-browsing context from showing a payment request");
-
- promise_test(async t => {
- const iframe = await attachIframe();
- const iframeWindow = iframe.contentWindow;
-
- // Create requests
- const windowRequest = new window.PaymentRequest(methods, details);
- const iframeRequest = new iframeWindow.PaymentRequest(methods, details);
-
- // Open a popup window
- const [popupWindow, popupRequest] =
- await test_driver.bless(
- "open popup to test multiple context and window calls show() first",
- async () => {
- const popupWindow = await loadPopupInsideUserGesture();
- const popupRequest = new popupWindow.PaymentRequest(methods, details);
- return [popupWindow, popupRequest];
- },
- );
-
- // Get the showPromise for each browsing context. Doing this in a separate
- // test_driver.bless() is important because the user gesture brings
- // |window| to the foreground, so that the payment sheet can show.
- const [
- windowShowPromise,
- popupShowPromise,
- iframeShowPromise,
- ] = await test_driver.bless(
- "test multiple nested browsing context",
- () => {
- return [
- windowRequest.show(),
- popupRequest.show(),
- iframeRequest.show(),
- ];
- },
- );
- // popupRequest and iframeRequest will both reject
- await promise_rejects_dom(
- t,
- "AbortError",
- popupShowPromise,
- "Expected popupShowPromise to reject, request is already showing.",
- );
-
- await promise_rejects_dom(
- t,
- "AbortError",
- iframeShowPromise,
- "Expected iframeShowPromise to reject, request is already showing.",
- );
-
- await windowRequest.abort();
- await promise_rejects_dom(
- t,
- "AbortError",
- windowShowPromise,
- "Expect window promise to be aborted by test."
- );
- popupWindow.close();
- iframe.remove();
- }, "Given multiple nested browsing contexts, and window calls show() first, other nested browsing contexts can't show a request.");
-
- promise_test(async t => {
- const iframe = await attachIframe();
- const iframeWindow = iframe.contentWindow;
-
- // Create requests
- const windowRequest = new window.PaymentRequest(methods, details);
- const iframeRequest = new iframeWindow.PaymentRequest(methods, details);
-
- // Open a popup window
- const [
- popupWindow,
- popupRequest,
- popupShowPromise,
- windowShowPromise,
- iframeShowPromise
- ] = await test_driver.bless(
- "test multiple browsing context and iframe calls show() first",
- async () => {
- const popupWindow = await loadPopupInsideUserGesture();
- const popupRequest = new popupWindow.PaymentRequest(methods, details);
- const popupShowPromise = popupRequest.show();
- const windowShowPromise = windowRequest.show();
- const iframeShowPromise = iframeRequest.show();
- return [popupWindow,
- popupRequest,
- popupShowPromise,
- windowShowPromise,
- iframeShowPromise];
+ // We navigate away, causing the payment sheet to close
+ // and the request is showing boolean to become false.
+ await new Promise((resolve) => {
+ iframe._onload_ = resolve;
+ iframe.src = ""
});
- // windowShowPromise and iframeRequest will both reject
- await promise_rejects_dom(
- t,
- "AbortError",
- windowShowPromise,
- "Expected windowShowPromise to reject, the popup is showing a payment request.",
- );
-
- await promise_rejects_dom(
- t,
- "AbortError",
- iframeShowPromise,
- "Expected iframeShowPromise to reject, the popup is showing a payment request.",
- );
-
- await popupRequest.abort();
- await promise_rejects_dom(
- t,
- "AbortError",
- popupShowPromise,
- "Expected popupShowPromise to be aborted by test.",
- );
- popupWindow.close();
iframe.remove();
- }, "Given multiple nested browsing contexts, and popup calls show() first, other nested browsing contexts can't show a request.");
- promise_test(async t => {
- const iframe = await attachIframe();
- const iframeWindow = iframe.contentWindow;
-
- // Create requests
- const windowRequest = new window.PaymentRequest(methods, details);
- const iframeRequest = new iframeWindow.PaymentRequest(methods, details);
-
- const [popupWindow, popupRequest] = await test_driver.bless(
- "open popup to test multiple context and iframe calls show() first",
- async () => {
- const w = await loadPopupInsideUserGesture();
- const r = new w.PaymentRequest(methods, details);
- return [w, r];
- },
- );
-
- // Get the showPromise for each browsing context. Doing this in a separate
- // test_driver.bless() is important because the user gesture brings
- // |window| to the foreground, so that the payment sheet can show.
- const [
- iframeShowPromise,
- popupShowPromise,
- windowShowPromise,
- ] = await test_driver.bless(
- "test multiple browsing context and iframe calls show() first",
- async () => {
- return [
- iframeRequest.show(),
- popupRequest.show(),
- windowRequest.show(),
- ];
- },
- );
-
- // windowShowPromise and iframeRequest will both reject
+ // Now we should be ok to spin up a new payment request
+ const request = new window.PaymentRequest(methods, details);
+ const [showPromise] = await getShowPromiseFromContext(request);
+ await request.abort();
await promise_rejects_dom(
t,
"AbortError",
- windowShowPromise,
- "Expected windowShowPromise to reject, the popup is showing a payment request.",
+ showPromise,
+ "Normal abort."
);
-
- await promise_rejects_dom(
- t,
- "AbortError",
- popupShowPromise,
- "Expected popupShowPromise to reject, the popup is showing a payment request.",
- );
-
- await iframeRequest.abort();
- await promise_rejects_dom(
- t,
- "AbortError",
- iframeShowPromise,
- "Expected iframeShowPromise to be aborted by test."
- );
- popupWindow.close();
- iframe.remove();
- }, "Given multiple nested browsing contexts, and an iframe calls show() first, other nested browsing contexts can't show a request.");
-
- promise_test(async t => {
- const iframe = await attachIframe();
- const iframeWindow = iframe.contentWindow;
- const iframeRequest = new iframeWindow.PaymentRequest(methods, details);
- const iframeShowPromise = test_driver.bless(
- "test navigating iframe after show()",
- () => iframeRequest.show(),
- );
-
- // We navigate away, causing the payment sheet to close
- // and the request is showing boolean to become false.
- iframe.src = ""
- await new Promise(resolve => (iframe._onload_ = resolve));
- await promise_rejects_dom(
- t,
- "AbortError",
- iframeShowPromise,
- "Navigating iframe away must cause the iframeShowPromise to reject.",
- );
- iframe.remove();
-
- // Now we should be ok to spin up a new payment request
- const request = new window.PaymentRequest(methods, details);
- const [showPromise] = await test_driver.bless(
- "start a new payment request",
- () => {
- return [request.show()];
- });
-
- // If a payment sheet fails to show, it should reject immediately. If it
- // hasn't rejected in 1 second, then the test has passed.
- t.step_timeout(async () => {
- // We're done. Clean up.
- await request.abort();
- t.done();
- });
-
- // If the navigation in iframe failed to close the original payment sheet
- // there, |showPromise| should reject immediately and this indicates a
- // failure of this test.
- await showPromise.then(() => {
- assert_true(false,
- "Second payment sheet should be pending but is resolved.");
- })
- .catch(e => {
- assert_true(false,
- "Second payment sheet should be pending but is rejected." + e.message);
- });
}, "Navigating an iframe as a nested browsing context sets 'payment request is showing boolean' to false.");
-
- promise_test(async t => {
- const [popupWindow, popupRequest, popupShowPromise] =
- await test_driver.bless(
- "trigger payment in a popup window",
- async () => {
- const popupWindow = await loadPopupInsideUserGesture();
- const popupRequest = new popupWindow.PaymentRequest(methods, details);
- return [popupWindow, popupRequest, popupRequest.show()];
- });
-
- // We navigate away, causing the payment sheet to close
- // and the request is showing boolean to become false.
- popupWindow.location = "blank.html?abc=123";
- await new Promise(resolve => (popupWindow._onload_ = resolve));
-
- // Don't wait for |popupShowPromise| to reject because it may never do
- // (see https://github.com/w3c/payment-request/issues/872). Instead, try
- // to spin up a new payment request and make sure it succeeds.
- const request = new window.PaymentRequest(methods, details);
- const [showPromise] = await test_driver.bless(
- "trigger payment in main window",
- () => {
- return [request.show()];
- });
-
- // If a payment sheet fails to show, it should reject immediately. If it
- // hasn't rejected in 1 second, then the test has passed.
- t.step_timeout(async () => {
- // We're done. Clean up.
- popupWindow.close();
- await request.abort();
- t.done();
- }, 1000);
-
- // If the navigation in popup window failed to close the original payment
- // sheet there, |showPromise| should reject immediately and this indicates
- // a failure of this test.
- await showPromise.then(() => {
- assert_true(false,
- "Second payment sheet should be pending but is resolved.");
- })
- .catch(e => {
- assert_true(false,
- "Second payment sheet should be pending but is rejected.");
- });
- }, "Navigating a popup as a nested browsing context sets 'payment request is showing boolean' to false.");
</script>
</body>
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-canmakepayment-method.https-expected.txt (281829 => 281830)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-canmakepayment-method.https-expected.txt 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-canmakepayment-method.https-expected.txt 2021-08-31 23:07:44 UTC (rev 281830)
@@ -4,6 +4,6 @@
PASS If request.[[state]] is "created", then return a promise that resolves to true for known method.
PASS All methods are unsupported
PASS Mix of supported and unsupported methods, at least one method is supported.
-FAIL If request.[[state]] is "interactive", then return a promise rejected with an "InvalidStateError" DOMException. assert_unreached: Should have rejected: undefined Reached unreachable code
-FAIL If request.[[state]] is "closed", then return a promise rejected with an "InvalidStateError" DOMException. promise_test: Unhandled rejection with value: object "InvalidStateError: The object is in an invalid state."
+PASS If request.[[state]] is "interactive", then return a promise rejected with an "InvalidStateError" DOMException.
+PASS If request.[[state]] is "closed", then return a promise rejected with an "InvalidStateError" DOMException.
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-hasenrolledinstrument-method.tentative.https-expected.txt (281829 => 281830)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-hasenrolledinstrument-method.tentative.https-expected.txt 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-hasenrolledinstrument-method.tentative.https-expected.txt 2021-08-31 23:07:44 UTC (rev 281830)
@@ -2,5 +2,5 @@
FAIL hasEnrolledInstrument() resolves to false for unsupported payment methods. promise_test: Unhandled rejection with value: object "TypeError: request.hasEnrolledInstrument is not a function. (In 'request.hasEnrolledInstrument()', 'request.hasEnrolledInstrument' is undefined)"
FAIL If request.[[state]] is "interactive", then return a promise rejected with an "InvalidStateError" DOMException. promise_test: Unhandled rejection with value: object "TypeError: request.hasEnrolledInstrument is not a function. (In 'request.hasEnrolledInstrument()', 'request.hasEnrolledInstrument' is undefined)"
-FAIL If request.[[state]] is "closed", then return a promise rejected with an "InvalidStateError" DOMException. promise_test: Unhandled rejection with value: object "InvalidStateError: The object is in an invalid state."
+FAIL If request.[[state]] is "closed", then return a promise rejected with an "InvalidStateError" DOMException. promise_rejects_dom: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." that is not a DOMException AbortError: property "code" is equal to 9, expected 20
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-show-method.https-expected.txt (0 => 281830)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-show-method.https-expected.txt (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-show-method.https-expected.txt 2021-08-31 23:07:44 UTC (rev 281830)
@@ -0,0 +1,8 @@
+If you find a buggy test, please file a bug and tag one of the suggested reviewers.
+
+PASS Calling show() without being triggered by user interaction throws
+PASS Throws if the promise [[state]] is not 'created'.
+PASS If the user agent's "payment request is showing" boolean is true, then return a promise rejected with an "AbortError" DOMException.
+PASS If payment method consultation produces no supported method of payment, then return a promise rejected with a "NotSupportedError" DOMException.
+PASS Calling show() multiple times always returns a new promise.
+
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-show-method.https.html (281829 => 281830)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-show-method.https.html 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/payment-request-show-method.https.html 2021-08-31 23:07:44 UTC (rev 281830)
@@ -38,30 +38,20 @@
promise_test(async (t) => {
const request = new PaymentRequest(defaultMethods, defaultDetails);
const acceptPromise = request.show();
- // Abort the request after 2 seconds if it has not rejected. This allows the
- // other tests in this file to run even if a non-compliant browser shows the
- // payment sheet without user activation.
- t.step_timeout(() => {
- t.force_timeout();
- request.abort();
- }, 2000);
-
- await promise_rejects_dom(t, "NotAllowedError", acceptPromise);
+ await promise_rejects_dom(t, "SecurityError", acceptPromise);
}, `Calling show() without being triggered by user interaction throws`);
promise_test(async (t) => {
const request = new PaymentRequest(defaultMethods, defaultDetails);
- await test_driver.bless(
- "test: throws if the promise [[state]] is not 'created'"
- );
- const acceptPromise = request.show(); // Sets state to "interactive"
- // No user activation...
- await promise_rejects_dom(t, "NotAllowedError", request.show());
+ await promise_rejects_dom(t, "SecurityError", request.show());
- // Get user activation
- await test_driver.bless(
- "test: throws if the promise [[state]] is not 'created'"
- );
+ await test_driver.bless();
+ // Sets state to "interactive", but consumes user interaction.
+ const acceptPromise = request.show();
+ await promise_rejects_dom(t, "SecurityError", request.show());
+
+ // With user activation, but already showing the sheet...
+ await test_driver.bless();
await promise_rejects_dom(t, "InvalidStateError", request.show());
await request.abort();
@@ -70,16 +60,15 @@
promise_test(async (t) => {
const request1 = new PaymentRequest(defaultMethods, defaultDetails);
- const request2 = new PaymentRequest(defaultMethods, defaultDetails);
-
- await test_driver.bless("payment request");
+ await test_driver.bless();
const acceptPromise1 = request1.show();
// User activation consumed, so...
- await promise_rejects_dom(t, "NotAllowedError", request2.show());
+ const request2 = new PaymentRequest(defaultMethods, defaultDetails);
+ await promise_rejects_dom(t, "SecurityError", request2.show());
// Payment request already showing, so...
- await test_driver.bless("payment request");
+ await test_driver.bless();
await promise_rejects_dom(t, "AbortError", request2.show());
await request1.abort();
@@ -91,7 +80,7 @@
[{ supportedMethods: "this-is-not-supported" }],
defaultDetails
);
- await test_driver.bless(`test: reject promise with "NotSupportedError"`);
+ await test_driver.bless();
const acceptPromise = request.show();
await promise_rejects_dom(t, "NotSupportedError", acceptPromise);
}, `If payment method consultation produces no supported method of payment, then return a promise rejected with a "NotSupportedError" DOMException.`);
@@ -98,20 +87,20 @@
promise_test(async (t) => {
const request = new PaymentRequest(defaultMethods, defaultDetails);
- await test_driver.bless("call show()");
+ await test_driver.bless();
const p1 = request.show();
- await test_driver.bless("call show()");
+ await test_driver.bless();
const p2 = request.show();
- await test_driver.bless("call show()");
+ await test_driver.bless();
const p3 = request.show();
+ await request.abort();
+
const promises = new Set([p1, p2, p3]);
assert_equals(promises.size, 3, "Must have three unique objects");
+ await promise_rejects_dom(t, "AbortError", p1);
await promise_rejects_dom(t, "InvalidStateError", p2);
await promise_rejects_dom(t, "InvalidStateError", p3);
-
- await request.abort();
- await promise_rejects_dom(t, "AbortError", p1);
}, "Calling show() multiple times always returns a new promise.");
</script>
<small>
@@ -120,5 +109,6 @@
tag one of the
<a
href=""
- >suggested reviewers</a>.
+ >suggested reviewers</a
+ >.
</small>
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/rejects_if_not_active.https.html (281829 => 281830)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/rejects_if_not_active.https.html 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/rejects_if_not_active.https.html 2021-08-31 23:07:44 UTC (rev 281830)
@@ -1,7 +1,7 @@
<!DOCTYPE html>
-<meta charset=utf-8>
+<meta charset="utf-8" />
<title>PaymentRequest show() rejects if doc is not fully active</title>
-<link rel="help" href=""
+<link rel="help" href="" />
<script src=""
<script src=""
<script src=""
@@ -16,7 +16,7 @@
countryCode: "US",
merchantCapabilities: ["supports3DS"],
supportedNetworks: ["visa"],
- }
+ },
});
const validMethod = Object.freeze({
supportedMethods: "basic-card",
@@ -34,64 +34,56 @@
total: validTotal,
});
-function getLoadedPaymentRequest(iframe, url) {
- return new Promise(resolve => {
- iframe.addEventListener(
- "load",
- () => {
- const { PaymentRequest } = iframe.contentWindow;
- const request = new PaymentRequest(validMethods, validDetails);
- resolve(request);
- },
- { once: true }
- );
+async function getLoadedPaymentRequest(iframe, url) {
+ await new Promise((resolve) => {
+ iframe.addEventListener("load", resolve, { once: true });
iframe.src = ""
});
+ const { PaymentRequest } = iframe.contentWindow;
+ const request = new PaymentRequest(validMethods, validDetails);
+ return request;
}
-promise_test(async t => {
+promise_test(async (t) => {
const iframe = document.createElement("iframe");
- iframe.allow = "payment";
document.body.appendChild(iframe);
// Make a request in the iframe.
const request = await getLoadedPaymentRequest(
iframe,
- "/payment-request/resources/page1.html"
+ "resources/page1.html"
);
- const [showPromise] = await test_driver.bless("show payment request", () => {
- return [request.show()];
- });
+ await test_driver.bless("show payment request");
+ const showPromise = request.show();
+ const firstCtor = iframe.contentWindow.DOMException;
+
// Navigate the iframe to a new location. Wait for the load event to fire.
- await new Promise(resolve => {
- iframe.addEventListener("load", resolve);
- iframe.src = ""
- // An implementation may optionally reject |showPromise|.
- showPromise.catch(e => {});
- });
+ const request2 = await getLoadedPaymentRequest(
+ iframe,
+ "resources/page2.html"
+ );
- // The navigaton should have dismissed the previous payment request so it
- // should be possible to show another one now.
- const request2 = new iframe.contentWindow.PaymentRequest(
- validMethods, validDetails);
- const [showPromise2] = await test_driver.bless(
- "show 2nd payment request", () => {
- return [request2.show()];
- });
+ await promise_rejects_dom(
+ t,
+ "AbortError",
+ firstCtor,
+ showPromise,
+ "Show promise should abort."
+ );
- // Stop the test in 1 second if it has not rejected, which means that a
- // payment sheet is successfully shown.
- t.step_timeout(async () => {
- // We are done, so clean up.
- iframe.remove();
- t.done();
- }, 1000);
-
- // This should never settle because the payment sheet should be pending.
- await showPromise2.then(() => {
- assert_true(false, "Second payment should be pending but is resolved.");
- })
- .catch(e => {
- assert_true(false, "Second payment should be pending but is rejected.");
- });
+ // The navigation dismisses the previous payment request so it
+ // now possible to show another one.
+ await test_driver.bless("show 2nd payment request");
+ const showPromise2 = request2.show();
+ const secondCtor = iframe.contentWindow.DOMException;
+ // no longer fully active
+ iframe.remove();
+ await promise_rejects_dom(
+ t,
+ "AbortError",
+ secondCtor,
+ showPromise2,
+ "Show promise should abort."
+ );
}, "If a payment request is showing, but its document is navigated away (so no longer fully active), the payment sheet is dismissed.");
</script>
+</body>
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/show-method-optional-promise-rejects.https-expected.txt (281829 => 281830)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/show-method-optional-promise-rejects.https-expected.txt 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/payment-request/show-method-optional-promise-rejects.https-expected.txt 2021-08-31 23:07:44 UTC (rev 281830)
@@ -1,30 +1,12 @@
-FAIL Rejection of detailsPromise must abort the update with an 'AbortError' DOMException. promise_rejects_dom: badDetails must cause acceptPromise to reject with expectedError function "function () { throw e }" threw object "SecurityError: show() must be triggered by user activation." that is not a DOMException AbortError: property "code" is equal to 18, expected 20
-FAIL Total in the update is a string, so converting to IDL must abort the update with a TypeError. promise_rejects_js: badDetails must cause acceptPromise to reject with expectedError function "function () { throw e }" threw object "SecurityError: show() must be triggered by user activation." ("SecurityError") expected instance of function "function TypeError() {
- [native code]
-}" ("TypeError")
-FAIL Total is recursive, so converting to IDL must abort the update with a TypeError. promise_rejects_js: badDetails must cause acceptPromise to reject with expectedError function "function () { throw e }" threw object "SecurityError: show() must be triggered by user activation." ("SecurityError") expected instance of function "function TypeError() {
- [native code]
-}" ("TypeError")
-FAIL Updating with a negative total results in a TypeError. promise_rejects_js: badDetails must cause acceptPromise to reject with expectedError function "function () { throw e }" threw object "SecurityError: show() must be triggered by user activation." ("SecurityError") expected instance of function "function TypeError() {
- [native code]
-}" ("TypeError")
-FAIL Updating with a displayItem with an invalid currency results in RangeError. promise_rejects_js: badDetails must cause acceptPromise to reject with expectedError function "function () { throw e }" threw object "SecurityError: show() must be triggered by user activation." ("SecurityError") expected instance of function "function RangeError() {
- [native code]
-}" ("RangeError")
-FAIL Updating with duplicate shippingOptions (same IDs) results in a TypeError. promise_rejects_js: badDetails must cause acceptPromise to reject with expectedError function "function () { throw e }" threw object "SecurityError: show() must be triggered by user activation." ("SecurityError") expected instance of function "function TypeError() {
- [native code]
-}" ("TypeError")
-FAIL Updating with a shippingOption with an invalid currency value results in a RangError. promise_rejects_js: badDetails must cause acceptPromise to reject with expectedError function "function () { throw e }" threw object "SecurityError: show() must be triggered by user activation." ("SecurityError") expected instance of function "function RangeError() {
- [native code]
-}" ("RangeError")
-FAIL Must throw a RangeError when a modifier's total item has an invalid currency. promise_rejects_js: badDetails must cause acceptPromise to reject with expectedError function "function () { throw e }" threw object "SecurityError: show() must be triggered by user activation." ("SecurityError") expected instance of function "function RangeError() {
- [native code]
-}" ("RangeError")
-FAIL Must throw a RangeError when a modifier display item has an invalid currency. promise_rejects_js: badDetails must cause acceptPromise to reject with expectedError function "function () { throw e }" threw object "SecurityError: show() must be triggered by user activation." ("SecurityError") expected instance of function "function RangeError() {
- [native code]
-}" ("RangeError")
-FAIL Must throw as Modifier has a recursive dictionary. promise_rejects_js: badDetails must cause acceptPromise to reject with expectedError function "function () { throw e }" threw object "SecurityError: show() must be triggered by user activation." ("SecurityError") expected instance of function "function TypeError() {
- [native code]
-}" ("TypeError")
+PASS Rejection of detailsPromise must abort the update with an 'AbortError' DOMException.
+PASS Total in the update is a string, so converting to IDL must abort the update with a TypeError.
+PASS Total is recursive, so converting to IDL must abort the update with a TypeError.
+PASS Updating with a negative total results in a TypeError.
+PASS Updating with a displayItem with an invalid currency results in RangeError.
+PASS Updating with duplicate shippingOptions (same IDs) results in a TypeError.
+PASS Updating with a shippingOption with an invalid currency value results in a RangError.
+PASS Must throw a RangeError when a modifier's total item has an invalid currency.
+PASS Must throw a RangeError when a modifier display item has an invalid currency.
+PASS Must throw as Modifier has a recursive dictionary.
Modified: trunk/Source/WebCore/ChangeLog (281829 => 281830)
--- trunk/Source/WebCore/ChangeLog 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/Source/WebCore/ChangeLog 2021-08-31 23:07:44 UTC (rev 281830)
@@ -1,3 +1,15 @@
+2021-08-31 Marcos Caceres <[email protected]>
+
+ [Payment Request] Calling PaymentRequest's show() should consume user activation
+ https://bugs.webkit.org/show_bug.cgi?id=217365
+
+ Reviewed by Youenn Fablet and Devin Rousso.
+
+ Tested by existing WPT tests.
+
+ * Modules/paymentrequest/PaymentRequest.cpp:
+ (WebCore::PaymentRequest::show):
+
2021-08-31 Thibault Saunier <[email protected]>
[GStreamer] Fix deadlock tearing down pipeline when using fallback sink
Modified: trunk/Source/WebCore/Modules/paymentrequest/PaymentRequest.cpp (281829 => 281830)
--- trunk/Source/WebCore/Modules/paymentrequest/PaymentRequest.cpp 2021-08-31 22:59:26 UTC (rev 281829)
+++ trunk/Source/WebCore/Modules/paymentrequest/PaymentRequest.cpp 2021-08-31 23:07:44 UTC (rev 281830)
@@ -363,7 +363,7 @@
ASSERT(!m_activePaymentHandler);
}
-// https://www.w3.org/TR/payment-request/#show()-method
+// https://www.w3.org/TR/payment-request/#show-method
void PaymentRequest::show(Document& document, RefPtr<DOMPromise>&& detailsPromise, ShowPromise&& promise)
{
if (!document.frame()) {
@@ -371,7 +371,8 @@
return;
}
- if (!UserGestureIndicator::processingUserGesture()) {
+ auto* window = document.frame()->window();
+ if (!window || !window->consumeTransientActivation()) {
promise.reject(Exception { SecurityError, "show() must be triggered by user activation." });
return;
}