Match-id-9696388f05085d0c1eb211cd745a75e2c2a6ab46

This commit is contained in:
* 2022-04-19 15:28:54 +08:00 committed by *
parent e9b9f69269
commit 18a5f36604
9 changed files with 136 additions and 60 deletions

View File

@ -1,5 +1,6 @@
import { checkData, packagePayload } from '../utils/transferTool'; import { checkMessage, packagePayload, changeSource } from '../utils/transferTool';
import { requestAllVNodeTreeInfos, initDevToolPageConnection } from '../utils/constants'; import { RequestAllVNodeTreeInfos, InitDevToolPageConnection, DevToolBackground } from '../utils/constants';
import { DevToolPanel, DevToolContentScript } from './../utils/constants';
// 多个页面、tab页共享一个 background需要建立连接池给每个tab建立连接 // 多个页面、tab页共享一个 background需要建立连接池给每个tab建立连接
const connections = {}; const connections = {};
@ -7,22 +8,21 @@ 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) { function extensionListener(message) {
const isHorizonMessage = checkData(message); const isHorizonMessage = checkMessage(message, DevToolPanel);
if (isHorizonMessage) { if (isHorizonMessage) {
console.log('received message', message);
const { payload } = message; const { payload } = message;
const { type, data } = payload; const { type, data } = payload;
let passMessage; let passMessage;
if (type === initDevToolPageConnection) { if (type === InitDevToolPageConnection) {
if (!connections[data]) { if (!connections[data]) {
// 获取 panel 所在 tab 页的tabId // 获取 panel 所在 tab 页的tabId
connections[data] = port; connections[data] = port;
} }
passMessage = packagePayload({ type: requestAllVNodeTreeInfos }); passMessage = packagePayload({ type: RequestAllVNodeTreeInfos }, DevToolBackground);
} else { } else {
passMessage = message; passMessage = message;
changeSource(passMessage, DevToolBackground);
} }
console.log('post message:', passMessage);
// 查询参数有 active 和 currentWindow 如果开发者工具与页面分离会导致currentWindow为false才能找到 // 查询参数有 active 和 currentWindow 如果开发者工具与页面分离会导致currentWindow为false才能找到
// 所以只用 active 参数查找,但不确定这么写是否会引发查询错误的情况 // 所以只用 active 参数查找,但不确定这么写是否会引发查询错误的情况
// 或许需要用不同的查询参数查找两次 // 或许需要用不同的查询参数查找两次
@ -53,12 +53,13 @@ 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 (message, 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 && checkData(request)) { if (tabId in connections && checkMessage(message, DevToolContentScript)) {
connections[tabId].postMessage(request); changeSource(message, DevToolBackground);
connections[tabId].postMessage(message);
} else { } else {
console.log('Tab not found in connection list.'); console.log('Tab not found in connection list.');
} }

View File

@ -4,7 +4,7 @@
import { useState, useRef, useEffect } from 'horizon'; import { useState, useRef, useEffect } from 'horizon';
import styles from './VList.less'; import styles from './VList.less';
interface IProps<T extends { id: string }> { interface IProps<T extends { id: number | string }> {
data: T[], data: T[],
width: number, // 暂时未用到,当需要支持横向滚动时使用 width: number, // 暂时未用到,当需要支持横向滚动时使用
height: number, // VList 的高度 height: number, // VList 的高度
@ -20,7 +20,7 @@ export type renderInfoType<T> = {
skipItemCountBeforeScrollItem: number, skipItemCountBeforeScrollItem: number,
}; };
export function VList<T extends { id: string }>(props: IProps<T>) { export function VList<T extends { id: number | string }>(props: IProps<T>) {
const { const {
data, data,
height, height,

View File

@ -6,7 +6,7 @@ import { SizeObserver } from './SizeObserver';
import { renderInfoType, VList } from './VList'; import { renderInfoType, VList } from './VList';
export interface IData { export interface IData {
id: string; id: number;
name: string; name: string;
indentation: number; indentation: number;
userKey: string; userKey: string;

View File

@ -1,5 +1,7 @@
import { injectCode } from '../utils/injectUtils'; import { injectCode } from '../utils/injectUtils';
import { checkData } from '../utils/transferTool'; import { checkMessage } from '../utils/transferTool';
import { DevToolContentScript, DevToolHook, DevToolBackground } from './../utils/constants';
import { changeSource } 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'));
@ -12,7 +14,8 @@ window.addEventListener('message', event => {
} }
const data = event.data; const data = event.data;
if (checkData(data)) { if (checkMessage(data, DevToolHook)) {
changeSource(data, DevToolContentScript);
// 传递给background // 传递给background
chrome.runtime.sendMessage(data); chrome.runtime.sendMessage(data);
} }
@ -22,12 +25,13 @@ window.addEventListener('message', event => {
// 监听来自background的消息 // 监听来自background的消息
chrome.runtime.onMessage.addListener( chrome.runtime.onMessage.addListener(
function (request, sender) { function (message, sender) {
// 该方法可以监听页面 contentScript 和插件的消息 // 该方法可以监听页面 contentScript 和插件的消息
// 没有 tab 信息说明消息来自插件 // 没有 tab 信息说明消息来自插件
if (!sender.tab && checkData(request)) { if (!sender.tab && checkMessage(message, DevToolBackground)) {
changeSource(message, DevToolContentScript);
// 传递消息给页面 // 传递消息给页面
window.postMessage(request, '*'); window.postMessage(message, '*');
} }
} }
); );

View File

@ -1,6 +1,23 @@
import parseTreeRoot, { deleteVNode } from '../parser/parseVNode'; import parseTreeRoot, { deleteVNode, queryVNode } from '../parser/parseVNode';
import { packagePayload, checkData } from './../utils/transferTool'; import { packagePayload, checkMessage } from './../utils/transferTool';
import { oneVNodeTreeInfos, allVNodeTreesInfos, requestAllVNodeTreeInfos } from './../utils/constants'; import {
RequestAllVNodeTreeInfos,
AllVNodeTreesInfos,
RequestComponentAttrs,
ComponentAttrs,
DevToolHook,
DevToolContentScript
} from './../utils/constants';
import { VNode } from './../../../horizon/src/renderer/vnode/VNode';
import { ClassComponent } from '../../../horizon/src/renderer/vnode/VNodeTags';
import { parseAttr } from '../parser/parseAttr';
function postMessage(type: string, data) {
window.postMessage(packagePayload({
type: type,
data: data,
}, DevToolHook), '*');
}
const roots = []; const roots = [];
@ -11,19 +28,20 @@ function injectHook() {
Object.defineProperty(window, '__HORIZON_DEV_HOOK__', { Object.defineProperty(window, '__HORIZON_DEV_HOOK__', {
enumerable: false, enumerable: false,
value: { value: {
addIfNotInclude: function( treeRoot: any) { addIfNotInclude: function (treeRoot: VNode) {
if (!roots.includes(treeRoot)) { if (!roots.includes(treeRoot)) {
roots.push(treeRoot); roots.push(treeRoot);
} }
}, },
send: function (vNode: any) { send: function () {
const result = parseTreeRoot(vNode); const result = roots.reduce((pre, current) => {
window.postMessage(packagePayload({ const info = parseTreeRoot(current);
data: result, pre.push(info);
type: oneVNodeTreeInfos return pre;
}), '*'); }, []);
postMessage(AllVNodeTreesInfos, result);
}, },
delete: function (vNode: any) { delete: function (vNode: VNode) {
// 开发工具中保存了 vNode 的引用,在清理 VNode 的时候需要一并删除 // 开发工具中保存了 vNode 的引用,在清理 VNode 的时候需要一并删除
deleteVNode(vNode); deleteVNode(vNode);
const index = roots.indexOf(vNode); const index = roots.indexOf(vNode);
@ -39,19 +57,28 @@ function injectHook() {
return; return;
} }
const request = event.data; const request = event.data;
if (checkData(request)) { if (checkMessage(request, DevToolContentScript)) {
const { payload } = request; const { payload } = request;
const { type, data } = payload; const { type, data } = payload;
if (type === requestAllVNodeTreeInfos) { if (type === RequestAllVNodeTreeInfos) {
const result = roots.reduce((pre, current) => { const result = roots.reduce((pre, current) => {
const info = parseTreeRoot(current); const info = parseTreeRoot(current);
pre.push(info); pre.push(info);
return pre; return pre;
}, []); }, []);
window.postMessage(packagePayload({ postMessage(AllVNodeTreesInfos, result);
data: result, } else if (type === RequestComponentAttrs) {
type: allVNodeTreesInfos const vNode: VNode = queryVNode(data);
}), '*'); const tag = vNode.tag;
if (tag === ClassComponent) {
const { props, state } = vNode;
const parsedProps = parseAttr(props);
const parsedState = parseAttr(state);
postMessage(ComponentAttrs, {
parsedProps,
parsedState,
});
}
} }
} }
}); });

View File

@ -8,7 +8,13 @@ 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 {
InitDevToolPageConnection,
AllVNodeTreesInfos,
RequestComponentAttrs,
ComponentAttrs,
DevToolPanel,
} from './../utils/constants';
import { packagePayload } from './../utils/transferTool'; import { packagePayload } from './../utils/transferTool';
const parseVNodeData = (rawData) => { const parseVNodeData = (rawData) => {
@ -18,7 +24,7 @@ const parseVNodeData = (rawData) => {
const data: IData[] = []; const data: IData[] = [];
let i = 0; let i = 0;
while (i < rawData.length) { while (i < rawData.length) {
const id = rawData[i] as string; const id = rawData[i] as number;
i++; i++;
const name = rawData[i] as string; const name = rawData[i] as string;
i++; i++;
@ -53,11 +59,26 @@ const getParents = (item: IData | null, parsedVNodeData: IData[]) => {
return parents; return parents;
}; };
let connection;
if (!isDev) {
// 与 background 的唯一连接
connection = chrome.runtime.connect({
name: 'panel'
});
}
function postMessage(type: string, data: any) {
connection.postMessage(packagePayload({
type: type,
data: data,
}, DevToolPanel));
}
function App() { 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 treeRootInfos = useRef<{id: number, length: number}[]>([]); // 记录保存的根节点 id 和长度
const { const {
filterValue, filterValue,
@ -81,30 +102,31 @@ function App() {
props: parsedMockState, props: parsedMockState,
}); });
} else { } else {
const connection = chrome.runtime.connect({
name: 'panel'
});
// 页面打开后发送初始化请求 // 页面打开后发送初始化请求
connection.postMessage(packagePayload({ postMessage(InitDevToolPageConnection, chrome.devtools.inspectedWindow.tabId);
type: initDevToolPageConnection,
data: chrome.devtools.inspectedWindow.tabId
}));
// 监听 background消息 // 监听 background消息
connection.onMessage.addListener(function (message) { connection.onMessage.addListener(function (message) {
const { payload } = message; const { payload } = message;
if (payload) { if (payload) {
const { type, data } = payload; const { type, data } = payload;
if (type === allVNodeTreesInfos) { if (type === AllVNodeTreesInfos) {
const allTreeData = data.reduce((pre, current) => { const allTreeData = data.reduce((pre, current) => {
const parsedTreeData = parseVNodeData(current); const parsedTreeData = parseVNodeData(current);
const length = parsedTreeData.length; const length = parsedTreeData.length;
treeRootInfos.current.length = 0;
if (length) { if (length) {
const treeRoot = parsedTreeData[0]; const treeRoot = parsedTreeData[0];
treeRootInfos.current[treeRoot.id] = length; treeRootInfos.current.push({id: treeRoot.id, length: length});
} }
return pre.concat(parsedTreeData); return pre.concat(parsedTreeData);
}, []); }, []);
setParsedVNodeData(allTreeData); setParsedVNodeData(allTreeData);
} else if (type === ComponentAttrs) {
const {parsedProps, parsedState} = data;
setComponentAttrs({
state: parsedProps,
props: parsedState,
});
} }
} }
}); });
@ -122,10 +144,7 @@ function App() {
props: parsedMockState, props: parsedMockState,
}); });
} else { } else {
connection.postMessage({ postMessage(RequestComponentAttrs, item.id);
name: requestComponentAttrs,
data: item.id
});
} }
setSelectComp(item); setSelectComp(item);
}; };

View File

@ -57,6 +57,10 @@ function parseTreeRoot(treeRoot: VNode) {
return result; return result;
} }
export function queryVNode(id: number) {
return IdToVNodeMap.get(id);
}
export function deleteVNode(vNode: VNode) { export function deleteVNode(vNode: VNode) {
if (VNodeToIdMap.has(vNode)) { if (VNodeToIdMap.has(vNode)) {
const id = VNodeToIdMap.get(vNode); const id = VNodeToIdMap.get(vNode);

View File

@ -1,12 +1,22 @@
// panel 页面打开后初始化连接标志 // panel 页面打开后初始化连接标志
export const initDevToolPageConnection = 'init dev tool page connection'; export const InitDevToolPageConnection = 'init dev tool page connection';
// background 解析全部 root VNodes 标志 // background 解析全部 root VNodes 标志
export const requestAllVNodeTreeInfos = 'request all vNodes tree infos'; export const RequestAllVNodeTreeInfos = 'request all vNodes tree infos';
// vNodes 全部树解析结果标志 // vNodes 全部树解析结果标志
export const allVNodeTreesInfos = 'vNode trees Infos'; export const AllVNodeTreesInfos = 'vNode trees Infos';
// 一棵树的解析 // 一棵树的解析
export const oneVNodeTreeInfos = 'one vNode tree'; export const OneVNodeTreeInfos = 'one vNode tree';
// 获取组件属性 // 获取组件属性
export const requestComponentAttrs = 'get component attrs'; export const RequestComponentAttrs = 'get component attrs';
// 返回组件属性 // 返回组件属性
export const componentAttrs = 'component attrs'; export const ComponentAttrs = 'component attrs';
// 传递消息来源标志
export const DevToolPanel = 'dev tool panel';
export const DevToolBackground = 'dev tool background';
export const DevToolContentScript = 'dev tool content script';
export const DevToolHook = 'dev tool hook';

View File

@ -5,17 +5,28 @@ interface payLoadType {
data?: any, data?: any,
} }
export function packagePayload(payload: payLoadType) { interface message {
type: typeof devTools,
payload: payLoadType,
from: string,
}
export function packagePayload(payload: payLoadType, from: string): message {
return { return {
type: devTools, type: devTools,
payload, payload,
from,
}; };
} }
export function checkData(data: any) { export function checkMessage(data: any, from: string) {
if (data?.type === devTools) { if (data?.type === devTools && data?.from === from) {
return true; return true;
} }
return false; return false;
} }
export function changeSource(message: message, from: string) {
message.from = from;
}