From c2e1dcc9ac1b53022c047c2df5e4cd6767ee9427 Mon Sep 17 00:00:00 2001
From: trickypr <23250792+trickypr@users.noreply.github.com>
Date: Fri, 29 Dec 2023 14:29:10 +1100
Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=B8=20Assorted=20new=20tab=20fixes=20(?=
=?UTF-8?q?#43)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/actors/ClickHandlerChild.ts | 37 +++++++++++++++++++
src/actors/ClickHandlerParent.ts | 15 ++++++++
src/actors/link.json | 2 +
src/content/browser/lib/window/tab.ts | 9 ++++-
.../browser/lib/xul/NSBrowserAccess.ts | 33 +++++++++++++++--
src/modules/BrowserGlue.ts | 12 ++++++
6 files changed, 102 insertions(+), 6 deletions(-)
create mode 100644 src/actors/ClickHandlerChild.ts
create mode 100644 src/actors/ClickHandlerParent.ts
diff --git a/src/actors/ClickHandlerChild.ts b/src/actors/ClickHandlerChild.ts
new file mode 100644
index 0000000..aa368fd
--- /dev/null
+++ b/src/actors/ClickHandlerChild.ts
@@ -0,0 +1,37 @@
+/* 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 type ClickHandlerMessage = {
+ name: 'openlink'
+ data: { href: string }
+} & { target: JSWindowActorParent }
+
+export class ClickHandlerChild extends JSWindowActorChild {
+ getHrefIfExists(target: Node): string | undefined {
+ if ((target as HTMLAnchorElement).href) {
+ return (target as HTMLAnchorElement).href
+ }
+
+ if (target.parentElement) {
+ return this.getHrefIfExists(target.parentElement)
+ }
+ }
+
+ handleEvent(event: MouseEvent) {
+ if (event.defaultPrevented || !event.target) return
+
+ const href = this.getHrefIfExists(event.target as Node)
+
+ const ctrlClick = event.button === 0 && event.ctrlKey
+ const middleClick = event.button === 1
+
+ const shouldOpenNewTab = href && (ctrlClick || middleClick)
+
+ if (!shouldOpenNewTab) return
+
+ this.sendAsyncMessage('openlink', { href })
+ }
+}
diff --git a/src/actors/ClickHandlerParent.ts b/src/actors/ClickHandlerParent.ts
new file mode 100644
index 0000000..fbc003c
--- /dev/null
+++ b/src/actors/ClickHandlerParent.ts
@@ -0,0 +1,15 @@
+/* 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/. */
+///
+import { ClickHandlerMessage } from './ClickHandlerChild'
+
+export class ClickHandlerParent extends JSWindowActorParent {
+ receiveMessage(event: ClickHandlerMessage) {
+ if (event.name == 'openlink') {
+ const win = event.target.browsingContext.embedderElement.ownerGlobal
+ const uri = Services.io.newURI(event.data.href)
+ win.windowApi.tabs.openTab(uri)
+ }
+ }
+}
diff --git a/src/actors/link.json b/src/actors/link.json
index 758dc8a..3435b4d 100644
--- a/src/actors/link.json
+++ b/src/actors/link.json
@@ -1,4 +1,6 @@
[
+ "ClickHandlerChild",
+ "ClickHandlerParent",
"ContextMenuChild",
"ContextMenuParent",
"LinkHandlerChild",
diff --git a/src/content/browser/lib/window/tab.ts b/src/content/browser/lib/window/tab.ts
index f50f6ae..625675b 100644
--- a/src/content/browser/lib/window/tab.ts
+++ b/src/content/browser/lib/window/tab.ts
@@ -51,11 +51,12 @@ export class Tab {
public hidden = writable(false)
constructor(uri: nsIURIType) {
- this.uri = viewableWritable(uri)
- this.goToUri(uri)
this.browserElement = createBrowser({
remoteType: getBrowserRemoteType(uri),
})
+
+ this.uri = viewableWritable(uri)
+ this.goToUri(uri)
this.title.set(uri.asciiHost)
this.uri.subscribe(async (uri) =>
@@ -79,6 +80,10 @@ export class Tab {
return this.tabId || 0
}
+ public getBrowserElement() {
+ return this.browserElement
+ }
+
public getDragRepresentation() {
return {
windowId: window.windowApi.id,
diff --git a/src/content/browser/lib/xul/NSBrowserAccess.ts b/src/content/browser/lib/xul/NSBrowserAccess.ts
index 60113aa..201f9e1 100644
--- a/src/content/browser/lib/xul/NSBrowserAccess.ts
+++ b/src/content/browser/lib/xul/NSBrowserAccess.ts
@@ -4,6 +4,22 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
+export enum OpenWhere {
+ DefaultWindow = 0,
+ CurrentWindow = 1,
+ NewWindow = 2,
+ NewTab = 3,
+ PrintBrowser = 4,
+}
+
+export enum OpenFlags {
+ New = 0x0,
+ /** Open was triggered by a thirdparty application */
+ External = 0x1,
+ NoOpener = 0x4,
+ NoReferer = 0x8,
+}
+
export class NSBrowserAccess {
createContentWindow(
aURI: nsIURIType,
@@ -15,15 +31,24 @@ export class NSBrowserAccess {
) {
throw new Error('Method not implemented.')
}
+
createContentWindowInFrame(
aURI: nsIURIType,
params: nsIOpenURIInFrameParamsType,
aWhere: number,
- aFlags: number,
- aName: string,
- ): Element {
- throw new Error('Method not implemented.')
+ ): Element | null {
+ if (aWhere !== OpenWhere.NewTab) {
+ console.warn('NSBrowserAccess: Only OpenWhere.NewTab is supported')
+ return null
+ }
+
+ // TODO: Handle params
+ // TODO: Handle unhandled arguments (see nsIBrowserDOMWindow)
+ const tab = window.windowApi.tabs.openTab(aURI)
+ const browser = tab.getBrowserElement()
+ return browser
}
+
openURI(
aURI: nsIURIType,
aOpenWindowInfo: nsIOpenWindowInfoType,
diff --git a/src/modules/BrowserGlue.ts b/src/modules/BrowserGlue.ts
index e2b3acf..de848ac 100644
--- a/src/modules/BrowserGlue.ts
+++ b/src/modules/BrowserGlue.ts
@@ -10,6 +10,18 @@ const lazy = lazyESModuleGetters({
const JS_PROCESS_ACTORS = {}
const JS_WINDOW_ACTORS = {
+ ClickHandler: {
+ parent: { esModuleURI: 'resource://app/actors/ClickHandlerParent.sys.mjs' },
+ child: {
+ esModuleURI: 'resource://app/actors/ClickHandlerChild.sys.mjs',
+ events: {
+ chromelinkclick: { capture: true, mozSystemGroup: true },
+ },
+ },
+
+ allFrames: true,
+ },
+
ContextMenu: {
parent: {
esModuleURI: 'resource://app/actors/ContextMenuParent.sys.mjs',