前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Chrome DevTools 远程调试协议分析及实战

Chrome DevTools 远程调试协议分析及实战

作者头像
winty
发布于 2020-04-26 03:31:56
发布于 2020-04-26 03:31:56
7.6K00
代码可运行
举报
文章被收录于专栏:前端Q前端Q
运行总次数:0
代码可运行

Chrome DevTools 可以说是前端开发最常用的工具,无论是普通页面、移动端 webview、小程序、甚至 node 应用,都可以用它来调试。

Chrome DevTools 提供的功能非常丰富,包含 DOM、debugger、网络、性能等许多能力。

为什么 Chrome DevTools 能够适用这么多场景?如何把 Chrome DevTools 移植到新的应用场景?Chrome DevTools 提供的功能我们能不能拆解出模块单独使用?今天我们来尝试探索这些问题。

Chrome DevTools 组成

Chrome DevTools 包括四个部分:

  • 调试器协议:devtools-protocol[1],基于 json rpc 2.0。
  • 调试器后端:实现了调试协议的可调试实体,例如 chrome、node.js。
  • 调试器前端:通常指内嵌在 chrome 中的调试面板,通过调试器协议和调试器后端交互,除此之外还有 Puppeteer[2]ndb[3] 等。
  • 消息通道:前后端通信方式,例如 websocket、usb、adb 等,本质都是 socket 通信。

Chrome DevTools

我们可以看到,Chrome DevTools 的核心是调试器协议。

Chrome DevTools Protocol

协议按域「Domain」划分能力,每个域下有 Method、Event 和 Types。

Method 对应 socket 通信的请求/响应模式,Events 对应 socket 通信的发布/订阅模式,Types 为交互中使用到的实体。

例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# https://chromedevtools.github.io/devtools-protocol/1-3/Log
Log Domain 

Provides access to log entries.

Methods

Log.clear
Log.disable
Log.enable
Log.startViolationsReport
Log.stopViolationsReport

Events

Log.entryAdded

Types

LogEntry
ViolationSetting

一个调试器后端,应当实现对 Method 的响应,并在适当的时候发布 Event。

一个调试器前端,应当使用 Method 请求需要的数据,订阅需要的 Event。

browser_protocol & js_protocol

协议分为 browser_protocol[4]js_protocol[5] 两种。

browser_protocol 是浏览器后端使用,js_protocol 是 node 后端使用。除此之外,还有对应的 Typescript 类型定义[6]

js_protocol 只有以下四个域「Console、Schema 已废弃」:

  • Debugger
  • Profiler
  • Runtime 「js Runtime」
  • HeapProfiler

能力比 browser_protocol 少很多,这是因为页面有相对固定的工作模式,node 应用却千差万别。

browser_protocol 主要有以下几个域:

  • DOM
  • DOMDebugger
  • Emulation 「环境模拟」
  • Network
  • Page
  • Performance
  • Profiler

涉及了页面开发的方方面面。

Chrome DevTools Frontend

devtools-frontend 即调试器前端,我们平常使用的调试面板,其源码可以从 ChromeDevTools/devtools-frontend[7] 获得。我们先来看一下它是怎么工作的。

项目结构

ChromeDevTools/devtools-frontend[8] 下载源码后,我们进入 front_end 目录,可以看到如下结构:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# tree -L 1
.
├── accessibility
├── accessibility_test_runner
│   ├── AccessibilityPaneTestRunner.js
│   └── module.json
├── animation
├── application_test_runner
├── axe_core_test_runner
...
├── input
├── inspector.html
├── inspector.js
├── inspector.json
├── network
├── network_test_runner
├── node_app.html
├── node_app.js
├── node_app.json
├── worker_app.html
├── worker_app.js
└── worker_app.json

front_end 目录下的每一个 json 文件会有一个同名的 js 文件,有的还会有一个同名的 html 文件。

它们都代表一个应用,如 inspector.json 是其配置文件。如果此应用有界面,则带有 html,可以在浏览器中打开 html 运行应用。

我们可以看到熟悉的应用,inspector、node、devtools、ndb 等等。

devtools_app 即我们常用的调试面板,如图所示:

devtools

inspector 在 devtools_app 基础上增加了页面快照,可以实时看到页面的变化,并且可以在页面快照上交互,如图所示:

inspector


以 devtools_app 为例,我们来看配置文件的语义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// devtools_frontend/front_end/devtools_app.json
{
  "modules" : [
    { "name": "emulation", "type": "autostart" },
    { "name": "inspector_main", "type": "autostart" },
    { "name": "mobile_throttling", "type": "autostart" },
    ...
    { "name": "timeline" },
    { "name": "timeline_model" },
    { "name": "web_audio" },
    { "name": "media" }
  ],
  "extends": "shell",
  "has_html": true
}

  • modules 表示此应用包含的模块,每个模块都对应 front_end 目录下的一个目录。
  • extends 表示此应用是否继承自另外一个应用,devtools_app 继承自 shell 应用,我们可以在 front_end 目录下看到 shell.js、shell.json。
  • has_html 表示此应用有 html 界面,即同名的 devtools_app.json。

我们再来看一下模块,所有的模块都平级放在 front_end 目录下,不存在嵌套,每个模块都有一个 module.json 文件,表示此模块的配置。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
    "extensions": [
        {
            "type": "view",
            "location": "drawer-view"
        }
    ],
    "dependencies": [
        "elements"
    ],
    "scripts": [],
    "modules": [
        "animation.js",
        "animation-legacy.js",
        "AnimationUI.js"
    ],
    "resources": [
        "animationScreenshotPopover.css",
        "animationTimeline.css"
    ]
}

  • extensions 表示此模块的自定义属性。
  • dependencies 表示此模块依赖的模块。
  • modules 表示此模块包括的 js 文件。
  • resources 表示此模块包括的静态资源,主要是 css。

之所以有这些配置,是因为,front_end 有自己的一套模块加载逻辑,和通常的 node 应用和前端应用都不一样。

初始化

front_end 各个应用初始化的过程类似,基本如下:

  • 从对应的 json 文件中加载配置,并根据配置加载需要的模块
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// devtools-frontend/front_end/RuntimeInstantiator.js
export async function startApplication(appName) {
  console.timeStamp('Root.Runtime.startApplication');
  const allDescriptorsByName = {};
  for (let i = 0; i < Root.allDescriptors.length; ++i) {
    const d = Root.allDescriptors[i];
    allDescriptorsByName[d['name']] = d;
  }
  if (!Root.applicationDescriptor) {
    // 加载应用配置 <appName>.json
    let data = await RootModule.Runtime.loadResourcePromise(appName + '.json');
    Root.applicationDescriptor = JSON.parse(data);
    let descriptor = Root.applicationDescriptor;
    while (descriptor.extends) {
      // 加载父级配置直到没有父级
      data = await RootModule.Runtime.loadResourcePromise(descriptor.extends + '.json');
      descriptor = JSON.parse(data);
      Root.applicationDescriptor.modules = descriptor.modules.concat(Root.applicationDescriptor.modules);
    }
  }
  const configuration = Root.applicationDescriptor.modules;
  const moduleJSONPromises = [];
  const coreModuleNames = [];
  for (let i = 0; i < configuration.length; ++i) {
    const descriptor = configuration[i];
    const name = descriptor['name'];
    const moduleJSON = allDescriptorsByName[name];
    // 根据每个模块的 module.json 加载模块
    if (moduleJSON) { 
      moduleJSONPromises.push(Promise.resolve(moduleJSON));
    } else {
      moduleJSONPromises.push(
          RootModule.Runtime.loadResourcePromise(name + '/module.json').then(JSON.parse.bind(JSON)));
    }
  }
    // ...
}
  • 实例化模块

虽然 js 代码都是通过 import 来引用依赖,但是 front_end 并非使用 import 来加载模块,而是自己写了一个模块加载逻辑,先请求模块文件,然后在根据依赖关系把代码 eval。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// devtools-frontend/front_end/root/Runtime.js
function evaluateScript(sourceURL, scriptSource) {
    loadedScripts[sourceURL] = true;
    if (!scriptSource) {
      // Do not reject, as this is normal in the hosted mode.
      console.error('Empty response arrived for script \'' + sourceURL + '\'');
      return;
    }
    self.eval(scriptSource + '\n//# sourceURL=' + sourceURL);
}
  • 模块加载完成后,才是真正的初始化

作为调试器前端,socket 通信是不可或缺的,初始化的主要工作就是对调试器后端建立 socket 连接,准备好调试协议。

对于页面应用来说,还需要初始化 UI,front_end 未使用任何渲染框架,全部都是原生 DOM 操作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// devtools-frontend/front_end/main/MainImpl.js
new MainImpl(); // 初始化SDK(协议),初始化socket连接,初始化通信

应用

远程调试

我们可以用 front_end 来实现远程调试页面,例如:用户在自己的 PC、APP 上操作页面,开发人员在另外一台电脑上观察页面、网络、控制台里发生的变化,甚至通过协议控制页面。

开启调试端口

不同后端打开调试端口的方式不同,以 chrome 为例:

chrome 和内嵌的调试面板使用 Embedder channel 通信,这个消息通道不能被用来做远程调试,远程调试我们需要使用 websocket channel。

使用 websocket channel 我们还需要打开 chrome 的远程调试端口,以命令行参数 remote-debugging-port 打开 chrome。

[path]/chrome.exe --remote-debugging-port=9222

或者使用脚本 devtools-frontend/scripts/hosted_mode/launch_chrome.js

调试端口打开后,chrome 会启动一个内置的 http 服务,我们可以从中获取 chrome 的基本信息,其中最重要的是各个 tab 页的 websocket 通信地址。

chrome 提供的 http 接口如下,访问方式全部为 GET:

  • /json/protocol 获取当前 chrome 支持的协议,协议为 json 格式。
  • /json/list 获取可调试的目标列表,一般每个 tab 就是一个可调试目标,可调试目标的 webSocketDebuggerUrl 属性就是我们需要的 websocket 通信地址。例如:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[{
   "description": "",
   "devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/8ED9DABCE2A6BD36952657AEBAA0DE02",
   "faviconUrl": "https://github.githubassets.com/favicon.ico",
   "id": "8ED9DABCE2A6BD36952657AEBAA0DE02",
   "title": "GitHub - Unitech/pm2: Node.js Production Process Manager with a built-in Load Balancer.",
   "type": "page",
   "url": "https://github.com/Unitech/pm2",
   "webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/8ED9DABCE2A6BD36952657AEBAA0DE02"
}]

  • /json/new 创建新的 tab 页
  • /json/activate/:id 根据 id 激活 tab 页
  • /json/close/:id 根据 id 关闭 tab 页
  • /json/version 获取浏览器/协议/v8/webkit 版本,例如:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
   "Browser": "Chrome/80.0.3987.149",
   "Protocol-Version": "1.3",
   "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36",
   "V8-Version": "8.0.426.27",
   "WebKit-Version": "537.36 (@5f4eb224680e5d7dca88504586e9fd951840cac6)",
   "webSocketDebuggerUrl": "ws://localhost:9222/devtools/browser/ad007235-aa36-4465-beb1-70864067ea49"
}

注意:这些接口都不能跨域,可以通过服务器访问,或者直接在浏览器中打开,但是不能使用 ajax 访问。

连接

获取到 webSocketDebuggerUrl 后,我们就可以用此连接来调试页面。front_end 下的 devtool、inspector 等应用均可使用。

观察 初始化 socket 链接的代码可以得知,我们需要把 webSocketDebuggerUrl 以 url 参数的形式传给应用,参数名为 ws。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// devtools-frontend/front_end/sdk/Connections.js
export function _createMainConnection(websocketConnectionLost) {
  const wsParam = Root.Runtime.queryParam('ws');
  const wssParam = Root.Runtime.queryParam('wss');
  if (wsParam || wssParam) {
    const ws = wsParam ? `ws://${wsParam}` : `wss://${wssParam}`;
    return new WebSocketConnection(ws, websocketConnectionLost); 
  }
  if (Host.InspectorFrontendHost.InspectorFrontendHostInstance.isHostedMode()) {
    return new StubConnection();
  }
  return new MainConnection();
}

我们在 front_end 目录下启动静态服务器。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
serve -p 8002

然后访问 http://localhost:8002/inspector?ws=localhost:9222/devtools/page/8ED9DABCE2A6BD36952657AEBAA0DE02

我们可以看到页面上的一切变化都会出现在 inspector 的界面中。

跨域

如果前端和后端都在同一网段,我们使用以上方式就可以进行调试了,但是如果前后端在不同的内网内,我们如何实现远程调试?

只要我们有一台放在公网的服务器就可以调试。

前端和后端都在各自的内网内,因此相互之间肯定无法直接访问。但是它们都可以访问公网的服务器,并且,websocket 是可以跨域的。

因此我们可以通过两次转发,让不同内网的前端和后端交互,具体步骤如下:

  • 创建一个转发用的 websocket 服务,放在公网。
  • 我们在被调试的页面中增加一个自定义的 launcher.js,对公网的 websocket 服务建立连接,把页面的基本信息传递给服务器,同时通过 json/list 接口找出自身的 webSocketDebuggerUrl 建立连接。

注意:因为 json/list 是 http 接口,无法跨域,这一步必须手动获取,然后把 webSocketDebuggerUrl 放在 url 参数上传给 launcher.js

手动获取 webSocketDebuggerUrl

  • 把 front_end 页面 url 的 ws 参数改为公网的 websocket 服务。

这样,我们的 socket 链路上有了四个节点,分别是:

  • front_end(调试器前端)
  • 公网服务器(server)
  • laucher.js
  • debugger(调试器后端)

server 和 laucher 完全作为转发器,转发两边传来的信息,即可实现 front_end 到 debugger 的交互。

注意:如果 front_end 请求了 Network.enable, 就不能把 laucher.js 所在的页面作为调试页面,因为 laucher.js 收到 debugger 传来的数据会触发 Network.webSocketFrameReceived 推送,这个推送本身又会触发 Network.webSocketFrameReceived ,造成无限循环。处理方式有两种,一是拦截掉 Network.enable 请求,这样会取消掉所有的 Network 的推送。二是不把 laucher.js 所在的页面作为调试页面,仅作数据中转用。

远程调试

websocket 服务代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// server.js
var WebSocketServer = require('websocket').server;
var http = require('http');
var server = http.createServer(function(request, response) {
    response.writeHead(404);
    response.end();
});
server.listen(3232, function() {
    console.log((new Date()) + ' Server is listening on port 3232');
});
wsServer = new WebSocketServer({
    httpServer: server
});
var frontendConnection;
var debugConnection;

wsServer.on('request', async function(request) {
    var requestedProtocols = request.requestedProtocols;
    if(requestedProtocols.indexOf("frontend") != -1){  // 处理来自调试器前端的请求
        frontendConnection = request.accept('frontend', request.origin);
        frontendConnection.on('message', function(message) {
            if (message.type === 'utf8') {
                // 把调试器前端的请求直接转发给被调试页面
                if(debugConnection){
                    debugConnection.sendUTF(message.utf8Data)
                }else{
                    frontendConnection.sendUTF(JSON.stringify({msg:'调试器后端未准备好,先打开被调试的页面'}))
                }  
            }
        })
        frontendConnection.on('close', function(reasonCode, description) {
            console.log('frontendConnection disconnected.');
        });
    }
    if(requestedProtocols.indexOf("remote-debug") != -1){ // 处理来自被调试页面的请求
        debugConnection = request.accept('remote-debug', request.origin);
        debugConnection.on('message', function(message) {
            if (message.type === 'utf8') {
                var feed = JSON.parse(message.utf8Data);
                if(feed.type == "remote_debug_page"){   // 确认连接
                    debugConnection.sendUTF(JSON.stringify({"type":"start_debug"}));
                }else if(feed.type == "start_debug_ready"){
                    // 被调试页面已连接好
                } else{
                    // 把被调试页面的数据全部转发给调试器前端
                    if(frontendConnection){
                        frontendConnection.sendUTF(message.utf8Data)
                    }else{
                        console.log('无法转发给frontend,没有建立连接')
                    }
                }                
            }
        });
        debugConnection.on('close', function(reasonCode, description) {
            console.log((new Date()) + ' Peer remote' + debugConnection.remoteAddress + ' disconnected.');
        });
    }
});

laucher.js 代码示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

var host = "localhost:3232"
var ws = new WebSocket(`ws://${host}`,'remote-debug');       
var search = location.search.slice(1);
var urlParams = {};
search.split('&').forEach(s=>{
    var pair = s.split('=');
    if(pair.length == 2){
        urlParams[pair[0]] = pair[1]
    }
})
ws.onopen = function() {
    ws.send(JSON.stringify({type:"remote_debug_page",url:location.href}))
};
ws.onmessage = function (evt)  { 
    var feed = JSON.parse(received_msg);
    if(feed.type == "start_debug") {
        // 连接到 webSocketDebuggerUrl
        var debugWS = new WebSocket(`ws://${urlParams.ws}`);  
        debugWS.onopen = function() {  
            ws.send(JSON.stringify({type:"start_debug_ready"})); // 确认可以开始调试
            ws.onmessage = function (evt) { // 转发到 debugger
                debugWS.send(evt.data);
            }
            ws.onclose = function (evt) {
                debugWS.close()
            }
        }
        debugWS.onmessage = function (evt)  { 
            ws.send(evt.data); // 转发到 server
        }
        debugWS.onclose = function() { 
            ws.send(JSON.stringify({type:"remote_page_lost",url:location.href}))
        };
    }
};
ws.onclose = function() { 
    console.log("连接已关闭..."); 
};

回放

使用 inspector 时我们可以发现,只要开启了 Page.enable 和 Network.enable,就可以一直接收到调试器后端推送的页面快照和网络请求数据。

我们可以略微改造一下 server.js 的代码,把所有收到的推送数据打时间戳后保存到一个文件,持久化存储起来。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (message.type === 'utf8') {
    var feed = JSON.parse(message.utf8Data);
    if(feed.type == "remote_debug_page"){  
        debugConnection.sendUTF(JSON.stringify({"type":"start_debug"}));
    }else if(feed.type == "start_debug_ready"){
        writeStream = fs.createWriteStream(saveFilePath,{flags:'as',encoding: 'utf8'});
    } else{
        // 全部转发给 frontendConnection
        if(frontendConnection){
            frontendConnection.sendUTF(message.utf8Data)
        }else{
            console.log('无法转发给frontend,没有建立连接')
        }
        // 保存数据到文件
        if(feed.method)writeStream.write(message.utf8Data+'\n') 
    }                
}

然后我们给 websocket 服务增加一个协议类型,和 inspector 建立连接后,读取文件中保存的数据,按照时间戳上的时间间隔推送数据。

这样就实现了回放功能,把之前调试时的现场重现一遍。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if(requestedProtocols.indexOf("feedback") != -1){
    feedbackConnection = request.accept('feedback', request.origin);
    feedbackConnection.on('message', function(message) {
        // 忽略来的消息
    })
    const fileStream = fs.createReadStream(saveFilePath);
    const rl = readline.createInterface({
        input: fileStream,
        crlfDelay: Infinity
    });
    for await (const line of rl) {  // 逐行读取数据
        feedbackConnection.sendUTF(line)
        rl.pause();
        setTimeout(_=>{rl.resume()},1000)
    }
    feedbackConnection.on('close', function(reasonCode, description) {
        console.log('feedbackConnection disconnected.');
    });
}

甚至可以更进一步,创建一个 websocket 服务作为调试器前端,模拟 inspector 发送请求的逻辑并保存推送数据到文件,这样就实现了一个录制服务器,可以随时录制调试现场,然后在需要的时候播放,因为记录了时间戳,pause、seek、resume、stop 都可以实现。

devtools-frontend 的调用方式

一般来说,我们习惯用 require/import 的方式调用模块,devtools-frontend 虽然也是个 npm 包 ,chrome-devtools-frontend[9],但是却不方便用 require/import 的方式直接引用。

主要是因为之前所述的 front_end 应用有自己的一套模块加载逻辑,应用的 js、json 配置文件必须在同一个目录下,模块也必须在同一个目录下,否则就会出现路径错误。

如果仅使用 front_end 的某个模块,还可以用 require/import 来引用。

如果想创建一个新的应用,最好是把整个 front_end 复制过来修改。

Chrome DevTools Extensions

如果想在 chrome 内嵌的调试面板中增加自定义的能力,可以用 chrome 插件的方式实现,例如vue-devtools[10]

参考资料

ChromeDevTools/awesome-chrome-devtools[11]

ChromeDevTools/devtools-protocol[12]

参考资料

[1]

devtools-protocol: https://github.com/chromedevtools/devtools-protocol

[2]

Puppeteer: https://github.com/GoogleChrome/puppeteer/

[3]

ndb: https://github.com/GoogleChromeLabs/ndb

[4]

browser_protocol: https://github.com/ChromeDevTools/devtools-protocol/blob/master/json/browser_protocol.json

[5]

js_protocol: https://github.com/ChromeDevTools/devtools-protocol/blob/master/json/js_protocol.json

[6]

Typescript 类型定义: https://github.com/ChromeDevTools/devtools-protocol/tree/master/types

[7]

ChromeDevTools/devtools-frontend: https://github.com/ChromeDevTools/devtools-frontend

[8]

ChromeDevTools/devtools-frontend: https://github.com/ChromeDevTools/devtools-frontend

[9]

chrome-devtools-frontend: https://www.npmjs.com/package/chrome-devtools-frontend

[10]

vue-devtools: https://github.com/vuejs/vue-devtools

[11]

ChromeDevTools/awesome-chrome-devtools: https://github.com/ChromeDevTools/awesome-chrome-devtools

[12]

ChromeDevTools/devtools-protocol: https://github.com/chromedevtools/devtools-protocol

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端Q 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Node.js 调试一路走来经历了什么
做为前端开发,想必大家都写过 Node.js 的代码,也大概率用 debugger 断点调试过。
神说要有光zxg
2022/06/06
6370
Node.js 调试一路走来经历了什么
玩转 Chrome DevTools,定制自己的调试工具
Chrome DevTools 是我们每天都用的工具,它可以查看元素、网络请求、断点调试 JS、分析性能问题等,是辅助开发的利器。
神说要有光zxg
2022/11/11
3.9K1
玩转 Chrome DevTools,定制自己的调试工具
聊一聊如何基于Chrome Devtools 进行远程调试
Devtools-Remote-Debugger 正如其名,是一个基于最新版 Chrome Devtools 开发者工具构建的 Web 远程调试工具。它能够让 Web 开发者使用最熟悉的 devtools 调试工具来远程调试 Web 项目。
前端达人
2023/08/31
1.6K0
聊一聊如何基于Chrome Devtools 进行远程调试
cdp 远程调试方案
cdp 协议简称 chrome 调试协议,是基于 scoket(websocket、usb、adb )消息的 json rpc 协议。用来调用 chrome 内部的方法实现 js、css 、dom 的开发调试。 可以将实现了 cdp 协议的应用看做 rpc 调用的服务端( chrome 、puppeteer), 将调试面板看做 rpc 调用的客户端(devtools)。
政采云前端团队
2022/04/27
2.8K0
cdp 远程调试方案
通过 Chrome devtools protocol 和 adb 调试 Android Webview
前提条件:usb 连接手机,adb 能查找到 devices,如果有问题,多拔插几次,确认授权。
用户1258909
2021/08/20
4.3K0
通过 Chrome devtools protocol 和 adb 调试 Android Webview
Android远程调试Web页面
曾经有一段时间专门处理了一下远程调试的事情,要解决的问题是不依赖Chrome如何在移动设备中调试Web页面。有时候真的奇怪,因为我们老是自己制造问题,因为“墙”的存在被迫去解决这些本来不是问题的问题。
icepy
2019/06/24
1.6K0
Android远程调试Web页面
Go每日一库之97:chromedp
chromedp是一个更快、更简单的Golang库用于调用支持Chrome DevTools协议的浏览器,同时不需要额外的依赖(例如Selenium和PhantomJS)
luckpunk
2023/09/30
2.2K0
【Node.js】如何调试你的 Node.js 代码?
很多时候,我苦恼于 Node.js 的调试,只会使用 console.log 这种带有侵入性的方法,但是其实 Node.js 也可以做到跟浏览器调试一样的方便。
GopalFeng
2022/08/01
8.4K0
【Node.js】如何调试你的 Node.js 代码?
Stetho工具介绍
stetho是Facebook推出的安卓APP网络诊断和数据监控的工具,接入方便,功能强大,是开发者必备的好工具。 主要功能包括: 查看App的布局 网络请求抓包 数据库、sp文件查看 自定义dumpapp插件 对于JavaScript的支持 无需root,只要能通过adb连接设备,操作方便。 接入方法 gradle配置 因为目前我们的项目中已经集成了okhttp,只需要在build.gradle添加如下两行配置 dependencies { //... compile 'com.
felix
2018/06/07
1.2K0
如何设计一个前端远程调试工具
它通过对浏览器/微信小程序 API 的封装,将调用原生方法时的参数进行过滤、转化,整理成指定格式的消息供调试端消费;调试端收到消息后,在类似 Chrome devtools 的面板中将数据呈现出来。对于前端开发者来说,上手零成本。
神说要有光zxg
2024/01/02
4300
如何设计一个前端远程调试工具
调试工具的通用原理:调试四要素
有同学说,我用 Chrome DevTools 调试网页,可以查看元素,网络请求,断点运行 JS,用 Performance 工具分析性能等,这是网页的调试。
神说要有光zxg
2022/11/11
2.4K0
调试工具的通用原理:调试四要素
Pixel Stream 源码分析
2021年10月18日。备份一下像素流源代码,防止以后万一GitHub用不了了,代码给整没了就不好办了,顺便讲解一下文件的组织架构。https://github.com/xosg/PixelStreamer
Jean
2021/10/27
2.3K0
Pixel Stream 源码分析
流动图书馆小程序实战
流动图书馆是一个图书漂流和借阅工具,旨在共享闲置图书,并链接趣味相投的小伙伴。
疯狂的小程序
2018/01/25
2.2K1
如何实现 JS 运行时的 Inspector 能力
前言:无论什么语言,调试能力都是非常重要的,像 C、C++ 等语言,我们可以使用现成的工具去调试。JS 也不例外,我们可以通过浏览器来实现对 JS 的调试,但是 JS 运行时就不太一样了,因为 JS 运行时通常独立于浏览器运行,所以无法直接使用浏览器提供的能力,这时候就需要自己实现了。当然 JS 运行时不需要完全实现调试的功能,核心的能力都是由 V8 提供,JS 运行时只需要按照 V8 的规范实现一个 Inspector 代理就行。本文介绍以 V8 为基础,实现一个简单的 JS 运行时(严格来说不算,本文只是用它来代替一个描述),并基于这个 JS 运行时实现调试 JS 的能力。
theanarkh
2022/12/06
1.4K0
如何实现 JS 运行时的 Inspector 能力
Node.js子线程调试和诊断指南
调试、诊断子线程最直接的方式就是像调试、诊断主线程一样,但是无论是动态开启还是静态开启,子线程都不可避免地需要内置一些相关的非业务代码,本文介绍另外一种对子线程代码无侵入的调试方式,另外也介绍一下通过子线程调试主线程的方式。
theanarkh
2021/08/25
1.2K0
Node.js子线程调试和诊断指南
软件测试|Appium WebView 技术原理
混合应用测试或微信小程序测试,都会涉及到 WebView 组件,这节内容将分析一下 WebView 的技术原理。首先通过日志分析查看 Appium 的运行过程。
霍格沃兹测试开发Muller老师
2023/01/09
8140
Chrome DevTools 远程调试安卓网页的原理
作为前端开发,我们每天都会用 Chrome DevTools 调试 Chrome 的网页,但其实它还可以远程调试安卓手机的网页。
神说要有光zxg
2022/11/11
2.1K0
Chrome DevTools 远程调试安卓网页的原理
史诗级更新,VSCODE 可无缝调试浏览器了!
2021-07-16 微软发布了一篇博客专门介绍了这个功能,VSCODE 牛逼!
秋风的笔记
2021/08/27
1.4K0
史诗级更新,VSCODE 可无缝调试浏览器了!
Selenium及Headless Ch
一般的的静态HTML页面可以使用requests等库直接抓取,但还有一部分比较复杂的动态页面,这些页面的DOM是动态生成的,有些还需要用户与其点击互动,这些页面只能使用真实的浏览器引擎动态解析,Selenium和Chrome Headless可以很好的达到这种目的。
py3study
2020/01/19
1K0
RPC远程调用浏览器函数
早闻 RPC(Remote Procedure Call)远程过程调用,这一词了,应该是在安卓逆向的时候听闻的,当时吹嘘的意思是这样的,通过另一个远端服务器来调用安卓代码中的函数,并将执行后的结果返回。比如有一个加密算法,如果要实现脱机(脱离当前环境)运行的话,就需要扣除相对应的代码,补齐对应的环境(模块,上下文,语言),然而要在补齐该加密算法的环境可不好实现,而通过 RPC 则可以免除扣代码,通过数据通信来达到远程调用的目的,听起来是挺牛逼的,实际上也确实挺骚的。这里我将以浏览器与本地搭建一个 websocket 来实现调用浏览器内的函数。
愧怍
2022/12/27
1.1K0
RPC远程调用浏览器函数
相关推荐
Node.js 调试一路走来经历了什么
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验