javascript 内容脚本和web_accessible_resources iframe之间的通信

9wbgstp7  于 2023-01-29  发布在  Java
关注(0)|答案(1)|浏览(109)

我有一个内容脚本,注入一个iframe到网页。
content.js

var iframe = document.createElement('iframe');
    iframe.id = "frame";
    iframe.style.cssText = "position:fixed;top: 15px;right: 15px;width: 250px;height: 245px;overflow: hidden;background-color:#FFFFFF;border-radius: 5px;";
    iframe.src = chrome.runtime.getURL('frame.html');
    document.body.appendChild(iframe);

iframe显示一些文本值,有一个提交和一个关闭按钮。
frame.html的一部分

<div class="header">
   <span class="close">Name</span>
   <span class="close-btn" id="close-btn">&times;</span>
</div>
<div class="details-container">
    <span class="label">First Name : </span> 
    <span id="fname" type="text" ></span>
</div>
<div class="details-container">
    <span class="label">Last Name : </span> 
    <span id="lname" type="text" /></span>
</div>
<div class="btn-details-container">
    <button class="copy" id="copy-name">Copy</button>
</div>

frame.html有frame.js链接到它。
我想做两件事。
1.用户单击iframe上的关闭按钮时关闭/删除/隐藏iframe(#close-btn)
1.动态设置span中名和姓的值(从当前网页的DOM中提取)
问题:
1)我不知道如何将frame.html上的click事件传播到内容脚本以关闭iframe(无法在frame.js和content.js之间建立通信)
2)无法为#fname和#lname设置span. textContent,因为frame.js无法读取网页DOM。

zphenhs4

zphenhs41#

扩展消息传递(iframe控制逻辑)

使用chrome. tabs. sendMessage与iframe的所有者选项卡通信,可以使用iframe内部的chrome. tabs. getCurrent检索所有者选项卡。
content.js:

var FRAME_URL = chrome.runtime.getURL('frame.html');
var iframe = document.createElement('iframe');
iframe.src = FRAME_URL;
document.body.appendChild(iframe);
    
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  switch (msg.cmd) {
    case 'close':
      iframe.remove();
      iframe = null;
      break;
    case 'getData':
      sendResponse([
        ['fname', document.querySelector('.web.page.selector.for.fname').textContent],
        ['lname', document.querySelector('.web.page.selector.for.lname').textContent],
      ]);
      break;
  }
});

iframe.js:

tellParent({cmd: 'getData'}, data => {
  for (const [id, val] of data) {
    document.getElementById(id).textContent = val;
  }
});

document.querySelector('.close-btn').onclick = () => {
  tellParent({cmd: 'close'});
};

function tellParent(msg, callback) {
  chrome.tabs.getCurrent(tab => {
    chrome.tabs.sendMessage(tab.id, msg, {frameId: 0}, callback);
  });
}

扩展消息(双向端口)

在iframe中使用chrome.tabs.connect初始化端口,然后在内容脚本中使用它。
内容脚本:

let framePort;
chrome.runtime.onConnect.addListener(port => {
  if (port.name === 'frame') {
    // global framePort can be used by code that will run in the future
    framePort = port;
    port.postMessage({foo: 'bar'});
  }
});

// add iframe element and point it to chrome.runtime.getURL('iframe.html')
//...........

iframe脚本:

chrome.tabs.getCurrent(tab => {
  const port = chrome.tabs.connect(tab.id, {name: 'frame', frameId: 0});
  port.onMessage.addListener(msg => {
    if (msg.foo === 'bar') {
      console.log(msg);
    }
  });
});

Web消息传递(双向MessagePort)

它的速度非常快,支持二进制数据类型,如Blob或ArrayBuffer,但需要一定的注意,以避免被网页拦截:
1.在封闭的ShadowDOM内创建iframe以避免暴露window[0]
1.不要设置iframe的src,而是使用url参数中的随机密钥导航其内部的location,这样其URL就不会被使用chrome.dom.openOrClosedShadowRoot的网页或其他扩展欺骗。
1.通过postMessage将安全的MessagePort传递到iframe
1.使用此安全MessagePort进行双向通信
//内容. js

(async () => {
  const port = await makeExtensionFramePort('/iframe.html');
  port.onmessage = e => {
    console.log('from iframe:', e.data);
  };
  port.postMessage(123);
  port.postMessage({ foo: bar });
  port.postMessage(new Blob(['foo']));
})();

async function makeExtensionFramePort(path) {
  const secret = Math.random().toString(36);
  const url = new URL(chrome.runtime.getURL(path));
  url.searchParams.set('secret', secret);
  const el = document.createElement('div');
  const root = el.attachShadow({mode: 'closed'});
  const iframe = document.createElement('iframe');
  iframe.hidden = true;
  root.appendChild(iframe);
  (document.body || document.documentElement).appendChild(el);
  await new Promise((resolve, reject) => {
    iframe.onload = resolve;
    iframe.onerror = reject;
    iframe.contentWindow.location.href = url;
  });
  const mc = new MessageChannel();
  iframe.contentWindow.postMessage(1, '*', [mc.port2]);
  return mc.port1; 
}

//文件名:

<script src="iframe.js"></script>

//文件系统

let port;
window.onmessage = e => {
  if (e.data === new URLSearchParams(location.search).get('secret')) {
    window.onmessage = null;
    port = e.ports[0];
    port.onmessage = onContentMessage;
  }
};

function onContentMessage(e) {
  console.log('from content:', e.data);
  port.postMessage('ok');
}
      • 修改:通过使用iframe中的navigator.serviceWorker消息传递,在内容脚本和扩展的服务工作器**之间建立直接双向端口:

//文件系统

let port;
window.onmessage = e => {
  if (e.data === new URLSearchParams(location.search).get('secret')) {
    window.onmessage = null;
    navigator.serviceWorker.ready.then(swr => {
      swr.active.postMessage('port', [e.ports[0]]);
    });
  }
};

//背景. js

self.onmessage = e => {
  if (e.data === 'port') {
    e.ports[0].onmessage = onContentMessage;
  }
}
function onContentMessage(e) {
  // prints both in the background console and in the iframe's console
  console.log('from content:', e.data);
  port.postMessage('ok');
}

相关问题