215 lines
6.1 KiB
JavaScript
215 lines
6.1 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
ChromeUtils.defineESModuleGetters(this, {
|
|
NetworkHelper:
|
|
"resource://devtools/shared/network-observer/NetworkHelper.sys.mjs",
|
|
NetworkObserver:
|
|
"resource://devtools/shared/network-observer/NetworkObserver.sys.mjs",
|
|
NetworkUtils:
|
|
"resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
|
|
});
|
|
|
|
const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
|
|
const CHROME_URL_ROOT = TEST_DIR + "/";
|
|
const URL_ROOT = CHROME_URL_ROOT.replace(
|
|
"chrome://mochitests/content/",
|
|
"https://example.com/"
|
|
);
|
|
|
|
/**
|
|
* Load the provided url in an existing browser.
|
|
* Returns a promise which will resolve when the page is loaded.
|
|
*
|
|
* @param {Browser} browser
|
|
* The browser element where the URL should be loaded.
|
|
* @param {string} url
|
|
* The URL to load in the new tab
|
|
*/
|
|
async function loadURL(browser, url) {
|
|
const loaded = BrowserTestUtils.browserLoaded(browser);
|
|
BrowserTestUtils.startLoadingURIString(browser, url);
|
|
return loaded;
|
|
}
|
|
|
|
/**
|
|
* Create a new foreground tab loading the provided url.
|
|
* Returns a promise which will resolve when the page is loaded.
|
|
*
|
|
* @param {string} url
|
|
* The URL to load in the new tab
|
|
*/
|
|
async function addTab(url) {
|
|
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
|
registerCleanupFunction(() => {
|
|
gBrowser.removeTab(tab);
|
|
});
|
|
return tab;
|
|
}
|
|
|
|
/**
|
|
* Base network event owner class implementing all mandatory callbacks and
|
|
* keeping track of which callbacks have been called.
|
|
*/
|
|
class NetworkEventOwner {
|
|
hasCacheDetails = false;
|
|
hasEventTimings = false;
|
|
hasRawHeaders = false;
|
|
hasResponseCache = false;
|
|
hasResponseContent = false;
|
|
hasResponseContentComplete = false;
|
|
hasResponseStart = false;
|
|
hasSecurityInfo = false;
|
|
hasServerTimings = false;
|
|
|
|
addCacheDetails() {
|
|
this.hasCacheDetails = true;
|
|
}
|
|
addEventTimings() {
|
|
this.hasEventTimings = true;
|
|
}
|
|
addRawHeaders() {
|
|
this.hasRawHeaders = true;
|
|
}
|
|
addResponseCache() {
|
|
this.hasResponseCache = true;
|
|
}
|
|
addResponseContent() {
|
|
this.hasResponseContent = true;
|
|
}
|
|
addResponseContentComplete() {
|
|
this.hasResponseContentComplete = true;
|
|
}
|
|
addResponseStart() {
|
|
this.hasResponseStart = true;
|
|
}
|
|
addSecurityInfo() {
|
|
this.hasSecurityInfo = true;
|
|
}
|
|
addServerTimings() {
|
|
this.hasServerTimings = true;
|
|
}
|
|
addServiceWorkerTimings() {
|
|
this.hasServiceWorkerTimings = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a simple network event owner, with mock implementations of all
|
|
* the expected APIs for a NetworkEventOwner.
|
|
*/
|
|
function createNetworkEventOwner() {
|
|
return new NetworkEventOwner();
|
|
}
|
|
|
|
/**
|
|
* Wait for network events matching the provided URL, until the count reaches
|
|
* the provided expected count.
|
|
*
|
|
* @param {string|null} expectedUrl
|
|
* The URL which should be monitored by the NetworkObserver.If set to null watch for
|
|
* all requests
|
|
* @param {number} expectedRequestsCount
|
|
* How many different events (requests) are expected.
|
|
* @returns {Promise}
|
|
* A promise which will resolve with an array of network event owners, when
|
|
* the expected event count is reached.
|
|
*/
|
|
async function waitForNetworkEvents(expectedUrl = null, expectedRequestsCount) {
|
|
const events = [];
|
|
const networkObserver = new NetworkObserver({
|
|
ignoreChannelFunction: channel =>
|
|
expectedUrl ? channel.URI.spec !== expectedUrl : false,
|
|
onNetworkEvent: () => {
|
|
info("waitForNetworkEvents received a new event");
|
|
const owner = createNetworkEventOwner();
|
|
events.push(owner);
|
|
return owner;
|
|
},
|
|
});
|
|
registerCleanupFunction(() => networkObserver.destroy());
|
|
|
|
info("Wait until the events count reaches " + expectedRequestsCount);
|
|
await TestUtils.waitForCondition(
|
|
() => events.length >= expectedRequestsCount
|
|
);
|
|
return events;
|
|
}
|
|
|
|
/**
|
|
* NetworkEventOwner class which can be used to assert the response content of
|
|
* a network event.
|
|
*/
|
|
class ResponseContentOwner extends NetworkEventOwner {
|
|
addResponseContent(response) {
|
|
super.addResponseContent();
|
|
this.compressionEncodings = response.compressionEncodings;
|
|
this.contentCharset = response.contentCharset;
|
|
this.decodedBodySize = response.decodedBodySize;
|
|
this.encodedBodySize = response.encodedBodySize;
|
|
this.encodedData = response.encodedData;
|
|
this.encoding = response.encoding;
|
|
this.isContentEncoded = response.isContentEncoded;
|
|
this.text = response.text;
|
|
}
|
|
|
|
addResponseContentComplete({ truncated }) {
|
|
super.addResponseContentComplete();
|
|
this.truncated = truncated;
|
|
}
|
|
|
|
/**
|
|
* Simple helper to decode the content of a response from a network event.
|
|
*/
|
|
async getDecodedContent() {
|
|
if (!this.isContentEncoded) {
|
|
// If the content is not encoded we can directly return the text property.
|
|
return this.text;
|
|
}
|
|
|
|
// Otherwise call the dedicated NetworkUtils decodeResponseChunks helper.
|
|
return NetworkUtils.decodeResponseChunks(this.encodedData, {
|
|
charset: this.contentCharset,
|
|
compressionEncodings: this.compressionEncodings,
|
|
encodedBodySize: this.encodedBodySize,
|
|
encoding: this.encoding,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper to compress a string using gzip.
|
|
*/
|
|
function gzipCompressString(string) {
|
|
return new Promise(resolve => {
|
|
const observer = {
|
|
onStreamComplete(loader, context, status, length, result) {
|
|
resolve(String.fromCharCode.apply(this, result));
|
|
},
|
|
};
|
|
|
|
const scs = Cc["@mozilla.org/streamConverters;1"].getService(
|
|
Ci.nsIStreamConverterService
|
|
);
|
|
const listener = Cc["@mozilla.org/network/stream-loader;1"].createInstance(
|
|
Ci.nsIStreamLoader
|
|
);
|
|
listener.init(observer);
|
|
const converter = scs.asyncConvertData(
|
|
"uncompressed",
|
|
"gzip",
|
|
listener,
|
|
null
|
|
);
|
|
const stringStream = Cc[
|
|
"@mozilla.org/io/string-input-stream;1"
|
|
].createInstance(Ci.nsIStringInputStream);
|
|
stringStream.setByteStringData(string);
|
|
converter.onStartRequest(null, null);
|
|
converter.onDataAvailable(null, stringStream, 0, string.length);
|
|
converter.onStopRequest(null, null, null);
|
|
});
|
|
}
|