feat(extension): 添加调试器功能并优化界面布局
- 集成 Chrome 调试器 API 支持 attach/detach 功能 - 新增 tabUtils 工具模块用于获取活动标签页 ID - 在背景脚本中添加标签页激活和更新事件监听 - 使用 Material-UI 容器和堆叠组件重构页面布局 - 移除 Chromium 启动参数中的自动打开开发者工具选项 - 更新权限配置添加调试器相关权限 - 在测试页面添加多个调试功能按钮和状态管理
This commit is contained in:
@@ -40,7 +40,7 @@ export function TimestampExecution(): JSX.Element {
|
|||||||
const toggleButtonText = isRunningTimestamp ? '停止' : '开始';
|
const toggleButtonText = isRunningTimestamp ? '停止' : '开始';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper elevation={3} sx={{ p: 2, my: 2, borderRadius: 2, minWidth: 320, textAlign: 'center' }}>
|
<Paper elevation={3} sx={{ p: 2, my: 2, borderRadius: 2 }}>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -53,7 +53,7 @@ export function TimestampExecution(): JSX.Element {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography
|
<Typography
|
||||||
variant="h4"
|
variant="h5"
|
||||||
component="span"
|
component="span"
|
||||||
sx={{
|
sx={{
|
||||||
fontFamily: 'monospace',
|
fontFamily: 'monospace',
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import '../.wxt/types/imports.d.ts';
|
|||||||
import { browser } from 'wxt/browser';
|
import { browser } from 'wxt/browser';
|
||||||
import { downloadHtmlInBackground } from '@/utils/recordUtils.tsx';
|
import { downloadHtmlInBackground } from '@/utils/recordUtils.tsx';
|
||||||
import { sendMessage, onMessage } from '@/utils/messages';
|
import { sendMessage, onMessage } from '@/utils/messages';
|
||||||
|
import { getActiveTabId } from '@/utils/tabUtils';
|
||||||
|
|
||||||
const events: unknown[] = [];
|
const events: unknown[] = [];
|
||||||
|
|
||||||
@@ -52,6 +53,14 @@ export default defineBackground(() => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
chrome.tabs.onActivated.addListener((activeInfo) => {
|
||||||
|
console.log('当前活跃的 Tab ID:', activeInfo.tabId);
|
||||||
|
});
|
||||||
|
|
||||||
|
chrome.tabs.onUpdated.addListener((tabId) => {
|
||||||
|
console.log('加载完成的 Tab ID:', tabId);
|
||||||
|
});
|
||||||
|
|
||||||
onMessage('popup:check-status', async (message) => {
|
onMessage('popup:check-status', async (message) => {
|
||||||
console.log(`[background] popup:check-status: ${message}`);
|
console.log(`[background] popup:check-status: ${message}`);
|
||||||
const obj = {
|
const obj = {
|
||||||
@@ -109,35 +118,4 @@ export default defineBackground(() => {
|
|||||||
events.push(eventsList);
|
events.push(eventsList);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
async function getActiveTabId() {
|
|
||||||
const activeTabs = await chrome.tabs.query({
|
|
||||||
active: true,
|
|
||||||
currentWindow: true,
|
|
||||||
});
|
|
||||||
const activeTab = activeTabs[0];
|
|
||||||
|
|
||||||
if (!activeTab) {
|
|
||||||
throw new Error('No active tab found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查URL是否受限
|
|
||||||
if (activeTab?.url) {
|
|
||||||
const restrictedProtocols = [
|
|
||||||
'chrome:',
|
|
||||||
'chrome-extension:',
|
|
||||||
'about:',
|
|
||||||
'edge:',
|
|
||||||
'view-source:',
|
|
||||||
'data:',
|
|
||||||
'file:',
|
|
||||||
];
|
|
||||||
if (restrictedProtocols.some((protocol) => activeTab.url!.startsWith(protocol))) {
|
|
||||||
throw new Error(`Cannot send message to a restricted URL: ${activeTab.url}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const sendTo = activeTab.id;
|
|
||||||
return sendTo!;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState, useMemo } from 'react';
|
import { useEffect, useState, useMemo } from 'react';
|
||||||
import { AppState } from '../types';
|
import { AppState } from '../types';
|
||||||
import { sendMessage, onMessage } from '@/utils/messages';
|
import { sendMessage, onMessage } from '@/utils/messages';
|
||||||
import { Button } from '@mui/material';
|
import { Button, Container, Stack } from '@mui/material';
|
||||||
|
|
||||||
const RecordeReplayPage = () => {
|
const RecordeReplayPage = () => {
|
||||||
const [status, setStatus] = useState<AppState>(AppState.READ);
|
const [status, setStatus] = useState<AppState>(AppState.READ);
|
||||||
@@ -54,8 +54,8 @@ const RecordeReplayPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: '20px' }}>
|
<Container maxWidth={false} sx={{ py: 2.5 }}>
|
||||||
<div style={{ display: 'flex', gap: '10px', marginBottom: '20px' }}>
|
<Stack direction="row" spacing={1.25} sx={{ mb: 2.5 }}>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="medium"
|
size="medium"
|
||||||
@@ -64,8 +64,8 @@ const RecordeReplayPage = () => {
|
|||||||
>
|
>
|
||||||
{isRecording ? '停止录制' : '开始录制'}
|
{isRecording ? '停止录制' : '开始录制'}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</Stack>
|
||||||
</div>
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,84 @@
|
|||||||
import { sendMessage } from '@/utils/messages';
|
import { sendMessage } from '@/utils/messages';
|
||||||
import { Button } from '@mui/material';
|
import { Button } from '@mui/material';
|
||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
const TestPage = () => {
|
const TestPage = () => {
|
||||||
|
const [tabId, setTabId] = useState(-1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const activeTabs = await chrome.tabs.query({
|
||||||
|
active: true,
|
||||||
|
currentWindow: true,
|
||||||
|
});
|
||||||
|
console.log(`activeTabs:`, activeTabs[0].id);
|
||||||
|
if (activeTabs[0].id && activeTabs[0].id > 0) setTabId(activeTabs[0].id);
|
||||||
|
else throw new Error('no active tab');
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
|
||||||
const handleSeedMessage = async () => {
|
const handleSeedMessage = async () => {
|
||||||
const status = await sendMessage('popup:check-status');
|
const status = await sendMessage('popup:check-status');
|
||||||
console.log(`[popup]status: ${status}`);
|
console.log(`[popup]status: ${status}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAttach = () => {
|
||||||
|
try {
|
||||||
|
chrome.debugger.attach({ tabId: tabId }, '1.2', () => {
|
||||||
|
console.log('[debugger] attached');
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDetach = async () => {
|
||||||
|
try {
|
||||||
|
chrome.debugger.detach({ tabId: tabId }, () => {
|
||||||
|
console.log('[debugger] detached');
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleGetTarget = () => {
|
||||||
|
chrome.debugger.getTargets((targets) => {
|
||||||
|
console.log('[debugger] targets:', targets);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const enableRuntime = () => {
|
||||||
|
chrome.debugger.sendCommand({ tabId: tabId }, 'Runtime.enable', {}, (res) => {
|
||||||
|
console.log('[debugger] Runtime.enable:', res);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const disableRuntime = () => {
|
||||||
|
chrome.debugger.sendCommand({ tabId: tabId }, 'Runtime.disable', {}, (res) => {
|
||||||
|
console.log('[debugger] Runtime.disable:', res);
|
||||||
|
});
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button variant="contained" color="primary" onClick={handleSeedMessage}>
|
<Button variant="contained" color="primary" onClick={handleSeedMessage}>
|
||||||
发送消息
|
发送消息
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button variant="contained" color="primary" onClick={handleAttach}>
|
||||||
|
attach
|
||||||
|
</Button>
|
||||||
|
<Button variant="contained" color="primary" onClick={handleDetach}>
|
||||||
|
detach
|
||||||
|
</Button>
|
||||||
|
<Button variant="contained" color="primary" onClick={handleGetTarget}>
|
||||||
|
getTargets
|
||||||
|
</Button>
|
||||||
|
<Button variant="contained" color="primary" onClick={enableRuntime}>
|
||||||
|
enableRuntime
|
||||||
|
</Button>
|
||||||
|
<Button variant="contained" color="primary" onClick={disableRuntime}>
|
||||||
|
disableRuntime
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import { TimestampToDatetime } from '@/components/TimestampToDatetime';
|
import { TimestampToDatetime } from '@/components/TimestampToDatetime';
|
||||||
import { DatetimeToTimestamp } from '@/components/DatetimeToTimestamp';
|
import { DatetimeToTimestamp } from '@/components/DatetimeToTimestamp';
|
||||||
import { TimestampExecution } from '@/components/TimestampExecution';
|
import { TimestampExecution } from '@/components/TimestampExecution';
|
||||||
|
import { Container, Box } from '@mui/material';
|
||||||
|
|
||||||
const TimestampPage = () => {
|
const TimestampPage = () => {
|
||||||
return (
|
return (
|
||||||
<div className="timestamp-utils">
|
<Container maxWidth="md" sx={{ py: 2 }}>
|
||||||
<TimestampExecution />
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, width: '100%' }}>
|
||||||
<TimestampToDatetime />
|
<TimestampExecution />
|
||||||
<DatetimeToTimestamp />
|
<TimestampToDatetime />
|
||||||
</div>
|
<DatetimeToTimestamp />
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { browser } from 'wxt/browser';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前活动标签页的 ID
|
||||||
|
* @returns Promise<number | undefined> 返回活动标签页的 ID,如果未找到则返回 undefined
|
||||||
|
*/
|
||||||
|
export async function getActiveTabId(): Promise<number | undefined> {
|
||||||
|
const activeTabs = await browser.tabs.query({
|
||||||
|
active: true,
|
||||||
|
currentWindow: true,
|
||||||
|
});
|
||||||
|
const activeTab = activeTabs[0];
|
||||||
|
|
||||||
|
if (!activeTab) {
|
||||||
|
throw new Error('No active tab found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查URL是否受限
|
||||||
|
if (activeTab?.url) {
|
||||||
|
const restrictedProtocols = [
|
||||||
|
'chrome:',
|
||||||
|
'chrome-extension:',
|
||||||
|
'about:',
|
||||||
|
'edge:',
|
||||||
|
'view-source:',
|
||||||
|
'data:',
|
||||||
|
'file:',
|
||||||
|
];
|
||||||
|
if (restrictedProtocols.some((protocol) => activeTab.url!.startsWith(protocol))) {
|
||||||
|
throw new Error(`Cannot send message to a restricted URL: ${activeTab.url}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return activeTab.id;
|
||||||
|
}
|
||||||
+1
-1
@@ -2,5 +2,5 @@ import { defineWebExtConfig } from 'wxt';
|
|||||||
|
|
||||||
export default defineWebExtConfig({
|
export default defineWebExtConfig({
|
||||||
startUrls: ['https://www.baidu.com', 'chrome://extensions/'],
|
startUrls: ['https://www.baidu.com', 'chrome://extensions/'],
|
||||||
chromiumArgs: ['chrome://extensions/', '--auto-open-devtools-for-tabs'],
|
chromiumArgs: ['chrome://extensions/'],
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export default defineConfig({
|
|||||||
'tabs',
|
'tabs',
|
||||||
'offscreen',
|
'offscreen',
|
||||||
'downloads',
|
'downloads',
|
||||||
|
'debugger',
|
||||||
],
|
],
|
||||||
host_permissions: ['<all_urls>'],
|
host_permissions: ['<all_urls>'],
|
||||||
action: {
|
action: {
|
||||||
|
|||||||
Reference in New Issue
Block a user