Fenced frames feature needs to be enabled to run tests. A convenient way to
do this is to define the following variable for fenced frames virtual test
suites
directories.
The <fencedframe> element has a strict requirement that it cannot directly
communicate with or reach its embedder document. The fenced frame does have
network access however, so we can use a server as a middleman to communicate
with the outer page. There are two main test patterns we use: remote execution
(recommended) and message passing (deprecated).
Remote execution
Remote execution uses the helper attachFencedFrameContext() defined in
resources/utils.js, which requires
/common/dispatcher/dispatcher.js and
/common/utils.js. This returns a fenced frame that is
wrapped with additional functionality from RemoteContext, which allows you to
perform a remote procedure call into the frame using the function
execute(function, [arguments]=[]).
This interface allows us to write an entire test in only one file, with minimal
boilerplate and an obvious control flow between all the frames on the page
(including nested fenced frames, which can be achieved with nested execute
calls).
Let's see an example of communication between the top-level frame and the fenced
frame.
promise_test(async()=>{constimportant_value="Hello";// First, create an empty fenced frame.
constframe=attachFencedFrameContext();// Next, make a function call into the frame, passing a particular string
// "Hello" as an argument. Make sure to `await` the call.
constresponse=awaitframe.execute((message_from_embedder)=>{// This code runs inside the fenced frame.
if(message_from_embedder=="Hello"){// Message that we received was expected.
return"Hello to you too");}else{// Message that we received was *not* expected, let's report an error to
// the outer page so it fails the test.
return"Unexpected message";}},[important_value]);// Assert that the returned value was what we expected.
// Keep in mind that in a less contrived example, you can perform this assert
// inside the fenced frame.
assert_equals(response,"Hello to you too","The fenced frame received the message, and said hello back to us".)},"Fenced frame and receive and send a greeting");
Some tips to keep in mind while writing tests using remote execution:
The functions attachFencedFrameContext() and attachIFrameContext()
optionally take a dictionary of configs as an argument. You can use it to
pass:
The API you want to use to generate the fenced frame urn. Either 'fledge',
'sharedstorage', or default (case-insensitive). When you use this option,
the return value becomes a promise so you must await it.For example:
Number of ad components to create the frame with. Note that this only works
with generator_api: 'fledge'. Protected Audience supports up to 20 ad
components per auction.
After creating the frame with ad components, the ad component frame won't
be created until you explicitly call a special creator from within the
frame.
This takes in an index, and, optionally, the html and attributes fields
as described above.
There is also a helper attachIFrameContext(), which does the same thing
but for iframes instead of fencedframes.
There is also a helper replaceFrameContext(frame, {options}) which will
replace an existing frame context using the same underlying element (i.e., you
can use it to test when happens when you navigate an existing frame).
Make sure to await the result of an execute call, even if it doesn't
return anything.
In order to save a global variable, you need to explicitly assign to
window.variable_name. Assigning to variable_name without declaring it
will not persist across execute calls. This is especially important for
tests with nested frames, if you want to keep a handle to the nested frame
across multiple calls.
Remember to declare the function passed to execute as async if it itself
needs to invoke any async functions, including to create nested frames.
Message passing (deprecated)
Message passing is done by using the helpers
defined in
resources/utils.js
to send a message to the server, and poll the server for a response. All
messages have a unique key associated with them so that documents that want to
receive messages can poll the server for a given message that can be identified
by a unique key.
Let's see an example of sending a message to the server that a fenced frame will
receive and respond to.
outer-page.js:
promise_test(async()=>{constimportant_message_key=token();constfenced_frame_ack_key=token();constimportant_value="Hello";// First, let's embed a new fenced frame in our test, and pass the key we
// just created into it as a parameter.
constframe_url=generateURL("resources/page-inner.html",[important_message_key,fenced_frame_ack_key]);attachFencedFrame(frame_url);// Then, let's send the message over to the fenced frame.
writeValueToServer(important_message_key,important_value);// Now that the message has been sent to the fenced frame, let's wait for its
// ACK, so that we don't exit the test before the fenced frame gets the
// message.
constresponse_from_fenced_frame=awaitnextValueFromServer(fenced_frame_ack_key);assert_equals(response_from_fenced_frame,"Hello to you too","The fenced frame received the message, and said hello back to us");},"Fenced frame and receive and send a greeting");
inner-fenced-frame.js:
asyncfunctioninit(){// Needed in order to use top-level await.
const[important_message_key,fenced_frame_ack_key]=parseKeylist();constgreeting_from_embedder=awaitnextValueFromServer(important_message_key);if(greeting_from_embedder=="Hello"){// Message that we received was expected.
writeValueToServer(fenced_frame_ack_key,"Hello to you too");}else{// Message that we received was *not* expected, let's report an error to the
// outer page so it fails the test.
writeValueToServer(fenced_frame_ack_key,"Unexpected message");}}init();
When you write a new web platform test, it will likely involve passing a new
message like the messages above, to and from the fenced frame. Keep in mind
that you may have to use a pair of keys, so that when one document writes a
message associated with one unique key, it can listen for an ACK from the
receiving document, so that it doesn't write over the message again before the
receiving document actually reads it. No two tests should ever use the same
key to communicate information to and from a fenced frame, as this will cause
server-side race conditions.