在新选项卡/窗口中传递数据或修改扩展 html [英] Pass data or modify extension html in a new tab/window

查看:25
本文介绍了在新选项卡/窗口中传递数据或修改扩展 html的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试将 DOM 附加到从 popup.js 打开的自 chrome 扩展文件.假设我有一个名为 temp.html 的 chrome 扩展文件中存在的文件,在执行 popup.js 时,我使用 chrome 打开这个文件.tabs.create 然后我想在这个 html 文件中附加一个 DOM.

无论如何我可以从 popup.js 这样做吗?

扩展文件:1-manifest.json2-功能函数.jsdomToTables.js3-libsjquery-3.3.1.min.jsbootstrap-4.2.1-dist4-myTables样式表.css*temp.html* \这个文件5-弹出样式表.css索引.html弹出窗口.js6-background.js7-content.js

解决方案

尽管您可以使用 chrome.extension.getViews 直接访问在新选项卡/窗口中打开的您自己的扩展程序页面的 DOM(如果 window.open 甚至更简单)被使用),但这是 UI 原始时代的方法,因此如果您打开的页面使用演示框架,它将无法工作.此外,当从弹出窗口使用时,您必须先在后台打开标签(chrome.tabs.create 的参数中的active:false),否则弹出窗口将自动关闭,因此不会运行更多代码,不幸的是,这仍然不可靠,因为另一个扩展程序可能会强制激活选项卡.

可靠/正确的方法是将数据传递到另一个页面,并让它通过标准 <script src="other-page.js" 处理加载在该页面 html 中的脚本中的数据;></script>.

1.MV2:HTML5 localStorage + 同步访问

如果您需要在第一个绘制的框架之前在另一个页面内加载期间访问数据,请使用选择浅色/深色主题.

缺点:大量数据可能会显着降低预算设备的速度,并且您必须对非字符串类型(例如对象或数组)进行 JSON 化.

popup.js:

localStorage.sharedData = JSON.stringify({foo: 123, bar: [1, 2, 3], theme: 'dark'});chrome.tabs.create({url: 'other-page.html'});

other-page.js:

let sharedData;尝试 {sharedData = JSON.parse(localStorage.sharedData);if (sharedData.theme === 'dark') {document.documentElement.style = '背景:#000;颜色:#aaa;';}} 赶上 (e) {}删除 localStorage.sharedData;


2.MV2/MV3:URL参数+同步访问

如果您需要在第一个绘制的框架之前在另一个页面内加载期间访问数据,请使用选择浅色/深色主题.

缺点:地址栏中的 url 很长,您必须对非字符串类型(例如对象或数组)进行 JSON 化.

popup.js:

chrome.tabs.create({url: 'other-page.html?data=' + encodeURIComponent(JSON.stringify({foo: [1, 2, 3]})),});

other-page.js:

let sharedData;尝试 {sharedData = JSON.parse(new URLSearchParams(location.search).get('data'));} 赶上 (e) {}//简化地址栏中显示的 URLhistory.replace({}, document.title, location.origin + location.pathname);


3.MV2:后台脚本的全局变量+同步访问

如果您需要在第一个绘制的框架之前在另一个页面内加载期间访问数据,请使用选择浅色/深色主题.

缺点一:需要后台页面.

缺点 2:需要通过使用 JSON.parse(JSON.stringify(data)) 或自定义 deepClone 对对象进行深度克隆,该深度克隆适用于跨窗口上下文,因为没有一个流行的 deepClone 实现这样做 AFAIK:具体来说,它应该使用目标 window 对对象构造函数的引用.

manifest.json:

背景":{脚本":[bg.js"],持久":假}

popup.js:

//确保非持久化后台页面被加载chrome.runtime.getBackgroundPage(bg => {//使用 JSON'ification 来避免死的跨窗口引用.bg.sharedData = JSON.stringify({foo: 123, bar: [1, 2, 3], theme: 'dark'});chrome.tabs.create({url: 'other-page.html'});});

other-page.js:

//如果这个选项卡被重新加载,后台页面可能会被卸载并且变量丢失//但我们在 HTML5 sessionStorage 中保存了一个副本!让 sharedData = sessionStorage.sharedData;如果(!共享数据){const bg = chrome.extension.getBackgroundPage();sharedData = bg &&bg.sharedData;如果(共享数据){sessionStorage.sharedData = sharedData;}}//使用 JSON'ification 来避免死的跨窗口引用.尝试 {sharedData = JSON.parse(sharedData);} 赶上 (e) {}


4.MV2/MV3:后台脚本在两跳中中继消息

如果您需要在后台页面中执行一系列操作,其中打开选项卡只是第一步,请使用.比如我们需要在第二步传递数据.

需要后台页面,因为当活动选项卡在显示弹出窗口的同一窗口中打开时,弹出窗口将关闭并且其脚本将不再运行.有人可能认为创建一个带有 active: false 的选项卡可以解决问题,但前提是用户决定安装另一个覆盖选项卡打开行为的扩展.您可能会认为您可以打开一个新窗口,但同样不能保证其他扩展程序不会将新窗口的选项卡重新附加到现有窗口中,从而关闭您的弹出窗口.

缺点 1:在 Chrome 中,数据在内部是 JSON 化的,因此它会破坏所有非标准类型,例如 WeakMap、TypedArray、Blob 等.在 Firefox 中,它们似乎使用结构化克隆,因此可以共享更多类型.

缺点 2:我们发送了两次相同的数据消息.

注意:我使用的是 Mozilla 的 WebExtension polyfill.

manifest.json:

背景":{脚本":[浏览器-polyfill.min.js",bg.js"],持久":假}

popup.js:

chrome.runtime.sendMessage({动作:'openTab',url: '/other-page.html',数据: {foo: 123, bar: [1, 2, 3], 主题: 'dark'},});

bg.js:

function onTabLoaded(tabId) {返回新的承诺(解决 => {browser.tabs.onUpdated.addListener(function onUpdated(id, change) {if (id === tabId && change.status === '完成') {browser.tabs.onUpdated.removeListener(onUpdated);解决();}});});}browser.runtime.onMessage.addListener(async (msg = {}, sender) => {if (msg.action === 'openTab') {const tab = await browser.tabs.create({url: msg.url});等待 onTabLoaded(tab.id);等待 browser.tabs.sendMessage(tab.id, {动作:'setData',数据:msg.data,});}});

other-page.html:

<p id=文本"></p><!-- 页面末尾的脚本在 DOM 准备好时运行 --><script src="other-page.js"></script>

other-page.js:

chrome.runtime.onMessage.addListener((msg, sender) => {if (msg.action === 'setData') {控制台日志(味精数据);document.getElementById('text').textContent = JSON.stringify(msg.data, null, ' ');//你只能在这个回调中使用 msg.data//并且您可以将其保存在全局变量中以在代码中使用//保证在稍后的时间点运行}});


5.MV2/MV3:chrome.storage.local

请参阅此答案中的 chrome.storage.local 示例.

I'm trying to append a DOM to a self chrome extension file opened from popup.js. Let's say I have a file that exists among chrome extension files called temp.html, At some point while executing popup.js, I open this file using chrome.tabs.create then I want to append a DOM to this html file.

Is there anyway I could do so from 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

解决方案

Although you can directly access DOM of your own extension's page opened in a new tab/window by using chrome.extension.getViews (or even simpler if window.open was used), but it's the approach from the times when UI was primitive so it won't work if your opened page uses a presentation framework. Moreover, when used from the popup you would have to open the tab in background first (active:false in the parameters of chrome.tabs.create), otherwise the popup will auto-close so no further code would run, which is unfortunately still unreliable because another extension may force-activate the tab.

The reliable/proper approach is to pass the data to the other page and let it handle the data inside its script that's loaded in that page html via the standard <script src="other-page.js"></script>.

1. MV2: HTML5 localStorage + synchronous access

Use if you need to access the data during loading inside the other page before the first painted frame e.g. to choose a light/dark theme.

Disadvantage: big amounts of data could noticeably slow down a budget device and you'll have to JSON'ify the non-string types such as objects or arrays.

popup.js:

localStorage.sharedData = JSON.stringify({foo: 123, bar: [1, 2, 3], theme: 'dark'});
chrome.tabs.create({url: 'other-page.html'});

other-page.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: URL parameters + synchronous access

Use if you need to access the data during loading inside the other page before the first painted frame e.g. to choose a light/dark theme.

Disadvantage: a long url in the address bar and you'll have to JSON'ify the non-string types such as objects or arrays.

popup.js:

chrome.tabs.create({
  url: 'other-page.html?data=' + encodeURIComponent(JSON.stringify({foo: [1, 2, 3]})),
});

other-page.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: Background script's global variable + synchronous access

Use if you need to access the data during loading inside the other page before the first painted frame e.g. to choose a light/dark theme.

Disadvantage 1: the need for a background page.

Disadvantage 2: the need to deep-clone the objects by using JSON.parse(JSON.stringify(data)) or a custom deepClone that works properly for cross-window contexts because none of the popular deepClone implementations do it AFAIK: specifically it should use the target window's reference of the object constructor.

manifest.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'});
});

other-page.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: Background script relay messaging in two hops

Use if you need to perform a sequence of actions in the background page of which opening the tab is just the first step. For example we need to pass the data in the second step.

The background page is needed because the popup would be closed and its scripts won't run anymore when an active tab opens in the same window where the popup is displayed. One might think that creating a tab with active: false would solve the problem but only until a user decides to install another extension that overrides tab opening behavior. You would think you could open a new window but again there's no guarantee some other extension doesn't re-attach the new window's tab into the existing window thus closing your popup.

Disadvantage 1: in Chrome the data is JSON'ified internally so it nukes all the nonstandard types such as WeakMap, TypedArray, Blob, etc. In Firefox they seem to be using structured cloning so more types can be shared.

Disadvantage 2: we're sending the same data message twice.

Note: I'm using Mozilla's WebExtension polyfill.

manifest.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,
    });
  }
});

other-page.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>

other-page.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.storage.local

See the example for chrome.storage.local in this answer.

这篇关于在新选项卡/窗口中传递数据或修改扩展 html的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆