diff --git a/actors/DotLinkStatusChild.sys.mjs b/actors/DotLinkStatusChild.sys.mjs new file mode 100644 index 0000000000..435775e180 --- /dev/null +++ b/actors/DotLinkStatusChild.sys.mjs @@ -0,0 +1,72 @@ +/* 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/. */ + +const { setTimeout, clearTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +const { BrowserUtils } = ChromeUtils.importESModule( + "resource://gre/modules/BrowserUtils.sys.mjs" +); + +export class LinkStatusChild extends JSWindowActorChild { + /** + * The current mouseover href + */ + _currentHref = null; + + /** + * The timer before a mouseout event should clear the status + */ + _clearStatusInt = null; + + /** + * Handles the over link status + * @param {string} href + */ + handleOverLink(href) { + // Prevent duplicate mouseover events from the + // same href firing multiple overLink events. + if (href && this._currentHref == href) return; + + this.sendAsyncMessage("LinkStatus:OverLink", { + href + }); + + this._currentHref = href; + } + + /** + * Handles incoming events to the LinkStatus actor + * @param {Event} event + */ + handleEvent(event) { + clearTimeout(this._clearStatusInt); + + const [href, linkNode] = + BrowserUtils.hrefAndLinkNodeForClickEvent(event); + + switch (event.type) { + case "mousemove": + this.handleOverLink(href); + break; + case "mouseout": + if (linkNode) { + // Clearing the overLink right after receiving `mouseout` is a bad idea. + // + // We rely on `mouseout` for determining if the mouse left the region + // of a link (for example tabbing out of the window, but leaving the cursor in the same position). + // + // Therefore, adding a timer allows us to bypass clearing overLink + // straight away, if the user happened to move their mouse between + // two links, giving `mousemove` enough time to re-update the href + // before `mouseout` can clear it. + this._clearStatusInt = setTimeout(() => { + this.handleOverLink(null); + }, 10); + } + break; + } + } +} diff --git a/actors/DotLinkStatusParent.sys.mjs b/actors/DotLinkStatusParent.sys.mjs new file mode 100644 index 0000000000..ef2eb3e436 --- /dev/null +++ b/actors/DotLinkStatusParent.sys.mjs @@ -0,0 +1,28 @@ +/* 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/. */ + +export class LinkStatusParent extends JSWindowActorParent { + /** + * + * @param {import("third_party/dothq/gecko-types/lib").ReceiveMessageArgument} msg + */ + receiveMessage(msg) { + if (msg.name !== "LinkStatus:OverLink") return; + + const { href } = msg.data; + + const browser = this.manager.browsingContext.top.embedderElement; + const win = browser.ownerGlobal; + + if (!win.gDot) return; + + const tab = win.gDot.tabs?.getTabForWebContents(browser); + + if (tab) { + win.gDot.status.setTabStatus(tab, "overLink", href); + } else { + win.gDot.status.setStatus("overLink", href); + } + } +} diff --git a/actors/moz.build b/actors/moz.build index ce88482d7c..0b6b4f2076 100644 --- a/actors/moz.build +++ b/actors/moz.build @@ -11,6 +11,8 @@ FINAL_TARGET_FILES.actors += [ "DotDevToolsChild.sys.mjs", "DotGeckoCommandsChild.sys.mjs", "DotLinkHandlerParent.sys.mjs", + "DotLinkStatusChild.sys.mjs", + "DotLinkStatusParent.sys.mjs", "DotPromptParent.sys.mjs", "DotTooltipListenerChild.sys.mjs", "DotUAStylesChild.sys.mjs", diff --git a/components/DotGlue.sys.mjs b/components/DotGlue.sys.mjs index f3598c789e..c241692822 100644 --- a/components/DotGlue.sys.mjs +++ b/components/DotGlue.sys.mjs @@ -178,6 +178,22 @@ const JSWINDOWACTORS = { messageManagerGroups: ["browsers"] }, + LinkStatus: { + parent: { + esModuleURI: "resource:///actors/DotLinkStatusParent.sys.mjs" + }, + + child: { + esModuleURI: "resource:///actors/DotLinkStatusChild.sys.mjs", + events: { + mousemove: { capture: true, mozSystemGroup: true }, + mouseout: { capture: true, mozSystemGroup: true } + } + }, + + allFrames: true + }, + PageInfo: { child: { esModuleURI: "resource:///actors/PageInfoChild.sys.mjs" diff --git a/components/XULBrowserWindow.sys.mjs b/components/XULBrowserWindow.sys.mjs index 8775d3783e..daef32d758 100644 --- a/components/XULBrowserWindow.sys.mjs +++ b/components/XULBrowserWindow.sys.mjs @@ -21,11 +21,9 @@ export class XULBrowserWindow { status = ""; defaultStatus = ""; - overLink = ""; startTime = 0; isBusy = false; busyUI = false; - hideOverLinkImmediately = false; _state = null; _lastLocation = null; @@ -36,8 +34,6 @@ export class XULBrowserWindow { // with the the HTTPS-Only Mode error page (more details in bug 1656027) _isSecureContext = null; - _overlinkInt; - QueryInterface = ChromeUtils.generateQI([ "nsIWebProgressListener", "nsIWebProgressListener2", @@ -81,12 +77,7 @@ export class XULBrowserWindow { * @param {string} url */ setOverLink(url) { - clearTimeout(this._overlinkInt); - - this._overlinkInt = setTimeout(() => { - console.log("todo: rewrite to use actor"); - this.#win.gDot.status.setStatus("overLink", url); - }, 100); + // noop: Handled by LinkStatus actor } /** diff --git a/third_party/dothq/gecko-types/lib/WindowGlobalParent.d.ts b/third_party/dothq/gecko-types/lib/WindowGlobalParent.d.ts index 03c6d44bb5..6501c04d81 100644 --- a/third_party/dothq/gecko-types/lib/WindowGlobalParent.d.ts +++ b/third_party/dothq/gecko-types/lib/WindowGlobalParent.d.ts @@ -2,6 +2,7 @@ * 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/. */ +import { BrowsingContext } from "./BrowsingContext"; import { FrameLoader } from "./FrameLoader"; import { JSWindowActorParent } from "./JSWindowActorParent"; import { WindowGlobalChild } from "./WindowGlobalChild"; @@ -17,6 +18,8 @@ export interface WindowGlobalParentInstance { readonly rootFrameLoader?: FrameLoader; readonly childActor?: WindowGlobalChild; + readonly browsingContext: BrowsingContext; + /** * Checks for any WindowContexts with "beforeunload" listeners in this * WindowGlobal's subtree. If any exist, a "beforeunload" event is