Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
- Manifest: netwerk/test/browser/browser.toml
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
"use strict";
const { HttpServer } = ChromeUtils.importESModule(
);
const TEST_PATH = "/browser/netwerk/test/browser/";
let gServer;
let gServerURL;
let gConnectionNumberWhenRequested = 0;
add_setup(async function () {
await SpecialPowers.pushPrefEnv({
set: [
// Enable speculative connections for hover on HTTPS
["network.predictor.enable-hover-on-ssl", true],
// Enable network debugging observations
["network.http.debug-observations", true],
],
});
// Set up local HTTP server for the target page
gServer = new HttpServer();
gServer.start(-1);
// Register handler for the target page
gServer.registerPathHandler("/target.html", handleTarget);
registerCleanupFunction(async () => {
await gServer.stop();
});
});
function handleTarget(request, response) {
response.setHeader("Content-Type", "text/html", false);
response.setHeader("Cache-Control", "no-cache", false);
// Record which connection number handled this request
gConnectionNumberWhenRequested = response._connection.number;
let body = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Target Page</title>
</head>
<body>
<h1>Target Page</h1>
<p>Connection: ${gConnectionNumberWhenRequested}</p>
</body>
</html>
`;
response.write(body);
}
/**
* Helper function to simulate hovering over a link element
*/
function hoverOverLink(browser, linkId) {
return SpecialPowers.spawn(browser, [linkId], async id => {
let link = content.document.getElementById(id);
// Dispatch mouseover event which should trigger the speculative connection
let event = new content.MouseEvent("mouseover", {
bubbles: true,
cancelable: true,
view: content,
});
link.dispatchEvent(event);
});
}
/**
* Helper function to click a link and wait for navigation
*/
async function clickLink(browser, linkId) {
let loadedPromise = BrowserTestUtils.browserLoaded(browser);
await SpecialPowers.spawn(browser, [linkId], async id => {
let link = content.document.getElementById(id);
link.click();
});
await loadedPromise;
}
add_task(async function test_link_hover_https_page() {
let speculativeConnectObserved = false;
let observer = {
QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
observe(aSubject, aTopic, aData) {
if (
aTopic == "speculative-connect-request" &&
aData.includes("localhost")
) {
info("Observed speculative connection request for: " + aData);
speculativeConnectObserved = true;
}
},
};
Services.obs.addObserver(observer, "speculative-connect-request");
// Load the test page from HTTPS example.com with the target URL pointing to our local server
const targetURL = encodeURIComponent(gServerURL + "/target.html");
const pageURL =
TEST_PATH +
"file_link_hover.sjs?target=" +
targetURL;
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: pageURL,
waitForLoad: true,
},
async function (browser) {
// Record the current connection count before hovering
let connectionCountBeforeHover = gServer.connectionNumber;
info("Connection count before hover: " + connectionCountBeforeHover);
// Wait for the link element to be available in the page
await SpecialPowers.spawn(browser, [], async () => {
await ContentTaskUtils.waitForCondition(
() => content.document.getElementById("testLink"),
"Waiting for link element to be available"
);
});
// Hover over the link to trigger speculative connection
await hoverOverLink(browser, "testLink");
// Wait for the speculative connection to be fully established
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 1000));
// Check connection count after hover
let connectionCountAfterHover = gServer.connectionNumber;
info("Connection count after hover: " + connectionCountAfterHover);
// Verify that a speculative connection request was observed
Assert.ok(
speculativeConnectObserved,
"Speculative connection should be triggered on link hover"
);
// Now click the link - it should use the speculative connection
await clickLink(browser, "testLink");
// Check connection count after click
let connectionCountAfterClick = gServer.connectionNumber;
info("Connection count after click: " + connectionCountAfterClick);
info(
"Connection number that handled the request: " +
gConnectionNumberWhenRequested
);
// Verify that exactly one NEW connection was established
Assert.equal(
connectionCountAfterClick,
connectionCountBeforeHover + 1,
"Exactly one connection should be established for the HTTP request"
);
// Verify that the request was handled by the new connection
Assert.equal(
gConnectionNumberWhenRequested,
connectionCountBeforeHover + 1,
"The HTTP request should be handled by the new connection"
);
}
);
Services.obs.removeObserver(observer, "speculative-connect-request");
});
add_task(async function test_link_hover_http_page() {
let speculativeConnectObserved = false;
let observer = {
QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
observe(aSubject, aTopic, aData) {
if (
aTopic == "speculative-connect-request" &&
aData.includes("localhost")
) {
info("Observed speculative connection request for: " + aData);
speculativeConnectObserved = true;
}
},
};
Services.obs.addObserver(observer, "speculative-connect-request");
// Load the test page from HTTP example.com with the target URL pointing to our local server
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
const targetURL = encodeURIComponent(gServerURL + "/target.html");
const pageURL =
// eslint-disable-next-line @microsoft/sdl/no-insecure-url
TEST_PATH +
"file_link_hover.sjs?target=" +
targetURL;
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: pageURL,
waitForLoad: true,
},
async function (browser) {
// Record the current connection count before hovering
// This should be 0 since we haven't connected to our server yet
let connectionCountBeforeHover = gServer.connectionNumber;
info("Connection count before hover: " + connectionCountBeforeHover);
// Wait for the link element to be available in the page
await SpecialPowers.spawn(browser, [], async () => {
await ContentTaskUtils.waitForCondition(
() => content.document.getElementById("testLink"),
"Waiting for link element to be available"
);
});
// Hover over the link to trigger speculative connection
await hoverOverLink(browser, "testLink");
// Wait for the speculative connection to be fully established
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 1000));
// Check connection count after hover
let connectionCountAfterHover = gServer.connectionNumber;
info("Connection count after hover: " + connectionCountAfterHover);
// Verify that a speculative connection request was observed
Assert.ok(
speculativeConnectObserved,
"Speculative connection should be triggered on link hover from HTTP page"
);
// Now click the link - it should use the speculative connection
await clickLink(browser, "testLink");
// Check connection count after click
let connectionCountAfterClick = gServer.connectionNumber;
info("Connection count after click: " + connectionCountAfterClick);
info(
"Connection number that handled the request: " +
gConnectionNumberWhenRequested
);
// Verify that exactly one NEW connection was established
Assert.equal(
connectionCountAfterClick,
connectionCountBeforeHover + 1,
"Exactly one connection should be established for the HTTP request"
);
// Verify that the request was handled by the new connection
Assert.equal(
gConnectionNumberWhenRequested,
connectionCountBeforeHover + 1,
"The HTTP request should be handled by the new connection"
);
}
);
Services.obs.removeObserver(observer, "speculative-connect-request");
});
add_task(async function test_link_hover_https_page_pref_disabled() {
// Disable the pref for speculative connections on HTTPS
await SpecialPowers.pushPrefEnv({
set: [["network.predictor.enable-hover-on-ssl", false]],
});
let speculativeConnectObserved = false;
let observer = {
QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
observe(aSubject, aTopic, aData) {
if (
aTopic == "speculative-connect-request" &&
aData.includes("localhost")
) {
info("Observed speculative connection request for: " + aData);
speculativeConnectObserved = true;
}
},
};
Services.obs.addObserver(observer, "speculative-connect-request");
// Load the test page from HTTPS example.com with the target URL pointing to our local server
const targetURL = encodeURIComponent(gServerURL + "/target.html");
const pageURL =
TEST_PATH +
"file_link_hover.sjs?target=" +
targetURL;
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: pageURL,
waitForLoad: true,
},
async function (browser) {
// Record the current connection count before hovering
let connectionCountBeforeHover = gServer.connectionNumber;
info("Connection count before hover: " + connectionCountBeforeHover);
// Wait for the link element to be available in the page
await SpecialPowers.spawn(browser, [], async () => {
await ContentTaskUtils.waitForCondition(
() => content.document.getElementById("testLink"),
"Waiting for link element to be available"
);
});
// Hover over the link
await hoverOverLink(browser, "testLink");
// Wait a bit to see if any speculative connection would be triggered
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await new Promise(resolve => setTimeout(resolve, 1000));
// Check connection count after hover
let connectionCountAfterHover = gServer.connectionNumber;
info("Connection count after hover: " + connectionCountAfterHover);
// Verify that NO speculative connection request was observed
Assert.ok(
!speculativeConnectObserved,
"Speculative connection should NOT be triggered on link hover from HTTPS page when pref is disabled"
);
// Now click the link - it will create a new connection
await clickLink(browser, "testLink");
// Check connection count after click
let connectionCountAfterClick = gServer.connectionNumber;
info("Connection count after click: " + connectionCountAfterClick);
// Verify that exactly one NEW connection was created (by the click, not hover)
Assert.equal(
connectionCountAfterClick,
connectionCountBeforeHover + 1,
"Exactly one connection should be established (from the click, not hover)"
);
}
);
Services.obs.removeObserver(observer, "speculative-connect-request");
// Restore the pref
await SpecialPowers.popPrefEnv();
});