LCOV - code coverage report
Current view: top level - src/inspector - custom-preview.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 122 203 60.1 %
Date: 2019-04-19 Functions: 5 6 83.3 %

          Line data    Source code
       1             : // Copyright 2018 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/custom-preview.h"
       6             : 
       7             : #include "src/debug/debug-interface.h"
       8             : #include "src/inspector/injected-script.h"
       9             : #include "src/inspector/inspected-context.h"
      10             : #include "src/inspector/string-util.h"
      11             : #include "src/inspector/v8-console-message.h"
      12             : #include "src/inspector/v8-inspector-impl.h"
      13             : #include "src/inspector/v8-stack-trace-impl.h"
      14             : 
      15             : namespace v8_inspector {
      16             : 
      17             : using protocol::Runtime::CustomPreview;
      18             : 
      19             : namespace {
      20          40 : void reportError(v8::Local<v8::Context> context, const v8::TryCatch& tryCatch) {
      21             :   DCHECK(tryCatch.HasCaught());
      22          40 :   v8::Isolate* isolate = context->GetIsolate();
      23             :   V8InspectorImpl* inspector =
      24          40 :       static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate));
      25          40 :   int contextId = InspectedContext::contextId(context);
      26          40 :   int groupId = inspector->contextGroupId(contextId);
      27          80 :   v8::Local<v8::String> message = tryCatch.Message()->Get();
      28             :   v8::Local<v8::String> prefix =
      29          80 :       toV8String(isolate, "Custom Formatter Failed: ");
      30          40 :   message = v8::String::Concat(isolate, prefix, message);
      31             :   std::vector<v8::Local<v8::Value>> arguments;
      32          40 :   arguments.push_back(message);
      33             :   V8ConsoleMessageStorage* storage =
      34          40 :       inspector->ensureConsoleMessageStorage(groupId);
      35          40 :   if (!storage) return;
      36         200 :   storage->addMessage(V8ConsoleMessage::createForConsoleAPI(
      37             :       context, contextId, groupId, inspector,
      38          40 :       inspector->client()->currentTimeMS(), ConsoleAPIType::kError, arguments,
      39          40 :       String16(), nullptr));
      40             : }
      41             : 
      42           0 : void reportError(v8::Local<v8::Context> context, const v8::TryCatch& tryCatch,
      43             :                  const String16& message) {
      44           0 :   v8::Isolate* isolate = context->GetIsolate();
      45           0 :   isolate->ThrowException(toV8String(isolate, message));
      46           0 :   reportError(context, tryCatch);
      47           0 : }
      48             : 
      49          80 : InjectedScript* getInjectedScript(v8::Local<v8::Context> context,
      50             :                                   int sessionId) {
      51          80 :   v8::Isolate* isolate = context->GetIsolate();
      52             :   V8InspectorImpl* inspector =
      53          80 :       static_cast<V8InspectorImpl*>(v8::debug::GetInspector(isolate));
      54             :   InspectedContext* inspectedContext =
      55          80 :       inspector->getContext(InspectedContext::contextId(context));
      56          80 :   if (!inspectedContext) return nullptr;
      57          80 :   return inspectedContext->getInjectedScript(sessionId);
      58             : }
      59             : 
      60         125 : bool substituteObjectTags(int sessionId, const String16& groupName,
      61             :                           v8::Local<v8::Context> context,
      62             :                           v8::Local<v8::Array> jsonML, int maxDepth) {
      63         125 :   if (!jsonML->Length()) return true;
      64         125 :   v8::Isolate* isolate = context->GetIsolate();
      65         250 :   v8::TryCatch tryCatch(isolate);
      66             : 
      67         125 :   if (maxDepth <= 0) {
      68           0 :     reportError(context, tryCatch,
      69           0 :                 "Too deep hierarchy of inlined custom previews");
      70           0 :     return false;
      71             :   }
      72             : 
      73             :   v8::Local<v8::Value> firstValue;
      74         250 :   if (!jsonML->Get(context, 0).ToLocal(&firstValue)) {
      75           0 :     reportError(context, tryCatch);
      76           0 :     return false;
      77             :   }
      78         250 :   v8::Local<v8::String> objectLiteral = toV8String(isolate, "object");
      79         185 :   if (jsonML->Length() == 2 && firstValue->IsString() &&
      80          30 :       firstValue.As<v8::String>()->StringEquals(objectLiteral)) {
      81             :     v8::Local<v8::Value> attributesValue;
      82          60 :     if (!jsonML->Get(context, 1).ToLocal(&attributesValue)) {
      83           0 :       reportError(context, tryCatch);
      84           0 :       return false;
      85             :     }
      86          30 :     if (!attributesValue->IsObject()) {
      87           0 :       reportError(context, tryCatch, "attributes should be an Object");
      88           0 :       return false;
      89             :     }
      90             :     v8::Local<v8::Object> attributes = attributesValue.As<v8::Object>();
      91             :     v8::Local<v8::Value> originValue;
      92          60 :     if (!attributes->Get(context, objectLiteral).ToLocal(&originValue)) {
      93           0 :       reportError(context, tryCatch);
      94           0 :       return false;
      95             :     }
      96          30 :     if (originValue->IsUndefined()) {
      97           0 :       reportError(context, tryCatch,
      98           0 :                   "obligatory attribute \"object\" isn't specified");
      99           0 :       return false;
     100             :     }
     101             : 
     102             :     v8::Local<v8::Value> configValue;
     103         120 :     if (!attributes->Get(context, toV8String(isolate, "config"))
     104             :              .ToLocal(&configValue)) {
     105           0 :       reportError(context, tryCatch);
     106           0 :       return false;
     107             :     }
     108             : 
     109          30 :     InjectedScript* injectedScript = getInjectedScript(context, sessionId);
     110          30 :     if (!injectedScript) {
     111           0 :       reportError(context, tryCatch, "cannot find context with specified id");
     112           0 :       return false;
     113             :     }
     114          30 :     std::unique_ptr<protocol::Runtime::RemoteObject> wrapper;
     115             :     protocol::Response response =
     116             :         injectedScript->wrapObject(originValue, groupName, WrapMode::kNoPreview,
     117          60 :                                    configValue, maxDepth - 1, &wrapper);
     118          60 :     if (!response.isSuccess() || !wrapper) {
     119           0 :       reportError(context, tryCatch, "cannot wrap value");
     120           0 :       return false;
     121             :     }
     122             :     v8::Local<v8::Value> jsonWrapper;
     123          30 :     String16 serialized = wrapper->toJSON();
     124          60 :     if (!v8::JSON::Parse(context, toV8String(isolate, serialized))
     125             :              .ToLocal(&jsonWrapper)) {
     126           0 :       reportError(context, tryCatch, "cannot wrap value");
     127           0 :       return false;
     128             :     }
     129          60 :     if (jsonML->Set(context, 1, jsonWrapper).IsNothing()) {
     130           0 :       reportError(context, tryCatch);
     131           0 :       return false;
     132             :     }
     133             :   } else {
     134         915 :     for (uint32_t i = 0; i < jsonML->Length(); ++i) {
     135             :       v8::Local<v8::Value> value;
     136         820 :       if (!jsonML->Get(context, i).ToLocal(&value)) {
     137           0 :         reportError(context, tryCatch);
     138           0 :         return false;
     139             :       }
     140         440 :       if (value->IsArray() && value.As<v8::Array>()->Length() > 0 &&
     141          30 :           !substituteObjectTags(sessionId, groupName, context,
     142             :                                 value.As<v8::Array>(), maxDepth - 1)) {
     143             :         return false;
     144             :       }
     145             :     }
     146             :   }
     147             :   return true;
     148             : }
     149             : 
     150          35 : void bodyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
     151             :   v8::Isolate* isolate = info.GetIsolate();
     152          70 :   v8::TryCatch tryCatch(isolate);
     153          35 :   v8::Local<v8::Context> context = isolate->GetCurrentContext();
     154             :   v8::Local<v8::Object> bodyConfig = info.Data().As<v8::Object>();
     155             : 
     156             :   v8::Local<v8::Value> objectValue;
     157         140 :   if (!bodyConfig->Get(context, toV8String(isolate, "object"))
     158             :            .ToLocal(&objectValue)) {
     159           0 :     reportError(context, tryCatch);
     160           0 :     return;
     161             :   }
     162          35 :   if (!objectValue->IsObject()) {
     163           0 :     reportError(context, tryCatch, "object should be an Object");
     164           0 :     return;
     165             :   }
     166             :   v8::Local<v8::Object> object = objectValue.As<v8::Object>();
     167             : 
     168             :   v8::Local<v8::Value> formatterValue;
     169         140 :   if (!bodyConfig->Get(context, toV8String(isolate, "formatter"))
     170             :            .ToLocal(&formatterValue)) {
     171           0 :     reportError(context, tryCatch);
     172           0 :     return;
     173             :   }
     174          35 :   if (!formatterValue->IsObject()) {
     175           0 :     reportError(context, tryCatch, "formatter should be an Object");
     176           0 :     return;
     177             :   }
     178             :   v8::Local<v8::Object> formatter = formatterValue.As<v8::Object>();
     179             : 
     180             :   v8::Local<v8::Value> bodyValue;
     181         140 :   if (!formatter->Get(context, toV8String(isolate, "body"))
     182             :            .ToLocal(&bodyValue)) {
     183           0 :     reportError(context, tryCatch);
     184           0 :     return;
     185             :   }
     186          35 :   if (!bodyValue->IsFunction()) {
     187           0 :     reportError(context, tryCatch, "body should be a Function");
     188           0 :     return;
     189             :   }
     190             :   v8::Local<v8::Function> bodyFunction = bodyValue.As<v8::Function>();
     191             : 
     192             :   v8::Local<v8::Value> configValue;
     193         140 :   if (!bodyConfig->Get(context, toV8String(isolate, "config"))
     194             :            .ToLocal(&configValue)) {
     195           0 :     reportError(context, tryCatch);
     196           0 :     return;
     197             :   }
     198             : 
     199             :   v8::Local<v8::Value> sessionIdValue;
     200         140 :   if (!bodyConfig->Get(context, toV8String(isolate, "sessionId"))
     201             :            .ToLocal(&sessionIdValue)) {
     202           0 :     reportError(context, tryCatch);
     203           0 :     return;
     204             :   }
     205          35 :   if (!sessionIdValue->IsInt32()) {
     206           0 :     reportError(context, tryCatch, "sessionId should be an Int32");
     207           0 :     return;
     208             :   }
     209             : 
     210             :   v8::Local<v8::Value> groupNameValue;
     211         140 :   if (!bodyConfig->Get(context, toV8String(isolate, "groupName"))
     212             :            .ToLocal(&groupNameValue)) {
     213           0 :     reportError(context, tryCatch);
     214           0 :     return;
     215             :   }
     216          35 :   if (!groupNameValue->IsString()) {
     217           0 :     reportError(context, tryCatch, "groupName should be a string");
     218           0 :     return;
     219             :   }
     220             : 
     221             :   v8::Local<v8::Value> formattedValue;
     222          35 :   v8::Local<v8::Value> args[] = {object, configValue};
     223          70 :   if (!bodyFunction->Call(context, formatter, 2, args)
     224             :            .ToLocal(&formattedValue)) {
     225           0 :     reportError(context, tryCatch);
     226           0 :     return;
     227             :   }
     228          35 :   if (!formattedValue->IsArray()) {
     229           0 :     reportError(context, tryCatch, "body should return an Array");
     230           0 :     return;
     231             :   }
     232             :   v8::Local<v8::Array> jsonML = formattedValue.As<v8::Array>();
     233         105 :   if (jsonML->Length() &&
     234          35 :       !substituteObjectTags(
     235             :           sessionIdValue.As<v8::Int32>()->Value(),
     236         105 :           toProtocolString(isolate, groupNameValue.As<v8::String>()), context,
     237             :           jsonML, kMaxCustomPreviewDepth)) {
     238             :     return;
     239             :   }
     240             :   info.GetReturnValue().Set(jsonML);
     241             : }
     242             : }  // anonymous namespace
     243             : 
     244         170 : void generateCustomPreview(int sessionId, const String16& groupName,
     245             :                            v8::Local<v8::Context> context,
     246             :                            v8::Local<v8::Object> object,
     247             :                            v8::MaybeLocal<v8::Value> maybeConfig, int maxDepth,
     248             :                            std::unique_ptr<CustomPreview>* preview) {
     249         170 :   v8::Isolate* isolate = context->GetIsolate();
     250             :   v8::MicrotasksScope microtasksScope(isolate,
     251         240 :                                       v8::MicrotasksScope::kDoNotRunMicrotasks);
     252         240 :   v8::TryCatch tryCatch(isolate);
     253             : 
     254             :   v8::Local<v8::Value> configValue;
     255         170 :   if (!maybeConfig.ToLocal(&configValue)) configValue = v8::Undefined(isolate);
     256             : 
     257         170 :   v8::Local<v8::Object> global = context->Global();
     258             :   v8::Local<v8::Value> formattersValue;
     259         680 :   if (!global->Get(context, toV8String(isolate, "devtoolsFormatters"))
     260             :            .ToLocal(&formattersValue)) {
     261          10 :     reportError(context, tryCatch);
     262          10 :     return;
     263             :   }
     264         160 :   if (!formattersValue->IsArray()) return;
     265             :   v8::Local<v8::Array> formatters = formattersValue.As<v8::Array>();
     266         320 :   v8::Local<v8::String> headerLiteral = toV8String(isolate, "header");
     267         320 :   v8::Local<v8::String> hasBodyLiteral = toV8String(isolate, "hasBody");
     268         850 :   for (uint32_t i = 0; i < formatters->Length(); ++i) {
     269             :     v8::Local<v8::Value> formatterValue;
     270         870 :     if (!formatters->Get(context, i).ToLocal(&formatterValue)) {
     271          10 :       reportError(context, tryCatch);
     272         100 :       return;
     273             :     }
     274         425 :     if (!formatterValue->IsObject()) {
     275           0 :       reportError(context, tryCatch, "formatter should be an Object");
     276           0 :       return;
     277             :     }
     278             :     v8::Local<v8::Object> formatter = formatterValue.As<v8::Object>();
     279             : 
     280             :     v8::Local<v8::Value> headerValue;
     281         850 :     if (!formatter->Get(context, headerLiteral).ToLocal(&headerValue)) {
     282          10 :       reportError(context, tryCatch);
     283          10 :       return;
     284             :     }
     285         415 :     if (!headerValue->IsFunction()) {
     286           0 :       reportError(context, tryCatch, "header should be a Function");
     287           0 :       return;
     288             :     }
     289             :     v8::Local<v8::Function> headerFunction = headerValue.As<v8::Function>();
     290             : 
     291             :     v8::Local<v8::Value> formattedValue;
     292         415 :     v8::Local<v8::Value> args[] = {object, configValue};
     293         830 :     if (!headerFunction->Call(context, formatter, 2, args)
     294             :              .ToLocal(&formattedValue)) {
     295          10 :       reportError(context, tryCatch);
     296          10 :       return;
     297             :     }
     298         750 :     if (!formattedValue->IsArray()) continue;
     299             :     v8::Local<v8::Array> jsonML = formattedValue.As<v8::Array>();
     300             : 
     301             :     v8::Local<v8::Value> hasBodyFunctionValue;
     302         120 :     if (!formatter->Get(context, hasBodyLiteral)
     303             :              .ToLocal(&hasBodyFunctionValue)) {
     304           0 :       reportError(context, tryCatch);
     305           0 :       return;
     306             :     }
     307          60 :     if (!hasBodyFunctionValue->IsFunction()) continue;
     308             :     v8::Local<v8::Function> hasBodyFunction =
     309             :         hasBodyFunctionValue.As<v8::Function>();
     310             :     v8::Local<v8::Value> hasBodyValue;
     311         120 :     if (!hasBodyFunction->Call(context, formatter, 2, args)
     312             :              .ToLocal(&hasBodyValue)) {
     313           0 :       reportError(context, tryCatch);
     314           0 :       return;
     315             :     }
     316         120 :     bool hasBody = hasBodyValue->ToBoolean(isolate)->Value();
     317             : 
     318          60 :     if (jsonML->Length() && !substituteObjectTags(sessionId, groupName, context,
     319             :                                                   jsonML, maxDepth)) {
     320             :       return;
     321             :     }
     322             : 
     323             :     v8::Local<v8::String> header;
     324         120 :     if (!v8::JSON::Stringify(context, jsonML).ToLocal(&header)) {
     325           0 :       reportError(context, tryCatch);
     326           0 :       return;
     327             :     }
     328             : 
     329             :     v8::Local<v8::Function> bodyFunction;
     330          60 :     if (hasBody) {
     331          50 :       v8::Local<v8::Object> bodyConfig = v8::Object::New(isolate);
     332          50 :       if (bodyConfig
     333         100 :               ->CreateDataProperty(context, toV8String(isolate, "sessionId"),
     334         200 :                                    v8::Integer::New(isolate, sessionId))
     335             :               .IsNothing()) {
     336           0 :         reportError(context, tryCatch);
     337           0 :         return;
     338             :       }
     339          50 :       if (bodyConfig
     340         100 :               ->CreateDataProperty(context, toV8String(isolate, "formatter"),
     341         150 :                                    formatter)
     342             :               .IsNothing()) {
     343           0 :         reportError(context, tryCatch);
     344           0 :         return;
     345             :       }
     346          50 :       if (bodyConfig
     347         100 :               ->CreateDataProperty(context, toV8String(isolate, "groupName"),
     348         200 :                                    toV8String(isolate, groupName))
     349             :               .IsNothing()) {
     350           0 :         reportError(context, tryCatch);
     351           0 :         return;
     352             :       }
     353          50 :       if (bodyConfig
     354         100 :               ->CreateDataProperty(context, toV8String(isolate, "config"),
     355         150 :                                    configValue)
     356             :               .IsNothing()) {
     357           0 :         reportError(context, tryCatch);
     358           0 :         return;
     359             :       }
     360          50 :       if (bodyConfig
     361         100 :               ->CreateDataProperty(context, toV8String(isolate, "object"),
     362         150 :                                    object)
     363             :               .IsNothing()) {
     364           0 :         reportError(context, tryCatch);
     365           0 :         return;
     366             :       }
     367         100 :       if (!v8::Function::New(context, bodyCallback, bodyConfig)
     368             :                .ToLocal(&bodyFunction)) {
     369           0 :         reportError(context, tryCatch);
     370           0 :         return;
     371             :       }
     372             :     }
     373          60 :     *preview = CustomPreview::create()
     374         120 :                    .setHeader(toProtocolString(isolate, header))
     375             :                    .build();
     376          60 :     if (!bodyFunction.IsEmpty()) {
     377          50 :       InjectedScript* injectedScript = getInjectedScript(context, sessionId);
     378          50 :       if (!injectedScript) {
     379           0 :         reportError(context, tryCatch, "cannot find context with specified id");
     380           0 :         return;
     381             :       }
     382             :       (*preview)->setBodyGetterId(
     383         150 :           injectedScript->bindObject(bodyFunction, groupName));
     384             :     }
     385             :     return;
     386             :   }
     387             : }
     388             : }  // namespace v8_inspector

Generated by: LCOV version 1.10