Het <iframe>
-element wordt doorgaans gebruikt om externe bronnen in een browsercontext te embedden. Iframes handhaven het beveiligingsbeleid van het web door ingesloten content van verschillende bronnen te isoleren van de hostpagina en vice versa. Hoewel deze aanpak de beveiliging verbetert door een veilige grens tussen bronnen te creëren, beperkt het sommige use cases. Gebruikers moeten bijvoorbeeld mogelijk dynamisch content van verschillende bronnen laden en beheren, zoals een docent die een navigatiegebeurtenis activeert om een webpagina op een scherm in de klas weer te geven. Veel websites blokkeren embedden in iframes echter expliciet met behulp van beveiligingsheaders zoals X-Frame-Options en Content Security Policy (CSP). Bovendien voorkomen iframebeperkingen dat pagina's de navigatie of het gedrag van de ingesloten content rechtstreeks kunnen beheren.
De Controlled Frame API pakt deze beperking aan door het laden van alle webcontent toe te staan, zelfs als er restrictieve embeddingsregels gelden. Deze API is uitsluitend beschikbaar binnen geïsoleerde webapplicaties (IWA's) , die extra beveiligingsmaatregelen bevatten om zowel gebruikers als ontwikkelaars te beschermen tegen mogelijke risico's.
Gecontroleerde frames implementeren
Voordat u een Controlled Frame gebruikt, moet u een functionele IWA instellen . Vervolgens kunt u Controlled Frames in uw pagina's integreren.
Toestemmingsbeleid toevoegen
Om Controlled Frames te gebruiken, schakelt u de bijbehorende machtiging in door een veld permissions_policy
met de waarde "controlled-frame"
toe te voegen aan uw IWA-manifest. Voeg daarnaast de cross-origin-isolated sleutel toe. Deze sleutel is niet specifiek voor Controlled Frames, maar is vereist voor alle IWA's en bepaalt of het document toegang heeft tot API's die cross-origin-isolatie vereisen.
{
...
"permissions_policy": {
...
"controlled-frame": ["self"],
"cross-origin-isolated": ["self"]
...
}
...
}
De sleutel controlled-frame
in een IWA-manifest (Isolated Web App) definieert een toestemmingslijst voor machtigingsbeleid, waarin wordt aangegeven welke bronnen gecontroleerde frames mogen gebruiken. Hoewel het manifest de volledige syntaxis van het machtigingsbeleid ondersteunt (waardoor waarden zoals *
, specifieke bronnen of trefwoorden zoals self
en src
worden toegestaan), is het cruciaal om te weten dat IWA-specifieke API's niet kunnen worden gedelegeerd aan andere bronnen. Zelfs als de toestemmingslijst een wildcard of externe bronnen bevat, worden deze machtigingen niet van kracht voor IWA-functies zoals controlled-frame
. In tegenstelling tot standaard webapps stellen IWA's alle door beleid gecontroleerde functies standaard in op geen, waardoor expliciete declaraties vereist zijn. Voor IWA-specifieke functies betekent dit dat alleen waarden zoals self
(de eigen oorsprong van de IWA) of src
(de oorsprong van een ingebed frame) functioneel effectief zijn.
Voeg een Controlled Frame-element toe
Voeg een <controlledframe>
-element in uw HTML in om inhoud van derden in uw IWA in te sluiten.
<controlledframe id="controlledframe_1" src="https://example.com">
</controlledframe>
Met het optionele partition
configureert u opslagpartitionering voor ingesloten inhoud, zodat u gegevens zoals cookies en lokale opslag kunt isoleren om gegevens over sessies heen te behouden.
Voorbeeld: In-memory opslagpartitie
Maak een gecontroleerd frame met behulp van een in-memory opslagpartitie met de naam "session1"
. Gegevens die op deze partitie zijn opgeslagen (bijvoorbeeld cookies en localStorage) worden gewist wanneer het frame wordt vernietigd of de applicatiesessie wordt beëindigd.
<controlledframe id="controlledframe_1" src="https://example.com">
</controlledframe>
Voorbeeld: permanente opslagpartitie
Maak een Controlled Frame met behulp van een persistente opslagpartitie met de naam "user_data"
. Het voorvoegsel "persist:"
zorgt ervoor dat de gegevens die in deze partitie zijn opgeslagen, op schijf worden opgeslagen en beschikbaar zijn voor alle applicatiesessies.
<controlledframe id="frame_2" src="..." partition="persist:user_data">
</controlledframe>
Elementreferentie ophalen
Verkrijg een referentie naar het <controlledframe>
-element, zodat u ermee kunt interacteren zoals met elk standaard HTML-element:
const controlledframe = document.getElementById('controlledframe_1');
Veelvoorkomende scenario's en use cases
Kies in het algemeen de beste technologie die aan uw behoeften voldoet, zonder onnodige complexiteit. De afgelopen jaren hebben Progressive Web Apps (PWA's) de kloof met native apps gedicht en krachtige webervaringen mogelijk gemaakt. Als een webapplicatie content van derden moet insluiten, is het raadzaam om eerst de reguliere <iframe>
-aanpak te verkennen. Als de vereisten de mogelijkheden van iframes overstijgen, zijn Controlled Frames op IWA's mogelijk de beste optie. Veelvoorkomende use cases worden in de volgende secties beschreven.
Het insluiten van webinhoud van derden
Veel applicaties moeten de mogelijkheid hebben om content van derden te laden en weer te geven binnen hun gebruikersinterface. Wanneer er echter meerdere webapp-eigenaren bij betrokken zijn – een veelvoorkomend scenario bij embedded applicaties – wordt het lastig om consistent end-to-end beleid vast te stellen. Beveiligingsinstellingen kunnen bijvoorbeeld voorkomen dat een traditionele <iframe>
bepaalde soorten content insluit, zelfs wanneer bedrijven daar een legitieme reden voor hebben. In tegenstelling tot <iframe>
-elementen zijn Controlled Frames ontworpen om deze beperkingen te omzeilen, waardoor applicaties content kunnen laden en weergeven, zelfs als standaard insluiting expliciet wordt verboden.
Gebruiksscenario's
- Presentaties in het klaslokaal : een docent gebruikt een touchscreen in het klaslokaal om te schakelen tussen educatieve bronnen die normaal gesproken de iframe-insluiting zouden blokkeren.
- Digitale bewegwijzering in de detailhandel of winkelcentra : een kiosk in een winkelcentrum doorloopt websites van verschillende winkels. Controlled Frames zorgen ervoor dat deze pagina's correct laden, zelfs als ze de insluiting beperken.
Codevoorbeelden
De volgende Controlled Frame API's zijn handig voor het beheren van ingesloten inhoud.
Navigatie : Controlled Frames bieden meerdere methoden om de navigatie en navigatiegeschiedenis van de ingesloten inhoud programmatisch te beheren en te controleren.
Met het src
-kenmerk wordt de URL van de in het frame weergegeven inhoud opgehaald of ingesteld. Dit werkt op dezelfde manier als het HTML-kenmerk.
controlledframe.src = "https://example.com";
De back()
-methode navigeert één stap terug in de geschiedenis van het frame. De geretourneerde promise wordt omgezet in een boolean die aangeeft of de navigatie succesvol was.
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);
});
});
De forward()
-methode navigeert één stap vooruit in de geschiedenis van het frame. De geretourneerde promise wordt omgezet in een boolean die aangeeft of de navigatie succesvol was.
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);
});
});
De reload()
-methode laadt de huidige pagina in het frame opnieuw.
document.getElementById('reloadBtn').addEventListener('click', () => {
controlledframe.reload();
});
Bovendien bieden Controlled Frames gebeurtenissen waarmee u de volledige levenscyclus van navigatieaanvragen kunt volgen: van initiatie en omleidingen tot het laden, voltooien of afbreken van de inhoud.
-
loadstart
: wordt geactiveerd wanneer een navigatie in het frame begint. -
loadcommit
: wordt geactiveerd wanneer het navigatieverzoek is verwerkt en de inhoud van het hoofddocument wordt geladen. -
contentload
: wordt geactiveerd wanneer het hoofddocument en de essentiële bronnen zijn geladen (vergelijkbaar met DOMContentLoaded). -
loadstop
: wordt geactiveerd wanneer alle bronnen voor de pagina (inclusief subframes en afbeeldingen) zijn geladen. -
loadabort
: Wordt geactiveerd als een navigatie wordt afgebroken (bijvoorbeeld door een actie van de gebruiker of als een andere navigatie start). -
loadredirect
: wordt geactiveerd wanneer er tijdens de navigatie een omleiding aan de serverzijde plaatsvindt.
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}`);
});
U kunt ook specifieke interacties of verzoeken die worden geïnitieerd door de inhoud die in het gecontroleerde frame is geladen, bewaken en mogelijk onderscheppen. Denk hierbij aan pogingen om dialoogvensters te openen, machtigingen aan te vragen of nieuwe vensters te openen.
-
dialog
: wordt geactiveerd wanneer de ingesloten inhoud probeert een dialoogvenster te openen (waarschuwing, bevestiging, prompt). U ontvangt details en kunt reageren. -
consolemessage
: wordt geactiveerd wanneer een bericht in het frame naar de console wordt gelogd. -
permissionrequest
: wordt geactiveerd wanneer de ingesloten content toestemming vraagt (bijvoorbeeld geolocatie en meldingen). U ontvangt details en kunt het verzoek toestaan of weigeren. -
newwindow
: Wordt geactiveerd wanneer de ingesloten inhoud probeert een nieuw venster of tabblad te openen (bijvoorbeeld met window.open of een link mettarget="_blank"
). U ontvangt details en kunt de actie afhandelen of blokkeren.
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');
});
Er zijn ook statuswijzigingsgebeurtenissen die u op de hoogte stellen van wijzigingen die betrekking hebben op de eigen renderingstatus van het beheerde frame, zoals wijzigingen in de afmetingen of het zoomniveau.
-
sizechanged
: Wordt geactiveerd wanneer de afmetingen van de inhoud van het frame veranderen. -
zoomchange
: wordt geactiveerd wanneer het zoomniveau van de inhoud van het frame verandert.
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});
});
Opslagmethoden : Controlled Frames bieden API's voor het beheren van gegevens die zijn opgeslagen in de partitie van een frame.
Gebruik clearData()
om alle opgeslagen gegevens te verwijderen, wat vooral handig is om het frame na een gebruikersessie te resetten of een schone status te garanderen. De methode retourneert een Promise die wordt opgelost wanneer de bewerking is voltooid. Optionele configuratieopties kunnen ook worden opgegeven:
-
types
: Een reeks strings die specificeren welke gegevenstypen moeten worden gewist (bijvoorbeeld['cookies', 'localStorage', 'indexedDB']
). Als deze weggelaten wordt, worden doorgaans alle toepasselijke gegevenstypen gewist. -
options
: Beheer het wisproces, bijvoorbeeld door een tijdsbereik op te geven met behulp van een sinds-eigenschap (tijdstempel in milliseconden sinds tijdperk) om alleen gegevens te wissen die na dat tijdstip zijn gemaakt.
Voorbeeld: Wis alle opslag die is gekoppeld aan het gecontroleerde frame
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);
});
}
Voorbeeld: Wis alleen cookies en localStorage die in het laatste uur zijn aangemaakt
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);
});
}
Uitbreiden of wijzigen van applicaties van derden
Naast eenvoudige insluiting bieden de Controlled Frames mechanismen waarmee de insluitende IWA controle kan uitoefenen over de ingesloten webcontent van derden. U kunt scripts uitvoeren binnen de ingesloten content, netwerkverzoeken onderscheppen en standaardcontextmenu's overschrijven – allemaal in een veilige, geïsoleerde omgeving.
Gebruiksscenario's
- Branding op externe sites toepassen : voeg aangepaste CSS en JavaScript toe aan ingesloten websites om een uniform visueel thema toe te passen.
- Beperk navigatie- en koppelingsgedrag : onderschep of schakel bepaald
<a>
-taggedrag uit met scriptinjectie. - Automatisch herstel na crashes of inactiviteit : controleer ingesloten inhoud op fouten (bijvoorbeeld een leeg scherm, scriptfouten) en laad of reset de sessie programmatisch na een time-out.
Codevoorbeelden
Scriptinjectie : Gebruik executeScript()
om JavaScript in het gecontroleerde frame te injecteren, zodat u het gedrag kunt aanpassen, overlays kunt toevoegen of gegevens kunt extraheren uit ingesloten pagina's van derden. U kunt inline code als een tekenreeks opgeven of verwijzen naar een of meer scriptbestanden (met behulp van relatieve paden binnen het IWA-pakket). De methode retourneert een promise die resulteert in het resultaat van de uitvoering van het script, meestal de waarde van de laatste instructie.
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);
});
});
Stijlinjectie : gebruik insertCSS()
om aangepaste stijlen toe te passen op pagina's die in een gecontroleerd frame worden geladen.
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);
});
});
Netwerkverzoekonderschepping : gebruik de WebRequest API om netwerkverzoeken van de ingesloten pagina te observeren en mogelijk te wijzigen. U kunt bijvoorbeeld verzoeken blokkeren, headers wijzigen of het gebruik loggen.
// 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);
});
Aangepaste contextmenu's toevoegen : gebruik de contextMenus
API om aangepaste rechtsklikmenu's toe te voegen, te verwijderen en te verwerken binnen het ingesloten frame. Dit voorbeeld laat zien hoe u een aangepast menu 'Selectie kopiëren' toevoegt aan een gecontroleerd frame. Wanneer tekst is geselecteerd en de gebruiker met de rechtermuisknop klikt, verschijnt het menu. Door erop te klikken, wordt de geselecteerde tekst naar het klembord gekopieerd, wat eenvoudige en gebruiksvriendelijke interacties binnen ingesloten content mogelijk maakt.
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));
}
});
Demonstratie
Bekijk de Controlled Frame-demo voor een overzicht van de methoden van Controlled Frames.
Als alternatief is er IWA Kitchen Sink , een app met meerdere tabbladen die elk een andere IWA API demonstreren, zoals Controlled Frames, Direct Sockets en meer.
Conclusie
Controlled Frames bieden een krachtige en veilige manier om webcontent van derden in te sluiten, uit te breiden en ermee te interacteren in geïsoleerde webapps (IWA's). Door de beperkingen van iframes te omzeilen, maken ze nieuwe mogelijkheden mogelijk, zoals het uitvoeren van scripts in ingesloten content, het onderscheppen van netwerkverzoeken en het implementeren van aangepaste contextmenu's – dit alles met behoud van strikte isolatiegrenzen. Omdat deze API's echter diepgaande controle over ingesloten content bieden, brengen ze ook extra beveiligingsbeperkingen met zich mee en zijn ze alleen beschikbaar binnen IWA's, die zijn ontworpen om sterkere garanties af te dwingen voor zowel gebruikers als ontwikkelaars. Voor de meeste use cases moeten ontwikkelaars eerst overwegen om standaard <iframe>
-elementen te gebruiken, die in veel scenario's eenvoudiger en toereikend zijn. Controlled Frames moeten worden geëvalueerd wanneer iframe-gebaseerde oplossingen worden geblokkeerd door insluitingsbeperkingen of de benodigde controle- en interactiemogelijkheden missen. Of u nu kioskervaringen bouwt, tools van derden integreert of modulaire plug-insystemen ontwerpt, Controlled Frames maken gedetailleerde controle mogelijk in een gestructureerde, geautoriseerde en veilige omgeving, waardoor ze een cruciale tool zijn in de volgende generatie geavanceerde webapplicaties.