Match-id-645d6786f413f96296f5fbf6cc123242c12adf82

This commit is contained in:
* 2022-04-18 19:45:55 +08:00 committed by *
parent 4834261253
commit e9b9f69269
9 changed files with 176 additions and 57 deletions

View File

@ -1,30 +1,41 @@
import { checkData, packagePayload } from '../utils/transferTool';
import { requestAllVNodeTreeInfos, initDevToolPageConnection } from '../utils/constants';
// 多个页面、tab页共享一个 background需要建立连接池给每个tab建立连接 // 多个页面、tab页共享一个 background需要建立连接池给每个tab建立连接
const connections = {}; const connections = {};
// panel 代码中调用 let backgroundPageConnection = chrome.runtime.connect({...}) 会触发回调函数 // panel 代码中调用 let backgroundPageConnection = chrome.runtime.connect({...}) 会触发回调函数
chrome.runtime.onConnect.addListener(function (port) { chrome.runtime.onConnect.addListener(function (port) {
function extensionListener(message) {
// The original connection event doesn't include the tab ID of the const isHorizonMessage = checkData(message);
// DevTools page, so we need to send it explicitly. if (isHorizonMessage) {
function extensionListener(message, sender, sendResponse) { console.log('received message', message);
// 在backgroundPageConnection创建后会发送初始化请求这样就可以获取tabId给连接编号 const { payload } = message;
if (message.name === 'init') { const { type, data } = payload;
let passMessage;
if (type === initDevToolPageConnection) {
if (!connections[data]) {
// 获取 panel 所在 tab 页的tabId // 获取 panel 所在 tab 页的tabId
connections[message.tabId] = port; connections[data] = port;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { }
chrome.tabs.sendMessage(tabs[0].id, {tag: 'init horizon info'}, function(response) { passMessage = packagePayload({ type: requestAllVNodeTreeInfos });
console.log(response.farewell); } 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 // Listen to messages sent from the DevTools page
port.onMessage.addListener(extensionListener); port.onMessage.addListener(extensionListener);
@ -42,11 +53,11 @@ chrome.runtime.onConnect.addListener(function (port) {
}); });
// 监听来自 content script 的消息,并将消息发送给对应的 devTools page也就是 panel // 监听来自 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 // Messages from content scripts should have sender.tab set
if (sender.tab) { if (sender.tab) {
const tabId = sender.tab.id; const tabId = sender.tab.id;
if (tabId in connections) { if (tabId in connections && checkData(request)) {
connections[tabId].postMessage(request); connections[tabId].postMessage(request);
} else { } else {
console.log('Tab not found in connection list.'); console.log('Tab not found in connection list.');

View File

@ -1,4 +1,5 @@
import { injectCode } from '../utils/injectUtils'; import { injectCode } from '../utils/injectUtils';
import { checkData } from '../utils/transferTool';
// 页面的window对象不能直接通过 contentScript 代码修改,只能通过添加 js 代码往页面 window 注入hook // 页面的window对象不能直接通过 contentScript 代码修改,只能通过添加 js 代码往页面 window 注入hook
injectCode(chrome.runtime.getURL('/injector.js')); injectCode(chrome.runtime.getURL('/injector.js'));
@ -10,12 +11,10 @@ window.addEventListener('message', event => {
return; return;
} }
if (event.data.type && (event.data.type === 'HORIZON_DEV_TOOLS')) { const data = event.data;
console.log('Content script received: ' + JSON.stringify(event.data.vNode)); if (checkData(data)) {
// 传递给background // 传递给background
chrome.runtime.sendMessage(event.data.vNode, function (response) { chrome.runtime.sendMessage(data);
console.log(response);
});
} }
}, false); }, false);
@ -23,14 +22,12 @@ window.addEventListener('message', event => {
// 监听来自background的消息 // 监听来自background的消息
chrome.runtime.onMessage.addListener( chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) { function (request, sender) {
console.log(sender.tab ? // 该方法可以监听页面 contentScript 和插件的消息
'from a content script:' + sender.tab.url : // 没有 tab 信息说明消息来自插件
'from the extension'); if (!sender.tab && checkData(request)) {
if (request.tag === 'init horizon info') {
// 传递消息给页面 // 传递消息给页面
console.log('start pass info to webpage'); window.postMessage(request, '*');
window.postMessage({type: 'HORIZON_DEV_TOOLS', id: 1}, '*');
} }
} }
); );

View File

@ -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() { function injectHook() {
if (window.__HORIZON_DEV_HOOK__) { if (window.__HORIZON_DEV_HOOK__) {
@ -7,26 +11,49 @@ function injectHook() {
Object.defineProperty(window, '__HORIZON_DEV_HOOK__', { Object.defineProperty(window, '__HORIZON_DEV_HOOK__', {
enumerable: false, enumerable: false,
value: { value: {
roots: [], addIfNotInclude: function( treeRoot: any) {
if (!roots.includes(treeRoot)) {
roots.push(treeRoot);
}
},
send: function (vNode: any) { send: function (vNode: any) {
const result = parseTreeRoot(vNode); const result = parseTreeRoot(vNode);
window.postMessage({ window.postMessage(packagePayload({
type: 'HORIZON_DEV_TOOLS', vNode: result data: result,
}, '*'); type: oneVNodeTreeInfos
}), '*');
}, },
listen: function (id: number) { delete: function (vNode: any) {
window.addEventListener('message', function(event) { // 开发工具中保存了 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 // We only accept messages from ourselves
if (event.source !== window) { if (event.source !== window) {
return; return;
} }
const request = event.data;
if (event.data.type && (event.data.type === 'HORIZON_DEV_TOOLS') && event.data.id === id) { if (checkData(request)) {
console.log('todo'); 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(); injectHook();

View File

@ -1,4 +1,4 @@
import { useState, useEffect } from 'horizon'; import { useState, useEffect, useRef } from 'horizon';
import VTree, { IData } from '../components/VTree'; import VTree, { IData } from '../components/VTree';
import Search from '../components/Search'; import Search from '../components/Search';
import ComponentInfo from '../components/ComponentInfo'; import ComponentInfo from '../components/ComponentInfo';
@ -8,6 +8,8 @@ import { mockParsedVNodeData, parsedMockState } from '../devtools/mock';
import { FilterTree } from '../hooks/FilterTree'; import { FilterTree } from '../hooks/FilterTree';
import Close from '../svgs/Close'; import Close from '../svgs/Close';
import Arrow from './../svgs/Arrow'; import Arrow from './../svgs/Arrow';
import { initDevToolPageConnection, allVNodeTreesInfos, requestComponentAttrs } from './../utils/constants';
import { packagePayload } from './../utils/transferTool';
const parseVNodeData = (rawData) => { const parseVNodeData = (rawData) => {
const idIndentationMap: { const idIndentationMap: {
@ -55,6 +57,7 @@ function App() {
const [parsedVNodeData, setParsedVNodeData] = useState([]); const [parsedVNodeData, setParsedVNodeData] = useState([]);
const [componentAttrs, setComponentAttrs] = useState({}); const [componentAttrs, setComponentAttrs] = useState({});
const [selectComp, setSelectComp] = useState(null); const [selectComp, setSelectComp] = useState(null);
const treeRootInfos = useRef<{[id: string]: number}>({}); // 记录保存的根节点 id 和长度
const { const {
filterValue, filterValue,
@ -77,6 +80,34 @@ function App() {
state: parsedMockState, state: parsedMockState,
props: 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) => { const handleSelectComp = (item: IData) => {
if (isDev) {
setComponentAttrs({ setComponentAttrs({
state: parsedMockState, state: parsedMockState,
props: parsedMockState, props: parsedMockState,
}); });
} else {
connection.postMessage({
name: requestComponentAttrs,
data: item.id
});
}
setSelectComp(item); setSelectComp(item);
}; };
@ -134,8 +172,8 @@ function App() {
</div> </div>
<div className={styles.right}> <div className={styles.right}>
<ComponentInfo <ComponentInfo
name={selectComp ? selectComp.name: null} name={selectComp ? selectComp.name : null}
attrs={selectComp ? componentAttrs: {}} attrs={selectComp ? componentAttrs : {}}
parents={parents} parents={parents}
onClickParent={handleClickParent} /> onClickParent={handleClickParent} />
</div> </div>

View File

@ -57,4 +57,12 @@ function parseTreeRoot(treeRoot: VNode) {
return result; return result;
} }
export function deleteVNode(vNode: VNode) {
if (VNodeToIdMap.has(vNode)) {
const id = VNodeToIdMap.get(vNode);
VNodeToIdMap.delete(vNode);
IdToVNodeMap.delete(id);
}
}
export default parseTreeRoot; export default parseTreeRoot;

View File

@ -0,0 +1,12 @@
// panel 页面打开后初始化连接标志
export const initDevToolPageConnection = 'init dev tool page connection';
// background 解析全部 root VNodes 标志
export const requestAllVNodeTreeInfos = 'request all vNodes tree infos';
// vNodes 全部树解析结果标志
export const allVNodeTreesInfos = 'vNode trees Infos';
// 一棵树的解析
export const oneVNodeTreeInfos = 'one vNode tree';
// 获取组件属性
export const requestComponentAttrs = 'get component attrs';
// 返回组件属性
export const componentAttrs = 'component attrs';

View File

@ -0,0 +1,21 @@
const devTools = 'HORIZON_DEV_TOOLS';
interface payLoadType {
type: string,
data?: any,
}
export function packagePayload(payload: payLoadType) {
return {
type: devTools,
payload,
};
}
export function checkData(data: any) {
if (data?.type === devTools) {
return true;
}
return false;
}

View File

@ -275,6 +275,11 @@ function renderFromRoot(treeRoot) {
// 2. 提交变更 // 2. 提交变更
submitToRender(treeRoot); submitToRender(treeRoot);
if (window.__HORIZON_DEV_HOOK__) {
const hook = window.__HORIZON_DEV_HOOK__;
hook.addIfNotInclude(treeRoot);
hook.send(treeRoot);
}
return null; return null;
} }