<iframe>
元素通常用於在瀏覽環境中嵌入外部資源。iframe 會將跨來源的內嵌內容與主機網頁隔離,反之亦然,藉此強制執行網路安全政策。雖然這種做法可確保來源之間的安全界線,進而提升安全性,但會限制部分用途。舉例來說,使用者可能需要從不同來源動態載入及管理內容,例如老師觸發導覽事件,在教室螢幕上顯示網頁。不過,許多網站會使用 X-Frame-Options 和內容安全政策 (CSP) 等安全性標頭,明確禁止在 iframe 中嵌入內容。此外,iframe 限制會禁止內嵌網頁直接管理內嵌內容的導覽或行為。
Controlled Frame API 可載入任何網路內容,即使內容強制執行限制性嵌入政策,也能解決這項限制。這項 API 僅適用於獨立網頁應用程式 (IWA),其中包含額外的安全措施,可保護使用者和開發人員免於潛在風險。
實作受控頁框
使用受控影格前,請先設定可正常運作的 IWA。接著,您就可以將受控影格整合至網頁。
新增權限政策
如要使用受控頁框,請在 IWA 資訊清單中新增 permissions_policy
欄位,並將值設為 "controlled-frame"
,啟用對應的權限。此外,還包括 cross-origin-isolated 鍵。這個金鑰並非 Controlled Frames 專用,但所有 IWA 都必須使用,且會決定文件是否可存取需要跨來源隔離的 API。
{
...
"permissions_policy": {
...
"controlled-frame": ["self"],
"cross-origin-isolated": ["self"]
...
}
...
}
隔離網頁應用程式 (IWA) 資訊清單中的 controlled-frame
鍵會定義權限政策允許清單,指定哪些來源可以使用受控頁框。雖然資訊清單支援完整的權限政策語法,允許 *
等值、特定來源或 self
和 src
等關鍵字,但請務必注意,IWA 專屬 API 無法委派給其他來源。即使許可清單包含萬用字元或外部來源,這些權限也不會對 controlled-frame
等 IWA 功能生效。與標準網頁應用程式不同,IWA 預設會將所有受政策控管的功能設為「無」,因此需要明確聲明。如果是 IWA 專屬功能,這表示只有 self
(IWA 自己的來源) 或 src
(內嵌框架的來源) 等值才有效。
新增受控頁框元素
在 HTML 中插入 <controlledframe>
元素,將第三方內容嵌入 IWA。
<controlledframe id="controlledframe_1" src="https://example.com">
</controlledframe>
選用的 partition
屬性會設定內嵌內容的儲存空間分割區,讓您隔離 Cookie 和本機儲存空間等資料,以便在工作階段之間保留資料。
範例:記憶體內儲存空間分割區
使用名為 "session1"
的記憶體內儲存空間分割區建立受控影格。當影格遭到刪除或應用程式工作階段結束時,系統會清除這個分割區中儲存的資料 (例如 Cookie 和 localStorage)。
<controlledframe id="controlledframe_1" src="https://example.com">
</controlledframe>
範例:永久儲存空間分割區
使用名為 "user_data"
的永久儲存空間分割區建立受控影格。"persist:"
前置字串可確保儲存在這個分割區的資料會儲存到磁碟,並可在應用程式工作階段中使用。
<controlledframe id="frame_2" src="..." partition="persist:user_data">
</controlledframe>
取得元素參照
取得 <controlledframe>
元素的參照,以便像任何標準 HTML 元素一樣與其互動:
const controlledframe = document.getElementById('controlledframe_1');
常見情境和用途
一般來說,請選擇最符合需求的技術,同時避免不必要的複雜性。近年來,漸進式網頁應用程式 (PWA) 已縮小與原生應用程式的差距,可提供強大的網頁體驗。如果網頁應用程式需要嵌入第三方內容,建議您先探索一般 <iframe>
方法。如果需求超出 iframe 的功能範圍,IWA 的受控影格可能是最佳替代方案。以下各節將說明常見用途。
嵌入第三方網頁內容
許多應用程式都需要在使用者介面中載入及顯示第三方內容。不過,如果涉及多位網頁應用程式擁有者 (內嵌應用程式的常見情況),就很難建立一致的端對端政策。舉例來說,即使商家有正當需求,安全設定仍可防止傳統 <iframe>
嵌入特定類型的內容。與 <iframe>
元素不同,受控影格的設計目的是略過這些限制,讓應用程式載入及顯示內容,即使內容明確禁止標準嵌入也沒問題。
用途
- 課堂簡報:老師使用課堂觸控螢幕切換教育資源,這些資源通常會封鎖 iframe 嵌入。
- 零售店或購物中心的數位看板:購物中心資訊站會輪播不同商店的網站。即使這些網頁限制嵌入,受控影格也能確保網頁正確載入。
程式碼範例
下列 Controlled Frame API 有助於管理嵌入的內容。
導覽:受控影格提供多種方法,以程式輔助方式管理及控制內嵌內容的導覽和導覽記錄。
src
屬性會取得或設定影格中顯示的內容網址,運作方式與 HTML 屬性相同。
controlledframe.src = "https://example.com";
back()
方法會在影格的記錄中返回上一步。傳回的 Promise 會解析為布林值,指出導覽是否成功。
document.getElementById('backBtn').addEventListener('click', () => {
controlledframe.back().then((success) => {
console.log(`Back navigation ${success ? 'succeeded' : 'failed'}`); }).catch((error) => {
console.error('Error during back navigation:', error);
});
});
forward()
方法會在影格的記錄中向前導覽一步。傳回的 Promise 會解析為布林值,指出導覽是否成功。
document.getElementById('forwardBtn').addEventListener('click', () => {
controlledframe.forward().then((success) => {
console.log(`Forward navigation ${success ? 'succeeded' : 'failed'}`);
}).catch((error) => {
console.error('Error during forward navigation:', error);
});
});
reload()
方法會重新載入框架中的目前頁面。
document.getElementById('reloadBtn').addEventListener('click', () => {
controlledframe.reload();
});
此外,受控影格還會提供事件,讓您追蹤導覽要求的完整生命週期,包括啟動、重新導向、載入內容、完成或中止。
loadstart
:在影格中開始導覽時觸發。loadcommit
:導覽要求處理完畢,且主要文件內容開始載入時觸發。contentload
:在主要文件及其必要資源載入完成時觸發 (類似於 DOMContentLoaded)。loadstop
:網頁的所有資源 (包括子框架、圖片) 載入完畢時觸發。loadabort
:如果導覽作業遭到中止 (例如使用者採取動作或開始其他導覽作業),就會觸發這個事件。loadredirect
:在導覽期間發生伺服器端重新導向時觸發。
controlledframe.addEventListener('loadstart', (event) => {
console.log('Navigation started:', event.url);
// Example: Show loading indicator
});
controlledframe.addEventListener('loadcommit', (event) => {
console.log('Navigation committed:', event.url);
});
controlledframe.addEventListener('contentload', (event) => {
console.log('Content loaded for:', controlledframe.src);
// Example: Hide loading indicator, maybe run initial script
});
controlledframe.addEventListener('loadstop', (event) => {
console.log('All resources loaded for:', controlledframe.src);
});
controlledframe.addEventListener('loadabort', (event) => {
console.warn(`Navigation aborted: ${event.url}, Reason: ${event.detail.reason}`);
});
controlledframe.addEventListener('loadredirect', (event) => {
console.log(`Redirect detected: ${event.oldUrl} -> ${event.newUrl}`);
});
您也可以監控並攔截受控影格中載入的內容所發起的特定互動或要求,例如嘗試開啟對話方塊、要求權限或開啟新視窗。
dialog
:當內嵌內容嘗試開啟對話方塊 (快訊、確認、提示) 時觸發。你收到詳細資料並可回覆。consolemessage
:在影格內將訊息記錄到控制台時觸發。permissionrequest
:在嵌入的內容要求權限時觸發 (例如地理位置和通知)。您會收到詳細資料,並可允許或拒絕要求。newwindow
:當嵌入內容嘗試開啟新視窗或分頁時 (例如使用 window.open 或含有target="_blank"
的連結),系統會觸發此事件。您會收到詳細資料,並可處理或封鎖該動作。
controlledframe.addEventListener('dialog', (event) => {
console.log(Dialog opened: Type=${event.messageType}, Message=${event.messageText});
// You will need to respond, e.g., event.dialog.ok() or .cancel()
});
controlledframe.addEventListener('consolemessage', (event) => {
console.log(Frame Console [${event.level}]: ${event.message});
});
controlledframe.addEventListener('permissionrequest', (event) => {
console.log(Permission requested: Type=${event.permission});
// You must respond, e.g., event.request.allow() or .deny()
console.warn('Permission request needs handling - Denying by default');
if (event.request && event.request.deny) {
event.request.deny();
}
});
controlledframe.addEventListener('newwindow', (event) => {
console.log(New window requested: URL=${event.targetUrl}, Name=${event.name});
// Decide how to handle this, e.g., open in a new controlled frame and call event.window.attach(), ignore, or block
console.warn('New window request needs handling - Blocking by default');
});
此外,狀態變更事件也會通知您受控影格本身算繪狀態的相關變更,例如維度或縮放層級的修改。
sizechanged
:在影格內容的尺寸變更時觸發。zoomchange
:在影格內容的縮放比例變更時觸發。
controlledframe.addEventListener('sizechanged', (event) => {
console.log(Frame size changed: Width=${event.width}, Height=${event.height});
});
controlledframe.addEventListener('zoomchange', (event) => {
console.log(Frame zoom changed: Factor=${event.newZoomFactor});
});
儲存方法:受控影格提供 API,可管理影格分割區內儲存的資料。
使用 clearData()
移除所有儲存的資料,這在使用者工作階段後重設影格或確保狀態乾淨時特別有用。方法會傳回 Promise,並在作業完成時解析。您也可以提供選用的設定選項:
types
:字串陣列,指定要清除的資料類型 (例如['cookies', 'localStorage', 'indexedDB']
)。如果省略,通常會清除所有適用的資料類型。options
:控制清除程序,例如使用 since 屬性 (以紀元後毫秒為單位的時間戳記) 指定時間範圍,只清除該時間之後建立的資料。
範例:清除與受控影格相關聯的所有儲存空間
function clearAllPartitionData() {
console.log('Clearing all data for partition:', controlledframe.partition);
controlledframe.clearData()
.then(() => {
console.log('Partition data cleared successfully.');
})
.catch((error) => {
console.error('Error clearing partition data:', error);
});
}
範例:僅清除過去一小時內建立的 Cookie 和 localStorage
function clearRecentCookiesAndStorage() {
const oneHourAgo = Date.now() - (60 * 60 * 1000);
const dataTypesArray = ['cookies', 'localStorage'];
const dataTypesToClearObject = {};
for (const type of dataTypesArray) {
dataTypesToClearObject[type] = true;
}
const clearOptions = { since: oneHourAgo };
console.log(`Clearing ${dataTypesArray.join(', ')} since ${new Date(oneHourAgo).toISOString()}`); controlledframe.clearData(clearOptions, dataTypesToClearObject) .then(() => {
console.log('Specified partition data cleared successfully.');
}).catch((error) => {
console.error('Error clearing specified partition data:', error);
});
}
擴充或變更第三方應用程式
除了簡單的嵌入功能,受控框架還提供機制,讓嵌入的 IWA 能控管嵌入的第三方網路內容。您可以在內嵌內容中執行指令碼、攔截網路要求,以及覆寫預設內容選單,所有操作都會在安全的隔離環境中進行。
用途
- 在第三方網站上強制執行品牌宣傳:將自訂 CSS 和 JavaScript 插入嵌入的網站,套用統一的視覺主題。
- 限制導覽和連結行為:透過指令碼插入功能,攔截或停用特定
<a>
代碼行為。 - 在異常終止或閒置後自動復原:監控內嵌內容的失敗狀態 (例如空白畫面、指令碼錯誤),並在逾時後以程式輔助方式重新載入或重設工作階段。
程式碼範例
指令碼插入:使用 executeScript()
將 JavaScript 插入受控影格,自訂行為、新增疊加層,或從嵌入的第三方網頁擷取資料。您可以提供內嵌程式碼做為字串,或參照一或多個指令碼檔案 (使用 IWA 套件中的相對路徑)。這個方法會傳回承諾,解析為指令碼的執行結果,通常是最後一個陳述式的值。
document.getElementById('scriptBtn').addEventListener('click', () => {
controlledframe.executeScript({
code: `document.body.style.backgroundColor = 'lightblue';
document.querySelectorAll('a').forEach(link => link.style.pointerEvents = 'none');
document.title; // Return a value
`,
// You can also inject files:
// files: ['./injected_script.js'],
}) .then((result) => {
// The result of the last statement in the script is usually returned.
console.log('Script execution successful. Result (e.g., page title):', result); }).catch((error) => {
console.error('Script execution failed:', error);
});
});
樣式注入:使用 insertCSS()
將自訂樣式套用至受控影格中載入的網頁。
document.getElementById('cssBtn').addEventListener('click', () => {
controlledframe.insertCSS({
code: `body { font-family: monospace; }`
// You can also inject files:
// files: ['./injected_styles.css']
})
.then(() => {
console.log('CSS injection successful.');
})
.catch((error) => {
console.error('CSS injection failed:', error);
});
});
攔截網路要求:使用 WebRequest API 觀察並修改內嵌網頁的網路要求,例如封鎖要求、變更標頭或記錄用量。
// Get the request object
const webRequest = controlledframe.request;
// Create an interceptor for a specific URL pattern
const interceptor = webRequest.createWebRequestInterceptor({
urlPatterns: ["*://evil.com/*"],
blocking: true,
includeHeaders: "all"
});
// Add a listener to block the request
interceptor.addEventListener("beforerequest", (event) => {
console.log('Blocking request to:', event.url);
event.preventDefault();
});
// Add a listener to modify request headers
interceptor.addEventListener("beforesendheaders", (event) => {
console.log('Modifying headers for:', event.url);
const newHeaders = new Headers(event.headers);
newHeaders.append('X-Custom-Header', 'MyValue');
event.setRequestHeaders(newHeaders);
});
新增自訂內容選單:使用 contextMenus
API 在內嵌框架中新增、移除及處理自訂右鍵選單。本範例說明如何在受控影格中新增自訂的「複製所選內容」選單。選取文字後,使用者按一下滑鼠右鍵,就會顯示選單。按一下即可將所選文字複製到剪貼簿,方便使用者在嵌入內容中輕鬆互動。
const menuItemProperties = {
id: "copy-selection",
title: "Copy selection",
contexts: ["selection"],
documentURLPatterns: [new URLPattern({ hostname: '*.example.com'})]
};
// Create the context menu item using a promise
try {
await controlledframe.contextMenus.create(menuItemProperties);
console.log(`Context menu item "${menuItemProperties.id}" created successfully.`);
} catch (error) {
console.error(`Failed to create context menu item:`, error);
}
// Add a standard event listener for the 'click' event
controlledframe.contextMenus.addEventListener('click', (event) => {
if (event.menuItemId === "copy-selection" && event.selectionText) {
navigator.clipboard.writeText(event.selectionText)
.then(() => console.log("Text copied to clipboard."))
.catch(err => console.error("Failed to copy text:", err));
}
});
示範
如要瞭解 Controlled Frames 的方法總覽,請參閱Controlled Frame 示範。
或者,您也可以使用 IWA Kitchen Sink, 這個應用程式有多個分頁,每個分頁都展示不同的 IWA API,例如受控影格和直接套接字等。
結論
受控頁框提供強大且安全的方式,可在隔離網頁應用程式 (IWA) 中嵌入、擴充及與第三方網頁內容互動。這類元素克服了 iframe 的限制,可執行內嵌內容中的指令碼、攔截網路要求,以及實作自訂內容選單等新功能,同時維持嚴格的隔離界線。不過,由於這些 API 可深入控管內嵌內容,因此也附帶額外的安全性限制,且僅適用於 IWA。IWA 的設計宗旨是為使用者和開發人員提供更強大的保障。在多數情況下,開發人員應優先考慮使用標準 <iframe>
元素,因為這些元素較簡單,且足以應付許多情況。如果 iframe 型解決方案受到嵌入限制封鎖,或是缺乏必要的控制和互動功能,就應評估受控框架。無論您是建構資訊亭體驗、整合第三方工具,還是設計模組化外掛程式系統,受控框架都能在結構化、具備權限且安全的環境中,提供精細的控制功能,因此是下一代進階網路應用程式的重要工具。