Web Inspector: Provide $event in the console when paused on an event listener
[WebKit-https.git] / Source / JavaScriptCore / inspector / InjectedScript.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  * Copyright (C) 2012 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include "config.h"
33 #include "InjectedScript.h"
34
35 #include "JSCInlines.h"
36 #include "JSLock.h"
37 #include "ScriptFunctionCall.h"
38 #include "ScriptObject.h"
39 #include <wtf/JSONValues.h>
40 #include <wtf/Vector.h>
41 #include <wtf/text/WTFString.h>
42
43 namespace Inspector {
44
45 InjectedScript::InjectedScript()
46     : InjectedScriptBase("InjectedScript"_s)
47 {
48 }
49
50 InjectedScript::InjectedScript(Deprecated::ScriptObject injectedScriptObject, InspectorEnvironment* environment)
51     : InjectedScriptBase("InjectedScript"_s, injectedScriptObject, environment)
52 {
53 }
54
55 InjectedScript::~InjectedScript()
56 {
57 }
58
59 void InjectedScript::execute(ErrorString& errorString, const String& functionString, ExecuteOptions&& options, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown, Optional<int>& savedResultIndex)
60 {
61     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "execute"_s, inspectorEnvironment()->functionCallHandler());
62     function.appendArgument(functionString);
63     function.appendArgument(options.objectGroup);
64     function.appendArgument(options.includeCommandLineAPI);
65     function.appendArgument(options.returnByValue);
66     function.appendArgument(options.generatePreview);
67     function.appendArgument(options.saveResult);
68     function.appendArgument(arrayFromVector(WTFMove(options.args)));
69     makeEvalCall(errorString, function, result, wasThrown, savedResultIndex);
70 }
71
72 void InjectedScript::evaluate(ErrorString& errorString, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown, Optional<int>& savedResultIndex)
73 {
74     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "evaluate"_s, inspectorEnvironment()->functionCallHandler());
75     function.appendArgument(expression);
76     function.appendArgument(objectGroup);
77     function.appendArgument(includeCommandLineAPI);
78     function.appendArgument(returnByValue);
79     function.appendArgument(generatePreview);
80     function.appendArgument(saveResult);
81     makeEvalCall(errorString, function, result, wasThrown, savedResultIndex);
82 }
83
84 void InjectedScript::awaitPromise(const String& promiseObjectId, bool returnByValue, bool generatePreview, bool saveResult, AsyncCallCallback&& callback)
85 {
86     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "awaitPromise"_s, inspectorEnvironment()->functionCallHandler());
87     function.appendArgument(promiseObjectId);
88     function.appendArgument(returnByValue);
89     function.appendArgument(generatePreview);
90     function.appendArgument(saveResult);
91     makeAsyncCall(function, WTFMove(callback));
92 }
93
94 void InjectedScript::callFunctionOn(ErrorString& errorString, const String& objectId, const String& expression, const String& arguments, bool returnByValue, bool generatePreview, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown)
95 {
96     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "callFunctionOn"_s, inspectorEnvironment()->functionCallHandler());
97     function.appendArgument(objectId);
98     function.appendArgument(expression);
99     function.appendArgument(arguments);
100     function.appendArgument(returnByValue);
101     function.appendArgument(generatePreview);
102
103     Optional<int> savedResultIndex;
104     makeEvalCall(errorString, function, result, wasThrown, savedResultIndex);
105     ASSERT(!savedResultIndex);
106 }
107
108 void InjectedScript::evaluateOnCallFrame(ErrorString& errorString, JSC::JSValue callFrames, const String& callFrameId, const String& expression, const String& objectGroup, bool includeCommandLineAPI, bool returnByValue, bool generatePreview, bool saveResult, RefPtr<Protocol::Runtime::RemoteObject>& result, Optional<bool>& wasThrown, Optional<int>& savedResultIndex)
109 {
110     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "evaluateOnCallFrame"_s, inspectorEnvironment()->functionCallHandler());
111     function.appendArgument(callFrames);
112     function.appendArgument(callFrameId);
113     function.appendArgument(expression);
114     function.appendArgument(objectGroup);
115     function.appendArgument(includeCommandLineAPI);
116     function.appendArgument(returnByValue);
117     function.appendArgument(generatePreview);
118     function.appendArgument(saveResult);
119     makeEvalCall(errorString, function, result, wasThrown, savedResultIndex);
120 }
121
122 void InjectedScript::getFunctionDetails(ErrorString& errorString, const String& functionId, RefPtr<Protocol::Debugger::FunctionDetails>& result)
123 {
124     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getFunctionDetails"_s, inspectorEnvironment()->functionCallHandler());
125     function.appendArgument(functionId);
126
127     RefPtr<JSON::Value> resultValue = makeCall(function);
128     if (!resultValue || resultValue->type() != JSON::Value::Type::Object) {
129         if (!resultValue->asString(errorString))
130             errorString = "Internal error"_s;
131         return;
132     }
133
134     result = BindingTraits<Protocol::Debugger::FunctionDetails>::runtimeCast(WTFMove(resultValue));
135 }
136
137 void InjectedScript::functionDetails(ErrorString& errorString, JSC::JSValue value, RefPtr<Protocol::Debugger::FunctionDetails>& result)
138 {
139     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "functionDetails"_s, inspectorEnvironment()->functionCallHandler());
140     function.appendArgument(value);
141
142     RefPtr<JSON::Value> resultValue = makeCall(function);
143     if (!resultValue || resultValue->type() != JSON::Value::Type::Object) {
144         if (!resultValue->asString(errorString))
145             errorString = "Internal error"_s;
146         return;
147     }
148
149     result = BindingTraits<Protocol::Debugger::FunctionDetails>::runtimeCast(WTFMove(resultValue));
150 }
151
152 void InjectedScript::getPreview(ErrorString& errorString, const String& objectId, RefPtr<Protocol::Runtime::ObjectPreview>& result)
153 {
154     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getPreview"_s, inspectorEnvironment()->functionCallHandler());
155     function.appendArgument(objectId);
156
157     RefPtr<JSON::Value> resultValue = makeCall(function);
158     if (!resultValue || resultValue->type() != JSON::Value::Type::Object) {
159         if (!resultValue->asString(errorString))
160             errorString = "Internal error"_s;
161         return;
162     }
163
164     result = BindingTraits<Protocol::Runtime::ObjectPreview>::runtimeCast(WTFMove(resultValue));
165 }
166
167 void InjectedScript::getProperties(ErrorString& errorString, const String& objectId, bool ownProperties, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties)
168 {
169     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getProperties"_s, inspectorEnvironment()->functionCallHandler());
170     function.appendArgument(objectId);
171     function.appendArgument(ownProperties);
172     function.appendArgument(generatePreview);
173
174     RefPtr<JSON::Value> result = makeCall(function);
175     if (!result || result->type() != JSON::Value::Type::Array) {
176         errorString = "Internal error"_s;
177         return;
178     }
179
180     properties = BindingTraits<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>::runtimeCast(WTFMove(result));
181 }
182
183 void InjectedScript::getDisplayableProperties(ErrorString& errorString, const String& objectId, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>& properties)
184 {
185     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getDisplayableProperties"_s, inspectorEnvironment()->functionCallHandler());
186     function.appendArgument(objectId);
187     function.appendArgument(generatePreview);
188
189     RefPtr<JSON::Value> result = makeCall(function);
190     if (!result || result->type() != JSON::Value::Type::Array) {
191         errorString = "Internal error"_s;
192         return;
193     }
194
195     properties = BindingTraits<JSON::ArrayOf<Protocol::Runtime::PropertyDescriptor>>::runtimeCast(WTFMove(result));
196 }
197
198 void InjectedScript::getInternalProperties(ErrorString& errorString, const String& objectId, bool generatePreview, RefPtr<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>& properties)
199 {
200     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getInternalProperties"_s, inspectorEnvironment()->functionCallHandler());
201     function.appendArgument(objectId);
202     function.appendArgument(generatePreview);
203
204     RefPtr<JSON::Value> result = makeCall(function);
205     if (!result || result->type() != JSON::Value::Type::Array) {
206         errorString = "Internal error"_s;
207         return;
208     }
209
210     auto array = BindingTraits<JSON::ArrayOf<Protocol::Runtime::InternalPropertyDescriptor>>::runtimeCast(WTFMove(result));
211     properties = array->length() > 0 ? array : nullptr;
212 }
213
214 void InjectedScript::getCollectionEntries(ErrorString& errorString, const String& objectId, const String& objectGroup, int startIndex, int numberToFetch, RefPtr<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>& entries)
215 {
216     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "getCollectionEntries"_s, inspectorEnvironment()->functionCallHandler());
217     function.appendArgument(objectId);
218     function.appendArgument(objectGroup);
219     function.appendArgument(startIndex);
220     function.appendArgument(numberToFetch);
221
222     RefPtr<JSON::Value> result = makeCall(function);
223     if (!result || result->type() != JSON::Value::Type::Array) {
224         errorString = "Internal error"_s;
225         return;
226     }
227
228     entries = BindingTraits<JSON::ArrayOf<Protocol::Runtime::CollectionEntry>>::runtimeCast(WTFMove(result));
229 }
230
231 void InjectedScript::saveResult(ErrorString& errorString, const String& callArgumentJSON, Optional<int>& savedResultIndex)
232 {
233     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "saveResult"_s, inspectorEnvironment()->functionCallHandler());
234     function.appendArgument(callArgumentJSON);
235
236     RefPtr<JSON::Value> result = makeCall(function);
237     if (!result || result->type() != JSON::Value::Type::Integer) {
238         errorString = "Internal error"_s;
239         return;
240     }
241
242     int resultIndex = 0;
243     if (result->asInteger(resultIndex) && resultIndex > 0)
244         savedResultIndex = resultIndex;
245 }
246
247 Ref<JSON::ArrayOf<Protocol::Debugger::CallFrame>> InjectedScript::wrapCallFrames(JSC::JSValue callFrames) const
248 {
249     ASSERT(!hasNoValue());
250     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "wrapCallFrames"_s, inspectorEnvironment()->functionCallHandler());
251     function.appendArgument(callFrames);
252
253     bool hadException = false;
254     auto callFramesValue = callFunctionWithEvalEnabled(function, hadException);
255     if (!callFramesValue)
256         return JSON::ArrayOf<Protocol::Debugger::CallFrame>::create();
257     ASSERT(!hadException);
258     RefPtr<JSON::Value> result = toInspectorValue(*scriptState(), callFramesValue);
259     if (result->type() == JSON::Value::Type::Array)
260         return BindingTraits<JSON::ArrayOf<Protocol::Debugger::CallFrame>>::runtimeCast(WTFMove(result)).releaseNonNull();
261
262     return JSON::ArrayOf<Protocol::Debugger::CallFrame>::create();
263 }
264
265 RefPtr<Protocol::Runtime::RemoteObject> InjectedScript::wrapObject(JSC::JSValue value, const String& groupName, bool generatePreview) const
266 {
267     ASSERT(!hasNoValue());
268     Deprecated::ScriptFunctionCall wrapFunction(injectedScriptObject(), "wrapObject"_s, inspectorEnvironment()->functionCallHandler());
269     wrapFunction.appendArgument(value);
270     wrapFunction.appendArgument(groupName);
271     wrapFunction.appendArgument(hasAccessToInspectedScriptState());
272     wrapFunction.appendArgument(generatePreview);
273
274     bool hadException = false;
275     auto r = callFunctionWithEvalEnabled(wrapFunction, hadException);
276     if (hadException)
277         return nullptr;
278
279     RefPtr<JSON::Object> resultObject;
280     bool castSucceeded = toInspectorValue(*scriptState(), r)->asObject(resultObject);
281     ASSERT_UNUSED(castSucceeded, castSucceeded);
282
283     return BindingTraits<Protocol::Runtime::RemoteObject>::runtimeCast(resultObject);
284 }
285
286 RefPtr<Protocol::Runtime::RemoteObject> InjectedScript::wrapJSONString(const String& json, const String& groupName, bool generatePreview) const
287 {
288     ASSERT(!hasNoValue());
289     Deprecated::ScriptFunctionCall wrapFunction(injectedScriptObject(), "wrapJSONString"_s, inspectorEnvironment()->functionCallHandler());
290     wrapFunction.appendArgument(json);
291     wrapFunction.appendArgument(groupName);
292     wrapFunction.appendArgument(generatePreview);
293
294     bool hadException = false;
295     auto evalResult = callFunctionWithEvalEnabled(wrapFunction, hadException);
296     if (hadException)
297         return nullptr;
298
299     if (evalResult.isNull())
300         return nullptr;
301
302     RefPtr<JSON::Object> resultObject;
303     bool castSucceeded = toInspectorValue(*scriptState(), evalResult)->asObject(resultObject);
304     ASSERT_UNUSED(castSucceeded, castSucceeded);
305
306     return BindingTraits<Protocol::Runtime::RemoteObject>::runtimeCast(resultObject);
307 }
308
309 RefPtr<Protocol::Runtime::RemoteObject> InjectedScript::wrapTable(JSC::JSValue table, JSC::JSValue columns) const
310 {
311     ASSERT(!hasNoValue());
312     Deprecated::ScriptFunctionCall wrapFunction(injectedScriptObject(), "wrapTable"_s, inspectorEnvironment()->functionCallHandler());
313     wrapFunction.appendArgument(hasAccessToInspectedScriptState());
314     wrapFunction.appendArgument(table);
315     if (!columns)
316         wrapFunction.appendArgument(false);
317     else
318         wrapFunction.appendArgument(columns);
319
320     bool hadException = false;
321     auto r = callFunctionWithEvalEnabled(wrapFunction, hadException);
322     if (hadException)
323         return nullptr;
324
325     RefPtr<JSON::Object> resultObject;
326     bool castSucceeded = toInspectorValue(*scriptState(), r)->asObject(resultObject);
327     ASSERT_UNUSED(castSucceeded, castSucceeded);
328
329     return BindingTraits<Protocol::Runtime::RemoteObject>::runtimeCast(resultObject);
330 }
331
332 RefPtr<Protocol::Runtime::ObjectPreview> InjectedScript::previewValue(JSC::JSValue value) const
333 {
334     ASSERT(!hasNoValue());
335     Deprecated::ScriptFunctionCall wrapFunction(injectedScriptObject(), "previewValue"_s, inspectorEnvironment()->functionCallHandler());
336     wrapFunction.appendArgument(value);
337
338     bool hadException = false;
339     auto r = callFunctionWithEvalEnabled(wrapFunction, hadException);
340     if (hadException)
341         return nullptr;
342
343     RefPtr<JSON::Object> resultObject;
344     bool castSucceeded = toInspectorValue(*scriptState(), r)->asObject(resultObject);
345     ASSERT_UNUSED(castSucceeded, castSucceeded);
346
347     return BindingTraits<Protocol::Runtime::ObjectPreview>::runtimeCast(resultObject);
348 }
349
350 void InjectedScript::setEventValue(JSC::JSValue value)
351 {
352     ASSERT(!hasNoValue());
353     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "setEventValue"_s, inspectorEnvironment()->functionCallHandler());
354     function.appendArgument(value);
355     makeCall(function);
356 }
357
358 void InjectedScript::clearEventValue()
359 {
360     ASSERT(!hasNoValue());
361     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "clearEventValue"_s, inspectorEnvironment()->functionCallHandler());
362     makeCall(function);
363 }
364
365 void InjectedScript::setExceptionValue(JSC::JSValue value)
366 {
367     ASSERT(!hasNoValue());
368     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "setExceptionValue"_s, inspectorEnvironment()->functionCallHandler());
369     function.appendArgument(value);
370     makeCall(function);
371 }
372
373 void InjectedScript::clearExceptionValue()
374 {
375     ASSERT(!hasNoValue());
376     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "clearExceptionValue"_s, inspectorEnvironment()->functionCallHandler());
377     makeCall(function);
378 }
379
380 JSC::JSValue InjectedScript::findObjectById(const String& objectId) const
381 {
382     ASSERT(!hasNoValue());
383     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "findObjectById"_s, inspectorEnvironment()->functionCallHandler());
384     function.appendArgument(objectId);
385
386     bool hadException = false;
387     auto resultValue = callFunctionWithEvalEnabled(function, hadException);
388     ASSERT(!hadException);
389
390     return resultValue;
391 }
392
393 void InjectedScript::inspectObject(JSC::JSValue value)
394 {
395     ASSERT(!hasNoValue());
396     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "inspectObject"_s, inspectorEnvironment()->functionCallHandler());
397     function.appendArgument(value);
398     makeCall(function);
399 }
400
401 void InjectedScript::releaseObject(const String& objectId)
402 {
403     Deprecated::ScriptFunctionCall function(injectedScriptObject(), "releaseObject"_s, inspectorEnvironment()->functionCallHandler());
404     function.appendArgument(objectId);
405     makeCall(function);
406 }
407
408 void InjectedScript::releaseObjectGroup(const String& objectGroup)
409 {
410     ASSERT(!hasNoValue());
411     Deprecated::ScriptFunctionCall releaseFunction(injectedScriptObject(), "releaseObjectGroup"_s, inspectorEnvironment()->functionCallHandler());
412     releaseFunction.appendArgument(objectGroup);
413
414     bool hadException = false;
415     callFunctionWithEvalEnabled(releaseFunction, hadException);
416     ASSERT(!hadException);
417 }
418
419 JSC::JSValue InjectedScript::arrayFromVector(Vector<JSC::JSValue>&& vector)
420 {
421     JSC::ExecState* execState = scriptState();
422     if (!execState)
423         return JSC::jsUndefined();
424
425     JSC::JSLockHolder lock(execState);
426
427     JSC::JSArray* array = JSC::constructEmptyArray(execState, nullptr);
428     if (!array)
429         return JSC::jsUndefined();
430
431     for (auto& item : vector)
432         array->putDirectIndex(execState, array->length(), item);
433
434     return array;
435 }
436
437 } // namespace Inspector
438