2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "core/inspector/InjectedScript.h"
36 #include "bindings/core/v8/ScriptFunctionCall.h"
37 #include "bindings/core/v8/V8Binding.h"
38 #include "core/inspector/InjectedScriptHost.h"
39 #include "core/inspector/InspectorTraceEvents.h"
40 #include "core/inspector/JSONParser.h"
41 #include "core/inspector/RemoteObjectId.h"
42 #include "platform/JSONValues.h"
43 #include "wtf/text/WTFString.h"
45 using blink::TypeBuilder::Array
;
46 using blink::TypeBuilder::Debugger::CallFrame
;
47 using blink::TypeBuilder::Debugger::CollectionEntry
;
48 using blink::TypeBuilder::Debugger::FunctionDetails
;
49 using blink::TypeBuilder::Debugger::GeneratorObjectDetails
;
50 using blink::TypeBuilder::Runtime::PropertyDescriptor
;
51 using blink::TypeBuilder::Runtime::InternalPropertyDescriptor
;
52 using blink::TypeBuilder::Runtime::RemoteObject
;
56 PassRefPtr
<JSONValue
> toJSONValue(const ScriptValue
& value
)
58 ScriptState
* scriptState
= value
.scriptState();
59 ASSERT(scriptState
->contextIsValid());
60 ScriptState::Scope
scope(scriptState
);
61 NonThrowableExceptionState exceptionState
;
62 return ScriptValue::to
<JSONValuePtr
>(scriptState
->isolate(), value
, exceptionState
);
65 static PassRefPtr
<TypeBuilder::Debugger::ExceptionDetails
> toExceptionDetails(PassRefPtr
<JSONObject
> object
)
68 if (!object
->getString("text", &text
))
71 RefPtr
<TypeBuilder::Debugger::ExceptionDetails
> exceptionDetails
= TypeBuilder::Debugger::ExceptionDetails::create().setText(text
);
73 if (object
->getString("url", &url
))
74 exceptionDetails
->setUrl(url
);
76 if (object
->getNumber("line", &line
))
77 exceptionDetails
->setLine(line
);
79 if (object
->getNumber("column", &column
))
80 exceptionDetails
->setColumn(column
);
81 int originScriptId
= 0;
82 object
->getNumber("scriptId", &originScriptId
);
84 RefPtr
<JSONArray
> stackTrace
= object
->getArray("stackTrace");
85 if (stackTrace
&& stackTrace
->length() > 0) {
86 RefPtr
<TypeBuilder::Array
<TypeBuilder::Console::CallFrame
>> frames
= TypeBuilder::Array
<TypeBuilder::Console::CallFrame
>::create();
87 for (unsigned i
= 0; i
< stackTrace
->length(); ++i
) {
88 RefPtr
<JSONObject
> stackFrame
= stackTrace
->get(i
)->asObject();
90 stackFrame
->getNumber("lineNumber", &lineNumber
);
92 stackFrame
->getNumber("column", &column
);
94 stackFrame
->getNumber("scriptId", &scriptId
);
95 if (i
== 0 && scriptId
== originScriptId
)
99 stackFrame
->getString("scriptNameOrSourceURL", &sourceURL
);
101 stackFrame
->getString("functionName", &functionName
);
103 RefPtr
<TypeBuilder::Console::CallFrame
> callFrame
= TypeBuilder::Console::CallFrame::create()
104 .setFunctionName(functionName
)
105 .setScriptId(String::number(scriptId
))
107 .setLineNumber(lineNumber
)
108 .setColumnNumber(column
);
110 frames
->addItem(callFrame
.release());
112 exceptionDetails
->setStackTrace(frames
.release());
115 exceptionDetails
->setScriptId(String::number(originScriptId
));
116 return exceptionDetails
.release();
119 InjectedScript::InjectedScript()
120 : m_inspectedStateAccessCheck(nullptr)
124 InjectedScript::InjectedScript(ScriptValue injectedScriptObject
, InspectedStateAccessCheck accessCheck
, PassRefPtr
<InjectedScriptNative
> injectedScriptNative
)
125 : m_injectedScriptObject(injectedScriptObject
)
126 , m_inspectedStateAccessCheck(accessCheck
)
127 , m_native(injectedScriptNative
)
131 InjectedScript::~InjectedScript()
135 void InjectedScript::evaluate(ErrorString
* errorString
, const String
& expression
, const String
& objectGroup
, bool includeCommandLineAPI
, bool returnByValue
, bool generatePreview
, RefPtr
<TypeBuilder::Runtime::RemoteObject
>* result
, TypeBuilder::OptOutput
<bool>* wasThrown
, RefPtr
<TypeBuilder::Debugger::ExceptionDetails
>* exceptionDetails
)
137 ScriptFunctionCall
function(injectedScriptObject(), "evaluate");
138 function
.appendArgument(expression
);
139 function
.appendArgument(objectGroup
);
140 function
.appendArgument(includeCommandLineAPI
);
141 function
.appendArgument(returnByValue
);
142 function
.appendArgument(generatePreview
);
143 makeEvalCall(errorString
, function
, result
, wasThrown
, exceptionDetails
);
146 void InjectedScript::callFunctionOn(ErrorString
* errorString
, const String
& objectId
, const String
& expression
, const String
& arguments
, bool returnByValue
, bool generatePreview
, RefPtr
<TypeBuilder::Runtime::RemoteObject
>* result
, TypeBuilder::OptOutput
<bool>* wasThrown
)
148 ScriptFunctionCall
function(injectedScriptObject(), "callFunctionOn");
149 function
.appendArgument(objectId
);
150 function
.appendArgument(expression
);
151 function
.appendArgument(arguments
);
152 function
.appendArgument(returnByValue
);
153 function
.appendArgument(generatePreview
);
154 makeEvalCall(errorString
, function
, result
, wasThrown
);
157 void InjectedScript::evaluateOnCallFrame(ErrorString
* errorString
, v8::Local
<v8::Object
> callFrames
, bool isAsyncCallStack
, const String
& callFrameId
, const String
& expression
, const String
& objectGroup
, bool includeCommandLineAPI
, bool returnByValue
, bool generatePreview
, RefPtr
<RemoteObject
>* result
, TypeBuilder::OptOutput
<bool>* wasThrown
, RefPtr
<TypeBuilder::Debugger::ExceptionDetails
>* exceptionDetails
)
159 ScriptFunctionCall
function(injectedScriptObject(), "evaluateOnCallFrame");
160 function
.appendArgument(callFrames
);
161 function
.appendArgument(isAsyncCallStack
);
162 function
.appendArgument(callFrameId
);
163 function
.appendArgument(expression
);
164 function
.appendArgument(objectGroup
);
165 function
.appendArgument(includeCommandLineAPI
);
166 function
.appendArgument(returnByValue
);
167 function
.appendArgument(generatePreview
);
168 makeEvalCall(errorString
, function
, result
, wasThrown
, exceptionDetails
);
171 void InjectedScript::restartFrame(ErrorString
* errorString
, v8::Local
<v8::Object
> callFrames
, const String
& callFrameId
)
173 ScriptFunctionCall
function(injectedScriptObject(), "restartFrame");
174 function
.appendArgument(callFrames
);
175 function
.appendArgument(callFrameId
);
176 RefPtr
<JSONValue
> resultValue
;
177 makeCall(function
, &resultValue
);
179 if (resultValue
->type() == JSONValue::TypeString
) {
180 resultValue
->asString(errorString
);
183 ASSERT_UNUSED(value
, resultValue
->asBoolean(&value
) && value
);
187 *errorString
= "Internal error";
190 void InjectedScript::getStepInPositions(ErrorString
* errorString
, v8::Local
<v8::Object
> callFrames
, const String
& callFrameId
, RefPtr
<Array
<TypeBuilder::Debugger::Location
>>& positions
)
192 ScriptFunctionCall
function(injectedScriptObject(), "getStepInPositions");
193 function
.appendArgument(callFrames
);
194 function
.appendArgument(callFrameId
);
195 RefPtr
<JSONValue
> resultValue
;
196 makeCall(function
, &resultValue
);
198 if (resultValue
->type() == JSONValue::TypeString
) {
199 resultValue
->asString(errorString
);
202 if (resultValue
->type() == JSONValue::TypeArray
) {
203 positions
= Array
<TypeBuilder::Debugger::Location
>::runtimeCast(resultValue
);
207 *errorString
= "Internal error";
210 void InjectedScript::setVariableValue(ErrorString
* errorString
, v8::Local
<v8::Object
> callFrames
, const String
* callFrameIdOpt
, const String
* functionObjectIdOpt
, int scopeNumber
, const String
& variableName
, const String
& newValueStr
)
212 ScriptFunctionCall
function(injectedScriptObject(), "setVariableValue");
213 if (callFrameIdOpt
) {
214 function
.appendArgument(callFrames
);
215 function
.appendArgument(*callFrameIdOpt
);
217 function
.appendArgument(false);
218 function
.appendArgument(false);
220 if (functionObjectIdOpt
)
221 function
.appendArgument(*functionObjectIdOpt
);
223 function
.appendArgument(false);
224 function
.appendArgument(scopeNumber
);
225 function
.appendArgument(variableName
);
226 function
.appendArgument(newValueStr
);
227 RefPtr
<JSONValue
> resultValue
;
228 makeCall(function
, &resultValue
);
230 *errorString
= "Internal error";
233 if (resultValue
->type() == JSONValue::TypeString
) {
234 resultValue
->asString(errorString
);
240 void InjectedScript::getFunctionDetails(ErrorString
* errorString
, const String
& functionId
, RefPtr
<FunctionDetails
>* result
)
242 ScriptFunctionCall
function(injectedScriptObject(), "getFunctionDetails");
243 function
.appendArgument(functionId
);
244 RefPtr
<JSONValue
> resultValue
;
245 makeCall(function
, &resultValue
);
246 if (!resultValue
|| resultValue
->type() != JSONValue::TypeObject
) {
247 if (!resultValue
->asString(errorString
))
248 *errorString
= "Internal error";
251 *result
= FunctionDetails::runtimeCast(resultValue
);
254 void InjectedScript::getGeneratorObjectDetails(ErrorString
* errorString
, const String
& objectId
, RefPtr
<GeneratorObjectDetails
>* result
)
256 ScriptFunctionCall
function(injectedScriptObject(), "getGeneratorObjectDetails");
257 function
.appendArgument(objectId
);
258 RefPtr
<JSONValue
> resultValue
;
259 makeCall(function
, &resultValue
);
260 if (!resultValue
|| resultValue
->type() != JSONValue::TypeObject
) {
261 if (!resultValue
->asString(errorString
))
262 *errorString
= "Internal error";
265 *result
= GeneratorObjectDetails::runtimeCast(resultValue
);
268 void InjectedScript::getCollectionEntries(ErrorString
* errorString
, const String
& objectId
, RefPtr
<Array
<CollectionEntry
> >* result
)
270 ScriptFunctionCall
function(injectedScriptObject(), "getCollectionEntries");
271 function
.appendArgument(objectId
);
272 RefPtr
<JSONValue
> resultValue
;
273 makeCall(function
, &resultValue
);
274 if (!resultValue
|| resultValue
->type() != JSONValue::TypeArray
) {
275 if (!resultValue
->asString(errorString
))
276 *errorString
= "Internal error";
279 *result
= Array
<CollectionEntry
>::runtimeCast(resultValue
);
282 void InjectedScript::getProperties(ErrorString
* errorString
, const String
& objectId
, bool ownProperties
, bool accessorPropertiesOnly
, bool generatePreview
, RefPtr
<Array
<PropertyDescriptor
>>* properties
, RefPtr
<TypeBuilder::Debugger::ExceptionDetails
>* exceptionDetails
)
284 ScriptFunctionCall
function(injectedScriptObject(), "getProperties");
285 function
.appendArgument(objectId
);
286 function
.appendArgument(ownProperties
);
287 function
.appendArgument(accessorPropertiesOnly
);
288 function
.appendArgument(generatePreview
);
290 RefPtr
<JSONValue
> result
;
291 makeCallWithExceptionDetails(function
, &result
, exceptionDetails
);
292 if (*exceptionDetails
) {
293 // FIXME: make properties optional
294 *properties
= Array
<PropertyDescriptor
>::create();
297 if (!result
|| result
->type() != JSONValue::TypeArray
) {
298 *errorString
= "Internal error";
301 *properties
= Array
<PropertyDescriptor
>::runtimeCast(result
);
304 void InjectedScript::getInternalProperties(ErrorString
* errorString
, const String
& objectId
, RefPtr
<Array
<InternalPropertyDescriptor
>>* properties
, RefPtr
<TypeBuilder::Debugger::ExceptionDetails
>* exceptionDetails
)
306 ScriptFunctionCall
function(injectedScriptObject(), "getInternalProperties");
307 function
.appendArgument(objectId
);
309 RefPtr
<JSONValue
> result
;
310 makeCallWithExceptionDetails(function
, &result
, exceptionDetails
);
311 if (*exceptionDetails
)
313 if (!result
|| result
->type() != JSONValue::TypeArray
) {
314 *errorString
= "Internal error";
317 RefPtr
<Array
<InternalPropertyDescriptor
> > array
= Array
<InternalPropertyDescriptor
>::runtimeCast(result
);
318 if (array
->length() > 0)
322 void InjectedScript::releaseObject(const String
& objectId
)
324 RefPtr
<JSONValue
> parsedObjectId
= parseJSON(objectId
);
327 RefPtr
<JSONObject
> object
;
328 if (!parsedObjectId
->asObject(&object
))
331 if (!object
->getNumber("id", &boundId
))
333 m_native
->unbind(boundId
);
336 PassRefPtr
<Array
<CallFrame
>> InjectedScript::wrapCallFrames(v8::Local
<v8::Object
> callFrames
, int asyncOrdinal
)
339 ScriptFunctionCall
function(injectedScriptObject(), "wrapCallFrames");
340 function
.appendArgument(callFrames
);
341 function
.appendArgument(asyncOrdinal
);
342 bool hadException
= false;
343 ScriptValue callFramesValue
= callFunctionWithEvalEnabled(function
, hadException
);
344 ASSERT(!hadException
);
345 RefPtr
<JSONValue
> result
= toJSONValue(callFramesValue
);
346 if (result
&& result
->type() == JSONValue::TypeArray
)
347 return Array
<CallFrame
>::runtimeCast(result
);
348 return Array
<CallFrame
>::create();
351 PassRefPtr
<TypeBuilder::Runtime::RemoteObject
> InjectedScript::wrapObject(const ScriptValue
& value
, const String
& groupName
, bool generatePreview
) const
354 ScriptFunctionCall
wrapFunction(injectedScriptObject(), "wrapObject");
355 wrapFunction
.appendArgument(value
);
356 wrapFunction
.appendArgument(groupName
);
357 wrapFunction
.appendArgument(canAccessInspectedWindow());
358 wrapFunction
.appendArgument(generatePreview
);
359 bool hadException
= false;
360 ScriptValue r
= callFunctionWithEvalEnabled(wrapFunction
, hadException
);
363 RefPtr
<JSONObject
> rawResult
= toJSONValue(r
)->asObject();
364 return TypeBuilder::Runtime::RemoteObject::runtimeCast(rawResult
);
367 PassRefPtr
<TypeBuilder::Runtime::RemoteObject
> InjectedScript::wrapTable(const ScriptValue
& table
, const ScriptValue
& columns
) const
370 ScriptFunctionCall
wrapFunction(injectedScriptObject(), "wrapTable");
371 wrapFunction
.appendArgument(canAccessInspectedWindow());
372 wrapFunction
.appendArgument(table
);
373 if (columns
.isEmpty())
374 wrapFunction
.appendArgument(false);
376 wrapFunction
.appendArgument(columns
);
377 bool hadException
= false;
378 ScriptValue r
= callFunctionWithEvalEnabled(wrapFunction
, hadException
);
381 RefPtr
<JSONObject
> rawResult
= toJSONValue(r
)->asObject();
382 return TypeBuilder::Runtime::RemoteObject::runtimeCast(rawResult
);
385 v8::Local
<v8::Value
> InjectedScript::findObject(const RemoteObjectId
& objectId
) const
388 return m_native
->objectForId(objectId
.id());
391 String
InjectedScript::objectIdToObjectGroupName(const String
& objectId
) const
393 RefPtr
<JSONValue
> parsedObjectId
= parseJSON(objectId
);
396 RefPtr
<JSONObject
> object
;
397 if (!parsedObjectId
->asObject(&object
))
400 if (!object
->getNumber("id", &boundId
))
402 return m_native
->groupName(boundId
);
405 void InjectedScript::releaseObjectGroup(const String
& objectGroup
)
408 m_native
->releaseObjectGroup(objectGroup
);
409 if (objectGroup
== "console") {
410 ScriptFunctionCall
releaseFunction(injectedScriptObject(), "clearLastEvaluationResult");
411 bool hadException
= false;
412 callFunctionWithEvalEnabled(releaseFunction
, hadException
);
413 ASSERT(!hadException
);
417 void InjectedScript::setCustomObjectFormatterEnabled(bool enabled
)
420 ScriptFunctionCall
function(injectedScriptObject(), "setCustomObjectFormatterEnabled");
421 function
.appendArgument(enabled
);
422 RefPtr
<JSONValue
> result
;
423 makeCall(function
, &result
);
426 void InjectedScript::initialize(ScriptValue injectedScriptObject
, InspectedStateAccessCheck accessCheck
)
428 m_injectedScriptObject
= injectedScriptObject
;
429 m_inspectedStateAccessCheck
= accessCheck
;
432 bool InjectedScript::canAccessInspectedWindow() const
435 return m_inspectedStateAccessCheck(m_injectedScriptObject
.scriptState());
438 const ScriptValue
& InjectedScript::injectedScriptObject() const
440 return m_injectedScriptObject
;
443 ScriptValue
InjectedScript::callFunctionWithEvalEnabled(ScriptFunctionCall
& function
, bool& hadException
) const
447 ScriptState
* scriptState
= m_injectedScriptObject
.scriptState();
448 ScriptState::Scope
scope(scriptState
);
449 bool evalIsDisabled
= !scriptState
->evalEnabled();
450 // Temporarily enable allow evals for inspector.
452 scriptState
->setEvalEnabled(true);
454 ScriptValue resultValue
= function
.call(hadException
);
457 scriptState
->setEvalEnabled(false);
459 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", TRACE_EVENT_SCOPE_THREAD
, "data", InspectorUpdateCountersEvent::data());
463 void InjectedScript::makeCall(ScriptFunctionCall
& function
, RefPtr
<JSONValue
>* result
)
465 if (isEmpty() || !canAccessInspectedWindow()) {
466 *result
= JSONValue::null();
470 bool hadException
= false;
471 ScriptValue resultValue
= callFunctionWithEvalEnabled(function
, hadException
);
473 ASSERT(!hadException
);
475 *result
= toJSONValue(resultValue
);
477 *result
= JSONString::create(String::format("Object has too long reference chain(must not be longer than %d)", JSONValue::maxDepth
));
479 *result
= JSONString::create("Exception while making a call.");
483 void InjectedScript::makeEvalCall(ErrorString
* errorString
, ScriptFunctionCall
& function
, RefPtr
<TypeBuilder::Runtime::RemoteObject
>* objectResult
, TypeBuilder::OptOutput
<bool>* wasThrown
, RefPtr
<TypeBuilder::Debugger::ExceptionDetails
>* exceptionDetails
)
485 RefPtr
<JSONValue
> result
;
486 makeCall(function
, &result
);
488 *errorString
= "Internal error: result value is empty";
491 if (result
->type() == JSONValue::TypeString
) {
492 result
->asString(errorString
);
493 ASSERT(errorString
->length());
496 RefPtr
<JSONObject
> resultPair
= result
->asObject();
498 *errorString
= "Internal error: result is not an Object";
501 RefPtr
<JSONObject
> resultObj
= resultPair
->getObject("result");
502 bool wasThrownVal
= false;
503 if (!resultObj
|| !resultPair
->getBoolean("wasThrown", &wasThrownVal
)) {
504 *errorString
= "Internal error: result is not a pair of value and wasThrown flag";
508 RefPtr
<JSONObject
> objectExceptionDetails
= resultPair
->getObject("exceptionDetails");
509 if (objectExceptionDetails
)
510 *exceptionDetails
= toExceptionDetails(objectExceptionDetails
.release());
512 *objectResult
= TypeBuilder::Runtime::RemoteObject::runtimeCast(resultObj
);
513 *wasThrown
= wasThrownVal
;
516 void InjectedScript::makeCallWithExceptionDetails(ScriptFunctionCall
& function
, RefPtr
<JSONValue
>* result
, RefPtr
<TypeBuilder::Debugger::ExceptionDetails
>* exceptionDetails
)
518 ScriptState::Scope
scope(injectedScriptObject().scriptState());
519 v8::TryCatch tryCatch
;
520 ScriptValue resultValue
= function
.callWithoutExceptionHandling();
521 if (tryCatch
.HasCaught()) {
522 v8::Local
<v8::Message
> message
= tryCatch
.Message();
523 String text
= !message
.IsEmpty() ? toCoreStringWithUndefinedOrNullCheck(message
->Get()) : "Internal error";
524 *exceptionDetails
= TypeBuilder::Debugger::ExceptionDetails::create().setText(text
);
526 *result
= toJSONValue(resultValue
);
528 *result
= JSONString::create(String::format("Object has too long reference chain(must not be longer than %d)", JSONValue::maxDepth
));