Skip to content

Preloading WPT: run invalid-rules for prerender too #40667

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
'sec-fetch-mode': '{{header_or_default(sec-fetch-mode, absent)}}',
'sec-fetch-site': '{{header_or_default(sec-fetch-site, absent)}}',
'sec-fetch-dest': '{{header_or_default(sec-fetch-dest, absent)}}',
'purpose': "{{header_or_default(Purpose, absent)}}",
'sec-purpose': "{{header_or_default(Sec-Purpose, absent)}}",
'referer': '{{header_or_default(referer, absent)}}',
};
requestExecutor();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,95 +161,6 @@
*/
const DEFAULT_CONTEXT_CONFIG = new RemoteContextConfig();

/**
* This class represents a configuration for creating remote contexts. This is
* the entry-point
* for creating remote contexts, providing @see addWindow .
*/
class RemoteContextHelper {
/**
* @param {RemoteContextConfig|object} config The configuration
* for this remote context.
*/
constructor(config) {
this.config = RemoteContextConfig.ensure(config);
}

/**
* Creates a new remote context and returns a `RemoteContextWrapper` giving
* access to it.
* @private
* @param {Object} options
* @param {(url: string) => Promise<undefined>} options.executorCreator A
* function that takes a URL and causes the browser to navigate some
* window to that URL, e.g. via an iframe or a new window.
* @param {RemoteContextConfig|object} [options.extraConfig] If supplied,
* extra configuration for this remote context to be merged with
* `this`'s existing config. If it's not a `RemoteContextConfig`, it
* will be used to construct a new one.
* @returns {Promise<RemoteContextWrapper>}
*/
async createContext({
executorCreator,
extraConfig,
isWorker = false,
}) {
const config =
this.config.merged(RemoteContextConfig.ensure(extraConfig));

const origin = finalizeOrigin(config.origin);
const url = new URL(
isWorker ? WORKER_EXECUTOR_PATH : WINDOW_EXECUTOR_PATH, origin);

// UUID is needed for executor.
const uuid = token();
url.searchParams.append('uuid', uuid);

if (config.headers) {
addHeaders(url, config.headers);
}
for (const script of config.scripts) {
url.searchParams.append('script', makeAbsolute(script));
}

if (config.startOn) {
url.searchParams.append('startOn', config.startOn);
}

await executorCreator(url.href);
return new RemoteContextWrapper(new RemoteContext(uuid), this, url.href);
}

/**
* Creates a window with a remote context. @see createContext for
* @param {RemoteContextConfig|object} [extraConfig] Will be
* merged with `this`'s config.
* @param {Object} [options]
* @param {string} [options.target] Passed to `window.open` as the
* 2nd argument
* @param {string} [options.features] Passed to `window.open` as the
* 3rd argument
* @returns {Promise<RemoteContextWrapper>}
*/
addWindow(extraConfig, options) {
return this.createContext({
executorCreator: windowExecutorCreator(options),
extraConfig,
});
}

async createContextWithUrl(extraConfig) {
let saveUrl;
let wrapper = await this.createContext({
executorCreator: (url) => {saveUrl = url},
extraConfig,
});
return [wrapper, saveUrl];
}
}
// Export this class.
self.RemoteContextHelper = RemoteContextHelper;

/**
* Attaches header to the URL. See
* https://web-platform-tests.org/writing-tests/server-pipes.html#headers
Expand Down Expand Up @@ -330,6 +241,15 @@
return this.context.execute_script(fn, args);
}

/**
* Returns a JavaScript object containing a subset of the request headers.
* The specific headers that are recorded are in ./executor.sub.html.
* @returns {Object<string, string>}
*/
async getRequestHeaders() {
return this.executeScript(() => window.requestHeaders);
}

/**
* Adds a string of HTML to the executor's document.
* @param {string} html
Expand Down Expand Up @@ -530,4 +450,103 @@
}
}
}


/**
* This class represents a configuration for creating remote contexts. This is
* the entry-point
* for creating remote contexts, providing @see addWindow .
*/
class RemoteContextHelper {
/**
* The constructor to use when creating new remote context wrappers.
* Can be overridden by subclasses.
*/
static RemoteContextWrapper = RemoteContextWrapper;

/**
* @param {RemoteContextConfig|object} config The configuration
* for this remote context.
*/
constructor(config) {
this.config = RemoteContextConfig.ensure(config);
}

/**
* Creates a new remote context and returns a `RemoteContextWrapper` giving
* access to it.
* @private
* @param {Object} options
* @param {(url: string) => Promise<undefined>} options.executorCreator A
* function that takes a URL and causes the browser to navigate some
* window to that URL, e.g. via an iframe or a new window.
* @param {RemoteContextConfig|object} [options.extraConfig] If supplied,
* extra configuration for this remote context to be merged with
* `this`'s existing config. If it's not a `RemoteContextConfig`, it
* will be used to construct a new one.
* @param {Function} [options.remoteContextWrapperConstructor] If supplied,
* the constructor to use when creating the returned
* `RemoteContextWrapper`. (Useful for subclassing.)
* @returns {Promise<RemoteContextWrapper>}
*/
async createContext({
executorCreator,
extraConfig,
isWorker = false
}) {
const config =
this.config.merged(RemoteContextConfig.ensure(extraConfig));

const origin = finalizeOrigin(config.origin);
const url = new URL(
isWorker ? WORKER_EXECUTOR_PATH : WINDOW_EXECUTOR_PATH, origin);

// UUID is needed for executor.
const uuid = token();
url.searchParams.append('uuid', uuid);

if (config.headers) {
addHeaders(url, config.headers);
}
for (const script of config.scripts) {
url.searchParams.append('script', makeAbsolute(script));
}

if (config.startOn) {
url.searchParams.append('startOn', config.startOn);
}

await executorCreator(url.href);
return new this.constructor.RemoteContextWrapper(new RemoteContext(uuid), this, url.href);
}

/**
* Creates a window with a remote context. @see createContext for
* @param {RemoteContextConfig|object} [extraConfig] Will be
* merged with `this`'s config.
* @param {Object} [options]
* @param {string} [options.target] Passed to `window.open` as the
* 2nd argument
* @param {string} [options.features] Passed to `window.open` as the
* 3rd argument
* @returns {Promise<RemoteContextWrapper>}
*/
addWindow(extraConfig, options) {
return this.createContext({
executorCreator: windowExecutorCreator(options),
extraConfig,
});
}

async createContextWithUrl(extraConfig) {
let saveUrl;
let wrapper = await this.createContext({
executorCreator: (url) => {saveUrl = url},
extraConfig,
});
return [wrapper, saveUrl];
}
}
// Export this class.
self.RemoteContextHelper = RemoteContextHelper;
}
29 changes: 29 additions & 0 deletions speculation-rules/common/invalid-rules.https.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/utils.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js"></script>
<script src="../resources/utils.js"></script>

<meta name="variant" content="?prefetch">
<meta name="variant" content="?prerender">

<script>
setup(() => assertSpeculationRulesIsSupported());

const preloadingType = location.search.substring(1);

promise_test(async t => {
const rcHelper = new PreloadingRemoteContextHelper();
const referrerRC = await rcHelper.addWindow();

const destinationRC = await referrerRC.createPreloadedContext(preloadingType, {
extrasInSpeculationRule: { invalid_key: "value" }
});

await referrerRC.navigateTo(destinationRC.url);

assert_equals(await destinationRC.getPreloadingStatusFromHeaders(), "none");
}, `an unrecognized key in a ${preloadingType} rule should prevent it from being preloaded`);
</script>
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
<!DOCTYPE html>
<html>
<title>HTMLScriptElement.supports speculationrules</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<script>
test(function() {
test(() => {
assert_true(HTMLScriptElement.supports('speculationrules'));
}, 'HTMLScriptElement.supports returns true for \'speculationrules\'');

test(function() {
test(() => {
assert_false(HTMLScriptElement.supports(' speculationrules'));
assert_false(HTMLScriptElement.supports('speculationrules '));
assert_false(HTMLScriptElement.supports('Speculationrules'));
assert_false(HTMLScriptElement.supports('SpeculationRules'));
assert_false(HTMLScriptElement.supports('speculationRules'));
assert_false(HTMLScriptElement.supports('speculation-rules'));
}, 'HTMLScriptElement.supports returns false for unsupported types');

</script>
19 changes: 0 additions & 19 deletions speculation-rules/prefetch/invalid-rules.https.html

This file was deleted.

6 changes: 3 additions & 3 deletions speculation-rules/prerender/activation-start.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
promise_test(async t => {
const ACTIVATION_DELAY = 10;

const rcHelper = new RemoteContextHelper();
const rcHelper = new PrerenderingRemoteContextHelper();
const referrerRC = await rcHelper.addWindow(undefined, { features: 'noopener' });
const prerenderedRC = await addPrerenderRC(referrerRC);
const prerenderedRC = await referrerRC.createPrerenderedContext();
const iframeRC = await prerenderedRC.addIframe();

assert_equals(
Expand All @@ -36,7 +36,7 @@
// Wait ACTIVATION_DELAY ms before activation.
await new Promise(resolve => t.step_timeout(resolve, ACTIVATION_DELAY));

await activatePrerenderRC(referrerRC, prerenderedRC);
await referrerRC.navigateExpectingPrerenderingActivation(prerenderedRC);

assert_greater_than_equal(
await getActivationStart(prerenderedRC),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
setup(() => assertSpeculationRulesIsSupported());

promise_test(async t => {
const rcHelper = new RemoteContextHelper();
const rcHelper = new PrerenderingRemoteContextWrapper();
const referrerRC = await rcHelper.addWindow({origin: 'HTTPS_ORIGIN'}, { features: 'noopener' });
const prerenderedRC = await addPrerenderRC(referrerRC, {origin: 'HTTPS_REMOTE_ORIGIN'});
const prerenderedRC = await referrerRC.createPrerenderedContext({origin: 'HTTPS_REMOTE_ORIGIN'});

// Because the prerender doesn't use opt-in header, it is expected to be canceled.
// And the navigation is expected to create another page instead of activation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
promise_test(async t => {
const rcHelper = new RemoteContextHelper();
const referrerRC = await rcHelper.addWindow({origin: 'HTTPS_ORIGIN'}, { features: 'noopener' });
const prerenderedRC = await addPrerenderRC(referrerRC, {origin: 'HTTPS_REMOTE_ORIGIN', headers: [['Supports-Loading-Mode', 'credentialed-prerender']] });
const prerenderedRC = await referrerRC.createPrerenderedContext({
origin: 'HTTPS_REMOTE_ORIGIN',
headers: [['Supports-Loading-Mode', 'credentialed-prerender']]
});

await activatePrerenderRC(referrerRC, prerenderedRC);
await referrerRC.navigateExpectingPrerenderingActivation(prerenderedRC);
});
</script>
Loading