Skip to content

Commit

Permalink
Support adding iframes with srcdoc attributes in remote context helper.
Browse files Browse the repository at this point in the history
This makes executor-window.py a bit smarter
- have it pass the UUID when starting the executor.
- move extra-script handling for the window executor into the python
- move extracting `startOn` param into the python

This makes it possible to just put the whole thing into the srcdoc attribute (after fixing some URL generating code to handle srcdocs).


Bug: 1491597
Change-Id: Ib4825ad7f117fd9db04a4203315989ec1c26412c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4979845
Reviewed-by: Domenic Denicola <domenic@chromium.org>
Commit-Queue: Fergal Daly <fergal@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1216784}
  • Loading branch information
fergald authored and Chromium LUCI CQ committed Oct 30, 2023
1 parent 6ef2fe4 commit 4d35a57
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,26 @@
// Define a universal message passing API. It works cross-origin and across
// browsing context groups.
const dispatcher_path = "/common/dispatcher/dispatcher.py";
const dispatcher_url = new URL(dispatcher_path, location.href).href;

// Finds the nearest ancestor window that has a non srcdoc location. This should
// give us a usable location for constructing further URLs.
function findLocationFromAncestors(w) {
if (w.location.href == 'about:srcdoc') {
return findLocationFromAncestors(w.parent);
}
return w.location;
}

// Handles differences between workers vs frames (src vs srcdoc).
function findLocation() {
if (location.href == 'about:srcdoc') {
return findLocationFromAncestors(window.parent);
}
return location;
}

const dispatcherLocation = findLocation();
const dispatcher_url = new URL(dispatcher_path, dispatcherLocation).href;

// Return a promise, limiting the number of concurrent accesses to a shared
// resources to |max_concurrent_access|.
Expand Down Expand Up @@ -138,7 +157,7 @@ const cacheableShowRequestHeaders = function(origin, uuid) {
// protocol: (optional) Sets the returned URL's `protocol` property.
// }
function remoteExecutorUrl(uuid, options) {
const url = new URL("/common/dispatcher/remote-executor.html", location);
const url = new URL("/common/dispatcher/remote-executor.html", dispatcherLocation);
url.searchParams.set("uuid", uuid);

if (options?.host) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// META: title=RemoteContextWrapper addIframe with srcdoc and startOn
// META: script=/common/dispatcher/dispatcher.js
// META: script=/common/get-host-info.sub.js
// META: script=/common/utils.js
// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
// META: script=./resources/test-helper.js

'use strict';

promise_test(async t => {
const rcHelper = new RemoteContextHelper();

const main = await rcHelper.addWindow();

const iframe = await main.addIframeSrcdoc(
/*extraConfig=*/ {startOn: 'pageshow'});

await assertSimplestScriptRuns(iframe);
await assert_equals(
await iframe.executeScript(() => {
return executorStartEvent.type;
}),
'pageshow');
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// META: title=RemoteContextWrapper addIframe with srcdoc
// META: script=/common/dispatcher/dispatcher.js
// META: script=/common/get-host-info.sub.js
// META: script=/common/utils.js
// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
// META: script=./resources/test-helper.js

'use strict';

// This tests that arguments passed to the constructor are respected.
promise_test(async t => {
const rcHelper = new RemoteContextHelper();

const main = await rcHelper.addWindow();

const iframe = await main.addIframeSrcdoc(
/*extraConfig=*/ {scripts: ['./resources/test-script.js']},
/*attributes=*/ {id: 'test-id'},
);

await assertSimplestScriptRuns(iframe);
await assertFunctionRuns(iframe, () => testFunction(), 'testFunction exists');

assert_equals(
await main.executeScript(() => document.getElementById('test-id').id),
'test-id', 'verify id');
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ function addScripts(urls) {
return Promise.all(urls.map(addScript));
}

function startExecutor() {
const params = new URLSearchParams(location.search);
addScripts(params.getAll('script'));
const uuid = params.get('uuid');
function startExecutor(uuid) {
executor = new Executor(uuid);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,14 @@

let executorStartEvent = null;

function requestExecutor() {
const params = new URLSearchParams(location.search);
const startOn = params.get('startOn');

function requestExecutor(uuid, startOn) {
if (startOn) {
addEventListener(startOn, (e) => {
executorStartEvent = e;
startExecutor();
startExecutor(uuid);
});
} else {
startExecutor();
startExecutor(uuid);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import html
import json
from urllib import parse

def main(request, response):
initRequestHeaders = ""
Expand All @@ -11,17 +13,28 @@ def main(request, response):
status = int(request.GET.first(b"status"))
else:
status = 200
query = parse.parse_qs(request.url_parts.query)
scripts = []
for script in query.get("script", []):
scripts.append(f"<script src='{html.escape(script)}'></script>")
scripts_s = "\n".join(scripts)

uuid = query.get("uuid")[0]

start_on = query.get("startOn")
start_on_s = f"'{start_on[0]}'" if start_on else "null"

return (status, [("Content-Type", "text/html")], f"""
<!DOCTYPE HTML>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="./executor-common.js"></script>
<script src="./executor-window.js"></script>
{scripts_s}
<body>
<script>
window.__requestHeaders = new Headers();
{initRequestHeaders}
requestExecutor();
requestExecutor("{uuid}", {start_on_s});
</script>
""")
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ function addScript(url) {
importScripts(url);
}

startExecutor();
const params = new URLSearchParams(location.search);
addScripts(params.getAll('script'));

startExecutor(params.get('uuid'));
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,17 @@
};
}

function iframeSrcdocExecutorCreator(remoteContextWrapper, attributes) {
return async (url) => {
// `url` points to the content needed to run an `Executor` in the frame.
// So we download the content and pass it via the `srcdoc` attribute,
// setting the iframe's `src` to `undefined`.
attributes['srcdoc'] = await fetch(url).then(r => r.text());
elementExecutorCreator(
remoteContextWrapper, 'iframe', attributes)(undefined);
};
}

function workerExecutorCreator() {
return url => {
new Worker(url);
Expand Down Expand Up @@ -370,7 +381,7 @@
}

/**
* Adds an iframe to the current document.
* Adds an iframe with `src` attribute to the current document.
* @param {RemoteContextConfig} [extraConfig]
* @param {[string, string][]} [attributes] A list of pairs of strings
* of attribute name and value these will be set on the iframe element
Expand All @@ -384,6 +395,21 @@
});
}

/**
* Adds an iframe with `srcdoc` attribute to the current document
* @param {RemoteContextConfig} [extraConfig]
* @param {[string, string][]} [attributes] A list of pairs of strings
* of attribute name and value these will be set on the iframe element
* when added to the document.
* @returns {Promise<RemoteContextWrapper>} The remote context.
*/
addIframeSrcdoc(extraConfig, attributes = {}) {
return this.helper.createContext({
executorCreator: iframeSrcdocExecutorCreator(this, attributes),
extraConfig,
});
}

/**
* Adds a dedicated worker to the current document.
* @param {RemoteContextConfig} [extraConfig]
Expand Down

0 comments on commit 4d35a57

Please sign in to comment.