跳至主要内容

[Chrome Extension] 在 contentScript 中注入 iframe

在 Chrome Extension 中,為了避免樣式或其他原因造成的干擾,很常使用的一種技巧是透過 content script 在網頁中注入 iframe,而 iframe 的 src 則帶入 chrome extension 的 html 檔。

Imgur

Message API Sandbox @ PJCHENder Github

實作步驟

透過下面的步驟就可以在網頁中載入 iframe

建立 contentScript

這支 contentScript 是用來在網頁上執行,而目的是要注入自己 extension 的 iframe,在 <iframe src="" /> 的地方,src 要帶入的是 extension 內的 HTML 檔,這個檔案的路徑可以透過 chrome.runtime.getURL(<檔案名稱>) 取得。

  1. 建立一個 div 元素,並將它 append 在網頁內
  2. 建立一個 iframe 元素,src 的地方透過 chrome.runtime.getURL(<檔案名稱>) 載入 extension 內部的檔案
  3. iframe 元素透過 prepend 方法掛載到 root 元素內
// contentScript.js
console.log('Here is contentScript');

createIframe();

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log('Get response in contentScript, the request is from: ', request.from);
});

// 透過下面的程式碼可以注入 iframe
function createIframe() {
// STEP 1:建立一個 `div`,並將它 `append` 在網頁內
const root = document.createElement('div');
root.id = 'root';
document.body.appendChild(root);

// STEP 2:建立一個 `iframe` 元素,`src` 的地方透過
// `chrome.runtime.getURL(<檔案名稱>)` 載入 extension 內部的檔案
const iframe = document.createElement('iframe');
iframe.id = 'iframe-in-root';
iframe.allow = 'microphone;camera;';
iframe.sandbox = 'allow-scripts allow-same-origin allow-forms';
iframe.setAttribute('allowFullScreen', '');
iframe.scrolling = 'no';
iframe.style.cssText = `
width: 50%;
height: 50%;
border: 0;
margin: 0;
z-index: 2147483647;
background-color: #EAEAEA;
border: 1px solid #EAEAEA;
filter: none;
display: block;
`;

// ⚠️ 重點:src 要帶入的連結是 chrome extension 內部的網址
iframe.src = chrome.runtime.getURL('index.html');

root.style.cssText = `
position: fixed;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
z-index: 2147483647;
`;

// STEP 3:將 iframe 元素掛載到 root 元素內
root.prepend(iframe);
}

建立 iframe 的內容

index.html

這是 iframe 中在載入的 html 檔案:

<!-- Copyright 2017 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file. -->
<html>
<head>
<title>Water Popup</title>
<style>
body {
text-align: center;
}

#hydrateImage {
width: 100px;
margin: 5px;
}

button {
margin: 5px;
outline: none;
}

button:hover {
outline: #80deea dotted thick;
}
</style>
</head>
<body>
<img src="./stay_hydrated.png" id="hydrateImage" />
<button id="send-message">SendMessage</button>
<button id="send-tabs-message">SendTabsMessage</button>

<!-- ⚠️ 重點:若有需要使用 JS 可在此載入 -->
<script src="index.js"></script>
</body>
</html>

index.js

iframe 中 HTML 的檔案會在載入 index.js 這支檔案:

'use strict';
console.log('Here is iframe');

function sendMessage() {
chrome.runtime.sendMessage({ from: 'iframe' });
}

function sendTabsMessage() {
chrome.tabs.query({ active: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { from: 'iframe' });
});
}

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
console.log('Get response in iframe, the request is from: ', request.from);
});

document.getElementById('send-message').addEventListener('click', sendMessage);
document.getElementById('send-tabs-message').addEventListener('click', sendTabsMessage);

設定 manifest.json

manifest.json 中有兩幾主要的地方需要設定:

  1. 透過 content_script 要執行 contentScript.js 的情況
  2. 因為在網頁上的 iframe 需要載入 chrome extension 寫好的 HTML 檔案,因此需要設定 web_accessible_resources
{
// ...
"permissions": ["tabs", "activeTab"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_title": "Drink Water Event",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["contentScript.js"]
}
],
"web_accessible_resources": ["index.html"]
}

重要提醒

當我們透過 iframe 載入 extension 內的檔案後,這個 iframe 的內容其實就是歸屬於 extension,因此真正要區分是 content script 和 chrome extension ,最準確的是看執行該程式碼的位置是在別人頁面內(Content Script)還是 Chrome Extension 的檔案內(檔案是以 chrome-extension:// 開頭的都算是包含在 Chrome Extension 內)。

之所以要區分是屬於 content script 或 chrome extension 的原因在於 Message API 傳送訊息的方式會有所不同。

Imgur

範例程式碼

Message API Sandbox @ PJCHENder Github