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