diff options
Diffstat (limited to 'devtools/server/tests/browser/browser_webextension_inspected_window.js')
-rw-r--r-- | devtools/server/tests/browser/browser_webextension_inspected_window.js | 597 |
1 files changed, 597 insertions, 0 deletions
diff --git a/devtools/server/tests/browser/browser_webextension_inspected_window.js b/devtools/server/tests/browser/browser_webextension_inspected_window.js new file mode 100644 index 0000000000..9ec089e7a4 --- /dev/null +++ b/devtools/server/tests/browser/browser_webextension_inspected_window.js @@ -0,0 +1,597 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const TEST_RELOAD_URL = `${MAIN_DOMAIN}/inspectedwindow-reload-target.sjs`; + +async function setup(pageUrl) { + const extension = ExtensionTestUtils.loadExtension({ + background() { + // This is just an empty extension used to ensure that the caller extension uuid + // actually exists. + }, + }); + + await extension.startup(); + + const fakeExtCallerInfo = { + url: WebExtensionPolicy.getByID(extension.id).getURL( + "fake-caller-script.js" + ), + lineNumber: 1, + addonId: extension.id, + }; + + const target = await addTabTarget(pageUrl); + + await target.attach(); + + const { client } = target; + const webConsoleFront = await target.getFront("console"); + const inspectedWindowFront = await target.getFront( + "webExtensionInspectedWindow" + ); + + return { + client, + target, + webConsoleFront, + inspectedWindowFront, + extension, + fakeExtCallerInfo, + }; +} + +async function teardown({ client, extension }) { + await client.close(); + DevToolsServer.destroy(); + gBrowser.removeCurrentTab(); + await extension.unload(); +} + +function waitForNextTabNavigated(target) { + return new Promise(resolve => { + target.on("tabNavigated", function tabNavigatedListener(pkt) { + if (pkt.state == "stop" && !pkt.isFrameSwitching) { + target.off("tabNavigated", tabNavigatedListener); + resolve(); + } + }); + }); +} + +// Script used as the injectedScript option in the inspectedWindow.reload tests. +function injectedScript() { + if (!window.pageScriptExecutedFirst) { + window.addEventListener( + "DOMContentLoaded", + function() { + if (document.querySelector("pre")) { + document.querySelector("pre").textContent = + "injected script executed first"; + } + }, + { once: true } + ); + } +} + +// Script evaluated in the target tab, to collect the results of injectedScript +// evaluation in the inspectedWindow.reload tests. +function collectEvalResults() { + const results = []; + let iframeDoc = document; + + while (iframeDoc) { + if (iframeDoc.querySelector("pre")) { + results.push(iframeDoc.querySelector("pre").textContent); + } + const iframe = iframeDoc.querySelector("iframe"); + iframeDoc = iframe ? iframe.contentDocument : null; + } + return JSON.stringify(results); +} + +add_task(async function test_successfull_inspectedWindowEval_result() { + const { + client, + inspectedWindowFront, + extension, + fakeExtCallerInfo, + } = await setup(MAIN_DOMAIN); + + const result = await inspectedWindowFront.eval( + fakeExtCallerInfo, + "window.location", + {} + ); + + ok(result.value, "Got a result from inspectedWindow eval"); + is( + result.value.href, + MAIN_DOMAIN, + "Got the expected window.location.href property value" + ); + is( + result.value.protocol, + "http:", + "Got the expected window.location.protocol property value" + ); + + await teardown({ client, extension }); +}); + +add_task(async function test_successfull_inspectedWindowEval_resultAsGrip() { + const { + client, + inspectedWindowFront, + extension, + fakeExtCallerInfo, + webConsoleFront, + } = await setup(MAIN_DOMAIN); + + let result = await inspectedWindowFront.eval(fakeExtCallerInfo, "window", { + evalResultAsGrip: true, + toolboxConsoleActorID: webConsoleFront.actor, + }); + + ok(result.valueGrip, "Got a result from inspectedWindow eval"); + ok(result.valueGrip.actor, "Got a object actor as expected"); + is(result.valueGrip.type, "object", "Got a value grip of type object"); + is( + result.valueGrip.class, + "Window", + "Got a value grip which is instanceof Location" + ); + + // Test invalid evalResultAsGrip request. + result = await inspectedWindowFront.eval(fakeExtCallerInfo, "window", { + evalResultAsGrip: true, + }); + + ok( + !result.value && !result.valueGrip, + "Got a null result from the invalid inspectedWindow eval call" + ); + ok( + result.exceptionInfo.isError, + "Got an API Error result from inspectedWindow eval" + ); + ok( + !result.exceptionInfo.isException, + "An error isException is false as expected" + ); + is( + result.exceptionInfo.code, + "E_PROTOCOLERROR", + "Got the expected 'code' property in the error result" + ); + is( + result.exceptionInfo.description, + "Inspector protocol error: %s - %s", + "Got the expected 'description' property in the error result" + ); + is( + result.exceptionInfo.details.length, + 2, + "The 'details' array property should contains 1 element" + ); + is( + result.exceptionInfo.details[0], + "Unexpected invalid sidebar panel expression request", + "Got the expected content in the error results's details" + ); + is( + result.exceptionInfo.details[1], + "missing toolboxConsoleActorID", + "Got the expected content in the error results's details" + ); + + await teardown({ client, extension }); +}); + +add_task(async function test_error_inspectedWindowEval_result() { + const { + client, + inspectedWindowFront, + extension, + fakeExtCallerInfo, + } = await setup(MAIN_DOMAIN); + + const result = await inspectedWindowFront.eval( + fakeExtCallerInfo, + "window", + {} + ); + + ok(!result.value, "Got a null result from inspectedWindow eval"); + ok( + result.exceptionInfo.isError, + "Got an API Error result from inspectedWindow eval" + ); + ok( + !result.exceptionInfo.isException, + "An error isException is false as expected" + ); + is( + result.exceptionInfo.code, + "E_PROTOCOLERROR", + "Got the expected 'code' property in the error result" + ); + is( + result.exceptionInfo.description, + "Inspector protocol error: %s", + "Got the expected 'description' property in the error result" + ); + is( + result.exceptionInfo.details.length, + 1, + "The 'details' array property should contains 1 element" + ); + ok( + result.exceptionInfo.details[0].includes("cyclic object value"), + "Got the expected content in the error results's details" + ); + + await teardown({ client, extension }); +}); + +add_task( + async function test_system_principal_denied_error_inspectedWindowEval_result() { + const { + client, + inspectedWindowFront, + extension, + fakeExtCallerInfo, + } = await setup("about:addons"); + + const result = await inspectedWindowFront.eval( + fakeExtCallerInfo, + "window", + {} + ); + + ok(!result.value, "Got a null result from inspectedWindow eval"); + ok( + result.exceptionInfo.isError, + "Got an API Error result from inspectedWindow eval on a system principal page" + ); + is( + result.exceptionInfo.code, + "E_PROTOCOLERROR", + "Got the expected 'code' property in the error result" + ); + is( + result.exceptionInfo.description, + "Inspector protocol error: %s", + "Got the expected 'description' property in the error result" + ); + is( + result.exceptionInfo.details.length, + 1, + "The 'details' array property should contains 1 element" + ); + is( + result.exceptionInfo.details[0], + "This target has a system principal. inspectedWindow.eval denied.", + "Got the expected content in the error results's details" + ); + + await teardown({ client, extension }); + } +); + +add_task(async function test_exception_inspectedWindowEval_result() { + const { + client, + inspectedWindowFront, + extension, + fakeExtCallerInfo, + } = await setup(MAIN_DOMAIN); + + const result = await inspectedWindowFront.eval( + fakeExtCallerInfo, + "throw Error('fake eval error');", + {} + ); + + ok(result.exceptionInfo.isException, "Got an exception as expected"); + ok(!result.value, "Got an undefined eval value"); + ok(!result.exceptionInfo.isError, "An exception should not be isError=true"); + ok( + result.exceptionInfo.value.includes("Error: fake eval error"), + "Got the expected exception message" + ); + + const expectedCallerInfo = `called from ${fakeExtCallerInfo.url}:${fakeExtCallerInfo.lineNumber}`; + ok( + result.exceptionInfo.value.includes(expectedCallerInfo), + "Got the expected caller info in the exception message" + ); + + const expectedStack = `eval code:1:7`; + ok( + result.exceptionInfo.value.includes(expectedStack), + "Got the expected stack trace in the exception message" + ); + + await teardown({ client, extension }); +}); + +add_task(async function test_exception_inspectedWindowReload() { + const { + client, + webConsoleFront, + inspectedWindowFront, + extension, + fakeExtCallerInfo, + target, + } = await setup(`${TEST_RELOAD_URL}?test=cache`); + + // Test reload with bypassCache=false. + + const waitForNoBypassCacheReload = waitForNextTabNavigated(target); + const reloadResult = await inspectedWindowFront.reload(fakeExtCallerInfo, { + ignoreCache: false, + }); + + ok( + !reloadResult, + "Got the expected undefined result from inspectedWindow reload" + ); + + await waitForNoBypassCacheReload; + + const noBypassCacheEval = await webConsoleFront.evaluateJSAsync( + "document.body.textContent" + ); + + is( + noBypassCacheEval.result, + "empty cache headers", + "Got the expected result with reload forceBypassCache=false" + ); + + // Test reload with bypassCache=true. + + const waitForForceBypassCacheReload = waitForNextTabNavigated(target); + await inspectedWindowFront.reload(fakeExtCallerInfo, { ignoreCache: true }); + + await waitForForceBypassCacheReload; + + const forceBypassCacheEval = await webConsoleFront.evaluateJSAsync( + "document.body.textContent" + ); + + is( + forceBypassCacheEval.result, + "no-cache:no-cache", + "Got the expected result with reload forceBypassCache=true" + ); + + await teardown({ client, extension }); +}); + +add_task(async function test_exception_inspectedWindowReload_customUserAgent() { + const { + client, + webConsoleFront, + inspectedWindowFront, + extension, + fakeExtCallerInfo, + target, + } = await setup(`${TEST_RELOAD_URL}?test=user-agent`); + + // Test reload with custom userAgent. + + const waitForCustomUserAgentReload = waitForNextTabNavigated(target); + await inspectedWindowFront.reload(fakeExtCallerInfo, { + userAgent: "Customized User Agent", + }); + + await waitForCustomUserAgentReload; + + const customUserAgentEval = await webConsoleFront.evaluateJSAsync( + "document.body.textContent" + ); + + is( + customUserAgentEval.result, + "Customized User Agent", + "Got the expected result on reload with a customized userAgent" + ); + + // Test reload with no custom userAgent. + + const waitForNoCustomUserAgentReload = waitForNextTabNavigated(target); + await inspectedWindowFront.reload(fakeExtCallerInfo, {}); + + await waitForNoCustomUserAgentReload; + + const noCustomUserAgentEval = await webConsoleFront.evaluateJSAsync( + "document.body.textContent" + ); + + is( + noCustomUserAgentEval.result, + window.navigator.userAgent, + "Got the expected result with reload without a customized userAgent" + ); + + await teardown({ client, extension }); +}); + +add_task(async function test_exception_inspectedWindowReload_injectedScript() { + const { + client, + webConsoleFront, + inspectedWindowFront, + extension, + fakeExtCallerInfo, + target, + } = await setup(`${TEST_RELOAD_URL}?test=injected-script&frames=3`); + + // Test reload with an injectedScript. + + const waitForInjectedScriptReload = waitForNextTabNavigated(target); + await inspectedWindowFront.reload(fakeExtCallerInfo, { + injectedScript: `new ${injectedScript}`, + }); + await waitForInjectedScriptReload; + + const injectedScriptEval = await webConsoleFront.evaluateJSAsync( + `(${collectEvalResults})()` + ); + + const expectedResult = new Array(5).fill("injected script executed first"); + + SimpleTest.isDeeply( + JSON.parse(injectedScriptEval.result), + expectedResult, + "Got the expected result on reload with an injected script" + ); + + // Test reload without an injectedScript. + + const waitForNoInjectedScriptReload = waitForNextTabNavigated(target); + await inspectedWindowFront.reload(fakeExtCallerInfo, {}); + await waitForNoInjectedScriptReload; + + const noInjectedScriptEval = await webConsoleFront.evaluateJSAsync( + `(${collectEvalResults})()` + ); + + const newExpectedResult = new Array(5).fill("injected script NOT executed"); + + SimpleTest.isDeeply( + JSON.parse(noInjectedScriptEval.result), + newExpectedResult, + "Got the expected result on reload with no injected script" + ); + + await teardown({ client, extension }); +}); + +add_task(async function test_exception_inspectedWindowReload_multiple_calls() { + const { + client, + webConsoleFront, + inspectedWindowFront, + extension, + fakeExtCallerInfo, + target, + } = await setup(`${TEST_RELOAD_URL}?test=user-agent`); + + // Test reload with custom userAgent three times (and then + // check that only the first one has affected the page reload. + + const waitForCustomUserAgentReload = waitForNextTabNavigated(target); + + inspectedWindowFront.reload(fakeExtCallerInfo, { + userAgent: "Customized User Agent 1", + }); + inspectedWindowFront.reload(fakeExtCallerInfo, { + userAgent: "Customized User Agent 2", + }); + + await waitForCustomUserAgentReload; + + const customUserAgentEval = await webConsoleFront.evaluateJSAsync( + "document.body.textContent" + ); + + is( + customUserAgentEval.result, + "Customized User Agent 1", + "Got the expected result on reload with a customized userAgent" + ); + + // Test reload with no custom userAgent. + + const waitForNoCustomUserAgentReload = waitForNextTabNavigated(target); + await inspectedWindowFront.reload(fakeExtCallerInfo, {}); + + await waitForNoCustomUserAgentReload; + + const noCustomUserAgentEval = await webConsoleFront.evaluateJSAsync( + "document.body.textContent" + ); + + is( + noCustomUserAgentEval.result, + window.navigator.userAgent, + "Got the expected result with reload without a customized userAgent" + ); + + await teardown({ client, extension }); +}); + +add_task(async function test_exception_inspectedWindowReload_stopped() { + const { + client, + webConsoleFront, + inspectedWindowFront, + extension, + fakeExtCallerInfo, + target, + } = await setup(`${TEST_RELOAD_URL}?test=injected-script&frames=3`); + + // Test reload on a page that calls window.stop() immediately during the page loading + + const waitForPageLoad = waitForNextTabNavigated(target); + await inspectedWindowFront.eval( + fakeExtCallerInfo, + "window.location += '&stop=windowStop'" + ); + + info("Load a webpage that calls 'window.stop()' while is still loading"); + await waitForPageLoad; + + info("Starting a reload with an injectedScript"); + const waitForInjectedScriptReload = waitForNextTabNavigated(target); + await inspectedWindowFront.reload(fakeExtCallerInfo, { + injectedScript: `new ${injectedScript}`, + }); + await waitForInjectedScriptReload; + + const injectedScriptEval = await webConsoleFront.evaluateJSAsync( + `(${collectEvalResults})()` + ); + + // The page should have stopped during the reload and only one injected script + // is expected. + const expectedResult = new Array(1).fill("injected script executed first"); + + SimpleTest.isDeeply( + JSON.parse(injectedScriptEval.result), + expectedResult, + "The injected script has been executed on the 'stopped' page reload" + ); + + // Reload again with no options. + + info("Reload the tab again without any reload options"); + const waitForNoInjectedScriptReload = waitForNextTabNavigated(target); + await inspectedWindowFront.reload(fakeExtCallerInfo, {}); + await waitForNoInjectedScriptReload; + + const noInjectedScriptEval = await webConsoleFront.evaluateJSAsync( + `(${collectEvalResults})()` + ); + + // The page should have stopped during the reload and no injected script should + // have been executed during this second reload (or it would mean that the previous + // customized reload was still pending and has wrongly affected the second reload) + const newExpectedResult = new Array(1).fill("injected script NOT executed"); + + SimpleTest.isDeeply( + JSON.parse(noInjectedScriptEval.result), + newExpectedResult, + "No injectedScript should have been evaluated during the second reload" + ); + + await teardown({ client, extension }); +}); + +// TODO: check eval with $0 binding once implemented (Bug 1300590) |