1. Introduction
This specification describes a content embedding API that satisfies some
critical use cases for IWAs that iframe
does not support. This embedding
environment should allow embedding all content without express permission from
the embedded site, including content which iframe
cannot embed, and provide
embedding sites more control over that embedded content.
Since this is a particularly powerful API, its use and availability makes an app a target of various types of hacking. As a result, this API is limited to use in Isolated Web Applications (IWAs) which have addtional safeguards in place to protect users and developers. IWAs are not a normal web application and can exist only at a special 'isolated-app:' scheme. This means by design that this API will not be available to normal web pages.
Note: This API is not intended to be a replacement or substitute for iframe
.
All iframe
use cases are still valid and should continue to use iframe
,
including IWAs where possible.
2. The Fenced Frame specification
For convenience, the Controlled Frame specification assumes that the Fenced Frame specification is in place. There are concepts introduced in the Fenced Frame specification, such as nested top-level traversibles, that are broadly useful to refer to in the context of Controlled Frame.
The Fenced Frame specification achieves defining these concepts via monkey patching some specifications, such as HTML. We will also require monkey patching specifications for some parts of this Controlled Frame specification.
3. The controlledframe
element
- Contexts in which this element can be used:
- Where embedded content is expected.
- Content model:
- Nothing.
- Content attributes:
- Global attributes
src
— Content source URL to embedpartition
— Partition name to hold data related to this content - Properties
contentWindow
— Embedded content window DOM accessorcontextMenus
— Context menus accessorrequest
— WebRequest accessor- Accessibility considerations:
-
Screen readers should be able to traverse into the embedded content similar to how a reader can traverse into iframes and other related embedded content.
- DOM interface:
-
[
Exposed =Window ,IsolatedContext ]interface
:HTMLControlledFrameElement HTMLElement { [HTMLConstructor ]
(); [constructor CEReactions ]attribute USVString
;src attribute DOMString
;partition readonly attribute WindowProxy ?
;contentWindow readonly attribute ContextMenus
;contextMenus readonly attribute WebRequest
; // Navigation methods.request Promise <undefined >back ();boolean canGoBack ();boolean canGoForward ();Promise <undefined >forward ();Promise <undefined >go (long
);relativeIndex undefined reload ();undefined stop (); // Scripting methods.Promise <undefined >addContentScripts (sequence <ContentScriptDetails >
);contentScriptList Promise <any >executeScript (optional InjectDetails
= {});details Promise <undefined >insertCSS (optional InjectDetails
= {});details Promise <undefined >removeContentScripts (sequence <DOMString >?
); // Configuration methods.scriptNameList Promise <undefined >clearData (optional ClearDataOptions
= {},options optional ClearDataTypeSet
= {});types Promise <boolean >getAudioState ();Promise <long >getZoom ();Promise <boolean >isAudioMuted ();undefined setAudioMuted (boolean
);mute Promise <undefined >setZoom (long
); // Capture methods.zoomFactor Promise <undefined >captureVisibleRegion (optional ImageDetails
= {});options undefined print (); };
The controlledframe
element represents its embedded navigable which
has a embedderParent that is valid and refers back to the embedder navigable.
The embedded navigable must appear as a top-level traversible. As part of the embedded navigable being a top-level traversible, the embedded navigable must have a null parent. Content within the embedded navigable cannot distinguish that it is embedded within another navigable.
The Controlled Frame element is exposed to any Document
with the
"controlled-frame" policy-controlled feature whose environment settings object is an isolated context.
The partition attribute takes an identifier specifying where data related to the Controlled Frame element’s instance should be stored. The identifier is composed of a string of alphanumeric digits. All data for the embedded navigable will be stored in this partition.
By default, all data stored will be held in an in-memory storage partition so that when the Controlled Frame element is destroyed the data is also destroyed. While the data is held in this partition, no data will persist from that Controlled Frame embedded navigable.
If the partition attribute identifier contains the prefix "persist:", the user agent will use a disk-based storage environment rather than an in-memory storage partition.
If multiple Controlled Frames share the same partition identifier, all of their embedded navigable instances will share the same storage partition.
-
If embeddedContent does not have an embedderParent, return false.
-
If embeddedContent’s embedderParent is not valid, return false.
-
If embeddedContent’s embedderParent is not the current navigable, return false.
-
Return true.
3.1. Navigation methods
back
()-
Goes back one step in the overall session history entries list for the traversable navigable in the Controlled Frame.
If there is no previous page, does nothing.
canGoBack
()-
Returns true if the current current session history entry is not the first one in the navigation history entry list. This means that there is a previous session history entry for this navigable.
forward
()-
Goes forward one step in the overall session history entries list for the traversable navigable in the Controlled Frame.
If there is no next page, does nothing.
go
()-
Reloads the current page.
go
(relativeIndex)-
Goes back or forward relativeIndex number of steps in the overall session history entries list for the current traversable navigable.
A zero relative index will reload the current page.
If the relative index is out of range, does nothing.
reload
()-
Reloads the current page.
stop
()-
Cancels the document load.
object
state, run the following steps:
-
If state is not an integer, return false.
-
Attempt to navigate the embedded content by state history items. If an error is encountered, return false.
-
Return true.
back()
method steps for
an embeddedContent are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content with an embedded navigable embeddedContent is false, reject resultPromise with a
TypeError
and abort these steps. -
If the result of running navigate embedded content to state with "-1" is false, reject resultPromise with a
TypeError
and abort these steps. -
Then, resolve resultPromise.
canGoBack()
method steps for an embeddedContent are:
-
If the result of running validate embedded content with an embedded navigable embeddedContent is false, return false.
-
If embedded content is already at its first history state, return false.
-
Return true.
canGoForward()
method steps for an embeddedContent are:
-
If the result of running validate embedded content with an embedded navigable embeddedContent is false, return false.
-
If embedded content is already at its last history state, return false.
-
Return true.
forward()
method steps for an embeddedContent are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content with an embedded navigable embeddedContent is false, reject resultPromise with a
TypeError
and abort these steps. -
If the result of running navigate embedded content to state with "1" is false, reject resultPromise with a
TypeError
and abort these steps. -
Then, resolve resultPromise.
go(relativeIndex)
method steps for an embeddedContent are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content with an embedded navigable embeddedContent is false, reject resultPromise with a
TypeError
and abort these steps. -
If the result of running navigate embedded content to state with relativeIndex is false, reject resultPromise with a
TypeError
and abort these steps. -
Then, resolve resultPromise.
reload()
method steps for an embeddedContent are:
-
If the result of running validate embedded content with an embedded navigable embeddedContent is false, return false.
-
Reload embedded content for its top-level page.
-
Return true.
stop()
method steps for an embeddedContent are:
-
If the result of running validate embedded content with an embedded navigable embeddedContent is false, return false.
-
If embedded content is not currently loading a navigation, abort these steps.
-
Stop the current navigation in embedded content.
-
Return true.
3.2. Scripting methods
// One of |code| or |file| must be specified but not both.dictionary {
InjectDetails DOMString ;
code DOMString ; };
file dictionary {
InjectionItems DOMString ;
code sequence <DOMString >; };
files enum {
RunAt ,
"document_start" ,
"document_end" , };
"document_idle" dictionary {
ContentScriptDetails boolean ;
all_frames InjectionItems ;
css sequence <DOMString >;
exclude_globs sequence <DOMString >;
exclude_matches sequence <DOMString >;
include_globs InjectionItems ;
js boolean ;
match_about_blank required sequence <DOMString >;
matches required DOMString ;
name RunAt ; };
run_at
In fetch an injection item, there are references to "associate" some code with each load. This specific functionality is referring to an element of the user agent’s page load process that is considered to be unspecified. This section will elaborate:
When a page refers to some inline CSS or JS code, this will be injected into the appropriate CSS or JS subsystem at the correct point during the page load process.
If a page refers to some external file at a URL, then the user agent will fetch that URL. If the fetch returns successfully, the user agent will now have a body of either CSS or JS code. The user agent can then inject that code into the appropriate CSS or JS subsystem at the correct point during the page load process.
The way that fetch an injection item works is analogous to this process, except assume that the page never referred to the external file yet the content specified at that file URL was still injected into the page during page load.
Since there is no reference within the page to that external file URL, the user agent will not have a "correct point" at which to inject the content into the appropriate CSS or JS subsystem. Instead the content will be injected at the end of the page load process, as if the external file URL reference existed at the end of the page body.
object
injectionItem and a type, run the following steps:
-
If type is neither "css" or "js", return false.
-
If injectionItem is not a dictionary, return false.
-
If injectionItem is empty, return false.
-
If injectionItem has both fields "code" and "file" or neither field:
-
Return false.
-
-
If injectionItem has field "code":
-
Let injectionCode be injectionItem["code"].
-
-
If injectionItem has field "file":
-
If type is "css":
-
If type is "js":
-
Return true.
addContentScripts(contentScriptList)
method steps for an embeddedContent are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content with an embedded navigable embeddedContent is false, reject resultPromise with a
TypeError
and abort these steps. -
For each contentScriptDetail in contentScriptList:
-
If contentScriptDetail has field "css":
-
For each cssInjectionItem in contentScriptDetail["css"]:
-
If the result of running fetch an injection item with embeddedContent, cssInjectionItem and type "css" is false, reject resultPromise with a
TypeError
and abort these steps.
-
-
-
If contentScriptDetail has field "js":
-
For each jsInjectionItem in contentScriptDetail["js"]:
-
If the result of running fetch an injection item with embeddedContent, jsInjectionItem and type "js" is false, reject resultPromise with a
TypeError
and abort these steps.
-
-
-
-
Otherwise, resolve resultPromise.
executeScript(optional details)
method steps are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content is false, reject resultPromise with a
TypeError
and abort these steps. -
If details has field "code":
-
Let detailsCode be details["code"].
-
-
If details has field "file":
-
Let detailsFile be details["file"].
-
-
If both detailsCode and detailsFile are set, reject resultPromise with a
TypeError
and abort these steps. -
If detailsFile is set:
-
In the embedded content, execute the script detailsCode.
-
Then, resolve resultPromise.
insertCSS(optional details)
method steps are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content is false, reject resultPromise with a
TypeError
and abort these steps. -
If details has field "code":
-
Let detailsCode be details["code"].
-
-
If details has field "file":
-
Let detailsFile be details["file"].
-
-
If both detailsCode and detailsFile are set, reject resultPromise with a
TypeError
and abort these steps. an error. -
If detailsFile is set:
-
In the embedded content, insert the stylesheet content detailsCode.
-
Resolve the promise successfully.
-
Then, resolve resultPromise.
removeContentScripts(scriptNameList)
method steps are:
3.3. Configuration methods
dictionary {
ClearDataOptions long ; };
since dictionary {
ClearDataTypeSet boolean ;
appcache boolean ;
cache boolean ;
cookies boolean ;
fileSystems boolean ;
indexedDB boolean ;
localStorage boolean ;
persistentCookies boolean ;
sessionCookies boolean ; };
webSQL
clearData(optional options, optional types)
method steps are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content is false, reject resultPromise with a
TypeError
and abort these steps. -
Let clearSince be 0. This will represent to clear all items.
-
If options["since"] is set:
-
Let clearSince be options["since"].
-
-
If types["appcache"] is set:
-
Clear the partition’s appcache items with a matching clearSince.
-
-
If types["cache"] is set:
-
Clear the partition’s cache items with a matching clearSince.
-
-
If types["cookies"] is set:
-
Clear the partition’s cookie items with a matching clearSince.
-
-
If types["fileSystems"] is set:
-
Clear the partition’s fileSystem items with a matching clearSince.
-
-
If types["indexedDB"] is set:
-
Clear the partition’s indexedDB items with a matching clearSince.
-
-
If types["localStorage"] is set:
-
Clear the partition’s localStorage items with a matching clearSince.
-
-
If types["persistentCookies"] is set:
-
Clear the partition’s persistentCookies items with a matching clearSince.
-
-
If types["sessionCookies"] is set:
-
Clear the partition’s sessionCookies items with a matching clearSince.
-
-
If types["webSQL"] is set:
-
Clear the partition’s webSQL items with a matching clearSince.
-
-
Then, resolve resultPromise.
getAudioState()
method steps are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content is false, reject resultPromise with a
TypeError
and abort these steps. -
Let muteState be the current audio mute state of embedded content.
-
Then, resolve resultPromise with muteState.
getZoom()
method steps are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content is false, reject resultPromise with a
TypeError
and abort these steps. -
Let zoomFactor be the current zoom setting for the embedded content.
-
Then, resolve resultPromise with zoomFactor.
isAudioMuted()
method steps are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content is false, reject resultPromise with a
TypeError
and abort these steps. -
Let muteState be the current audio mute state for embedded content.
-
Then, resolve resultPromise with muteState.
setAudioMuted(mute)
method steps are:
-
If the result of running validate embedded content is false, return false.
-
Change the audio mute state for embedded content to match mute state.
setZoom(zoomFactor)
method steps are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content is false, reject resultPromise with a
TypeError
and abort these steps. -
Change the zoom for the embedded content to be zoomFactor.
-
Then, resolve resultPromise.
3.4. Capture methods
// One of |code| or |file| must be specified but not both.dictionary {
ImageDetails DOMString ;
format DOMString ; };
quality
captureVisibleRegion(optional options)
method steps are:
-
Let resultPromise be a new promise.
-
If the result of running validate embedded content is false, reject resultPromise with a
TypeError
and abort these steps. -
Let optionsFormat be "JPEG" by default.
-
Let optionsQuality be 100 by default.
-
If options has field "format":
-
Let optionsFormat be options["format"].
-
-
If optionsFormat is an unrecognized format, reject resultPromise with a
TypeError
and abort these steps. -
If options has field "quality":
-
Let optionsQuality be options["quality"].
-
-
If optionsQuality is not an integer and is not between 0 and 100 inclusive, reject resultPromise with a
TypeError
and abort these steps. -
Create an image in format
optionsFormat
at qualityoptionsQuality
showing the visible region of embedded content. -
Then, resolve resultPromise containing the image data.
print()
method steps are:
-
If the result of running validate embedded content is false, abort these steps.
-
Initiate the browser print page feature for embedded content.
3.5. Event listener API
3.6. Integration with other specifications
This specification will make some modifications to specifications to accommodate the needs of Controlled Frame.
3.6.1. Monkey Patch to HTML
Each navigable has:
-
A frameId integer, initially 0.
-
A next frameId integer, initially 1.
-
An embedderParent, a navigable or null.
The initialize the navigable algorithm given a navigable navigable and an optional navigable-or-null parent (default null) is monkeypatched as follows:
-
Set navigable’s parent to parent.
- If this navigable has been created via a Controlled Frame embedder, then set embedderParent to the embedder navigable. Otherwise, set embedderParent to null.
-
If parent is not null (navigable is not a top-level traversable), then:
- Set navigable’s frameId to parent’s next frameId.
- Increment parent’s next frameId.
4. Web Request API
enum {
ResourceType ,
"main_frame" ,
"sub_frame" ,
"stylesheet" ,
"script" ,
"image" ,
"font" ,
"object" ,
"xmlhttprequest" ,
"ping" ,
"csp_report" ,
"media" ,
"websocket" ,
"webbundle" , };
"other" callback interface {
WebRequestEventListener BlockingResponse ?(
handleEvent WebRequestEventDetails ); };
details dictionary {
RequestFilter sequence <ResourceType >;
types sequence <USVString >; };
urls enum {
ExtraInfoSpec ,
"asyncBlocking" ,
"blocking" ,
"extraHeaders" ,
"requestHeaders" , }; [
"responseHeaders" Exposed =Window ,IsolatedContext ]interface {
WebRequestEvent undefined addListener (WebRequestEventListener ,
listener optional RequestFilter = {},
filter optional sequence <ExtraInfoSpec >);
extraInfoSpec boolean hasListener (WebRequestEventListener );
listener boolean hasListeners ();undefined removeListener (WebRequestEventListener ); };
listener dictionary {
WebRequestAuthCredentials required DOMString ;
username required DOMString ; };
password dictionary {
BlockingResponse WebRequestAuthCredentials ;
authCredentials boolean ;
cancel USVString ;
redirectUrl sequence <HttpHeader >;
requestHeaders sequence <HttpHeader >; };
responseHeaders enum {
DocumentLifecycle ,
"prerender" ,
"active" ,
"cached" , };
"pending_deletion" enum {
FrameType ,
"outermost_frame" ,
"fenced_frame" , };
"sub_frame" dictionary {
WebRequestEventDetails DOMString ;
documentId DocumentLifecycle ;
documentLifecycle required long ;
frameId FrameType ;
frameType USVString ;
initiator required DOMString ;
method DOMString ;
parentDocumentId required long ;
parentFrameId required DOMString ;
requestId required long ;
timeStamp required ResourceType ;
type required USVString ; };
url dictionary {
UploadData ArrayBuffer ;
bytes DOMString ; };
file dictionary {
RequestBody DOMString ;
error any ;
formData sequence <UploadData >; };
raw dictionary :
WebRequestBeforeRequestDetails WebRequestEventDetails {RequestBody ; };
requestBody dictionary {
HttpHeader required DOMString ;
name DOMString ;
value sequence <byte >; };
binaryValue dictionary :
WebRequestBeforeSendHeadersDetails WebRequestEventDetails {sequence <HttpHeader >; };
requestHeaders dictionary :
WebRequestSendHeadersDetails WebRequestEventDetails {sequence <HttpHeader >; };
requestHeaders dictionary :
WebRequestResponseEventDetails WebRequestEventDetails {required long ;
statusCode required DOMString ;
statusLine sequence <HttpHeader >; };
responseHeaders dictionary :
WebRequestHeadersReceivedDetails WebRequestResponseEventDetails {};dictionary {
AuthChallenger DOMString ;
host long ; };
port dictionary :
WebRequestAuthRequiredDetails WebRequestResponseEventDetails {required AuthChallenger ;
challenger required boolean ;
isProxy required DOMString ;
scheme DOMString ; };
realm dictionary :
WebRequestResponseWithIpEventDetails WebRequestResponseEventDetails {required boolean ;
fromCache DOMString ; };
ip dictionary :
WebRequestBeforeRedirectDetails WebRequestResponseWithIpEventDetails {required USVString ; };
redirectUrl dictionary :
WebRequestResponseStartedDetails WebRequestResponseWithIpEventDetails {};dictionary :
WebRequestCompletedDetails WebRequestResponseWithIpEventDetails {}; // TODO: is this the right base type?dictionary :
WebRequestErrorOccurredDetails WebRequestEventDetails {required DOMString ;
error required boolean ;
fromCache DOMString ; };
ip callback =
HandlerBehaviorChangedCallback undefined (); [Exposed =Window ,IsolatedContext ]interface {
WebRequest readonly attribute WebRequestEvent ;
onBeforeRequest readonly attribute WebRequestEvent ;
onBeforeSendHeaders readonly attribute WebRequestEvent ;
onSendHeaders readonly attribute WebRequestEvent ;
onHeadersReceived readonly attribute WebRequestEvent ;
onAuthRequired readonly attribute WebRequestEvent ;
onBeforeRedirect readonly attribute WebRequestEvent ;
onResponseStarted readonly attribute WebRequestEvent ;
onCompleted readonly attribute WebRequestEvent ;
onErrorOccurred undefined handlerBehaviorChanged (optional HandlerBehaviorChangedCallback ); };
callback
Each WebRequest
has a handler map, which is a map whose keys are strings and whose values are lists of WebRequest handler configs.
Each WebRequestEvent
has:
-
An eventName, a string.
-
A webRequest, a
WebRequest
instance that it is a member of.
addListener(listener, filter, extraInfoSpec)
method steps are:
-
Call
removeListener
with listener. -
Let handlerConfig be a new WebRequest handler config with the following items:
-
Let specSet be a set containing the items in extraInfoSpec.
-
If eventName equals "beforeRequest", then:
-
If specSet is not a subset of the set « "blocking", "requestBody", "extraHeaders" », then throw a
TypeError
. -
If specSet contains "blocking", then set handlerConfig’s blocking to true.
-
If specSet contains "requestBody", then set handlerConfig’s requestsBody to true.
-
If specSet contains "extraHeaders", then set handlerConfig’s requestsAllHeaders to true.
-
-
Otherwise, if eventName equals "beforeSendHeaders", then:
-
If specSet is not a subset of the set « "blocking", "requestHeaders", "extraHeaders" », then throw a
TypeError
. -
If specSet contains "blocking", then set handlerConfig’s blocking to true.
-
If specSet contains "requestHeaders", then set handlerConfig’s requestsHeaders to true.
-
If specSet contains "extraHeaders", then set handlerConfig’s requestsAllHeaders to true.
-
-
Otherwise, if eventName equals "sendHeaders", then:
-
If specSet is not a subset of the set « "requestHeaders", "extraHeaders" », then throw a
TypeError
. -
If specSet contains "requestHeaders", then set handlerConfig’s requestsHeaders to true.
-
If specSet contains "extraHeaders", then set handlerConfig’s requestsAllHeaders to true.
-
-
Otherwise, if eventName equals "headersReceived", then:
-
If specSet is not a subset of the set « "blocking", "responseHeaders", "extraHeaders" », then throw a
TypeError
. -
If specSet contains "blocking", then set handlerConfig’s blocking to true.
-
If specSet contains "responseHeaders", then set handlerConfig’s requestsHeaders to true.
-
If specSet contains "extraHeaders", then set handlerConfig’s requestsAllHeaders to true.
-
-
Otherwise, if eventName equals "authRequired", then:
-
If specSet is not a subset of the set « "asyncBlocking", "blocking", "responseHeaders", "extraHeaders" », then throw a
TypeError
. -
If specSet contains "asyncBlocking", then set handlerConfig asyncBlocking to true.
-
If specSet contains "blocking", then set handlerConfig’s blocking to true.
-
If specSet contains "responseHeaders", then set handlerConfig’s requestsHeaders to true.
-
If specSet contains "extraHeaders", then set handlerConfig’s requestsAllHeaders to true.
-
-
Otherwise, if eventName equals "beforeRedirect", then:
-
If specSet is not a subset of the set « "responseHeaders", "extraHeaders" », then throw a
TypeError
. -
If specSet contains "responseHeaders", then set handlerConfig’s requestsHeaders to true.
-
If specSet contains "extraHeaders", then set handlerConfig’s requestsAllHeaders to true.
-
-
Otherwise, if eventName equals "responseStarted", then:
-
If specSet is not a subset of the set « "responseHeaders", "extraHeaders" », then throw a
TypeError
. -
If specSet contains "responseHeaders", then set handlerConfig’s requestsHeaders to true.
-
If specSet contains "extraHeaders", then set handlerConfig’s requestsAllHeaders to true.
-
-
Otherwise, if eventName equals "completed", then:
-
If specSet is not a subset of the set « "responseHeaders", "extraHeaders" », then throw a
TypeError
. -
If specSet contains "responseHeaders", then set handlerConfig’s requestsHeaders to true.
-
If specSet contains "extraHeaders", then set handlerConfig’s requestsAllHeaders to true.
-
-
Otherwise, if eventName equals "errorOccurred", then:
-
Let handlerMap be the handler map of this’s webRequest.
-
Append handlerConfig to handlerMap[eventName].
hasListener(listener)
method steps are:
-
Let handlerMap be the handler map of this’s webRequest.
-
For each handlerConfig in handlerMap[event name]:
-
If handlerConfig’s handler equals listener, return true.
-
-
Return false.
hasListeners()
method steps are:
-
Let handlerMap be the handler map of this’s webRequest.
-
If handlerMap[event name] is empty, return false, otherwise return true.
removeListener(listener)
method steps are:
-
Let handlerMap be the handler map of this’s webRequest.
-
Let handlerConfigs be handlerMap[event name].
-
Remove all items from handlerConfigs whose handler equals listener.
handlerBehaviorChanged(callback)
method steps are:
-
callback TODO
-
Let handlers be the result of calling lookup registered WebRequest handler configs given "beforeRequest", and request.
-
For each handler in handlers:
-
Let details be the result of calling create a WebRequestEventDetails object given request.
-
If request’s body is not null, then:
-
Let requestBody be a new
RequestBody
. -
Let body be request’s body.
TODO: serialize stream if present.
-
Switch on body’s source:
- byte sequence
-
Append a new
UploadData
withbytes
equal to the serialized body’s source to requestBody’sraw
. Blob
-
Append a new
UploadData
withbytes
equal to the serialized body’s source to requestBody’sraw
. FormData
-
-
Let formData be a new
object
. -
For each entry in body’s source’s entry list:
-
Switch on entry[1]:
-
-
Set details’s
formData
to formData.
-
-
Set details’s
requestBody
to requestBody.
-
-
If handler’s blocking flag is true, then:
-
Call handler[handler] given details in parallel.
-
Return null.
-
WebRequestEventDetails
object given a request request, run the following steps:
-
Let environmentSettingsObject be request’s client.
-
Let crossOriginIsolatedCapability be environmentSettingsObject’s cross-origin isolated capability, or false if environmentSettingsObject is null.
-
Let details be a new
WebRequestEventDetails
with the following fields:timeStamp
-
The coarsened shared current time given crossOriginIsolatedCapability.
url
-
The last element of request’s URL list.
method
-
request’s method.
initiator
-
The serialization of an origin given request’s origin.
Note: An opaque origin will result in
initiator
being set to the string "null". type
-
The result of calling get a request’s ResourceType given request.
requestId
-
request’s requestId.
frameId
-
-1
parentFrameId
-
-1
-
If environmentSettingsObject’s global object is a
Window
object, then:-
Let window be environmentSettingsObject’s global object.
-
Update the following fields of details:
documentId
-
environmentSettingsObject’s id.
documentLifecycle
-
It’s unclear if
DocumentLifecycle
values map to existing concepts. frameId
frameType
-
"fenced_frame"
if window’sfence
is non-null,"outermost_frame"
if window’stop
equals window,"sub_frame"
otherwise.
-
If window’s
parent
is not equal to window, then update the following values of details:parentDocumentId
-
The id of window’s
parent
’s active document’s relevant global object. parentFrameId
-
-
Return details.
ResourceType
given a request request, run the following steps:
-
Return request’s initiator type.
TODO: initiator type doesn’t exactly map to
ResourceType
. Define this mapping.
- status
-
301
- header list
-
« ("Location", redirectUrl) »
4.1. WebRequest handler config
A WebRequest handler config is a struct with the following items:
- handler
- filter
- requestsBody
-
a boolean
- requestsHeaders
-
a boolean
- requestsAllHeaders
-
a boolean
- blocking
-
a boolean
- asyncBlocking
-
a boolean
-
url url pattern TODO: Spec this, ideally referring to a not-yet-defined Match pattern spec.
-
Let client be request’s client.
-
If client is null, return an empty list.
-
Let controlled frame be the
HTMLControlledFrameElement
client is embedded within, or null.The ESO to CF mapping needs to be formalized.
-
If controlled frame is null, return an empty list.
-
Let valid handlers be an empty list.
-
Let handlerMap be controlled frame’s
request
’s handler map. -
For each handlerConfig in handlerMap[event name]:
-
Let filter be handlerConfig’s filter
-
Let types be filter[
types
]. -
If types is not empty and types does not contain the result of calling get a request’s ResourceType given request, then continue.
-
Let urls be filter[
urls
]. -
Let is valid url be true if urls is empty, false otherwise.
-
For each url pattern in urls:
-
If request’s URL matches a URL pattern given url pattern, set is valid url to true.
-
-
Append handlerConfig to valid handlers.
-
-
Return valid handlers.
4.2. Monkey Patches
4.2.1. Fetch
A request has an associated requestId, which is an opaque string, randomly assigned at the request’s creation.
Open questions:
-
How does WR interact with HSTS? When is the https upgrade applied? If HSTS is pre-onbeforerequest then we need to move the event later
-
How does WR interact with preload?
-
Does WR intercept worker scripts or requests from workers?
The main fetch algorithm is monkeypatched as follows:
-
Let request be fetchParam’s request.
-
Let response be null.
- Let webRequestResult be the result of calling process beforeRequest events given request.
-
If webRequestResult is not null, then:
-
If webRequestResult["
cancel
"] is true, then set response to a network error. -
Otherwise, if webRequestResult ["
redirectUrl
"] is not an empty string, then set response to the result of creating a redirect response given webRequestResult["redirectUrl
"].
-
If webRequestResult["
5. Context Menus API
enum {
ContextType ,
"all" ,
"page" ,
"frame" ,
"selection" ,
"link" ,
"editable" ,
"image" ,
"video" , };
"audio" enum {
ItemType ,
"normal" ,
"checkbox" ,
"radio" , };
"separator" dictionary {
OnClickData boolean ;
checked required boolean ;
editable long ;
frameId USVString ;
frameUrl USVString ;
linkUrl DOMString ;
mediaType required (DOMString or long );
menuItemId USVString ; (
pageUrl DOMString or long );
parentMenuId DOMString ;
selectionText USVString ;
srcUrl boolean ; };
wasChecked callback =
ContextMenusEventListener undefined (OnClickData );
data dictionary {
ContextMenusProperties boolean ;
checked sequence <ContextType >;
context DOMString ;
documentUrlPatterns boolean ;
enabled DOMString ;
parentId DOMString ;
targetUrlPatterns DOMString ;
title ItemType ;
type ContextMenusEventListener ; };
onclick dictionary :
ContextMenusCreateProperties ContextMenusProperties {DOMString ; };
id callback =
ContextMenusCallback undefined (); [Exposed =Window ,IsolatedContext ]interface { // TODO: Define the `onShow` property. // Returns the ID of the newly created menu item. (
ContextMenus DOMString or long )(
create ContextMenusCreateProperties ,
properties ContextMenusCallback ?);
callback undefined ( (
remove DOMString or long ),
menuItemId ContextMenusCallback ?);
callback undefined (
removeAll ContextMenusCallback ?);
callback undefined ( (
update DOMString or long ),
id ContextMenusProperties ,
properties ContextMenusCallback ?); };
callback
6. Usage Overview
Lorem ipsum. Insert basic info and example here.
7. Motivating Applications
This section is non-normative.
7.1. Latency-sensitive applications in virtualized sessions
In virtualized environments, users typically have a local thin client that renders a full virtual desktop. The actual desktop execution environment will be running on a remote virtualization server. If the user’s browser navigates to a latency-sensitive application (such as a video app), the rendered content will have additional latency ("lag") that makes the experience difficult or impossible for the user. This also applies for applications that record the user, such as video conferencing applications. In these latency-sensitive applications, the virtual desktop application can render the latency-sensitive content locally and overlay it on top of the rendered remote content to reduce this latency. This use case is also known as "browser content redirection."
7.2. Embedding third party web content without restriction
In a kiosk environment, applications must load content from third parties and
display that content on screens within their applications. A teacher may trigger
the navigation event, or it may be configured by an administrator such as a
shopping mall manager. The content may prohibit embedding by iframe
through
the use of X-Frame-Options and CSP. An controlled frame, however, should be able
to load all content, even content that prohibits embedding by iframe
.
7.3. Remote display and manipulation of web content
In a kiosk environment, applications must ensure that content continues to display on screens and may need to interrupt content with their own supplied behaviors. This behavior should work without local attendance by an administrator, and ideally can be managed remotely over the network. If content were to crash, for example, these applications should observe and respond to the crash by reloading the content in a fresh embedded view.
7.4. Clearing user content after each session
In some environments, someone only uses a single device for a brief time to complete their task, like ordering in a restaurant. When their task is complete, the embedder application should be able to clear all of the local user data associated with the task and then restart the embedded instance.
7.5. Monitor for idle sessions
While users interact with embedded content, the user may not explicitly end their session. This content may assume the user is present when they have actually finished or departed without completing the task. Embedder applications want to detect when users idle over their case’s threshold and begin a fresh session.
7.6. Arbitrarily blocking navigations
While displaying embedded web content that’s not authored by the embedder, pages may link to third party web content that’s disallowed. Allowing the embedder to edit elements in embedded content through arbitrary script injection into the web content can ensure navigation cannot occur to blocked pages. The embedder can also use the Controlled Frame API to capture navigation events and ensure that only pages to approved sites can be loaded within that controlled frame.
8. Security, Privacy, and Accessibility Considerations
This section is non-normative.
8.1. Security
Controlled Frame is based upon [Isolated-Web-Apps] (IWA) and integrates with core security specs
Since Controlled Frame is a particularly powerful API, using it or even having it available makes an app a target of various types of hacking. As a result, this API is limited to use in IWA which have additional safeguards in place to protect application developers and users. The Isolated Web App explainer has this to say:
"A user agent may also force an application to adopt this threat model if the developer needs access to APIs which would make the application an appealing target for XSS or server-side attacks."
Controlled Frame makes just such an appealing target, and to expose this with caution we’re opting into IWA to guard against certain attacks. Generally, IWAs provide strong security assurances that each of the resources in an application are secure both at rest and in-transit. You can read more about IWAs security and permissions in the IWA explainer and the IWAs [High-Watermark-Permissions] explainer.
Controlled Frame integrates with [Permissions-Policy] and [Permissions]. You can read more about Permissions Policy § 12. Privacy and Security and Permissions § E Security considerations (note the entry is currently sparse).
Attacking web sites could display content that doesn’t otherwise allow itself to be embedded and trick users on non-IWAs.
Planned mitigation:
-
Controlled Frame will only be available within IWAs
An IWA may embed another IWA (or itself) via Controlled Frame to manipulate our IWA policies somehow (e.g. an Controlled Frame embedded IWA may detect it’s being embedded due to the absence of the "controlled-frame" policy-controlled feature).
Planned mitigation:
-
Controlled Frame can only point to "https" schemes, excluding the "isolated-app" scheme used for IWAs
Controlled Frame could gain access to the powerful <controlledframe> element.
An IWA that’s not expected to use Controlled Frame may attempt to embed content.
Planned mitigation:
-
IWA APIs can never be delegated to cross-origin, so it will not be possible for any nested top-level navigable to access an IWA.
-
Secondly, only embedder applications and their same-origin IWA child navigables that have been granted the "controlled-frame" policy-controlled feature will have the Controlled Frame element available.
-
Same-origin child navigables without the "controlled-frame" policy-controlled feature will not be provided a Controlled Frame element. Their inner same-origin nested navigables will always not have it available.
An IWA may attempt to embed content from non-https schemes, such as 'http:' or 'isolated-app:'
Planned mitigation:
-
Controlled Frame will only work when the navigable’s "src" URL has an 'https:' scheme.
Malicious Controlled Frame could access the embedder’s running process (eg. Spectre attack)
Planned mitigation:
-
Controlled Frame will be executed in a separate process from the embedder’s process
Controlled Frame for a given "https origin" could interact or interfere with the user’s own storage data for that https origin
Planned mitigation:
-
We’re adding a Partition concept. Every Partition is a tuple of StorageKey and a separate object key.
-
Let there be a default partition with key=0 that stores "non-IWA" window and tab usage.
-
Controlled Frame will always store data in a certain StorageKey which is apart from the default partition.
-
Data written to by a given "https origin" while the user accesses that origin via an IWA Controlled Frame will be isolated from the default partition.
-
All usage will be separated between IWA and each partition will be fully isolated from each other and from default usage outside of IWA.
Malicious Controlled Frame could overwrite embedder’s stored data
-
The embedder and embedded storage user agent could overlap, and possibly multiple same-site IWA child navigables could be affected by activity in the Controlled Frame
-
if storage user agents were shared between the embedder and embedded sites, clearing data for either one could negatively impact the other
Planned mitigation:
-
IWA and Controlled Frame will always have separate storage user agents
-
A Controlled Frame should not have read or write access to other storage user agents besides its own
Malicious Controlled Frame may detect it is embedded and attempt to attack the embedder application
Planned mitigation:
-
The user agent will match the browser.
-
The Controlled Frame storage user agent will be separate from the IWA and the default storage user agents.
-
The Controlled Frame process will be separate from the IWA and the default renderer and browser processes.
-
The Controlled Frame environment will appear to be the top-most navigable:
-
window should match window.parent and window.top
-
List of policy-controlled features and their disable/enable status should match the default for a navigable
-
Ideas:
-
Investigate for potential interactions around filesystem, quota storage, and localStorage APIs
User may not be able to verify the origin of the page being viewed in the Controlled Frame
Ideas:
-
Expose the origin to the user somehow, such as adding UI at the top of a Controlled Frame that displays the origin?
-
Have the IWA specify in the manifest the origins that they expect to access?
Controlled Frame may exploit vulnerabilities in out-of-date browser engine
Already addressed with:
-
Existing browser engine auto-update mechanisms
8.2. Privacy
Controlled Frame integrates with Permissions Policy and Permissions. You can read more about Permissions Policy § 12. Privacy and Security. You can read more about Permissions § E Security considerations.
For Controlled Frame specifically, we’ve identified the following privacy considerations:
-
Users' browsing within Controlled Frame will be visible to the IWA
-
IWAs can access and exfiltrate the Controlled Frame’s session cookies (this only applies to the Controlled Frame’s session since they use a separate storage partition from the IWA and the third party origin when browsed in a tab)
-
User activity in Controlled Frame can be observed by the IWA (e.g. keyboard events can be monitored, password entry can be sniffed)
-
User file upload to Controlled Frame can be hijacked
-
User data held in the Controlled Frame’s remote server could be accessed by code implanted by the IWA
-
Users that wish to clear their session history must also do so via the IWA, which will then need to clear the associated storage user agents
-
This would be necessary since embedded storage user agents are separate from the non-embedded storage user agents for any given https origin
-
-
We plan to investigate browser UX to allow users to clear the Controlled Frame storage user agents, the following cases will be considered:
-
If a user wants to clear site data for an IWA, the associated embedded storage user agents will also be cleared
-
This is because if the IWA’s data is cleared, the app will no longer have any context for the associated embedded storage user agents and therefore will no longer be used or useful to the user or organization
-
As a result, we expect that clearing an IWA’s site data will require clearing all of the associated embedded storage user agents
-
-
A user may want to clear all site data for a given "https origin", even if that origin is stored within an IWA’s embedded storage user agent
-
We may choose to provide the ability to clear all IWA site data for that "https origin" even if that site data is held within an embedded storage user agent
-
If we chose to clear the "https origin" data, IWAs would need to prepare for the possibility that embedded storage user agents may be removed outside of their control, and this may be disruptive to the IWA and introduce complexity of implementation
-
Supporting this in the browser user agent exposes browser vendors, developers, and users to additional complexity, so we may choose not to support this approach and instead leave this up to IWA developers to implement
-
As a counter example to supporting clearing a single given "https origin"'s embedded storage user agent, consider that to our knowledge no operating system behaves that way
-
i.e. there’s no central "clear browsing data" option which clears storage for all installed browser engines, each application’s storage is treated as its own to manage
-
-
-
User wants to clear the site data for a given IWA’s Controlled Frame-embedded storage user agent for a given "https origin"
-
User wants to clear the site data for a given IWA’s Controlled Frame-embedded storage user agents for all "https origins"
-
-
An IWA will need the ability to clear the storage user agent’s Controlled Frame-embedded storage user agent for a given "https origin"
8.3. Accessibility
For Controlled Frame, we’ve identified the following accessibility considerations:
-
Browser user agents' accessibility tools and APIs should have visibility into Controlled Frame
-
IWAs should expect to provide their own accessibility tools for Controlled Frame content in order to properly integrate accessibility features for some use cases (such as "browser content redirection")
9. Acknowledgements
The following people contributed to the development of this document.