Element <iframe>
jest zwykle używany do osadzania zasobów zewnętrznych w kontekście przeglądania.
Elementy iframe wymuszają stosowanie zasad zabezpieczeń internetowych, izolując osadzone treści z innych domen od strony hosta i odwrotnie. Chociaż to podejście zwiększa bezpieczeństwo, ponieważ zapewnia bezpieczną granicę między źródłami, ogranicza niektóre przypadki użycia. Użytkownicy mogą na przykład potrzebować dynamicznego wczytywania treści z różnych źródeł i zarządzania nimi, np. nauczyciel może wywołać zdarzenie nawigacji, aby wyświetlić stronę internetową na ekranie w klasie. Wiele witryn blokuje jednak umieszczanie w ramkach iframe za pomocą nagłówków zabezpieczeń, takich jak X-Frame-Options i Content Security Policy (CSP).
Ograniczenia dotyczące elementów iframe uniemożliwiają też bezpośrednie zarządzanie nawigacją lub działaniem umieszczonych treści.
Interfejs Controlled Frame API rozwiązuje to ograniczenie, umożliwiając wczytywanie dowolnych treści internetowych, nawet jeśli wymuszają one restrykcyjne zasady umieszczania. Ten interfejs API jest dostępny wyłącznie w izolowanych aplikacjach internetowych (IWA), które zawierają dodatkowe środki bezpieczeństwa chroniące użytkowników i deweloperów przed potencjalnymi zagrożeniami.
Wdrażanie interfejsu Controlled Frames
Zanim zaczniesz korzystać z kontrolowanej ramki, musisz skonfigurować działającą usługę IWA. Następnie możesz zintegrować ramki kontrolowane ze swoimi stronami.
Dodawanie zasad dotyczących uprawnień
Aby używać interfejsu Controlled Frame, włącz odpowiednie uprawnienie, dodając pole
permissions_policy
z wartością "controlled-frame"
do pliku manifestu izolowanej aplikacji internetowej.
Dodatkowo uwzględnij klucz cross-origin-isolated. Ten klucz nie jest specyficzny dla interfejsu Controlled Frame, ale jest wymagany w przypadku wszystkich izolowanych aplikacji internetowych i określa, czy dokument może uzyskiwać dostęp do interfejsów API, które wymagają izolacji między źródłami.
{
...
"permissions_policy": {
...
"controlled-frame": ["self"],
"cross-origin-isolated": ["self"]
...
}
...
}
Klucz controlled-frame
w pliku manifestu izolowanej aplikacji internetowej (IWA) definiuje listę dozwolonych zasad uprawnień, która określa, które źródła mogą używać interfejsu Controlled Frame. Plik manifestu obsługuje pełną składnię zasady Permissions-Policy, która umożliwia stosowanie wartości takich jak *
, konkretne źródła lub słowa kluczowe, np. self
i src
. Należy jednak pamiętać, że interfejsów API specyficznych dla IWA nie można delegować do innych źródeł.
Nawet jeśli lista dozwolonych zawiera symbol wieloznaczny lub zewnętrzne źródła, te uprawnienia nie będą miały wpływu na funkcje IWA, takie jak controlled-frame
. W przeciwieństwie do standardowych aplikacji internetowych aplikacje IWA domyślnie nie mają żadnych funkcji kontrolowanych przez zasady i wymagają wyraźnych deklaracji. W przypadku funkcji specyficznych dla aplikacji IWA oznacza to, że tylko wartości takie jak self
(własna domena aplikacji IWA) lub src
(domena osadzonej ramki) są funkcjonalnie skuteczne.
Dodawanie elementu Kontrolowana ramka
Wstaw element <controlledframe>
do kodu HTML, aby umieścić w IWA treści pochodzące od innych firm.
<controlledframe id="controlledframe_1" src="https://example.com">
</controlledframe>
Opcjonalny atrybut partition
konfiguruje podział pamięci na potrzeby treści osadzonych, umożliwiając odseparowanie danych, takich jak pliki cookie i pamięć lokalna, aby zachować dane między sesjami.
Przykład: partycja pamięci
Utwórz kontrolowaną ramkę, używając partycji pamięci o nazwie "session1"
. Dane przechowywane w tej partycji (np. pliki cookie i localStorage) zostaną wyczyszczone, gdy ramka zostanie zniszczona lub sesja aplikacji się zakończy.
<controlledframe id="controlledframe_1" src="https://example.com">
</controlledframe>
Przykład: partycja pamięci trwałej
Utwórz kontrolowaną ramkę, używając trwałej partycji pamięci o nazwie "user_data"
. Prefiks "persist:"
zapewnia, że dane przechowywane w tej partycji są zapisywane na dysku i będą dostępne w różnych sesjach aplikacji.
<controlledframe id="frame_2" src="..." partition="persist:user_data">
</controlledframe>
Pobieranie odwołania do elementu
Uzyskaj odwołanie do elementu <controlledframe>
, aby móc z nim wchodzić w interakcję tak jak z dowolnym standardowym elementem HTML:
const controlledframe = document.getElementById('controlledframe_1');
Częste scenariusze i przypadki użycia
Ogólnie rzecz biorąc, wybierz technologię, która najlepiej odpowiada Twoim potrzebom, unikając niepotrzebnej złożoności. W ostatnich latach progresywne aplikacje internetowe (PWA) zmniejszyły różnicę w porównaniu z aplikacjami natywnymi, umożliwiając korzystanie z zaawansowanych funkcji internetowych. Jeśli aplikacja internetowa musi osadzać treści pochodzące od osób trzecich, zalecamy najpierw wypróbowanie zwykłego <iframe>
podejścia. Jeśli wymagania przekraczają możliwości elementów iframe, najlepszą alternatywą mogą być kontrolowane ramki w przypadku interaktywnych aplikacji internetowych. Typowe przypadki użycia są opisane w kolejnych sekcjach.
Umieszczanie treści internetowych innych firm
Wiele aplikacji musi mieć możliwość wczytywania i wyświetlania treści innych firm w swoim interfejsie. Jeśli jednak zaangażowanych jest wielu właścicieli aplikacji internetowych (co jest częstym przypadkiem w przypadku aplikacji osadzonych), trudno jest ustalić spójne zasady kompleksowe. Na przykład ustawienia zabezpieczeń mogą uniemożliwiać tradycyjnemu elementowi <iframe>
osadzanie określonych typów treści, nawet jeśli firmy mają uzasadnioną potrzebę ich osadzania. W przeciwieństwie do elementów <iframe>
kontrolowane ramki zostały zaprojektowane tak, aby omijać te ograniczenia, umożliwiając aplikacjom wczytywanie i wyświetlanie treści, nawet jeśli wyraźnie zabraniają one standardowego osadzania.
Przypadki użycia
- Prezentacje w Classroom: nauczyciel używa ekranu dotykowego w klasie, aby przełączać się między materiałami edukacyjnymi, które zwykle blokują osadzanie w ramce iframe.
- Digital signage w sklepach lub centrach handlowych: kiosk w centrum handlowym wyświetla na przemian strony internetowe różnych sklepów. Kontrolowane ramki zapewniają prawidłowe wczytywanie tych stron, nawet jeśli ograniczają one umieszczanie.
Przykładowe fragmenty kodu
Do zarządzania umieszczonymi treściami przydadzą Ci się te interfejsy Controlled Frame API:
Nawigacja: interfejs Controlled Frames udostępnia wiele metod programowego zarządzania nawigacją i historią nawigacji w osadzonych treściach oraz kontrolowania ich.
Atrybut src
pobiera lub ustawia adres URL treści wyświetlanych w ramce, działając tak samo jak atrybut HTML.
controlledframe.src = "https://example.com";
Metoda back()
cofa o jeden krok w historii ramki. Zwrócony obiekt Promise przyjmuje wartość logiczną wskazującą, czy nawigacja się powiodła.
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);
});
});
Metoda forward()
cofa się o jeden krok w historii ramki. Zwrócony obiekt Promise przyjmuje wartość logiczną wskazującą, czy nawigacja się powiodła.
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);
});
});
Metoda reload()
ponownie ładuje bieżącą stronę w ramce.
document.getElementById('reloadBtn').addEventListener('click', () => {
controlledframe.reload();
});
Dodatkowo ramki kontrolowane udostępniają zdarzenia, które umożliwiają śledzenie pełnego cyklu życia żądań nawigacji – od zainicjowania i przekierowań po wczytanie, ukończenie lub przerwanie treści.
loadstart
: uruchamiane, gdy w ramce rozpoczyna się nawigacja.loadcommit
: Wywoływane, gdy żądanie nawigacji zostało przetworzone i rozpoczyna się wczytywanie głównej zawartości dokumentu.contentload
: Wywoływane, gdy główny dokument i jego niezbędne zasoby zostały załadowane (podobnie jak DOMContentLoaded).loadstop
: uruchamia się, gdy wszystkie zasoby strony (w tym ramki podrzędne i obrazy) zostaną wczytane.loadabort
: wywoływane, jeśli nawigacja zostanie przerwana (np. przez działanie użytkownika lub rozpoczęcie innej nawigacji).loadredirect
: wywoływane, gdy podczas nawigacji nastąpi przekierowanie po stronie serwera.
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}`);
});
Możesz też monitorować i potencjalnie przechwytywać określone interakcje lub żądania zainicjowane przez treści załadowane w kontrolowanej ramce, takie jak próby otwierania okien dialogowych, proszenia o uprawnienia lub otwierania nowych okien.
dialog
: wywoływane, gdy osadzona treść próbuje otworzyć okno (alert, potwierdzenie, prompt). Otrzymasz szczegóły i będziesz mieć możliwość odpowiedzi.consolemessage
: zdarzenie wywoływane, gdy w ramce zostanie zarejestrowana wiadomość w konsoli.permissionrequest
: wywoływane, gdy umieszczone treści proszą o pozwolenie (np. na dostęp do geolokalizacji i powiadomień). Otrzymasz szczegóły i będziesz mieć możliwość zezwolenia na prośbę lub jej odrzucenia.newwindow
: wywoływane, gdy umieszczone treści próbują otworzyć nowe okno lub kartę (np. za pomocą window.open lub linku z atrybutemtarget="_blank"
). Otrzymujesz szczegóły i możesz obsłużyć lub zablokować to działanie.
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');
});
Istnieją też zdarzenia zmiany stanu, które informują o zmianach związanych ze stanem renderowania kontrolowanej ramki, np. o modyfikacjach jej wymiarów lub poziomu powiększenia.
sizechanged
: wywoływane, gdy zmieniają się wymiary treści ramki.zoomchange
: wywoływane, gdy zmieni się poziom powiększenia treści ramki.
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});
});
Metody przechowywania: Controlled Frames udostępniają interfejsy API do zarządzania danymi przechowywanymi w partycji ramki.
Użyj clearData()
, aby usunąć wszystkie zapisane dane. Jest to szczególnie przydatne do resetowania ramki po sesji użytkownika lub zapewnienia czystego stanu. Metoda zwraca obiekt Promise, który jest spełniany po zakończeniu operacji. Możesz też podać opcjonalne opcje konfiguracji:
types
: tablica ciągów znaków określająca typy danych do wyczyszczenia (np.['cookies', 'localStorage', 'indexedDB']
). Jeśli ten parametr zostanie pominięty, zwykle wyczyszczone zostaną wszystkie odpowiednie typy danych.options
: kontrolować proces czyszczenia, np. określać zakres czasu za pomocą właściwości since (sygnatura czasowa w milisekundach od początku epoki), aby czyścić tylko dane utworzone po tym czasie.
Przykład: usuwanie wszystkich danych powiązanych z kontrolowaną ramką
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);
});
}
Przykład: wyczyść tylko pliki cookie i pamięć lokalną utworzone w ciągu ostatniej godziny
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);
});
}
rozszerzać lub modyfikować aplikacje innych firm,
Oprócz prostego umieszczania treści interfejs Controlled Frames oferuje mechanizmy, które umożliwiają umieszczającej izolowanej aplikacji internetowej kontrolowanie umieszczonych treści internetowych innych firm. Możesz wykonywać skrypty w treściach osadzonych, przechwytywać żądania sieciowe i zastępować domyślne menu kontekstowe – wszystko to w bezpiecznym, izolowanym środowisku.
Przypadki użycia
- Wymuszanie stosowania marki w witrynach osób trzecich: wstrzykiwanie niestandardowego kodu CSS i JavaScript do osadzonych witryn w celu zastosowania jednolitego motywu wizualnego.
- Ograniczanie nawigacji i działania linków: przechwytywanie lub wyłączanie niektórych zachowań tagów
<a>
za pomocą wstrzykiwania skryptów. - Automatyzacja przywracania po awariach lub braku aktywności: monitoruj treści umieszczone na stronie pod kątem stanów awarii (np. pusty ekran, błędy skryptu) i po upływie limitu czasu programowo ponownie załaduj lub zresetuj sesję.
Przykładowe fragmenty kodu
Wstrzykiwanie skryptu: użyj executeScript()
, aby wstrzyknąć JavaScript do kontrolowanej ramki, co pozwoli Ci dostosować zachowanie, dodać nakładki lub wyodrębnić dane z osadzonych stron innych firm. Możesz podać kod wbudowany jako ciąg tekstowy lub odwołać się do co najmniej 1 pliku skryptu (używając ścieżek względnych w pakiecie IWA). Metoda zwraca obietnicę, która jest rozwiązywana jako wynik wykonania skryptu – zwykle wartość ostatniej instrukcji.
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);
});
});
Wstrzykiwanie stylów: użyj insertCSS()
, aby zastosować niestandardowe style do stron wczytanych w ramce kontrolowanej.
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);
});
});
Przechwytywanie żądań sieciowych: użyj interfejsu WebRequest API, aby obserwować i potencjalnie modyfikować żądania sieciowe z osadzonej strony, np. blokować żądania, zmieniać nagłówki lub rejestrować użycie.
// 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);
});
Dodawanie niestandardowych menu kontekstowych: użyj interfejsu contextMenus
API, aby dodawać, usuwać i obsługiwać niestandardowe menu kontekstowe w ramce osadzonej. W tym przykładzie pokazujemy, jak dodać niestandardowe menu „Kopiuj zaznaczenie” w ramce kontrolowanej. Gdy tekst
jest zaznaczony i użytkownik kliknie go prawym przyciskiem myszy, pojawi się menu. Po kliknięciu tego przycisku wybrany tekst zostanie skopiowany do schowka, co umożliwi proste i wygodne interakcje w osadzonych treściach.
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));
}
});
Prezentacja
Zapoznaj się z demonstracją Controlled Frame, aby poznać metody Controlled Frames.
IWA Kitchen Sink to aplikacja z wieloma kartami, z których każda prezentuje inny interfejs IWA API, np. Controlled Frames czy Direct Sockets.
Podsumowanie
Interfejs Controlled Frame to skuteczny i bezpieczny sposób na umieszczanie, rozszerzanie i interakcję z treściami internetowymi innych firm w izolowanych aplikacjach internetowych (IWA). Przezwyciężają one ograniczenia elementów iframe, umożliwiając nowe funkcje, takie jak wykonywanie skryptów w treściach osadzonych, przechwytywanie żądań sieciowych i wdrażanie niestandardowych menu kontekstowych – a wszystko to przy zachowaniu ścisłych granic izolacji. Jednak ze względu na to, że te interfejsy API zapewniają dużą kontrolę nad treściami osadzonymi, wiążą się też z dodatkowymi ograniczeniami dotyczącymi bezpieczeństwa i są dostępne tylko w przypadku interaktywnych aplikacji internetowych, które zostały zaprojektowane tak, aby zapewniać większe bezpieczeństwo zarówno użytkownikom, jak i deweloperom. W większości przypadków deweloperzy powinni najpierw rozważyć użycie standardowych elementów <iframe>
, które są prostsze i wystarczające w wielu scenariuszach. Kontrolowane ramki należy ocenić, gdy rozwiązania oparte na ramkach iframe są blokowane przez ograniczenia dotyczące osadzania lub nie mają niezbędnych możliwości kontroli i interakcji. Niezależnie od tego, czy tworzysz interfejsy kiosków, integrujesz narzędzia innych firm czy projektujesz modułowe systemy wtyczek, kontrolowane ramki umożliwiają precyzyjne sterowanie w ustrukturyzowanym, bezpiecznym środowisku z odpowiednimi uprawnieniami. Dzięki temu są one kluczowym narzędziem w przypadku zaawansowanych aplikacji internetowych nowej generacji.