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-message.h" 6 7 #include "src/debug/debug-interface.h" 8 #include "src/inspector/inspected-context.h" 9 #include "src/inspector/protocol/Protocol.h" 10 #include "src/inspector/string-util.h" 11 #include "src/inspector/v8-console-agent-impl.h" 12 #include "src/inspector/v8-inspector-impl.h" 13 #include "src/inspector/v8-inspector-session-impl.h" 14 #include "src/inspector/v8-runtime-agent-impl.h" 15 #include "src/inspector/v8-stack-trace-impl.h" 16 17 #include "include/v8-inspector.h" 18 19 namespace v8_inspector { 20 21 namespace { 22 23 String16 consoleAPITypeValue(ConsoleAPIType type) { 24 switch (type) { 25 case ConsoleAPIType::kLog: 26 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Log; 27 case ConsoleAPIType::kDebug: 28 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Debug; 29 case ConsoleAPIType::kInfo: 30 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Info; 31 case ConsoleAPIType::kError: 32 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Error; 33 case ConsoleAPIType::kWarning: 34 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Warning; 35 case ConsoleAPIType::kClear: 36 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Clear; 37 case ConsoleAPIType::kDir: 38 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Dir; 39 case ConsoleAPIType::kDirXML: 40 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Dirxml; 41 case ConsoleAPIType::kTable: 42 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Table; 43 case ConsoleAPIType::kTrace: 44 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Trace; 45 case ConsoleAPIType::kStartGroup: 46 return protocol::Runtime::ConsoleAPICalled::TypeEnum::StartGroup; 47 case ConsoleAPIType::kStartGroupCollapsed: 48 return protocol::Runtime::ConsoleAPICalled::TypeEnum::StartGroupCollapsed; 49 case ConsoleAPIType::kEndGroup: 50 return protocol::Runtime::ConsoleAPICalled::TypeEnum::EndGroup; 51 case ConsoleAPIType::kAssert: 52 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Assert; 53 case ConsoleAPIType::kTimeEnd: 54 return protocol::Runtime::ConsoleAPICalled::TypeEnum::TimeEnd; 55 case ConsoleAPIType::kCount: 56 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Count; 57 } 58 return protocol::Runtime::ConsoleAPICalled::TypeEnum::Log; 59 } 60 61 const char kGlobalConsoleMessageHandleLabel[] = "DevTools console"; 62 const unsigned maxConsoleMessageCount = 1000; 63 const int maxConsoleMessageV8Size = 10 * 1024 * 1024; 64 const unsigned maxArrayItemsLimit = 10000; 65 const unsigned maxStackDepthLimit = 32; 66 67 class V8ValueStringBuilder { 68 public: 69 static String16 toString(v8::Local<v8::Value> value, 70 v8::Local<v8::Context> context) { 71 V8ValueStringBuilder builder(context); 72 if (!builder.append(value)) return String16(); 73 return builder.toString(); 74 } 75 76 private: 77 enum { 78 IgnoreNull = 1 << 0, 79 IgnoreUndefined = 1 << 1, 80 }; 81 82 explicit V8ValueStringBuilder(v8::Local<v8::Context> context) 83 : m_arrayLimit(maxArrayItemsLimit), 84 m_isolate(context->GetIsolate()), 85 m_tryCatch(context->GetIsolate()), 86 m_context(context) {} 87 88 bool append(v8::Local<v8::Value> value, unsigned ignoreOptions = 0) { 89 if (value.IsEmpty()) return true; 90 if ((ignoreOptions & IgnoreNull) && value->IsNull()) return true; 91 if ((ignoreOptions & IgnoreUndefined) && value->IsUndefined()) return true; 92 if (value->IsString()) return append(v8::Local<v8::String>::Cast(value)); 93 if (value->IsStringObject()) 94 return append(v8::Local<v8::StringObject>::Cast(value)->ValueOf()); 95 if (value->IsBigInt()) return append(v8::Local<v8::BigInt>::Cast(value)); 96 if (value->IsBigIntObject()) 97 return append(v8::Local<v8::BigIntObject>::Cast(value)->ValueOf()); 98 if (value->IsSymbol()) return append(v8::Local<v8::Symbol>::Cast(value)); 99 if (value->IsSymbolObject()) 100 return append(v8::Local<v8::SymbolObject>::Cast(value)->ValueOf()); 101 if (value->IsNumberObject()) { 102 m_builder.append(String16::fromDouble( 103 v8::Local<v8::NumberObject>::Cast(value)->ValueOf(), 6)); 104 return true; 105 } 106 if (value->IsBooleanObject()) { 107 m_builder.append(v8::Local<v8::BooleanObject>::Cast(value)->ValueOf() 108 ? "true" 109 : "false"); 110 return true; 111 } 112 if (value->IsArray()) return append(v8::Local<v8::Array>::Cast(value)); 113 if (value->IsProxy()) { 114 m_builder.append("[object Proxy]"); 115 return true; 116 } 117 if (value->IsObject() && !value->IsDate() && !value->IsFunction() && 118 !value->IsNativeError() && !value->IsRegExp()) { 119 v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(value); 120 v8::Local<v8::String> stringValue; 121 if (object->ObjectProtoToString(m_context).ToLocal(&stringValue)) 122 return append(stringValue); 123 } 124 v8::Local<v8::String> stringValue; 125 if (!value->ToString(m_context).ToLocal(&stringValue)) return false; 126 return append(stringValue); 127 } 128 129 bool append(v8::Local<v8::Array> array) { 130 for (const auto& it : m_visitedArrays) { 131 if (it == array) return true; 132 } 133 uint32_t length = array->Length(); 134 if (length > m_arrayLimit) return false; 135 if (m_visitedArrays.size() > maxStackDepthLimit) return false; 136 137 bool result = true; 138 m_arrayLimit -= length; 139 m_visitedArrays.push_back(array); 140 for (uint32_t i = 0; i < length; ++i) { 141 if (i) m_builder.append(','); 142 v8::Local<v8::Value> value; 143 if (!array->Get(m_context, i).ToLocal(&value)) continue; 144 if (!append(value, IgnoreNull | IgnoreUndefined)) { 145 result = false; 146 break; 147 } 148 } 149 m_visitedArrays.pop_back(); 150 return result; 151 } 152 153 bool append(v8::Local<v8::Symbol> symbol) { 154 m_builder.append("Symbol("); 155 bool result = append(symbol->Name(), IgnoreUndefined); 156 m_builder.append(')'); 157 return result; 158 } 159 160 bool append(v8::Local<v8::BigInt> bigint) { 161 v8::Local<v8::String> bigint_string; 162 if (!bigint->ToString(m_context).ToLocal(&bigint_string)) return false; 163 bool result = append(bigint_string); 164 if (m_tryCatch.HasCaught()) return false; 165 m_builder.append('n'); 166 return result; 167 } 168 169 bool append(v8::Local<v8::String> string) { 170 if (m_tryCatch.HasCaught()) return false; 171 if (!string.IsEmpty()) { 172 m_builder.append(toProtocolString(m_isolate, string)); 173 } 174 return true; 175 } 176 177 String16 toString() { 178 if (m_tryCatch.HasCaught()) return String16(); 179 return m_builder.toString(); 180 } 181 182 uint32_t m_arrayLimit; 183 v8::Isolate* m_isolate; 184 String16Builder m_builder; 185 std::vector<v8::Local<v8::Array>> m_visitedArrays; 186 v8::TryCatch m_tryCatch; 187 v8::Local<v8::Context> m_context; 188 }; 189 190 } // namespace 191 192 V8ConsoleMessage::V8ConsoleMessage(V8MessageOrigin origin, double timestamp, 193 const String16& message) 194 : m_origin(origin), 195 m_timestamp(timestamp), 196 m_message(message), 197 m_lineNumber(0), 198 m_columnNumber(0), 199 m_scriptId(0), 200 m_contextId(0), 201 m_type(ConsoleAPIType::kLog), 202 m_exceptionId(0), 203 m_revokedExceptionId(0) {} 204 205 V8ConsoleMessage::~V8ConsoleMessage() {} 206 207 void V8ConsoleMessage::setLocation(const String16& url, unsigned lineNumber, 208 unsigned columnNumber, 209 std::unique_ptr<V8StackTraceImpl> stackTrace, 210 int scriptId) { 211 m_url = url; 212 m_lineNumber = lineNumber; 213 m_columnNumber = columnNumber; 214 m_stackTrace = std::move(stackTrace); 215 m_scriptId = scriptId; 216 } 217 218 void V8ConsoleMessage::reportToFrontend( 219 protocol::Console::Frontend* frontend) const { 220 DCHECK_EQ(V8MessageOrigin::kConsole, m_origin); 221 String16 level = protocol::Console::ConsoleMessage::LevelEnum::Log; 222 if (m_type == ConsoleAPIType::kDebug || m_type == ConsoleAPIType::kCount || 223 m_type == ConsoleAPIType::kTimeEnd) 224 level = protocol::Console::ConsoleMessage::LevelEnum::Debug; 225 else if (m_type == ConsoleAPIType::kError || 226 m_type == ConsoleAPIType::kAssert) 227 level = protocol::Console::ConsoleMessage::LevelEnum::Error; 228 else if (m_type == ConsoleAPIType::kWarning) 229 level = protocol::Console::ConsoleMessage::LevelEnum::Warning; 230 else if (m_type == ConsoleAPIType::kInfo) 231 level = protocol::Console::ConsoleMessage::LevelEnum::Info; 232 std::unique_ptr<protocol::Console::ConsoleMessage> result = 233 protocol::Console::ConsoleMessage::create() 234 .setSource(protocol::Console::ConsoleMessage::SourceEnum::ConsoleApi) 235 .setLevel(level) 236 .setText(m_message) 237 .build(); 238 result->setLine(static_cast<int>(m_lineNumber)); 239 result->setColumn(static_cast<int>(m_columnNumber)); 240 result->setUrl(m_url); 241 frontend->messageAdded(std::move(result)); 242 } 243 244 std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> 245 V8ConsoleMessage::wrapArguments(V8InspectorSessionImpl* session, 246 bool generatePreview) const { 247 V8InspectorImpl* inspector = session->inspector(); 248 int contextGroupId = session->contextGroupId(); 249 int contextId = m_contextId; 250 if (!m_arguments.size() || !contextId) return nullptr; 251 InspectedContext* inspectedContext = 252 inspector->getContext(contextGroupId, contextId); 253 if (!inspectedContext) return nullptr; 254 255 v8::Isolate* isolate = inspectedContext->isolate(); 256 v8::HandleScope handles(isolate); 257 v8::Local<v8::Context> context = inspectedContext->context(); 258 259 std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> args = 260 protocol::Array<protocol::Runtime::RemoteObject>::create(); 261 if (m_type == ConsoleAPIType::kTable && generatePreview) { 262 v8::Local<v8::Value> table = m_arguments[0]->Get(isolate); 263 v8::Local<v8::Value> columns = m_arguments.size() > 1 264 ? m_arguments[1]->Get(isolate) 265 : v8::Local<v8::Value>(); 266 std::unique_ptr<protocol::Runtime::RemoteObject> wrapped = 267 session->wrapTable(context, table, columns); 268 inspectedContext = inspector->getContext(contextGroupId, contextId); 269 if (!inspectedContext) return nullptr; 270 if (wrapped) 271 args->addItem(std::move(wrapped)); 272 else 273 args = nullptr; 274 } else { 275 for (size_t i = 0; i < m_arguments.size(); ++i) { 276 std::unique_ptr<protocol::Runtime::RemoteObject> wrapped = 277 session->wrapObject(context, m_arguments[i]->Get(isolate), "console", 278 generatePreview); 279 inspectedContext = inspector->getContext(contextGroupId, contextId); 280 if (!inspectedContext) return nullptr; 281 if (!wrapped) { 282 args = nullptr; 283 break; 284 } 285 args->addItem(std::move(wrapped)); 286 } 287 } 288 return args; 289 } 290 291 void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend, 292 V8InspectorSessionImpl* session, 293 bool generatePreview) const { 294 int contextGroupId = session->contextGroupId(); 295 V8InspectorImpl* inspector = session->inspector(); 296 297 if (m_origin == V8MessageOrigin::kException) { 298 std::unique_ptr<protocol::Runtime::RemoteObject> exception = 299 wrapException(session, generatePreview); 300 if (!inspector->hasConsoleMessageStorage(contextGroupId)) return; 301 std::unique_ptr<protocol::Runtime::ExceptionDetails> exceptionDetails = 302 protocol::Runtime::ExceptionDetails::create() 303 .setExceptionId(m_exceptionId) 304 .setText(exception ? m_message : m_detailedMessage) 305 .setLineNumber(m_lineNumber ? m_lineNumber - 1 : 0) 306 .setColumnNumber(m_columnNumber ? m_columnNumber - 1 : 0) 307 .build(); 308 if (m_scriptId) 309 exceptionDetails->setScriptId(String16::fromInteger(m_scriptId)); 310 if (!m_url.isEmpty()) exceptionDetails->setUrl(m_url); 311 if (m_stackTrace) { 312 exceptionDetails->setStackTrace( 313 m_stackTrace->buildInspectorObjectImpl(inspector->debugger())); 314 } 315 if (m_contextId) exceptionDetails->setExecutionContextId(m_contextId); 316 if (exception) exceptionDetails->setException(std::move(exception)); 317 frontend->exceptionThrown(m_timestamp, std::move(exceptionDetails)); 318 return; 319 } 320 if (m_origin == V8MessageOrigin::kRevokedException) { 321 frontend->exceptionRevoked(m_message, m_revokedExceptionId); 322 return; 323 } 324 if (m_origin == V8MessageOrigin::kConsole) { 325 std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> 326 arguments = wrapArguments(session, generatePreview); 327 if (!inspector->hasConsoleMessageStorage(contextGroupId)) return; 328 if (!arguments) { 329 arguments = protocol::Array<protocol::Runtime::RemoteObject>::create(); 330 if (!m_message.isEmpty()) { 331 std::unique_ptr<protocol::Runtime::RemoteObject> messageArg = 332 protocol::Runtime::RemoteObject::create() 333 .setType(protocol::Runtime::RemoteObject::TypeEnum::String) 334 .build(); 335 messageArg->setValue(protocol::StringValue::create(m_message)); 336 arguments->addItem(std::move(messageArg)); 337 } 338 } 339 Maybe<String16> consoleContext; 340 if (!m_consoleContext.isEmpty()) consoleContext = m_consoleContext; 341 frontend->consoleAPICalled( 342 consoleAPITypeValue(m_type), std::move(arguments), m_contextId, 343 m_timestamp, 344 m_stackTrace 345 ? m_stackTrace->buildInspectorObjectImpl(inspector->debugger()) 346 : nullptr, 347 std::move(consoleContext)); 348 return; 349 } 350 UNREACHABLE(); 351 } 352 353 std::unique_ptr<protocol::Runtime::RemoteObject> 354 V8ConsoleMessage::wrapException(V8InspectorSessionImpl* session, 355 bool generatePreview) const { 356 if (!m_arguments.size() || !m_contextId) return nullptr; 357 DCHECK_EQ(1u, m_arguments.size()); 358 InspectedContext* inspectedContext = 359 session->inspector()->getContext(session->contextGroupId(), m_contextId); 360 if (!inspectedContext) return nullptr; 361 362 v8::Isolate* isolate = inspectedContext->isolate(); 363 v8::HandleScope handles(isolate); 364 // TODO(dgozman): should we use different object group? 365 return session->wrapObject(inspectedContext->context(), 366 m_arguments[0]->Get(isolate), "console", 367 generatePreview); 368 } 369 370 V8MessageOrigin V8ConsoleMessage::origin() const { return m_origin; } 371 372 ConsoleAPIType V8ConsoleMessage::type() const { return m_type; } 373 374 // static 375 std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI( 376 v8::Local<v8::Context> v8Context, int contextId, int groupId, 377 V8InspectorImpl* inspector, double timestamp, ConsoleAPIType type, 378 const std::vector<v8::Local<v8::Value>>& arguments, 379 const String16& consoleContext, 380 std::unique_ptr<V8StackTraceImpl> stackTrace) { 381 v8::Isolate* isolate = v8Context->GetIsolate(); 382 383 std::unique_ptr<V8ConsoleMessage> message( 384 new V8ConsoleMessage(V8MessageOrigin::kConsole, timestamp, String16())); 385 if (stackTrace && !stackTrace->isEmpty()) { 386 message->m_url = toString16(stackTrace->topSourceURL()); 387 message->m_lineNumber = stackTrace->topLineNumber(); 388 message->m_columnNumber = stackTrace->topColumnNumber(); 389 } 390 message->m_stackTrace = std::move(stackTrace); 391 message->m_consoleContext = consoleContext; 392 message->m_type = type; 393 message->m_contextId = contextId; 394 for (size_t i = 0; i < arguments.size(); ++i) { 395 std::unique_ptr<v8::Global<v8::Value>> argument( 396 new v8::Global<v8::Value>(isolate, arguments.at(i))); 397 argument->AnnotateStrongRetainer(kGlobalConsoleMessageHandleLabel); 398 message->m_arguments.push_back(std::move(argument)); 399 message->m_v8Size += 400 v8::debug::EstimatedValueSize(isolate, arguments.at(i)); 401 } 402 if (arguments.size()) 403 message->m_message = 404 V8ValueStringBuilder::toString(arguments[0], v8Context); 405 406 v8::Isolate::MessageErrorLevel clientLevel = v8::Isolate::kMessageInfo; 407 if (type == ConsoleAPIType::kDebug || type == ConsoleAPIType::kCount || 408 type == ConsoleAPIType::kTimeEnd) { 409 clientLevel = v8::Isolate::kMessageDebug; 410 } else if (type == ConsoleAPIType::kError || 411 type == ConsoleAPIType::kAssert) { 412 clientLevel = v8::Isolate::kMessageError; 413 } else if (type == ConsoleAPIType::kWarning) { 414 clientLevel = v8::Isolate::kMessageWarning; 415 } else if (type == ConsoleAPIType::kInfo || type == ConsoleAPIType::kLog) { 416 clientLevel = v8::Isolate::kMessageInfo; 417 } 418 419 if (type != ConsoleAPIType::kClear) { 420 inspector->client()->consoleAPIMessage( 421 groupId, clientLevel, toStringView(message->m_message), 422 toStringView(message->m_url), message->m_lineNumber, 423 message->m_columnNumber, message->m_stackTrace.get()); 424 } 425 426 return message; 427 } 428 429 // static 430 std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForException( 431 double timestamp, const String16& detailedMessage, const String16& url, 432 unsigned lineNumber, unsigned columnNumber, 433 std::unique_ptr<V8StackTraceImpl> stackTrace, int scriptId, 434 v8::Isolate* isolate, const String16& message, int contextId, 435 v8::Local<v8::Value> exception, unsigned exceptionId) { 436 std::unique_ptr<V8ConsoleMessage> consoleMessage( 437 new V8ConsoleMessage(V8MessageOrigin::kException, timestamp, message)); 438 consoleMessage->setLocation(url, lineNumber, columnNumber, 439 std::move(stackTrace), scriptId); 440 consoleMessage->m_exceptionId = exceptionId; 441 consoleMessage->m_detailedMessage = detailedMessage; 442 if (contextId && !exception.IsEmpty()) { 443 consoleMessage->m_contextId = contextId; 444 consoleMessage->m_arguments.push_back( 445 std::unique_ptr<v8::Global<v8::Value>>( 446 new v8::Global<v8::Value>(isolate, exception))); 447 consoleMessage->m_v8Size += 448 v8::debug::EstimatedValueSize(isolate, exception); 449 } 450 return consoleMessage; 451 } 452 453 // static 454 std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForRevokedException( 455 double timestamp, const String16& messageText, 456 unsigned revokedExceptionId) { 457 std::unique_ptr<V8ConsoleMessage> message(new V8ConsoleMessage( 458 V8MessageOrigin::kRevokedException, timestamp, messageText)); 459 message->m_revokedExceptionId = revokedExceptionId; 460 return message; 461 } 462 463 void V8ConsoleMessage::contextDestroyed(int contextId) { 464 if (contextId != m_contextId) return; 465 m_contextId = 0; 466 if (m_message.isEmpty()) m_message = "<message collected>"; 467 Arguments empty; 468 m_arguments.swap(empty); 469 m_v8Size = 0; 470 } 471 472 // ------------------------ V8ConsoleMessageStorage ---------------------------- 473 474 V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8InspectorImpl* inspector, 475 int contextGroupId) 476 : m_inspector(inspector), m_contextGroupId(contextGroupId) {} 477 478 V8ConsoleMessageStorage::~V8ConsoleMessageStorage() { clear(); } 479 480 void V8ConsoleMessageStorage::addMessage( 481 std::unique_ptr<V8ConsoleMessage> message) { 482 int contextGroupId = m_contextGroupId; 483 V8InspectorImpl* inspector = m_inspector; 484 if (message->type() == ConsoleAPIType::kClear) clear(); 485 486 inspector->forEachSession( 487 contextGroupId, [&message](V8InspectorSessionImpl* session) { 488 if (message->origin() == V8MessageOrigin::kConsole) 489 session->consoleAgent()->messageAdded(message.get()); 490 session->runtimeAgent()->messageAdded(message.get()); 491 }); 492 if (!inspector->hasConsoleMessageStorage(contextGroupId)) return; 493 494 DCHECK(m_messages.size() <= maxConsoleMessageCount); 495 if (m_messages.size() == maxConsoleMessageCount) { 496 m_estimatedSize -= m_messages.front()->estimatedSize(); 497 m_messages.pop_front(); 498 } 499 while (m_estimatedSize + message->estimatedSize() > maxConsoleMessageV8Size && 500 !m_messages.empty()) { 501 m_estimatedSize -= m_messages.front()->estimatedSize(); 502 m_messages.pop_front(); 503 } 504 505 m_messages.push_back(std::move(message)); 506 m_estimatedSize += m_messages.back()->estimatedSize(); 507 } 508 509 void V8ConsoleMessageStorage::clear() { 510 m_messages.clear(); 511 m_estimatedSize = 0; 512 m_inspector->forEachSession(m_contextGroupId, 513 [](V8InspectorSessionImpl* session) { 514 session->releaseObjectGroup("console"); 515 }); 516 m_data.clear(); 517 } 518 519 bool V8ConsoleMessageStorage::shouldReportDeprecationMessage( 520 int contextId, const String16& method) { 521 std::set<String16>& reportedDeprecationMessages = 522 m_data[contextId].m_reportedDeprecationMessages; 523 auto it = reportedDeprecationMessages.find(method); 524 if (it != reportedDeprecationMessages.end()) return false; 525 reportedDeprecationMessages.insert(it, method); 526 return true; 527 } 528 529 int V8ConsoleMessageStorage::count(int contextId, const String16& id) { 530 return ++m_data[contextId].m_count[id]; 531 } 532 533 void V8ConsoleMessageStorage::time(int contextId, const String16& id) { 534 m_data[contextId].m_time[id] = m_inspector->client()->currentTimeMS(); 535 } 536 537 bool V8ConsoleMessageStorage::countReset(int contextId, const String16& id) { 538 std::map<String16, int>& count_map = m_data[contextId].m_count; 539 if (count_map.find(id) == count_map.end()) return false; 540 541 count_map[id] = 0; 542 return true; 543 } 544 545 double V8ConsoleMessageStorage::timeEnd(int contextId, const String16& id) { 546 std::map<String16, double>& time = m_data[contextId].m_time; 547 auto it = time.find(id); 548 if (it == time.end()) return 0.0; 549 double elapsed = m_inspector->client()->currentTimeMS() - it->second; 550 time.erase(it); 551 return elapsed; 552 } 553 554 bool V8ConsoleMessageStorage::hasTimer(int contextId, const String16& id) { 555 const std::map<String16, double>& time = m_data[contextId].m_time; 556 return time.find(id) != time.end(); 557 } 558 559 void V8ConsoleMessageStorage::contextDestroyed(int contextId) { 560 m_estimatedSize = 0; 561 for (size_t i = 0; i < m_messages.size(); ++i) { 562 m_messages[i]->contextDestroyed(contextId); 563 m_estimatedSize += m_messages[i]->estimatedSize(); 564 } 565 auto it = m_data.find(contextId); 566 if (it != m_data.end()) m_data.erase(contextId); 567 } 568 569 } // namespace v8_inspector 570