Pasar datos o modificar la extensión html en una nueva pestaña/ventana
Estoy intentando agregar un DOM a un archivo de extensión de Chrome abierto desde popup.js
. Digamos que tengo un archivo que existe entre los archivos de extensión de Chrome llamado temp.html
. En algún momento durante la ejecución popup.js
, abro este archivo usandochrome.tabs.create
y luego quiero agregar un DOM a este archivo html.
¿ Hay alguna forma de hacerlo popup.js
?
Extension files:
1-manifest.json
2-functions
functions.js
domToTables.js
3-libs
jquery-3.3.1.min.js
bootstrap-4.2.1-dist
4-myTables
stylesheet.css
*temp.html* \\this file
5-popup
stylesheet.css
index.html
popup.js
6-background.js
7-content.js
Aunque puede acceder directamente al DOM de la página de su propia extensión abierta en una nueva pestaña/ventana usando chrome.extension.getViews (o incluso más simple si se usó window.open), pero es el enfoque de los tiempos en que la interfaz de usuario era primitiva, por lo que no funcionará si la página abierta utiliza un marco de presentación. Además, cuando se usa desde la ventana emergente, primero tendría que abrir la pestaña en segundo plano ( active:false
en los parámetros de chrome.tabs.create) ; de lo contrario, la ventana emergente se cerrará automáticamente, por lo que no se ejecutará más código, lo que desafortunadamente aún no es confiable porque otro La extensión puede forzar la activación de la pestaña.
El enfoque confiable/adecuado es pasar los datos a la otra página y dejar que maneje los datos dentro de su script que está cargado en esa página html a través del estándar <script src="other-page.js"></script>
.
1. MV2/MV3: HTML5 localStorage + acceso sincrónico
* MV3 solo admite localStorage
páginas de extensión (por ejemplo, ventanas emergentes de acción u opciones) y no en el script de fondo.
Úselo si necesita acceder a los datos durante la carga dentro de la otra página antes del primer marco pintado, por ejemplo, para elegir un tema claro/oscuro.
Desventaja: grandes cantidades de datos podrían ralentizar notablemente un dispositivo económico y tendrás que JSONificar los tipos que no son cadenas, como objetos o matrices.
popup.js:
localStorage.sharedData = JSON.stringify({foo: 123, bar: [1, 2, 3], theme: 'dark'});
chrome.tabs.create({url: 'other-page.html'});
otra-página.js:
let sharedData;
try {
sharedData = JSON.parse(localStorage.sharedData);
if (sharedData.theme === 'dark') {
document.documentElement.style = 'background: #000; color: #aaa;';
}
} catch (e) {}
delete localStorage.sharedData;
2. MV2/MV3: parámetros de URL + acceso sincrónico
Úselo si necesita acceder a los datos durante la carga dentro de la otra página antes del primer marco pintado, por ejemplo, para elegir un tema claro/oscuro.
Desventaja: una URL larga en la barra de direcciones y tendrás que JSON'ificar los tipos que no son cadenas, como objetos o matrices.
popup.js:
chrome.tabs.create({
url: 'other-page.html?data=' + encodeURIComponent(JSON.stringify({foo: [1, 2, 3]})),
});
otra-página.js:
let sharedData;
try {
sharedData = JSON.parse(new URLSearchParams(location.search).get('data'));
} catch (e) {}
// simplify the displayed URL in the address bar
history.replace({}, document.title, location.origin + location.pathname);
3. MV2: variable global del script en segundo plano + acceso sincrónico
Úselo si necesita acceder a los datos durante la carga dentro de la otra página antes del primer marco pintado, por ejemplo, para elegir un tema claro/oscuro.
Desventaja 1: la necesidad de una página de fondo.
Desventaja 2: la necesidad de clonar en profundidad los objetos mediante JSON.parse(JSON.stringify(data)) o un deepClone personalizado que funcione correctamente para contextos entre ventanas porque ninguna de las implementaciones populares de deepClone lo hace AFAIK: específicamente debería utilice la window
referencia del objetivo del constructor del objeto.
manifiesto.json:
"background": {
"scripts": ["bg.js"],
"persistent": false
}
popup.js:
// ensure the non-persistent background page is loaded
chrome.runtime.getBackgroundPage(bg => {
// using JSON'ification to avoid dead cross-window references.
bg.sharedData = JSON.stringify({foo: 123, bar: [1, 2, 3], theme: 'dark'});
chrome.tabs.create({url: 'other-page.html'});
});
otra-página.js:
// if this tab was reloaded the background page may be unloaded and the variable is lost
// but we were saving a copy in HTML5 sessionStorage!
let sharedData = sessionStorage.sharedData;
if (!sharedData) {
const bg = chrome.extension.getBackgroundPage();
sharedData = bg && bg.sharedData;
if (sharedData) {
sessionStorage.sharedData = sharedData;
}
}
// using JSON'ification to avoid dead cross-window references.
try {
sharedData = JSON.parse(sharedData);
} catch (e) {}
4. MV2/MV3: mensajes de retransmisión de secuencias de comandos en segundo plano en dos saltos
Úselo si necesita realizar una secuencia de acciones en la página de fondo de las cuales abrir la pestaña es solo el primer paso. Por ejemplo, necesitamos pasar los datos en el segundo paso.
La página de fondo es necesaria porque la ventana emergente se cerrará y sus scripts ya no se ejecutarán cuando se abra una pestaña activa en la misma ventana donde se muestra la ventana emergente. Se podría pensar que crear una pestaña active: false
resolvería el problema, pero solo hasta que un usuario decida instalar otra extensión que anule el comportamiento de apertura de pestañas. Se podría pensar que podría abrir una nueva ventana, pero nuevamente no hay garantía de que alguna otra extensión no vuelva a adjuntar la pestaña de la nueva ventana a la ventana existente, cerrando así la ventana emergente.
Desventaja 1: en Chrome los datos están JSON'ificados internamente, por lo que destruyen todos los tipos no estándar como WeakMap, TypedArray, Blob, etc. En Firefox parecen estar usando clonación estructurada para que se puedan compartir más tipos.
Desventaja 2: enviamos el mismo mensaje de datos dos veces.
Nota: estoy usando Polyfill WebExtension de Mozilla .
manifiesto.json:
"background": {
"scripts": [
"browser-polyfill.min.js",
"bg.js"
],
"persistent": false
}
popup.js:
chrome.runtime.sendMessage({
action: 'openTab',
url: '/other-page.html',
data: {foo: 123, bar: [1, 2, 3], theme: 'dark'},
});
bg.js:
function onTabLoaded(tabId) {
return new Promise(resolve => {
browser.tabs.onUpdated.addListener(function onUpdated(id, change) {
if (id === tabId && change.status === 'complete') {
browser.tabs.onUpdated.removeListener(onUpdated);
resolve();
}
});
});
}
browser.runtime.onMessage.addListener(async (msg = {}, sender) => {
if (msg.action === 'openTab') {
const tab = await browser.tabs.create({url: msg.url});
await onTabLoaded(tab.id);
await browser.tabs.sendMessage(tab.id, {
action: 'setData',
data: msg.data,
});
}
});
otra-página.html:
<!doctype html>
<p id="text"></p>
<!-- scripts at the end of the page run when DOM is ready -->
<script src="other-page.js"></script>
otra-página.js:
chrome.runtime.onMessage.addListener((msg, sender) => {
if (msg.action === 'setData') {
console.log(msg.data);
document.getElementById('text').textContent = JSON.stringify(msg.data, null, ' ');
// you can use msg.data only inside this callback
// and you can save it in a global variable to use in the code
// that's guaranteed to run at a later point in time
}
});
5. MV2/MV3: chrome.almacenamiento.local
Vea el ejemplo de chrome.storage.local en esta respuesta .