Screenshot from: https://developer.chrome.com/docs/devtools/
Author of this article: Yuan Grass
foreword
There are many applications developed using react native in the cloud music app, such as cloud shell center, cloud music mall, membership center, etc. In order to better improve development efficiency and improve the debugging experience, the team decided to develop a react native debugging tool. By adding some extended functions to the react native debugger, it can realize the display and debugging capabilities of business information, such as: cross-end communication information display, network information display Wait.
Because the network requests of the cloud music react native application are sent through the client, I want to display the client network information in the network through the chrome devtools protocol. After trying, some problems encountered and the attempts made are recorded as follows.
Introducing chrome devtools
Chrome devtools is a common debugging tool in the front-end, integrated in chrome. The web application establishes a connection with the devtools frontend (the page that usually opens the f12 debug panel, which is also a front-end project, represented by frontend below) through the chrome devtools protocol, and transmits the debugged application information to the frontend for display.
ChromeDevTools/devtools-frontend is the project code for chrome devtools frontend. Also available as a separate project for custom debugging features. The devtools frontend used in the project is version 3964. The new version of frontend has adjusted the packaging method and code structure.
devtools frontend module loading
By configuring the remote debugging port --remote-debugging-port=9222
start chrome, http://localhost:9222/
you can see the debugging link, for example: http://localhost:9222/devtools/inspector.html?ws=localhost:9222/devtools/page/7B2421304FE8EF659B264D4F476083DA
is the address of an inspector. How the project started.
inspector.html loads Runtime.js and inspector.js, inspector.js only does one thing
Runtime.startApplication('inspector');
The module loading process is as follows
After the module goes through the loading and parsing process , start the application:
- The front-end application obtains module information by reading module.json;
- Instantiate new Runtime, establish Runtime -> Module -> Extension dependency;
- Load core module resources (script and css resources);
- Start the core module entry (Main.Main).
This is the loading process of the module.
Inspector startup method
View inspector.json content
{
"modules" : [
{ "name": "screencast", "type": "autostart" }
],
"extends": "devtools_app",
"has_html": true
}
The inspector app inherits from devtools_app. There is only one more module than devtools_app screencast
page snapshot, which can be used to view page changes in real time.
The devtools_app application is a commonly used devtools tool, which can be used for web application debugging. The contents of the modules that devtools_app.json depends on are as follows:
{
"modules" : [
{ "name": "emulation", "type": "autostart" },
{ "name": "inspector_main", "type": "autostart" },
{ "name": "browser_debugger" },
{ "name": "elements" },
{ "name": "network" },
...
],
"extends": "shell",
"has_html": true
}
devtools_app.json inherits from shell.json and should be the core module that devtools depends on. The contents of the modules that shell.json depends on are as follows:
{
"modules" : [
{ "name": "bindings", "type": "autostart" },
{ "name": "common", "type": "autostart" },
{ "name": "components", "type": "autostart"},
{ "name": "extensions", "type": "autostart" },
{ "name": "host", "type": "autostart" },
{ "name": "main", "type": "autostart" },
{ "name": "protocol", "type": "autostart" },
{ "name": "ui", "type": "autostart" },
...
]
}
The inspector_main module included in devtools_app will establish a socket connection through the ws parameter passed in the link parameter to obtain the protocol information of the monitoring backend.
SDK._createMainConnection = function() {
const wsParam = Runtime.queryParam('ws');
const wssParam = Runtime.queryParam('wss');
if (wsParam || wssParam) {
const ws = wsParam ? `ws://${wsParam}` : `wss://${wssParam}`;
SDK._mainConnection = new SDK.WebSocketConnection(ws, SDK._websocketConnectionLost);
} else if (InspectorFrontendHost.isHostedMode()) {
SDK._mainConnection = new SDK.StubConnection();
} else {
SDK._mainConnection = new SDK.MainConnection();
}
return SDK._mainConnection;
};
Since then, the connection with the debugged project has been established. If you want to know the content of other modules, you can also view it according to the above ideas.
react native debugger debugging process
React native debugger is an electron stand-alone program for debugging react native applications. Based on the official remote debugging function , react-devtools-core and redux-devtools-extension are added as extension support.
React native is not a web page on the app. The react native dedug process is after opening the debug remote:
- The app sends a /launch-js-devtools request to the server to open the debugging tab and establish a socket connection;
- tab to load the debugger-ui page and establish a socket connection with the server (/debugger-proxy?role=debugger&name=Chrome);
- Tab to open debugger-ui At the same time, start the worker to run the debuggerWorker.js script, which is used to run the bundle code;
- Finally, the debugging of the react native application is achieved by inspecting the worker.
In react native, if the network request sent by fetch or XMLHttpRequest is used, it can be obtained during the frontend debugging process. The network requests of the react native application in the cloud music app are all sent through the client, so the client network information is displayed on the network through the chrome devtools protocol, what should I do?
chrome devtools inspector extension
chrome devtools protocol
The most direct way to increase the network began to think of, that is, through the socket link between the web and the frontend.
The chrome devtools protocol (CDP for short) is a debugger protocol passed between devtools and web applications. Based on websocket to establish a fast data channel between devtools and the browser kernel, the chrome devtools protocol also allows third parties to debug web applications.
The CDP protocol divides capabilities according to Domain, and each domain has Method, Event and Types.
Method corresponds to the request/response mode of socket communication, Events corresponds to the publish/subscribe mode of socket communication, and Types is the entity used in the interaction.
- Method: Contains request/response, like an asynchronous call, through the request information, get the corresponding return result, the communication needs to have a message id
request: {"id":1,"method":"Page.canScreencast"}
response: {"id":1,"result":{"result":false}}
Event: The event information that occurred, used to send notification information.
{"method":"Network.loadingFinished","params:{"requestId":"14307.143","timestamp":1424097364.31611,"encodedDataLength":0}}
Types: interacting entities
Network.BlockedReason Network.ConnectionType Network.Cookie ...
In chrome you can use "Protocol Monitor" to send Method.
In electron, you can open chrome debugging port 9222 by adding chrome's startup parameter remote-debugging-port through app.commandLine.appendSwitch
.
const CHROME_REMOTE_DEBUG_PORT = 9222; // devtools frontend 调试端口
app.commandLine.appendSwitch('remote-debugging-port', `${CHROME_REMOTE_DEBUG_PORT}`);
Find the debugging address webSocketDebuggerUrl of the React Native Debugger on the current page through the http://localhost:9222/json interface request.
[ {
"description": "",
"devtoolsFrontendUrl": "/devtools/inspector.html?ws=localhost:9222/devtools/page/ADC97007929236D82B4613E4E6B36C4B",
"id": "ADC97007929236D82B4613E4E6B36C4B",
"title": "React Native Debugger - Waiting for client connection (port 8081)",
"type": "page",
"url": "file:///Users/jarry/netease/react-native-debugger/electron/app.html",
"webSocketDebuggerUrl": "ws://localhost:9222/devtools/page/ADC97007929236D82B4613E4E6B36C4B"
} ]
Then establish a socket channel to the app.
Try to find that the network information is not displayed. The information sent by the app through the socket connection is sent to the browser kernel, but the browser kernel does not forward the information to the frontend. It is a matter of course that the web application is used as the debug information processing object.
Using proxy method
If the web application and the frontend are in the same network segment, the above debugging method can be used. If the two parts are not in the same intranet, the remote debugging method needs to be used. Create a proxy service between the web application and the frontend to implement CDP message forwarding to achieve the goal of cross-domain debugging.
Inspired by cross-domain debugging, proxy can forward other CDP messages while forwarding between web and frontend.
- Start ws as proxy service
proxy.js sample code:
const WS_PROXY_PORT = 9233; // proxy ws 端口
const CHROME_REMOTE_DEBUG_PORT = 9222; // devtools frontend 调试端口
const proxyStart = async () => {
// 创建 proxy server
const server = http.createServer((request, response) => {
response.writeHead(404);
response.end();
});
server.listen(WS_PROXY_PORT);
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws, request, client) => {
// 处理来自 remote-debug 的请求
if (request.headers['sec-websocket-protocol'] === 'remote-debug') {
axios.get(`http://127.0.0.1:${CHROME_REMOTE_DEBUG_PORT}/json`).then(res => {
// 查询被调试页 webSocketDebuggerUrl
const { data } = res;
if (data && data.length > 0) {
for (let i = 0; i < data.length; i++) {
const page = data[i];
if (page.title.indexOf('React Native Debugger') === 0) {
debugConnection = new WebSocket(page.webSocketDebuggerUrl);
});
// 把被调试页面的数据全部转发给调试器前端
debugConnection.on('message', (message) => {
if (frontendConnection) {
if (debugMessageArr.length) {
debugMessageArr.forEach(item => {
frontendConnection.send(item);
});
debugMessageArr = [];
}
frontendConnection.send(message);
} else {
debugMessageArr.push(message);
console.log('无法转发给 frontend, 没有建立连接\n');
}
});
break;
}
}
}
}).catch(error => {
console.log(error);
});
}
// 处理来自 frontend 的请求
if (request.url === '/frontend') {
frontendConnection = ws;
frontendConnection.on('message', (message) => {
// 把调试器前端的请求直接转发给被调试页面
if (debugConnection) {
if (frontendMessageArr.length) {
frontendMessageArr.forEach(item => {
debugConnection.send(item);
});
frontendMessageArr = [];
}
debugConnection.send(message);
} else {
frontendMessageArr.push(message);
console.log('调试器后端未准备好, 先打开被调试的页面');
}
});
}
// 处理来自客户端请求
if (request.url === '/app') {
appConnection = ws;
appConnection.on('message', (message) => {
// 把客户端的请求直接转发给前端调试器
if (frontendConnection) {
if (debugMessageArr.length) {
debugMessageArr.forEach(item => {
frontendConnection.send(item);
});
debugMessageArr = [];
}
frontendConnection.send(message);
} else {
debugMessageArr.push(message);
console.log('无法转发给frontend,没有建立连接\n');
}
});
}
});
};
Three aspects of data are processed here
- remote-debug: The debugged page data. By searching
http://127.0.0.1:${CHROME_REMOTE_DEBUG_PORT}/json
obtain webSocketDebuggerUrl, establish a debugConnection connection, and the debugged information will be sent to frontend; - frontend: debugger data. Forward this data to debugConnection.
- app: client data, forward this data to debugConnection.
- remote-debug build connection
The remote-debug runs on the web to notify the proxy to establish a connection, because my proxy runs on the electron and is in the same domain as the remote debug, so I get the webSocketDebuggerUrl directly from the proxy. You can also get webSocketDebuggerUrl in remote-debug and tell proxy by way of notification.
const ws = new WebSocket('ws://localhost:9233', 'remote-debug');
- frontend
Since frontend needs to change the ws address, the devtools that comes with chrome is not used here, but the locally started frontend service, and the ws connection is replaced by the proxy frontend connection. The frontend web service will be introduced in the next section.
http://localhost:8090/front_end/devtools_app.html?ws=localhost:9233/frontend
- app side
The app side will add an interceptor to the okhttp framework to intercept all request data and send it to the proxy through ws.
Since then, the function of the inspector to expand the network information has been realized.
frontend local service
Because of the need for custom development, the devtools frontend version is released with the chrome version. It is best to use the frontend that is consistent with the chrome version. For example, I get the chrome version number in electron through process.versions.chrome
which corresponds to 78.0.3904.130. The version number of devtools is 3904, but the earliest version that can be obtained is 3964, and no other problems have been encountered temporarily using 3964.
devtools-fronend->scripts->server.js
Start this service to get a frontend service running on 8090.
Summarize
Starting from the extension of chrome devtools inspector, this article introduces the debugging principle and module loading method of devtools frontend, the debugging principle of react native debugger, the cross-domain debugging scheme, and finally realizes the debugging extension of devtools inspector. The article involves a lot of knowledge of various debugging tools, most of which are summarized, and the technical details are also left with document links that can be obtained by yourself. I hope it will inspire and help students who are working as chrome debugging tools. Criticisms and corrections on relevant issues in the text are welcome.
Reference link
- https://github.com/jhen0409/react-native-debugger
- https://segmentfault.com/a/1190000013665754
- https://github.com/ChromeDevTools/devtools-frontend
- https://memoryza.gitbook.io/chromedevtools-devtools-frontend/xiang-mu-dai-ma-yun-hang-yuan-li
- https://zhaomenghuan.js.org/blog/chrome-devtools-frontend-analysis-of-principle.html#startapplication-%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8 %8B
This article is published from the NetEase Cloud Music technical team, and any form of reprinting of the article is prohibited without authorization. We recruit various technical positions all year round. If you are ready to change jobs and happen to like cloud music, then join us at grp.music-fe(at)corp.netease.com!
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。