From e9b9f69269099bd74a2da63e9b63de26056de5d7 Mon Sep 17 00:00:00 2001 From: * <8> Date: Mon, 18 Apr 2022 19:45:55 +0800 Subject: [PATCH] Match-id-645d6786f413f96296f5fbf6cc123242c12adf82 --- libs/extension/src/background/index.ts | 53 ++++++++++++-------- libs/extension/src/contentScript/index.ts | 21 ++++---- libs/extension/src/injector/index.ts | 59 +++++++++++++++++------ libs/extension/src/panel/App.tsx | 52 +++++++++++++++++--- libs/extension/src/panel/index.tsx | 2 +- libs/extension/src/parser/parseVNode.ts | 8 +++ libs/extension/src/utils/constants.ts | 12 +++++ libs/extension/src/utils/transferTool.ts | 21 ++++++++ libs/horizon/src/renderer/TreeBuilder.ts | 5 ++ 9 files changed, 176 insertions(+), 57 deletions(-) create mode 100644 libs/extension/src/utils/constants.ts create mode 100644 libs/extension/src/utils/transferTool.ts diff --git a/libs/extension/src/background/index.ts b/libs/extension/src/background/index.ts index 2745c61a..cc724452 100644 --- a/libs/extension/src/background/index.ts +++ b/libs/extension/src/background/index.ts @@ -1,30 +1,41 @@ +import { checkData, packagePayload } from '../utils/transferTool'; +import { requestAllVNodeTreeInfos, initDevToolPageConnection } from '../utils/constants'; + // 多个页面、tab页共享一个 background,需要建立连接池,给每个tab建立连接 const connections = {}; // panel 代码中调用 let backgroundPageConnection = chrome.runtime.connect({...}) 会触发回调函数 chrome.runtime.onConnect.addListener(function (port) { - - // The original connection event doesn't include the tab ID of the - // DevTools page, so we need to send it explicitly. - function extensionListener(message, sender, sendResponse) { - // 在backgroundPageConnection创建后会发送初始化请求,这样就可以获取tabId,给连接编号 - if (message.name === 'init') { - // 获取 panel 所在 tab 页的tabId - connections[message.tabId] = port; - chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { - chrome.tabs.sendMessage(tabs[0].id, {tag: 'init horizon info'}, function(response) { - console.log(response.farewell); - }); + function extensionListener(message) { + const isHorizonMessage = checkData(message); + if (isHorizonMessage) { + console.log('received message', message); + const { payload } = message; + const { type, data } = payload; + let passMessage; + if (type === initDevToolPageConnection) { + if (!connections[data]) { + // 获取 panel 所在 tab 页的tabId + connections[data] = port; + } + passMessage = packagePayload({ type: requestAllVNodeTreeInfos }); + } else { + passMessage = message; + } + console.log('post message:', passMessage); + // 查询参数有 active 和 currentWindow, 如果开发者工具与页面分离,会导致currentWindow为false才能找到 + // 所以只用 active 参数查找,但不确定这么写是否会引发查询错误的情况 + // 或许需要用不同的查询参数查找两次 + chrome.tabs.query({ active: true }, function (tabs) { + if (tabs.length) { + chrome.tabs.sendMessage(tabs[0].id, passMessage); + console.log('post message end'); + } else { + console.log('do not find message'); + } }); - return; } - - if (message.name === 'update') { - return; - } - // other message handling } - // Listen to messages sent from the DevTools page port.onMessage.addListener(extensionListener); @@ -42,11 +53,11 @@ chrome.runtime.onConnect.addListener(function (port) { }); // 监听来自 content script 的消息,并将消息发送给对应的 devTools page,也就是 panel -chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { +chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { // Messages from content scripts should have sender.tab set if (sender.tab) { const tabId = sender.tab.id; - if (tabId in connections) { + if (tabId in connections && checkData(request)) { connections[tabId].postMessage(request); } else { console.log('Tab not found in connection list.'); diff --git a/libs/extension/src/contentScript/index.ts b/libs/extension/src/contentScript/index.ts index 2236496a..9357f69f 100644 --- a/libs/extension/src/contentScript/index.ts +++ b/libs/extension/src/contentScript/index.ts @@ -1,4 +1,5 @@ import { injectCode } from '../utils/injectUtils'; +import { checkData } from '../utils/transferTool'; // 页面的window对象不能直接通过 contentScript 代码修改,只能通过添加 js 代码往页面 window 注入hook injectCode(chrome.runtime.getURL('/injector.js')); @@ -10,12 +11,10 @@ window.addEventListener('message', event => { return; } - if (event.data.type && (event.data.type === 'HORIZON_DEV_TOOLS')) { - console.log('Content script received: ' + JSON.stringify(event.data.vNode)); + const data = event.data; + if (checkData(data)) { // 传递给background - chrome.runtime.sendMessage(event.data.vNode, function (response) { - console.log(response); - }); + chrome.runtime.sendMessage(data); } }, false); @@ -23,14 +22,12 @@ window.addEventListener('message', event => { // 监听来自background的消息 chrome.runtime.onMessage.addListener( - function (request, sender, sendResponse) { - console.log(sender.tab ? - 'from a content script:' + sender.tab.url : - 'from the extension'); - if (request.tag === 'init horizon info') { + function (request, sender) { + // 该方法可以监听页面 contentScript 和插件的消息 + // 没有 tab 信息说明消息来自插件 + if (!sender.tab && checkData(request)) { // 传递消息给页面 - console.log('start pass info to webpage'); - window.postMessage({type: 'HORIZON_DEV_TOOLS', id: 1}, '*'); + window.postMessage(request, '*'); } } ); diff --git a/libs/extension/src/injector/index.ts b/libs/extension/src/injector/index.ts index 178cf608..d1920678 100644 --- a/libs/extension/src/injector/index.ts +++ b/libs/extension/src/injector/index.ts @@ -1,4 +1,8 @@ -import parseTreeRoot from "../parser/parseVNode"; +import parseTreeRoot, { deleteVNode } from '../parser/parseVNode'; +import { packagePayload, checkData } from './../utils/transferTool'; +import { oneVNodeTreeInfos, allVNodeTreesInfos, requestAllVNodeTreeInfos } from './../utils/constants'; + +const roots = []; function injectHook() { if (window.__HORIZON_DEV_HOOK__) { @@ -7,26 +11,49 @@ function injectHook() { Object.defineProperty(window, '__HORIZON_DEV_HOOK__', { enumerable: false, value: { - roots: [], + addIfNotInclude: function( treeRoot: any) { + if (!roots.includes(treeRoot)) { + roots.push(treeRoot); + } + }, send: function (vNode: any) { const result = parseTreeRoot(vNode); - window.postMessage({ - type: 'HORIZON_DEV_TOOLS', vNode: result - }, '*'); + window.postMessage(packagePayload({ + data: result, + type: oneVNodeTreeInfos + }), '*'); }, - listen: function (id: number) { - window.addEventListener('message', function(event) { - // We only accept messages from ourselves - if (event.source !== window) { - return; - } - - if (event.data.type && (event.data.type === 'HORIZON_DEV_TOOLS') && event.data.id === id) { - console.log('todo'); - } - }); + delete: function (vNode: any) { + // 开发工具中保存了 vNode 的引用,在清理 VNode 的时候需要一并删除 + deleteVNode(vNode); + const index = roots.indexOf(vNode); + if (index !== -1) { + roots.splice(index, 1); + } } }, }); + window.addEventListener('message', function (event) { + // We only accept messages from ourselves + if (event.source !== window) { + return; + } + const request = event.data; + if (checkData(request)) { + const { payload } = request; + const { type, data } = payload; + if (type === requestAllVNodeTreeInfos) { + const result = roots.reduce((pre, current) => { + const info = parseTreeRoot(current); + pre.push(info); + return pre; + }, []); + window.postMessage(packagePayload({ + data: result, + type: allVNodeTreesInfos + }), '*'); + } + } + }); } injectHook(); diff --git a/libs/extension/src/panel/App.tsx b/libs/extension/src/panel/App.tsx index 0c62fb19..7ba1151f 100644 --- a/libs/extension/src/panel/App.tsx +++ b/libs/extension/src/panel/App.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'horizon'; +import { useState, useEffect, useRef } from 'horizon'; import VTree, { IData } from '../components/VTree'; import Search from '../components/Search'; import ComponentInfo from '../components/ComponentInfo'; @@ -8,6 +8,8 @@ import { mockParsedVNodeData, parsedMockState } from '../devtools/mock'; import { FilterTree } from '../hooks/FilterTree'; import Close from '../svgs/Close'; import Arrow from './../svgs/Arrow'; +import { initDevToolPageConnection, allVNodeTreesInfos, requestComponentAttrs } from './../utils/constants'; +import { packagePayload } from './../utils/transferTool'; const parseVNodeData = (rawData) => { const idIndentationMap: { @@ -55,6 +57,7 @@ function App() { const [parsedVNodeData, setParsedVNodeData] = useState([]); const [componentAttrs, setComponentAttrs] = useState({}); const [selectComp, setSelectComp] = useState(null); + const treeRootInfos = useRef<{[id: string]: number}>({}); // 记录保存的根节点 id 和长度 const { filterValue, @@ -77,6 +80,34 @@ function App() { state: parsedMockState, props: parsedMockState, }); + } else { + const connection = chrome.runtime.connect({ + name: 'panel' + }); + // 页面打开后发送初始化请求 + connection.postMessage(packagePayload({ + type: initDevToolPageConnection, + data: chrome.devtools.inspectedWindow.tabId + })); + // 监听 background消息 + connection.onMessage.addListener(function (message) { + const { payload } = message; + if (payload) { + const { type, data } = payload; + if (type === allVNodeTreesInfos) { + const allTreeData = data.reduce((pre, current) => { + const parsedTreeData = parseVNodeData(current); + const length = parsedTreeData.length; + if (length) { + const treeRoot = parsedTreeData[0]; + treeRootInfos.current[treeRoot.id] = length; + } + return pre.concat(parsedTreeData); + }, []); + setParsedVNodeData(allTreeData); + } + } + }); } }, []); @@ -85,10 +116,17 @@ function App() { }; const handleSelectComp = (item: IData) => { - setComponentAttrs({ - state: parsedMockState, - props: parsedMockState, - }); + if (isDev) { + setComponentAttrs({ + state: parsedMockState, + props: parsedMockState, + }); + } else { + connection.postMessage({ + name: requestComponentAttrs, + data: item.id + }); + } setSelectComp(item); }; @@ -134,8 +172,8 @@ function App() {