/** * This test checks the Secure Context state of documents for various * permutations of document URI types and loading methods. * * The hierarchy that is tested is: * * creator-doc > createe-doc * * The creator-doc is one of: * * http: * https: * * The createe-doc is loaded as either a: * * popup * iframe * sandboxed-iframe * * into which we load and test: * * http: * https: * blob: * javascript: * about:blank * initial about:blank * srcdoc * * TODO once web-platform-tests supports it: * - test http://localhost * - test file: */ setup({explicit_done:true}); const host_and_dirname = location.host + location.pathname.substr(0, location.pathname.lastIndexOf("/") + 1); // Flags to indicate where document types should be loaded for testing: const eLoadInPopup = (1 << 0); const eLoadInUnsandboxedIframe = (1 << 1); const eLoadInSandboxedIframe = (1 << 2); const eLoadInEverything = eLoadInPopup | eLoadInUnsandboxedIframe | eLoadInSandboxedIframe; // Flags indicating if a document type is expected to be a Secure Context: const eSecureNo = 1; const eSecureIfNewWindow = 2; const eSecureIfCreatorSecure = 3; // Flags indicating how the result of a test is obtained: const eResultFromPostMessage = 1; const eResultFromExaminationOnLoad = 2; const eResultFromExaminationSync = 3; const loadTypes = [ new LoadType("an http: URI", eLoadInEverything, http_dir + "postMessage-helper.html", eSecureNo, eResultFromPostMessage), new LoadType("an https: URI", eLoadInEverything, https_dir + "postMessage-helper.https.html", eSecureIfNewWindow, eResultFromPostMessage), new LoadType("a blob: URI", eLoadInEverything, URL.createObjectURL(new Blob([""], {type: "text/html"})), eSecureIfCreatorSecure, eResultFromPostMessage), new LoadType("a srcdoc", // popup not relevant: eLoadInUnsandboxedIframe | eLoadInSandboxedIframe, "", eSecureIfNewWindow, eResultFromPostMessage), new LoadType("a javascript: URI", // can't load in sandbox: eLoadInUnsandboxedIframe | eLoadInPopup, "javascript:(opener||parent).postMessage(isSecureContext, '*')", eSecureIfCreatorSecure, eResultFromPostMessage), new LoadType("about:blank", // can't obtain state if sandboxed: eLoadInUnsandboxedIframe | eLoadInPopup, "about:blank", eSecureIfCreatorSecure, eResultFromExaminationOnLoad), new LoadType("initial about:blank", // can't obtain state if sandboxed: eLoadInUnsandboxedIframe | eLoadInPopup, "about:blank", // we don't wait for this to load, so whatever eSecureIfCreatorSecure, eResultFromExaminationSync), new LoadType("a data: URL", // can't load in a top-level browsing context eLoadInUnsandboxedIframe | eLoadInSandboxedIframe, "data:text/html,", eSecureIfCreatorSecure, eResultFromPostMessage), ]; const loadTargets = [ new LoadTarget("an iframe", eLoadInUnsandboxedIframe), new LoadTarget("a sandboxed iframe", eLoadInSandboxedIframe), new LoadTarget("a popup", eLoadInPopup), ]; function LoadType(description, loadInFlags, uri, expectedSecureFlag, resultFrom) { this.desc = description; this.loadInFlags = loadInFlags; this.uri = uri; this.expectedSecureFlag = expectedSecureFlag; this.resultFrom = resultFrom; } function LoadTarget(description, loadInFlag) { this.desc = description; this.loadInFlag = loadInFlag; } LoadTarget.prototype.open = function(loadType) { let loadTarget = this; this.currentTest.step(function() { assert_true((loadTarget.loadInFlag & loadType.loadInFlags) != 0, loadType.desc + " cannot be tested in " + loadTarget.desc); }); if (this.loadInFlag == eLoadInUnsandboxedIframe) { let iframe = document.createElement("iframe"); document.body.appendChild(iframe); iframe[loadType.desc == "a srcdoc" ? "srcdoc" : "src"] = loadType.uri; return iframe; } if (this.loadInFlag == eLoadInSandboxedIframe) { let iframe = document.body.appendChild(document.createElement("iframe")); iframe.setAttribute("sandbox", "allow-scripts"); iframe[loadType.desc == "a srcdoc" ? "srcdoc" : "src"] = loadType.uri; return iframe; } if (this.loadInFlag == eLoadInPopup) { return window.open(loadType.uri); } this.currentTest.step(function() { assert_unreached("Unknown load type flag: " + loadInFlags); }); return null; } LoadTarget.prototype.close = function(domTarget) { if (this.loadInFlag == eLoadInUnsandboxedIframe || this.loadInFlag == eLoadInSandboxedIframe) { domTarget.remove(); return; } if (this.loadInFlag == eLoadInPopup) { domTarget.close(); return; } this.currentTest.step(function() { assert_unreached("Unknown load type flag: " + loadInFlags); }); } LoadTarget.prototype.load_and_get_result_for = function(loadType) { if (!(loadType.loadInFlags & this.loadInFlag)) { return Promise.reject("not applicable"); } if (!(this.loadInFlag & eLoadInPopup) && location.protocol == "https:" && loadType.uri.substr(0,5) == "http:") { // Mixed content blocker will prevent this load return Promise.reject("not applicable"); } this.currentTest = async_test("Test Window.isSecureContext in " + this.desc + " loading " + loadType.desc) if (loadType.resultFrom == eResultFromExaminationSync) { let domTarget = this.open(loadType); let isFrame = domTarget instanceof HTMLIFrameElement; let result = !isFrame ? domTarget.isSecureContext : domTarget.contentWindow.isSecureContext; this.close(domTarget); return Promise.resolve({ result: result, isFrame: isFrame}); } let target = this; if (loadType.resultFrom == eResultFromExaminationOnLoad) { return new Promise(function(resolve, reject) { function handleLoad(event) { clearTimeout(timer); let isFrame = domTarget instanceof HTMLIFrameElement; let result = !isFrame ? domTarget.isSecureContext : domTarget.contentWindow.isSecureContext; domTarget.removeEventListener("load", handleLoad); target.close(domTarget); resolve({ result: result, isFrame: isFrame}); } let domTarget = target.open(loadType); domTarget.addEventListener("load", handleLoad, false); // Some browsers don't fire `load` events for `about:blank`. That's weird, but it also // isn't what we're testing here. let timer = setTimeout(handleLoad, 500); }); } if (loadType.resultFrom == eResultFromPostMessage) { return new Promise(function(resolve, reject) { function handleMessage(event) { let isFrame = domTarget instanceof HTMLIFrameElement; window.removeEventListener("message", handleMessage); target.close(domTarget); resolve({ result: event.data, isFrame: isFrame}); } window.addEventListener("message", handleMessage, false); let domTarget = target.open(loadType); }); } return Promise.reject("unexpected 'result from' type"); } let current_type_index = -1; let current_target_index = 0; function run_next_test() { current_type_index++; if (current_type_index >= loadTypes.length) { current_type_index = 0; current_target_index++; if (current_target_index >= loadTargets.length) { done(); return; // all test permutations complete } } let loadTarget = loadTargets[current_target_index]; let loadType = loadTypes[current_type_index]; loadTarget.load_and_get_result_for(loadType).then( function(value) { run_next_test_soon(); loadTarget.currentTest.step(function() { // If the new context is always non-secure, the assertion is straightforward. if (loadType.expectedSecureFlag == eSecureNo) { assert_false(value.result, loadType.desc + " in " + loadTarget.desc + " should not create a Secure Context"); // If the new context is always secure if opened in a new window, and it's // been opened in a new window, the assertion is likewise straightforward. } else if (loadType.expectedSecureFlag == eSecureIfNewWindow && !value.isFrame) { assert_true(value.result, loadType.desc + " in " + loadTarget.desc + " should create a secure context regardless of its creator's state."); // Otherwise, we're either dealing with a context that's secure if and only // if its creator context (e.g. this window) is secure. } else if ((loadType.expectedSecureFlag == eSecureIfNewWindow && value.isFrame) || (loadType.expectedSecureFlag == eSecureIfCreatorSecure)) { if (!window.isSecureContext) { assert_false(value.result, loadType.desc + " in " + loadTarget.desc + " should not create a Secure Context when its creator is not a Secure Context."); } else { assert_true(value.result, loadType.desc + " in " + loadTarget.desc + " should create a Secure Context when its creator is a Secure Context"); } } else { assert_unreached(loadType.desc + " - unknown expected secure flag: " + expectedSecureFlag); } loadTarget.currentTest.done(); }); }, function(failReason) { run_next_test_soon(); if (failReason == "not applicable") { return; } loadTarget.currentTest.step(function() { assert_unreached(loadType.desc + " - got unexpected rejected promise"); }); } ); } function run_next_test_soon() { setTimeout(run_next_test, 0); } function begin() { test(function() { if (location.protocol == "http:") { assert_false(isSecureContext, "http: creator should not be a Secure Context"); } else if (location.protocol == "https:") { assert_true(isSecureContext, "https: creator should be a Secure Context"); } else { assert_unreached("Unknown location.protocol"); } }); run_next_test(); }