From 08e85322aef65f3a1f6176b09eb604b5163eba9f Mon Sep 17 00:00:00 2001 From: John Bieling Date: Wed, 1 Feb 2023 22:43:39 +0000 Subject: [PATCH] Bug 1812530 - Fix openInBrowser context menu. r=mkmelin Differential Revision: https://phabricator.services.mozilla.com/D167916 --HG-- extra : moz-landing-system : lando --- mail/base/content/widgets/browserPopups.js | 13 +- .../extensions/test/browser/browser.ini | 3 + .../test/browser/browser_ext_bug1812530.js | 156 ++++++++++++++++++ .../extensions/test/browser/data/content.html | 2 +- 4 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 mail/components/extensions/test/browser/browser_ext_bug1812530.js diff --git a/mail/base/content/widgets/browserPopups.js b/mail/base/content/widgets/browserPopups.js index 6a70b8f337..f80cdbd6f6 100644 --- a/mail/base/content/widgets/browserPopups.js +++ b/mail/base/content/widgets/browserPopups.js @@ -527,18 +527,14 @@ class nsContextMenu { this.showItem("browserContext-stop", notOnSpecialItem); this.showItem("browserContext-reload", notOnSpecialItem); - let loadedProtocol = ""; - if (this.target && this.target.ownerGlobal?.top.location) { - loadedProtocol = this.target.ownerGlobal?.top.location.protocol; - } - // Only show open in browser if we're not on a special item and we're not // on an about: or chrome: protocol - for these protocols the browser is // unlikely to show the same thing as we do (if at all), so therefore don't // offer the option. this.showItem( "browserContext-openInBrowser", - notOnSpecialItem && ["http:", "https:"].includes(loadedProtocol) + notOnSpecialItem && + ["http", "https"].includes(this.contentData?.documentURIObject?.scheme) ); // Only show browserContext-openLinkInBrowser if we're on a link and it isn't @@ -886,7 +882,10 @@ class nsContextMenu { } openInBrowser() { - let url = this.target.ownerGlobal?.top.location.href; + let url = this.contentData?.documentURIObject?.spec; + if (!url) { + return; + } PlacesUtils.history .insert({ url, diff --git a/mail/components/extensions/test/browser/browser.ini b/mail/components/extensions/test/browser/browser.ini index a6d0cc37a0..ab932154b9 100644 --- a/mail/components/extensions/test/browser/browser.ini +++ b/mail/components/extensions/test/browser/browser.ini @@ -16,6 +16,9 @@ tags = webextensions [browser_ext_addressBooksUI.js] tags = addrbook +[browser_ext_bug1812530.js] +support-files = data/content.html +tags = contextmenu [browser_ext_browserAction.js] skip-if = true # TODO [browser_ext_browserAction_popup_click.js] diff --git a/mail/components/extensions/test/browser/browser_ext_bug1812530.js b/mail/components/extensions/test/browser/browser_ext_bug1812530.js new file mode 100644 index 0000000000..8e24fec8fb --- /dev/null +++ b/mail/components/extensions/test/browser/browser_ext_bug1812530.js @@ -0,0 +1,156 @@ +/* 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/. * + */ + +// Load subscript shared with all menu tests. +Services.scriptloader.loadSubScript( + new URL("head_menus.js", gTestPath).href, + this +); + +let { MockRegistrar } = ChromeUtils.importESModule( + "resource://testing-common/MockRegistrar.sys.mjs" +); + +/** @implements {nsIExternalProtocolService} */ +let mockExternalProtocolService = { + _loadedURLs: [], + externalProtocolHandlerExists(protocolScheme) {}, + getApplicationDescription(scheme) {}, + getProtocolHandlerInfo(protocolScheme) {}, + getProtocolHandlerInfoFromOS(protocolScheme, found) {}, + isExposedProtocol(protocolScheme) {}, + loadURI(uri, windowContext) { + this._loadedURLs.push(uri.spec); + }, + setProtocolHandlerDefaults(handlerInfo, osHandlerExists) {}, + urlLoaded(url) { + return this._loadedURLs.includes(url); + }, + QueryInterface: ChromeUtils.generateQI(["nsIExternalProtocolService"]), +}; + +let mockExternalProtocolServiceCID = MockRegistrar.register( + "@mozilla.org/uriloader/external-protocol-service;1", + mockExternalProtocolService +); + +registerCleanupFunction(() => { + MockRegistrar.unregister(mockExternalProtocolServiceCID); +}); + +const subtest_clickOpenInBrowserContextMenu = async (extension, getBrowser) => { + async function contextClick(elementId, browser) { + if ( + browser.webProgress?.isLoadingDocument || + !browser.currentURI || + browser.currentURI?.spec == "about:blank" + ) { + await BrowserTestUtils.browserLoaded( + browser, + undefined, + url => url != "about:blank" + ); + } + + let menuId = browser.getAttribute("context"); + let menu = browser.ownerDocument.getElementById(menuId); + let hiddenPromise = BrowserTestUtils.waitForEvent(menu, "popuphidden"); + await rightClickOnContent(menu, elementId, browser); + Assert.ok( + menu.querySelector("#browserContext-openInBrowser"), + "menu item should exist" + ); + menu.activateItem(menu.querySelector("#browserContext-openInBrowser")); + await hiddenPromise; + } + + await extension.startup(); + + // Wait for click on #description + { + let { elementId, url } = await extension.awaitMessage("contextClick"); + Assert.equal( + "#description", + elementId, + `Test should click on the correct element.` + ); + Assert.equal( + "https://example.org/browser/comm/mail/components/extensions/test/browser/data/content.html", + url, + `Test should open the correct page.` + ); + await contextClick(elementId, getBrowser()); + Assert.ok( + mockExternalProtocolService.urlLoaded(url), + `Page should have correctly been opened in external browser.` + ); + await extension.sendMessage(); + } + + await extension.awaitFinish(); + await extension.unload(); +}; + +add_task(async function test_tabs() { + let extension = ExtensionTestUtils.loadExtension({ + files: { + "utils.js": await getUtilsJS(), + "background.js": async () => { + // Open remote file and re-open it in the browser. + const url = + "https://example.org/browser/comm/mail/components/extensions/test/browser/data/content.html"; + const elementId = "#description"; + + let testTab = await browser.tabs.create({ url }); + await window.sendMessage("contextClick", { elementId, url }); + await browser.tabs.remove(testTab.id); + + browser.test.notifyPass(); + }, + }, + manifest: { + background: { + scripts: ["utils.js", "background.js"], + }, + permissions: ["tabs"], + }, + }); + + await subtest_clickOpenInBrowserContextMenu( + extension, + () => document.getElementById("tabmail").currentTabInfo.browser + ); +}); + +add_task(async function test_windows() { + let extension = ExtensionTestUtils.loadExtension({ + files: { + "utils.js": await getUtilsJS(), + "background.js": async () => { + // Open remote file and re-open it in the browser. + const url = + "https://example.org/browser/comm/mail/components/extensions/test/browser/data/content.html"; + const elementId = "#description"; + + let testWindow = await browser.windows.create({ type: "popup", url }); + await window.sendMessage("contextClick", { elementId, url }); + await browser.windows.remove(testWindow.id); + + browser.test.notifyPass(); + }, + }, + manifest: { + background: { + scripts: ["utils.js", "background.js"], + }, + permissions: ["tabs"], + }, + }); + + await subtest_clickOpenInBrowserContextMenu( + extension, + () => Services.wm.getMostRecentWindow("mail:extensionPopup").browser + ); +}); diff --git a/mail/components/extensions/test/browser/data/content.html b/mail/components/extensions/test/browser/data/content.html index 701157d19e..6a56ee6a5a 100644 --- a/mail/components/extensions/test/browser/data/content.html +++ b/mail/components/extensions/test/browser/data/content.html @@ -5,7 +5,7 @@ A test document -

This is text.

+

This is text.

This is a link with text.