@donggui/web 1.5.5-donggui.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/bin/midscene-playground +3 -0
- package/bin/midscene-web +2 -0
- package/dist/es/bin.mjs +23 -0
- package/dist/es/bin.mjs.map +1 -0
- package/dist/es/bridge-mode/agent-cli-side.mjs +137 -0
- package/dist/es/bridge-mode/agent-cli-side.mjs.map +1 -0
- package/dist/es/bridge-mode/browser.mjs +2 -0
- package/dist/es/bridge-mode/common.mjs +43 -0
- package/dist/es/bridge-mode/common.mjs.map +1 -0
- package/dist/es/bridge-mode/index.mjs +4 -0
- package/dist/es/bridge-mode/io-client.mjs +101 -0
- package/dist/es/bridge-mode/io-client.mjs.map +1 -0
- package/dist/es/bridge-mode/io-server.mjs +210 -0
- package/dist/es/bridge-mode/io-server.mjs.map +1 -0
- package/dist/es/bridge-mode/page-browser-side.mjs +118 -0
- package/dist/es/bridge-mode/page-browser-side.mjs.map +1 -0
- package/dist/es/chrome-extension/agent.mjs +9 -0
- package/dist/es/chrome-extension/agent.mjs.map +1 -0
- package/dist/es/chrome-extension/cdpInput.mjs +174 -0
- package/dist/es/chrome-extension/cdpInput.mjs.LICENSE.txt +5 -0
- package/dist/es/chrome-extension/cdpInput.mjs.map +1 -0
- package/dist/es/chrome-extension/dynamic-scripts.mjs +38 -0
- package/dist/es/chrome-extension/dynamic-scripts.mjs.map +1 -0
- package/dist/es/chrome-extension/index.mjs +5 -0
- package/dist/es/chrome-extension/page.mjs +651 -0
- package/dist/es/chrome-extension/page.mjs.map +1 -0
- package/dist/es/cli.mjs +16 -0
- package/dist/es/cli.mjs.map +1 -0
- package/dist/es/common/cache-helper.mjs +28 -0
- package/dist/es/common/cache-helper.mjs.map +1 -0
- package/dist/es/index.mjs +6 -0
- package/dist/es/mcp-server.mjs +35 -0
- package/dist/es/mcp-server.mjs.map +1 -0
- package/dist/es/mcp-tools-puppeteer.mjs +215 -0
- package/dist/es/mcp-tools-puppeteer.mjs.map +1 -0
- package/dist/es/mcp-tools.mjs +78 -0
- package/dist/es/mcp-tools.mjs.map +1 -0
- package/dist/es/playwright/ai-fixture.mjs +367 -0
- package/dist/es/playwright/ai-fixture.mjs.map +1 -0
- package/dist/es/playwright/index.mjs +40 -0
- package/dist/es/playwright/index.mjs.map +1 -0
- package/dist/es/playwright/page.mjs +44 -0
- package/dist/es/playwright/page.mjs.map +1 -0
- package/dist/es/playwright/reporter/index.mjs +216 -0
- package/dist/es/playwright/reporter/index.mjs.map +1 -0
- package/dist/es/puppeteer/agent-launcher.mjs +185 -0
- package/dist/es/puppeteer/agent-launcher.mjs.map +1 -0
- package/dist/es/puppeteer/base-page.mjs +564 -0
- package/dist/es/puppeteer/base-page.mjs.map +1 -0
- package/dist/es/puppeteer/index.mjs +34 -0
- package/dist/es/puppeteer/index.mjs.map +1 -0
- package/dist/es/puppeteer/page.mjs +9 -0
- package/dist/es/puppeteer/page.mjs.map +1 -0
- package/dist/es/static/index.mjs +3 -0
- package/dist/es/static/static-agent.mjs +12 -0
- package/dist/es/static/static-agent.mjs.map +1 -0
- package/dist/es/static/static-page.mjs +122 -0
- package/dist/es/static/static-page.mjs.map +1 -0
- package/dist/es/utils.mjs +8 -0
- package/dist/es/utils.mjs.map +1 -0
- package/dist/es/web-element.mjs +59 -0
- package/dist/es/web-element.mjs.map +1 -0
- package/dist/es/web-page.mjs +260 -0
- package/dist/es/web-page.mjs.map +1 -0
- package/dist/lib/bin.js +29 -0
- package/dist/lib/bin.js.map +1 -0
- package/dist/lib/bridge-mode/agent-cli-side.js +174 -0
- package/dist/lib/bridge-mode/agent-cli-side.js.map +1 -0
- package/dist/lib/bridge-mode/browser.js +38 -0
- package/dist/lib/bridge-mode/browser.js.map +1 -0
- package/dist/lib/bridge-mode/common.js +107 -0
- package/dist/lib/bridge-mode/common.js.map +1 -0
- package/dist/lib/bridge-mode/index.js +46 -0
- package/dist/lib/bridge-mode/index.js.map +1 -0
- package/dist/lib/bridge-mode/io-client.js +135 -0
- package/dist/lib/bridge-mode/io-client.js.map +1 -0
- package/dist/lib/bridge-mode/io-server.js +247 -0
- package/dist/lib/bridge-mode/io-server.js.map +1 -0
- package/dist/lib/bridge-mode/page-browser-side.js +162 -0
- package/dist/lib/bridge-mode/page-browser-side.js.map +1 -0
- package/dist/lib/chrome-extension/agent.js +43 -0
- package/dist/lib/chrome-extension/agent.js.map +1 -0
- package/dist/lib/chrome-extension/cdpInput.js +208 -0
- package/dist/lib/chrome-extension/cdpInput.js.LICENSE.txt +5 -0
- package/dist/lib/chrome-extension/cdpInput.js.map +1 -0
- package/dist/lib/chrome-extension/dynamic-scripts.js +88 -0
- package/dist/lib/chrome-extension/dynamic-scripts.js.map +1 -0
- package/dist/lib/chrome-extension/index.js +60 -0
- package/dist/lib/chrome-extension/index.js.map +1 -0
- package/dist/lib/chrome-extension/page.js +685 -0
- package/dist/lib/chrome-extension/page.js.map +1 -0
- package/dist/lib/cli.js +22 -0
- package/dist/lib/cli.js.map +1 -0
- package/dist/lib/common/cache-helper.js +68 -0
- package/dist/lib/common/cache-helper.js.map +1 -0
- package/dist/lib/index.js +60 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/mcp-server.js +75 -0
- package/dist/lib/mcp-server.js.map +1 -0
- package/dist/lib/mcp-tools-puppeteer.js +259 -0
- package/dist/lib/mcp-tools-puppeteer.js.map +1 -0
- package/dist/lib/mcp-tools.js +112 -0
- package/dist/lib/mcp-tools.js.map +1 -0
- package/dist/lib/playwright/ai-fixture.js +404 -0
- package/dist/lib/playwright/ai-fixture.js.map +1 -0
- package/dist/lib/playwright/index.js +93 -0
- package/dist/lib/playwright/index.js.map +1 -0
- package/dist/lib/playwright/page.js +78 -0
- package/dist/lib/playwright/page.js.map +1 -0
- package/dist/lib/playwright/reporter/index.js +250 -0
- package/dist/lib/playwright/reporter/index.js.map +1 -0
- package/dist/lib/puppeteer/agent-launcher.js +253 -0
- package/dist/lib/puppeteer/agent-launcher.js.map +1 -0
- package/dist/lib/puppeteer/base-page.js +607 -0
- package/dist/lib/puppeteer/base-page.js.map +1 -0
- package/dist/lib/puppeteer/index.js +84 -0
- package/dist/lib/puppeteer/index.js.map +1 -0
- package/dist/lib/puppeteer/page.js +43 -0
- package/dist/lib/puppeteer/page.js.map +1 -0
- package/dist/lib/static/index.js +52 -0
- package/dist/lib/static/index.js.map +1 -0
- package/dist/lib/static/static-agent.js +46 -0
- package/dist/lib/static/static-agent.js.map +1 -0
- package/dist/lib/static/static-page.js +156 -0
- package/dist/lib/static/static-page.js.map +1 -0
- package/dist/lib/utils.js +40 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/lib/web-element.js +96 -0
- package/dist/lib/web-element.js.map +1 -0
- package/dist/lib/web-page.js +310 -0
- package/dist/lib/web-page.js.map +1 -0
- package/dist/types/bin.d.ts +1 -0
- package/dist/types/bridge-mode/agent-cli-side.d.ts +49 -0
- package/dist/types/bridge-mode/browser.d.ts +2 -0
- package/dist/types/bridge-mode/common.d.ts +74 -0
- package/dist/types/bridge-mode/index.d.ts +4 -0
- package/dist/types/bridge-mode/io-client.d.ts +10 -0
- package/dist/types/bridge-mode/io-server.d.ts +27 -0
- package/dist/types/bridge-mode/page-browser-side.d.ts +21 -0
- package/dist/types/chrome-extension/agent.d.ts +5 -0
- package/dist/types/chrome-extension/cdpInput.d.ts +52 -0
- package/dist/types/chrome-extension/dynamic-scripts.d.ts +3 -0
- package/dist/types/chrome-extension/index.d.ts +5 -0
- package/dist/types/chrome-extension/page.d.ts +110 -0
- package/dist/types/cli.d.ts +1 -0
- package/dist/types/common/cache-helper.d.ts +20 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/mcp-server.d.ts +26 -0
- package/dist/types/mcp-tools-puppeteer.d.ts +13 -0
- package/dist/types/mcp-tools.d.ts +12 -0
- package/dist/types/playwright/ai-fixture.d.ts +131 -0
- package/dist/types/playwright/index.d.ts +13 -0
- package/dist/types/playwright/page.d.ts +11 -0
- package/dist/types/playwright/reporter/index.d.ts +42 -0
- package/dist/types/puppeteer/agent-launcher.d.ts +61 -0
- package/dist/types/puppeteer/base-page.d.ts +106 -0
- package/dist/types/puppeteer/index.d.ts +10 -0
- package/dist/types/puppeteer/page.d.ts +6 -0
- package/dist/types/static/index.d.ts +2 -0
- package/dist/types/static/static-agent.d.ts +5 -0
- package/dist/types/static/static-page.d.ts +42 -0
- package/dist/types/utils.d.ts +6 -0
- package/dist/types/web-element.d.ts +48 -0
- package/dist/types/web-page.d.ts +62 -0
- package/package.json +166 -0
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
import { limitOpenNewTabScript } from "../web-element.mjs";
|
|
2
|
+
import { treeToList } from "@midscene/shared/extractor";
|
|
3
|
+
import { createImgBase64ByFormat } from "@midscene/shared/img";
|
|
4
|
+
import { getDebug } from "@midscene/shared/logger";
|
|
5
|
+
import { assert } from "@midscene/shared/utils";
|
|
6
|
+
import { buildRectFromElementInfo, judgeOrderSensitive, sanitizeXpaths } from "../common/cache-helper.mjs";
|
|
7
|
+
import { commonWebActionsForWebPage } from "../web-page.mjs";
|
|
8
|
+
import { CdpKeyboard } from "./cdpInput.mjs";
|
|
9
|
+
import { getHtmlElementScript, injectStopWaterFlowAnimation, injectWaterFlowAnimation } from "./dynamic-scripts.mjs";
|
|
10
|
+
function _define_property(obj, key, value) {
|
|
11
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
12
|
+
value: value,
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true
|
|
16
|
+
});
|
|
17
|
+
else obj[key] = value;
|
|
18
|
+
return obj;
|
|
19
|
+
}
|
|
20
|
+
const debug = getDebug('web:chrome-extension:page');
|
|
21
|
+
function sleep(ms) {
|
|
22
|
+
return new Promise((resolve)=>setTimeout(resolve, ms));
|
|
23
|
+
}
|
|
24
|
+
class ChromeExtensionProxyPage {
|
|
25
|
+
actionSpace() {
|
|
26
|
+
return commonWebActionsForWebPage(this);
|
|
27
|
+
}
|
|
28
|
+
async setActiveTabId(tabId) {
|
|
29
|
+
if (this.activeTabId) throw new Error(`Active tab id is already set, which is ${this.activeTabId}, cannot set it to ${tabId}`);
|
|
30
|
+
await chrome.tabs.update(tabId, {
|
|
31
|
+
active: true
|
|
32
|
+
});
|
|
33
|
+
this.activeTabId = tabId;
|
|
34
|
+
}
|
|
35
|
+
async getActiveTabId() {
|
|
36
|
+
return this.activeTabId;
|
|
37
|
+
}
|
|
38
|
+
async getBrowserTabList() {
|
|
39
|
+
const tabs = await chrome.tabs.query({
|
|
40
|
+
currentWindow: true
|
|
41
|
+
});
|
|
42
|
+
return tabs.map((tab)=>({
|
|
43
|
+
id: `${tab.id}`,
|
|
44
|
+
title: tab.title,
|
|
45
|
+
url: tab.url,
|
|
46
|
+
currentActiveTab: tab.active
|
|
47
|
+
})).filter((tab)=>tab.id && tab.title && tab.url);
|
|
48
|
+
}
|
|
49
|
+
async getTabIdOrConnectToCurrentTab() {
|
|
50
|
+
if (this.activeTabId) return this.activeTabId;
|
|
51
|
+
const tabId = await chrome.tabs.query({
|
|
52
|
+
active: true,
|
|
53
|
+
currentWindow: true
|
|
54
|
+
}).then((tabs)=>tabs[0]?.id);
|
|
55
|
+
this.activeTabId = tabId || 0;
|
|
56
|
+
return this.activeTabId;
|
|
57
|
+
}
|
|
58
|
+
async ensureDebuggerAttached() {
|
|
59
|
+
assert(!this.destroyed, 'Page is destroyed');
|
|
60
|
+
const url = await this.url();
|
|
61
|
+
if (url.startsWith('chrome://')) throw new Error('Cannot attach debugger to chrome:// pages, please use Midscene in a normal page with http://, https:// or file://');
|
|
62
|
+
const tabId = await this.getTabIdOrConnectToCurrentTab();
|
|
63
|
+
try {
|
|
64
|
+
await chrome.debugger.attach({
|
|
65
|
+
tabId
|
|
66
|
+
}, '1.3');
|
|
67
|
+
console.log('Debugger attached to tab:', tabId);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
const errorMsg = error?.message || '';
|
|
70
|
+
if (errorMsg.includes('Another debugger is already attached')) return void console.log('Debugger already attached to tab:', tabId);
|
|
71
|
+
if (this._continueWhenFailedToAttachDebugger) return void console.warn('Failed to attach debugger, but continuing due to _continueWhenFailedToAttachDebugger flag', error);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
await sleep(500);
|
|
75
|
+
await this.enableWaterFlowAnimation();
|
|
76
|
+
}
|
|
77
|
+
async showMousePointer(x, y) {
|
|
78
|
+
const pointerScript = `(() => {
|
|
79
|
+
if(typeof window.midsceneWaterFlowAnimation !== 'undefined') {
|
|
80
|
+
window.midsceneWaterFlowAnimation.enable();
|
|
81
|
+
window.midsceneWaterFlowAnimation.showMousePointer(${x}, ${y});
|
|
82
|
+
} else {
|
|
83
|
+
console.log('midsceneWaterFlowAnimation is not defined');
|
|
84
|
+
}
|
|
85
|
+
})()`;
|
|
86
|
+
await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
87
|
+
expression: `${pointerScript}`
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async hideMousePointer() {
|
|
91
|
+
await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
92
|
+
expression: `(() => {
|
|
93
|
+
if(typeof window.midsceneWaterFlowAnimation !== 'undefined') {
|
|
94
|
+
window.midsceneWaterFlowAnimation.hideMousePointer();
|
|
95
|
+
}
|
|
96
|
+
})()`
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async detachDebugger(tabId) {
|
|
100
|
+
const tabIdToDetach = tabId || await this.getTabIdOrConnectToCurrentTab();
|
|
101
|
+
console.log('detaching debugger from tab:', tabIdToDetach);
|
|
102
|
+
try {
|
|
103
|
+
await this.disableWaterFlowAnimation(tabIdToDetach);
|
|
104
|
+
await sleep(200);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.warn('Failed to disable water flow animation', error);
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
await chrome.debugger.detach({
|
|
110
|
+
tabId: tabIdToDetach
|
|
111
|
+
});
|
|
112
|
+
console.log('Debugger detached successfully from tab:', tabIdToDetach);
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.warn('Failed to detach debugger (may already be detached):', error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async enableWaterFlowAnimation() {
|
|
118
|
+
const tabId = await this.getTabIdOrConnectToCurrentTab();
|
|
119
|
+
if (this.forceSameTabNavigation) await chrome.debugger.sendCommand({
|
|
120
|
+
tabId
|
|
121
|
+
}, 'Runtime.evaluate', {
|
|
122
|
+
expression: limitOpenNewTabScript
|
|
123
|
+
});
|
|
124
|
+
const script = await injectWaterFlowAnimation();
|
|
125
|
+
await chrome.debugger.sendCommand({
|
|
126
|
+
tabId
|
|
127
|
+
}, 'Runtime.evaluate', {
|
|
128
|
+
expression: script
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async disableWaterFlowAnimation(tabId) {
|
|
132
|
+
const script = await injectStopWaterFlowAnimation();
|
|
133
|
+
await chrome.debugger.sendCommand({
|
|
134
|
+
tabId
|
|
135
|
+
}, 'Runtime.evaluate', {
|
|
136
|
+
expression: script
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async sendCommandToDebugger(command, params, retryCount = 0) {
|
|
140
|
+
const MAX_RETRIES = 2;
|
|
141
|
+
const tabId = await this.getTabIdOrConnectToCurrentTab();
|
|
142
|
+
try {
|
|
143
|
+
const result = await chrome.debugger.sendCommand({
|
|
144
|
+
tabId
|
|
145
|
+
}, command, params);
|
|
146
|
+
this.enableWaterFlowAnimation().catch((err)=>{
|
|
147
|
+
console.warn('Failed to enable water flow animation:', err);
|
|
148
|
+
});
|
|
149
|
+
return result;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
const errorMsg = error?.message || '';
|
|
152
|
+
const isDetachError = errorMsg.includes('Debugger is not attached') || errorMsg.includes('Cannot access a Target') || errorMsg.includes('No target with given id');
|
|
153
|
+
if (isDetachError && retryCount < MAX_RETRIES) {
|
|
154
|
+
console.log(`Debugger not attached for command "${command}", attempting to attach (retry ${retryCount + 1}/${MAX_RETRIES})`);
|
|
155
|
+
await this.ensureDebuggerAttached();
|
|
156
|
+
return this.sendCommandToDebugger(command, params, retryCount + 1);
|
|
157
|
+
}
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async getPageContentByCDP() {
|
|
162
|
+
const script = await getHtmlElementScript();
|
|
163
|
+
await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
164
|
+
expression: script
|
|
165
|
+
});
|
|
166
|
+
const expression = ()=>{
|
|
167
|
+
const tree = window.midscene_element_inspector.webExtractNodeTree();
|
|
168
|
+
return {
|
|
169
|
+
tree,
|
|
170
|
+
size: {
|
|
171
|
+
width: document.documentElement.clientWidth,
|
|
172
|
+
height: document.documentElement.clientHeight
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
const returnValue = await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
177
|
+
expression: `(${expression.toString()})()`,
|
|
178
|
+
returnByValue: true
|
|
179
|
+
});
|
|
180
|
+
if (!returnValue.result.value) {
|
|
181
|
+
const errorDescription = returnValue.exceptionDetails?.exception?.description || '';
|
|
182
|
+
if (!errorDescription) console.error('returnValue from cdp', returnValue);
|
|
183
|
+
throw new Error(`Failed to get page content from page, error: ${errorDescription}`);
|
|
184
|
+
}
|
|
185
|
+
return returnValue.result.value;
|
|
186
|
+
}
|
|
187
|
+
async evaluateJavaScript(script) {
|
|
188
|
+
return this.sendCommandToDebugger('Runtime.evaluate', {
|
|
189
|
+
expression: script,
|
|
190
|
+
awaitPromise: true
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async beforeInvokeAction() {
|
|
194
|
+
try {
|
|
195
|
+
await this.waitUntilNetworkIdle();
|
|
196
|
+
} catch (error) {}
|
|
197
|
+
}
|
|
198
|
+
async waitUntilNetworkIdle() {
|
|
199
|
+
const timeout = 10000;
|
|
200
|
+
const startTime = Date.now();
|
|
201
|
+
let lastReadyState = '';
|
|
202
|
+
while(Date.now() - startTime < timeout){
|
|
203
|
+
const result = await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
204
|
+
expression: 'document.readyState'
|
|
205
|
+
});
|
|
206
|
+
lastReadyState = result.result.value;
|
|
207
|
+
if ('complete' === lastReadyState) return void await new Promise((resolve)=>setTimeout(resolve, 300));
|
|
208
|
+
await new Promise((resolve)=>setTimeout(resolve, 300));
|
|
209
|
+
}
|
|
210
|
+
throw new Error(`Failed to wait until network idle, last readyState: ${lastReadyState}`);
|
|
211
|
+
}
|
|
212
|
+
async getElementsInfo() {
|
|
213
|
+
const tree = await this.getElementsNodeTree();
|
|
214
|
+
return treeToList(tree);
|
|
215
|
+
}
|
|
216
|
+
async getXpathsByPoint(point, isOrderSensitive = false) {
|
|
217
|
+
const script = await getHtmlElementScript();
|
|
218
|
+
await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
219
|
+
expression: script
|
|
220
|
+
});
|
|
221
|
+
const result = await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
222
|
+
expression: `window.midscene_element_inspector.getXpathsByPoint({left: ${point.left}, top: ${point.top}}, ${isOrderSensitive})`,
|
|
223
|
+
returnByValue: true
|
|
224
|
+
});
|
|
225
|
+
return result.result.value;
|
|
226
|
+
}
|
|
227
|
+
async getElementInfoByXpath(xpath) {
|
|
228
|
+
const script = await getHtmlElementScript();
|
|
229
|
+
await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
230
|
+
expression: script
|
|
231
|
+
});
|
|
232
|
+
const result = await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
233
|
+
expression: `window.midscene_element_inspector.getElementInfoByXpath(${JSON.stringify(xpath)})`,
|
|
234
|
+
returnByValue: true
|
|
235
|
+
});
|
|
236
|
+
return result.result.value;
|
|
237
|
+
}
|
|
238
|
+
async cacheFeatureForPoint(center, options) {
|
|
239
|
+
const point = {
|
|
240
|
+
left: center[0],
|
|
241
|
+
top: center[1]
|
|
242
|
+
};
|
|
243
|
+
try {
|
|
244
|
+
const isOrderSensitive = await judgeOrderSensitive(options, debug);
|
|
245
|
+
const xpaths = await this.getXpathsByPoint(point, isOrderSensitive);
|
|
246
|
+
return {
|
|
247
|
+
xpaths: sanitizeXpaths(xpaths)
|
|
248
|
+
};
|
|
249
|
+
} catch (error) {
|
|
250
|
+
debug('cacheFeatureForPoint failed: %O', error);
|
|
251
|
+
return {
|
|
252
|
+
xpaths: []
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async rectMatchesCacheFeature(feature) {
|
|
257
|
+
const xpaths = sanitizeXpaths(feature.xpaths);
|
|
258
|
+
for (const xpath of xpaths)try {
|
|
259
|
+
const elementInfo = await this.getElementInfoByXpath(xpath);
|
|
260
|
+
if (elementInfo?.rect) return buildRectFromElementInfo(elementInfo);
|
|
261
|
+
} catch (error) {
|
|
262
|
+
debug('rectMatchesCacheFeature failed for xpath %s: %O', xpath, error);
|
|
263
|
+
}
|
|
264
|
+
throw new Error(`No matching element rect found for cache feature (tried ${xpaths.length} xpath(s))`);
|
|
265
|
+
}
|
|
266
|
+
async getElementsNodeTree() {
|
|
267
|
+
await this.hideMousePointer();
|
|
268
|
+
const content = await this.getPageContentByCDP();
|
|
269
|
+
if (content?.size) this.viewportSize = content.size;
|
|
270
|
+
return content?.tree || {
|
|
271
|
+
node: null,
|
|
272
|
+
children: []
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
async size() {
|
|
276
|
+
if (this.viewportSize) return this.viewportSize;
|
|
277
|
+
const result = await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
278
|
+
expression: '({width: window.innerWidth, height: window.innerHeight})',
|
|
279
|
+
returnByValue: true
|
|
280
|
+
});
|
|
281
|
+
const sizeInfo = result.result.value;
|
|
282
|
+
console.log('sizeInfo', sizeInfo);
|
|
283
|
+
this.viewportSize = sizeInfo;
|
|
284
|
+
return sizeInfo;
|
|
285
|
+
}
|
|
286
|
+
async screenshotBase64() {
|
|
287
|
+
await this.hideMousePointer();
|
|
288
|
+
const format = 'jpeg';
|
|
289
|
+
const base64 = await this.sendCommandToDebugger('Page.captureScreenshot', {
|
|
290
|
+
format,
|
|
291
|
+
quality: 90
|
|
292
|
+
});
|
|
293
|
+
return createImgBase64ByFormat(format, base64.data);
|
|
294
|
+
}
|
|
295
|
+
async url() {
|
|
296
|
+
const tabId = await this.getTabIdOrConnectToCurrentTab();
|
|
297
|
+
const url = await chrome.tabs.get(tabId).then((tab)=>tab.url);
|
|
298
|
+
return url || '';
|
|
299
|
+
}
|
|
300
|
+
async navigate(url) {
|
|
301
|
+
const tabId = await this.getTabIdOrConnectToCurrentTab();
|
|
302
|
+
await chrome.tabs.update(tabId, {
|
|
303
|
+
url
|
|
304
|
+
});
|
|
305
|
+
await this.waitUntilNetworkIdle();
|
|
306
|
+
}
|
|
307
|
+
async reload() {
|
|
308
|
+
const tabId = await this.getTabIdOrConnectToCurrentTab();
|
|
309
|
+
await chrome.tabs.reload(tabId);
|
|
310
|
+
await this.waitUntilNetworkIdle();
|
|
311
|
+
}
|
|
312
|
+
async goBack() {
|
|
313
|
+
const tabId = await this.getTabIdOrConnectToCurrentTab();
|
|
314
|
+
await chrome.tabs.goBack(tabId);
|
|
315
|
+
await this.waitUntilNetworkIdle();
|
|
316
|
+
}
|
|
317
|
+
async scrollUntilTop(startingPoint) {
|
|
318
|
+
if (startingPoint) await this.mouse.move(startingPoint.left, startingPoint.top);
|
|
319
|
+
return this.mouse.wheel(0, -9999999);
|
|
320
|
+
}
|
|
321
|
+
async scrollUntilBottom(startingPoint) {
|
|
322
|
+
if (startingPoint) await this.mouse.move(startingPoint.left, startingPoint.top);
|
|
323
|
+
return this.mouse.wheel(0, 9999999);
|
|
324
|
+
}
|
|
325
|
+
async scrollUntilLeft(startingPoint) {
|
|
326
|
+
if (startingPoint) await this.mouse.move(startingPoint.left, startingPoint.top);
|
|
327
|
+
return this.mouse.wheel(-9999999, 0);
|
|
328
|
+
}
|
|
329
|
+
async scrollUntilRight(startingPoint) {
|
|
330
|
+
if (startingPoint) await this.mouse.move(startingPoint.left, startingPoint.top);
|
|
331
|
+
return this.mouse.wheel(9999999, 0);
|
|
332
|
+
}
|
|
333
|
+
async scrollUp(distance, startingPoint) {
|
|
334
|
+
const { height } = await this.size();
|
|
335
|
+
const scrollDistance = distance || 0.7 * height;
|
|
336
|
+
return this.mouse.wheel(0, -scrollDistance, startingPoint?.left, startingPoint?.top);
|
|
337
|
+
}
|
|
338
|
+
async scrollDown(distance, startingPoint) {
|
|
339
|
+
const { height } = await this.size();
|
|
340
|
+
const scrollDistance = distance || 0.7 * height;
|
|
341
|
+
return this.mouse.wheel(0, scrollDistance, startingPoint?.left, startingPoint?.top);
|
|
342
|
+
}
|
|
343
|
+
async scrollLeft(distance, startingPoint) {
|
|
344
|
+
const { width } = await this.size();
|
|
345
|
+
const scrollDistance = distance || 0.7 * width;
|
|
346
|
+
return this.mouse.wheel(-scrollDistance, 0, startingPoint?.left, startingPoint?.top);
|
|
347
|
+
}
|
|
348
|
+
async scrollRight(distance, startingPoint) {
|
|
349
|
+
const { width } = await this.size();
|
|
350
|
+
const scrollDistance = distance || 0.7 * width;
|
|
351
|
+
return this.mouse.wheel(scrollDistance, 0, startingPoint?.left, startingPoint?.top);
|
|
352
|
+
}
|
|
353
|
+
async clearInput(element) {
|
|
354
|
+
if (!element) return void console.warn('No element to clear input');
|
|
355
|
+
await this.mouse.click(element.center[0], element.center[1]);
|
|
356
|
+
await this.sendCommandToDebugger('Input.dispatchKeyEvent', {
|
|
357
|
+
type: 'keyDown',
|
|
358
|
+
commands: [
|
|
359
|
+
'selectAll'
|
|
360
|
+
]
|
|
361
|
+
});
|
|
362
|
+
await this.sendCommandToDebugger('Input.dispatchKeyEvent', {
|
|
363
|
+
type: 'keyUp',
|
|
364
|
+
commands: [
|
|
365
|
+
'selectAll'
|
|
366
|
+
]
|
|
367
|
+
});
|
|
368
|
+
await sleep(100);
|
|
369
|
+
await this.keyboard.press({
|
|
370
|
+
key: 'Backspace'
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
async destroy() {
|
|
374
|
+
this.destroyed = true;
|
|
375
|
+
const tabIdToDetach = this.activeTabId;
|
|
376
|
+
this.activeTabId = null;
|
|
377
|
+
if (tabIdToDetach) await this.detachDebugger(tabIdToDetach);
|
|
378
|
+
}
|
|
379
|
+
async longPress(x, y, duration) {
|
|
380
|
+
duration = duration || 500;
|
|
381
|
+
const LONG_PRESS_THRESHOLD = 600;
|
|
382
|
+
const MIN_PRESS_THRESHOLD = 300;
|
|
383
|
+
if (duration > LONG_PRESS_THRESHOLD) duration = LONG_PRESS_THRESHOLD;
|
|
384
|
+
if (duration < MIN_PRESS_THRESHOLD) duration = MIN_PRESS_THRESHOLD;
|
|
385
|
+
await this.mouse.move(x, y);
|
|
386
|
+
if (null === this.isMobileEmulation) {
|
|
387
|
+
const result = await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
388
|
+
expression: `(() => {
|
|
389
|
+
return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent);
|
|
390
|
+
})()`,
|
|
391
|
+
returnByValue: true
|
|
392
|
+
});
|
|
393
|
+
this.isMobileEmulation = result?.result?.value;
|
|
394
|
+
}
|
|
395
|
+
if (this.isMobileEmulation) {
|
|
396
|
+
const touchPoints = [
|
|
397
|
+
{
|
|
398
|
+
x: Math.round(x),
|
|
399
|
+
y: Math.round(y)
|
|
400
|
+
}
|
|
401
|
+
];
|
|
402
|
+
await this.sendCommandToDebugger('Input.dispatchTouchEvent', {
|
|
403
|
+
type: 'touchStart',
|
|
404
|
+
touchPoints,
|
|
405
|
+
modifiers: 0
|
|
406
|
+
});
|
|
407
|
+
await new Promise((res)=>setTimeout(res, duration));
|
|
408
|
+
await this.sendCommandToDebugger('Input.dispatchTouchEvent', {
|
|
409
|
+
type: 'touchEnd',
|
|
410
|
+
touchPoints: [],
|
|
411
|
+
modifiers: 0
|
|
412
|
+
});
|
|
413
|
+
} else {
|
|
414
|
+
await this.sendCommandToDebugger('Input.dispatchMouseEvent', {
|
|
415
|
+
type: 'mousePressed',
|
|
416
|
+
x,
|
|
417
|
+
y,
|
|
418
|
+
button: 'left',
|
|
419
|
+
clickCount: 1
|
|
420
|
+
});
|
|
421
|
+
await new Promise((res)=>setTimeout(res, duration));
|
|
422
|
+
await this.sendCommandToDebugger('Input.dispatchMouseEvent', {
|
|
423
|
+
type: 'mouseReleased',
|
|
424
|
+
x,
|
|
425
|
+
y,
|
|
426
|
+
button: 'left',
|
|
427
|
+
clickCount: 1
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
this.latestMouseX = x;
|
|
431
|
+
this.latestMouseY = y;
|
|
432
|
+
}
|
|
433
|
+
async swipe(from, to, duration) {
|
|
434
|
+
const LONG_PRESS_THRESHOLD = 500;
|
|
435
|
+
const MIN_PRESS_THRESHOLD = 150;
|
|
436
|
+
duration = duration || 300;
|
|
437
|
+
if (duration < MIN_PRESS_THRESHOLD) duration = MIN_PRESS_THRESHOLD;
|
|
438
|
+
if (duration > LONG_PRESS_THRESHOLD) duration = LONG_PRESS_THRESHOLD;
|
|
439
|
+
if (null === this.isMobileEmulation) {
|
|
440
|
+
const result = await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
441
|
+
expression: `(() => {
|
|
442
|
+
return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent);
|
|
443
|
+
})()`,
|
|
444
|
+
returnByValue: true
|
|
445
|
+
});
|
|
446
|
+
this.isMobileEmulation = result?.result?.value;
|
|
447
|
+
}
|
|
448
|
+
const steps = 30;
|
|
449
|
+
const delay = duration / steps;
|
|
450
|
+
if (this.isMobileEmulation) {
|
|
451
|
+
await this.sendCommandToDebugger('Input.dispatchTouchEvent', {
|
|
452
|
+
type: 'touchStart',
|
|
453
|
+
touchPoints: [
|
|
454
|
+
{
|
|
455
|
+
x: Math.round(from.x),
|
|
456
|
+
y: Math.round(from.y)
|
|
457
|
+
}
|
|
458
|
+
],
|
|
459
|
+
modifiers: 0
|
|
460
|
+
});
|
|
461
|
+
for(let i = 1; i <= steps; i++){
|
|
462
|
+
const x = from.x + (to.x - from.x) * (i / steps);
|
|
463
|
+
const y = from.y + (to.y - from.y) * (i / steps);
|
|
464
|
+
await this.sendCommandToDebugger('Input.dispatchTouchEvent', {
|
|
465
|
+
type: 'touchMove',
|
|
466
|
+
touchPoints: [
|
|
467
|
+
{
|
|
468
|
+
x: Math.round(x),
|
|
469
|
+
y: Math.round(y)
|
|
470
|
+
}
|
|
471
|
+
],
|
|
472
|
+
modifiers: 0
|
|
473
|
+
});
|
|
474
|
+
await new Promise((res)=>setTimeout(res, delay));
|
|
475
|
+
}
|
|
476
|
+
await this.sendCommandToDebugger('Input.dispatchTouchEvent', {
|
|
477
|
+
type: 'touchEnd',
|
|
478
|
+
touchPoints: [],
|
|
479
|
+
modifiers: 0
|
|
480
|
+
});
|
|
481
|
+
} else {
|
|
482
|
+
await this.mouse.move(from.x, from.y);
|
|
483
|
+
await this.sendCommandToDebugger('Input.dispatchMouseEvent', {
|
|
484
|
+
type: 'mousePressed',
|
|
485
|
+
x: from.x,
|
|
486
|
+
y: from.y,
|
|
487
|
+
button: 'left',
|
|
488
|
+
clickCount: 1
|
|
489
|
+
});
|
|
490
|
+
for(let i = 1; i <= steps; i++){
|
|
491
|
+
const x = from.x + (to.x - from.x) * (i / steps);
|
|
492
|
+
const y = from.y + (to.y - from.y) * (i / steps);
|
|
493
|
+
await this.mouse.move(x, y);
|
|
494
|
+
await new Promise((res)=>setTimeout(res, delay));
|
|
495
|
+
}
|
|
496
|
+
await this.sendCommandToDebugger('Input.dispatchMouseEvent', {
|
|
497
|
+
type: 'mouseReleased',
|
|
498
|
+
x: to.x,
|
|
499
|
+
y: to.y,
|
|
500
|
+
button: 'left',
|
|
501
|
+
clickCount: 1
|
|
502
|
+
});
|
|
503
|
+
}
|
|
504
|
+
this.latestMouseX = to.x;
|
|
505
|
+
this.latestMouseY = to.y;
|
|
506
|
+
}
|
|
507
|
+
constructor(forceSameTabNavigation){
|
|
508
|
+
_define_property(this, "interfaceType", 'chrome-extension-proxy');
|
|
509
|
+
_define_property(this, "forceSameTabNavigation", void 0);
|
|
510
|
+
_define_property(this, "viewportSize", void 0);
|
|
511
|
+
_define_property(this, "activeTabId", null);
|
|
512
|
+
_define_property(this, "destroyed", false);
|
|
513
|
+
_define_property(this, "isMobileEmulation", null);
|
|
514
|
+
_define_property(this, "_continueWhenFailedToAttachDebugger", false);
|
|
515
|
+
_define_property(this, "latestMouseX", 100);
|
|
516
|
+
_define_property(this, "latestMouseY", 100);
|
|
517
|
+
_define_property(this, "mouse", {
|
|
518
|
+
click: async (x, y, options)=>{
|
|
519
|
+
const { button = 'left', count = 1 } = options || {};
|
|
520
|
+
await this.mouse.move(x, y);
|
|
521
|
+
if (null === this.isMobileEmulation) {
|
|
522
|
+
const result = await this.sendCommandToDebugger('Runtime.evaluate', {
|
|
523
|
+
expression: `(() => {
|
|
524
|
+
return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent);
|
|
525
|
+
})()`,
|
|
526
|
+
returnByValue: true
|
|
527
|
+
});
|
|
528
|
+
this.isMobileEmulation = result?.result?.value;
|
|
529
|
+
}
|
|
530
|
+
if (this.isMobileEmulation && 'left' === button) {
|
|
531
|
+
const touchPoints = [
|
|
532
|
+
{
|
|
533
|
+
x: Math.round(x),
|
|
534
|
+
y: Math.round(y)
|
|
535
|
+
}
|
|
536
|
+
];
|
|
537
|
+
await this.sendCommandToDebugger('Input.dispatchTouchEvent', {
|
|
538
|
+
type: 'touchStart',
|
|
539
|
+
touchPoints,
|
|
540
|
+
modifiers: 0
|
|
541
|
+
});
|
|
542
|
+
await this.sendCommandToDebugger('Input.dispatchTouchEvent', {
|
|
543
|
+
type: 'touchEnd',
|
|
544
|
+
touchPoints: [],
|
|
545
|
+
modifiers: 0
|
|
546
|
+
});
|
|
547
|
+
} else for(let i = 0; i < count; i++){
|
|
548
|
+
await this.sendCommandToDebugger('Input.dispatchMouseEvent', {
|
|
549
|
+
type: 'mousePressed',
|
|
550
|
+
x,
|
|
551
|
+
y,
|
|
552
|
+
button,
|
|
553
|
+
clickCount: 1
|
|
554
|
+
});
|
|
555
|
+
await this.sendCommandToDebugger('Input.dispatchMouseEvent', {
|
|
556
|
+
type: 'mouseReleased',
|
|
557
|
+
x,
|
|
558
|
+
y,
|
|
559
|
+
button,
|
|
560
|
+
clickCount: 1
|
|
561
|
+
});
|
|
562
|
+
await sleep(50);
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
wheel: async (deltaX, deltaY, startX, startY)=>{
|
|
566
|
+
const finalX = startX || this.latestMouseX;
|
|
567
|
+
const finalY = startY || this.latestMouseY;
|
|
568
|
+
await this.showMousePointer(finalX, finalY);
|
|
569
|
+
await this.sendCommandToDebugger('Input.dispatchMouseEvent', {
|
|
570
|
+
type: 'mouseWheel',
|
|
571
|
+
x: finalX,
|
|
572
|
+
y: finalY,
|
|
573
|
+
deltaX,
|
|
574
|
+
deltaY
|
|
575
|
+
});
|
|
576
|
+
this.latestMouseX = finalX;
|
|
577
|
+
this.latestMouseY = finalY;
|
|
578
|
+
},
|
|
579
|
+
move: async (x, y)=>{
|
|
580
|
+
await this.showMousePointer(x, y);
|
|
581
|
+
await this.sendCommandToDebugger('Input.dispatchMouseEvent', {
|
|
582
|
+
type: 'mouseMoved',
|
|
583
|
+
x,
|
|
584
|
+
y
|
|
585
|
+
});
|
|
586
|
+
this.latestMouseX = x;
|
|
587
|
+
this.latestMouseY = y;
|
|
588
|
+
},
|
|
589
|
+
drag: async (from, to)=>{
|
|
590
|
+
await this.mouse.move(from.x, from.y);
|
|
591
|
+
await sleep(200);
|
|
592
|
+
await this.sendCommandToDebugger('Input.dispatchMouseEvent', {
|
|
593
|
+
type: 'mousePressed',
|
|
594
|
+
x: from.x,
|
|
595
|
+
y: from.y,
|
|
596
|
+
button: 'left',
|
|
597
|
+
clickCount: 1
|
|
598
|
+
});
|
|
599
|
+
await sleep(300);
|
|
600
|
+
await this.sendCommandToDebugger('Input.dispatchMouseEvent', {
|
|
601
|
+
type: 'mouseMoved',
|
|
602
|
+
x: to.x,
|
|
603
|
+
y: to.y
|
|
604
|
+
});
|
|
605
|
+
await sleep(500);
|
|
606
|
+
await this.sendCommandToDebugger('Input.dispatchMouseEvent', {
|
|
607
|
+
type: 'mouseReleased',
|
|
608
|
+
x: to.x,
|
|
609
|
+
y: to.y,
|
|
610
|
+
button: 'left',
|
|
611
|
+
clickCount: 1
|
|
612
|
+
});
|
|
613
|
+
await sleep(200);
|
|
614
|
+
await this.mouse.move(to.x, to.y);
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
_define_property(this, "keyboard", {
|
|
618
|
+
type: async (text)=>{
|
|
619
|
+
const cdpKeyboard = new CdpKeyboard({
|
|
620
|
+
send: this.sendCommandToDebugger.bind(this)
|
|
621
|
+
});
|
|
622
|
+
await cdpKeyboard.type(text, {
|
|
623
|
+
delay: 0
|
|
624
|
+
});
|
|
625
|
+
},
|
|
626
|
+
press: async (action)=>{
|
|
627
|
+
const cdpKeyboard = new CdpKeyboard({
|
|
628
|
+
send: this.sendCommandToDebugger.bind(this)
|
|
629
|
+
});
|
|
630
|
+
const keys = Array.isArray(action) ? action : [
|
|
631
|
+
action
|
|
632
|
+
];
|
|
633
|
+
for (const k of keys){
|
|
634
|
+
const commands = k.command ? [
|
|
635
|
+
k.command
|
|
636
|
+
] : [];
|
|
637
|
+
await cdpKeyboard.down(k.key, {
|
|
638
|
+
commands
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
for (const k of [
|
|
642
|
+
...keys
|
|
643
|
+
].reverse())await cdpKeyboard.up(k.key);
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
this.forceSameTabNavigation = forceSameTabNavigation;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
export { ChromeExtensionProxyPage as default };
|
|
650
|
+
|
|
651
|
+
//# sourceMappingURL=page.mjs.map
|