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, "nullifyPrototype",
     59                       V8InjectedScriptHost::nullifyPrototypeCallback,
     60                       debuggerExternal);
     61   setFunctionProperty(context, injectedScriptHost, "internalConstructorName",
     62                       V8InjectedScriptHost::internalConstructorNameCallback,
     63                       debuggerExternal);
     64   setFunctionProperty(
     65       context, injectedScriptHost, "formatAccessorsAsProperties",
     66       V8InjectedScriptHost::formatAccessorsAsProperties, debuggerExternal);
     67   setFunctionProperty(context, injectedScriptHost, "subtype",
     68                       V8InjectedScriptHost::subtypeCallback, debuggerExternal);
     69   setFunctionProperty(context, injectedScriptHost, "getInternalProperties",
     70                       V8InjectedScriptHost::getInternalPropertiesCallback,
     71                       debuggerExternal);
     72   setFunctionProperty(context, injectedScriptHost, "objectHasOwnProperty",
     73                       V8InjectedScriptHost::objectHasOwnPropertyCallback,
     74                       debuggerExternal);
     75   setFunctionProperty(context, injectedScriptHost, "bind",
     76                       V8InjectedScriptHost::bindCallback, debuggerExternal);
     77   setFunctionProperty(context, injectedScriptHost, "proxyTargetValue",
     78                       V8InjectedScriptHost::proxyTargetValueCallback,
     79                       debuggerExternal);
     80   return injectedScriptHost;
     81 }
     82 
     83 void V8InjectedScriptHost::nullifyPrototypeCallback(
     84     const v8::FunctionCallbackInfo<v8::Value>& info) {
     85   CHECK(info.Length() == 1 && info[0]->IsObject());
     86   v8::Isolate* isolate = info.GetIsolate();
     87   info[0]
     88       .As<v8::Object>()
     89       ->SetPrototype(isolate->GetCurrentContext(), v8::Null(isolate))
     90       .ToChecked();
     91 }
     92 
     93 void V8InjectedScriptHost::internalConstructorNameCallback(
     94     const v8::FunctionCallbackInfo<v8::Value>& info) {
     95   if (info.Length() < 1 || !info[0]->IsObject()) return;
     96 
     97   v8::Local<v8::Object> object = info[0].As<v8::Object>();
     98   info.GetReturnValue().Set(object->GetConstructorName());
     99 }
    100 
    101 void V8InjectedScriptHost::formatAccessorsAsProperties(
    102     const v8::FunctionCallbackInfo<v8::Value>& info) {
    103   DCHECK_EQ(info.Length(), 2);
    104   info.GetReturnValue().Set(false);
    105   if (!info[1]->IsFunction()) return;
    106   // Check that function is user-defined.
    107   if (info[1].As<v8::Function>()->ScriptId() != v8::UnboundScript::kNoScriptId)
    108     return;
    109   info.GetReturnValue().Set(
    110       unwrapInspector(info)->client()->formatAccessorsAsProperties(info[0]));
    111 }
    112 
    113 void V8InjectedScriptHost::subtypeCallback(
    114     const v8::FunctionCallbackInfo<v8::Value>& info) {
    115   if (info.Length() < 1) return;
    116 
    117   v8::Isolate* isolate = info.GetIsolate();
    118   v8::Local<v8::Value> value = info[0];
    119   if (value->IsObject()) {
    120     v8::Local<v8::Value> internalType = v8InternalValueTypeFrom(
    121         isolate->GetCurrentContext(), v8::Local<v8::Object>::Cast(value));
    122     if (internalType->IsString()) {
    123       info.GetReturnValue().Set(internalType);
    124       return;
    125     }
    126   }
    127   if (value->IsArray() || value->IsArgumentsObject()) {
    128     info.GetReturnValue().Set(toV8StringInternalized(isolate, "array"));
    129     return;
    130   }
    131   if (value->IsTypedArray()) {
    132     info.GetReturnValue().Set(toV8StringInternalized(isolate, "typedarray"));
    133     return;
    134   }
    135   if (value->IsDate()) {
    136     info.GetReturnValue().Set(toV8StringInternalized(isolate, "date"));
    137     return;
    138   }
    139   if (value->IsRegExp()) {
    140     info.GetReturnValue().Set(toV8StringInternalized(isolate, "regexp"));
    141     return;
    142   }
    143   if (value->IsMap() || value->IsWeakMap()) {
    144     info.GetReturnValue().Set(toV8StringInternalized(isolate, "map"));
    145     return;
    146   }
    147   if (value->IsSet() || value->IsWeakSet()) {
    148     info.GetReturnValue().Set(toV8StringInternalized(isolate, "set"));
    149     return;
    150   }
    151   if (value->IsMapIterator() || value->IsSetIterator()) {
    152     info.GetReturnValue().Set(toV8StringInternalized(isolate, "iterator"));
    153     return;
    154   }
    155   if (value->IsGeneratorObject()) {
    156     info.GetReturnValue().Set(toV8StringInternalized(isolate, "generator"));
    157     return;
    158   }
    159   if (value->IsNativeError()) {
    160     info.GetReturnValue().Set(toV8StringInternalized(isolate, "error"));
    161     return;
    162   }
    163   if (value->IsProxy()) {
    164     info.GetReturnValue().Set(toV8StringInternalized(isolate, "proxy"));
    165     return;
    166   }
    167   if (value->IsPromise()) {
    168     info.GetReturnValue().Set(toV8StringInternalized(isolate, "promise"));
    169     return;
    170   }
    171   std::unique_ptr<StringBuffer> subtype =
    172       unwrapInspector(info)->client()->valueSubtype(value);
    173   if (subtype) {
    174     info.GetReturnValue().Set(toV8String(isolate, subtype->string()));
    175     return;
    176   }
    177 }
    178 
    179 void V8InjectedScriptHost::getInternalPropertiesCallback(
    180     const v8::FunctionCallbackInfo<v8::Value>& info) {
    181   if (info.Length() < 1) return;
    182 
    183   std::unordered_set<String16> allowedProperties;
    184   if (info[0]->IsBooleanObject() || info[0]->IsNumberObject() ||
    185       info[0]->IsStringObject() || info[0]->IsSymbolObject()) {
    186     allowedProperties.insert(String16("[[PrimitiveValue]]"));
    187   } else if (info[0]->IsPromise()) {
    188     allowedProperties.insert(String16("[[PromiseStatus]]"));
    189     allowedProperties.insert(String16("[[PromiseValue]]"));
    190   } else if (info[0]->IsGeneratorObject()) {
    191     allowedProperties.insert(String16("[[GeneratorStatus]]"));
    192   } else if (info[0]->IsMapIterator() || info[0]->IsSetIterator()) {
    193     allowedProperties.insert(String16("[[IteratorHasMore]]"));
    194     allowedProperties.insert(String16("[[IteratorIndex]]"));
    195     allowedProperties.insert(String16("[[IteratorKind]]"));
    196     allowedProperties.insert(String16("[[Entries]]"));
    197   } else if (info[0]->IsMap() || info[0]->IsWeakMap() || info[0]->IsSet() ||
    198              info[0]->IsWeakSet()) {
    199     allowedProperties.insert(String16("[[Entries]]"));
    200   }
    201   if (!allowedProperties.size()) return;
    202 
    203   v8::Isolate* isolate = info.GetIsolate();
    204   v8::Local<v8::Array> allProperties;
    205   if (!unwrapInspector(info)
    206            ->debugger()
    207            ->internalProperties(isolate->GetCurrentContext(), info[0])
    208            .ToLocal(&allProperties) ||
    209       !allProperties->IsArray() || allProperties->Length() % 2 != 0)
    210     return;
    211 
    212   {
    213     v8::Local<v8::Context> context = isolate->GetCurrentContext();
    214     v8::TryCatch tryCatch(isolate);
    215     v8::Isolate::DisallowJavascriptExecutionScope throwJs(
    216         isolate,
    217         v8::Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
    218 
    219     v8::Local<v8::Array> properties = v8::Array::New(isolate);
    220     if (tryCatch.HasCaught()) return;
    221 
    222     uint32_t outputIndex = 0;
    223     for (uint32_t i = 0; i < allProperties->Length(); i += 2) {
    224       v8::Local<v8::Value> key;
    225       if (!allProperties->Get(context, i).ToLocal(&key)) continue;
    226       if (tryCatch.HasCaught()) {
    227         tryCatch.Reset();
    228         continue;
    229       }
    230       String16 keyString = toProtocolStringWithTypeCheck(key);
    231       if (keyString.isEmpty() ||
    232           allowedProperties.find(keyString) == allowedProperties.end())
    233         continue;
    234       v8::Local<v8::Value> value;
    235       if (!allProperties->Get(context, i + 1).ToLocal(&value)) continue;
    236       if (tryCatch.HasCaught()) {
    237         tryCatch.Reset();
    238         continue;
    239       }
    240       createDataProperty(context, properties, outputIndex++, key);
    241       createDataProperty(context, properties, outputIndex++, value);
    242     }
    243     info.GetReturnValue().Set(properties);
    244   }
    245 }
    246 
    247 void V8InjectedScriptHost::objectHasOwnPropertyCallback(
    248     const v8::FunctionCallbackInfo<v8::Value>& info) {
    249   if (info.Length() < 2 || !info[0]->IsObject() || !info[1]->IsString()) return;
    250   bool result = info[0]
    251                     .As<v8::Object>()
    252                     ->HasOwnProperty(info.GetIsolate()->GetCurrentContext(),
    253                                      v8::Local<v8::String>::Cast(info[1]))
    254                     .FromMaybe(false);
    255   info.GetReturnValue().Set(v8::Boolean::New(info.GetIsolate(), result));
    256 }
    257 
    258 void V8InjectedScriptHost::bindCallback(
    259     const v8::FunctionCallbackInfo<v8::Value>& info) {
    260   if (info.Length() < 2 || !info[1]->IsString()) return;
    261   InjectedScriptNative* injectedScriptNative =
    262       InjectedScriptNative::fromInjectedScriptHost(info.GetIsolate(),
    263                                                    info.Holder());
    264   if (!injectedScriptNative) return;
    265 
    266   v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext();
    267   v8::Local<v8::String> v8groupName =
    268       info[1]->ToString(context).ToLocalChecked();
    269   String16 groupName = toProtocolStringWithTypeCheck(v8groupName);
    270   int id = injectedScriptNative->bind(info[0], groupName);
    271   info.GetReturnValue().Set(id);
    272 }
    273 
    274 void V8InjectedScriptHost::proxyTargetValueCallback(
    275     const v8::FunctionCallbackInfo<v8::Value>& info) {
    276   if (info.Length() != 1 || !info[0]->IsProxy()) {
    277     UNREACHABLE();
    278     return;
    279   }
    280   v8::Local<v8::Object> target = info[0].As<v8::Proxy>();
    281   while (target->IsProxy())
    282     target = v8::Local<v8::Proxy>::Cast(target)->GetTarget();
    283   info.GetReturnValue().Set(target);
    284 }
    285 
    286 }  // namespace v8_inspector
    287