Skip to content

Commit 965b21d

Browse files
authored
Merge pull request #1942 from AutomaApp/migrate-mv3
Migrate mv3
2 parents 619ae0e + a9eafaa commit 965b21d

16 files changed

+586
-128
lines changed

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,20 @@ yarn lint
7777
2. Enable the "Developer mode".
7878
3. Click "Load unpacked extension" button, browse the `automa/build` directory and select it.
7979

80-
![Install in chrome](https://res.cloudinary.com/chat-story/image/upload/v1665128418/automa/chrome_QWxClxFcio_d9pqp8.png)
80+
![Install in chrome](https://user-images.githubusercontent.com/22908993/166417152-f870bfbd-1770-4c28-b69d-a7303aebc9a6.png)
8181

8282
### Firefox
8383
1. Open firefox and navigate to `about:debugging#/runtime/this-firefox`.
8484
2. Click the "Load Temporary Add-on" button.
8585
3. Browse the `automa/build` directory and select the `manifest.json` file.
8686

87-
![Install in firefox](https://res.cloudinary.com/chat-story/image/upload/v1665128974/automa/firefox_30wkpfGM7N_lihajj.png)
87+
![Install in firefox](https://user-images.githubusercontent.com/22908993/166417727-3481fef4-00e5-4cf0-bb03-27fb880d993c.png)
8888

8989
## Contributors
9090
Thanks to everyone who has submitted issues, made suggestions, and generally helped make this a better project.
9191

92-
<a href="https://github.com/kholid060/automa/graphs/contributors">
93-
<img src="https://contrib.rocks/image?repo=kholid060/automa" />
92+
<a href="https://github.com/AutomaApp/automa/graphs/contributors">
93+
<img src="https://contrib.rocks/image?repo=AutomaApp/automa" />
9494
</a>
9595

9696
## License

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "automa",
3-
"version": "1.29.1",
3+
"version": "1.29.8",
44
"description": "An extension for automating your browser by connecting blocks",
55
"repository": {
66
"type": "git",

src/assets/images/icon-dev-128.png

6.74 KB
Loading

src/background/index.js

+176-37
Original file line numberDiff line numberDiff line change
@@ -223,14 +223,50 @@ message.on(
223223
return new Promise((resolve) => {
224224
const escapePolicy = (script) => {
225225
if (window?.trustedTypes?.createPolicy) {
226-
const escapeElPolicy = window.trustedTypes.createPolicy(
227-
'forceInner',
228-
{
229-
createHTML: (to_escape) => to_escape,
230-
createScript: (to_escape) => to_escape,
226+
try {
227+
// 尝试使用可能在CSP白名单中的名称
228+
const policyNames = [
229+
'default',
230+
'dompurify',
231+
'jSecure',
232+
'forceInner',
233+
];
234+
let escapeElPolicy = null;
235+
236+
// 尝试创建策略,如果一个名称失败,尝试下一个
237+
for (const policyName of policyNames) {
238+
try {
239+
escapeElPolicy = window.trustedTypes.createPolicy(
240+
policyName,
241+
{
242+
createHTML: (to_escape) => to_escape,
243+
createScript: (to_escape) => to_escape,
244+
}
245+
);
246+
// 如果成功创建,跳出循环
247+
break;
248+
} catch (e) {
249+
// 该名称失败,继续尝试下一个
250+
console.debug(
251+
`Policy name ${policyName} failed, trying next one`
252+
);
253+
}
231254
}
232-
);
233-
return escapeElPolicy.createScript(script);
255+
256+
// 如果成功创建了策略,使用它
257+
if (escapeElPolicy) {
258+
return escapeElPolicy.createScript(script);
259+
}
260+
// 如果所有策略名称都失败,返回原始脚本
261+
console.debug(
262+
'All trusted policy creation attempts failed, falling back to raw script'
263+
);
264+
return script;
265+
} catch (e) {
266+
// 捕获任何其他错误并降级
267+
console.debug('Error creating trusted policy:', e);
268+
return script;
269+
}
234270
}
235271
return script;
236272
};
@@ -267,10 +303,61 @@ message.on(
267303
chrome.debugger.attach({ tabId: target.tabId }, '1.3', resolve);
268304
});
269305

270-
// eslint-disable-next-line no-new-func
271-
const callbackFn = new Function(`return ${callback}`)();
306+
// 直接执行回调函数字符串
307+
// 避免使用new Function构造函数,因为它会被CSP阻止
308+
const callbackFn = callback;
309+
310+
// 检查回调函数字符串是否有效
311+
if (!callbackFn) {
312+
throw new Error('Callback function is missing or invalid');
313+
}
314+
315+
// 创建一个包装函数,直接执行回调函数
316+
const wrappedCallback = `
317+
(function() {
318+
return (${callbackFn})();
319+
})()
320+
`;
321+
322+
// 执行回调函数以获取JavaScript代码
323+
const jsCodeResult = await chrome.debugger.sendCommand(
324+
{ tabId: target.tabId },
325+
'Runtime.evaluate',
326+
{
327+
expression: wrappedCallback,
328+
userGesture: true,
329+
returnByValue: true,
330+
...options,
331+
}
332+
);
333+
334+
if (!jsCodeResult || !jsCodeResult.result) {
335+
console.error('无法获取JavaScript代码,结果为空');
336+
throw new Error('Unable to get JavaScript code');
337+
}
338+
339+
if (
340+
jsCodeResult.result.subtype === 'error' ||
341+
jsCodeResult.exceptionDetails
342+
) {
343+
console.error(
344+
'执行回调函数时出错:',
345+
jsCodeResult.result.description || jsCodeResult.exceptionDetails
346+
);
347+
throw new Error(
348+
jsCodeResult.result.description || 'Error executing callback'
349+
);
350+
}
351+
352+
// 确保我们获取到的是字符串类型的JavaScript代码
353+
if (typeof jsCodeResult.result.value !== 'string') {
354+
console.error('回调函数返回的不是JavaScript代码字符串');
355+
throw new Error('Callback did not return JavaScript code string');
356+
}
272357

273-
const jsCode = await callbackFn();
358+
const jsCode = jsCodeResult.result.value;
359+
360+
// 执行生成的JavaScript代码
274361
const execResult = await chrome.debugger.sendCommand(
275362
{ tabId: target.tabId },
276363
'Runtime.evaluate',
@@ -279,7 +366,7 @@ message.on(
279366
userGesture: true,
280367
awaitPromise: true,
281368
returnByValue: true,
282-
...(options || {}),
369+
...options,
283370
}
284371
);
285372

@@ -288,24 +375,33 @@ message.on(
288375
}
289376

290377
if (!execResult || !execResult.result) {
378+
console.error('无法执行代码,结果为空');
291379
throw new Error('Unable execute code');
292380
}
293381

294-
if (execResult.result.subtype === 'error') {
295-
throw new Error(execResult.result.description);
382+
if (
383+
execResult.result.subtype === 'error' ||
384+
execResult.exceptionDetails
385+
) {
386+
console.error(
387+
'执行JavaScript代码时出错:',
388+
execResult.result.description || execResult.exceptionDetails
389+
);
390+
throw new Error(
391+
execResult.result.description || 'Error executing JavaScript code'
392+
);
296393
}
297394

298395
return {
299396
isBlocked: true,
300397
value: execResult.result.value || null,
301398
};
302399
}
303-
// todo: 这里没有实现callback的调用逻辑
304400

305-
return { isBlocked: false, value: null };
306-
} catch (err) {
307-
console.error('CSP check error:', err);
308-
return { isBlocked: false, value: null };
401+
return { isBlocked: false };
402+
} catch (error) {
403+
console.error(error);
404+
return { isBlocked: false, error: error.message };
309405
}
310406
}
311407
);
@@ -352,7 +448,7 @@ function automaFetchClient(id, { type, resource }) {
352448
return;
353449
}
354450
355-
const eventName = '__automa-fetch-response-' + id +__;
451+
const eventName = \`__automa-fetch-response-\${id}__\`;
356452
const eventListener = ({ detail }) => {
357453
if (detail.id !== id) return;
358454
@@ -367,7 +463,7 @@ function automaFetchClient(id, { type, resource }) {
367463
368464
window.addEventListener(eventName, eventListener);
369465
window.dispatchEvent(
370-
new CustomEvent('__automa-fetch__', {
466+
new CustomEvent(\`__automa-fetch__\`, {
371467
detail: {
372468
id,
373469
type,
@@ -378,9 +474,8 @@ function automaFetchClient(id, { type, resource }) {
378474
});
379475
}
380476
381-
382477
function automaFetch(type, resource) {
383-
return automaFetchClient.toString()('${varName}', { type, resource });
478+
return automaFetchClient('${varName}', { type, resource });
384479
}
385480
`;
386481

@@ -548,21 +643,65 @@ message.on(
548643
);
549644

550645
message.on('script:execute-callback', async ({ target, callback }) => {
551-
await browser.scripting.executeScript({
552-
target,
553-
func: ($callbackFn) => {
554-
const script = document.createElement('script');
555-
script.textContent = `
556-
(() => {
557-
${$callbackFn}
558-
})()
559-
`;
560-
document.body.appendChild(script);
561-
},
562-
world: 'MAIN',
563-
args: [callback],
564-
});
565-
return true;
646+
try {
647+
// 首先尝试使用scripting API执行脚本
648+
const result = await browser.scripting.executeScript({
649+
target,
650+
func: ($callbackFn) => {
651+
try {
652+
const script = document.createElement('script');
653+
script.textContent = `
654+
(() => {
655+
${$callbackFn}
656+
})()
657+
`;
658+
document.body.appendChild(script);
659+
return { success: true };
660+
} catch (error) {
661+
console.error('执行脚本时出错:', error);
662+
return { success: false, error: error.message };
663+
}
664+
},
665+
world: 'MAIN',
666+
args: [callback],
667+
});
668+
669+
// 检查执行结果
670+
const executionResult = result[0]?.result;
671+
if (executionResult && executionResult.success) {
672+
return true;
673+
}
674+
675+
// 如果常规方法失败,尝试使用debugger API
676+
await new Promise((resolve) => {
677+
chrome.debugger.attach({ tabId: target.tabId }, '1.3', resolve);
678+
});
679+
680+
// 使用debugger API执行脚本
681+
const execResult = await chrome.debugger.sendCommand(
682+
{ tabId: target.tabId },
683+
'Runtime.evaluate',
684+
{
685+
expression: `(() => { ${callback} })()`,
686+
userGesture: true,
687+
awaitPromise: false,
688+
returnByValue: true,
689+
}
690+
);
691+
692+
// 执行完成后分离debugger
693+
await chrome.debugger.detach({ tabId: target.tabId });
694+
695+
if (!execResult || !execResult.result) {
696+
console.error('使用debugger API执行脚本失败');
697+
return false;
698+
}
699+
700+
return true;
701+
} catch (error) {
702+
console.error('执行script:execute-callback时出错:', error);
703+
return false;
704+
}
566705
});
567706

568707
automa('background', message);

src/content/blocksHandler/handlerUploadFile.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default async function (block) {
2929
fileObject = new File([arrayBuffer], filename, { type: mime });
3030
} else {
3131
const file = await sendMessage('get:file', path, 'background');
32-
const name = file.path?.replace(/^.*[\\/]/, '') || '';
32+
const name = file?.path?.replace(/^.*[\\/]/, '') || '';
3333
const blob = await fetch(file.objUrl).then((response) => response.blob());
3434

3535
if (file.objUrl.startsWith('blob')) URL.revokeObjectURL(file.objUrl);

src/content/elementSelector/App.vue

+16-16
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@
4545
</div>
4646
<div class="p-4">
4747
<selector-query
48-
v-model:selectorType="state.selectorType"
49-
v-model:selectList="state.selectList"
48+
v-model:selector-type="state.selectorType"
49+
v-model:select-list="state.selectList"
5050
:selector="state.elSelector"
5151
:settings-active="state.showSettings"
5252
:selected-count="state.selectedElements.length"
@@ -151,24 +151,24 @@
151151
/>
152152
</template>
153153
<script setup>
154+
import SelectorElementsDetail from '@/components/content/selector/SelectorElementsDetail.vue';
155+
import SelectorQuery from '@/components/content/selector/SelectorQuery.vue';
156+
import SharedElementSelector from '@/components/content/shared/SharedElementSelector.vue';
157+
import findSelector from '@/lib/findSelector';
158+
import FindElement from '@/utils/FindElement';
159+
import { debounce } from '@/utils/helper';
154160
import {
155-
reactive,
156-
ref,
157-
watch,
158161
inject,
159-
onMounted,
160162
onBeforeUnmount,
163+
onMounted,
164+
reactive,
165+
ref,
161166
toRaw,
167+
watch,
162168
} from 'vue';
163169
import browser from 'webextension-polyfill';
164-
import { debounce } from '@/utils/helper';
165-
import findSelector from '@/lib/findSelector';
166-
import FindElement from '@/utils/FindElement';
167-
import SelectorQuery from '@/components/content/selector/SelectorQuery.vue';
168-
import SharedElementSelector from '@/components/content/shared/SharedElementSelector.vue';
169-
import SelectorElementsDetail from '@/components/content/selector/SelectorElementsDetail.vue';
170-
import getSelectorOptions from './getSelectorOptions';
171170
import { getElementRect } from '../utils';
171+
import getSelectorOptions from './getSelectorOptions';
172172
173173
let connectedPort = null;
174174
const originalFontSize = document.documentElement.style.fontSize;
@@ -352,7 +352,7 @@ function clearConnectedPort() {
352352
connectedPort = null;
353353
state.isSelectBlockElement = false;
354354
}
355-
function onVisivilityChange() {
355+
function onVisibilityChange() {
356356
if (!connectedPort || document.visibilityState !== 'hidden') return;
357357
358358
clearConnectedPort();
@@ -370,15 +370,15 @@ function attachListeners() {
370370
window.addEventListener('message', onMessage);
371371
window.addEventListener('mouseup', onMouseup);
372372
window.addEventListener('mousemove', onMousemove);
373-
document.addEventListener('visibilitychange', onVisivilityChange);
373+
document.addEventListener('visibilitychange', onVisibilityChange);
374374
}
375375
function detachListeners() {
376376
cardElementObserver.disconnect();
377377
378378
window.removeEventListener('message', onMessage);
379379
window.removeEventListener('mouseup', onMouseup);
380380
window.removeEventListener('mousemove', onMousemove);
381-
document.removeEventListener('visibilitychange', onVisivilityChange);
381+
document.removeEventListener('visibilitychange', onVisibilityChange);
382382
}
383383
384384
watch(

0 commit comments

Comments
 (0)