Web Inspector: Show Internal properties of PaymentRequest in Web Inspector Console
authorjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Nov 2017 00:36:15 +0000 (00:36 +0000)
committerjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 9 Nov 2017 00:36:15 +0000 (00:36 +0000)
https://bugs.webkit.org/show_bug.cgi?id=179276

Reviewed by Andy Estes.

Source/JavaScriptCore:

* inspector/InjectedScriptHost.h:
* inspector/JSInjectedScriptHost.cpp:
(Inspector::JSInjectedScriptHost::getInternalProperties):
Call through to virtual implementation so that WebCore can provide custom
internal properties for Web / DOM objects.

Source/WebCore:

Test: http/tests/inspector/runtime/internal-properties-payment-request.https.html

* Modules/paymentrequest/PaymentRequest.h:
Expose access to internal state.

* inspector/WebInjectedScriptHost.h:
* inspector/WebInjectedScriptHost.cpp:
(WebCore::constructInternalProperty):
(WebCore::WebInjectedScriptHost::getInternalProperties):
Provide internal properties for a PaymentRequest.

* testing/Internals.cpp:
(WebCore::Internals::withUserGesture):
* testing/Internals.h:
* testing/Internals.idl:
Provide a simple way to run code inside of a user gesture.

Source/WebInspectorUI:

* UserInterface/Test.html:
* UserInterface/Test/FrontendTestHarness.js:
(FrontendTestHarness.prototype.evaluateInPage):
* UserInterface/Test/TestUtilities.js: Added.
(promisify):
Make async tests a little easier to work with by providing promises
in some cases that would normally take a callback.

LayoutTests:

* TestExpectations:
* platform/mac-wk2/TestExpectations:
Pass test on platforms that support Payment Requests.

* http/tests/inspector/paymentrequest/payment-request-internal-properties.https-expected.txt: Added.
* http/tests/inspector/paymentrequest/payment-request-internal-properties.https.html: Added.
Test for internal properties on PaymentRequest instances.

* resources/ui-helper.js:
(window.UIHelper.withUserGesture):
Provide an easier way to simulate work inside of a user gesture.

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

22 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/http/tests/inspector/network/resource-response-service-worker.html
LayoutTests/http/tests/inspector/paymentrequest/payment-request-internal-properties.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/inspector/paymentrequest/payment-request-internal-properties.https.html [new file with mode: 0644]
LayoutTests/platform/mac-wk2/TestExpectations
LayoutTests/resources/ui-helper.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/InjectedScriptHost.h
Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp
Source/WebCore/ChangeLog
Source/WebCore/Modules/paymentrequest/PaymentRequest.h
Source/WebCore/html/VoidCallback.idl
Source/WebCore/inspector/WebInjectedScriptHost.cpp
Source/WebCore/inspector/WebInjectedScriptHost.h
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Test.html
Source/WebInspectorUI/UserInterface/Test/FrontendTestHarness.js
Source/WebInspectorUI/UserInterface/Test/TestUtilities.js [new file with mode: 0644]

index 14d4c64..5e3d379 100644 (file)
@@ -1,3 +1,22 @@
+2017-11-08  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Show Internal properties of PaymentRequest in Web Inspector Console
+        https://bugs.webkit.org/show_bug.cgi?id=179276
+
+        Reviewed by Andy Estes.
+
+        * TestExpectations:
+        * platform/mac-wk2/TestExpectations:
+        Pass test on platforms that support Payment Requests.
+
+        * http/tests/inspector/paymentrequest/payment-request-internal-properties.https-expected.txt: Added.
+        * http/tests/inspector/paymentrequest/payment-request-internal-properties.https.html: Added.
+        Test for internal properties on PaymentRequest instances.
+
+        * resources/ui-helper.js:
+        (window.UIHelper.withUserGesture):
+        Provide an easier way to simulate work inside of a user gesture.
+
 2017-11-08  Ryan Haddad  <ryanhaddad@apple.com>
 
         Mark many service worker tests as flaky on iOS.
index defd7a3..3664337 100644 (file)
@@ -200,6 +200,7 @@ fast/text/font-lookup-dot-prefix-case-sensitive.html [ Skip ]
 
 # Payment Request is only enabled on Cocoa platforms.
 http/tests/paymentrequest [ Skip ]
+http/tests/inspector/paymentrequest [ Skip ]
 imported/w3c/web-platform-tests/payment-request [ Skip ]
 
 imported/w3c/web-platform-tests/2dcontext/transformations/canvas_transformations_reset_001.html [ ImageOnlyFailure ]
index 5c7d49e..1551cb3 100644 (file)
@@ -16,7 +16,6 @@ async function triggerServiceWorkerLoad(url) {
 
 function test()
 {
-    InspectorTest.debug();
     let suite = InspectorTest.createAsyncSuite("Resource.ResponseSource.ServiceWorker");
 
     function addTestCase({name, description, setup, expression, statusCode, responseSource}) {
diff --git a/LayoutTests/http/tests/inspector/paymentrequest/payment-request-internal-properties.https-expected.txt b/LayoutTests/http/tests/inspector/paymentrequest/payment-request-internal-properties.https-expected.txt
new file mode 100644 (file)
index 0000000..7bd401c
--- /dev/null
@@ -0,0 +1,168 @@
+Internal properties of a PaymentRequest.
+
+
+== Running test suite: RemoteObject.InternalProperties.PaymentRequest
+-- Running test case: RemoteObject.InternalProperties.PaymentRequest.Created.Large
+INTERNAL PROPERTY: state: "created"
+INTERNAL PROPERTY: details: {
+  "id": "<filtered>",
+  "total": {
+    "label": "Total",
+    "amount": {
+      "currency": "USD",
+      "value": "99.00",
+      "currencySystem": "<filtered>"
+    },
+    "pending": false
+  },
+  "displayItems": [
+    {
+      "label": "Item 1",
+      "amount": {
+        "currency": "USD",
+        "value": "90.00",
+        "currencySystem": "<filtered>"
+      },
+      "pending": false
+    },
+    {
+      "label": "Item 2",
+      "amount": {
+        "currency": "USD",
+        "value": "90.00",
+        "currencySystem": "<filtered>"
+      },
+      "pending": true
+    },
+    {
+      "label": "Item 3",
+      "amount": {
+        "currency": "USD",
+        "value": "9.00",
+        "currencySystem": "<filtered>"
+      },
+      "pending": false
+    }
+  ],
+  "shippingOptions": [
+    {
+      "id": "<filtered>",
+      "label": "Shipping Label 1",
+      "amount": {
+        "currency": "USD",
+        "value": "5.00",
+        "currencySystem": "<filtered>"
+      },
+      "selected": false
+    },
+    {
+      "id": "<filtered>",
+      "label": "Shipping Label 2",
+      "amount": {
+        "currency": "USD",
+        "value": "10.00",
+        "currencySystem": "<filtered>"
+      },
+      "selected": true
+    }
+  ],
+  "modifiers": [
+    {
+      "supportedMethods": "basic-card",
+      "total": {
+        "label": "Total",
+        "amount": {
+          "currency": "USD",
+          "value": "99.00",
+          "currencySystem": "<filtered>"
+        },
+        "pending": false
+      },
+      "additionalDisplayItems": [],
+      "data": null
+    }
+  ]
+}
+INTERNAL PROPERTY: options: {
+  "requestPayerName": false,
+  "requestPayerEmail": false,
+  "requestPayerPhone": false,
+  "requestShipping": false,
+  "shippingType": "shipping"
+}
+
+-- Running test case: RemoteObject.InternalProperties.PaymentRequest.Created
+INTERNAL PROPERTY: state: "created"
+INTERNAL PROPERTY: details: {
+  "id": "<filtered>",
+  "total": {
+    "label": "Total",
+    "amount": {
+      "currency": "USD",
+      "value": "9.99",
+      "currencySystem": "<filtered>"
+    },
+    "pending": false
+  },
+  "displayItems": [],
+  "shippingOptions": [],
+  "modifiers": []
+}
+INTERNAL PROPERTY: options: {
+  "requestPayerName": false,
+  "requestPayerEmail": false,
+  "requestPayerPhone": false,
+  "requestShipping": false,
+  "shippingType": "shipping"
+}
+
+-- Running test case: RemoteObject.InternalProperties.PaymentRequest.Initiated
+INTERNAL PROPERTY: state: "interactive"
+INTERNAL PROPERTY: details: {
+  "id": "<filtered>",
+  "total": {
+    "label": "Total",
+    "amount": {
+      "currency": "USD",
+      "value": "9.99",
+      "currencySystem": "<filtered>"
+    },
+    "pending": false
+  },
+  "displayItems": [],
+  "shippingOptions": [],
+  "modifiers": []
+}
+INTERNAL PROPERTY: options: {
+  "requestPayerName": false,
+  "requestPayerEmail": false,
+  "requestPayerPhone": false,
+  "requestShipping": false,
+  "shippingType": "shipping"
+}
+
+-- Running test case: RemoteObject.InternalProperties.PaymentRequest.Closed
+INTERNAL PROPERTY: state: "closed"
+INTERNAL PROPERTY: details: {
+  "id": "<filtered>",
+  "total": {
+    "label": "Total",
+    "amount": {
+      "currency": "USD",
+      "value": "9.99",
+      "currencySystem": "<filtered>"
+    },
+    "pending": false
+  },
+  "displayItems": [],
+  "shippingOptions": [],
+  "modifiers": []
+}
+INTERNAL PROPERTY: options: {
+  "requestPayerName": false,
+  "requestPayerEmail": false,
+  "requestPayerPhone": false,
+  "requestShipping": false,
+  "shippingType": "shipping"
+}
+
diff --git a/LayoutTests/http/tests/inspector/paymentrequest/payment-request-internal-properties.https.html b/LayoutTests/http/tests/inspector/paymentrequest/payment-request-internal-properties.https.html
new file mode 100644 (file)
index 0000000..e35f925
--- /dev/null
@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/js-test-resources/ui-helper.js"></script>
+<script src="../resources/inspector-test.js"></script>
+<script>
+const method = {
+    supportedMethods: "https://apple.com/apple-pay",
+    data: {
+        version: 2,
+        merchantIdentifier: "",
+        merchantCapabilities: ["supports3DS"],
+        supportedNetworks: ["visa", "masterCard"],
+        countryCode: "US",
+    },
+};
+
+const simpleDetails = {
+    total: {
+        label: "Total",
+        amount: {currency: "USD", value: "9.99"}
+    }
+};
+
+const largeDetails = {
+    total: {
+        label: "Total",
+        amount: {currency: "USD", value: "99.00"},
+    },
+    displayItems: [
+        {
+            label: "Item 1",
+            amount: {currency: "USD", value: "90.00"},
+        }, {
+            label: "Item 2",
+            amount: {currency: "USD", value: "90.00"},
+            pending: true,
+        }, {
+            label: "Item 3",
+            amount: {currency: "USD", value: "9.00"},
+        }
+    ],
+    shippingOptions: [
+        {
+            id: "shippingId1",
+            label: "Shipping Label 1",
+            amount: {currency: "USD", value: "5.00"},
+            selected: false,
+        }, {
+            id: "shippingId2",
+            label: "Shipping Label 2",
+            amount: {currency: "USD", value: "10.00"},
+            selected: true,
+        }
+    ],
+    modifiers: [
+        {
+            supportedMethods: "basic-card",
+            total: {
+                label: "Total",
+                amount: {currency: "USD", value: "99.00"},
+            },
+        }
+    ],
+};
+
+let currentRequest = null;
+function createPaymentRequest() {
+    if (currentRequest)
+        currentRequest.abort().catch(() => {});
+    currentRequest = new PaymentRequest([method], simpleDetails);
+    return currentRequest;
+}
+
+function triggerLargePaymentRequest() {
+    let request = new PaymentRequest([method], largeDetails);
+    Promise.resolve().then(() => { TestPage.dispatchEventToFrontend("Continue"); });
+    return request;
+}
+
+function triggerCreatedPaymentRequest() {
+    let request = createPaymentRequest();
+    Promise.resolve().then(() => { TestPage.dispatchEventToFrontend("Continue"); });
+    return request;
+}
+
+function triggerInitiatedPaymentRequest() {
+    let request = createPaymentRequest();
+    UIHelper.withUserGesture(async () => {
+        request.show().catch(() => {});
+        TestPage.dispatchEventToFrontend("Continue");
+    });
+    return request;
+}
+
+function triggerClosedPaymentRequest() {
+    let request = createPaymentRequest();
+    UIHelper.withUserGesture(async () => {
+        request.show().catch(() => {});
+        await request.abort();
+        TestPage.dispatchEventToFrontend("Continue");
+    });
+    return request;
+}
+
+// ----
+
+function test()
+{
+    async function propertyDescriptorString(propertyDescriptor) {
+        function inspectedPage_internalValue_JSON() {
+            return JSON.stringify(this, (key, value) => {
+                if (key === "id")
+                    return "<filtered>";
+                if (key === "currencySystem")
+                    return "<filtered>";
+                return value;
+            }, 2);
+        }
+
+        // Primitive. We have the value.
+        if (propertyDescriptor.value.type !== "object")
+            return propertyDescriptor.name + ": " + JSON.stringify(propertyDescriptor.value.description);
+
+        // Object. Stringify on the page.
+        let [jsonValue] = await promisify((cb) => { propertyDescriptor.value.callFunctionJSON(inspectedPage_internalValue_JSON, undefined, cb); });
+        return propertyDescriptor.name + ": " + jsonValue;
+    }
+
+    let suite = InspectorTest.createAsyncSuite("RemoteObject.InternalProperties.PaymentRequest");
+
+    function addTestCase({name, description, expression}) {
+        suite.addTestCase({
+            name, description,
+            async test() {
+                let [result] = await Promise.all([
+                    InspectorTest.evaluateInPage(expression),
+                    InspectorTest.awaitEvent("Continue"),
+                ]);
+                let remoteObject = WI.RemoteObject.fromPayload(result.result);
+                let [propertyDescriptors] = await promisify((cb) => { remoteObject.getAllPropertyDescriptors(cb); });
+                for (let propertyDescriptor of propertyDescriptors.reverse()) {
+                    if (propertyDescriptor.isInternalProperty) {
+                        let displayString = await propertyDescriptorString(propertyDescriptor);
+                        InspectorTest.log("INTERNAL PROPERTY: " + displayString);
+                    }
+                }
+            }
+        });
+    }
+
+    addTestCase({
+        name: "RemoteObject.InternalProperties.PaymentRequest.Created.Large",
+        expression: `triggerLargePaymentRequest()`,
+    });
+
+    addTestCase({
+        name: "RemoteObject.InternalProperties.PaymentRequest.Created",
+        expression: `triggerCreatedPaymentRequest()`,
+    });
+
+    addTestCase({
+        name: "RemoteObject.InternalProperties.PaymentRequest.Initiated",
+        expression: `triggerInitiatedPaymentRequest()`,
+    });
+
+    addTestCase({
+        name: "RemoteObject.InternalProperties.PaymentRequest.Closed",
+        expression: `triggerClosedPaymentRequest()`,
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+<p>Internal properties of a PaymentRequest.</p>
+</body>
+</html>
index 374d782..8df4968 100644 (file)
@@ -26,6 +26,7 @@ fast/visual-viewport/rubberbanding-viewport-rects.html [ Pass ]
 fast/visual-viewport/rubberbanding-viewport-rects-header-footer.html  [ Pass ]
 
 [ Sierra+ ] http/tests/paymentrequest [ Pass ]
+[ Sierra+ ] http/tests/inspector/paymentrequest [ Pass ]
 [ Sierra+ ] imported/w3c/web-platform-tests/payment-request [ Pass ]
 webkit.org/b/178107 [ Sierra+ ] http/tests/paymentrequest/payment-request-abort-method.https.html [ Pass Failure ]
 webkit.org/b/175611 imported/w3c/web-platform-tests/payment-request/allowpaymentrequest/allowpaymentrequest-attribute-cross-origin-bc-containers.https.html [ Skip ]
index cbc43ae..44b7689 100644 (file)
@@ -156,4 +156,9 @@ window.UIHelper = class UIHelper {
 
         return promise.then(finish, finish);
     }
+
+    static withUserGesture(callback)
+    {
+        internals.withUserGesture(callback);
+    }
 }
index 549cb81..455b01a 100644 (file)
@@ -1,3 +1,16 @@
+2017-11-08  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Show Internal properties of PaymentRequest in Web Inspector Console
+        https://bugs.webkit.org/show_bug.cgi?id=179276
+
+        Reviewed by Andy Estes.
+
+        * inspector/InjectedScriptHost.h:
+        * inspector/JSInjectedScriptHost.cpp:
+        (Inspector::JSInjectedScriptHost::getInternalProperties):
+        Call through to virtual implementation so that WebCore can provide custom
+        internal properties for Web / DOM objects.
+
 2017-11-08  Saam Barati  <sbarati@apple.com>
 
         A JSFunction's ObjectAllocationProfile should watch the poly prototype watchpoint so it can clear its object allocation profile
index 2eb8f76..3256c25 100644 (file)
@@ -37,6 +37,7 @@ public:
     virtual ~InjectedScriptHost();
 
     virtual JSC::JSValue subtype(JSC::ExecState*, JSC::JSValue) { return JSC::jsUndefined(); }
+    virtual JSC::JSValue getInternalProperties(JSC::VM&, JSC::ExecState*, JSC::JSValue) { return { }; }
     virtual bool isHTMLAllCollection(JSC::VM&, JSC::JSValue) { return false; }
 
     JSC::JSValue wrapper(JSC::ExecState*, JSC::JSGlobalObject*);
index adf7c6c..e3d3cf0 100644 (file)
@@ -272,6 +272,10 @@ JSValue JSInjectedScriptHost::getInternalProperties(ExecState* exec)
     auto scope = DECLARE_THROW_SCOPE(vm);
     JSValue value = exec->uncheckedArgument(0);
 
+    JSValue internalProperties = impl().getInternalProperties(vm, exec, value);
+    if (internalProperties)
+        return internalProperties;
+
     if (JSPromise* promise = jsDynamicCast<JSPromise*>(vm, value)) {
         unsigned index = 0;
         JSArray* array = constructEmptyArray(exec, nullptr);
index da2f6b5..629b2df 100644 (file)
@@ -1,3 +1,27 @@
+2017-11-08  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Show Internal properties of PaymentRequest in Web Inspector Console
+        https://bugs.webkit.org/show_bug.cgi?id=179276
+
+        Reviewed by Andy Estes.
+
+        Test: http/tests/inspector/runtime/internal-properties-payment-request.https.html
+
+        * Modules/paymentrequest/PaymentRequest.h:
+        Expose access to internal state.
+
+        * inspector/WebInjectedScriptHost.h:
+        * inspector/WebInjectedScriptHost.cpp:
+        (WebCore::constructInternalProperty):
+        (WebCore::WebInjectedScriptHost::getInternalProperties):
+        Provide internal properties for a PaymentRequest.
+
+        * testing/Internals.cpp:
+        (WebCore::Internals::withUserGesture):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+        Provide a simple way to run code inside of a user gesture.
+
 2017-11-08  Ryosuke Niwa  <rniwa@webkit.org>
 
         REGRESSION(r224534): Crash inside Document::updateStyleIfNeeded
index d5c7c3e..5eff30a 100644 (file)
@@ -65,6 +65,14 @@ public:
     const String& shippingOption() const { return m_shippingOption; }
     std::optional<PaymentShippingType> shippingType() const;
 
+    enum class State {
+        Created,
+        Interactive,
+        Closed,
+    };
+
+    State state() const { return m_state; }
+
     const PaymentOptions& paymentOptions() const { return m_options; }
     const PaymentDetailsInit& paymentDetails() const { return m_details; }
 
@@ -80,12 +88,6 @@ public:
     using RefCounted<PaymentRequest>::deref;
 
 private:
-    enum class State {
-        Created,
-        Interactive,
-        Closed,
-    };
-
     struct Method {
         MethodIdentifier identifier;
         String serializedData;
index 6618d7d..a9c92aa 100644 (file)
@@ -20,7 +20,9 @@
  * 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. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-callback VoidCallback = void ();
+[
+    ExportMacro=WEBCORE_EXPORT
+] callback VoidCallback = void ();
index b286de7..5f2343b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-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
 #include "JSNode.h"
 #include "JSNodeList.h"
 
+#if ENABLE(PAYMENT_REQUEST)
+#include "JSPaymentRequest.h"
+#include "JSPaymentShippingType.h"
+#include "PaymentOptions.h"
+#include "PaymentRequest.h"
+#endif
 
 namespace WebCore {
+
 using namespace JSC;
 
-JSValue WebInjectedScriptHost::subtype(JSC::ExecState* exec, JSC::JSValue value)
+JSValue WebInjectedScriptHost::subtype(ExecState* exec, JSValue value)
 {
     VM& vm = exec->vm();
     if (value.inherits(vm, JSNode::info()))
@@ -52,6 +59,129 @@ JSValue WebInjectedScriptHost::subtype(JSC::ExecState* exec, JSC::JSValue value)
     return jsUndefined();
 }
 
+#if ENABLE(PAYMENT_REQUEST)
+static JSObject* constructInternalProperty(VM& vm, ExecState* exec, const String& name, JSValue value)
+{
+    auto* object = constructEmptyObject(exec);
+    object->putDirect(vm, Identifier::fromString(exec, "name"), jsString(exec, name));
+    object->putDirect(vm, Identifier::fromString(exec, "value"), value);
+    return object;
+}
+
+static JSObject* objectForPaymentOptions(VM& vm, ExecState* exec, const PaymentOptions& paymentOptions)
+{
+    auto* object = constructEmptyObject(exec);
+    object->putDirect(vm, Identifier::fromString(exec, "requestPayerName"), jsBoolean(paymentOptions.requestPayerName));
+    object->putDirect(vm, Identifier::fromString(exec, "requestPayerEmail"), jsBoolean(paymentOptions.requestPayerEmail));
+    object->putDirect(vm, Identifier::fromString(exec, "requestPayerPhone"), jsBoolean(paymentOptions.requestPayerPhone));
+    object->putDirect(vm, Identifier::fromString(exec, "requestShipping"), jsBoolean(paymentOptions.requestShipping));
+    object->putDirect(vm, Identifier::fromString(exec, "shippingType"), jsNontrivialString(exec, convertEnumerationToString(paymentOptions.shippingType)));
+    return object;
+}
+
+static JSObject* objectForPaymentCurrencyAmount(VM& vm, ExecState* exec, const PaymentCurrencyAmount& paymentCurrencyAmount)
+{
+    auto* object = constructEmptyObject(exec);
+    object->putDirect(vm, Identifier::fromString(exec, "currency"), jsString(exec, paymentCurrencyAmount.currency));
+    object->putDirect(vm, Identifier::fromString(exec, "value"), jsString(exec, paymentCurrencyAmount.value));
+    object->putDirect(vm, Identifier::fromString(exec, "currencySystem"), jsString(exec, paymentCurrencyAmount.currencySystem));
+    return object;
+}
+
+static JSObject* objectForPaymentItem(VM& vm, ExecState* exec, const PaymentItem& paymentItem)
+{
+    auto* object = constructEmptyObject(exec);
+    object->putDirect(vm, Identifier::fromString(exec, "label"), jsString(exec, paymentItem.label));
+    object->putDirect(vm, Identifier::fromString(exec, "amount"), objectForPaymentCurrencyAmount(vm, exec, paymentItem.amount));
+    object->putDirect(vm, Identifier::fromString(exec, "pending"), jsBoolean(paymentItem.pending));
+    return object;
+}
+
+static JSObject* objectForPaymentShippingOption(VM& vm, ExecState* exec, const PaymentShippingOption& paymentShippingOption)
+{
+    auto* object = constructEmptyObject(exec);
+    object->putDirect(vm, Identifier::fromString(exec, "id"), jsString(exec, paymentShippingOption.id));
+    object->putDirect(vm, Identifier::fromString(exec, "label"), jsString(exec, paymentShippingOption.label));
+    object->putDirect(vm, Identifier::fromString(exec, "amount"), objectForPaymentCurrencyAmount(vm, exec, paymentShippingOption.amount));
+    object->putDirect(vm, Identifier::fromString(exec, "selected"), jsBoolean(paymentShippingOption.selected));
+    return object;
+}
+
+static JSObject* objectForPaymentDetailsModifier(VM& vm, ExecState* exec, const PaymentDetailsModifier& modifier)
+{
+    auto* additionalDisplayItems = constructEmptyArray(exec, nullptr);
+    for (unsigned i = 0; i < modifier.additionalDisplayItems.size(); ++i)
+        additionalDisplayItems->putDirectIndex(exec, i, objectForPaymentItem(vm, exec, modifier.additionalDisplayItems[i]));
+
+    auto* object = constructEmptyObject(exec);
+    object->putDirect(vm, Identifier::fromString(exec, "supportedMethods"), jsString(exec, modifier.supportedMethods));
+    object->putDirect(vm, Identifier::fromString(exec, "total"), !modifier.total ? jsNull() : objectForPaymentItem(vm, exec, *modifier.total));
+    object->putDirect(vm, Identifier::fromString(exec, "additionalDisplayItems"), additionalDisplayItems);
+    object->putDirect(vm, Identifier::fromString(exec, "data"), !modifier.data ? jsNull() : modifier.data.get());
+    return object;
+}
+
+static JSObject* objectForPaymentDetails(VM& vm, ExecState* exec, const PaymentDetailsInit& paymentDetails)
+{
+    auto* displayItems = constructEmptyArray(exec, nullptr);
+    for (unsigned i = 0; i < paymentDetails.displayItems.size(); ++i)
+        displayItems->putDirectIndex(exec, i, objectForPaymentItem(vm, exec, paymentDetails.displayItems[i]));
+
+    auto* shippingOptions = constructEmptyArray(exec, nullptr);
+    for (unsigned i = 0; i < paymentDetails.shippingOptions.size(); ++i)
+        shippingOptions->putDirectIndex(exec, i, objectForPaymentShippingOption(vm, exec, paymentDetails.shippingOptions[i]));
+
+    auto* modifiers = constructEmptyArray(exec, nullptr);
+    for (unsigned i = 0; i < paymentDetails.modifiers.size(); ++i)
+        modifiers->putDirectIndex(exec, i, objectForPaymentDetailsModifier(vm, exec, paymentDetails.modifiers[i]));
+
+    auto* object = constructEmptyObject(exec);
+    object->putDirect(vm, Identifier::fromString(exec, "id"), jsString(exec, paymentDetails.id));
+    object->putDirect(vm, Identifier::fromString(exec, "total"), objectForPaymentItem(vm, exec, paymentDetails.total));
+    object->putDirect(vm, Identifier::fromString(exec, "displayItems"), displayItems);
+    object->putDirect(vm, Identifier::fromString(exec, "shippingOptions"), shippingOptions);
+    object->putDirect(vm, Identifier::fromString(exec, "modifiers"), modifiers);
+    return object;
+}
+
+static JSString* jsStringForPaymentRequestState(VM& vm, ExecState* exec, PaymentRequest::State state)
+{
+    switch (state) {
+    case PaymentRequest::State::Created:
+        return jsNontrivialString(exec, ASCIILiteral("created"));
+    case PaymentRequest::State::Interactive:
+        return jsNontrivialString(exec, ASCIILiteral("interactive"));
+    case PaymentRequest::State::Closed:
+        return jsNontrivialString(exec, ASCIILiteral("closed"));
+    }
+
+    ASSERT_NOT_REACHED();
+    return jsEmptyString(&vm);
+}
+#endif
+
+JSValue WebInjectedScriptHost::getInternalProperties(VM& vm, ExecState* exec, JSC::JSValue value)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+#if ENABLE(PAYMENT_REQUEST)
+    if (PaymentRequest* paymentRequest = JSPaymentRequest::toWrapped(vm, value)) {
+        unsigned index = 0;
+        auto* array = constructEmptyArray(exec, nullptr);
+        array->putDirectIndex(exec, index++, constructInternalProperty(vm, exec, ASCIILiteral("options"), objectForPaymentOptions(vm, exec, paymentRequest->paymentOptions())));
+        array->putDirectIndex(exec, index++, constructInternalProperty(vm, exec, ASCIILiteral("details"), objectForPaymentDetails(vm, exec, paymentRequest->paymentDetails())));
+        array->putDirectIndex(exec, index++, constructInternalProperty(vm, exec, ASCIILiteral("state"), jsStringForPaymentRequestState(vm, exec, paymentRequest->state())));
+        RETURN_IF_EXCEPTION(scope, { });
+        return array;
+    }
+#else
+    UNUSED_PARAM(exec);
+    UNUSED_PARAM(value);
+#endif
+
+    return { };
+}
+
 bool WebInjectedScriptHost::isHTMLAllCollection(JSC::VM& vm, JSC::JSValue value)
 {
     return value.inherits(vm, JSHTMLAllCollection::info());
index 17436ea..061f7c0 100644 (file)
@@ -34,6 +34,7 @@ public:
     static Ref<WebInjectedScriptHost> create() { return adoptRef(*new WebInjectedScriptHost); }
 
     JSC::JSValue subtype(JSC::ExecState*, JSC::JSValue) override;
+    JSC::JSValue getInternalProperties(JSC::VM&, JSC::ExecState*, JSC::JSValue) override;
     bool isHTMLAllCollection(JSC::VM&, JSC::JSValue) override;
 };
 
index ee09e77..4a1d247 100644 (file)
 #include "UserGestureIndicator.h"
 #include "UserMediaController.h"
 #include "ViewportArguments.h"
+#include "VoidCallback.h"
 #include "WebCoreJSClientData.h"
 #if ENABLE(WEBGL)
 #include "WebGLRenderingContext.h"
@@ -3897,6 +3898,12 @@ bool Internals::isProcessingUserGesture()
     return UserGestureIndicator::processingUserGesture();
 }
 
+void Internals::withUserGesture(RefPtr<VoidCallback>&& callback)
+{
+    UserGestureIndicator gestureIndicator(ProcessingUserGesture, contextDocument());
+    callback->handleEvent();
+}
+
 double Internals::lastHandledUserGestureTimestamp()
 {
     Document* document = contextDocument();
index db2f496..f3f8fff 100644 (file)
@@ -76,9 +76,9 @@ class MockPageOverlay;
 class MockPaymentCoordinator;
 class NodeList;
 class Page;
+class RTCPeerConnection;
 class Range;
 class RenderedDocumentMarker;
-class RTCPeerConnection;
 class SVGSVGElement;
 class SerializedScriptValue;
 class SourceBuffer;
@@ -86,6 +86,7 @@ class StringCallback;
 class StyleSheet;
 class TimeRanges;
 class TypeConversions;
+class VoidCallback;
 class WebGLRenderingContext;
 class XMLHttpRequest;
 
@@ -558,6 +559,8 @@ public:
     bool isProcessingUserGesture();
     double lastHandledUserGestureTimestamp();
 
+    void withUserGesture(RefPtr<VoidCallback>&&);
+
     RefPtr<GCObservation> observeGC(JSC::JSValue);
 
     enum class UserInterfaceLayoutDirection { LTR, RTL };
index 40bde78..2cc752c 100644 (file)
@@ -513,6 +513,8 @@ enum EventThrottlingBehavior {
     boolean isProcessingUserGesture();
     double lastHandledUserGestureTimestamp();
 
+    void withUserGesture(VoidCallback callback);
+
     GCObservation? observeGC(any observed);
 
     void setUserInterfaceLayoutDirection(UserInterfaceLayoutDirection userInterfaceLayoutDirection);
index a8e0f78..c7590c2 100644 (file)
@@ -1,3 +1,18 @@
+2017-11-08  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Show Internal properties of PaymentRequest in Web Inspector Console
+        https://bugs.webkit.org/show_bug.cgi?id=179276
+
+        Reviewed by Andy Estes.
+
+        * UserInterface/Test.html:
+        * UserInterface/Test/FrontendTestHarness.js:
+        (FrontendTestHarness.prototype.evaluateInPage):
+        * UserInterface/Test/TestUtilities.js: Added.
+        (promisify):
+        Make async tests a little easier to work with by providing promises
+        in some cases that would normally take a callback.
+
 2017-11-08  Brian Burg  <bburg@apple.com>
 
         Web Inspector: fix incorrect curly quote orientation in a UIString
index bb10bfa..7f48279 100644 (file)
@@ -48,6 +48,7 @@
     <script src="Test/Test.js"></script>
     <script src="Controllers/AppControllerBase.js"></script>
     <script src="Test/TestAppController.js"></script>
+    <script src="Test/TestUtilities.js"></script>
 
     <script src="Base/DOMUtilities.js"></script>
     <script src="Base/EventListener.js"></script>
index d5fe1ea..4133e94 100644 (file)
@@ -86,7 +86,10 @@ FrontendTestHarness = class FrontendTestHarness extends TestHarness
             return;
         }
 
-        RuntimeAgent.evaluate.invoke({expression, objectGroup: "test", includeCommandLineAPI: false}, callback);
+        if (typeof callback === "function")
+            RuntimeAgent.evaluate.invoke({expression, objectGroup: "test", includeCommandLineAPI: false}, callback);
+        else
+            return RuntimeAgent.evaluate.invoke({expression, objectGroup: "test", includeCommandLineAPI: false});
     }
 
     debug()
diff --git a/Source/WebInspectorUI/UserInterface/Test/TestUtilities.js b/Source/WebInspectorUI/UserInterface/Test/TestUtilities.js
new file mode 100644 (file)
index 0000000..8336132
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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 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 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.
+ */
+
+//
+// This can be used to get a promise for any function that takes a callback.
+//
+// For example:
+//
+//    object.getValues(arg1, arg2, (callbackArg1, callbackArg2) => {
+//        ...
+//    });
+//
+// Can be promisified like so:
+//
+//    promisify((cb) => { object.getValues(arg1, arg2, cb); }).then([callbackArg1, callbackArg2]) {
+//        ...
+//    });
+//
+// Or more naturally with await:
+//
+//    let [callbackArg1, callbackArg2] = await promisify((cb) => { object.getValues(arg1, arg2, cb); });
+//
+
+function promisify(func) {
+    return new Promise((resolve, reject) => {
+        try {
+            func((...args) => { resolve(args); });
+        } catch (e) {
+            reject(e);
+        }
+    });
+}