summaryrefslogtreecommitdiffstats
path: root/devtools/shared/network-observer/test/browser
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/shared/network-observer/test/browser')
-rw-r--r--devtools/shared/network-observer/test/browser/browser.ini15
-rw-r--r--devtools/shared/network-observer/test/browser/browser_networkobserver.js72
-rw-r--r--devtools/shared/network-observer/test/browser/browser_networkobserver_invalid_constructor.js49
-rw-r--r--devtools/shared/network-observer/test/browser/browser_networkobserver_override.js179
-rw-r--r--devtools/shared/network-observer/test/browser/doc_network-observer.html25
-rw-r--r--devtools/shared/network-observer/test/browser/gzipped.sjs44
-rw-r--r--devtools/shared/network-observer/test/browser/head.js92
-rw-r--r--devtools/shared/network-observer/test/browser/override.html1
-rw-r--r--devtools/shared/network-observer/test/browser/override.js2
-rw-r--r--devtools/shared/network-observer/test/browser/sjs_network-observer-test-server.sjs198
10 files changed, 677 insertions, 0 deletions
diff --git a/devtools/shared/network-observer/test/browser/browser.ini b/devtools/shared/network-observer/test/browser/browser.ini
new file mode 100644
index 0000000000..f1d09d2c1b
--- /dev/null
+++ b/devtools/shared/network-observer/test/browser/browser.ini
@@ -0,0 +1,15 @@
+[DEFAULT]
+tags = devtools
+subsuite = devtools
+support-files =
+ head.js
+ doc_network-observer.html
+ gzipped.sjs
+ override.html
+ override.js
+ sjs_network-observer-test-server.sjs
+
+[browser_networkobserver.js]
+skip-if = http3 # Bug 1829298
+[browser_networkobserver_invalid_constructor.js]
+[browser_networkobserver_override.js]
diff --git a/devtools/shared/network-observer/test/browser/browser_networkobserver.js b/devtools/shared/network-observer/test/browser/browser_networkobserver.js
new file mode 100644
index 0000000000..73e9f8510a
--- /dev/null
+++ b/devtools/shared/network-observer/test/browser/browser_networkobserver.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URL = URL_ROOT + "doc_network-observer.html";
+const REQUEST_URL =
+ URL_ROOT + `sjs_network-observer-test-server.sjs?sts=200&fmt=html`;
+
+// Check that the NetworkObserver can detect basic requests and calls the
+// onNetworkEvent callback when expected.
+add_task(async function testSingleRequest() {
+ await addTab(TEST_URL);
+
+ const onNetworkEvents = waitForNetworkEvents(REQUEST_URL, 1);
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [REQUEST_URL], _url => {
+ content.wrappedJSObject.sendRequest(_url);
+ });
+
+ const eventsCount = await onNetworkEvents;
+ is(eventsCount, 1, "Received the expected number of network events");
+});
+
+add_task(async function testMultipleRequests() {
+ await addTab(TEST_URL);
+ const EXPECTED_REQUESTS_COUNT = 5;
+
+ const onNetworkEvents = waitForNetworkEvents(
+ REQUEST_URL,
+ EXPECTED_REQUESTS_COUNT
+ );
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [REQUEST_URL, EXPECTED_REQUESTS_COUNT],
+ (_url, _count) => {
+ for (let i = 0; i < _count; i++) {
+ content.wrappedJSObject.sendRequest(_url);
+ }
+ }
+ );
+
+ const eventsCount = await onNetworkEvents;
+ is(
+ eventsCount,
+ EXPECTED_REQUESTS_COUNT,
+ "Received the expected number of network events"
+ );
+});
+
+add_task(async function testOnNetworkEventArguments() {
+ await addTab(TEST_URL);
+
+ const onNetworkEvent = new Promise(resolve => {
+ const networkObserver = new NetworkObserver({
+ ignoreChannelFunction: () => false,
+ onNetworkEvent: (...args) => {
+ resolve(args);
+ return createNetworkEventOwner();
+ },
+ });
+ registerCleanupFunction(() => networkObserver.destroy());
+ });
+
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [REQUEST_URL], _url => {
+ content.wrappedJSObject.sendRequest(_url);
+ });
+
+ const args = await onNetworkEvent;
+ is(args.length, 2, "Received two arguments");
+ is(typeof args[0], "object", "First argument is an object");
+ ok(args[1] instanceof Ci.nsIChannel, "Second argument is a channel");
+});
diff --git a/devtools/shared/network-observer/test/browser/browser_networkobserver_invalid_constructor.js b/devtools/shared/network-observer/test/browser/browser_networkobserver_invalid_constructor.js
new file mode 100644
index 0000000000..76a93d938a
--- /dev/null
+++ b/devtools/shared/network-observer/test/browser/browser_networkobserver_invalid_constructor.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check that the NetworkObserver constructor validates its arguments.
+add_task(async function testInvalidConstructorArguments() {
+ Assert.throws(
+ () => new NetworkObserver(),
+ /Expected "ignoreChannelFunction" to be a function, got undefined/,
+ "NetworkObserver constructor should throw if no argument was provided"
+ );
+
+ Assert.throws(
+ () => new NetworkObserver({}),
+ /Expected "ignoreChannelFunction" to be a function, got undefined/,
+ "NetworkObserver constructor should throw if ignoreChannelFunction was not provided"
+ );
+
+ const invalidValues = [null, true, false, 12, "str", ["arr"], { obj: "obj" }];
+ for (const invalidValue of invalidValues) {
+ Assert.throws(
+ () => new NetworkObserver({ ignoreChannelFunction: invalidValue }),
+ /Expected "ignoreChannelFunction" to be a function, got/,
+ `NetworkObserver constructor should throw if a(n) ${typeof invalidValue} was provided for ignoreChannelFunction`
+ );
+ }
+
+ const EMPTY_FN = () => {};
+ Assert.throws(
+ () => new NetworkObserver({ ignoreChannelFunction: EMPTY_FN }),
+ /Expected "onNetworkEvent" to be a function, got undefined/,
+ "NetworkObserver constructor should throw if onNetworkEvent was not provided"
+ );
+
+ // Now we will pass a function for `ignoreChannelFunction`, and will do the
+ // same tests for onNetworkEvent
+ for (const invalidValue of invalidValues) {
+ Assert.throws(
+ () =>
+ new NetworkObserver({
+ ignoreChannelFunction: EMPTY_FN,
+ onNetworkEvent: invalidValue,
+ }),
+ /Expected "onNetworkEvent" to be a function, got/,
+ `NetworkObserver constructor should throw if a(n) ${typeof invalidValue} was provided for onNetworkEvent`
+ );
+ }
+});
diff --git a/devtools/shared/network-observer/test/browser/browser_networkobserver_override.js b/devtools/shared/network-observer/test/browser/browser_networkobserver_override.js
new file mode 100644
index 0000000000..3b00c4b2e9
--- /dev/null
+++ b/devtools/shared/network-observer/test/browser/browser_networkobserver_override.js
@@ -0,0 +1,179 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URL = URL_ROOT + "doc_network-observer.html";
+const REQUEST_URL =
+ URL_ROOT + `sjs_network-observer-test-server.sjs?sts=200&fmt=html`;
+const GZIPPED_REQUEST_URL = URL_ROOT + `gzipped.sjs`;
+const OVERRIDE_FILENAME = "override.js";
+const OVERRIDE_HTML_FILENAME = "override.html";
+
+add_task(async function testLocalOverride() {
+ await addTab(TEST_URL);
+
+ let eventsCount = 0;
+ const networkObserver = new NetworkObserver({
+ ignoreChannelFunction: channel => channel.URI.spec !== REQUEST_URL,
+ onNetworkEvent: event => {
+ info("received a network event");
+ eventsCount++;
+ return createNetworkEventOwner(event);
+ },
+ });
+
+ const overrideFile = getChromeDir(getResolvedURI(gTestPath));
+ overrideFile.append(OVERRIDE_FILENAME);
+ info(" override " + REQUEST_URL + " to " + overrideFile.path + "\n");
+ networkObserver.override(REQUEST_URL, overrideFile.path);
+
+ info("Assert that request and cached request are overriden");
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [REQUEST_URL],
+ async _url => {
+ const request = await content.wrappedJSObject.fetch(_url);
+ const requestcontent = await request.text();
+ is(
+ requestcontent,
+ `"use strict";\ndocument.title = "evaluated";\n`,
+ "the request content has been overriden"
+ );
+ const secondRequest = await content.wrappedJSObject.fetch(_url);
+ const secondRequestcontent = await secondRequest.text();
+ is(
+ secondRequestcontent,
+ `"use strict";\ndocument.title = "evaluated";\n`,
+ "the cached request content has been overriden"
+ );
+ }
+ );
+
+ info("Assert that JS scripts can be overriden");
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [REQUEST_URL],
+ async _url => {
+ const script = await content.document.createElement("script");
+ const onLoad = new Promise(resolve =>
+ script.addEventListener("load", resolve, { once: true })
+ );
+ script.src = _url;
+ content.document.body.appendChild(script);
+ await onLoad;
+ is(
+ content.document.title,
+ "evaluated",
+ "The <script> tag content has been overriden and correctly evaluated"
+ );
+ }
+ );
+
+ await BrowserTestUtils.waitForCondition(() => eventsCount >= 1);
+
+ networkObserver.destroy();
+});
+
+add_task(async function testHtmlFileOverride() {
+ let eventsCount = 0;
+ const networkObserver = new NetworkObserver({
+ ignoreChannelFunction: channel => channel.URI.spec !== TEST_URL,
+ onNetworkEvent: event => {
+ info("received a network event");
+ eventsCount++;
+ return createNetworkEventOwner(event);
+ },
+ });
+
+ const overrideFile = getChromeDir(getResolvedURI(gTestPath));
+ overrideFile.append(OVERRIDE_HTML_FILENAME);
+ info(" override " + TEST_URL + " to " + overrideFile.path + "\n");
+ networkObserver.override(TEST_URL, overrideFile.path);
+
+ await addTab(TEST_URL);
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [TEST_URL],
+ async pageUrl => {
+ is(
+ content.document.documentElement.outerHTML,
+ "<html><head></head><body>Overriden!\n</body></html>",
+ "The content of the HTML has been overriden"
+ );
+ // For now, all overriden request have their location changed to an internal data: URI
+ // Bug xxx aims at keeping the original URI.
+ todo_is(
+ content.location.href,
+ pageUrl,
+ "The location of the page is still the original one"
+ );
+ }
+ );
+ await BrowserTestUtils.waitForCondition(() => eventsCount >= 1);
+ networkObserver.destroy();
+});
+
+// Exact same test, but with a gzipped request, which requires very special treatment
+add_task(async function testLocalOverrideGzipped() {
+ await addTab(TEST_URL);
+
+ let eventsCount = 0;
+ const networkObserver = new NetworkObserver({
+ ignoreChannelFunction: channel => channel.URI.spec !== GZIPPED_REQUEST_URL,
+ onNetworkEvent: event => {
+ info("received a network event");
+ eventsCount++;
+ return createNetworkEventOwner(event);
+ },
+ });
+
+ const overrideFile = getChromeDir(getResolvedURI(gTestPath));
+ overrideFile.append(OVERRIDE_FILENAME);
+ info(" override " + GZIPPED_REQUEST_URL + " to " + overrideFile.path + "\n");
+ networkObserver.override(GZIPPED_REQUEST_URL, overrideFile.path);
+
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [GZIPPED_REQUEST_URL],
+ async _url => {
+ const request = await content.wrappedJSObject.fetch(_url);
+ const requestcontent = await request.text();
+ is(
+ requestcontent,
+ `"use strict";\ndocument.title = "evaluated";\n`,
+ "the request content has been overriden"
+ );
+ const secondRequest = await content.wrappedJSObject.fetch(_url);
+ const secondRequestcontent = await secondRequest.text();
+ is(
+ secondRequestcontent,
+ `"use strict";\ndocument.title = "evaluated";\n`,
+ "the cached request content has been overriden"
+ );
+ }
+ );
+
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [GZIPPED_REQUEST_URL],
+ async _url => {
+ const script = await content.document.createElement("script");
+ const onLoad = new Promise(resolve =>
+ script.addEventListener("load", resolve, { once: true })
+ );
+ script.src = _url;
+ content.document.body.appendChild(script);
+ await onLoad;
+ is(
+ content.document.title,
+ "evaluated",
+ "The <script> tag content has been overriden and correctly evaluated"
+ );
+ }
+ );
+
+ await BrowserTestUtils.waitForCondition(() => eventsCount >= 1);
+
+ networkObserver.destroy();
+});
diff --git a/devtools/shared/network-observer/test/browser/doc_network-observer.html b/devtools/shared/network-observer/test/browser/doc_network-observer.html
new file mode 100644
index 0000000000..90cd9be69b
--- /dev/null
+++ b/devtools/shared/network-observer/test/browser/doc_network-observer.html
@@ -0,0 +1,25 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
+ <meta http-equiv="Pragma" content="no-cache" />
+ <meta http-equiv="Expires" content="0" />
+ <title>Network Observer test page</title>
+ </head>
+ <body>
+ <p>Network Observer test page</p>
+ <script type="text/javascript">
+ "use strict";
+
+ window.sendRequest = url => {
+ const xhr = new XMLHttpRequest();
+ xhr.open("GET", url, true);
+ xhr.send(null);
+ }
+ </script>
+ </body>
+</html>
diff --git a/devtools/shared/network-observer/test/browser/gzipped.sjs b/devtools/shared/network-observer/test/browser/gzipped.sjs
new file mode 100644
index 0000000000..09d0b249b1
--- /dev/null
+++ b/devtools/shared/network-observer/test/browser/gzipped.sjs
@@ -0,0 +1,44 @@
+"use strict";
+
+function gzipCompressString(string, obs) {
+ const scs = Cc["@mozilla.org/streamConverters;1"].getService(
+ Ci.nsIStreamConverterService
+ );
+ const listener = Cc["@mozilla.org/network/stream-loader;1"].createInstance(
+ Ci.nsIStreamLoader
+ );
+ listener.init(obs);
+ const converter = scs.asyncConvertData(
+ "uncompressed",
+ "gzip",
+ listener,
+ null
+ );
+ const stringStream = Cc[
+ "@mozilla.org/io/string-input-stream;1"
+ ].createInstance(Ci.nsIStringInputStream);
+ stringStream.data = string;
+ converter.onStartRequest(null, null);
+ converter.onDataAvailable(null, stringStream, 0, string.length);
+ converter.onStopRequest(null, null, null);
+}
+
+const ORIGINAL_JS_CONTENT = `console.log("original javascript content");`;
+
+function handleRequest(request, response) {
+ response.processAsync();
+
+ // Generate data
+ response.setHeader("Content-Type", "application/javascript", false);
+ response.setHeader("Content-Encoding", "gzip", false);
+
+ const observer = {
+ onStreamComplete(loader, context, status, length, result) {
+ const buffer = String.fromCharCode.apply(this, result);
+ response.setHeader("Content-Length", "" + buffer.length, false);
+ response.write(buffer);
+ response.finish();
+ },
+ };
+ gzipCompressString(ORIGINAL_JS_CONTENT, observer);
+}
diff --git a/devtools/shared/network-observer/test/browser/head.js b/devtools/shared/network-observer/test/browser/head.js
new file mode 100644
index 0000000000..ab03163d9d
--- /dev/null
+++ b/devtools/shared/network-observer/test/browser/head.js
@@ -0,0 +1,92 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+ChromeUtils.defineESModuleGetters(this, {
+ NetworkObserver:
+ "resource://devtools/shared/network-observer/NetworkObserver.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.loadURIString(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;
+}
+
+/**
+ * Create a simple network event owner, with empty implementations of all
+ * the expected APIs for a NetworkEventOwner.
+ */
+function createNetworkEventOwner(event) {
+ return {
+ addEventTimings: () => {},
+ addResponseCache: () => {},
+ addResponseContent: () => {},
+ addResponseStart: () => {},
+ addSecurityInfo: () => {},
+ addServerTimings: () => {},
+ };
+}
+
+/**
+ * Wait for network events matching the provided URL, until the count reaches
+ * the provided expected count.
+ *
+ * @param {string} expectedUrl
+ * The URL which should be monitored by the NetworkObserver.
+ * @param {number} expectedRequestsCount
+ * How many different events (requests) are expected.
+ * @returns {Promise}
+ * A promise which will resolve with the current count, when the expected
+ * count is reached.
+ */
+async function waitForNetworkEvents(expectedUrl, expectedRequestsCount) {
+ let eventsCount = 0;
+ const networkObserver = new NetworkObserver({
+ ignoreChannelFunction: channel => channel.URI.spec !== expectedUrl,
+ onNetworkEvent: event => {
+ info("waitForNetworkEvents received a new event");
+ eventsCount++;
+ return createNetworkEventOwner(event);
+ },
+ });
+ registerCleanupFunction(() => networkObserver.destroy());
+
+ info("Wait until the events count reaches " + expectedRequestsCount);
+ await BrowserTestUtils.waitForCondition(
+ () => eventsCount >= expectedRequestsCount
+ );
+ return eventsCount;
+}
diff --git a/devtools/shared/network-observer/test/browser/override.html b/devtools/shared/network-observer/test/browser/override.html
new file mode 100644
index 0000000000..0e3878e313
--- /dev/null
+++ b/devtools/shared/network-observer/test/browser/override.html
@@ -0,0 +1 @@
+<html><head></head><body>Overriden!</body></html>
diff --git a/devtools/shared/network-observer/test/browser/override.js b/devtools/shared/network-observer/test/browser/override.js
new file mode 100644
index 0000000000..7b000fcd0f
--- /dev/null
+++ b/devtools/shared/network-observer/test/browser/override.js
@@ -0,0 +1,2 @@
+"use strict";
+document.title = "evaluated";
diff --git a/devtools/shared/network-observer/test/browser/sjs_network-observer-test-server.sjs b/devtools/shared/network-observer/test/browser/sjs_network-observer-test-server.sjs
new file mode 100644
index 0000000000..918d6e8447
--- /dev/null
+++ b/devtools/shared/network-observer/test/browser/sjs_network-observer-test-server.sjs
@@ -0,0 +1,198 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Cu.importGlobalProperties(["TextEncoder"]);
+
+// Simple server which can handle several response types and states.
+// Trimmed down from devtools/client/netmonitor/test/sjs_content-type-test-server.sjs
+// Additional features can be ported if needed.
+function handleRequest(request, response) {
+ response.processAsync();
+
+ const params = request.queryString.split("&");
+ const format = (params.filter(s => s.includes("fmt="))[0] || "").split(
+ "="
+ )[1];
+ const status =
+ (params.filter(s => s.includes("sts="))[0] || "").split("=")[1] || 200;
+
+ const cacheExpire = 60; // seconds
+
+ function setCacheHeaders() {
+ if (status != 304) {
+ response.setHeader(
+ "Cache-Control",
+ "no-cache, no-store, must-revalidate"
+ );
+ response.setHeader("Pragma", "no-cache");
+ response.setHeader("Expires", "0");
+ return;
+ }
+
+ response.setHeader("Expires", Date(Date.now() + cacheExpire * 1000), false);
+ }
+
+ let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+
+ timer.initWithCallback(
+ // eslint-disable-next-line complexity
+ () => {
+ // to avoid garbage collection
+ timer = null;
+ switch (format) {
+ case "txt": {
+ response.setStatusLine(request.httpVersion, status, "DA DA DA");
+ response.setHeader("Content-Type", "text/plain", false);
+ setCacheHeaders();
+
+ function convertToUtf8(str) {
+ return String.fromCharCode(...new TextEncoder().encode(str));
+ }
+
+ // This script must be evaluated as UTF-8 for this to write out the
+ // bytes of the string in UTF-8. If it's evaluated as Latin-1, the
+ // written bytes will be the result of UTF-8-encoding this string
+ // *twice*.
+ const data = "Братан, ты вообще качаешься?";
+ const stringOfUtf8Bytes = convertToUtf8(data);
+ response.write(stringOfUtf8Bytes);
+
+ response.finish();
+ break;
+ }
+ case "xml": {
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader("Content-Type", "text/xml; charset=utf-8", false);
+ setCacheHeaders();
+ response.write("<label value='greeting'>Hello XML!</label>");
+ response.finish();
+ break;
+ }
+ case "html": {
+ const content = (
+ params.filter(s => s.includes("res="))[0] || ""
+ ).split("=")[1];
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader("Content-Type", "text/html; charset=utf-8", false);
+ setCacheHeaders();
+ response.write(content || "<p>Hello HTML!</p>");
+ response.finish();
+ break;
+ }
+ case "xhtml": {
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader(
+ "Content-Type",
+ "application/xhtml+xml; charset=utf-8",
+ false
+ );
+ setCacheHeaders();
+ response.write("<p>Hello XHTML!</p>");
+ response.finish();
+ break;
+ }
+ case "html-long": {
+ const str = new Array(102400 /* 100 KB in bytes */).join(".");
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader("Content-Type", "text/html; charset=utf-8", false);
+ setCacheHeaders();
+ response.write("<p>" + str + "</p>");
+ response.finish();
+ break;
+ }
+ case "css": {
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader("Content-Type", "text/css; charset=utf-8", false);
+ setCacheHeaders();
+ response.write("body:pre { content: 'Hello CSS!' }");
+ response.finish();
+ break;
+ }
+ case "js": {
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader(
+ "Content-Type",
+ "application/javascript; charset=utf-8",
+ false
+ );
+ setCacheHeaders();
+ response.write("function() { return 'Hello JS!'; }");
+ response.finish();
+ break;
+ }
+ case "json": {
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader(
+ "Content-Type",
+ "application/json; charset=utf-8",
+ false
+ );
+ setCacheHeaders();
+ response.write('{ "greeting": "Hello JSON!" }');
+ response.finish();
+ break;
+ }
+
+ case "font": {
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader("Content-Type", "font/woff", false);
+ setCacheHeaders();
+ response.finish();
+ break;
+ }
+ case "image": {
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader("Content-Type", "image/png", false);
+ setCacheHeaders();
+ response.finish();
+ break;
+ }
+ case "application-ogg": {
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader("Content-Type", "application/ogg", false);
+ setCacheHeaders();
+ response.finish();
+ break;
+ }
+ case "audio": {
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader("Content-Type", "audio/ogg", false);
+ setCacheHeaders();
+ response.finish();
+ break;
+ }
+ case "video": {
+ response.setStatusLine(request.httpVersion, status, "OK");
+ response.setHeader("Content-Type", "video/webm", false);
+ setCacheHeaders();
+ response.finish();
+ break;
+ }
+ case "ws": {
+ response.setStatusLine(
+ request.httpVersion,
+ 101,
+ "Switching Protocols"
+ );
+ response.setHeader("Connection", "upgrade", false);
+ response.setHeader("Upgrade", "websocket", false);
+ setCacheHeaders();
+ response.finish();
+ break;
+ }
+ default: {
+ response.setStatusLine(request.httpVersion, 404, "Not Found");
+ response.setHeader("Content-Type", "text/html; charset=utf-8", false);
+ setCacheHeaders();
+ response.write("<blink>Not Found</blink>");
+ response.finish();
+ break;
+ }
+ }
+ },
+ 10,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ ); // Make sure this request takes a few ms.
+}