Web Inspector: support emulateUserGesture parameter in Runtime.callFunctionOn
https://bugs.webkit.org/show_bug.cgi?id=200262

Reviewed by Devin Rousso.

Source/JavaScriptCore:

* inspector/agents/InspectorRuntimeAgent.cpp:
(Inspector::InspectorRuntimeAgent::callFunctionOn):
* inspector/agents/InspectorRuntimeAgent.h:
* inspector/protocol/Runtime.json:

Source/WebCore:

Support emulateUserGesture in Runtime.callFunctionOn

Tests: inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting.html
       inspector/runtime/callFunctionOn-emulateUserGesture.html

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* inspector/agents/page/PageDebuggerAgent.cpp:
(WebCore::PageDebuggerAgent::evaluateOnCallFrame):
* inspector/agents/page/PageRuntimeAgent.cpp:
(WebCore::asBool):
(WebCore::PageRuntimeAgent::evaluate):
(WebCore::PageRuntimeAgent::callFunctionOn):
* inspector/agents/page/PageRuntimeAgent.h:
* inspector/agents/page/UserGestureEmulationScope.cpp: Added.
(WebCore::UserGestureEmulationScope::UserGestureEmulationScope): Extracted logic
of overriding user gesture into the scope object to share it between evaluate and
callFunctionOn as well as PageDebuggerAgent::evaluateOnCallFrame.
(WebCore::UserGestureEmulationScope::~UserGestureEmulationScope):
* inspector/agents/page/UserGestureEmulationScope.h: Added.

LayoutTests:

Supported emulateUserGesture in Runtime.callFunctionOn protocol command.

* TestExpectations:
* inspector/runtime/callFunctionOn-emulateUserGesture-expected.txt: Added.
* inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting-expected.txt: Added.
* inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting.html: Added.
* inspector/runtime/callFunctionOn-emulateUserGesture.html: Added.
* platform/wk2/TestExpectations:
Only enable the new test on WK2, as the user interaction state is only supported by WK2.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@251620 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 9b28102..9eb69e9 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,20 @@
+2019-10-25  Yury Semikhatsky  <yurys@chromium.org>
+
+        Web Inspector: support emulateUserGesture parameter in Runtime.callFunctionOn
+        https://bugs.webkit.org/show_bug.cgi?id=200262
+
+        Reviewed by Devin Rousso.
+
+        Supported emulateUserGesture in Runtime.callFunctionOn protocol command.
+
+        * TestExpectations:
+        * inspector/runtime/callFunctionOn-emulateUserGesture-expected.txt: Added.
+        * inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting-expected.txt: Added.
+        * inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting.html: Added.
+        * inspector/runtime/callFunctionOn-emulateUserGesture.html: Added.
+        * platform/wk2/TestExpectations:
+        Only enable the new test on WK2, as the user interaction state is only supported by WK2.
+
 2019-10-25  Megan Gardner  <megan_gardner@apple.com>
 
         Fix autoscroll test specifically for iPad
diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations
index a5fc06b..26bd957 100644
--- a/LayoutTests/TestExpectations
+++ b/LayoutTests/TestExpectations
@@ -914,6 +914,7 @@
 # User interaction is a WK2 concept.
 inspector/debugger/evaluateOnCallFrame-emulateUserGesture-userIsInteracting.html [ Skip ]
 inspector/runtime/evaluate-emulateUserGesture-userIsInteracting.html [ Skip ]
+inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting.html [ Skip ]
 
 # Target domain is only present in WK2.
 http/tests/inspector/target [ Skip ]
diff --git a/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture-expected.txt b/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture-expected.txt
new file mode 100644
index 0000000..859d028
--- /dev/null
+++ b/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture-expected.txt
@@ -0,0 +1,9 @@
+Tests for Runtime.callFunctionOn emulateUserGesture option.
+
+
+== Running test suite: Runtime.callFunctionOn.emulateUserGesture
+-- Running test case: CallFunctionOnWithoutEmulatingUserGesture
+Not in User Gesture
+-- Running test case: CallFunctionOnWithEmulatingUserGesture
+In User Gesture
+
diff --git a/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting-expected.txt b/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting-expected.txt
new file mode 100644
index 0000000..f19269b
--- /dev/null
+++ b/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting-expected.txt
@@ -0,0 +1,9 @@
+Tests for Runtime.callFunctionOn emulateUserGesture option.
+
+
+== Running test suite: Runtime.callFunctionOnUserGestureEmulation.userIsInteracting
+-- Running test case: CallFunctionOnWithoutEmulatingUserGesture.userIsInteracting
+User is NOT Interacting
+-- Running test case: CallFunctionOnWithEmulatingUserGesture.userIsInteracting
+User is Interacting
+
diff --git a/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting.html b/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting.html
new file mode 100644
index 0000000..b274df1
--- /dev/null
+++ b/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+
+function load() {
+    document.getElementById("test").addEventListener("click", (event) => {
+        if (!window.internals)
+            return;
+
+        TestPage.addResult(window.internals.userIsInteracting() ? "User is Interacting" : "User is NOT Interacting");
+    });
+
+    runTest();
+}
+
+function test()
+{
+    let suite = InspectorTest.createAsyncSuite("Runtime.callFunctionOnUserGestureEmulation.userIsInteracting");
+
+    suite.addTestCase({
+        name: "CallFunctionOnWithoutEmulatingUserGesture.userIsInteracting",
+        description: "Check that callFunctionOn is not considered a user interaction when emulateUserGesture is false.",
+        async test() {
+            let testElement = await RuntimeAgent.evaluate.invoke({expression: `document.getElementById("test")`});
+            await RuntimeAgent.callFunctionOn.invoke({functionDeclaration: `function() { this.click(); }`, emulateUserGesture: false, objectId: testElement.result.objectId});
+        },
+    });
+
+    suite.addTestCase({
+        name: "CallFunctionOnWithEmulatingUserGesture.userIsInteracting",
+        description: "Check that callFunctionOn is considered a user interaction when emulateUserGesture is true.",
+        async test() {
+            let testElement = await RuntimeAgent.evaluate.invoke({expression: `document.getElementById("test")`});
+            await RuntimeAgent.callFunctionOn.invoke({functionDeclaration: `function() { this.click(); }`, emulateUserGesture: true, objectId: testElement.result.objectId});
+        },
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="load()">
+    <p>Tests for Runtime.callFunctionOn emulateUserGesture option.</p>
+    <a id="test"></a>
+</body>
+</html>
diff --git a/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture.html b/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture.html
new file mode 100644
index 0000000..ab0995a
--- /dev/null
+++ b/LayoutTests/inspector/runtime/callFunctionOn-emulateUserGesture.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+function test()
+{
+    let suite = InspectorTest.createAsyncSuite("Runtime.callFunctionOn.emulateUserGesture");
+
+    suite.addTestCase({
+        name: "CallFunctionOnWithoutEmulatingUserGesture",
+        description: "Call function with the emulateUserGesture option set to false.",
+        async test() {
+            let testElement = await RuntimeAgent.evaluate.invoke({expression: `document.getElementById("foo")`});
+            let response = await RuntimeAgent.callFunctionOn.invoke({functionDeclaration: `function() { this.click(); }`, emulateUserGesture: false, objectId: testElement.result.objectId});
+            InspectorTest.assert(!response.error, "Should not be a protocol error.");
+        }
+    });
+
+    suite.addTestCase({
+        name: "CallFunctionOnWithEmulatingUserGesture",
+        description: "Call function with the emulateUserGesture option set to true.",
+        async test() {
+            let testElement = await RuntimeAgent.evaluate.invoke({expression: `document.getElementById("foo")`});
+            let response = await RuntimeAgent.callFunctionOn.invoke({functionDeclaration: `function() { this.click(); }`, emulateUserGesture: true, objectId: testElement.result.objectId});
+            InspectorTest.assert(!response.error, "Should not be a protocol error.");
+        }
+    });
+
+    suite.runTestCasesAndFinish();
+}
+
+function handleClick(event) {
+    if (window.internals)
+        TestPage.addResult(window.internals.isProcessingUserGesture() ? "In User Gesture" : "Not in User Gesture");
+}
+</script>
+</head>
+<body onload="runTest()">
+<a id="foo" onclick="handleClick(event)"></a>
+<p>Tests for Runtime.callFunctionOn emulateUserGesture option.</p>
+</body>
+</html>
diff --git a/LayoutTests/platform/gtk/TestExpectations b/LayoutTests/platform/gtk/TestExpectations
index bcd73f9..cddbd0f 100644
--- a/LayoutTests/platform/gtk/TestExpectations
+++ b/LayoutTests/platform/gtk/TestExpectations
@@ -2245,8 +2245,6 @@
 webkit.org/b/116958 http/tests/navigation/slowtimerredirect-basic.html [ Pass Slow ]
 
 webkit.org/b/149916 http/tests/inspector/dom/disconnect-dom-tree-after-main-frame-navigation.html [ Pass Slow ]
-webkit.org/b/149916 inspector/codemirror/prettyprinting-css.html [ Pass Slow ]
-webkit.org/b/149916 inspector/codemirror/prettyprinting-javascript.html [ Pass Slow ]
 webkit.org/b/149916 inspector/console/clearMessages.html [ Pass Slow ]
 webkit.org/b/149916 inspector/console/command-line-api.html [ Pass Slow ]
 webkit.org/b/149916 inspector/console/console-api.html [ Pass Timeout ]
diff --git a/LayoutTests/platform/wk2/TestExpectations b/LayoutTests/platform/wk2/TestExpectations
index 7d4fb66..ad79c26 100644
--- a/LayoutTests/platform/wk2/TestExpectations
+++ b/LayoutTests/platform/wk2/TestExpectations
@@ -763,6 +763,7 @@
 # User interaction is a WebKit2 concept.
 inspector/debugger/evaluateOnCallFrame-emulateUserGesture-userIsInteracting.html [ Pass ]
 inspector/runtime/evaluate-emulateUserGesture-userIsInteracting.html [ Pass ]
+inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting.html [ Pass ]
 
 # Target domain is only present in WebKit2.
 http/tests/inspector/target [ Pass ]
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index dea4d7f..1820161 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,15 @@
+2019-10-25  Yury Semikhatsky  <yurys@chromium.org>
+
+        Web Inspector: support emulateUserGesture parameter in Runtime.callFunctionOn
+        https://bugs.webkit.org/show_bug.cgi?id=200262
+
+        Reviewed by Devin Rousso.
+
+        * inspector/agents/InspectorRuntimeAgent.cpp:
+        (Inspector::InspectorRuntimeAgent::callFunctionOn):
+        * inspector/agents/InspectorRuntimeAgent.h:
+        * inspector/protocol/Runtime.json:
+
 2019-10-24  Mark Lam  <mark.lam@apple.com>
 
         Move JSC::Register inline methods into RegisterInlines.h.
diff --git a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp
index 935ee0d..9a710cc 100644
--- a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp
+++ b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.cpp
@@ -155,7 +155,7 @@
     });
 }
 
-void InspectorRuntimeAgent::callFunctionOn(ErrorString& errorString, const String& objectId, const String& expression, const JSON::Array* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown)
+void InspectorRuntimeAgent::callFunctionOn(ErrorString& errorString, const String& objectId, const String& expression, const JSON::Array* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, const bool* /* emulateUserGesture */, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown)
 {
     InjectedScript injectedScript = m_injectedScriptManager.injectedScriptForObjectId(objectId);
     if (injectedScript.hasNoValue()) {
diff --git a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h
index 7d45054..e3cb624 100644
--- a/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h
+++ b/Source/JavaScriptCore/inspector/agents/InspectorRuntimeAgent.h
@@ -63,7 +63,7 @@
     void parse(ErrorString&, const String& expression, Protocol::Runtime::SyntaxErrorType* result, Optional<String>& message, RefPtr<Protocol::Runtime::ErrorRange>&) final;
     void evaluate(ErrorString&, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, const bool* emulateUserGesture, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown, Optional<int>& savedResultIndex) override;
     void awaitPromise(const String& promiseObjectId, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, Ref<AwaitPromiseCallback>&&) final;
-    void callFunctionOn(ErrorString&, const String& objectId, const String& expression, const JSON::Array* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown) final;
+    void callFunctionOn(ErrorString&, const String& objectId, const String& expression, const JSON::Array* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, const bool* emulateUserGesture, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown) override;
     void releaseObject(ErrorString&, const ErrorString& objectId) final;
     void getPreview(ErrorString&, const String& objectId, RefPtr<Protocol::Runtime::ObjectPreview>&) final;
     void getProperties(ErrorString&, const String& objectId, const bool* ownProperties, const int* fetchStart, const int* fetchCount, const bool* generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& internalProperties) final;
diff --git a/Source/JavaScriptCore/inspector/protocol/Runtime.json b/Source/JavaScriptCore/inspector/protocol/Runtime.json
index c790c42..c17c15f 100644
--- a/Source/JavaScriptCore/inspector/protocol/Runtime.json
+++ b/Source/JavaScriptCore/inspector/protocol/Runtime.json
@@ -253,7 +253,8 @@
                 { "name": "arguments", "type": "array", "items": { "$ref": "CallArgument", "description": "Call argument." }, "optional": true, "description": "Call arguments. All call arguments must belong to the same JavaScript world as the target object." },
                 { "name": "doNotPauseOnExceptionsAndMuteConsole", "type": "boolean", "optional": true, "description": "Specifies whether function call should stop on exceptions and mute console. Overrides setPauseOnException state." },
                 { "name": "returnByValue", "type": "boolean", "optional": true, "description": "Whether the result is expected to be a JSON object which should be sent by value." },
-                { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." }
+                { "name": "generatePreview", "type": "boolean", "optional": true, "description": "Whether preview should be generated for the result." },
+                { "name": "emulateUserGesture", "type": "boolean", "optional": true, "description": "Whether the expression should be considered to be in a user gesture or not." }
             ],
             "returns": [
                 { "name": "result", "$ref": "RemoteObject", "description": "Call result." },
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 56838a2..f6d874d 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,31 @@
+2019-10-25  Yury Semikhatsky  <yurys@chromium.org>
+
+        Web Inspector: support emulateUserGesture parameter in Runtime.callFunctionOn
+        https://bugs.webkit.org/show_bug.cgi?id=200262
+
+        Reviewed by Devin Rousso.
+
+        Support emulateUserGesture in Runtime.callFunctionOn
+
+        Tests: inspector/runtime/callFunctionOn-emulateUserGesture-userIsInteracting.html
+               inspector/runtime/callFunctionOn-emulateUserGesture.html
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * inspector/agents/page/PageDebuggerAgent.cpp:
+        (WebCore::PageDebuggerAgent::evaluateOnCallFrame):
+        * inspector/agents/page/PageRuntimeAgent.cpp:
+        (WebCore::asBool):
+        (WebCore::PageRuntimeAgent::evaluate):
+        (WebCore::PageRuntimeAgent::callFunctionOn):
+        * inspector/agents/page/PageRuntimeAgent.h:
+        * inspector/agents/page/UserGestureEmulationScope.cpp: Added.
+        (WebCore::UserGestureEmulationScope::UserGestureEmulationScope): Extracted logic
+        of overriding user gesture into the scope object to share it between evaluate and
+        callFunctionOn as well as PageDebuggerAgent::evaluateOnCallFrame.
+        (WebCore::UserGestureEmulationScope::~UserGestureEmulationScope):
+        * inspector/agents/page/UserGestureEmulationScope.h: Added.
+
 2019-10-25  Zalan Bujtas  <zalan@apple.com>
 
         [LFC][IFC] Remove redundant LineLayout::LineInput c'tor
diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt
index 21d6e1e..d9fd6dd 100644
--- a/Source/WebCore/Sources.txt
+++ b/Source/WebCore/Sources.txt
@@ -1404,6 +1404,7 @@
 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
diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
index 76b93cb..a4c321c 100644
--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj
+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
@@ -4881,6 +4881,7 @@
 		EFCC6C8F20FE914400A2321B /* CanvasActivityRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = EFCC6C8D20FE914000A2321B /* CanvasActivityRecord.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		F12171F516A8CED2000053CA /* WebVTTElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F12171F316A8BC63000053CA /* WebVTTElement.cpp */; };
 		F12171F616A8CF0B000053CA /* WebVTTElement.h in Headers */ = {isa = PBXBuildFile; fileRef = F12171F416A8BC63000053CA /* WebVTTElement.h */; };
+		F32BDCD92363AACA0073B6AE /* UserGestureEmulationScope.h in Headers */ = {isa = PBXBuildFile; fileRef = F32BDCD72363AACA0073B6AE /* UserGestureEmulationScope.h */; };
 		F344C7141125B82C00F26EEE /* InspectorFrontendClient.h in Headers */ = {isa = PBXBuildFile; fileRef = F344C7121125B82C00F26EEE /* InspectorFrontendClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		F344C75311294D9D00F26EEE /* InspectorFrontendClientLocal.h in Headers */ = {isa = PBXBuildFile; fileRef = F344C75211294D9D00F26EEE /* InspectorFrontendClientLocal.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		F3ABFE0C130E9DA000E7F7D1 /* InstrumentingAgents.h in Headers */ = {isa = PBXBuildFile; fileRef = F3ABFE0B130E9DA000E7F7D1 /* InstrumentingAgents.h */; };
@@ -15240,6 +15241,8 @@
 		EFCC6C8D20FE914000A2321B /* CanvasActivityRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CanvasActivityRecord.h; sourceTree = "<group>"; };
 		F12171F316A8BC63000053CA /* WebVTTElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebVTTElement.cpp; sourceTree = "<group>"; };
 		F12171F416A8BC63000053CA /* WebVTTElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebVTTElement.h; sourceTree = "<group>"; };
+		F32BDCD52363AAC90073B6AE /* UserGestureEmulationScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserGestureEmulationScope.cpp; sourceTree = "<group>"; };
+		F32BDCD72363AACA0073B6AE /* UserGestureEmulationScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserGestureEmulationScope.h; sourceTree = "<group>"; };
 		F344C7121125B82C00F26EEE /* InspectorFrontendClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorFrontendClient.h; sourceTree = "<group>"; };
 		F344C75211294D9D00F26EEE /* InspectorFrontendClientLocal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorFrontendClientLocal.h; sourceTree = "<group>"; };
 		F344C75711294FF600F26EEE /* InspectorFrontendClientLocal.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorFrontendClientLocal.cpp; sourceTree = "<group>"; };
@@ -22779,6 +22782,8 @@
 				A5CB05211FB51F1700089B97 /* PageNetworkAgent.h */,
 				A5B81CC11FAA44B70037D1E6 /* PageRuntimeAgent.cpp */,
 				A5B81CBF1FAA44B70037D1E6 /* PageRuntimeAgent.h */,
+				F32BDCD52363AAC90073B6AE /* UserGestureEmulationScope.cpp */,
+				F32BDCD72363AACA0073B6AE /* UserGestureEmulationScope.h */,
 			);
 			path = page;
 			sourceTree = "<group>";
@@ -32465,6 +32470,7 @@
 				7C3F01C11C8E5ABA00ADD962 /* UserContentProvider.h in Headers */,
 				003F1FEA11E6AB43008258D9 /* UserContentTypes.h in Headers */,
 				BCACF3BD1072921A00C0C8A3 /* UserContentURLPattern.h in Headers */,
+				F32BDCD92363AACA0073B6AE /* UserGestureEmulationScope.h in Headers */,
 				2542F4DB1166C25A00E89A86 /* UserGestureIndicator.h in Headers */,
 				9920398318B95BC600B39AF9 /* UserInputBridge.h in Headers */,
 				71C916081D1483A300ACA47D /* UserInterfaceLayoutDirection.h in Headers */,
diff --git a/Source/WebCore/inspector/agents/page/PageDebuggerAgent.cpp b/Source/WebCore/inspector/agents/page/PageDebuggerAgent.cpp
index dad9945..a0d8f1f 100644
--- a/Source/WebCore/inspector/agents/page/PageDebuggerAgent.cpp
+++ b/Source/WebCore/inspector/agents/page/PageDebuggerAgent.cpp
@@ -33,8 +33,6 @@
 #include "PageDebuggerAgent.h"
 
 #include "CachedResource.h"
-#include "Chrome.h"
-#include "ChromeClient.h"
 #include "Document.h"
 #include "Frame.h"
 #include "InspectorPageAgent.h"
@@ -44,7 +42,7 @@
 #include "PageScriptDebugServer.h"
 #include "ScriptExecutionContext.h"
 #include "ScriptState.h"
-#include "UserGestureIndicator.h"
+#include "UserGestureEmulationScope.h"
 #include <JavaScriptCore/InjectedScript.h>
 #include <JavaScriptCore/InjectedScriptManager.h>
 #include <JavaScriptCore/ScriptCallStack.h>
@@ -71,24 +69,10 @@
 
 void PageDebuggerAgent::evaluateOnCallFrame(ErrorString& errorString, const String& callFrameId, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, const bool* emulateUserGesture, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown, Optional<int>& savedResultIndex)
 {
-    auto& pageChromeClient = m_inspectedPage.chrome().client();
-
     auto shouldEmulateUserGesture = emulateUserGesture && *emulateUserGesture;
-
-    Optional<ProcessingUserGestureState> userGestureState = shouldEmulateUserGesture ? Optional<ProcessingUserGestureState>(ProcessingUserGesture) : WTF::nullopt;
-    UserGestureIndicator gestureIndicator(userGestureState);
-
-    bool userWasInteracting = false;
-    if (shouldEmulateUserGesture) {
-        userWasInteracting = pageChromeClient.userIsInteracting();
-        if (!userWasInteracting)
-            pageChromeClient.setUserIsInteracting(true);
-    }
+    UserGestureEmulationScope userGestureScope(m_inspectedPage, shouldEmulateUserGesture);
 
     WebDebuggerAgent::evaluateOnCallFrame(errorString, callFrameId, expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, saveResult, emulateUserGesture, result, wasThrown, savedResultIndex);
-
-    if (shouldEmulateUserGesture && !userWasInteracting && pageChromeClient.userIsInteracting())
-        pageChromeClient.setUserIsInteracting(false);
 }
 
 void PageDebuggerAgent::enable()
diff --git a/Source/WebCore/inspector/agents/page/PageRuntimeAgent.cpp b/Source/WebCore/inspector/agents/page/PageRuntimeAgent.cpp
index f5dd183..8c4a104 100644
--- a/Source/WebCore/inspector/agents/page/PageRuntimeAgent.cpp
+++ b/Source/WebCore/inspector/agents/page/PageRuntimeAgent.cpp
@@ -32,8 +32,6 @@
 #include "config.h"
 #include "PageRuntimeAgent.h"
 
-#include "Chrome.h"
-#include "ChromeClient.h"
 #include "Document.h"
 #include "Frame.h"
 #include "InspectorPageAgent.h"
@@ -44,17 +42,21 @@
 #include "ScriptController.h"
 #include "ScriptState.h"
 #include "SecurityOrigin.h"
-#include "UserGestureIndicator.h"
+#include "UserGestureEmulationScope.h"
 #include <JavaScriptCore/InjectedScript.h>
 #include <JavaScriptCore/InjectedScriptManager.h>
 
 using Inspector::Protocol::Runtime::ExecutionContextDescription;
 
-
 namespace WebCore {
 
 using namespace Inspector;
 
+static bool asBool(const bool* b)
+{
+    return b && *b;
+}
+
 PageRuntimeAgent::PageRuntimeAgent(PageAgentContext& context)
     : InspectorRuntimeAgent(context)
     , m_frontendDispatcher(makeUnique<Inspector::RuntimeFrontendDispatcher>(context.frontendRouter))
@@ -166,24 +168,14 @@
 
 void PageRuntimeAgent::evaluate(ErrorString& errorString, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, const bool* emulateUserGesture, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown, Optional<int>& savedResultIndex)
 {
-    auto& pageChromeClient = m_inspectedPage.chrome().client();
-
-    auto shouldEmulateUserGesture = emulateUserGesture && *emulateUserGesture;
-
-    Optional<ProcessingUserGestureState> userGestureState = shouldEmulateUserGesture ? Optional<ProcessingUserGestureState>(ProcessingUserGesture) : WTF::nullopt;
-    UserGestureIndicator gestureIndicator(userGestureState);
-
-    bool userWasInteracting = false;
-    if (shouldEmulateUserGesture) {
-        userWasInteracting = pageChromeClient.userIsInteracting();
-        if (!userWasInteracting)
-            pageChromeClient.setUserIsInteracting(true);
-    }
-
+    UserGestureEmulationScope userGestureScope(m_inspectedPage, asBool(emulateUserGesture));
     InspectorRuntimeAgent::evaluate(errorString, expression, objectGroup, includeCommandLineAPI, doNotPauseOnExceptionsAndMuteConsole, executionContextId, returnByValue, generatePreview, saveResult, emulateUserGesture, result, wasThrown, savedResultIndex);
+}
 
-    if (shouldEmulateUserGesture && !userWasInteracting && pageChromeClient.userIsInteracting())
-        pageChromeClient.setUserIsInteracting(false);
+void PageRuntimeAgent::callFunctionOn(ErrorString& errorString, const String& objectId, const String& expression, const JSON::Array* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, const bool* emulateUserGesture, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown)
+{
+    UserGestureEmulationScope userGestureScope(m_inspectedPage, asBool(emulateUserGesture));
+    InspectorRuntimeAgent::callFunctionOn(errorString, objectId, expression, optionalArguments, doNotPauseOnExceptionsAndMuteConsole, returnByValue, generatePreview, emulateUserGesture, result, wasThrown);
 }
 
 } // namespace WebCore
diff --git a/Source/WebCore/inspector/agents/page/PageRuntimeAgent.h b/Source/WebCore/inspector/agents/page/PageRuntimeAgent.h
index ee2efe3..f388280 100644
--- a/Source/WebCore/inspector/agents/page/PageRuntimeAgent.h
+++ b/Source/WebCore/inspector/agents/page/PageRuntimeAgent.h
@@ -57,6 +57,7 @@
     void enable(ErrorString&) override;
     void disable(ErrorString&) override;
     void evaluate(ErrorString&, const String& expression, const String* objectGroup, const bool* includeCommandLineAPI, const bool* doNotPauseOnExceptionsAndMuteConsole, const int* executionContextId, const bool* returnByValue, const bool* generatePreview, const bool* saveResult, const bool* emulateUserGesture, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown, Optional<int>& savedResultIndex) override;
+    void callFunctionOn(ErrorString&, const String& objectId, const String& expression, const JSON::Array* optionalArguments, const bool* doNotPauseOnExceptionsAndMuteConsole, const bool* returnByValue, const bool* generatePreview, const bool* emulateUserGesture, RefPtr<Inspector::Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown) override;
 
     // InspectorInstrumentation
     void didClearWindowObjectInWorld(Frame&);
diff --git a/Source/WebCore/inspector/agents/page/UserGestureEmulationScope.cpp b/Source/WebCore/inspector/agents/page/UserGestureEmulationScope.cpp
new file mode 100644
index 0000000..633bce6
--- /dev/null
+++ b/Source/WebCore/inspector/agents/page/UserGestureEmulationScope.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 Microsoft Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "UserGestureEmulationScope.h"
+
+#include "Chrome.h"
+#include "ChromeClient.h"
+#include "Page.h"
+#include "UserGestureIndicator.h"
+#include <wtf/Optional.h>
+
+namespace WebCore {
+
+UserGestureEmulationScope::UserGestureEmulationScope(Page& inspectedPage, bool emulateUserGesture)
+    : m_pageChromeClient(inspectedPage.chrome().client())
+    , m_gestureIndicator(emulateUserGesture ? Optional<ProcessingUserGestureState>(ProcessingUserGesture) : WTF::nullopt)
+    , m_emulateUserGesture(emulateUserGesture)
+    , m_userWasInteracting(false)
+{
+    if (m_emulateUserGesture) {
+        m_userWasInteracting = m_pageChromeClient.userIsInteracting();
+        if (!m_userWasInteracting)
+            m_pageChromeClient.setUserIsInteracting(true);
+    }
+}
+
+UserGestureEmulationScope::~UserGestureEmulationScope()
+{
+    if (m_emulateUserGesture && !m_userWasInteracting && m_pageChromeClient.userIsInteracting())
+        m_pageChromeClient.setUserIsInteracting(false);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/inspector/agents/page/UserGestureEmulationScope.h b/Source/WebCore/inspector/agents/page/UserGestureEmulationScope.h
new file mode 100644
index 0000000..b94ed78
--- /dev/null
+++ b/Source/WebCore/inspector/agents/page/UserGestureEmulationScope.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 Microsoft Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include "UserGestureIndicator.h"
+
+namespace WebCore {
+
+class ChromeClient;
+class Page;
+
+class UserGestureEmulationScope {
+    WTF_MAKE_NONCOPYABLE(UserGestureEmulationScope);
+public:
+    UserGestureEmulationScope(Page& inspectedPage, bool emulateUserGesture);
+    ~UserGestureEmulationScope();
+
+private:
+    ChromeClient& m_pageChromeClient;
+    UserGestureIndicator m_gestureIndicator;
+    bool m_emulateUserGesture;
+    bool m_userWasInteracting;
+};
+
+} // namespace WebCore