Home | History | Annotate | Download | only in inspector
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/inspector/v8-injected-script-host.h"
      6 
      7 #include "src/base/macros.h"
      8 #include "src/inspector/injected-script-native.h"
      9 #include "src/inspector/string-util.h"
     10 #include "src/inspector/v8-debugger.h"
     11 #include "src/inspector/v8-inspector-impl.h"
     12 #include "src/inspector/v8-internal-value-type.h"
     13 #include "src/inspector/v8-value-copier.h"
     14 
     15 #include "include/v8-inspector.h"
     16 
     17 namespace v8_inspector {
     18 
     19 namespace {
     20 
     21 void setFunctionProperty(v8::Local<v8::Context> context,
     22                          v8::Local<v8::Object> obj, const char* name,
     23                          v8::FunctionCallback callback,
     24                          v8::Local<v8::External> external) {
     25   v8::Local<v8::String> funcName =
     26       toV8StringInternalized(context->GetIsolate(), name);
     27   v8::Local<v8::Function> func;
     28   if (!v8::Function::New(context, callback, external, 0,
     29                          v8::ConstructorBehavior::kThrow)
     30            .ToLocal(&func))
     31     return;
     32   func->SetName(funcName);
     33   createDataProperty(context, obj, funcName, func);
     34 }
     35 
     36 V8InspectorImpl* unwrapInspector(
     37     const v8::FunctionCallbackInfo<v8::Value>& info) {
     38   DCHECK(!info.Data().IsEmpty());
     39   DCHECK(info.Data()->IsExternal());
     40   V8InspectorImpl* inspector =
     41       static_cast<V8InspectorImpl*>(info.Data().As<v8::External>()->Value());
     42   DCHECK(inspector);
     43   return inspector;
     44 }
     45 
     46 }  // namespace
     47 
     48 v8::Local<v8::Object> V8InjectedScriptHost::create(
     49     v8::Local<v8::Context> context, V8InspectorImpl* inspector) {
     50   v8::Isolate* isolate = inspector->isolate();
     51   v8::Local<v8::Object> injectedScriptHost = v8::Object::New(isolate);
     52   bool success = injectedScriptHost->SetPrototype(context, v8::Null(isolate))
     53                      .FromMaybe(false);
     54   DCHECK(success);
     55   USE(success);
     56   v8::Local<v8::External> debuggerExternal =
     57       v8::External::New(isolate, inspector);
     58   setFunctionProperty(context, injectedScriptHost, "internalConstructorName",
     59                       V8InjectedScriptHost::internalConstructorNameCallback,
     60                       debuggerExternal);
     61   setFunctionProperty(
     62       context, injectedScriptHost, "formatAccessorsAsProperties",
     63       V8InjectedScriptHost::formatAccessorsAsProperties, debuggerExternal);
     64   setFunctionProperty(context, injectedScriptHost, "subtype",
     65                       V8InjectedScriptHost::subtypeCallback, debuggerExternal);
     66   setFunctionProperty(context, injectedScriptHost, "getInternalProperties",
     67                       V8InjectedScriptHost::getInternalPropertiesCallback,
     68                       debuggerExternal);
     69   setFunctionProperty(context, injectedScriptHost, "objectHasOwnProperty",
     70                       V8InjectedScriptHost::objectHasOwnPropertyCallback,
     71                       debuggerExternal);
     72   setFunctionProperty(context, injectedScriptHost, "bind",
     73                       V8InjectedScriptHost::bindCallback, debuggerExternal);
     74   setFunctionProperty(context, injectedScriptHost, "proxyTargetValue",
     75                       V8InjectedScriptHost::proxyTargetValueCallback,
     76                       debuggerExternal);
     77   return injectedScriptHost;
     78 }
     79 
     80 void V8InjectedScriptHost::internalConstructorNameCallback(
     81     const v8::FunctionCallbackInfo<v8::Value>& info) {
     82   if (info.Length() < 1 || !info[0]->IsObject()) return;
     83 
     84   v8::Local<v8::Object> object = info[0].As<v8::Object>();
     85   info.GetReturnValue().Set(object->GetConstructorName());
     86 }
     87 
     88 void V8InjectedScriptHost::formatAccessorsAsProperties(
     89     const v8::FunctionCallbackInfo<v8::Value>& info) {
     90   DCHECK_EQ(info.Length(), 2);
     91   info.GetReturnValue().Set(false);
     92   if (!info[1]->IsFunction()) return;
     93   // Check that function is user-defined.
     94   if (info[1].As<v8::Function>()->ScriptId() != v8::UnboundScript::kNoScriptId)
     95     return;
     96   info.GetReturnValue().Set(
     97       unwrapInspector(info)->client()->formatAccessorsAsProperties(info[0]));
     98 }
     99 
    100 void V8InjectedScriptHost::subtypeCallback(
    101     const v8::FunctionCallbackInfo<v8::Value>& info) {
    102   if (info.Length() < 1) return;
    103 
    104   v8::Isolate* isolate = info.GetIsolate();
    105   v8::Local<v8::Value> value = info[0];
    106   if (value->IsObject()) {
    107     v8::Local<v8::Value> internalType = v8InternalValueTypeFrom(
    108         isolate->GetCurrentContext(), v8::Local<v8::Object>::Cast(value));
    109     if (internalType->IsString()) {
    110       info.GetReturnValue().Set(internalType);
    111       return;
    112     }
    113   }
    114   if (value->IsArray() || value->IsArgumentsObject()) {
    115     info.GetReturnValue().Set(toV8StringInternalized(isolate, "array"));
    116     return;
    117   }
    118   if (value->IsTypedArray()) {
    119     info.GetReturnValue().Set(toV8StringInternalized(isolate, "typedarray"));
    120     return;
    121   }
    122   if (value->IsDate()) {
    123     info.GetReturnValue().Set(toV8StringInternalized(isolate, "date"));
    124     return;
    125   }
    126   if (value->IsRegExp()) {
    127     info.GetReturnValue().Set(toV8StringInternalized(isolate, "regexp"));
    128     return;
    129   }
    130   if (value->IsMap() || value->IsWeakMap()) {
    131     info.GetReturnValue().Set(toV8StringInternalized(isolate, "map"));
    132     return;
    133   }
    134   if (value->IsSet() || value->IsWeakSet()) {
    135     info.GetReturnValue().Set(toV8StringInternalized(isolate, "set"));
    136     return;
    137   }
    138   if (value->IsMapIterator() || value->IsSetIterator()) {
    139     info.GetReturnValue().Set(toV8StringInternalized(isolate, "iterator"));
    140     return;
    141   }
    142   if (value->IsGeneratorObject()) {
    143     info.GetReturnValue().Set(toV8StringInternalized(isolate, "generator"));
    144     return;
    145   }
    146   if (value->IsNativeError()) {
    147     info.GetReturnValue().Set(toV8StringInternalized(isolate, "error"));
    148     return;
    149   }
    150   if (value->IsProxy()) {
    151     info.GetReturnValue().Set(toV8StringInternalized(isolate, "proxy"));
    152     return;
    153   }
    154   if (value->IsPromise()) {
    155     info.GetReturnValue().Set(toV8StringInternalized(isolate, "promise"));
    156     return;
    157   }
    158   std::unique_ptr<StringBuffer> subtype =
    159       unwrapInspector(info)->client()->valueSubtype(value);
    160   if (subtype) {
    161     info.GetReturnValue().Set(toV8String(isolate, subtype->string()));
    162     return;
    163   }
    164 }
    165 
    166 void V8InjectedScriptHost::getInternalPropertiesCallback(
    167     const v8::FunctionCallbackInfo<v8::Value>& info) {
    168   if (info.Length() < 1) return;
    169 
    170   std::unordered_set<String16> allowedProperties;
    171   if (info[0]->IsBooleanObject() || info[0]->IsNumberObject() ||
    172       info[0]->IsStringObject() || info[0]->IsSymbolObject()) {
    173     allowedProperties.insert(String16("[[PrimitiveValue]]"));
    174   } else if (info[0]->IsPromise()) {
    175     allowedProperties.insert(String16("[[PromiseStatus]]"));
    176     allowedProperties.insert(String16("[[PromiseValue]]"));
    177   } else if (info[0]->IsGeneratorObject()) {
    178     allowedProperties.insert(String16("[[GeneratorStatus]]"));
    179   } else if (info[0]->IsMapIterator() || info[0]->IsSetIterator()) {
    180     allowedProperties.insert(String16("[[IteratorHasMore]]"));
    181     allowedProperties.insert(String16("[[IteratorIndex]]"));
    182     allowedProperties.insert(String16("[[IteratorKind]]"));
    183     allowedProperties.insert(String16("[[Entries]]"));
    184   } else if (info[0]->IsMap() || info[0]->IsWeakMap() || info[0]->IsSet() ||
    185              info[0]->IsWeakSet()) {
    186     allowedProperties.insert(String16("[[Entries]]"));
    187   }
    188   if (!allowedProperties.size()) return;
    189 
    190   v8::Isolate* isolate = info.GetIsolate();
    191   v8::Local<v8::Array> allProperties;
    192   if (!unwrapInspector(info)
    193            ->debugger()
    194            ->internalProperties(isolate->GetCurrentContext(), info[0])
    195            .ToLocal(&allProperties) ||
    196       !allProperties->IsArray() || allProperties->Length() % 2 != 0)
    197     return;
    198 
    199   {
    200     v8::Local<v8::Context> context = isolate->GetCurrentContext();
    201     v8::TryCatch tryCatch(isolate);
    202     v8::Isolate::DisallowJavascriptExecutionScope throwJs(
    203         isolate,
    204         v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
    205 
    206     v8::Local<v8::Array> properties = v8::Array::New(isolate);
    207     if (tryCatch.HasCaught()) return;
    208 
    209     uint32_t outputIndex = 0;
    210     for (uint32_t i = 0; i < allProperties->Length(); i += 2) {
    211       v8::Local<v8::Value> key;
    212       if (!allProperties->Get(context, i).ToLocal(&key)) continue;
    213       if (tryCatch.HasCaught()) {
    214         tryCatch.Reset();
    215         continue;
    216       }
    217       String16 keyString = toProtocolStringWithTypeCheck(key);
    218       if (keyString.isEmpty() ||
    219           allowedProperties.find(keyString) == allowedProperties.end())
    220         continue;
    221       v8::Local<v8::Value> value;
    222       if (!allProperties->Get(context, i + 1).ToLocal(&value)) continue;
    223       if (tryCatch.HasCaught()) {
    224         tryCatch.Reset();
    225         continue;
    226       }
    227       createDataProperty(context, properties, outputIndex++, key);
    228       createDataProperty(context, properties, outputIndex++, value);
    229     }
    230     info.GetReturnValue().Set(properties);
    231   }
    232 }
    233 
    234 void V8InjectedScriptHost::objectHasOwnPropertyCallback(
    235     const v8::FunctionCallbackInfo<v8::Value>& info) {
    236   if (info.Length() < 2 || !info[0]->IsObject() || !info[1]->IsString()) return;
    237   bool result = info[0]
    238                     .As<v8::Object>()
    239                     ->HasOwnProperty(info.GetIsolate()->GetCurrentContext(),
    240                                      v8::Local<v8::String>::Cast(info[1]))
    241                     .FromMaybe(false);
    242   info.GetReturnValue().Set(v8::Boolean::New(info.GetIsolate(), result));
    243 }
    244 
    245 void V8InjectedScriptHost::bindCallback(
    246     const v8::FunctionCallbackInfo<v8::Value>& info) {
    247   if (info.Length() < 2 || !info[1]->IsString()) return;
    248   InjectedScriptNative* injectedScriptNative =
    249       InjectedScriptNative::fromInjectedScriptHost(info.GetIsolate(),
    250                                                    info.Holder());
    251   if (!injectedScriptNative) return;
    252 
    253   v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
    254   v8::Local<v8::String> v8groupName =
    255       info[1]->ToString(context).ToLocalChecked();
    256   String16 groupName = toProtocolStringWithTypeCheck(v8groupName);
    257   int id = injectedScriptNative->bind(info[0], groupName);
    258   info.GetReturnValue().Set(id);
    259 }
    260 
    261 void V8InjectedScriptHost::proxyTargetValueCallback(
    262     const v8::FunctionCallbackInfo<v8::Value>& info) {
    263   if (info.Length() != 1 || !info[0]->IsProxy()) {
    264     UNREACHABLE();
    265     return;
    266   }
    267   v8::Local<v8::Object> target = info[0].As<v8::Proxy>();
    268   while (target->IsProxy())
    269     target = v8::Local<v8::Proxy>::Cast(target)->GetTarget();
    270   info.GetReturnValue().Set(target);
    271 }
    272 
    273 }  // namespace v8_inspector
    274