首页
社区
课程
招聘
[原创]魔改chromium, 改变 cdp 中 console 的实现, 让 devtools 无法再被检测
发表于: 2025-6-7 20:16 869

[原创]魔改chromium, 改变 cdp 中 console 的实现, 让 devtools 无法再被检测

2025-6-7 20:16
869

1deK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6*7K9s2g2S2L8X3I4S2L8W2)9J5k6i4A6Z5K9h3S2#2i4K6u0W2j5$3!0E0i4K6u0r3M7q4)9J5c8U0p5&6x3e0b7%4y4o6x3J5z5o6M7H3x3e0R3&6x3e0b7H3x3o6R3`.

改变 cdp 中 Console 的实现, 让 devtools 无法再通过 console、throw 检测

console.log 为例子, 当 JS 调用 console.log 时, V8 会通过​​外部引用​​机制调用注册的 C++ 函数, 具体实现:

而 console 所有要输出到控制台的内容都会通过 ConsoleHelper 类中的 reportCall 方法传输, 让我们来看看它的实现:

总结来说, 创建一个 V8ConsoleMessage 对象,用于封装控制台 API 调用的信息。然后如果不是清空操作,则将消息发送到宿主环境。让我们聚焦:

在 V8 的默认实现中,此方法是空方法或未实现。若运行时未主动处理,则无任何输出。需由运行时开发者通过继承 V8InspectorClient 并重写 consoleAPIMessage() 实现。

总结来说就是向 devtools 的控制台消息存储中添加一条新的控制台消息, 并将消息累积。让我们聚焦:

首先看看这个枚举对象 V8MessageOrigin::kConsole:

kConsole 代表了 console 输出的 devtools 控制台消息, kException 表示 throw 出来的异常消息, kRevokedException 表示被撤销的异常, 在 devtools 中出错的 js 会有红线标记, 改对后, 需要撤销红线。

再来看看 session->consoleAgent()->messageAdded(message.get()) consoleAgent 的 messageAdded, 在 V8 Inspector 那篇文章有提及, consoleAgent 是 devtools cdp 中控制台消息的代理, 负责向控制台输出消息。

这里判断了 devtools console 域是否打开, 打开的话就向其发送消息, 否则就是什么也不做。再跟进 reportMessage:

跟入 message->reportToFrontend:

在跟入 frontend->messageAdded, 来到了 out/Debug/gen/v8/src/inspector/protocol/Console.cpp 文件中的

看过 9ffK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6*7K9s2g2S2L8X3I4S2L8W2)9J5k6i4A6Z5K9h3S2#2i4K6u0W2j5$3!0E0i4K6u0r3M7q4)9J5c8U0p5&6x3e0b7%4y4o6x3J5z5o6M7H3x3e0R3&6x3e0b7H3x3o6R3`. 讲 V8 Inspector 这篇的小伙伴应该反应过来了, 这就是 cdp 中 Console.messageAdded 的实现, 将消息通过 cdp 协议发给 devtools。

回头再看 session->runtimeAgent()->messageAdded(message.get()) runtimeAgent 是 cdp 中的 runtime 域。和 consoleAgent 类似:

跳过上面这两个过程函数, 跟入 message->reportToFrontend

同样 frontend->exceptionThrown frontend->exceptionRevoked frontend->consoleAPICalled 是 runtime 域 cdp 协议的实现:

将各自的消息报告到 devtools。

分析到这, 有点小伙伴可能会疑惑在最开头说了 "console 所有要输出到控制台的内容都会通过 ConsoleHelper 类中的 reportCall 方法传输" , 那直接在 reportCall 一开始返回不就行了, 还扯这么多干嘛。事实上这样做只能解决 console 的检测, 无法过掉 throw 的检测(检测代码58aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6%4N6i4y4Z5k6h3&6H3k6i4u0K6L8$3&6Q4x3V1k6@1K9s2u0G2N6@1y4Z5k6h3y4C8e0%4m8W2L8R3`.`.), 为了过掉 throw 的检测, 还要继续我们的旅程, 搜索 V8MessageOrigin::kException, 定位到 v8/src/inspector/v8-console-message.ccV8ConsoleMessage::createForException 函数, 创建一个异常消息, 查看它的引用, 来到了 v8/src/inspector/v8-inspector-impl.cc 中的 V8InspectorImpl::exceptionThrown 函数:

跟进 ensureConsoleMessageStorage(groupId)->addMessage(std::move(consoleMessage)), 又回到了 V8ConsoleMessageStorage::addMessage 函数:

ok, 到此为止, 我们掌握 cdp 中向 devtools 控制台传输消息的大部分(还有一小部分是脚本段分离的报错消息, 和我们关注的 devtools 检测无关)。

让我们修改 V8ConsoleMessageStorage::addMessage 函数, 为它加一个默认参数:

四个检测:

全部通过:



附件放在 725K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6F1L8%4c8Z5K9h3&6Y4i4K6u0V1x3U0x3K6x3#2)9J5c8V1y4Z5M7X3!0E0K9i4g2E0e0h3!0V1

void V8Console::Log(const v8::debug::ConsoleCallArguments& info,
                    const v8::debug::ConsoleContext& consoleContext) {
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Log");
  ConsoleHelper(info, consoleContext, m_inspector)
      .reportCall(ConsoleAPIType::kLog);
}
void V8Console::Log(const v8::debug::ConsoleCallArguments& info,
                    const v8::debug::ConsoleContext& consoleContext) {
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.inspector"), "V8Console::Log");
  ConsoleHelper(info, consoleContext, m_inspector)
      .reportCall(ConsoleAPIType::kLog);
}
// 参数:
// - type: 控制台 API 类型(例如 log、error、trace 等)。
// - arguments: 调用时传递的参数列表。
void reportCall(ConsoleAPIType type,
                v8::MemorySpan<const v8::Local<v8::Value>> arguments) {
  // 如果当前上下文不属于任何上下文组,则直接返回,不进行任何操作。
  if (!groupId()) return;
 
  // 根据控制台消息的类型,决定捕获堆栈跟踪的深度。
  // 有些情况下可能只捕获部分堆栈,或者根本不捕获堆栈。
  std::unique_ptr<V8StackTraceImpl> stackTrace;
 
  // 根据控制台 API 类型选择合适的堆栈跟踪捕获策略。
  switch (type) {
    case ConsoleAPIType::kTrace:
      // `console.trace()` 的目的是在开发者工具控制台中输出完整的堆栈跟踪。
      // 因此,即使尚未连接调试器,也应始终尝试捕获完整的堆栈跟踪。
      stackTrace = m_inspector->debugger()->captureStackTrace(true);
      break;
 
    case ConsoleAPIType::kTimeEnd:
      // `console.time()` 和 `console.timeEnd()` API 用于性能分析。
      // 为了减少这些调用的总开销,并确保这些 API 的性能开销一致,
      // 始终只捕获顶部帧。否则,`console.timeEnd()` 的性能特性会因当前调用深度的不同而有所差异,从而歪曲结果。
      // 更多信息请参见:dc0K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0M7X3u0#2k6#2)9J5k6h3y4G2L8g2)9J5c8U0b7I4y4o6x3K6x3K6V1I4
      stackTrace = V8StackTraceImpl::capture(m_inspector->debugger(), 1);
      break;
 
    default:
      // 对于其他 API,只有在调试器连接时才捕获完整的堆栈跟踪,否则只记录顶部帧。
      stackTrace = m_inspector->debugger()->captureStackTrace(false);
      break;
  }
 
  // 创建一个控制台消息对象,用于封装控制台 API 调用的相关信息。
  std::unique_ptr<V8ConsoleMessage> message =
      V8ConsoleMessage::createForConsoleAPI(
          context(),  // 当前 V8 上下文
          contextId(),  // 当前上下文的 ID
          groupId(),  // 当前上下文所属的上下文组 ID
          m_inspector,  // V8InspectorImpl 实例
          m_inspector->client()->currentTimeMS(),  // 当前时间戳
          type,  // 控制台 API 类型
          arguments,  // 调用时传递的参数列表
          consoleContextToString(isolate(), m_consoleContext),  // 控制台上下文信息
          std::move(stackTrace)  // 捕获的堆栈跟踪
      );
 
  // 将创建的控制台消息添加到控制台消息存储中。
  consoleMessageStorage()->addMessage(std::move(message));
}
// 参数:
// - type: 控制台 API 类型(例如 log、error、trace 等)。
// - arguments: 调用时传递的参数列表。
void reportCall(ConsoleAPIType type,
                v8::MemorySpan<const v8::Local<v8::Value>> arguments) {
  // 如果当前上下文不属于任何上下文组,则直接返回,不进行任何操作。
  if (!groupId()) return;
 
  // 根据控制台消息的类型,决定捕获堆栈跟踪的深度。
  // 有些情况下可能只捕获部分堆栈,或者根本不捕获堆栈。
  std::unique_ptr<V8StackTraceImpl> stackTrace;
 
  // 根据控制台 API 类型选择合适的堆栈跟踪捕获策略。
  switch (type) {
    case ConsoleAPIType::kTrace:
      // `console.trace()` 的目的是在开发者工具控制台中输出完整的堆栈跟踪。
      // 因此,即使尚未连接调试器,也应始终尝试捕获完整的堆栈跟踪。
      stackTrace = m_inspector->debugger()->captureStackTrace(true);
      break;
 
    case ConsoleAPIType::kTimeEnd:
      // `console.time()` 和 `console.timeEnd()` API 用于性能分析。
      // 为了减少这些调用的总开销,并确保这些 API 的性能开销一致,
      // 始终只捕获顶部帧。否则,`console.timeEnd()` 的性能特性会因当前调用深度的不同而有所差异,从而歪曲结果。
      // 更多信息请参见:dc0K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0M7X3u0#2k6#2)9J5k6h3y4G2L8g2)9J5c8U0b7I4y4o6x3K6x3K6V1I4
      stackTrace = V8StackTraceImpl::capture(m_inspector->debugger(), 1);
      break;
 
    default:
      // 对于其他 API,只有在调试器连接时才捕获完整的堆栈跟踪,否则只记录顶部帧。
      stackTrace = m_inspector->debugger()->captureStackTrace(false);
      break;
  }
 
  // 创建一个控制台消息对象,用于封装控制台 API 调用的相关信息。
  std::unique_ptr<V8ConsoleMessage> message =
      V8ConsoleMessage::createForConsoleAPI(
          context(),  // 当前 V8 上下文
          contextId(),  // 当前上下文的 ID
          groupId(),  // 当前上下文所属的上下文组 ID
          m_inspector,  // V8InspectorImpl 实例
          m_inspector->client()->currentTimeMS(),  // 当前时间戳
          type,  // 控制台 API 类型
          arguments,  // 调用时传递的参数列表
          consoleContextToString(isolate(), m_consoleContext),  // 控制台上下文信息
          std::move(stackTrace)  // 捕获的堆栈跟踪
      );
 
  // 将创建的控制台消息添加到控制台消息存储中。
  consoleMessageStorage()->addMessage(std::move(message));
}
// 参数:
// - v8Context: 当前的 V8 上下文。
// - contextId: 当前上下文的 ID。
// - groupId: 当前上下文所属的上下文组 ID。
// - inspector: V8InspectorImpl 实例,用于访问调试器功能。
// - timestamp: 当前时间戳。
// - type: 控制台 API 类型(例如 log、error、warning 等)。
// - arguments: 控制台 API 调用时传递的参数列表。
// - consoleContext: 控制台上下文信息。
// - stackTrace: 捕获的堆栈跟踪。
std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI(
    v8::Local<v8::Context> v8Context, int contextId, int groupId,
    V8InspectorImpl* inspector, double timestamp, ConsoleAPIType type,
    v8::MemorySpan<const v8::Local<v8::Value>> arguments,
    const String16& consoleContext,
    std::unique_ptr<V8StackTraceImpl> stackTrace) {
  // 获取当前的 V8 引擎隔离区。
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
 
  // 创建一个 V8ConsoleMessage 对象,初始化其来源为控制台,时间戳为当前时间,消息为空字符串。
  std::unique_ptr<V8ConsoleMessage> message(
      new V8ConsoleMessage(V8MessageOrigin::kConsole, timestamp, String16()));
 
  // 如果有堆栈跟踪且堆栈跟踪不为空,则提取堆栈顶部的信息。
  if (stackTrace && !stackTrace->isEmpty()) {
    message->m_url = toString16(stackTrace->topSourceURL());  // 堆栈顶部的源代码 URL
    message->m_lineNumber = stackTrace->topLineNumber();     // 堆栈顶部的行号
    message->m_columnNumber = stackTrace->topColumnNumber(); // 堆栈顶部的列号
  }
 
  // 将堆栈跟踪对象附加到消息中。
  message->m_stackTrace = std::move(stackTrace);
 
  // 设置控制台上下文信息。
  message->m_consoleContext = consoleContext;
 
  // 设置控制台 API 类型。
  message->m_type = type;
 
  // 设置上下文 ID。
  message->m_contextId = contextId;
 
  // 遍历参数列表,将每个参数封装为全局句柄并附加到消息中。
  for (v8::Local<v8::Value> arg : arguments) {
    std::unique_ptr<v8::Global<v8::Value>> argument(
        new v8::Global<v8::Value>(isolate, arg)); // 创建全局句柄
    argument->AnnotateStrongRetainer(kGlobalConsoleMessageHandleLabel); // 标记强引用
    message->m_arguments.push_back(std::move(argument)); // 将参数附加到消息中
    message->m_v8Size += v8::debug::EstimatedValueSize(isolate, arg); // 累加参数的大小
  }
 
  // 将参数列表中的值转换为字符串,并拼接成一条完整的控制台消息。
  bool sep = false; // 用于控制参数之间的分隔符
  for (v8::Local<v8::Value> arg : arguments) {
    if (sep) {
      message->m_message += String16(" "); // 在参数之间添加空格
    } else {
      sep = true;
    }
    message->m_message += V8ValueStringBuilder::toString(arg, v8Context); // 将参数转换为字符串并拼接
  }
 
  // 根据控制台 API 类型,确定消息的级别。
  v8::Isolate::MessageErrorLevel clientLevel = v8::Isolate::kMessageInfo;
  if (type == ConsoleAPIType::kDebug || type == ConsoleAPIType::kCount ||
      type == ConsoleAPIType::kTimeEnd) {
    clientLevel = v8::Isolate::kMessageDebug; // 调试级别
  } else if (type == ConsoleAPIType::kError ||
             type == ConsoleAPIType::kAssert) {
    clientLevel = v8::Isolate::kMessageError; // 错误级别
  } else if (type == ConsoleAPIType::kWarning) {
    clientLevel = v8::Isolate::kMessageWarning; // 警告级别
  } else if (type == ConsoleAPIType::kInfo) {
    clientLevel = v8::Isolate::kMessageInfo; // 信息级别
  } else if (type == ConsoleAPIType::kLog) {
    clientLevel = v8::Isolate::kMessageLog; // 日志级别
  }
 
  // 如果不是清空操作,则将消息发送到宿主环境。
  if (type != ConsoleAPIType::kClear) {
    inspector->client()->consoleAPIMessage(
        groupId, clientLevel, toStringView(message->m_message), // 消息内容
        toStringView(message->m_url), message->m_lineNumber,    // 源代码位置
        message->m_columnNumber, message->m_stackTrace.get()); // 堆栈跟踪
  }
 
  // 返回创建的 V8ConsoleMessage 对象。
  return message;
}
// 参数:
// - v8Context: 当前的 V8 上下文。
// - contextId: 当前上下文的 ID。
// - groupId: 当前上下文所属的上下文组 ID。
// - inspector: V8InspectorImpl 实例,用于访问调试器功能。
// - timestamp: 当前时间戳。
// - type: 控制台 API 类型(例如 log、error、warning 等)。
// - arguments: 控制台 API 调用时传递的参数列表。
// - consoleContext: 控制台上下文信息。
// - stackTrace: 捕获的堆栈跟踪。
std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI(
    v8::Local<v8::Context> v8Context, int contextId, int groupId,
    V8InspectorImpl* inspector, double timestamp, ConsoleAPIType type,
    v8::MemorySpan<const v8::Local<v8::Value>> arguments,
    const String16& consoleContext,
    std::unique_ptr<V8StackTraceImpl> stackTrace) {
  // 获取当前的 V8 引擎隔离区。
  v8::Isolate* isolate = v8::Isolate::GetCurrent();
 
  // 创建一个 V8ConsoleMessage 对象,初始化其来源为控制台,时间戳为当前时间,消息为空字符串。
  std::unique_ptr<V8ConsoleMessage> message(
      new V8ConsoleMessage(V8MessageOrigin::kConsole, timestamp, String16()));
 
  // 如果有堆栈跟踪且堆栈跟踪不为空,则提取堆栈顶部的信息。
  if (stackTrace && !stackTrace->isEmpty()) {
    message->m_url = toString16(stackTrace->topSourceURL());  // 堆栈顶部的源代码 URL
    message->m_lineNumber = stackTrace->topLineNumber();     // 堆栈顶部的行号
    message->m_columnNumber = stackTrace->topColumnNumber(); // 堆栈顶部的列号
  }
 
  // 将堆栈跟踪对象附加到消息中。
  message->m_stackTrace = std::move(stackTrace);
 
  // 设置控制台上下文信息。
  message->m_consoleContext = consoleContext;
 
  // 设置控制台 API 类型。
  message->m_type = type;
 
  // 设置上下文 ID。
  message->m_contextId = contextId;
 
  // 遍历参数列表,将每个参数封装为全局句柄并附加到消息中。
  for (v8::Local<v8::Value> arg : arguments) {
    std::unique_ptr<v8::Global<v8::Value>> argument(
        new v8::Global<v8::Value>(isolate, arg)); // 创建全局句柄
    argument->AnnotateStrongRetainer(kGlobalConsoleMessageHandleLabel); // 标记强引用
    message->m_arguments.push_back(std::move(argument)); // 将参数附加到消息中
    message->m_v8Size += v8::debug::EstimatedValueSize(isolate, arg); // 累加参数的大小
  }
 
  // 将参数列表中的值转换为字符串,并拼接成一条完整的控制台消息。
  bool sep = false; // 用于控制参数之间的分隔符
  for (v8::Local<v8::Value> arg : arguments) {
    if (sep) {
      message->m_message += String16(" "); // 在参数之间添加空格
    } else {
      sep = true;
    }
    message->m_message += V8ValueStringBuilder::toString(arg, v8Context); // 将参数转换为字符串并拼接
  }
 
  // 根据控制台 API 类型,确定消息的级别。
  v8::Isolate::MessageErrorLevel clientLevel = v8::Isolate::kMessageInfo;
  if (type == ConsoleAPIType::kDebug || type == ConsoleAPIType::kCount ||
      type == ConsoleAPIType::kTimeEnd) {
    clientLevel = v8::Isolate::kMessageDebug; // 调试级别
  } else if (type == ConsoleAPIType::kError ||
             type == ConsoleAPIType::kAssert) {
    clientLevel = v8::Isolate::kMessageError; // 错误级别
  } else if (type == ConsoleAPIType::kWarning) {
    clientLevel = v8::Isolate::kMessageWarning; // 警告级别
  } else if (type == ConsoleAPIType::kInfo) {
    clientLevel = v8::Isolate::kMessageInfo; // 信息级别
  } else if (type == ConsoleAPIType::kLog) {
    clientLevel = v8::Isolate::kMessageLog; // 日志级别
  }
 
  // 如果不是清空操作,则将消息发送到宿主环境。
  if (type != ConsoleAPIType::kClear) {
    inspector->client()->consoleAPIMessage(
        groupId, clientLevel, toStringView(message->m_message), // 消息内容
        toStringView(message->m_url), message->m_lineNumber,    // 源代码位置
        message->m_columnNumber, message->m_stackTrace.get()); // 堆栈跟踪
  }
 
  // 返回创建的 V8ConsoleMessage 对象。
  return message;
}
inspector->client()->consoleAPIMessage(
    groupId, clientLevel, toStringView(message->m_message), // 消息内容
    toStringView(message->m_url), message->m_lineNumber,    // 源代码位置
    message->m_columnNumber, message->m_stackTrace.get()); // 堆栈跟踪
inspector->client()->consoleAPIMessage(
    groupId, clientLevel, toStringView(message->m_message), // 消息内容
    toStringView(message->m_url), message->m_lineNumber,    // 源代码位置
    message->m_columnNumber, message->m_stackTrace.get()); // 堆栈跟踪
// 参数:
// - message: 要添加的控制台消息对象。
void V8ConsoleMessageStorage::addMessage(
    std::unique_ptr<V8ConsoleMessage> message) {
  // 获取当前上下文组 ID 和关联的 V8InspectorImpl 实例。
  int contextGroupId = m_contextGroupId;
  V8InspectorImpl* inspector = m_inspector;
 
  // 如果消息类型是清空控制台(kClear),则清空当前存储的所有消息。
  if (message->type() == ConsoleAPIType::kClear) clear();
 
  // 记录控制台消息事件,用于跟踪消息的来源和类型。
  TraceV8ConsoleMessageEvent(message->origin(), message->type());
 
  // 遍历当前上下文组的所有调试会话,并通知会话中的控制台代理和运行时代理。
  inspector->forEachSession(
      contextGroupId, [&message](V8InspectorSessionImpl* session) {
        if (message->origin() == V8MessageOrigin::kConsole)
          session->consoleAgent()->messageAdded(message.get());
        session->runtimeAgent()->messageAdded(message.get());
      });
 
  // 如果当前上下文组没有启用控制台消息存储,则直接返回。
  if (!inspector->hasConsoleMessageStorage(contextGroupId)) return;
 
  // 确保消息队列的大小不超过最大限制。
  DCHECK(m_messages.size() <= maxConsoleMessageCount);
 
  // 如果消息队列已满(达到最大消息数量限制),则移除最早添加的消息。
  if (m_messages.size() == maxConsoleMessageCount) {
    m_estimatedSize -= m_messages.front()->estimatedSize(); // 减少总大小估计
    m_messages.pop_front(); // 移除最早的消息
  }
 
  // 如果添加当前消息后,总大小估计超过最大 V8 大小限制,则移除最早的消息,直到总大小符合限制。
  while (m_estimatedSize + message->estimatedSize() > maxConsoleMessageV8Size &&
         !m_messages.empty()) {
    m_estimatedSize -= m_messages.front()->estimatedSize(); // 减少总大小估计
    m_messages.pop_front(); // 移除最早的消息
  }
 
  // 将新消息添加到消息队列中。
  m_messages.push_back(std::move(message));
  // 更新总大小估计。
  m_estimatedSize += m_messages.back()->estimatedSize();
}
// 参数:
// - message: 要添加的控制台消息对象。
void V8ConsoleMessageStorage::addMessage(
    std::unique_ptr<V8ConsoleMessage> message) {
  // 获取当前上下文组 ID 和关联的 V8InspectorImpl 实例。
  int contextGroupId = m_contextGroupId;
  V8InspectorImpl* inspector = m_inspector;
 
  // 如果消息类型是清空控制台(kClear),则清空当前存储的所有消息。
  if (message->type() == ConsoleAPIType::kClear) clear();
 
  // 记录控制台消息事件,用于跟踪消息的来源和类型。
  TraceV8ConsoleMessageEvent(message->origin(), message->type());
 
  // 遍历当前上下文组的所有调试会话,并通知会话中的控制台代理和运行时代理。
  inspector->forEachSession(
      contextGroupId, [&message](V8InspectorSessionImpl* session) {
        if (message->origin() == V8MessageOrigin::kConsole)
          session->consoleAgent()->messageAdded(message.get());
        session->runtimeAgent()->messageAdded(message.get());
      });
 
  // 如果当前上下文组没有启用控制台消息存储,则直接返回。
  if (!inspector->hasConsoleMessageStorage(contextGroupId)) return;
 
  // 确保消息队列的大小不超过最大限制。
  DCHECK(m_messages.size() <= maxConsoleMessageCount);
 
  // 如果消息队列已满(达到最大消息数量限制),则移除最早添加的消息。
  if (m_messages.size() == maxConsoleMessageCount) {
    m_estimatedSize -= m_messages.front()->estimatedSize(); // 减少总大小估计
    m_messages.pop_front(); // 移除最早的消息
  }
 
  // 如果添加当前消息后,总大小估计超过最大 V8 大小限制,则移除最早的消息,直到总大小符合限制。
  while (m_estimatedSize + message->estimatedSize() > maxConsoleMessageV8Size &&
         !m_messages.empty()) {
    m_estimatedSize -= m_messages.front()->estimatedSize(); // 减少总大小估计
    m_messages.pop_front(); // 移除最早的消息
  }
 
  // 将新消息添加到消息队列中。
  m_messages.push_back(std::move(message));
  // 更新总大小估计。
  m_estimatedSize += m_messages.back()->estimatedSize();
}
inspector->forEachSession(
    contextGroupId, [&message](V8InspectorSessionImpl* session) {
    if (message->origin() == V8MessageOrigin::kConsole)
        session->consoleAgent()->messageAdded(message.get());
    session->runtimeAgent()->messageAdded(message.get());
    });
inspector->forEachSession(
    contextGroupId, [&message](V8InspectorSessionImpl* session) {
    if (message->origin() == V8MessageOrigin::kConsole)
        session->consoleAgent()->messageAdded(message.get());
    session->runtimeAgent()->messageAdded(message.get());
    });
enum class V8MessageOrigin { kConsole, kException, kRevokedException };
enum class V8MessageOrigin { kConsole, kException, kRevokedException };
void V8ConsoleAgentImpl::messageAdded(V8ConsoleMessage* message) {
  if (m_enabled) reportMessage(message, true);
}
void V8ConsoleAgentImpl::messageAdded(V8ConsoleMessage* message) {
  if (m_enabled) reportMessage(message, true);
}
bool V8ConsoleAgentImpl::reportMessage(V8ConsoleMessage* message,
                                       bool generatePreview) {
  DCHECK_EQ(V8MessageOrigin::kConsole, message->origin());
  message->reportToFrontend(&m_frontend);
  m_frontend.flush();
  return m_session->inspector()->hasConsoleMessageStorage(
      m_session->contextGroupId());
}
bool V8ConsoleAgentImpl::reportMessage(V8ConsoleMessage* message,
                                       bool generatePreview) {
  DCHECK_EQ(V8MessageOrigin::kConsole, message->origin());
  message->reportToFrontend(&m_frontend);
  m_frontend.flush();
  return m_session->inspector()->hasConsoleMessageStorage(
      m_session->contextGroupId());
}
void V8ConsoleMessage::reportToFrontend(
    protocol::Console::Frontend* frontend) const {
  DCHECK_EQ(V8MessageOrigin::kConsole, m_origin);
  String16 level = protocol::Console::ConsoleMessage::LevelEnum::Log;
  if (m_type == ConsoleAPIType::kDebug || m_type == ConsoleAPIType::kCount ||
      m_type == ConsoleAPIType::kTimeEnd)
    level = protocol::Console::ConsoleMessage::LevelEnum::Debug;
  else if (m_type == ConsoleAPIType::kError ||
           m_type == ConsoleAPIType::kAssert)
    level = protocol::Console::ConsoleMessage::LevelEnum::Error;
  else if (m_type == ConsoleAPIType::kWarning)
    level = protocol::Console::ConsoleMessage::LevelEnum::Warning;
  else if (m_type == ConsoleAPIType::kInfo)
    level = protocol::Console::ConsoleMessage::LevelEnum::Info;
  std::unique_ptr<protocol::Console::ConsoleMessage> result =
      protocol::Console::ConsoleMessage::create()
          .setSource(protocol::Console::ConsoleMessage::SourceEnum::ConsoleApi)
          .setLevel(level)
          .setText(m_message)
          .build();
  if (m_lineNumber) result->setLine(m_lineNumber);
  if (m_columnNumber) result->setColumn(m_columnNumber);
  if (!m_url.isEmpty()) result->setUrl(m_url);
  frontend->messageAdded(std::move(result));
}
void V8ConsoleMessage::reportToFrontend(
    protocol::Console::Frontend* frontend) const {
  DCHECK_EQ(V8MessageOrigin::kConsole, m_origin);
  String16 level = protocol::Console::ConsoleMessage::LevelEnum::Log;
  if (m_type == ConsoleAPIType::kDebug || m_type == ConsoleAPIType::kCount ||
      m_type == ConsoleAPIType::kTimeEnd)
    level = protocol::Console::ConsoleMessage::LevelEnum::Debug;
  else if (m_type == ConsoleAPIType::kError ||
           m_type == ConsoleAPIType::kAssert)
    level = protocol::Console::ConsoleMessage::LevelEnum::Error;
  else if (m_type == ConsoleAPIType::kWarning)
    level = protocol::Console::ConsoleMessage::LevelEnum::Warning;
  else if (m_type == ConsoleAPIType::kInfo)
    level = protocol::Console::ConsoleMessage::LevelEnum::Info;
  std::unique_ptr<protocol::Console::ConsoleMessage> result =
      protocol::Console::ConsoleMessage::create()
          .setSource(protocol::Console::ConsoleMessage::SourceEnum::ConsoleApi)
          .setLevel(level)
          .setText(m_message)
          .build();
  if (m_lineNumber) result->setLine(m_lineNumber);
  if (m_columnNumber) result->setColumn(m_columnNumber);
  if (!m_url.isEmpty()) result->setUrl(m_url);
  frontend->messageAdded(std::move(result));
}

[培训]科锐逆向工程师培训第53期2025年7月8日开班!

最后于 2025-6-9 19:13 被nothing233编辑 ,原因:
收藏
免费 8
支持
分享
最新回复 (3)
雪    币: 6
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2025-6-7 20:25
0
雪    币: 22
活跃值: (357)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
3天前
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
感谢大佬分享
1天前
0
游客
登录 | 注册 方可回帖
返回