OLD | NEW |
| (Empty) |
1 // Copyright 2016 The Chromium 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 "platform/v8_inspector/V8Console.h" | |
6 | |
7 #include "platform/v8_inspector/InjectedScript.h" | |
8 #include "platform/v8_inspector/InspectedContext.h" | |
9 #include "platform/v8_inspector/StringUtil.h" | |
10 #include "platform/v8_inspector/V8Compat.h" | |
11 #include "platform/v8_inspector/V8ConsoleMessage.h" | |
12 #include "platform/v8_inspector/V8DebuggerAgentImpl.h" | |
13 #include "platform/v8_inspector/V8InspectorImpl.h" | |
14 #include "platform/v8_inspector/V8InspectorSessionImpl.h" | |
15 #include "platform/v8_inspector/V8ProfilerAgentImpl.h" | |
16 #include "platform/v8_inspector/V8RuntimeAgentImpl.h" | |
17 #include "platform/v8_inspector/V8StackTraceImpl.h" | |
18 #include "platform/v8_inspector/V8ValueCopier.h" | |
19 #include "platform/v8_inspector/public/V8InspectorClient.h" | |
20 | |
21 namespace v8_inspector { | |
22 | |
23 namespace { | |
24 | |
25 v8::Local<v8::Private> inspectedContextPrivateKey(v8::Isolate* isolate) | |
26 { | |
27 return v8::Private::ForApi(isolate, toV8StringInternalized(isolate, "V8Conso
le#InspectedContext")); | |
28 } | |
29 | |
30 class ConsoleHelper { | |
31 V8_INSPECTOR_DISALLOW_COPY(ConsoleHelper); | |
32 public: | |
33 ConsoleHelper(const v8::FunctionCallbackInfo<v8::Value>& info) | |
34 : m_info(info) | |
35 , m_isolate(info.GetIsolate()) | |
36 , m_context(info.GetIsolate()->GetCurrentContext()) | |
37 , m_inspectedContext(nullptr) | |
38 , m_inspectorClient(nullptr) | |
39 { | |
40 } | |
41 | |
42 v8::Local<v8::Object> ensureConsole() | |
43 { | |
44 if (m_console.IsEmpty()) { | |
45 DCHECK(!m_info.Data().IsEmpty()); | |
46 DCHECK(!m_info.Data()->IsUndefined()); | |
47 m_console = m_info.Data().As<v8::Object>(); | |
48 } | |
49 return m_console; | |
50 } | |
51 | |
52 InspectedContext* ensureInspectedContext() | |
53 { | |
54 if (m_inspectedContext) | |
55 return m_inspectedContext; | |
56 v8::Local<v8::Object> console = ensureConsole(); | |
57 | |
58 v8::Local<v8::Private> key = inspectedContextPrivateKey(m_isolate); | |
59 v8::Local<v8::Value> inspectedContextValue; | |
60 if (!console->GetPrivate(m_context, key).ToLocal(&inspectedContextValue)
) | |
61 return nullptr; | |
62 DCHECK(inspectedContextValue->IsExternal()); | |
63 m_inspectedContext = static_cast<InspectedContext*>(inspectedContextValu
e.As<v8::External>()->Value()); | |
64 return m_inspectedContext; | |
65 } | |
66 | |
67 V8InspectorClient* ensureDebuggerClient() | |
68 { | |
69 if (m_inspectorClient) | |
70 return m_inspectorClient; | |
71 InspectedContext* inspectedContext = ensureInspectedContext(); | |
72 if (!inspectedContext) | |
73 return nullptr; | |
74 m_inspectorClient = inspectedContext->inspector()->client(); | |
75 return m_inspectorClient; | |
76 } | |
77 | |
78 void reportCall(ConsoleAPIType type) | |
79 { | |
80 if (!m_info.Length()) | |
81 return; | |
82 std::vector<v8::Local<v8::Value>> arguments; | |
83 for (int i = 0; i < m_info.Length(); ++i) | |
84 arguments.push_back(m_info[i]); | |
85 reportCall(type, arguments); | |
86 } | |
87 | |
88 void reportCallWithDefaultArgument(ConsoleAPIType type, const String16& mess
age) | |
89 { | |
90 std::vector<v8::Local<v8::Value>> arguments; | |
91 for (int i = 0; i < m_info.Length(); ++i) | |
92 arguments.push_back(m_info[i]); | |
93 if (!m_info.Length()) | |
94 arguments.push_back(toV8String(m_isolate, message)); | |
95 reportCall(type, arguments); | |
96 } | |
97 | |
98 void reportCallWithArgument(ConsoleAPIType type, const String16& message) | |
99 { | |
100 std::vector<v8::Local<v8::Value>> arguments(1, toV8String(m_isolate, mes
sage)); | |
101 reportCall(type, arguments); | |
102 } | |
103 | |
104 void reportCall(ConsoleAPIType type, const std::vector<v8::Local<v8::Value>>
& arguments) | |
105 { | |
106 InspectedContext* inspectedContext = ensureInspectedContext(); | |
107 if (!inspectedContext) | |
108 return; | |
109 V8InspectorImpl* inspector = inspectedContext->inspector(); | |
110 std::unique_ptr<V8ConsoleMessage> message = V8ConsoleMessage::createForC
onsoleAPI(inspector->client()->currentTimeMS(), type, arguments, inspector->debu
gger()->captureStackTrace(false), inspectedContext); | |
111 inspector->ensureConsoleMessageStorage(inspectedContext->contextGroupId(
))->addMessage(std::move(message)); | |
112 } | |
113 | |
114 void reportDeprecatedCall(const char* id, const String16& message) | |
115 { | |
116 if (checkAndSetPrivateFlagOnConsole(id, false)) | |
117 return; | |
118 std::vector<v8::Local<v8::Value>> arguments(1, toV8String(m_isolate, mes
sage)); | |
119 reportCall(ConsoleAPIType::kWarning, arguments); | |
120 } | |
121 | |
122 bool firstArgToBoolean(bool defaultValue) | |
123 { | |
124 if (m_info.Length() < 1) | |
125 return defaultValue; | |
126 if (m_info[0]->IsBoolean()) | |
127 return m_info[0].As<v8::Boolean>()->Value(); | |
128 return m_info[0]->BooleanValue(m_context).FromMaybe(defaultValue); | |
129 } | |
130 | |
131 String16 firstArgToString(const String16& defaultValue) | |
132 { | |
133 if (m_info.Length() < 1) | |
134 return defaultValue; | |
135 v8::Local<v8::String> titleValue; | |
136 if (m_info[0]->IsObject()) { | |
137 if (!m_info[0].As<v8::Object>()->ObjectProtoToString(m_context).ToLo
cal(&titleValue)) | |
138 return defaultValue; | |
139 } else { | |
140 if (!m_info[0]->ToString(m_context).ToLocal(&titleValue)) | |
141 return defaultValue; | |
142 } | |
143 return toProtocolString(titleValue); | |
144 } | |
145 | |
146 v8::MaybeLocal<v8::Object> firstArgAsObject() | |
147 { | |
148 if (m_info.Length() < 1 || !m_info[0]->IsObject()) | |
149 return v8::MaybeLocal<v8::Object>(); | |
150 return m_info[0].As<v8::Object>(); | |
151 } | |
152 | |
153 v8::MaybeLocal<v8::Function> firstArgAsFunction() | |
154 { | |
155 if (m_info.Length() < 1 || !m_info[0]->IsFunction()) | |
156 return v8::MaybeLocal<v8::Function>(); | |
157 return m_info[0].As<v8::Function>(); | |
158 } | |
159 | |
160 v8::MaybeLocal<v8::Map> privateMap(const char* name) | |
161 { | |
162 v8::Local<v8::Object> console = ensureConsole(); | |
163 v8::Local<v8::Private> privateKey = v8::Private::ForApi(m_isolate, toV8S
tringInternalized(m_isolate, name)); | |
164 v8::Local<v8::Value> mapValue; | |
165 if (!console->GetPrivate(m_context, privateKey).ToLocal(&mapValue)) | |
166 return v8::MaybeLocal<v8::Map>(); | |
167 if (mapValue->IsUndefined()) { | |
168 v8::Local<v8::Map> map = v8::Map::New(m_isolate); | |
169 if (!console->SetPrivate(m_context, privateKey, map).FromMaybe(false
)) | |
170 return v8::MaybeLocal<v8::Map>(); | |
171 return map; | |
172 } | |
173 return mapValue->IsMap() ? mapValue.As<v8::Map>() : v8::MaybeLocal<v8::M
ap>(); | |
174 } | |
175 | |
176 int64_t getIntFromMap(v8::Local<v8::Map> map, const String16& key, int64_t d
efaultValue) | |
177 { | |
178 v8::Local<v8::String> v8Key = toV8String(m_isolate, key); | |
179 if (!map->Has(m_context, v8Key).FromMaybe(false)) | |
180 return defaultValue; | |
181 v8::Local<v8::Value> intValue; | |
182 if (!map->Get(m_context, v8Key).ToLocal(&intValue)) | |
183 return defaultValue; | |
184 return intValue.As<v8::Integer>()->Value(); | |
185 } | |
186 | |
187 void setIntOnMap(v8::Local<v8::Map> map, const String16& key, int64_t value) | |
188 { | |
189 v8::Local<v8::String> v8Key = toV8String(m_isolate, key); | |
190 if (!map->Set(m_context, v8Key, v8::Integer::New(m_isolate, value)).ToLo
cal(&map)) | |
191 return; | |
192 } | |
193 | |
194 double getDoubleFromMap(v8::Local<v8::Map> map, const String16& key, double
defaultValue) | |
195 { | |
196 v8::Local<v8::String> v8Key = toV8String(m_isolate, key); | |
197 if (!map->Has(m_context, v8Key).FromMaybe(false)) | |
198 return defaultValue; | |
199 v8::Local<v8::Value> intValue; | |
200 if (!map->Get(m_context, v8Key).ToLocal(&intValue)) | |
201 return defaultValue; | |
202 return intValue.As<v8::Number>()->Value(); | |
203 } | |
204 | |
205 void setDoubleOnMap(v8::Local<v8::Map> map, const String16& key, double valu
e) | |
206 { | |
207 v8::Local<v8::String> v8Key = toV8String(m_isolate, key); | |
208 if (!map->Set(m_context, v8Key, v8::Number::New(m_isolate, value)).ToLoc
al(&map)) | |
209 return; | |
210 } | |
211 | |
212 V8ProfilerAgentImpl* profilerAgent() | |
213 { | |
214 if (V8InspectorSessionImpl* session = currentSession()) { | |
215 if (session && session->profilerAgent()->enabled()) | |
216 return session->profilerAgent(); | |
217 } | |
218 return nullptr; | |
219 } | |
220 | |
221 V8DebuggerAgentImpl* debuggerAgent() | |
222 { | |
223 if (V8InspectorSessionImpl* session = currentSession()) { | |
224 if (session && session->debuggerAgent()->enabled()) | |
225 return session->debuggerAgent(); | |
226 } | |
227 return nullptr; | |
228 } | |
229 | |
230 V8InspectorSessionImpl* currentSession() | |
231 { | |
232 InspectedContext* inspectedContext = ensureInspectedContext(); | |
233 if (!inspectedContext) | |
234 return nullptr; | |
235 return inspectedContext->inspector()->sessionForContextGroup(inspectedCo
ntext->contextGroupId()); | |
236 } | |
237 | |
238 private: | |
239 const v8::FunctionCallbackInfo<v8::Value>& m_info; | |
240 v8::Isolate* m_isolate; | |
241 v8::Local<v8::Context> m_context; | |
242 v8::Local<v8::Object> m_console; | |
243 InspectedContext* m_inspectedContext; | |
244 V8InspectorClient* m_inspectorClient; | |
245 | |
246 bool checkAndSetPrivateFlagOnConsole(const char* name, bool defaultValue) | |
247 { | |
248 v8::Local<v8::Object> console = ensureConsole(); | |
249 v8::Local<v8::Private> key = v8::Private::ForApi(m_isolate, toV8StringIn
ternalized(m_isolate, name)); | |
250 v8::Local<v8::Value> flagValue; | |
251 if (!console->GetPrivate(m_context, key).ToLocal(&flagValue)) | |
252 return defaultValue; | |
253 DCHECK(flagValue->IsUndefined() || flagValue->IsBoolean()); | |
254 if (flagValue->IsBoolean()) { | |
255 DCHECK(flagValue.As<v8::Boolean>()->Value()); | |
256 return true; | |
257 } | |
258 if (!console->SetPrivate(m_context, key, v8::True(m_isolate)).FromMaybe(
false)) | |
259 return defaultValue; | |
260 return false; | |
261 } | |
262 }; | |
263 | |
264 void returnDataCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
265 { | |
266 info.GetReturnValue().Set(info.Data()); | |
267 } | |
268 | |
269 void createBoundFunctionProperty(v8::Local<v8::Context> context, v8::Local<v8::O
bject> console, const char* name, v8::FunctionCallback callback, const char* des
cription = nullptr) | |
270 { | |
271 v8::Local<v8::String> funcName = toV8StringInternalized(context->GetIsolate(
), name); | |
272 v8::Local<v8::Function> func; | |
273 if (!V8_FUNCTION_NEW_REMOVE_PROTOTYPE(context, callback, console, 0).ToLocal
(&func)) | |
274 return; | |
275 func->SetName(funcName); | |
276 if (description) { | |
277 v8::Local<v8::String> returnValue = toV8String(context->GetIsolate(), de
scription); | |
278 v8::Local<v8::Function> toStringFunction; | |
279 if (V8_FUNCTION_NEW_REMOVE_PROTOTYPE(context, returnDataCallback, return
Value, 0).ToLocal(&toStringFunction)) | |
280 createDataProperty(context, func, toV8StringInternalized(context->Ge
tIsolate(), "toString"), toStringFunction); | |
281 } | |
282 createDataProperty(context, console, funcName, func); | |
283 } | |
284 | |
285 } // namespace | |
286 | |
287 void V8Console::debugCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
288 { | |
289 ConsoleHelper(info).reportCall(ConsoleAPIType::kDebug); | |
290 } | |
291 | |
292 void V8Console::errorCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
293 { | |
294 ConsoleHelper(info).reportCall(ConsoleAPIType::kError); | |
295 } | |
296 | |
297 void V8Console::infoCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
298 { | |
299 ConsoleHelper(info).reportCall(ConsoleAPIType::kInfo); | |
300 } | |
301 | |
302 void V8Console::logCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
303 { | |
304 ConsoleHelper(info).reportCall(ConsoleAPIType::kLog); | |
305 } | |
306 | |
307 void V8Console::warnCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
308 { | |
309 ConsoleHelper(info).reportCall(ConsoleAPIType::kWarning); | |
310 } | |
311 | |
312 void V8Console::dirCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
313 { | |
314 ConsoleHelper(info).reportCall(ConsoleAPIType::kDir); | |
315 } | |
316 | |
317 void V8Console::dirxmlCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
318 { | |
319 ConsoleHelper(info).reportCall(ConsoleAPIType::kDirXML); | |
320 } | |
321 | |
322 void V8Console::tableCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
323 { | |
324 ConsoleHelper(info).reportCall(ConsoleAPIType::kTable); | |
325 } | |
326 | |
327 void V8Console::traceCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
328 { | |
329 ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kTrace, St
ring16("console.trace")); | |
330 } | |
331 | |
332 void V8Console::groupCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
333 { | |
334 ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kStartGrou
p, String16("console.group")); | |
335 } | |
336 | |
337 void V8Console::groupCollapsedCallback(const v8::FunctionCallbackInfo<v8::Value>
& info) | |
338 { | |
339 ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kStartGrou
pCollapsed, String16("console.groupCollapsed")); | |
340 } | |
341 | |
342 void V8Console::groupEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info
) | |
343 { | |
344 ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kEndGroup,
String16("console.groupEnd")); | |
345 } | |
346 | |
347 void V8Console::clearCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
348 { | |
349 ConsoleHelper(info).reportCallWithDefaultArgument(ConsoleAPIType::kClear, St
ring16("console.clear")); | |
350 } | |
351 | |
352 void V8Console::countCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
353 { | |
354 ConsoleHelper helper(info); | |
355 | |
356 String16 title = helper.firstArgToString(String16()); | |
357 String16 identifier; | |
358 if (title.isEmpty()) { | |
359 std::unique_ptr<V8StackTraceImpl> stackTrace = V8StackTraceImpl::capture
(nullptr, 0, 1); | |
360 if (stackTrace) | |
361 identifier = toString16(stackTrace->topSourceURL()) + ":" + String16
::fromInteger(stackTrace->topLineNumber()); | |
362 } else { | |
363 identifier = title + "@"; | |
364 } | |
365 | |
366 v8::Local<v8::Map> countMap; | |
367 if (!helper.privateMap("V8Console#countMap").ToLocal(&countMap)) | |
368 return; | |
369 int64_t count = helper.getIntFromMap(countMap, identifier, 0) + 1; | |
370 helper.setIntOnMap(countMap, identifier, count); | |
371 helper.reportCallWithArgument(ConsoleAPIType::kCount, title + ": " + String1
6::fromInteger(count)); | |
372 } | |
373 | |
374 void V8Console::assertCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
375 { | |
376 ConsoleHelper helper(info); | |
377 if (helper.firstArgToBoolean(false)) | |
378 return; | |
379 | |
380 std::vector<v8::Local<v8::Value>> arguments; | |
381 for (int i = 1; i < info.Length(); ++i) | |
382 arguments.push_back(info[i]); | |
383 if (info.Length() < 2) | |
384 arguments.push_back(toV8String(info.GetIsolate(), String16("console.asse
rt"))); | |
385 helper.reportCall(ConsoleAPIType::kAssert, arguments); | |
386 | |
387 if (V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent()) | |
388 debuggerAgent->breakProgramOnException(protocol::Debugger::Paused::Reaso
nEnum::Assert, nullptr); | |
389 } | |
390 | |
391 void V8Console::markTimelineCallback(const v8::FunctionCallbackInfo<v8::Value>&
info) | |
392 { | |
393 ConsoleHelper(info).reportDeprecatedCall("V8Console#markTimelineDeprecated",
"'console.markTimeline' is deprecated. Please use 'console.timeStamp' instead."
); | |
394 timeStampCallback(info); | |
395 } | |
396 | |
397 void V8Console::profileCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
398 { | |
399 ConsoleHelper helper(info); | |
400 if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent()) | |
401 profilerAgent->consoleProfile(helper.firstArgToString(String16())); | |
402 } | |
403 | |
404 void V8Console::profileEndCallback(const v8::FunctionCallbackInfo<v8::Value>& in
fo) | |
405 { | |
406 ConsoleHelper helper(info); | |
407 if (V8ProfilerAgentImpl* profilerAgent = helper.profilerAgent()) | |
408 profilerAgent->consoleProfileEnd(helper.firstArgToString(String16())); | |
409 } | |
410 | |
411 static void timeFunction(const v8::FunctionCallbackInfo<v8::Value>& info, bool t
imelinePrefix) | |
412 { | |
413 ConsoleHelper helper(info); | |
414 if (V8InspectorClient* client = helper.ensureDebuggerClient()) { | |
415 String16 protocolTitle = helper.firstArgToString("default"); | |
416 if (timelinePrefix) | |
417 protocolTitle = "Timeline '" + protocolTitle + "'"; | |
418 client->consoleTime(toStringView(protocolTitle)); | |
419 | |
420 v8::Local<v8::Map> timeMap; | |
421 if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) | |
422 return; | |
423 helper.setDoubleOnMap(timeMap, protocolTitle, client->currentTimeMS()); | |
424 } | |
425 } | |
426 | |
427 static void timeEndFunction(const v8::FunctionCallbackInfo<v8::Value>& info, boo
l timelinePrefix) | |
428 { | |
429 ConsoleHelper helper(info); | |
430 if (V8InspectorClient* client = helper.ensureDebuggerClient()) { | |
431 String16 protocolTitle = helper.firstArgToString("default"); | |
432 if (timelinePrefix) | |
433 protocolTitle = "Timeline '" + protocolTitle + "'"; | |
434 client->consoleTimeEnd(toStringView(protocolTitle)); | |
435 | |
436 v8::Local<v8::Map> timeMap; | |
437 if (!helper.privateMap("V8Console#timeMap").ToLocal(&timeMap)) | |
438 return; | |
439 double elapsed = client->currentTimeMS() - helper.getDoubleFromMap(timeM
ap, protocolTitle, 0.0); | |
440 String16 message = protocolTitle + ": " + String16::fromDoublePrecision3
(elapsed) + "ms"; | |
441 helper.reportCallWithArgument(ConsoleAPIType::kTimeEnd, message); | |
442 } | |
443 } | |
444 | |
445 void V8Console::timelineCallback(const v8::FunctionCallbackInfo<v8::Value>& info
) | |
446 { | |
447 ConsoleHelper(info).reportDeprecatedCall("V8Console#timeline", "'console.tim
eline' is deprecated. Please use 'console.time' instead."); | |
448 timeFunction(info, true); | |
449 } | |
450 | |
451 void V8Console::timelineEndCallback(const v8::FunctionCallbackInfo<v8::Value>& i
nfo) | |
452 { | |
453 ConsoleHelper(info).reportDeprecatedCall("V8Console#timelineEnd", "'console.
timelineEnd' is deprecated. Please use 'console.timeEnd' instead."); | |
454 timeEndFunction(info, true); | |
455 } | |
456 | |
457 void V8Console::timeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
458 { | |
459 timeFunction(info, false); | |
460 } | |
461 | |
462 void V8Console::timeEndCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
463 { | |
464 timeEndFunction(info, false); | |
465 } | |
466 | |
467 void V8Console::timeStampCallback(const v8::FunctionCallbackInfo<v8::Value>& inf
o) | |
468 { | |
469 ConsoleHelper helper(info); | |
470 if (V8InspectorClient* client = helper.ensureDebuggerClient()) { | |
471 String16 title = helper.firstArgToString(String16()); | |
472 client->consoleTimeStamp(toStringView(title)); | |
473 } | |
474 } | |
475 | |
476 void V8Console::memoryGetterCallback(const v8::FunctionCallbackInfo<v8::Value>&
info) | |
477 { | |
478 if (V8InspectorClient* client = ConsoleHelper(info).ensureDebuggerClient())
{ | |
479 v8::Local<v8::Value> memoryValue; | |
480 if (!client->memoryInfo(info.GetIsolate(), info.GetIsolate()->GetCurrent
Context()).ToLocal(&memoryValue)) | |
481 return; | |
482 info.GetReturnValue().Set(memoryValue); | |
483 } | |
484 } | |
485 | |
486 void V8Console::memorySetterCallback(const v8::FunctionCallbackInfo<v8::Value>&
info) | |
487 { | |
488 // We can't make the attribute readonly as it breaks existing code that reli
es on being able to assign to console.memory in strict mode. Instead, the setter
just ignores the passed value. http://crbug.com/468611 | |
489 } | |
490 | |
491 void V8Console::keysCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
492 { | |
493 v8::Isolate* isolate = info.GetIsolate(); | |
494 info.GetReturnValue().Set(v8::Array::New(isolate)); | |
495 | |
496 ConsoleHelper helper(info); | |
497 v8::Local<v8::Object> obj; | |
498 if (!helper.firstArgAsObject().ToLocal(&obj)) | |
499 return; | |
500 v8::Local<v8::Array> names; | |
501 if (!obj->GetOwnPropertyNames(isolate->GetCurrentContext()).ToLocal(&names)) | |
502 return; | |
503 info.GetReturnValue().Set(names); | |
504 } | |
505 | |
506 void V8Console::valuesCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
507 { | |
508 v8::Isolate* isolate = info.GetIsolate(); | |
509 info.GetReturnValue().Set(v8::Array::New(isolate)); | |
510 | |
511 ConsoleHelper helper(info); | |
512 v8::Local<v8::Object> obj; | |
513 if (!helper.firstArgAsObject().ToLocal(&obj)) | |
514 return; | |
515 v8::Local<v8::Array> names; | |
516 v8::Local<v8::Context> context = isolate->GetCurrentContext(); | |
517 if (!obj->GetOwnPropertyNames(context).ToLocal(&names)) | |
518 return; | |
519 v8::Local<v8::Array> values = v8::Array::New(isolate, names->Length()); | |
520 for (size_t i = 0; i < names->Length(); ++i) { | |
521 v8::Local<v8::Value> key; | |
522 if (!names->Get(context, i).ToLocal(&key)) | |
523 continue; | |
524 v8::Local<v8::Value> value; | |
525 if (!obj->Get(context, key).ToLocal(&value)) | |
526 continue; | |
527 createDataProperty(context, values, i, value); | |
528 } | |
529 info.GetReturnValue().Set(values); | |
530 } | |
531 | |
532 static void setFunctionBreakpoint(ConsoleHelper& helper, v8::Local<v8::Function>
function, V8DebuggerAgentImpl::BreakpointSource source, const String16& conditi
on, bool enable) | |
533 { | |
534 V8DebuggerAgentImpl* debuggerAgent = helper.debuggerAgent(); | |
535 if (!debuggerAgent) | |
536 return; | |
537 String16 scriptId = String16::fromInteger(function->ScriptId()); | |
538 int lineNumber = function->GetScriptLineNumber(); | |
539 int columnNumber = function->GetScriptColumnNumber(); | |
540 if (lineNumber == v8::Function::kLineOffsetNotFound || columnNumber == v8::F
unction::kLineOffsetNotFound) | |
541 return; | |
542 if (enable) | |
543 debuggerAgent->setBreakpointAt(scriptId, lineNumber, columnNumber, sourc
e, condition); | |
544 else | |
545 debuggerAgent->removeBreakpointAt(scriptId, lineNumber, columnNumber, so
urce); | |
546 } | |
547 | |
548 void V8Console::debugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value>&
info) | |
549 { | |
550 ConsoleHelper helper(info); | |
551 v8::Local<v8::Function> function; | |
552 if (!helper.firstArgAsFunction().ToLocal(&function)) | |
553 return; | |
554 setFunctionBreakpoint(helper, function, V8DebuggerAgentImpl::DebugCommandBre
akpointSource, String16(), true); | |
555 } | |
556 | |
557 void V8Console::undebugFunctionCallback(const v8::FunctionCallbackInfo<v8::Value
>& info) | |
558 { | |
559 ConsoleHelper helper(info); | |
560 v8::Local<v8::Function> function; | |
561 if (!helper.firstArgAsFunction().ToLocal(&function)) | |
562 return; | |
563 setFunctionBreakpoint(helper, function, V8DebuggerAgentImpl::DebugCommandBre
akpointSource, String16(), false); | |
564 } | |
565 | |
566 void V8Console::monitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Value
>& info) | |
567 { | |
568 ConsoleHelper helper(info); | |
569 v8::Local<v8::Function> function; | |
570 if (!helper.firstArgAsFunction().ToLocal(&function)) | |
571 return; | |
572 v8::Local<v8::Value> name = function->GetName(); | |
573 if (!name->IsString() || !v8::Local<v8::String>::Cast(name)->Length()) | |
574 name = function->GetInferredName(); | |
575 String16 functionName = toProtocolStringWithTypeCheck(name); | |
576 String16Builder builder; | |
577 builder.append("console.log(\"function "); | |
578 if (functionName.isEmpty()) | |
579 builder.append("(anonymous function)"); | |
580 else | |
581 builder.append(functionName); | |
582 builder.append(" called\" + (arguments.length > 0 ? \" with arguments: \" +
Array.prototype.join.call(arguments, \", \") : \"\")) && false"); | |
583 setFunctionBreakpoint(helper, function, V8DebuggerAgentImpl::MonitorCommandB
reakpointSource, builder.toString(), true); | |
584 } | |
585 | |
586 void V8Console::unmonitorFunctionCallback(const v8::FunctionCallbackInfo<v8::Val
ue>& info) | |
587 { | |
588 ConsoleHelper helper(info); | |
589 v8::Local<v8::Function> function; | |
590 if (!helper.firstArgAsFunction().ToLocal(&function)) | |
591 return; | |
592 setFunctionBreakpoint(helper, function, V8DebuggerAgentImpl::MonitorCommandB
reakpointSource, String16(), false); | |
593 } | |
594 | |
595 void V8Console::lastEvaluationResultCallback(const v8::FunctionCallbackInfo<v8::
Value>& info) | |
596 { | |
597 ConsoleHelper helper(info); | |
598 InspectedContext* context = helper.ensureInspectedContext(); | |
599 if (!context) | |
600 return; | |
601 if (InjectedScript* injectedScript = context->getInjectedScript()) | |
602 info.GetReturnValue().Set(injectedScript->lastEvaluationResult()); | |
603 } | |
604 | |
605 static void inspectImpl(const v8::FunctionCallbackInfo<v8::Value>& info, bool co
pyToClipboard) | |
606 { | |
607 if (info.Length() < 1) | |
608 return; | |
609 if (!copyToClipboard) | |
610 info.GetReturnValue().Set(info[0]); | |
611 | |
612 ConsoleHelper helper(info); | |
613 InspectedContext* context = helper.ensureInspectedContext(); | |
614 if (!context) | |
615 return; | |
616 InjectedScript* injectedScript = context->getInjectedScript(); | |
617 if (!injectedScript) | |
618 return; | |
619 ErrorString errorString; | |
620 std::unique_ptr<protocol::Runtime::RemoteObject> wrappedObject = injectedScr
ipt->wrapObject(&errorString, info[0], "", false /** forceValueType */, false /*
* generatePreview */); | |
621 if (!wrappedObject || !errorString.isEmpty()) | |
622 return; | |
623 | |
624 std::unique_ptr<protocol::DictionaryValue> hints = protocol::DictionaryValue
::create(); | |
625 if (copyToClipboard) | |
626 hints->setBoolean("copyToClipboard", true); | |
627 if (V8InspectorSessionImpl* session = helper.currentSession()) | |
628 session->runtimeAgent()->inspect(std::move(wrappedObject), std::move(hin
ts)); | |
629 } | |
630 | |
631 void V8Console::inspectCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
632 { | |
633 inspectImpl(info, false); | |
634 } | |
635 | |
636 void V8Console::copyCallback(const v8::FunctionCallbackInfo<v8::Value>& info) | |
637 { | |
638 inspectImpl(info, true); | |
639 } | |
640 | |
641 void V8Console::inspectedObject(const v8::FunctionCallbackInfo<v8::Value>& info,
unsigned num) | |
642 { | |
643 DCHECK(num < V8InspectorSessionImpl::kInspectedObjectBufferSize); | |
644 ConsoleHelper helper(info); | |
645 if (V8InspectorSessionImpl* session = helper.currentSession()) { | |
646 V8InspectorSession::Inspectable* object = session->inspectedObject(num); | |
647 v8::Isolate* isolate = info.GetIsolate(); | |
648 if (object) | |
649 info.GetReturnValue().Set(object->get(isolate->GetCurrentContext()))
; | |
650 else | |
651 info.GetReturnValue().Set(v8::Undefined(isolate)); | |
652 } | |
653 } | |
654 | |
655 v8::Local<v8::Object> V8Console::createConsole(InspectedContext* inspectedContex
t, bool hasMemoryAttribute) | |
656 { | |
657 v8::Local<v8::Context> context = inspectedContext->context(); | |
658 v8::Context::Scope contextScope(context); | |
659 v8::Isolate* isolate = context->GetIsolate(); | |
660 v8::MicrotasksScope microtasksScope(isolate, v8::MicrotasksScope::kDoNotRunM
icrotasks); | |
661 | |
662 v8::Local<v8::Object> console = v8::Object::New(isolate); | |
663 bool success = console->SetPrototype(context, v8::Object::New(isolate)).From
Maybe(false); | |
664 DCHECK(success); | |
665 | |
666 createBoundFunctionProperty(context, console, "debug", V8Console::debugCallb
ack); | |
667 createBoundFunctionProperty(context, console, "error", V8Console::errorCallb
ack); | |
668 createBoundFunctionProperty(context, console, "info", V8Console::infoCallbac
k); | |
669 createBoundFunctionProperty(context, console, "log", V8Console::logCallback)
; | |
670 createBoundFunctionProperty(context, console, "warn", V8Console::warnCallbac
k); | |
671 createBoundFunctionProperty(context, console, "dir", V8Console::dirCallback)
; | |
672 createBoundFunctionProperty(context, console, "dirxml", V8Console::dirxmlCal
lback); | |
673 createBoundFunctionProperty(context, console, "table", V8Console::tableCallb
ack); | |
674 createBoundFunctionProperty(context, console, "trace", V8Console::traceCallb
ack); | |
675 createBoundFunctionProperty(context, console, "group", V8Console::groupCallb
ack); | |
676 createBoundFunctionProperty(context, console, "groupCollapsed", V8Console::g
roupCollapsedCallback); | |
677 createBoundFunctionProperty(context, console, "groupEnd", V8Console::groupEn
dCallback); | |
678 createBoundFunctionProperty(context, console, "clear", V8Console::clearCallb
ack); | |
679 createBoundFunctionProperty(context, console, "count", V8Console::countCallb
ack); | |
680 createBoundFunctionProperty(context, console, "assert", V8Console::assertCal
lback); | |
681 createBoundFunctionProperty(context, console, "markTimeline", V8Console::mar
kTimelineCallback); | |
682 createBoundFunctionProperty(context, console, "profile", V8Console::profileC
allback); | |
683 createBoundFunctionProperty(context, console, "profileEnd", V8Console::profi
leEndCallback); | |
684 createBoundFunctionProperty(context, console, "timeline", V8Console::timelin
eCallback); | |
685 createBoundFunctionProperty(context, console, "timelineEnd", V8Console::time
lineEndCallback); | |
686 createBoundFunctionProperty(context, console, "time", V8Console::timeCallbac
k); | |
687 createBoundFunctionProperty(context, console, "timeEnd", V8Console::timeEndC
allback); | |
688 createBoundFunctionProperty(context, console, "timeStamp", V8Console::timeSt
ampCallback); | |
689 | |
690 if (hasMemoryAttribute) | |
691 console->SetAccessorProperty(toV8StringInternalized(isolate, "memory"),
V8_FUNCTION_NEW_REMOVE_PROTOTYPE(context, V8Console::memoryGetterCallback, conso
le, 0).ToLocalChecked(), V8_FUNCTION_NEW_REMOVE_PROTOTYPE(context, V8Console::me
morySetterCallback, v8::Local<v8::Value>(), 0).ToLocalChecked(), static_cast<v8:
:PropertyAttribute>(v8::None), v8::DEFAULT); | |
692 | |
693 console->SetPrivate(context, inspectedContextPrivateKey(isolate), v8::Extern
al::New(isolate, inspectedContext)); | |
694 return console; | |
695 } | |
696 | |
697 void V8Console::clearInspectedContextIfNeeded(v8::Local<v8::Context> context, v8
::Local<v8::Object> console) | |
698 { | |
699 v8::Isolate* isolate = context->GetIsolate(); | |
700 console->SetPrivate(context, inspectedContextPrivateKey(isolate), v8::Extern
al::New(isolate, nullptr)); | |
701 } | |
702 | |
703 v8::Local<v8::Object> V8Console::createCommandLineAPI(InspectedContext* inspecte
dContext) | |
704 { | |
705 v8::Local<v8::Context> context = inspectedContext->context(); | |
706 v8::Isolate* isolate = context->GetIsolate(); | |
707 v8::MicrotasksScope microtasksScope(isolate, v8::MicrotasksScope::kDoNotRunM
icrotasks); | |
708 | |
709 v8::Local<v8::Object> commandLineAPI = v8::Object::New(isolate); | |
710 bool success = commandLineAPI->SetPrototype(context, v8::Null(isolate)).From
Maybe(false); | |
711 DCHECK(success); | |
712 | |
713 createBoundFunctionProperty(context, commandLineAPI, "dir", V8Console::dirCa
llback, "function dir(value) { [Command Line API] }"); | |
714 createBoundFunctionProperty(context, commandLineAPI, "dirxml", V8Console::di
rxmlCallback, "function dirxml(value) { [Command Line API] }"); | |
715 createBoundFunctionProperty(context, commandLineAPI, "profile", V8Console::p
rofileCallback, "function profile(title) { [Command Line API] }"); | |
716 createBoundFunctionProperty(context, commandLineAPI, "profileEnd", V8Console
::profileEndCallback, "function profileEnd(title) { [Command Line API] }"); | |
717 createBoundFunctionProperty(context, commandLineAPI, "clear", V8Console::cle
arCallback, "function clear() { [Command Line API] }"); | |
718 createBoundFunctionProperty(context, commandLineAPI, "table", V8Console::tab
leCallback, "function table(data, [columns]) { [Command Line API] }"); | |
719 | |
720 createBoundFunctionProperty(context, commandLineAPI, "keys", V8Console::keys
Callback, "function keys(object) { [Command Line API] }"); | |
721 createBoundFunctionProperty(context, commandLineAPI, "values", V8Console::va
luesCallback, "function values(object) { [Command Line API] }"); | |
722 createBoundFunctionProperty(context, commandLineAPI, "debug", V8Console::deb
ugFunctionCallback, "function debug(function) { [Command Line API] }"); | |
723 createBoundFunctionProperty(context, commandLineAPI, "undebug", V8Console::u
ndebugFunctionCallback, "function undebug(function) { [Command Line API] }"); | |
724 createBoundFunctionProperty(context, commandLineAPI, "monitor", V8Console::m
onitorFunctionCallback, "function monitor(function) { [Command Line API] }"); | |
725 createBoundFunctionProperty(context, commandLineAPI, "unmonitor", V8Console:
:unmonitorFunctionCallback, "function unmonitor(function) { [Command Line API] }
"); | |
726 createBoundFunctionProperty(context, commandLineAPI, "inspect", V8Console::i
nspectCallback, "function inspect(object) { [Command Line API] }"); | |
727 createBoundFunctionProperty(context, commandLineAPI, "copy", V8Console::copy
Callback, "function copy(value) { [Command Line API] }"); | |
728 createBoundFunctionProperty(context, commandLineAPI, "$_", V8Console::lastEv
aluationResultCallback); | |
729 createBoundFunctionProperty(context, commandLineAPI, "$0", V8Console::inspec
tedObject0); | |
730 createBoundFunctionProperty(context, commandLineAPI, "$1", V8Console::inspec
tedObject1); | |
731 createBoundFunctionProperty(context, commandLineAPI, "$2", V8Console::inspec
tedObject2); | |
732 createBoundFunctionProperty(context, commandLineAPI, "$3", V8Console::inspec
tedObject3); | |
733 createBoundFunctionProperty(context, commandLineAPI, "$4", V8Console::inspec
tedObject4); | |
734 | |
735 inspectedContext->inspector()->client()->installAdditionalCommandLineAPI(con
text, commandLineAPI); | |
736 | |
737 commandLineAPI->SetPrivate(context, inspectedContextPrivateKey(isolate), v8:
:External::New(isolate, inspectedContext)); | |
738 return commandLineAPI; | |
739 } | |
740 | |
741 static bool isCommandLineAPIGetter(const String16& name) | |
742 { | |
743 if (name.length() != 2) | |
744 return false; | |
745 // $0 ... $4, $_ | |
746 return name[0] == '$' && ((name[1] >= '0' && name[1] <= '4') || name[1] == '
_'); | |
747 } | |
748 | |
749 void V8Console::CommandLineAPIScope::accessorGetterCallback(v8::Local<v8::Name>
name, const v8::PropertyCallbackInfo<v8::Value>& info) | |
750 { | |
751 CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>(info.Data().A
s<v8::External>()->Value()); | |
752 DCHECK(scope); | |
753 | |
754 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); | |
755 if (scope->m_cleanup) { | |
756 bool removed = info.Holder()->Delete(context, name).FromMaybe(false); | |
757 DCHECK(removed); | |
758 return; | |
759 } | |
760 v8::Local<v8::Object> commandLineAPI = scope->m_commandLineAPI; | |
761 | |
762 v8::Local<v8::Value> value; | |
763 if (!commandLineAPI->Get(context, name).ToLocal(&value)) | |
764 return; | |
765 if (isCommandLineAPIGetter(toProtocolStringWithTypeCheck(name))) { | |
766 DCHECK(value->IsFunction()); | |
767 v8::MicrotasksScope microtasks(info.GetIsolate(), v8::MicrotasksScope::k
DoNotRunMicrotasks); | |
768 if (value.As<v8::Function>()->Call(context, commandLineAPI, 0, nullptr).
ToLocal(&value)) | |
769 info.GetReturnValue().Set(value); | |
770 } else { | |
771 info.GetReturnValue().Set(value); | |
772 } | |
773 } | |
774 | |
775 void V8Console::CommandLineAPIScope::accessorSetterCallback(v8::Local<v8::Name>
name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info) | |
776 { | |
777 CommandLineAPIScope* scope = static_cast<CommandLineAPIScope*>(info.Data().A
s<v8::External>()->Value()); | |
778 v8::Local<v8::Context> context = info.GetIsolate()->GetCurrentContext(); | |
779 if (!info.Holder()->Delete(context, name).FromMaybe(false)) | |
780 return; | |
781 if (!info.Holder()->CreateDataProperty(context, name, value).FromMaybe(false
)) | |
782 return; | |
783 bool removed = scope->m_installedMethods->Delete(context, name).FromMaybe(fa
lse); | |
784 DCHECK(removed); | |
785 } | |
786 | |
787 V8Console::CommandLineAPIScope::CommandLineAPIScope(v8::Local<v8::Context> conte
xt, v8::Local<v8::Object> commandLineAPI, v8::Local<v8::Object> global) | |
788 : m_context(context) | |
789 , m_commandLineAPI(commandLineAPI) | |
790 , m_global(global) | |
791 , m_installedMethods(v8::Set::New(context->GetIsolate())) | |
792 , m_cleanup(false) | |
793 { | |
794 v8::Local<v8::Array> names; | |
795 if (!m_commandLineAPI->GetOwnPropertyNames(context).ToLocal(&names)) | |
796 return; | |
797 v8::Local<v8::External> externalThis = v8::External::New(context->GetIsolate
(), this); | |
798 for (size_t i = 0; i < names->Length(); ++i) { | |
799 v8::Local<v8::Value> name; | |
800 if (!names->Get(context, i).ToLocal(&name) || !name->IsName()) | |
801 continue; | |
802 if (m_global->Has(context, name).FromMaybe(true)) | |
803 continue; | |
804 if (!m_installedMethods->Add(context, name).ToLocal(&m_installedMethods)
) | |
805 continue; | |
806 if (!m_global->SetAccessor(context, v8::Local<v8::Name>::Cast(name), Com
mandLineAPIScope::accessorGetterCallback, | |
807 CommandLineAPIScope::accessorSetterCallback, externalThis, | |
808 v8::DEFAULT, v8::DontEnum).FromMaybe(false)) { | |
809 bool removed = m_installedMethods->Delete(context, name).FromMaybe(f
alse); | |
810 DCHECK(removed); | |
811 continue; | |
812 } | |
813 } | |
814 } | |
815 | |
816 V8Console::CommandLineAPIScope::~CommandLineAPIScope() | |
817 { | |
818 m_cleanup = true; | |
819 v8::Local<v8::Array> names = m_installedMethods->AsArray(); | |
820 for (size_t i = 0; i < names->Length(); ++i) { | |
821 v8::Local<v8::Value> name; | |
822 if (!names->Get(m_context, i).ToLocal(&name) || !name->IsName()) | |
823 continue; | |
824 if (name->IsString()) { | |
825 v8::Local<v8::Value> descriptor; | |
826 bool success = m_global->GetOwnPropertyDescriptor(m_context, v8::Loc
al<v8::String>::Cast(name)).ToLocal(&descriptor); | |
827 DCHECK(success); | |
828 } | |
829 } | |
830 } | |
831 | |
832 } // namespace v8_inspector | |
OLD | NEW |