1 /* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 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 13 * distribution. 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. 17 * 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. 29 */ 30 31 #include "src/inspector/injected-script.h" 32 33 #include "src/inspector/injected-script-source.h" 34 #include "src/inspector/inspected-context.h" 35 #include "src/inspector/protocol/Protocol.h" 36 #include "src/inspector/remote-object-id.h" 37 #include "src/inspector/string-util.h" 38 #include "src/inspector/v8-console.h" 39 #include "src/inspector/v8-function-call.h" 40 #include "src/inspector/v8-injected-script-host.h" 41 #include "src/inspector/v8-inspector-impl.h" 42 #include "src/inspector/v8-inspector-session-impl.h" 43 #include "src/inspector/v8-stack-trace-impl.h" 44 #include "src/inspector/v8-value-utils.h" 45 46 #include "include/v8-inspector.h" 47 48 namespace v8_inspector { 49 50 namespace { 51 static const char privateKeyName[] = "v8-inspector#injectedScript"; 52 static const char kGlobalHandleLabel[] = "DevTools console"; 53 static bool isResolvableNumberLike(String16 query) { 54 return query == "Infinity" || query == "-Infinity" || query == "NaN"; 55 } 56 } // namespace 57 58 using protocol::Array; 59 using protocol::Runtime::PropertyDescriptor; 60 using protocol::Runtime::InternalPropertyDescriptor; 61 using protocol::Runtime::RemoteObject; 62 using protocol::Maybe; 63 64 class InjectedScript::ProtocolPromiseHandler { 65 public: 66 static bool add(V8InspectorSessionImpl* session, 67 v8::Local<v8::Context> context, v8::Local<v8::Value> value, 68 int executionContextId, const String16& objectGroup, 69 bool returnByValue, bool generatePreview, 70 EvaluateCallback* callback) { 71 v8::Local<v8::Promise::Resolver> resolver; 72 if (!v8::Promise::Resolver::New(context).ToLocal(&resolver)) { 73 callback->sendFailure(Response::InternalError()); 74 return false; 75 } 76 if (!resolver->Resolve(context, value).FromMaybe(false)) { 77 callback->sendFailure(Response::InternalError()); 78 return false; 79 } 80 81 v8::Local<v8::Promise> promise = resolver->GetPromise(); 82 V8InspectorImpl* inspector = session->inspector(); 83 ProtocolPromiseHandler* handler = 84 new ProtocolPromiseHandler(session, executionContextId, objectGroup, 85 returnByValue, generatePreview, callback); 86 v8::Local<v8::Value> wrapper = handler->m_wrapper.Get(inspector->isolate()); 87 v8::Local<v8::Function> thenCallbackFunction = 88 v8::Function::New(context, thenCallback, wrapper, 0, 89 v8::ConstructorBehavior::kThrow) 90 .ToLocalChecked(); 91 if (promise->Then(context, thenCallbackFunction).IsEmpty()) { 92 callback->sendFailure(Response::InternalError()); 93 return false; 94 } 95 v8::Local<v8::Function> catchCallbackFunction = 96 v8::Function::New(context, catchCallback, wrapper, 0, 97 v8::ConstructorBehavior::kThrow) 98 .ToLocalChecked(); 99 if (promise->Catch(context, catchCallbackFunction).IsEmpty()) { 100 callback->sendFailure(Response::InternalError()); 101 return false; 102 } 103 return true; 104 } 105 106 private: 107 static void thenCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { 108 ProtocolPromiseHandler* handler = static_cast<ProtocolPromiseHandler*>( 109 info.Data().As<v8::External>()->Value()); 110 DCHECK(handler); 111 v8::Local<v8::Value> value = 112 info.Length() > 0 113 ? info[0] 114 : v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate())); 115 handler->thenCallback(value); 116 delete handler; 117 } 118 119 static void catchCallback(const v8::FunctionCallbackInfo<v8::Value>& info) { 120 ProtocolPromiseHandler* handler = static_cast<ProtocolPromiseHandler*>( 121 info.Data().As<v8::External>()->Value()); 122 DCHECK(handler); 123 v8::Local<v8::Value> value = 124 info.Length() > 0 125 ? info[0] 126 : v8::Local<v8::Value>::Cast(v8::Undefined(info.GetIsolate())); 127 handler->catchCallback(value); 128 delete handler; 129 } 130 131 ProtocolPromiseHandler(V8InspectorSessionImpl* session, 132 int executionContextId, const String16& objectGroup, 133 bool returnByValue, bool generatePreview, 134 EvaluateCallback* callback) 135 : m_inspector(session->inspector()), 136 m_sessionId(session->sessionId()), 137 m_contextGroupId(session->contextGroupId()), 138 m_executionContextId(executionContextId), 139 m_objectGroup(objectGroup), 140 m_returnByValue(returnByValue), 141 m_generatePreview(generatePreview), 142 m_callback(std::move(callback)), 143 m_wrapper(m_inspector->isolate(), 144 v8::External::New(m_inspector->isolate(), this)) { 145 m_wrapper.SetWeak(this, cleanup, v8::WeakCallbackType::kParameter); 146 } 147 148 static void cleanup( 149 const v8::WeakCallbackInfo<ProtocolPromiseHandler>& data) { 150 if (!data.GetParameter()->m_wrapper.IsEmpty()) { 151 data.GetParameter()->m_wrapper.Reset(); 152 data.SetSecondPassCallback(cleanup); 153 } else { 154 data.GetParameter()->sendPromiseCollected(); 155 delete data.GetParameter(); 156 } 157 } 158 159 void thenCallback(v8::Local<v8::Value> result) { 160 V8InspectorSessionImpl* session = 161 m_inspector->sessionById(m_contextGroupId, m_sessionId); 162 if (!session) return; 163 InjectedScript::ContextScope scope(session, m_executionContextId); 164 Response response = scope.initialize(); 165 if (!response.isSuccess()) return; 166 if (m_objectGroup == "console") { 167 scope.injectedScript()->setLastEvaluationResult(result); 168 } 169 std::unique_ptr<EvaluateCallback> callback = 170 scope.injectedScript()->takeEvaluateCallback(m_callback); 171 if (!callback) return; 172 std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue; 173 response = scope.injectedScript()->wrapObject( 174 result, m_objectGroup, m_returnByValue, m_generatePreview, 175 &wrappedValue); 176 if (!response.isSuccess()) { 177 callback->sendFailure(response); 178 return; 179 } 180 callback->sendSuccess(std::move(wrappedValue), 181 Maybe<protocol::Runtime::ExceptionDetails>()); 182 } 183 184 void catchCallback(v8::Local<v8::Value> result) { 185 V8InspectorSessionImpl* session = 186 m_inspector->sessionById(m_contextGroupId, m_sessionId); 187 if (!session) return; 188 InjectedScript::ContextScope scope(session, m_executionContextId); 189 Response response = scope.initialize(); 190 if (!response.isSuccess()) return; 191 std::unique_ptr<EvaluateCallback> callback = 192 scope.injectedScript()->takeEvaluateCallback(m_callback); 193 if (!callback) return; 194 std::unique_ptr<protocol::Runtime::RemoteObject> wrappedValue; 195 response = scope.injectedScript()->wrapObject( 196 result, m_objectGroup, m_returnByValue, m_generatePreview, 197 &wrappedValue); 198 if (!response.isSuccess()) { 199 callback->sendFailure(response); 200 return; 201 } 202 String16 message; 203 std::unique_ptr<V8StackTraceImpl> stack; 204 v8::Isolate* isolate = session->inspector()->isolate(); 205 if (result->IsNativeError()) { 206 message = " " + toProtocolString( 207 isolate, 208 result->ToDetailString(isolate->GetCurrentContext()) 209 .ToLocalChecked()); 210 v8::Local<v8::StackTrace> stackTrace = v8::debug::GetDetailedStackTrace( 211 isolate, v8::Local<v8::Object>::Cast(result)); 212 if (!stackTrace.IsEmpty()) { 213 stack = m_inspector->debugger()->createStackTrace(stackTrace); 214 } 215 } 216 if (!stack) { 217 stack = m_inspector->debugger()->captureStackTrace(true); 218 } 219 std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails = 220 protocol::Runtime::ExceptionDetails::create() 221 .setExceptionId(m_inspector->nextExceptionId()) 222 .setText("Uncaught (in promise)" + message) 223 .setLineNumber(stack && !stack->isEmpty() ? stack->topLineNumber() 224 : 0) 225 .setColumnNumber( 226 stack && !stack->isEmpty() ? stack->topColumnNumber() : 0) 227 .setException(wrappedValue->clone()) 228 .build(); 229 if (stack) 230 exceptionDetails->setStackTrace( 231 stack->buildInspectorObjectImpl(m_inspector->debugger())); 232 if (stack && !stack->isEmpty()) 233 exceptionDetails->setScriptId(toString16(stack->topScriptId())); 234 callback->sendSuccess(std::move(wrappedValue), std::move(exceptionDetails)); 235 } 236 237 void sendPromiseCollected() { 238 V8InspectorSessionImpl* session = 239 m_inspector->sessionById(m_contextGroupId, m_sessionId); 240 if (!session) return; 241 InjectedScript::ContextScope scope(session, m_executionContextId); 242 Response response = scope.initialize(); 243 if (!response.isSuccess()) return; 244 std::unique_ptr<EvaluateCallback> callback = 245 scope.injectedScript()->takeEvaluateCallback(m_callback); 246 if (!callback) return; 247 callback->sendFailure(Response::Error("Promise was collected")); 248 } 249 250 V8InspectorImpl* m_inspector; 251 int m_sessionId; 252 int m_contextGroupId; 253 int m_executionContextId; 254 String16 m_objectGroup; 255 bool m_returnByValue; 256 bool m_generatePreview; 257 EvaluateCallback* m_callback; 258 v8::Global<v8::External> m_wrapper; 259 }; 260 261 std::unique_ptr<InjectedScript> InjectedScript::create( 262 InspectedContext* inspectedContext, int sessionId) { 263 v8::Isolate* isolate = inspectedContext->isolate(); 264 v8::HandleScope handles(isolate); 265 v8::TryCatch tryCatch(isolate); 266 v8::Local<v8::Context> context = inspectedContext->context(); 267 v8::debug::PostponeInterruptsScope postponeInterrupts(isolate); 268 v8::Context::Scope scope(context); 269 v8::MicrotasksScope microtasksScope(isolate, 270 v8::MicrotasksScope::kDoNotRunMicrotasks); 271 272 // Inject javascript into the context. The compiled script is supposed to 273 // evaluate into 274 // a single anonymous function(it's anonymous to avoid cluttering the global 275 // object with 276 // inspector's stuff) the function is called a few lines below with 277 // InjectedScriptHost wrapper, 278 // injected script id and explicit reference to the inspected global object. 279 // The function is expected 280 // to create and configure InjectedScript instance that is going to be used by 281 // the inspector. 282 StringView injectedScriptSource( 283 reinterpret_cast<const uint8_t*>(InjectedScriptSource_js), 284 sizeof(InjectedScriptSource_js)); 285 v8::Local<v8::Value> value; 286 if (!inspectedContext->inspector() 287 ->compileAndRunInternalScript( 288 context, toV8String(isolate, injectedScriptSource)) 289 .ToLocal(&value)) { 290 return nullptr; 291 } 292 DCHECK(value->IsFunction()); 293 v8::Local<v8::Object> scriptHostWrapper = 294 V8InjectedScriptHost::create(context, inspectedContext->inspector()); 295 v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(value); 296 v8::Local<v8::Object> windowGlobal = context->Global(); 297 v8::Local<v8::Value> info[] = { 298 scriptHostWrapper, windowGlobal, 299 v8::Number::New(isolate, inspectedContext->contextId())}; 300 301 int contextGroupId = inspectedContext->contextGroupId(); 302 int contextId = inspectedContext->contextId(); 303 V8InspectorImpl* inspector = inspectedContext->inspector(); 304 v8::Local<v8::Value> injectedScriptValue; 305 if (!function->Call(context, windowGlobal, arraysize(info), info) 306 .ToLocal(&injectedScriptValue)) 307 return nullptr; 308 if (inspector->getContext(contextGroupId, contextId) != inspectedContext) 309 return nullptr; 310 if (!injectedScriptValue->IsObject()) return nullptr; 311 312 std::unique_ptr<InjectedScript> injectedScript(new InjectedScript( 313 inspectedContext, injectedScriptValue.As<v8::Object>(), sessionId)); 314 v8::Local<v8::Private> privateKey = v8::Private::ForApi( 315 isolate, v8::String::NewFromUtf8(isolate, privateKeyName, 316 v8::NewStringType::kInternalized) 317 .ToLocalChecked()); 318 scriptHostWrapper->SetPrivate( 319 context, privateKey, v8::External::New(isolate, injectedScript.get())); 320 return injectedScript; 321 } 322 323 InjectedScript::InjectedScript(InspectedContext* context, 324 v8::Local<v8::Object> object, int sessionId) 325 : m_context(context), 326 m_value(context->isolate(), object), 327 m_sessionId(sessionId) {} 328 329 InjectedScript::~InjectedScript() { discardEvaluateCallbacks(); } 330 331 Response InjectedScript::getProperties( 332 v8::Local<v8::Object> object, const String16& groupName, bool ownProperties, 333 bool accessorPropertiesOnly, bool generatePreview, 334 std::unique_ptr<Array<PropertyDescriptor>>* properties, 335 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { 336 v8::HandleScope handles(m_context->isolate()); 337 v8::Local<v8::Context> context = m_context->context(); 338 V8FunctionCall function(m_context->inspector(), m_context->context(), 339 v8Value(), "getProperties"); 340 function.appendArgument(object); 341 function.appendArgument(groupName); 342 function.appendArgument(ownProperties); 343 function.appendArgument(accessorPropertiesOnly); 344 function.appendArgument(generatePreview); 345 346 v8::TryCatch tryCatch(m_context->isolate()); 347 v8::Local<v8::Value> resultValue = function.callWithoutExceptionHandling(); 348 if (tryCatch.HasCaught()) { 349 Response response = createExceptionDetails( 350 tryCatch, groupName, generatePreview, exceptionDetails); 351 if (!response.isSuccess()) return response; 352 // FIXME: make properties optional 353 *properties = Array<PropertyDescriptor>::create(); 354 return Response::OK(); 355 } 356 if (resultValue.IsEmpty()) return Response::InternalError(); 357 std::unique_ptr<protocol::Value> protocolValue; 358 Response response = toProtocolValue(context, resultValue, &protocolValue); 359 if (!response.isSuccess()) return response; 360 protocol::ErrorSupport errors; 361 std::unique_ptr<Array<PropertyDescriptor>> result = 362 Array<PropertyDescriptor>::fromValue(protocolValue.get(), &errors); 363 if (errors.hasErrors()) return Response::Error(errors.errors()); 364 *properties = std::move(result); 365 return Response::OK(); 366 } 367 368 void InjectedScript::releaseObject(const String16& objectId) { 369 std::unique_ptr<protocol::Value> parsedObjectId = 370 protocol::StringUtil::parseJSON(objectId); 371 if (!parsedObjectId) return; 372 protocol::DictionaryValue* object = 373 protocol::DictionaryValue::cast(parsedObjectId.get()); 374 if (!object) return; 375 int boundId = 0; 376 if (!object->getInteger("id", &boundId)) return; 377 unbindObject(boundId); 378 } 379 380 Response InjectedScript::wrapObject( 381 v8::Local<v8::Value> value, const String16& groupName, bool forceValueType, 382 bool generatePreview, 383 std::unique_ptr<protocol::Runtime::RemoteObject>* result) const { 384 v8::HandleScope handles(m_context->isolate()); 385 v8::Local<v8::Value> wrappedObject; 386 v8::Local<v8::Context> context = m_context->context(); 387 Response response = wrapValue(value, groupName, forceValueType, 388 generatePreview, &wrappedObject); 389 if (!response.isSuccess()) return response; 390 protocol::ErrorSupport errors; 391 std::unique_ptr<protocol::Value> protocolValue; 392 response = toProtocolValue(context, wrappedObject, &protocolValue); 393 if (!response.isSuccess()) return response; 394 395 *result = 396 protocol::Runtime::RemoteObject::fromValue(protocolValue.get(), &errors); 397 if (!result->get()) return Response::Error(errors.errors()); 398 return Response::OK(); 399 } 400 401 Response InjectedScript::wrapValue(v8::Local<v8::Value> value, 402 const String16& groupName, 403 bool forceValueType, bool generatePreview, 404 v8::Local<v8::Value>* result) const { 405 V8FunctionCall function(m_context->inspector(), m_context->context(), 406 v8Value(), "wrapObject"); 407 function.appendArgument(value); 408 function.appendArgument(groupName); 409 function.appendArgument(forceValueType); 410 function.appendArgument(generatePreview); 411 bool hadException = false; 412 *result = function.call(hadException); 413 if (hadException || result->IsEmpty()) return Response::InternalError(); 414 return Response::OK(); 415 } 416 417 std::unique_ptr<protocol::Runtime::RemoteObject> InjectedScript::wrapTable( 418 v8::Local<v8::Value> table, v8::Local<v8::Value> columns) const { 419 v8::HandleScope handles(m_context->isolate()); 420 v8::Local<v8::Context> context = m_context->context(); 421 V8FunctionCall function(m_context->inspector(), context, v8Value(), 422 "wrapTable"); 423 function.appendArgument(table); 424 if (columns.IsEmpty()) 425 function.appendArgument(false); 426 else 427 function.appendArgument(columns); 428 bool hadException = false; 429 v8::Local<v8::Value> r = function.call(hadException); 430 if (hadException || r.IsEmpty()) return nullptr; 431 std::unique_ptr<protocol::Value> protocolValue; 432 Response response = toProtocolValue(context, r, &protocolValue); 433 if (!response.isSuccess()) return nullptr; 434 protocol::ErrorSupport errors; 435 return protocol::Runtime::RemoteObject::fromValue(protocolValue.get(), 436 &errors); 437 } 438 439 void InjectedScript::addPromiseCallback( 440 V8InspectorSessionImpl* session, v8::MaybeLocal<v8::Value> value, 441 const String16& objectGroup, bool returnByValue, bool generatePreview, 442 std::unique_ptr<EvaluateCallback> callback) { 443 if (value.IsEmpty()) { 444 callback->sendFailure(Response::InternalError()); 445 return; 446 } 447 v8::MicrotasksScope microtasksScope(m_context->isolate(), 448 v8::MicrotasksScope::kRunMicrotasks); 449 if (ProtocolPromiseHandler::add( 450 session, m_context->context(), value.ToLocalChecked(), 451 m_context->contextId(), objectGroup, returnByValue, generatePreview, 452 callback.get())) { 453 m_evaluateCallbacks.insert(callback.release()); 454 } 455 } 456 457 void InjectedScript::discardEvaluateCallbacks() { 458 for (auto& callback : m_evaluateCallbacks) { 459 callback->sendFailure(Response::Error("Execution context was destroyed.")); 460 delete callback; 461 } 462 m_evaluateCallbacks.clear(); 463 } 464 465 std::unique_ptr<EvaluateCallback> InjectedScript::takeEvaluateCallback( 466 EvaluateCallback* callback) { 467 auto it = m_evaluateCallbacks.find(callback); 468 if (it == m_evaluateCallbacks.end()) return nullptr; 469 std::unique_ptr<EvaluateCallback> value(*it); 470 m_evaluateCallbacks.erase(it); 471 return value; 472 } 473 474 Response InjectedScript::findObject(const RemoteObjectId& objectId, 475 v8::Local<v8::Value>* outObject) const { 476 auto it = m_idToWrappedObject.find(objectId.id()); 477 if (it == m_idToWrappedObject.end()) 478 return Response::Error("Could not find object with given id"); 479 *outObject = it->second.Get(m_context->isolate()); 480 return Response::OK(); 481 } 482 483 String16 InjectedScript::objectGroupName(const RemoteObjectId& objectId) const { 484 if (objectId.id() <= 0) return String16(); 485 auto it = m_idToObjectGroupName.find(objectId.id()); 486 return it != m_idToObjectGroupName.end() ? it->second : String16(); 487 } 488 489 void InjectedScript::releaseObjectGroup(const String16& objectGroup) { 490 if (objectGroup == "console") m_lastEvaluationResult.Reset(); 491 if (objectGroup.isEmpty()) return; 492 auto it = m_nameToObjectGroup.find(objectGroup); 493 if (it == m_nameToObjectGroup.end()) return; 494 for (int id : it->second) unbindObject(id); 495 m_nameToObjectGroup.erase(it); 496 } 497 498 void InjectedScript::setCustomObjectFormatterEnabled(bool enabled) { 499 v8::HandleScope handles(m_context->isolate()); 500 V8FunctionCall function(m_context->inspector(), m_context->context(), 501 v8Value(), "setCustomObjectFormatterEnabled"); 502 function.appendArgument(enabled); 503 bool hadException = false; 504 function.call(hadException); 505 DCHECK(!hadException); 506 } 507 508 v8::Local<v8::Value> InjectedScript::v8Value() const { 509 return m_value.Get(m_context->isolate()); 510 } 511 512 v8::Local<v8::Value> InjectedScript::lastEvaluationResult() const { 513 if (m_lastEvaluationResult.IsEmpty()) 514 return v8::Undefined(m_context->isolate()); 515 return m_lastEvaluationResult.Get(m_context->isolate()); 516 } 517 518 void InjectedScript::setLastEvaluationResult(v8::Local<v8::Value> result) { 519 m_lastEvaluationResult.Reset(m_context->isolate(), result); 520 m_lastEvaluationResult.AnnotateStrongRetainer(kGlobalHandleLabel); 521 } 522 523 Response InjectedScript::resolveCallArgument( 524 protocol::Runtime::CallArgument* callArgument, 525 v8::Local<v8::Value>* result) { 526 if (callArgument->hasObjectId()) { 527 std::unique_ptr<RemoteObjectId> remoteObjectId; 528 Response response = 529 RemoteObjectId::parse(callArgument->getObjectId(""), &remoteObjectId); 530 if (!response.isSuccess()) return response; 531 if (remoteObjectId->contextId() != m_context->contextId()) 532 return Response::Error( 533 "Argument should belong to the same JavaScript world as target " 534 "object"); 535 return findObject(*remoteObjectId, result); 536 } 537 if (callArgument->hasValue() || callArgument->hasUnserializableValue()) { 538 String16 value; 539 if (callArgument->hasValue()) { 540 value = "(" + callArgument->getValue(nullptr)->serialize() + ")"; 541 } else { 542 String16 unserializableValue = callArgument->getUnserializableValue(""); 543 // Protect against potential identifier resolution for NaN and Infinity. 544 if (isResolvableNumberLike(unserializableValue)) 545 value = "Number(\"" + unserializableValue + "\")"; 546 else 547 value = unserializableValue; 548 } 549 if (!m_context->inspector() 550 ->compileAndRunInternalScript( 551 m_context->context(), toV8String(m_context->isolate(), value)) 552 .ToLocal(result)) { 553 return Response::Error("Couldn't parse value object in call argument"); 554 } 555 return Response::OK(); 556 } 557 *result = v8::Undefined(m_context->isolate()); 558 return Response::OK(); 559 } 560 561 Response InjectedScript::createExceptionDetails( 562 const v8::TryCatch& tryCatch, const String16& objectGroup, 563 bool generatePreview, Maybe<protocol::Runtime::ExceptionDetails>* result) { 564 if (!tryCatch.HasCaught()) return Response::InternalError(); 565 v8::Local<v8::Message> message = tryCatch.Message(); 566 v8::Local<v8::Value> exception = tryCatch.Exception(); 567 String16 messageText = 568 message.IsEmpty() 569 ? String16() 570 : toProtocolString(m_context->isolate(), message->Get()); 571 std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails = 572 protocol::Runtime::ExceptionDetails::create() 573 .setExceptionId(m_context->inspector()->nextExceptionId()) 574 .setText(exception.IsEmpty() ? messageText : String16("Uncaught")) 575 .setLineNumber( 576 message.IsEmpty() 577 ? 0 578 : message->GetLineNumber(m_context->context()).FromMaybe(1) - 579 1) 580 .setColumnNumber( 581 message.IsEmpty() 582 ? 0 583 : message->GetStartColumn(m_context->context()).FromMaybe(0)) 584 .build(); 585 if (!message.IsEmpty()) { 586 exceptionDetails->setScriptId(String16::fromInteger( 587 static_cast<int>(message->GetScriptOrigin().ScriptID()->Value()))); 588 v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace(); 589 if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0) 590 exceptionDetails->setStackTrace( 591 m_context->inspector() 592 ->debugger() 593 ->createStackTrace(stackTrace) 594 ->buildInspectorObjectImpl(m_context->inspector()->debugger())); 595 } 596 if (!exception.IsEmpty()) { 597 std::unique_ptr<protocol::Runtime::RemoteObject> wrapped; 598 Response response = 599 wrapObject(exception, objectGroup, false /* forceValueType */, 600 generatePreview && !exception->IsNativeError(), &wrapped); 601 if (!response.isSuccess()) return response; 602 exceptionDetails->setException(std::move(wrapped)); 603 } 604 *result = std::move(exceptionDetails); 605 return Response::OK(); 606 } 607 608 Response InjectedScript::wrapEvaluateResult( 609 v8::MaybeLocal<v8::Value> maybeResultValue, const v8::TryCatch& tryCatch, 610 const String16& objectGroup, bool returnByValue, bool generatePreview, 611 std::unique_ptr<protocol::Runtime::RemoteObject>* result, 612 Maybe<protocol::Runtime::ExceptionDetails>* exceptionDetails) { 613 v8::Local<v8::Value> resultValue; 614 if (!tryCatch.HasCaught()) { 615 if (!maybeResultValue.ToLocal(&resultValue)) 616 return Response::InternalError(); 617 Response response = wrapObject(resultValue, objectGroup, returnByValue, 618 generatePreview, result); 619 if (!response.isSuccess()) return response; 620 if (objectGroup == "console") { 621 m_lastEvaluationResult.Reset(m_context->isolate(), resultValue); 622 m_lastEvaluationResult.AnnotateStrongRetainer(kGlobalHandleLabel); 623 } 624 } else { 625 if (tryCatch.HasTerminated() || !tryCatch.CanContinue()) { 626 return Response::Error("Execution was terminated"); 627 } 628 v8::Local<v8::Value> exception = tryCatch.Exception(); 629 Response response = 630 wrapObject(exception, objectGroup, false, 631 generatePreview && !exception->IsNativeError(), result); 632 if (!response.isSuccess()) return response; 633 // We send exception in result for compatibility reasons, even though it's 634 // accessible through exceptionDetails.exception. 635 response = createExceptionDetails(tryCatch, objectGroup, generatePreview, 636 exceptionDetails); 637 if (!response.isSuccess()) return response; 638 } 639 return Response::OK(); 640 } 641 642 v8::Local<v8::Object> InjectedScript::commandLineAPI() { 643 if (m_commandLineAPI.IsEmpty()) { 644 m_commandLineAPI.Reset( 645 m_context->isolate(), 646 m_context->inspector()->console()->createCommandLineAPI( 647 m_context->context(), m_sessionId)); 648 m_commandLineAPI.AnnotateStrongRetainer(kGlobalHandleLabel); 649 } 650 return m_commandLineAPI.Get(m_context->isolate()); 651 } 652 653 InjectedScript::Scope::Scope(V8InspectorSessionImpl* session) 654 : m_inspector(session->inspector()), 655 m_injectedScript(nullptr), 656 m_handleScope(m_inspector->isolate()), 657 m_tryCatch(m_inspector->isolate()), 658 m_ignoreExceptionsAndMuteConsole(false), 659 m_previousPauseOnExceptionsState(v8::debug::NoBreakOnException), 660 m_userGesture(false), 661 m_allowEval(false), 662 m_contextGroupId(session->contextGroupId()), 663 m_sessionId(session->sessionId()) {} 664 665 Response InjectedScript::Scope::initialize() { 666 cleanup(); 667 V8InspectorSessionImpl* session = 668 m_inspector->sessionById(m_contextGroupId, m_sessionId); 669 if (!session) return Response::InternalError(); 670 Response response = findInjectedScript(session); 671 if (!response.isSuccess()) return response; 672 m_context = m_injectedScript->context()->context(); 673 m_context->Enter(); 674 if (m_allowEval) m_context->AllowCodeGenerationFromStrings(true); 675 return Response::OK(); 676 } 677 678 void InjectedScript::Scope::installCommandLineAPI() { 679 DCHECK(m_injectedScript && !m_context.IsEmpty() && 680 !m_commandLineAPIScope.get()); 681 m_commandLineAPIScope.reset(new V8Console::CommandLineAPIScope( 682 m_context, m_injectedScript->commandLineAPI(), m_context->Global())); 683 } 684 685 void InjectedScript::Scope::ignoreExceptionsAndMuteConsole() { 686 DCHECK(!m_ignoreExceptionsAndMuteConsole); 687 m_ignoreExceptionsAndMuteConsole = true; 688 m_inspector->client()->muteMetrics(m_contextGroupId); 689 m_inspector->muteExceptions(m_contextGroupId); 690 m_previousPauseOnExceptionsState = 691 setPauseOnExceptionsState(v8::debug::NoBreakOnException); 692 } 693 694 v8::debug::ExceptionBreakState InjectedScript::Scope::setPauseOnExceptionsState( 695 v8::debug::ExceptionBreakState newState) { 696 if (!m_inspector->debugger()->enabled()) return newState; 697 v8::debug::ExceptionBreakState presentState = 698 m_inspector->debugger()->getPauseOnExceptionsState(); 699 if (presentState != newState) 700 m_inspector->debugger()->setPauseOnExceptionsState(newState); 701 return presentState; 702 } 703 704 void InjectedScript::Scope::pretendUserGesture() { 705 DCHECK(!m_userGesture); 706 m_userGesture = true; 707 m_inspector->client()->beginUserGesture(); 708 } 709 710 void InjectedScript::Scope::allowCodeGenerationFromStrings() { 711 DCHECK(!m_allowEval); 712 if (m_context->IsCodeGenerationFromStringsAllowed()) return; 713 m_allowEval = true; 714 m_context->AllowCodeGenerationFromStrings(true); 715 } 716 717 void InjectedScript::Scope::cleanup() { 718 m_commandLineAPIScope.reset(); 719 if (!m_context.IsEmpty()) { 720 if (m_allowEval) m_context->AllowCodeGenerationFromStrings(false); 721 m_context->Exit(); 722 m_context.Clear(); 723 } 724 } 725 726 InjectedScript::Scope::~Scope() { 727 if (m_ignoreExceptionsAndMuteConsole) { 728 setPauseOnExceptionsState(m_previousPauseOnExceptionsState); 729 m_inspector->client()->unmuteMetrics(m_contextGroupId); 730 m_inspector->unmuteExceptions(m_contextGroupId); 731 } 732 if (m_userGesture) m_inspector->client()->endUserGesture(); 733 cleanup(); 734 } 735 736 InjectedScript::ContextScope::ContextScope(V8InspectorSessionImpl* session, 737 int executionContextId) 738 : InjectedScript::Scope(session), 739 m_executionContextId(executionContextId) {} 740 741 InjectedScript::ContextScope::~ContextScope() {} 742 743 Response InjectedScript::ContextScope::findInjectedScript( 744 V8InspectorSessionImpl* session) { 745 return session->findInjectedScript(m_executionContextId, m_injectedScript); 746 } 747 748 InjectedScript::ObjectScope::ObjectScope(V8InspectorSessionImpl* session, 749 const String16& remoteObjectId) 750 : InjectedScript::Scope(session), m_remoteObjectId(remoteObjectId) {} 751 752 InjectedScript::ObjectScope::~ObjectScope() {} 753 754 Response InjectedScript::ObjectScope::findInjectedScript( 755 V8InspectorSessionImpl* session) { 756 std::unique_ptr<RemoteObjectId> remoteId; 757 Response response = RemoteObjectId::parse(m_remoteObjectId, &remoteId); 758 if (!response.isSuccess()) return response; 759 InjectedScript* injectedScript = nullptr; 760 response = session->findInjectedScript(remoteId.get(), injectedScript); 761 if (!response.isSuccess()) return response; 762 m_objectGroupName = injectedScript->objectGroupName(*remoteId); 763 response = injectedScript->findObject(*remoteId, &m_object); 764 if (!response.isSuccess()) return response; 765 m_injectedScript = injectedScript; 766 return Response::OK(); 767 } 768 769 InjectedScript::CallFrameScope::CallFrameScope(V8InspectorSessionImpl* session, 770 const String16& remoteObjectId) 771 : InjectedScript::Scope(session), m_remoteCallFrameId(remoteObjectId) {} 772 773 InjectedScript::CallFrameScope::~CallFrameScope() {} 774 775 Response InjectedScript::CallFrameScope::findInjectedScript( 776 V8InspectorSessionImpl* session) { 777 std::unique_ptr<RemoteCallFrameId> remoteId; 778 Response response = RemoteCallFrameId::parse(m_remoteCallFrameId, &remoteId); 779 if (!response.isSuccess()) return response; 780 m_frameOrdinal = static_cast<size_t>(remoteId->frameOrdinal()); 781 return session->findInjectedScript(remoteId.get(), m_injectedScript); 782 } 783 784 InjectedScript* InjectedScript::fromInjectedScriptHost( 785 v8::Isolate* isolate, v8::Local<v8::Object> injectedScriptObject) { 786 v8::HandleScope handleScope(isolate); 787 v8::Local<v8::Context> context = isolate->GetCurrentContext(); 788 v8::Local<v8::Private> privateKey = v8::Private::ForApi( 789 isolate, v8::String::NewFromUtf8(isolate, privateKeyName, 790 v8::NewStringType::kInternalized) 791 .ToLocalChecked()); 792 v8::Local<v8::Value> value = 793 injectedScriptObject->GetPrivate(context, privateKey).ToLocalChecked(); 794 DCHECK(value->IsExternal()); 795 v8::Local<v8::External> external = value.As<v8::External>(); 796 return static_cast<InjectedScript*>(external->Value()); 797 } 798 799 int InjectedScript::bindObject(v8::Local<v8::Value> value, 800 const String16& groupName) { 801 if (m_lastBoundObjectId <= 0) m_lastBoundObjectId = 1; 802 int id = m_lastBoundObjectId++; 803 m_idToWrappedObject[id].Reset(m_context->isolate(), value); 804 m_idToWrappedObject[id].AnnotateStrongRetainer(kGlobalHandleLabel); 805 806 if (!groupName.isEmpty() && id > 0) { 807 m_idToObjectGroupName[id] = groupName; 808 m_nameToObjectGroup[groupName].push_back(id); 809 } 810 return id; 811 } 812 813 void InjectedScript::unbindObject(int id) { 814 m_idToWrappedObject.erase(id); 815 m_idToObjectGroupName.erase(id); 816 } 817 818 } // namespace v8_inspector 819