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