-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbackground.js
More file actions
113 lines (101 loc) · 4.52 KB
/
background.js
File metadata and controls
113 lines (101 loc) · 4.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// background.js
import { DOMParser as LinkeDOMParser } from './sources/vendor/linkedom.js';
import { runTranslatorOnHtml } from './sources/translatorRunner.js';
// Shim DOMParser for environments without it in the background
if (typeof globalThis.DOMParser === 'undefined') {
globalThis.DOMParser = LinkeDOMParser;
}
if (typeof globalThis.window === 'undefined') {
globalThis.window = globalThis;
}
if (typeof globalThis.window.DOMParser === 'undefined') {
globalThis.window.DOMParser = LinkeDOMParser;
}
console.debug('[background] module loaded');
// Provide a minimal compatibility shim: if `browser` is missing, alias it to `chrome`.
if (typeof browser === "undefined" && typeof chrome !== "undefined") {
globalThis.browser = chrome;
}
browser.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg && msg.type === 'getManifest') {
(async () => {
try {
const url = browser.runtime.getURL('translators/manifest.json');
const resp = await fetch(url);
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const manifest = await resp.json();
sendResponse({ ok: true, manifest });
} catch (e) {
sendResponse({ ok: false, error: String(e) });
}
})();
return true;
}
if (!msg || msg.type !== 'runTranslator') return;
const { translatorPath, translators, url } = msg;
// If offscreen is available (Chrome), forward the request so the offscreen
// document runs the translator. If not (Firefox/Safari), run the translator
// directly from the background page which has a DOM (or shimmed DOM).
if (browser.offscreen) {
// In Chrome, we don't need to do anything here because the content script
// or popup should have opened the offscreen document and sent the message
// there. However, if it was sent to background, we just acknowledge.
sendResponse({ ok: true });
return;
}
// Firefox / Safari / no offscreen: fetch page HTML and run translator here.
(async () => {
try {
const resp = await fetch(url, { credentials: 'omit' });
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const html = await resp.text();
// Normalize translators list: accept either `translators` array or single `translatorPath`.
const list = Array.isArray(translators) && translators.length ? translators : (translatorPath ? [translatorPath] : []);
if (!list.length) throw new Error('No translator paths provided');
// Run all translator attempts in parallel and resolve with the first
// successful (non-null/defined) result. We don't attempt to cancel other
// promises; they will continue running in the background.
const attempts = list.map((t) => (async () => {
try {
// In background.js we need to import the translator module because
// we are running in the extension context.
const translatorUrl = browser.runtime.getURL(t);
const mod = await import(translatorUrl);
const result = await runTranslatorOnHtml(mod, html, url);
if (result !== null && typeof result !== 'undefined') return { result, translator: t };
throw new Error('No result');
} catch (e) {
// Wrap error with translator id for diagnostics
throw { err: e, translator: t };
}
})());
// Custom Promise.any fallback to collect first fulfilled promise
const firstFulfilled = (proms) => new Promise((resolve, reject) => {
let pending = proms.length;
const errors = [];
proms.forEach(p => {
p.then(resolve).catch(e => {
errors.push(e);
pending -= 1;
if (pending === 0) reject(errors);
});
});
});
try {
const { result, translator: successful } = await firstFulfilled(attempts);
await browser.runtime.sendMessage({ type: 'offscreenResult', url, result, translator: successful });
sendResponse({ ok: true });
} catch (errors) {
// All attempts failed
const last = Array.isArray(errors) && errors.length ? errors[errors.length - 1] : errors;
const msg = last && last.err ? String(last.err) : String(last || 'All translators failed');
await browser.runtime.sendMessage({ type: 'offscreenResult', url, error: msg });
sendResponse({ ok: false, error: msg });
}
} catch (e) {
await browser.runtime.sendMessage({ type: 'offscreenResult', url, error: String(e) });
sendResponse({ ok: false, error: String(e) });
}
})();
return true;
});