Home | History | Annotate | Download | only in inspector
      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