将popup移植了过去
@ -49,10 +49,9 @@ const manifest: CocosPluginManifest = {
|
||||
view_popup: "src/views/popup/index.ts",
|
||||
script_background: "src/scripts/background.ts",
|
||||
script_content: "src/scripts/content.ts",
|
||||
script_inject: "src/scripts/inject.ts",
|
||||
script_inject: "src/scripts/inject/index.ts",
|
||||
},
|
||||
};
|
||||
// 这里的options变量名暂时不支持修改,发布时会进行必要的修改
|
||||
const options: CocosPluginOptions = {
|
||||
server: {
|
||||
enabled: true,
|
||||
|
@ -2,8 +2,12 @@
|
||||
"author": "cc-plugin",
|
||||
"description": "cocos creator plugin",
|
||||
"devDependencies": {
|
||||
"@types/chrome": "0.0.133",
|
||||
"@types/fs-extra": "9.0.1",
|
||||
"@types/kind-of": "^6.0.0",
|
||||
"@types/lodash": "^4.14.176",
|
||||
"@types/node": "16.11.12",
|
||||
"@types/uuid": "^8.3.1",
|
||||
"@xuyanfeng/cc-ui": "^0.2.14",
|
||||
"cc-plugin": "file:.yalc/cc-plugin"
|
||||
},
|
||||
@ -19,5 +23,9 @@
|
||||
"ccp-serve-web": "cc-plugin serve web",
|
||||
"ccp-serve-chrome": "cc-plugin serve chrome"
|
||||
},
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"uuid": "^8.3.2",
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
}
|
||||
|
@ -1 +1,288 @@
|
||||
console.log("background");
|
||||
import { Msg, Page, PluginEvent } from "../core/types";
|
||||
import { ChromeConst } from "cc-plugin/src/chrome/const";
|
||||
|
||||
// @ts-ignore
|
||||
// import * as UA from "universal-analytics";
|
||||
import { v4 } from "uuid";
|
||||
import { FrameDetails } from "../views/devtools/data";
|
||||
|
||||
// 统计服务
|
||||
const userID = localStorage.getItem("userID") || v4();
|
||||
// UA("UA-134924925-3", userID);
|
||||
|
||||
console.log("on background");
|
||||
|
||||
class PortMan {
|
||||
public currentUseContentFrameID = 0;
|
||||
public content: Array<chrome.runtime.Port> = []; // 因为iframe的原因,可能对应多个,主iframe的id===0
|
||||
public devtools: chrome.runtime.Port | null = null;
|
||||
public id: number | null = null; // tab.id作为唯一标识
|
||||
public title: string = "";
|
||||
public url: string = "";
|
||||
private mgr: PortManagement | null = null;
|
||||
|
||||
constructor(mgr: PortManagement, { id, url, title }: any) {
|
||||
this.mgr = mgr;
|
||||
this.id = id;
|
||||
this.url = url;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
private onPortConnect(
|
||||
port: chrome.runtime.Port,
|
||||
onMsg: Function,
|
||||
onDisconnect: Function
|
||||
) {
|
||||
console.log(`%c[Connect] ${port.name}`, "color:green");
|
||||
port.onMessage.addListener((data: any, sender: any) => {
|
||||
console.log(
|
||||
`%c[Connect-Message] ${sender.name}\n${JSON.stringify(data)}`,
|
||||
"color:blue;"
|
||||
);
|
||||
// 如果多个页面都监听 onMessage 事件,对于某一次事件只有第一次调用 sendResponse() 能成功发出回应,所有其他回应将被忽略。
|
||||
// sender.postMessage(data);
|
||||
onMsg && onMsg(data);
|
||||
});
|
||||
port.onDisconnect.addListener((port: chrome.runtime.Port) => {
|
||||
console.log(`%c[Connect-Dis] ${port.name}`, "color:red");
|
||||
onDisconnect && onDisconnect(port);
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentUseContent(): chrome.runtime.Port | null {
|
||||
return (
|
||||
this.content.find(
|
||||
(el) =>
|
||||
el.sender?.frameId !== undefined &&
|
||||
el.sender.frameId === this.currentUseContentFrameID
|
||||
) || null
|
||||
);
|
||||
}
|
||||
|
||||
_updateFrames() {
|
||||
let data: FrameDetails[] = this.content.map((item) => {
|
||||
return {
|
||||
url: item.sender?.url || "",
|
||||
frameID: item.sender?.frameId || 0,
|
||||
};
|
||||
});
|
||||
let event = new PluginEvent(
|
||||
Page.Background,
|
||||
Page.Devtools,
|
||||
Msg.UpdateFrames,
|
||||
data
|
||||
);
|
||||
this.sendDevtoolMsg(event);
|
||||
}
|
||||
|
||||
dealConnect(port: chrome.runtime.Port) {
|
||||
switch (port.name) {
|
||||
case Page.Content: {
|
||||
this.content.push(port);
|
||||
this._updateFrames();
|
||||
this.onPortConnect(
|
||||
port,
|
||||
(data: PluginEvent) => {
|
||||
if (data.target === Page.Devtools) {
|
||||
this.sendDevtoolMsg(data);
|
||||
}
|
||||
},
|
||||
(disPort: chrome.runtime.Port) => {
|
||||
const index = this.content.findIndex(
|
||||
(el) =>
|
||||
disPort.sender?.frameId !== undefined &&
|
||||
el.sender?.frameId !== undefined &&
|
||||
el.sender?.frameId === disPort.sender?.frameId
|
||||
);
|
||||
this.content.splice(index, 1);
|
||||
this._updateFrames();
|
||||
this.checkValid();
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Page.Devtools: {
|
||||
this.devtools = port;
|
||||
this._updateFrames(); // 当devtools链接后,主动派发frames数据
|
||||
this.onPortConnect(
|
||||
port,
|
||||
(data: PluginEvent) => {
|
||||
if (data.msg === Msg.UseFrame) {
|
||||
this.currentUseContentFrameID = data.data;
|
||||
// 更新这个frame的tree
|
||||
this.updateCurrentFrameTree();
|
||||
} else {
|
||||
// 从devtools过来的消息统一派发到Content中
|
||||
if (PluginEvent.check(data, Page.Devtools, Page.Background)) {
|
||||
if (data.msg === Msg.TreeInfo) {
|
||||
if (this.currentUseContentFrameID !== data.data) {
|
||||
console.log(`frameID[${data.data}]不一致`);
|
||||
}
|
||||
}
|
||||
PluginEvent.reset(data, Page.Background, Page.Content);
|
||||
this.getCurrentUseContent()?.postMessage(data);
|
||||
}
|
||||
}
|
||||
},
|
||||
() => {
|
||||
this.devtools = null;
|
||||
this.checkValid();
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateCurrentFrameTree() {
|
||||
const sendData = new PluginEvent(
|
||||
Page.Background,
|
||||
Page.Content,
|
||||
Msg.Support
|
||||
);
|
||||
this.getCurrentUseContent()?.postMessage(sendData);
|
||||
}
|
||||
|
||||
checkValid() {
|
||||
if (!this.devtools && !this.content.length) {
|
||||
this.mgr?.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
sendContentMsg(data: PluginEvent) {
|
||||
this.getCurrentUseContent()?.postMessage(data);
|
||||
}
|
||||
|
||||
sendDevtoolMsg(data: PluginEvent) {
|
||||
this.devtools?.postMessage(data);
|
||||
}
|
||||
}
|
||||
|
||||
class PortManagement {
|
||||
port: Array<PortMan> = [];
|
||||
|
||||
constructor() {
|
||||
this.initConnect();
|
||||
chrome.runtime.onMessage.addListener(
|
||||
(request: PluginEvent, sender: any, sendResponse: any) => {
|
||||
const tabID = sender.tab.id;
|
||||
const portMan: PortMan | undefined = this.find(tabID);
|
||||
if (portMan) {
|
||||
if (PluginEvent.check(request, Page.Content, Page.Background)) {
|
||||
// 监听来自content.js发来的事件,将消息转发到devtools
|
||||
PluginEvent.reset(request, Page.Background, Page.Devtools);
|
||||
console.log(
|
||||
`%c[Message]url:${sender.url}]\n${JSON.stringify(request)}`,
|
||||
"color:green"
|
||||
);
|
||||
portMan.sendDevtoolMsg(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
chrome.tabs.onActivated.addListener(({ tabId, windowId }) => {});
|
||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||
// 页面发生刷新,通知重新生成数据
|
||||
if (changeInfo.status === "complete") {
|
||||
const { id } = tab;
|
||||
// -1为自己
|
||||
if (id && id > -1) {
|
||||
let portMan = this.find(id);
|
||||
if (portMan) {
|
||||
let data = new PluginEvent(
|
||||
Page.Background,
|
||||
Page.Content,
|
||||
Msg.Support
|
||||
);
|
||||
portMan.sendContentMsg(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isDevtools(port: chrome.runtime.Port) {
|
||||
const devtoolsUrl = `chrome-extension://${port.sender?.id}/${ChromeConst.html.devtools}`;
|
||||
if (port.sender?.url === devtoolsUrl) {
|
||||
}
|
||||
}
|
||||
|
||||
initConnect() {
|
||||
chrome.runtime.onConnect.addListener((port: chrome.runtime.Port) => {
|
||||
if (port.name === Page.Devtools) {
|
||||
// devtool链接过来没有port.sender.tab
|
||||
chrome.tabs.getSelected((tab: chrome.tabs.Tab) => {
|
||||
this._onConnect(tab, port);
|
||||
});
|
||||
} else {
|
||||
const tab: chrome.tabs.Tab | undefined = port.sender?.tab;
|
||||
if (tab) {
|
||||
this._onConnect(tab, port);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _onConnect(tab: chrome.tabs.Tab, port: chrome.runtime.Port) {
|
||||
const { id, title, url } = tab;
|
||||
if (id !== undefined && id > -1) {
|
||||
let portMan: PortMan | undefined = this.find(id);
|
||||
if (!portMan) {
|
||||
portMan = new PortMan(this, { id, title, url });
|
||||
this.port.push(portMan);
|
||||
}
|
||||
portMan.dealConnect(port);
|
||||
}
|
||||
}
|
||||
|
||||
find(id: number): PortMan | undefined {
|
||||
return this.port.find((el) => el.id === id);
|
||||
}
|
||||
|
||||
remove(item: PortMan) {
|
||||
let index = this.port.findIndex((el) => el === item);
|
||||
if (index > -1) {
|
||||
this.port.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).backgroundInstance = new PortManagement();
|
||||
|
||||
function createPluginMenus() {
|
||||
const menus = [];
|
||||
|
||||
let parent = chrome.contextMenus.create({
|
||||
id: "parent",
|
||||
title: "CC-Inspector",
|
||||
});
|
||||
chrome.contextMenus.create({
|
||||
id: "test",
|
||||
title: "测试右键菜单",
|
||||
parentId: parent,
|
||||
// 上下文环境,可选:["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"],默认page
|
||||
contexts: ["page"],
|
||||
});
|
||||
chrome.contextMenus.create({
|
||||
id: "notify",
|
||||
parentId: parent,
|
||||
title: "通知",
|
||||
});
|
||||
|
||||
chrome.contextMenus.onClicked.addListener(function (info, tab) {
|
||||
if (info.menuItemId === "test") {
|
||||
alert("您点击了右键菜单!");
|
||||
} else if (info.menuItemId === "notify") {
|
||||
chrome.notifications.create("null", {
|
||||
type: "basic",
|
||||
iconUrl: "icons/48.png",
|
||||
title: "通知",
|
||||
message: "测试通知",
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
chrome.contextMenus.removeAll(function () {
|
||||
createPluginMenus();
|
||||
});
|
||||
|
@ -1 +1,61 @@
|
||||
console.log("content.ts");
|
||||
// content.js 和原始界面共享DOM,具有操作dom的能力
|
||||
// 但是不共享js,要想访问页面js,只能通过注入的方式
|
||||
import {injectScript} from "../core/util";
|
||||
import {Msg, Page, PluginEvent} from "../core/types";
|
||||
|
||||
injectScript("js/inject.js");
|
||||
|
||||
class Content {
|
||||
private connect: chrome.runtime.Port | null = null;
|
||||
|
||||
constructor() {
|
||||
// 接受来自inject.js的消息数据,然后中转到background.js
|
||||
window.addEventListener("message", (event) => {
|
||||
let data: PluginEvent = event.data;
|
||||
if (PluginEvent.check(data, Page.Inject, Page.Content)) {
|
||||
console.log("[Window-Message]: ", data);
|
||||
PluginEvent.reset(data, Page.Content, Page.Devtools)
|
||||
this.connect?.postMessage(data)
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
// 和background.js保持长连接通讯,background和content的交互也要通过这个链接进行通讯
|
||||
private connectToBackground() {
|
||||
this.connect = chrome.runtime.connect({name: Page.Content})
|
||||
this.connect.onMessage.addListener((data: PluginEvent, sender) => {
|
||||
if (PluginEvent.check(data, Page.Background, Page.Content)) {
|
||||
// console.log(`%c[Connect-Message] ${JSON.stringify(data)}`, "color:green;")
|
||||
console.log("[Connect-Message]: ", data);
|
||||
PluginEvent.reset(data, Page.Content, Page.Inject)
|
||||
window.postMessage(data, "*");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private sendMessageToBackground(data: PluginEvent) {
|
||||
if (this.connect) {
|
||||
this.connect.postMessage(data);
|
||||
}
|
||||
}
|
||||
|
||||
async run() {
|
||||
this.connectToBackground();
|
||||
this.checkGame();
|
||||
}
|
||||
|
||||
private checkGame() {
|
||||
let gameCanvas = document.querySelector("#GameCanvas");
|
||||
if (!gameCanvas) {
|
||||
let sendData = new PluginEvent(Page.Content, Page.Devtools, Msg.Support, {
|
||||
support: false,
|
||||
msg: "未发现GameCanvas,不支持调试游戏!"
|
||||
})
|
||||
this.sendMessageToBackground(sendData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const content = new Content();
|
||||
content.run();
|
||||
|
@ -1,3 +0,0 @@
|
||||
console.log("injected");
|
||||
const a = 1;
|
||||
const b = 2;
|
@ -8,39 +8,44 @@ import {
|
||||
Group,
|
||||
ImageData,
|
||||
Info,
|
||||
InvalidData, NodeInfoData,
|
||||
InvalidData,
|
||||
NodeInfoData,
|
||||
NumberData,
|
||||
ObjectData, ObjectItemRequestData,
|
||||
ObjectData,
|
||||
ObjectItemRequestData,
|
||||
Property,
|
||||
StringData,
|
||||
TreeData,
|
||||
Vec2Data,
|
||||
Vec3Data
|
||||
} from "@/devtools/data";
|
||||
import {Msg, Page, PluginEvent} from "@/core/types"
|
||||
import {BuildArrayOptions, BuildImageOptions, BuildObjectOptions, BuildVecOptions} from "@/inject/types";
|
||||
// @ts-ignore
|
||||
import {uniq} from "lodash"
|
||||
import {trySetValueWithConfig, getValue} from "@/inject/setValue";
|
||||
import {isHasProperty} from "@/inject/util";
|
||||
Vec3Data,
|
||||
} from "../../views/devtools/data";
|
||||
import { Msg, Page, PluginEvent } from "../../core/types";
|
||||
import {
|
||||
BuildArrayOptions,
|
||||
BuildImageOptions,
|
||||
BuildObjectOptions,
|
||||
BuildVecOptions,
|
||||
} from "./types";
|
||||
import { uniq } from "lodash";
|
||||
import { trySetValueWithConfig, getValue } from "./setValue";
|
||||
import { isHasProperty } from "./util";
|
||||
|
||||
declare const cc: any;
|
||||
|
||||
|
||||
class CCInspector {
|
||||
inspectorGameMemoryStorage: Record<string, any> = {}
|
||||
inspectorGameMemoryStorage: Record<string, any> = {};
|
||||
|
||||
private watchIsCocosGame() {
|
||||
const timer = setInterval(() => {
|
||||
if (this._isCocosGame()) {
|
||||
clearInterval(timer)
|
||||
clearInterval(timer);
|
||||
// @ts-ignore
|
||||
cc.director.on(cc.Director.EVENT_AFTER_SCENE_LAUNCH, () => {
|
||||
let isCocosGame = this._isCocosGame();
|
||||
this.notifySupportGame(isCocosGame)
|
||||
})
|
||||
this.notifySupportGame(isCocosGame);
|
||||
});
|
||||
}
|
||||
}, 300)
|
||||
}, 300);
|
||||
}
|
||||
|
||||
init() {
|
||||
@ -49,16 +54,19 @@ class CCInspector {
|
||||
window.addEventListener("message", (event) => {
|
||||
// 接受来自content的事件,有可能也会受到其他插件的
|
||||
if (!event || !event.data) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
let pluginEvent: PluginEvent = event.data;
|
||||
if (PluginEvent.check(pluginEvent, Page.Content, Page.Inject)) {
|
||||
console.log(`%c[Inject] ${JSON.stringify(pluginEvent)}`, "color:green;");
|
||||
PluginEvent.finish(pluginEvent)
|
||||
console.log(
|
||||
`%c[Inject] ${JSON.stringify(pluginEvent)}`,
|
||||
"color:green;"
|
||||
);
|
||||
PluginEvent.finish(pluginEvent);
|
||||
switch (pluginEvent.msg) {
|
||||
case Msg.Support: {
|
||||
let isCocosGame = this._isCocosGame();
|
||||
this.notifySupportGame(isCocosGame)
|
||||
this.notifySupportGame(isCocosGame);
|
||||
break;
|
||||
}
|
||||
case Msg.TreeInfo: {
|
||||
@ -81,7 +89,7 @@ class CCInspector {
|
||||
if (this.setValue(data.path, value)) {
|
||||
this.sendMsgToContent(Msg.UpdateProperty, data);
|
||||
} else {
|
||||
console.warn(`设置失败:${data.path}`)
|
||||
console.warn(`设置失败:${data.path}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -106,7 +114,7 @@ class CCInspector {
|
||||
let result: ObjectItemRequestData = {
|
||||
id: data.id,
|
||||
data: itemData,
|
||||
}
|
||||
};
|
||||
this.sendMsgToContent(Msg.GetObjectItemData, result);
|
||||
}
|
||||
break;
|
||||
@ -118,7 +126,10 @@ class CCInspector {
|
||||
|
||||
sendMsgToContent(msg: Msg, data: any) {
|
||||
// 发送给content.js处理,也会导致发送给了自身,死循环
|
||||
window.postMessage(new PluginEvent(Page.Inject, Page.Content, msg, data), "*");
|
||||
window.postMessage(
|
||||
new PluginEvent(Page.Inject, Page.Content, msg, data),
|
||||
"*"
|
||||
);
|
||||
}
|
||||
|
||||
notifySupportGame(b: boolean) {
|
||||
@ -132,18 +143,17 @@ class CCInspector {
|
||||
let scene = cc.director.getScene();
|
||||
if (scene) {
|
||||
let treeData = new TreeData();
|
||||
this.getNodeChildren(scene, treeData)
|
||||
this.getNodeChildren(scene, treeData);
|
||||
this.sendMsgToContent(Msg.TreeInfo, treeData);
|
||||
} else {
|
||||
console.warn("can't execute api : cc.director.getScene")
|
||||
console.warn("can't execute api : cc.director.getScene");
|
||||
this.notifySupportGame(false);
|
||||
}
|
||||
} else {
|
||||
this.notifySupportGame(false)
|
||||
this.notifySupportGame(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// @ts-ignore
|
||||
draw: cc.Graphics = null;
|
||||
|
||||
@ -158,14 +168,14 @@ class CCInspector {
|
||||
// @ts-ignore
|
||||
draw = this.draw = node.addComponent(cc.Graphics);
|
||||
}
|
||||
draw.clear()
|
||||
draw.clear();
|
||||
draw.lineWidth = 10;
|
||||
// @ts-ignore
|
||||
draw.strokeColor = new cc.Color().fromHEX("#ff0000")
|
||||
const {anchorX, anchorY, width, height, x, y} = node;
|
||||
draw.strokeColor = new cc.Color().fromHEX("#ff0000");
|
||||
const { anchorX, anchorY, width, height, x, y } = node;
|
||||
let halfWidth = width / 2;
|
||||
let halfHeight = height / 2;
|
||||
let leftBottom = node.convertToWorldSpaceAR(cc.v2(-halfWidth, -halfHeight))
|
||||
let leftBottom = node.convertToWorldSpaceAR(cc.v2(-halfWidth, -halfHeight));
|
||||
let leftTop = node.convertToWorldSpaceAR(cc.v2(-halfWidth, halfHeight));
|
||||
let rightBottom = node.convertToWorldSpaceAR(cc.v2(halfWidth, -halfHeight));
|
||||
let rightTop = node.convertToWorldSpaceAR(cc.v2(halfWidth, halfHeight));
|
||||
@ -175,10 +185,10 @@ class CCInspector {
|
||||
draw.lineTo(end.x, end.y);
|
||||
}
|
||||
|
||||
line(leftBottom, rightBottom)
|
||||
line(rightBottom, rightTop)
|
||||
line(rightTop, leftTop)
|
||||
line(leftTop, leftBottom)
|
||||
line(leftBottom, rightBottom);
|
||||
line(rightBottom, rightTop);
|
||||
line(rightTop, leftTop);
|
||||
line(leftTop, leftBottom);
|
||||
this.draw.stroke();
|
||||
}
|
||||
|
||||
@ -198,7 +208,7 @@ class CCInspector {
|
||||
let childItem = nodeChildren[i];
|
||||
let treeData = new TreeData();
|
||||
this.getNodeChildren(childItem, treeData);
|
||||
data.children.push(treeData)
|
||||
data.children.push(treeData);
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,45 +233,61 @@ class CCInspector {
|
||||
}
|
||||
const proto = Object.getPrototypeOf(root);
|
||||
if (proto) {
|
||||
circle(proto)
|
||||
circle(proto);
|
||||
}
|
||||
}
|
||||
|
||||
circle(obj)
|
||||
circle(obj);
|
||||
return keys;
|
||||
}
|
||||
|
||||
_getNodeKeys(node: any) {
|
||||
// 3.x变成了getter
|
||||
let excludeProperty = [
|
||||
"children", "quat", "node", "components", "parent",
|
||||
"children",
|
||||
"quat",
|
||||
"node",
|
||||
"components",
|
||||
"parent",
|
||||
// 生命周期函数
|
||||
"onFocusInEditor", "onRestore", "start", "lateUpdate", "update", "resetInEditor", "onLostFocusInEditor",
|
||||
"onEnable", "onDisable", "onDestroy", "onLoad",
|
||||
"onFocusInEditor",
|
||||
"onRestore",
|
||||
"start",
|
||||
"lateUpdate",
|
||||
"update",
|
||||
"resetInEditor",
|
||||
"onLostFocusInEditor",
|
||||
"onEnable",
|
||||
"onDisable",
|
||||
"onDestroy",
|
||||
"onLoad",
|
||||
];
|
||||
const keyHidden = this.getAllPropertyDescriptors(node);
|
||||
const keyVisible1 = Object.keys(node); // Object不走原型链
|
||||
let keyVisible2: string[] = [];
|
||||
for (let nodeKey in node) {// 走原型链
|
||||
keyVisible2.push(nodeKey)
|
||||
for (let nodeKey in node) {
|
||||
// 走原型链
|
||||
keyVisible2.push(nodeKey);
|
||||
}
|
||||
let allKeys: string[] = uniq(keyHidden.concat(keyVisible1, keyVisible2)).sort();
|
||||
allKeys = allKeys.filter(key => {
|
||||
let allKeys: string[] = uniq(
|
||||
keyHidden.concat(keyVisible1, keyVisible2)
|
||||
).sort();
|
||||
allKeys = allKeys.filter((key) => {
|
||||
return !key.startsWith("_") && !excludeProperty.includes(key);
|
||||
});
|
||||
|
||||
allKeys = allKeys.filter(key => {
|
||||
allKeys = allKeys.filter((key) => {
|
||||
try {
|
||||
return typeof node[key] !== "function"
|
||||
return typeof node[key] !== "function";
|
||||
} catch (e) {
|
||||
// console.warn(`属性${key}出现异常:\n`, e);
|
||||
return false;
|
||||
}
|
||||
})
|
||||
});
|
||||
return allKeys;
|
||||
}
|
||||
|
||||
_getPairProperty(key: string): null | { key: string, values: string[] } {
|
||||
_getPairProperty(key: string): null | { key: string; values: string[] } {
|
||||
let pairProperty: Record<string, any> = {
|
||||
rotation: ["rotationX", "rotationY"],
|
||||
anchor: ["anchorX", "anchorY"],
|
||||
@ -274,7 +300,7 @@ class CCInspector {
|
||||
if (pairProperty.hasOwnProperty(pairPropertyKey)) {
|
||||
let pair = pairProperty[pairPropertyKey];
|
||||
if (pair.includes(key) || key === pairPropertyKey) {
|
||||
return {key: pairPropertyKey, values: pair};
|
||||
return { key: pairPropertyKey, values: pair };
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -289,15 +315,15 @@ class CCInspector {
|
||||
const path: Array<string> = options.path;
|
||||
|
||||
if (ctor && value instanceof ctor) {
|
||||
let hasUnOwnProperty = keys.find(key => !value.hasOwnProperty(key))
|
||||
let hasUnOwnProperty = keys.find((key) => !value.hasOwnProperty(key));
|
||||
if (!hasUnOwnProperty) {
|
||||
for (let key in keys) {
|
||||
let propName = keys[key];
|
||||
if (value.hasOwnProperty(propName)) {
|
||||
let propPath = path.concat(propName);
|
||||
let itemData = this._genInfoData(value, propName, propPath)
|
||||
let itemData = this._genInfoData(value, propName, propPath);
|
||||
if (itemData) {
|
||||
data.add(new Property(propName, itemData))
|
||||
data.add(new Property(propName, itemData));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -326,7 +352,12 @@ class CCInspector {
|
||||
return null;
|
||||
}
|
||||
|
||||
_genInfoData(node: any, key: string | number, path: Array<string>, filterKey = true): Info | null {
|
||||
_genInfoData(
|
||||
node: any,
|
||||
key: string | number,
|
||||
path: Array<string>,
|
||||
filterKey = true
|
||||
): Info | null {
|
||||
let propertyValue = node[key];
|
||||
let info = null;
|
||||
let invalidType = this._isInvalidValue(propertyValue);
|
||||
@ -358,38 +389,43 @@ class CCInspector {
|
||||
path: path,
|
||||
value: propertyValue,
|
||||
keys: keys,
|
||||
})
|
||||
});
|
||||
} else {
|
||||
!info && (info = this._buildVecData({
|
||||
// @ts-ignore
|
||||
ctor: cc.Vec3,
|
||||
path: path,
|
||||
data: new Vec3Data(),
|
||||
keys: ["x", "y", "z"],
|
||||
value: propertyValue,
|
||||
}))
|
||||
!info && (info = this._buildVecData({
|
||||
// @ts-ignore
|
||||
ctor: cc.Vec2,
|
||||
path: path,
|
||||
data: new Vec2Data(),
|
||||
keys: ["x", "y"],
|
||||
value: propertyValue
|
||||
}))
|
||||
!info && (info = this._buildImageData({
|
||||
//@ts-ignore
|
||||
ctor: cc.SpriteFrame,
|
||||
data: new ImageData(),
|
||||
path: path,
|
||||
value: propertyValue,
|
||||
}))
|
||||
!info &&
|
||||
(info = this._buildVecData({
|
||||
// @ts-ignore
|
||||
ctor: cc.Vec3,
|
||||
path: path,
|
||||
data: new Vec3Data(),
|
||||
keys: ["x", "y", "z"],
|
||||
value: propertyValue,
|
||||
}));
|
||||
!info &&
|
||||
(info = this._buildVecData({
|
||||
// @ts-ignore
|
||||
ctor: cc.Vec2,
|
||||
path: path,
|
||||
data: new Vec2Data(),
|
||||
keys: ["x", "y"],
|
||||
value: propertyValue,
|
||||
}));
|
||||
!info &&
|
||||
(info = this._buildImageData({
|
||||
//@ts-ignore
|
||||
ctor: cc.SpriteFrame,
|
||||
data: new ImageData(),
|
||||
path: path,
|
||||
value: propertyValue,
|
||||
}));
|
||||
if (!info) {
|
||||
if (typeof propertyValue === "object") {
|
||||
let ctorName = propertyValue.constructor?.name;
|
||||
if (ctorName) {
|
||||
if (ctorName.startsWith("cc_") ||
|
||||
if (
|
||||
ctorName.startsWith("cc_") ||
|
||||
// 2.4.0
|
||||
ctorName === "CCClass") {
|
||||
ctorName === "CCClass"
|
||||
) {
|
||||
info = new EngineData();
|
||||
info.engineType = ctorName;
|
||||
info.engineName = propertyValue.name;
|
||||
@ -404,7 +440,7 @@ class CCInspector {
|
||||
path: path,
|
||||
value: propertyValue,
|
||||
filterKey: filterKey,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -413,7 +449,7 @@ class CCInspector {
|
||||
}
|
||||
}
|
||||
if (info) {
|
||||
info.readonly = this._isReadonly(node, key)
|
||||
info.readonly = this._isReadonly(node, key);
|
||||
info.path = path;
|
||||
} else {
|
||||
console.error(`暂不支持的属性值`, propertyValue);
|
||||
@ -421,31 +457,36 @@ class CCInspector {
|
||||
return info;
|
||||
}
|
||||
|
||||
_buildArrayData({value, path, data, keys}: BuildArrayOptions) {
|
||||
keys = keys.filter(key => !key.toString().startsWith("_"));
|
||||
_buildArrayData({ value, path, data, keys }: BuildArrayOptions) {
|
||||
keys = keys.filter((key) => !key.toString().startsWith("_"));
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
let propPath = path.concat(key.toString());
|
||||
let itemData = this._genInfoData(value, key, propPath);
|
||||
if (itemData) {
|
||||
data.add(new Property(key.toString(), itemData))
|
||||
data.add(new Property(key.toString(), itemData));
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
_buildObjectItemData({value, path, data, filterKey}: BuildObjectOptions): Property[] {
|
||||
_buildObjectItemData({
|
||||
value,
|
||||
path,
|
||||
data,
|
||||
filterKey,
|
||||
}: BuildObjectOptions): Property[] {
|
||||
let keys = Object.keys(value);
|
||||
if (filterKey) {
|
||||
keys = this.filterKeys(keys);// 不再进行开发者定义的数据
|
||||
keys = this.filterKeys(keys); // 不再进行开发者定义的数据
|
||||
}
|
||||
let ret: Property[] = []
|
||||
let ret: Property[] = [];
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let key = keys[i];
|
||||
let propPath = path.concat(key.toString());
|
||||
let itemData = this._genInfoData(value, key, propPath, filterKey);
|
||||
if (itemData) {
|
||||
ret.push(new Property(key, itemData))
|
||||
ret.push(new Property(key, itemData));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@ -453,33 +494,38 @@ class CCInspector {
|
||||
|
||||
filterKeys(keys: string[]) {
|
||||
// 剔除_开头的属性
|
||||
return keys.filter(key => !key.toString().startsWith("_"));
|
||||
return keys.filter((key) => !key.toString().startsWith("_"));
|
||||
}
|
||||
|
||||
_isInvalidValue(value: any) {
|
||||
// !!Infinity=true
|
||||
if ((value && value !== Infinity) || value === 0 || value === false || value === "") {
|
||||
if (
|
||||
(value && value !== Infinity) ||
|
||||
value === 0 ||
|
||||
value === false ||
|
||||
value === ""
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
return "null"
|
||||
return "null";
|
||||
} else if (value === Infinity) {
|
||||
return "Infinity"
|
||||
return "Infinity";
|
||||
} else if (value === undefined) {
|
||||
return "undefined"
|
||||
return "undefined";
|
||||
} else if (Number.isNaN(value)) {
|
||||
return "NaN";
|
||||
} else {
|
||||
debugger
|
||||
debugger;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_buildObjectData({value, path, data, filterKey}: BuildObjectOptions) {
|
||||
_buildObjectData({ value, path, data, filterKey }: BuildObjectOptions) {
|
||||
let keys = Object.keys(value);
|
||||
if (filterKey) {
|
||||
keys = this.filterKeys(keys)
|
||||
keys = this.filterKeys(keys);
|
||||
}
|
||||
// 只返回一级key,更深层级的key需要的时候,再获取,防止circle object导致的死循环
|
||||
let desc: Record<string, any> = {};
|
||||
@ -490,11 +536,10 @@ class CCInspector {
|
||||
let keyDesc = "";
|
||||
if (Array.isArray(propValue)) {
|
||||
// 只收集一级key
|
||||
propValue.forEach(item => {
|
||||
|
||||
})
|
||||
keyDesc = `(${propValue.length}) [...]`
|
||||
} else if (this._isInvalidValue(propValue)) { // 不能改变顺序
|
||||
propValue.forEach((item) => {});
|
||||
keyDesc = `(${propValue.length}) [...]`;
|
||||
} else if (this._isInvalidValue(propValue)) {
|
||||
// 不能改变顺序
|
||||
keyDesc = propValue;
|
||||
} else if (typeof propValue === "object") {
|
||||
keyDesc = `${propValue.constructor.name} {...}`;
|
||||
@ -536,14 +581,14 @@ class CCInspector {
|
||||
const name = this.getCompName(node);
|
||||
let nodeGroup = new Group(name, node.uuid);
|
||||
let keys = this._getNodeKeys(node);
|
||||
for (let i = 0; i < keys.length;) {
|
||||
for (let i = 0; i < keys.length; ) {
|
||||
let key = keys[i];
|
||||
let pair = this._getPairProperty(key);
|
||||
if (pair && this._checkKeysValid(node, pair.values)) {
|
||||
let bSplice = false;
|
||||
// 把这个成对的属性剔除掉
|
||||
pair.values.forEach((item: string) => {
|
||||
let index = keys.findIndex(el => el === item);
|
||||
let index = keys.findIndex((el) => el === item);
|
||||
if (index !== -1) {
|
||||
keys.splice(index, 1);
|
||||
if (pair && item === pair.key) {
|
||||
@ -607,7 +652,7 @@ class CCInspector {
|
||||
const data: NodeInfoData = {
|
||||
uuid: uuid,
|
||||
group: groupData,
|
||||
}
|
||||
};
|
||||
this.sendMsgToContent(Msg.NodeInfo, data);
|
||||
} else {
|
||||
// 未获取到节点数据
|
||||
@ -623,13 +668,13 @@ class CCInspector {
|
||||
}
|
||||
|
||||
_isReadonly(base: Object, key: string | number): boolean {
|
||||
let ret = Object.getOwnPropertyDescriptor(base, key)
|
||||
let ret = Object.getOwnPropertyDescriptor(base, key);
|
||||
if (ret) {
|
||||
return !(ret.set || ret.writable);
|
||||
} else {
|
||||
let proto = Object.getPrototypeOf(base);
|
||||
if (proto) {
|
||||
return this._isReadonly(proto, key)
|
||||
return this._isReadonly(proto, key);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -664,8 +709,8 @@ class CCInspector {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
onMemoryInfo() {
|
||||
const memory = console["memory"];
|
||||
this.sendMsgToContent(Msg.MemoryInfo, {
|
||||
performance: {
|
||||
// @ts-ignore
|
||||
@ -675,10 +720,11 @@ class CCInspector {
|
||||
// @ts-ignore
|
||||
usedJSHeapSize: window.performance.memory.usedJSHeapSize,
|
||||
},
|
||||
|
||||
console: {
|
||||
jsHeapSizeLimit: console.memory.jsHeapSizeLimit,
|
||||
totalJSHeapSize: console.memory.totalJSHeapSize,
|
||||
usedJSHeapSize: console.memory.usedJSHeapSize,
|
||||
jsHeapSizeLimit: memory.jsHeapSizeLimit,
|
||||
totalJSHeapSize: memory.totalJSHeapSize,
|
||||
usedJSHeapSize: memory.usedJSHeapSize,
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -688,7 +734,3 @@ let inspector = new CCInspector();
|
||||
inspector.init();
|
||||
//@ts-ignore
|
||||
window.CCInspector = inspector;
|
||||
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {isVersion3} from "@/inject/util";
|
||||
import {isVersion3} from "./util";
|
||||
|
||||
interface ConfigItem {
|
||||
path: string[],
|
@ -1,4 +1,4 @@
|
||||
import {ArrayData, ImageData, ObjectData, Vec2Data, Vec3Data} from "@/devtools/data";
|
||||
import {ArrayData, ImageData, ObjectData, Vec2Data, Vec3Data} from "../../views/devtools/data";
|
||||
|
||||
export interface BuildObjectOptions {
|
||||
path: string[];
|
@ -1,20 +1,151 @@
|
||||
<template>
|
||||
<div class="popup">popup</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref, provide, nextTick } from "vue";
|
||||
export default defineComponent({
|
||||
name: "popup",
|
||||
components: {},
|
||||
setup(props, ctx) {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.popup {
|
||||
widows: 10px;
|
||||
height: 10px;
|
||||
background-color: rebeccapurple;
|
||||
<div class="popup">
|
||||
<div class="head">
|
||||
<div class="name">{{ title }}</div>
|
||||
<div style="flex: 1"></div>
|
||||
<el-button
|
||||
class="el-icon-setting btn"
|
||||
@click="onClickOptions"
|
||||
></el-button>
|
||||
</div>
|
||||
|
||||
<div class="wechat">
|
||||
<div class="money">
|
||||
<img class="png" src="./res/money.png" alt="" />
|
||||
<div class="tips">请我喝杯奶茶</div>
|
||||
</div>
|
||||
<div class="space"></div>
|
||||
<div class="friends">
|
||||
<img class="png" src="./res/friend.png" alt="" />
|
||||
<div class="tips">交个朋友</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="foot">
|
||||
<a href="https://tidys.gitee.io/doc/#" target="_blank">
|
||||
<img class="icon" src="./res/tiezi.png" alt="" />
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/tidys/CocosCreatorPlugins/tree/master/CocosCreatorInspector"
|
||||
target="_blank"
|
||||
>
|
||||
<img class="icon" src="./res/github.png" alt="" />
|
||||
</a>
|
||||
<a href="https://jq.qq.com/?_wv=1027&k=5SdPdy2" target="_blank">
|
||||
<img class="icon" src="./res/qq.png" alt="" />
|
||||
</a>
|
||||
<div class="space"></div>
|
||||
<div v-if="version">ver:{{ version }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref, provide, nextTick } from "vue";
|
||||
import CCP from "cc-plugin/src/ccp/entry-render";
|
||||
import { ChromeConst } from "cc-plugin/src/chrome/const";
|
||||
export default defineComponent({
|
||||
name: "popup",
|
||||
components: {},
|
||||
setup(props, ctx) {
|
||||
const title = ref(CCP.manifest.name);
|
||||
const version = ref(CCP.manifest.version);
|
||||
let longConn: chrome.runtime.Port | null = null;
|
||||
function _initLongConn() {
|
||||
if (!longConn) {
|
||||
console.log("[popup] 初始化长连接");
|
||||
if (chrome && chrome.runtime) {
|
||||
longConn = chrome.runtime.connect({ name: "popup" });
|
||||
longConn.onMessage.addListener((data: any, sender: any) => {
|
||||
_onLongConnMsg(data, sender);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
function _onLongConnMsg(data: string, sender: any) {
|
||||
// console.log( title);
|
||||
}
|
||||
onMounted(() => {
|
||||
_initLongConn();
|
||||
});
|
||||
return {
|
||||
title,
|
||||
version,
|
||||
onClickOptions() {
|
||||
if (chrome && chrome.tabs) {
|
||||
chrome.tabs.create({ url: ChromeConst.html.popup });
|
||||
}
|
||||
},
|
||||
onBtnClickGitHub() {
|
||||
console.log("onBtnClickGitHub");
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.popup {
|
||||
widows: 10px;
|
||||
height: 10px;
|
||||
background-color: rebeccapurple;
|
||||
|
||||
width: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
|
||||
.head {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
.name {
|
||||
user-select: none;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
.wechat {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.png {
|
||||
width: auto;
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-size: 15px;
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
color: #6d6d6d;
|
||||
}
|
||||
}
|
||||
|
||||
.foot {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
|
||||
.space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin: 0 3px;
|
||||
width: auto;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
Before Width: | Height: | Size: 621 KiB After Width: | Height: | Size: 621 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 167 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
@ -275,6 +275,14 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/chrome@0.0.133":
|
||||
version "0.0.133"
|
||||
resolved "https://registry.yarnpkg.com/@types/chrome/-/chrome-0.0.133.tgz#9e1d55441584ba2d5274ca84db36427da9c5dc6e"
|
||||
integrity sha512-G8uIUdaCTBILprQvQXBWGXZxjAWbkCkFQit17cdH3zYQEwU8f/etNl8+M7e8MRz9Xj8daHaVpysneMZMx8/ldQ==
|
||||
dependencies:
|
||||
"@types/filesystem" "*"
|
||||
"@types/har-format" "*"
|
||||
|
||||
"@types/connect-history-api-fallback@^1.3.5":
|
||||
version "1.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3"
|
||||
@ -331,6 +339,18 @@
|
||||
"@types/qs" "*"
|
||||
"@types/serve-static" "*"
|
||||
|
||||
"@types/filesystem@*":
|
||||
version "0.0.35"
|
||||
resolved "https://registry.yarnpkg.com/@types/filesystem/-/filesystem-0.0.35.tgz#6d6766626083e2b397c09bdc57092827120db11d"
|
||||
integrity sha512-1eKvCaIBdrD2mmMgy5dwh564rVvfEhZTWVQQGRNn0Nt4ZEnJ0C8oSUCzvMKRA4lGde5oEVo+q2MrTTbV/GHDCQ==
|
||||
dependencies:
|
||||
"@types/filewriter" "*"
|
||||
|
||||
"@types/filewriter@*":
|
||||
version "0.0.32"
|
||||
resolved "https://registry.yarnpkg.com/@types/filewriter/-/filewriter-0.0.32.tgz#3cf7e0f870e54e60ed1bbd9280fa24a9444d3b48"
|
||||
integrity sha512-Kpi2GXQyYJdjL8mFclL1eDgihn1SIzorMZjD94kdPZh9E4VxGOeyjPxi5LpsM4Zku7P0reqegZTt2GxhmA9VBg==
|
||||
|
||||
"@types/fs-extra@9.0.1":
|
||||
version "9.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.1.tgz#91c8fc4c51f6d5dbe44c2ca9ab09310bd00c7918"
|
||||
@ -346,6 +366,11 @@
|
||||
"@types/minimatch" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/har-format@*":
|
||||
version "1.2.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/har-format/-/har-format-1.2.15.tgz#f352493638c2f89d706438a19a9eb300b493b506"
|
||||
integrity sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==
|
||||
|
||||
"@types/html-minifier-terser@^6.0.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
|
||||
@ -368,11 +393,23 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||
|
||||
"@types/kind-of@^6.0.0":
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/kind-of/-/kind-of-6.0.3.tgz#9951fdf237298edf705839ba376054c8a5f55eae"
|
||||
integrity sha512-JEwIR1oOC99PjUTd1LUHxq3gd6w9GGJ8nYUTHMfwOLszw6GuQB4ezsnk3WEeMOtOFx12Jk2JWrGttSnwUD4hrQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/lodash@4.14.185":
|
||||
version "4.14.185"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.185.tgz#c9843f5a40703a8f5edfd53358a58ae729816908"
|
||||
integrity sha512-evMDG1bC4rgQg4ku9tKpuMh5iBNEwNa3tf9zRHdP1qlv+1WUg44xat4IxCE14gIpZRGUUWAx2VhItCZc25NfMA==
|
||||
|
||||
"@types/lodash@^4.14.176":
|
||||
version "4.14.202"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.202.tgz#f09dbd2fb082d507178b2f2a5c7e74bd72ff98f8"
|
||||
integrity sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==
|
||||
|
||||
"@types/mime@*":
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45"
|
||||
@ -478,6 +515,11 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/uuid@^8.3.1":
|
||||
version "8.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
|
||||
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
|
||||
|
||||
"@types/ws@^8.5.5":
|
||||
version "8.5.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787"
|
||||
|
@ -14,21 +14,16 @@
|
||||
"fs-extra": "^9.1.0",
|
||||
"less": "^4.1.1",
|
||||
"less-loader": "^7.3.0",
|
||||
"lodash": "^4.17.21",
|
||||
"universal-analytics": "^0.4.23",
|
||||
"uuid": "^8.3.2",
|
||||
"vue": "^2.6.11",
|
||||
"vue-class-component": "^7.2.3",
|
||||
"vue-property-decorator": "^9.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/kind-of": "^6.0.0",
|
||||
"@types/lodash": "^4.14.176",
|
||||
|
||||
"babel-eslint": "^10.1.0",
|
||||
"@types/fs-extra": "^9.0.9",
|
||||
"@types/node": "^14.14.37",
|
||||
"@types/uuid": "^8.3.1",
|
||||
"@types/chrome": "0.0.133",
|
||||
|
||||
"@typescript-eslint/eslint-plugin": "^4.18.0",
|
||||
"@typescript-eslint/parser": "^4.18.0",
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
|
Before Width: | Height: | Size: 6.7 KiB |
@ -1,252 +0,0 @@
|
||||
import {Msg, Page, PluginEvent} from "@/core/types";
|
||||
|
||||
// @ts-ignore
|
||||
import * as UA from "universal-analytics"
|
||||
import {v4} from "uuid"
|
||||
import {devtools_page} from "./manifest.json"
|
||||
import {FrameDetails} from "@/devtools/data";
|
||||
|
||||
// 统计服务
|
||||
const userID = localStorage.getItem("userID") || v4()
|
||||
UA("UA-134924925-3", userID);
|
||||
|
||||
console.log("on background")
|
||||
|
||||
class PortMan {
|
||||
public currentUseContentFrameID = 0;
|
||||
public content: Array<chrome.runtime.Port> = []; // 因为iframe的原因,可能对应多个,主iframe的id===0
|
||||
public devtools: chrome.runtime.Port | null = null;
|
||||
public id: number | null = null;// tab.id作为唯一标识
|
||||
public title: string = "";
|
||||
public url: string = "";
|
||||
private mgr: PortManagement | null = null;
|
||||
|
||||
constructor(mgr: PortManagement, {id, url, title}: any) {
|
||||
this.mgr = mgr;
|
||||
this.id = id;
|
||||
this.url = url;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
private onPortConnect(port: chrome.runtime.Port, onMsg: Function, onDisconnect: Function) {
|
||||
console.log(`%c[Connect] ${port.name}`, "color:green");
|
||||
port.onMessage.addListener((data: any, sender: any) => {
|
||||
console.log(`%c[Connect-Message] ${sender.name}\n${JSON.stringify(data)}`, "color:blue;")
|
||||
// 如果多个页面都监听 onMessage 事件,对于某一次事件只有第一次调用 sendResponse() 能成功发出回应,所有其他回应将被忽略。
|
||||
// sender.postMessage(data);
|
||||
onMsg && onMsg(data);
|
||||
});
|
||||
port.onDisconnect.addListener((port: chrome.runtime.Port) => {
|
||||
console.log(`%c[Connect-Dis] ${port.name}`, "color:red");
|
||||
onDisconnect && onDisconnect(port)
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentUseContent(): chrome.runtime.Port | null {
|
||||
return this.content.find(el => el.sender?.frameId !== undefined && el.sender.frameId === this.currentUseContentFrameID) || null;
|
||||
}
|
||||
|
||||
_updateFrames() {
|
||||
let data: FrameDetails[] = this.content.map(item => {
|
||||
return {
|
||||
url: item.sender?.url || "",
|
||||
frameID: item.sender?.frameId || 0,
|
||||
}
|
||||
})
|
||||
let event = new PluginEvent(Page.Background, Page.Devtools, Msg.UpdateFrames, data)
|
||||
this.sendDevtoolMsg(event)
|
||||
}
|
||||
|
||||
dealConnect(port: chrome.runtime.Port) {
|
||||
switch (port.name) {
|
||||
case Page.Content: {
|
||||
this.content.push(port);
|
||||
this._updateFrames();
|
||||
this.onPortConnect(port,
|
||||
(data: PluginEvent) => {
|
||||
if (data.target === Page.Devtools) {
|
||||
this.sendDevtoolMsg(data);
|
||||
}
|
||||
},
|
||||
(disPort: chrome.runtime.Port) => {
|
||||
const index = this.content.findIndex(el =>
|
||||
disPort.sender?.frameId !== undefined
|
||||
&& el.sender?.frameId !== undefined
|
||||
&& el.sender?.frameId === disPort.sender?.frameId
|
||||
);
|
||||
this.content.splice(index, 1);
|
||||
this._updateFrames();
|
||||
this.checkValid();
|
||||
})
|
||||
break;
|
||||
}
|
||||
case Page.Devtools: {
|
||||
this.devtools = port;
|
||||
this._updateFrames(); // 当devtools链接后,主动派发frames数据
|
||||
this.onPortConnect(port,
|
||||
(data: PluginEvent) => {
|
||||
if (data.msg === Msg.UseFrame) {
|
||||
this.currentUseContentFrameID = data.data;
|
||||
// 更新这个frame的tree
|
||||
this.updateCurrentFrameTree();
|
||||
} else {
|
||||
// 从devtools过来的消息统一派发到Content中
|
||||
if (PluginEvent.check(data, Page.Devtools, Page.Background)) {
|
||||
if (data.msg === Msg.TreeInfo) {
|
||||
if (this.currentUseContentFrameID !== data.data) {
|
||||
console.log(`frameID[${data.data}]不一致`);
|
||||
}
|
||||
}
|
||||
PluginEvent.reset(data, Page.Background, Page.Content);
|
||||
this.getCurrentUseContent()?.postMessage(data)
|
||||
}
|
||||
}
|
||||
},
|
||||
() => {
|
||||
this.devtools = null;
|
||||
this.checkValid();
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateCurrentFrameTree() {
|
||||
const sendData = new PluginEvent(Page.Background, Page.Content, Msg.Support);
|
||||
this.getCurrentUseContent()?.postMessage(sendData);
|
||||
}
|
||||
|
||||
checkValid() {
|
||||
if (!this.devtools && !this.content.length) {
|
||||
this.mgr?.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
sendContentMsg(data: PluginEvent) {
|
||||
this.getCurrentUseContent()?.postMessage(data);
|
||||
}
|
||||
|
||||
sendDevtoolMsg(data: PluginEvent) {
|
||||
this.devtools?.postMessage(data)
|
||||
}
|
||||
}
|
||||
|
||||
class PortManagement {
|
||||
port: Array<PortMan> = [];
|
||||
|
||||
constructor() {
|
||||
this.initConnect();
|
||||
chrome.runtime.onMessage.addListener((request: PluginEvent, sender: any, sendResponse: any) => {
|
||||
const tabID = sender.tab.id;
|
||||
const portMan: PortMan | undefined = this.find(tabID);
|
||||
if (portMan) {
|
||||
if (PluginEvent.check(request, Page.Content, Page.Background)) {
|
||||
// 监听来自content.js发来的事件,将消息转发到devtools
|
||||
PluginEvent.reset(request, Page.Background, Page.Devtools)
|
||||
console.log(`%c[Message]url:${sender.url}]\n${JSON.stringify(request)}`, "color:green")
|
||||
portMan.sendDevtoolMsg(request);
|
||||
}
|
||||
}
|
||||
})
|
||||
chrome.tabs.onActivated.addListener(({tabId, windowId}) => {
|
||||
})
|
||||
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
|
||||
// 页面发生刷新,通知重新生成数据
|
||||
if (changeInfo.status === "complete") {
|
||||
const {id} = tab;
|
||||
// -1为自己
|
||||
if (id && id > -1) {
|
||||
let portMan = this.find(id);
|
||||
if (portMan) {
|
||||
let data = new PluginEvent(Page.Background, Page.Content, Msg.Support);
|
||||
portMan.sendContentMsg(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
isDevtools(port: chrome.runtime.Port) {
|
||||
const devtoolsUrl = `chrome-extension://${port.sender?.id}/${devtools_page}`
|
||||
if (port.sender?.url === devtoolsUrl) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
initConnect() {
|
||||
chrome.runtime.onConnect.addListener((port: chrome.runtime.Port) => {
|
||||
if (port.name === Page.Devtools) {
|
||||
// devtool链接过来没有port.sender.tab
|
||||
chrome.tabs.getSelected((tab: chrome.tabs.Tab) => {
|
||||
this._onConnect(tab, port)
|
||||
})
|
||||
} else {
|
||||
const tab: chrome.tabs.Tab | undefined = port.sender?.tab;
|
||||
if (tab) {
|
||||
this._onConnect(tab, port)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private _onConnect(tab: chrome.tabs.Tab, port: chrome.runtime.Port) {
|
||||
const {id, title, url} = tab;
|
||||
if (id !== undefined && id > -1) {
|
||||
let portMan: PortMan | undefined = this.find(id)
|
||||
if (!portMan) {
|
||||
portMan = new PortMan(this, {id, title, url});
|
||||
this.port.push(portMan);
|
||||
}
|
||||
portMan.dealConnect(port);
|
||||
}
|
||||
}
|
||||
|
||||
find(id: number): PortMan | undefined {
|
||||
return this.port.find(el => el.id === id)
|
||||
}
|
||||
|
||||
remove(item: PortMan) {
|
||||
let index = this.port.findIndex(el => el === item)
|
||||
if (index > -1) {
|
||||
this.port.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(window as any).backgroundInstance = new PortManagement();
|
||||
|
||||
function createPluginMenus() {
|
||||
const menus = [];
|
||||
|
||||
let parent = chrome.contextMenus.create({id: "parent", title: "CC-Inspector"});
|
||||
chrome.contextMenus.create({
|
||||
id: "test",
|
||||
title: "测试右键菜单",
|
||||
parentId: parent,
|
||||
// 上下文环境,可选:["all", "page", "frame", "selection", "link", "editable", "image", "video", "audio"],默认page
|
||||
contexts: ["page"],
|
||||
});
|
||||
chrome.contextMenus.create({
|
||||
id: "notify",
|
||||
parentId: parent,
|
||||
title: "通知"
|
||||
})
|
||||
|
||||
chrome.contextMenus.onClicked.addListener(function (info, tab) {
|
||||
if (info.menuItemId === "test") {
|
||||
alert("您点击了右键菜单!");
|
||||
} else if (info.menuItemId === "notify") {
|
||||
chrome.notifications.create("null", {
|
||||
type: "basic",
|
||||
iconUrl: "icons/48.png",
|
||||
title: "通知",
|
||||
message: "测试通知",
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
chrome.contextMenus.removeAll(function () {
|
||||
createPluginMenus();
|
||||
});
|
||||
|
@ -1,61 +0,0 @@
|
||||
// content.js 和原始界面共享DOM,具有操作dom的能力
|
||||
// 但是不共享js,要想访问页面js,只能通过注入的方式
|
||||
import {injectScript} from "@/core/util";
|
||||
import {Msg, Page, PluginEvent} from "@/core/types";
|
||||
|
||||
injectScript("js/inject.js");
|
||||
|
||||
class Content {
|
||||
private connect: chrome.runtime.Port | null = null;
|
||||
|
||||
constructor() {
|
||||
// 接受来自inject.js的消息数据,然后中转到background.js
|
||||
window.addEventListener("message", (event) => {
|
||||
let data: PluginEvent = event.data;
|
||||
if (PluginEvent.check(data, Page.Inject, Page.Content)) {
|
||||
console.log("[Window-Message]: ", data);
|
||||
PluginEvent.reset(data, Page.Content, Page.Devtools)
|
||||
this.connect?.postMessage(data)
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
// 和background.js保持长连接通讯,background和content的交互也要通过这个链接进行通讯
|
||||
private connectToBackground() {
|
||||
this.connect = chrome.runtime.connect({name: Page.Content})
|
||||
this.connect.onMessage.addListener((data: PluginEvent, sender) => {
|
||||
if (PluginEvent.check(data, Page.Background, Page.Content)) {
|
||||
// console.log(`%c[Connect-Message] ${JSON.stringify(data)}`, "color:green;")
|
||||
console.log("[Connect-Message]: ", data);
|
||||
PluginEvent.reset(data, Page.Content, Page.Inject)
|
||||
window.postMessage(data, "*");
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private sendMessageToBackground(data: PluginEvent) {
|
||||
if (this.connect) {
|
||||
this.connect.postMessage(data);
|
||||
}
|
||||
}
|
||||
|
||||
async run() {
|
||||
this.connectToBackground();
|
||||
this.checkGame();
|
||||
}
|
||||
|
||||
private checkGame() {
|
||||
let gameCanvas = document.querySelector("#GameCanvas");
|
||||
if (!gameCanvas) {
|
||||
let sendData = new PluginEvent(Page.Content, Page.Devtools, Msg.Support, {
|
||||
support: false,
|
||||
msg: "未发现GameCanvas,不支持调试游戏!"
|
||||
})
|
||||
this.sendMessageToBackground(sendData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const content = new Content();
|
||||
content.run();
|
@ -1,10 +0,0 @@
|
||||
import Vue from "vue";
|
||||
import App from "./index.vue";
|
||||
import "element-ui/lib/theme-chalk/index.css"
|
||||
import ElementUI from "element-ui";
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
Vue.use(ElementUI, {size: "mini"});
|
||||
new Vue({
|
||||
render: (h) => h(App),
|
||||
}).$mount("#app");
|
@ -1,159 +0,0 @@
|
||||
<template>
|
||||
<div id="popup">
|
||||
<div class="head">
|
||||
<div class="name">{{ title }}</div>
|
||||
<div style="flex: 1"></div>
|
||||
<el-button class="el-icon-setting btn" @click="onClickOptions"></el-button>
|
||||
</div>
|
||||
|
||||
<div class="wechat">
|
||||
<div class="money">
|
||||
<img class="png" src="./res/money.png" alt=""/>
|
||||
<div class="tips">请我喝杯奶茶</div>
|
||||
</div>
|
||||
<div class="space"></div>
|
||||
<div class="friends">
|
||||
<img class="png" src="./res/friend.png" alt=""/>
|
||||
<div class="tips">交个朋友</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="foot">
|
||||
<a href="https://tidys.gitee.io/doc/#" target="_blank">
|
||||
<img class="icon" src="./res/tiezi.png" alt="">
|
||||
</a>
|
||||
<a href="https://github.com/tidys/CocosCreatorPlugins/tree/master/CocosCreatorInspector" target="_blank">
|
||||
<img class="icon" src="./res/github.png" alt="">
|
||||
</a>
|
||||
<a href="https://jq.qq.com/?_wv=1027&k=5SdPdy2" target="_blank">
|
||||
<img class="icon" src="./res/qq.png" alt="">
|
||||
</a>
|
||||
<div class="space"></div>
|
||||
<div v-if="version">ver:{{ version }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {Component, Vue} from "vue-property-decorator";
|
||||
import Manifest from "../manifest.json"
|
||||
import {version} from "../../package.json"
|
||||
|
||||
@Component({
|
||||
components: {},
|
||||
})
|
||||
export default class App extends Vue {
|
||||
longConn: chrome.runtime.Port | null = null
|
||||
|
||||
data() {
|
||||
return {
|
||||
title: "cc-inspector",
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
created() {
|
||||
this._initLongConn();
|
||||
}
|
||||
|
||||
onBtnClickGitHub() {
|
||||
console.log("onBtnClickGitHub");
|
||||
}
|
||||
|
||||
onClickOptions() {
|
||||
if (chrome && chrome.tabs) {
|
||||
let {page} = Manifest.options_ui;
|
||||
if (page) {
|
||||
chrome.tabs.create({url: page})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_initLongConn() {
|
||||
if (!this.longConn) {
|
||||
console.log("[popup] 初始化长连接");
|
||||
if (chrome && chrome.runtime) {
|
||||
this.longConn = chrome.runtime.connect({name: "popup"});
|
||||
this.longConn.onMessage.addListener((data: any, sender: any) => {
|
||||
this._onLongConnMsg(data, sender);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_onLongConnMsg(data: string, sender: any) {
|
||||
// console.log(this.title);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@import "../index.less";
|
||||
|
||||
#popup {
|
||||
width: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
|
||||
.head {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.name {
|
||||
user-select: none;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn {
|
||||
}
|
||||
}
|
||||
|
||||
.wechat {
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
.space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.png {
|
||||
width: auto;
|
||||
height: 130px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
font-size: 15px;
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
color: #6d6d6d
|
||||
}
|
||||
}
|
||||
|
||||
.foot {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 30px;
|
||||
align-items: center;
|
||||
|
||||
.space {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin: 0 3px;
|
||||
width: auto;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
4
source/src/shims-chrome.d.ts
vendored
@ -1,4 +0,0 @@
|
||||
declare module "*.chrome" {
|
||||
import chrome from "chrome";
|
||||
export default chrome;
|
||||
}
|
13
source/src/shims-tsx.d.ts
vendored
@ -1,13 +0,0 @@
|
||||
import Vue, { VNode } from "vue";
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
// tslint:disable no-empty-interface
|
||||
interface Element extends VNode {}
|
||||
// tslint:disable no-empty-interface
|
||||
interface ElementClass extends Vue {}
|
||||
interface IntrinsicElements {
|
||||
[elem: string]: any;
|
||||
}
|
||||
}
|
||||
}
|
4
source/src/shims-vue.d.ts
vendored
@ -1,4 +0,0 @@
|
||||
declare module "*.vue" {
|
||||
import Vue from "vue";
|
||||
export default Vue;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import Vue from "vue";
|
||||
import App from "./index.vue";
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
new Vue({
|
||||
render: (h) => h(App),
|
||||
}).$mount("#app");
|
@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<div id="app">test</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {Component, Vue} from "vue-property-decorator";
|
||||
|
||||
@Component({
|
||||
components: {},
|
||||
})
|
||||
export default class Index extends Vue {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
|
||||
}
|
||||
</style>
|