Skip to content
Permalink
Browse files
Web Inspector: Audit: allow audits to be evaluated as a user gesture
https://bugs.webkit.org/show_bug.cgi?id=200276

Reviewed by Patrick Angle.

This will allow developers to create/run audits that pretend to be a user (e.g. play media, etc.).

* Source/WebCore/inspector/InspectorAuditDOMObject.idl:
* Source/WebCore/inspector/InspectorAuditDOMObject.h:
(WebCore::InspectorAuditDOMObject::create):
* Source/WebCore/inspector/InspectorAuditDOMObject.cpp:
(WebCore::InspectorAuditDOMObject::InspectorAuditDOMObject):
(WebCore::InspectorAuditDOMObject::simulateUserInteraction): Added.

* Source/JavaScriptCore/inspector/protocol/Audit.json:
* Source/WebInspectorUI/UserInterface/Models/AuditTestBase.js:
Increment the current audit version number.

* Source/WebCore/inspector/agents/page/PageAuditAgent.h:
(WebCore::PageAuditAgent::inspectedPage const): Added.
Expose a way for `InspectorAudit*Object` to access the inspected `Page`.

* Source/WebCore/inspector/UserGestureEmulationScope.h: Renamed from Source/WebCore/inspector/agents/page/UserGestureEmulationScope.h.
* Source/WebCore/inspector/UserGestureEmulationScope.cpp: Renamed from Source/WebCore/inspector/agents/page/UserGestureEmulationScope.cpp.
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
Drive-by: Move this file as it's now used by more than just the `Page*Agent` specializations.

* LayoutTests/inspector/audit/run-dom.html:
* LayoutTests/inspector/audit/run-dom-expected.txt:

* LayoutTests/inspector/model/auditTestCase.html:
* LayoutTests/inspector/model/auditTestCase-expected.txt:
* LayoutTests/inspector/model/auditTestGroup.html:
* LayoutTests/inspector/model/auditTestGroup-expected.txt:
Drive-by: Make it so that these tests no longer need to be updated when the audit version changes.

Canonical link: https://commits.webkit.org/251241@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295152 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
dcrousso committed Jun 2, 2022
1 parent 7351d19 commit 7b44a87191f69607ae6fefaa789da08bc1bfda3f
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 51 deletions.
@@ -3,59 +3,98 @@ Tests for the injected WebInspectorAudit.DOM functions.


== Running test suite: Audit.run.DOM
-- Running test case: Audit.run.DOM.hasEventListeners.noListeners
-- Running test case: Audit.run.DOM.hasEventListeners.None.General
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#noListeners"))`...
Result: false
Audit teardown...

-- Running test case: Audit.run.DOM.hasEventListeners.noListeners.WithArgs
-- Running test case: Audit.run.DOM.hasEventListeners.None.click
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#noListeners"), ["FakeEvent"])`...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#noListeners"), "click")`...
Result: false
Audit teardown...

-- Running test case: Audit.run.DOM.hasEventListeners.attributeListener
-- Running test case: Audit.run.DOM.hasEventListeners.None.FakeEvent
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#noListeners"), "FakeEvent")`...
Result: false
Audit teardown...

-- Running test case: Audit.run.DOM.hasEventListeners.Attribute.General
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#attributeListener"))`...
Result: true
Audit teardown...

-- Running test case: Audit.run.DOM.hasEventListeners.attributeListener.WithArgs
-- Running test case: Audit.run.DOM.hasEventListeners.Attribute.click
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#attributeListener"), ["FakeEvent"])`...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#attributeListener"), "click")`...
Result: true
Audit teardown...

-- Running test case: Audit.run.DOM.hasEventListeners.Attribute.FakeEvent
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#attributeListener"), "FakeEvent")`...
Result: false
Audit teardown...

-- Running test case: Audit.run.DOM.hasEventListeners.javascriptListener
-- Running test case: Audit.run.DOM.hasEventListeners.JavaScript.General
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#javascriptListener"))`...
Result: true
Audit teardown...

-- Running test case: Audit.run.DOM.hasEventListeners.javascriptListener.WithArgs
-- Running test case: Audit.run.DOM.hasEventListeners.JavaScript.click
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#javascriptListener"), "click")`...
Result: true
Audit teardown...

-- Running test case: Audit.run.DOM.hasEventListeners.JavaScript.FakeEvent
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#javascriptListener"), ["FakeEvent"])`...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#javascriptListener"), "FakeEvent")`...
Result: false
Audit teardown...

-- Running test case: Audit.run.DOM.hasEventListeners.builtinListener
-- Running test case: Audit.run.DOM.hasEventListeners.Builtin.General
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#builtinListener"))`...
Result: false
Audit teardown...

-- Running test case: Audit.run.DOM.hasEventListeners.builtinListener.WithArgs
-- Running test case: Audit.run.DOM.hasEventListeners.Builtin.play
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#builtinListener"), "play")`...
Result: false
Audit teardown...

-- Running test case: Audit.run.DOM.hasEventListeners.Builtin.FakeEvent
Audit setup...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#builtinListener"), ["FakeEvent"])`...
Audit run `WebInspectorAudit.DOM.hasEventListeners(document.querySelector("#builtinListener"), "FakeEvent")`...
Result: false
Audit teardown...

-- Running test case: Audit.run.DOM.simulateUserInteraction.UserGesture
Audit setup...
Audit run `(() => { let result = undefined; WebInspectorAudit.DOM.simulateUserInteraction(() => { result = internals.isProcessingUserGesture(); }); return result; })()`...
Result: true
Audit teardown...

-- Running test case: Audit.run.DOM.simulateUserInteraction.TransientActivation
Audit setup...
Audit run `(() => { let result = undefined; WebInspectorAudit.DOM.simulateUserInteraction(() => { result = internals.hasTransientActivation(); }); return result; })()`...
Result: true
Audit teardown...

-- Running test case: Audit.run.DOM.InvalidCopiedFunctionCall
Audit setup...
Copying WebInspectorAudit to window...
Audit teardown...
Testing copied hasEventListeners...
PASS: Should produce an exception.
Error: NotAllowedError: Cannot be called outside of a Web Inspector Audit
Testing copied simulateUserInteraction...
PASS: Should produce an exception.
Error: NotAllowedError: Cannot be called outside of a Web Inspector Audit

@@ -8,26 +8,41 @@
{
let suite = InspectorTest.Audit.createSuite("Audit.run.DOM");

function evaluateStringForTest(func, target, args) {
return `WebInspectorAudit.DOM.${func}(document.querySelector("#${target}")${args ? ", " + JSON.stringify(args) : ""})`;
function evaluateStringForTest(func, args, prefix = "") {
switch (func) {
case "hasEventListeners":
return `${prefix}WebInspectorAudit.DOM.${func}(${args.join(", ")})`;

case "simulateUserInteraction":
return `(() => { let result = undefined; ${prefix}WebInspectorAudit.DOM.${func}(${args.join(", ")}); return result; })()`;
}

return "'FAIL'";
}

const tests = [
{ func: "hasEventListeners", target: "noListeners" },
{ func: "hasEventListeners", target: "noListeners", args: ["FakeEvent"] },
{ func: "hasEventListeners", target: "attributeListener" },
{ func: "hasEventListeners", target: "attributeListener", args: ["FakeEvent"] },
{ func: "hasEventListeners", target: "javascriptListener" },
{ func: "hasEventListeners", target: "javascriptListener", args: ["FakeEvent"] },
{ func: "hasEventListeners", target: "builtinListener" },
{ func: "hasEventListeners", target: "builtinListener", args: ["FakeEvent"] },
{ func: "hasEventListeners", name: "None.General", args: [`document.querySelector("#noListeners")`] },
{ func: "hasEventListeners", name: "None.click", args: [`document.querySelector("#noListeners")`, `"click"`] },
{ func: "hasEventListeners", name: "None.FakeEvent", args: [`document.querySelector("#noListeners")`, `"FakeEvent"`] },
{ func: "hasEventListeners", name: "Attribute.General", args: [`document.querySelector("#attributeListener")`] },
{ func: "hasEventListeners", name: "Attribute.click", args: [`document.querySelector("#attributeListener")`, `"click"`] },
{ func: "hasEventListeners", name: "Attribute.FakeEvent", args: [`document.querySelector("#attributeListener")`, `"FakeEvent"`] },
{ func: "hasEventListeners", name: "JavaScript.General", args: [`document.querySelector("#javascriptListener")`] },
{ func: "hasEventListeners", name: "JavaScript.click", args: [`document.querySelector("#javascriptListener")`, `"click"`] },
{ func: "hasEventListeners", name: "JavaScript.FakeEvent", args: [`document.querySelector("#javascriptListener")`, `"FakeEvent"`] },
{ func: "hasEventListeners", name: "Builtin.General", args: [`document.querySelector("#builtinListener")`] },
{ func: "hasEventListeners", name: "Builtin.play", args: [`document.querySelector("#builtinListener")`, `"play"`] },
{ func: "hasEventListeners", name: "Builtin.FakeEvent", args: [`document.querySelector("#builtinListener")`, `"FakeEvent"`] },

{ func: "simulateUserInteraction", name: "UserGesture", args: [`() => { result = internals.isProcessingUserGesture(); }`] },
{ func: "simulateUserInteraction", name: "TransientActivation", args: [`() => { result = internals.hasTransientActivation(); }`] },
];

for (let {func, target, args} of tests) {
for (let {func, name, args} of tests) {
suite.addTestCase({
name: "Audit.run.DOM." + func + "." + target + (args ? ".WithArgs" : ""),
name: "Audit.run.DOM." + func + (name ? "." + name : ""),
async test() {
let functionString = evaluateStringForTest(func, target, args);
let functionString = evaluateStringForTest(func, args);

await InspectorTest.Audit.setupAudit();

@@ -58,10 +73,10 @@
InspectorTest.assert(!wasThrown, "Should not throw an exception.");
await InspectorTest.Audit.teardownAudit();

for (let {func, target, args} of functions.values()) {
for (let {func, args} of functions.values()) {
InspectorTest.log(`Testing copied ${func}...`);
await InspectorTest.expectException(async function() {
await InspectorTest.evaluateInPage("window.Copied" + evaluateStringForTest(func, target, args));
await InspectorTest.evaluateInPage(evaluateStringForTest(func, args, "window.Copied"));
});
}
},
@@ -28,7 +28,7 @@ WARN: Audit Warning: "validWithInvalidOptionals test name" is too new to run in
{
"type": "test-case",
"name": "validWithInvalidOptionals test name",
"supports": 4,
"supports": "<version + 1>",
"test": "validWithInvalidOptionals test function"
}

@@ -37,7 +37,7 @@ WARN: Audit Warning: "validWithInvalidOptionals test name" is too new to run in
"type": "test-case",
"name": "validWithValidOptionals test name",
"description": "validWithValidOptionals test description",
"supports": 2,
"supports": "<version + -1>",
"setup": "validWithValidOptionals test setup",
"test": "validWithValidOptionals test function"
}
@@ -12,7 +12,14 @@
name,
async test() {
let object = await WI.AuditTestCase.fromPayload(payload);
InspectorTest.log(object ? JSON.stringify(object, null, 2) : object);

function replacer(key, value) {
if (key === "supports")
return `<version + ${value - WI.AuditTestBase.Version}>`;

return value;
}
InspectorTest.log(object ? JSON.stringify(object, replacer, 2) : object);
},
});
}
@@ -43,12 +43,12 @@ WARN: Audit Warning: "validWithInvalidOptionals group name" is too new to run in
{
"type": "test-group",
"name": "validWithInvalidOptionals group name",
"supports": 4,
"supports": "<version + 1>",
"tests": [
{
"type": "test-case",
"name": "validWithInvalidOptionals test name",
"supports": 5,
"supports": "<version + 2>",
"test": "validWithInvalidOptionals test function"
}
]
@@ -59,14 +59,14 @@ WARN: Audit Warning: "validWithInvalidOptionals group name" is too new to run in
"type": "test-group",
"name": "validWithValidOptionals group name",
"description": "validWithValidOptionals group description",
"supports": 2,
"supports": "<version + -1>",
"setup": "validWithValidOptionals group setup",
"tests": [
{
"type": "test-case",
"name": "validWithValidOptionals test name",
"description": "validWithValidOptionals test description",
"supports": 1,
"supports": "<version + -2>",
"setup": "validWithValidOptionals test setup",
"test": "validWithValidOptionals test function"
}
@@ -78,21 +78,21 @@ WARN: Audit Warning: "validWithInvalidOptionals group name" is too new to run in
"type": "test-group",
"name": "validNested group name",
"description": "validNested group description",
"supports": 2,
"supports": "<version + -1>",
"setup": "validNested group setup",
"tests": [
{
"type": "test-group",
"name": "validNested nested group name",
"description": "validNested nested group description",
"supports": 1,
"supports": "<version + -2>",
"setup": "validNested nested group setup",
"tests": [
{
"type": "test-case",
"name": "validNested nested test name",
"description": "validNested nested test description",
"supports": 0,
"supports": "<version + -3>",
"setup": "validNested nested test setup",
"test": "validNested nested test function"
}
@@ -102,7 +102,7 @@ WARN: Audit Warning: "validWithInvalidOptionals group name" is too new to run in
"type": "test-case",
"name": "validNested test name",
"description": "validNested test description",
"supports": -1,
"supports": "<version + -4>",
"setup": "validNested test setup",
"test": "validNested test function"
}
@@ -12,7 +12,14 @@
name,
async test() {
let object = await WI.AuditTestGroup.fromPayload(payload);
InspectorTest.log(object ? JSON.stringify(object, null, 2) : object);

function replacer(key, value) {
if (key === "supports")
return `<version + ${value - WI.AuditTestBase.Version}>`;

return value;
}
InspectorTest.log(object ? JSON.stringify(object, replacer, 2) : object);
},
});
}
@@ -1,7 +1,7 @@
{
"domain": "Audit",
"description": "",
"version": 3,
"version": 4,
"debuggableTypes": ["itml", "javascript", "page", "service-worker", "web-page"],
"targetTypes": ["itml", "javascript", "page", "service-worker", "worker"],
"commands": [
@@ -1510,6 +1510,7 @@ inspector/InstrumentingAgents.cpp
inspector/NetworkResourcesData.cpp
inspector/PageDebugger.cpp
inspector/TimelineRecordFactory.cpp
inspector/UserGestureEmulationScope.cpp
inspector/WebInjectedScriptHost.cpp
inspector/WebInjectedScriptManager.cpp
inspector/WorkerInspectorController.cpp
@@ -1540,7 +1541,6 @@ inspector/agents/page/PageDebuggerAgent.cpp
inspector/agents/page/PageHeapAgent.cpp
inspector/agents/page/PageNetworkAgent.cpp
inspector/agents/page/PageRuntimeAgent.cpp
inspector/agents/page/UserGestureEmulationScope.cpp
inspector/agents/worker/ServiceWorkerAgent.cpp
inspector/agents/worker/WorkerAuditAgent.cpp
inspector/agents/worker/WorkerConsoleAgent.cpp
@@ -20559,6 +20559,8 @@
91B952221F58A58000931DC2 /* RecordingSwizzleType.h */,
7553CFE7108F473F00EA281E /* TimelineRecordFactory.cpp */,
7553CFE6108F473F00EA281E /* TimelineRecordFactory.h */,
F32BDCD52363AAC90073B6AE /* UserGestureEmulationScope.cpp */,
F32BDCD72363AACA0073B6AE /* UserGestureEmulationScope.h */,
A5840E22187B8AC200843B10 /* WebInjectedScriptHost.cpp */,
A5840E23187B8AC200843B10 /* WebInjectedScriptHost.h */,
A584FE2D1864CB8400843B10 /* WebInjectedScriptManager.cpp */,
@@ -27073,8 +27075,6 @@
A5CB05211FB51F1700089B97 /* PageNetworkAgent.h */,
A5B81CC11FAA44B70037D1E6 /* PageRuntimeAgent.cpp */,
A5B81CBF1FAA44B70037D1E6 /* PageRuntimeAgent.h */,
F32BDCD52363AAC90073B6AE /* UserGestureEmulationScope.cpp */,
F32BDCD72363AACA0073B6AE /* UserGestureEmulationScope.h */,
);
path = page;
sourceTree = "<group>";
@@ -27,7 +27,10 @@
#include "config.h"
#include "InspectorAuditDOMObject.h"

#include "Document.h"
#include "Node.h"
#include "UserGestureEmulationScope.h"
#include "VoidCallback.h"
#include <wtf/text/AtomString.h>
#include <wtf/text/WTFString.h>

@@ -39,7 +42,7 @@ using namespace Inspector;
if (!m_auditAgent.hasActiveAudit()) \
return Exception { NotAllowedError, "Cannot be called outside of a Web Inspector Audit"_s };

InspectorAuditDOMObject::InspectorAuditDOMObject(InspectorAuditAgent& auditAgent)
InspectorAuditDOMObject::InspectorAuditDOMObject(PageAuditAgent& auditAgent)
: m_auditAgent(auditAgent)
{
}
@@ -66,4 +69,14 @@ ExceptionOr<bool> InspectorAuditDOMObject::hasEventListeners(Node& node, const S
return false;
}

ExceptionOr<void> InspectorAuditDOMObject::simulateUserInteraction(Document& document, Ref<VoidCallback>&& callback)
{
ERROR_IF_NO_ACTIVE_AUDIT();

UserGestureEmulationScope userGestureScope(m_auditAgent.inspectedPage(), true, &document);
callback->handleEvent();

return { };
}

} // namespace WebCore

0 comments on commit 7b44a87

Please sign in to comment.