/* 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 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** * Test for bug 549340. * Test for browser.js::contentAreaClick() util. * * The test opens a new browser window, then replaces browser.js methods invoked * by contentAreaClick with a mock function that tracks which methods have been * called. * Each sub-test synthesizes a mouse click event on links injected in content, * the event is collected by a click handler that ensures that contentAreaClick * correctly prevent default events, and follows the correct code path. */ const { sinon } = ChromeUtils.importESModule( "resource://testing-common/Sinon.sys.mjs" ); var gTests = [ { desc: "Simple left click", setup() {}, clean() {}, event: {}, targets: ["commonlink", "mathlink", "svgxlink", "maplink"], expectedInvokedMethods: [], preventDefault: false, }, { desc: "Ctrl/Cmd left click", setup() {}, clean() {}, event: { ctrlKey: true, metaKey: true }, targets: ["commonlink", "mathlink", "svgxlink", "maplink"], expectedInvokedMethods: ["urlSecurityCheck", "openLinkIn"], preventDefault: true, }, // The next test should just be like Alt click. { desc: "Shift+Alt left click", setup() { Services.prefs.setBoolPref("browser.altClickSave", true); }, clean() { Services.prefs.clearUserPref("browser.altClickSave"); }, event: { shiftKey: true, altKey: true }, targets: ["commonlink", "maplink"], expectedInvokedMethods: ["gatherTextUnder", "saveURL"], preventDefault: true, }, { desc: "Shift+Alt left click on XLinks", setup() { Services.prefs.setBoolPref("browser.altClickSave", true); }, clean() { Services.prefs.clearUserPref("browser.altClickSave"); }, event: { shiftKey: true, altKey: true }, targets: ["mathlink", "svgxlink"], expectedInvokedMethods: ["saveURL"], preventDefault: true, }, { desc: "Shift click", setup() {}, clean() {}, event: { shiftKey: true }, targets: ["commonlink", "mathlink", "svgxlink", "maplink"], expectedInvokedMethods: ["urlSecurityCheck", "openLinkIn"], preventDefault: true, }, { desc: "Alt click", setup() { Services.prefs.setBoolPref("browser.altClickSave", true); }, clean() { Services.prefs.clearUserPref("browser.altClickSave"); }, event: { altKey: true }, targets: ["commonlink", "maplink"], expectedInvokedMethods: ["gatherTextUnder", "saveURL"], preventDefault: true, }, { desc: "Alt click on XLinks", setup() { Services.prefs.setBoolPref("browser.altClickSave", true); }, clean() { Services.prefs.clearUserPref("browser.altClickSave"); }, event: { altKey: true }, targets: ["mathlink", "svgxlink"], expectedInvokedMethods: ["saveURL"], preventDefault: true, }, { desc: "Panel click", setup() {}, clean() {}, event: {}, targets: ["panellink"], expectedInvokedMethods: ["urlSecurityCheck", "loadURI"], preventDefault: true, }, { desc: "Simple middle click opentab", setup() {}, clean() {}, event: { button: 1 }, wantedEvent: "auxclick", targets: ["commonlink", "mathlink", "svgxlink", "maplink"], expectedInvokedMethods: ["urlSecurityCheck", "openLinkIn"], preventDefault: true, }, { desc: "Simple middle click openwin", setup() { Services.prefs.setBoolPref("browser.tabs.opentabfor.middleclick", false); }, clean() { Services.prefs.clearUserPref("browser.tabs.opentabfor.middleclick"); }, event: { button: 1 }, wantedEvent: "auxclick", targets: ["commonlink", "mathlink", "svgxlink", "maplink"], expectedInvokedMethods: ["urlSecurityCheck", "openLinkIn"], preventDefault: true, }, { desc: "Middle mouse paste", setup() { Services.prefs.setBoolPref("middlemouse.contentLoadURL", true); Services.prefs.setBoolPref("general.autoScroll", false); }, clean() { Services.prefs.clearUserPref("middlemouse.contentLoadURL"); Services.prefs.clearUserPref("general.autoScroll"); }, event: { button: 1 }, wantedEvent: "auxclick", targets: ["emptylink"], expectedInvokedMethods: ["middleMousePaste"], preventDefault: true, }, ]; // Array of method names that will be replaced in the new window. var gReplacedMethods = [ "middleMousePaste", "urlSecurityCheck", "loadURI", "gatherTextUnder", "saveURL", "openLinkIn", "getShortcutOrURIAndPostData", ]; // Returns the target object for the replaced method. function getStub(replacedMethod) { let targetObj = replacedMethod == "getShortcutOrURIAndPostData" ? UrlbarUtils : gTestWin; return targetObj[replacedMethod]; } // Reference to the new window. var gTestWin = null; // The test currently running. var gCurrentTest = null; function test() { waitForExplicitFinish(); registerCleanupFunction(function () { sinon.restore(); }); gTestWin = openDialog(location, "", "chrome,all,dialog=no", "about:blank"); whenDelayedStartupFinished(gTestWin, function () { info("Browser window opened"); waitForFocus(function () { info("Browser window focused"); waitForFocus( function () { info("Setting up browser..."); setupTestBrowserWindow(); info("Running tests..."); executeSoon(runNextTest); }, gTestWin.content, true ); }, gTestWin); }); } // Click handler used to steal click events. var gClickHandler = { handleEvent(event) { if (event.type == "click" && event.button != 0) { return; } let linkId = event.target.id || event.target.localName; let wantedEvent = gCurrentTest.wantedEvent || "click"; is( event.type, wantedEvent, `${gCurrentTest.desc}:Handler received a ${wantedEvent} event on ${linkId}` ); let isPanelClick = linkId == "panellink"; gTestWin.contentAreaClick(event, isPanelClick); let prevent = event.defaultPrevented; is( prevent, gCurrentTest.preventDefault, gCurrentTest.desc + ": event.defaultPrevented is correct (" + prevent + ")" ); // Check that all required methods have been called. for (let expectedMethod of gCurrentTest.expectedInvokedMethods) { ok( getStub(expectedMethod).called, `${gCurrentTest.desc}:${expectedMethod} should have been invoked` ); } for (let method of gReplacedMethods) { if ( getStub(method).called && !gCurrentTest.expectedInvokedMethods.includes(method) ) { ok(false, `Should have not called ${method}`); } } event.preventDefault(); event.stopPropagation(); executeSoon(runNextTest); }, }; function setupTestBrowserWindow() { // Steal click events and don't propagate them. gTestWin.addEventListener("click", gClickHandler, true); gTestWin.addEventListener("auxclick", gClickHandler, true); // Replace methods. gReplacedMethods.forEach(function (methodName) { let targetObj = methodName == "getShortcutOrURIAndPostData" ? UrlbarUtils : gTestWin; sinon.stub(targetObj, methodName).returnsArg(0); }); // Inject links in content. let doc = gTestWin.content.document; let mainDiv = doc.createElement("div"); mainDiv.innerHTML = '

Common link

' + '

Panel link

' + '

Empty link

' + '

MathML XLink

' + '

SVG XLink

' + '

'; doc.body.appendChild(mainDiv); } function runNextTest() { if (!gCurrentTest) { gCurrentTest = gTests.shift(); gCurrentTest.setup(); } if (!gCurrentTest.targets.length) { info(gCurrentTest.desc + ": cleaning up..."); gCurrentTest.clean(); if (gTests.length) { gCurrentTest = gTests.shift(); gCurrentTest.setup(); } else { finishTest(); return; } } // Move to next target. sinon.resetHistory(); let target = gCurrentTest.targets.shift(); info(gCurrentTest.desc + ": testing " + target); // Fire (aux)click event. let targetElt = gTestWin.content.document.getElementById(target); ok(targetElt, gCurrentTest.desc + ": target is valid (" + targetElt.id + ")"); EventUtils.synthesizeMouseAtCenter( targetElt, gCurrentTest.event, gTestWin.content ); } function finishTest() { info("Restoring browser..."); gTestWin.removeEventListener("click", gClickHandler, true); gTestWin.removeEventListener("auxclick", gClickHandler, true); gTestWin.close(); finish(); }