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