[Chrome Extension] 在 contentScript 中注入 iframe
在 Chrome Extension 中,為了避免樣式或其他原因造成的干擾,很常使用的一種技巧是透過 content script 在網頁中注入 iframe,而 iframe 的 src
則帶入 chrome extension 的 html
檔。
Message API Sandbox @ PJCHENder Github
實作步驟
透過下面的步驟就可以在網頁中載入 iframe
建立 contentScript
這支 contentScript 是用來在網頁上執行,而目的是要注入自己 extension 的 iframe,在 <iframe src="" />
的地方,src 要帶入的是 extension 內的 HTML 檔,這個檔案的路徑可以透過 chrome.runtime.getURL(<檔案名稱>)
取得。
- 建立一個
div
元素,並將它append
在網頁內 - 建立一個
iframe
元素,src
的地方透過chrome.runtime.getURL(<檔案名稱>)
載入 extension 內部的檔案 - 將
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
中有兩幾主要的地方需要設定:
- 透過
content_script
要執行contentScript.js
的情況 - 因為在網頁上的 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 傳送訊息的方式會有所不同。
範例程式碼
Message API Sandbox @ PJCHENder Github