Skip to content

Commit

Permalink
✨ Click api support
Browse files Browse the repository at this point in the history
  • Loading branch information
trickypr committed Dec 24, 2023
1 parent 25ffc19 commit fc55569
Show file tree
Hide file tree
Showing 22 changed files with 510 additions and 64 deletions.
5 changes: 5 additions & 0 deletions docs/webext/background.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Background pages

## Differences

- Non-persistent background pages will not restart
5 changes: 5 additions & 0 deletions docs/webext/pageAction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# PageActions

## Differences

- `onClicked`: The first argument is the click info, not the `Tab` info
4 changes: 3 additions & 1 deletion scripts/lib/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ export const CHANGES: { file: string; append?: string | string[] }[] = [
},
{
file: 'components/components.manifest',
append:
append: [
'category webextension-modules browser chrome://browser/content/extensions/ext-browser.json',
'category webextension-scripts c-browser chrome://browser/content/extensions/parent/ext-browser.js',
],
},
]

Expand Down
17 changes: 13 additions & 4 deletions src/content/browser/components/omnibox/PageAction.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
getIconUrlForPreferredSize,
pageActionIcons,
} from '@browser/lib/modules/EPageActionsBindings'
import { clickModifiersFromEvent } from '@shared/domUtils'
export let pageAction: PageAction
const icons = pageActionIcons(pageAction)
Expand All @@ -41,7 +42,6 @@
browser.remove()
}
console.log(pageAction.popupUrl)
if (!pageAction.popupUrl) return
const uri = resource.NetUtil.newURI(pageAction.popupUrl)
Expand Down Expand Up @@ -80,9 +80,18 @@
setURI(lBrowser, uri)
}
const OPEN_PANEL = async () => {
const handleClick = async (event: MouseEvent) => {
// Send the event to the extension
pageAction.events.emit('click', {
clickData: {
modifiers: clickModifiersFromEvent(event),
button: event.button,
},
})
await buildPanelBrowser()
panel.openPopup(button, 'bottomright topright')
// Panel may not exist if there is no popupUrl
if (panel) panel.openPopup(button, 'bottomright topright')
}
onMount(() => () => {
Expand All @@ -99,7 +108,7 @@
<ToolbarButton
kind="page-icon"
bind:button
on:click={OPEN_PANEL}
on:click={handleClick}
tooltip={pageAction.tooltip}
>
{#if $icons}
Expand Down
12 changes: 11 additions & 1 deletion src/content/browser/lib/window/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ import {
} from './contextMenu'
import {
closeTab,
getCurrentTab,
getTabById,
openTab,
runOnCurrentTab,
setCurrentTab,
tabs,
} from './tabs'
import { id } from './window'
import { id, setId } from './window'

export type WindowTriggers = {
bookmarkCurrentPage: undefined
Expand All @@ -27,9 +28,17 @@ export const windowApi = {
/**
* Identify which window this is. This should be used for actions like tab
* moving that go across windows
*
* Note: You need to wait for the window watcher to register this window
* before you get a valid id
*/
id,

/**
* Sets the window ID. You should only use this if you are the WindowWatcher
*/
setId,

windowTriggers: mitt<WindowTriggers>(),
window: {
/**
Expand All @@ -50,6 +59,7 @@ export const windowApi = {
openTab,
runOnCurrentTab,
setCurrentTab,
getCurrentTab,
getTabById,
get tabs() {
return tabs.readOnce()
Expand Down
39 changes: 4 additions & 35 deletions src/content/browser/lib/window/initialize.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/* 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 { TAB_DATA_TYPE } from '@browser/components/tabs/tabDrag'

import { resource } from '../resources'
import { type WindowArguments, getFullWindowConfiguration } from './arguments'
import { openTab } from './tabs'
import {
initializeWindowDragOverHandler,
registerWithWindowTracker,
} from './window'

export function initializeWindow(args: WindowArguments | undefined) {
// When opened via nsIWindowWatcher.openWindow, the arguments are
Expand All @@ -26,36 +28,3 @@ export function initializeWindow(args: WindowArguments | undefined) {
initializeWindowDragOverHandler()
registerWithWindowTracker()
}

/**
* If we want to detect drops outside of the window, we need to ensure that all
* drops **within** a browser window are handled.
*
* This listens for all events with a type equivalent to {@link TAB_DATA_TYPE}
* and makes sure they have an attached drop type.
*/
function initializeWindowDragOverHandler() {
const handleDragEvent = (event: DragEvent) => {
const rawDragRepresentation = event.dataTransfer?.getData(TAB_DATA_TYPE)
if (!rawDragRepresentation) return

// Set this to some drop event other than 'none' so we can detect drops
// outside of the window in the tab's drag handler
if (event.dataTransfer) event.dataTransfer.dropEffect = 'link'
event.preventDefault()
}

window.addEventListener('dragover', handleDragEvent)
window.addEventListener('drop', handleDragEvent)
}

/**
* Ensures that the window tracker is aware of this window & that when it is
* closed, the correct cleanup is performed.
*/
function registerWithWindowTracker() {
resource.WindowTracker.registerWindow(window)
window.addEventListener('unload', () =>
resource.WindowTracker.removeWindow(window),
)
}
4 changes: 2 additions & 2 deletions src/content/browser/lib/window/tabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function getTabById(id: number): Tab | undefined {
return tabs.readOnce().find((tab) => tab.getId() == id)
}

function getCurrent(): Tab | undefined {
export function getCurrentTab(): Tab | undefined {
return getTabById(internalSelectedTab)
}

Expand All @@ -63,7 +63,7 @@ export function setCurrentTab(tab: Tab) {
}

export function runOnCurrentTab<R>(method: (tab: Tab) => R): R | void {
const currentTab = getCurrent()
const currentTab = getCurrentTab()
if (currentTab) return method(currentTab)
}

Expand Down
43 changes: 39 additions & 4 deletions src/content/browser/lib/window/window.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,47 @@
/* 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 { nanoid } from 'nanoid'
import { TAB_DATA_TYPE } from '@browser/components/tabs/tabDrag'

import { resource } from '../resources'

export const id = nanoid()
export let id = -1
export const setId = (newId: number) => (id = newId)

export const getWindowById = (id: string) =>
export const getWindowById = (id: number) =>
resource.WindowTracker.getWindowById(id)

/**
* If we want to detect drops outside of the window, we need to ensure that all
* drops **within** a browser window are handled.
*
* This listens for all events with a type equivalent to {@link TAB_DATA_TYPE}
* and makes sure they have an attached drop type.
*/
export function initializeWindowDragOverHandler() {
const handleDragEvent = (event: DragEvent) => {
const rawDragRepresentation = event.dataTransfer?.getData(TAB_DATA_TYPE)
if (!rawDragRepresentation) return

// Set this to some drop event other than 'none' so we can detect drops
// outside of the window in the tab's drag handler
if (event.dataTransfer) event.dataTransfer.dropEffect = 'link'
event.preventDefault()
}

window.addEventListener('dragover', handleDragEvent)
window.addEventListener('drop', handleDragEvent)
}

/**
* Ensures that the window tracker is aware of this window & that when it is
* closed, the correct cleanup is performed.
*/
export function registerWithWindowTracker() {
resource.WindowTracker.registerWindow(window)
window.addEventListener('unload', () =>
resource.WindowTracker.removeWindow(window),
)

window.addEventListener('focus', () => resource.WindowTracker.focusWindow(id))
}
27 changes: 27 additions & 0 deletions src/content/shared/domUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* 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 { lazy } from './lazy'

export type ClickModifiers = 'Shift' | 'Alt' | 'Command' | 'Ctrl' | 'MacCtrl'
export const clickModifiersFromEvent = (
event: MouseEvent,
): ClickModifiers[] => {
const map = {
shiftKey: 'Shift',
altKey: 'Alt',
metaKey: 'Command',
ctrlKey: 'Ctrl',
} as const

const modifiers: ClickModifiers[] = (Object.keys(map) as (keyof typeof map)[])
.filter((key) => event[key])
.map((key) => map[key])

if (event.ctrlKey && lazy.AppConstants.platform === 'macosx') {
modifiers.push('MacCtrl')
}

return modifiers
}
1 change: 1 addition & 0 deletions src/content/shared/lazy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import { lazyESModuleGetters } from './TypedImportUtilities'

export const lazy = lazyESModuleGetters({
AppConstants: 'resource://gre/modules/AppConstants.sys.mjs',
PlacesUtils: 'resource://gre/modules/PlacesUtils.sys.mjs',
Bookmarks: 'resource://gre/modules/Bookmarks.sys.mjs',
History: 'resource://gre/modules/History.sys.mjs',
Expand Down
4 changes: 4 additions & 0 deletions src/content/shared/xul/messageReciver.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/* 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 MessageReviver<
Cb extends (argument: ReceiveMessageArgument) => unknown,
> implements MessageListener
Expand Down
8 changes: 7 additions & 1 deletion src/link.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@

/// <reference types="gecko-types" />

/// <reference path="./types/AppConstants.d.ts" />
/// <reference path="./types/MatchPattern.d.ts" />
/// <reference path="./types/MessageManager.d.ts" />

declare type LazyImportType<Modules extends Partial<MozESMExportFile>> =
| { [Key in keyof Modules]: MozESMExportType[Key] }
| {}

declare module 'resource://app/modules/FaviconLoader.sys.mjs' {
export const FaviconLoader: typeof import('./modules/FaviconLoader').FaviconLoader
}
Expand All @@ -29,12 +34,14 @@ declare module 'resource://app/modules/EPageActions.sys.mjs' {
}

declare interface MozESMExportFile {
AppConstants: 'resource://gre/modules/AppConstants.sys.mjs'
TypedImportUtils: 'resource://app/modules/TypedImportUtils.sys.mjs'
WindowTracker: 'resource://app/modules/BrowserWindowTracker.sys.mjs'
EPageActions: 'resource://app/modules/EPageActions.sys.mjs'
}

declare interface MozESMExportType {
AppConstants: typeof import('resource://gre/modules/AppConstants.sys.mjs').AppConstants
TypedImportUtils: typeof import('./modules/TypedImportUtils')
WindowTracker: typeof import('./modules/BrowserWindowTracker').WindowTracker
EPageActions: typeof import('./modules/EPageActions').EPageActions
Expand Down Expand Up @@ -636,4 +643,3 @@ declare module ChromeUtils {
path: Path,
): T
}

40 changes: 36 additions & 4 deletions src/modules/BrowserWindowTracker.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,68 @@
/* 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/. */

/* eslint-disable @typescript-eslint/no-explicit-any */
import mitt from 'resource://app/modules/mitt.sys.mjs'

type Tab = unknown

export type WindowTrackerEvents = {
windowCreated: Window & typeof globalThis
windowDestroyed: Window & typeof globalThis

focus: Window & typeof globalThis
}

export const WindowTracker = {
/**
* @private
*/
nextWindowId: 0,

events: mitt<WindowTrackerEvents>(),

registeredWindows: new Map<string, typeof window>(),
activeWindow: null as number | null,
registeredWindows: new Map<number, typeof window>(),

/**
* Registers a new browser window to be tracked
*
* @param w The window to register
*/
registerWindow(w: typeof window) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
;(w as any).windowApi.id = this.nextWindowId++
this.registeredWindows.set((w as any).windowApi.id, w)
this.events.emit('windowCreated', w)
},

removeWindow(w: typeof window) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.registeredWindows.delete((w as any).windowApi.id)
this.events.emit('windowDestroyed', w)
},

getWindowById(wid: string) {
getWindowById(wid: number) {
return this.registeredWindows.get(wid)
},

getWindowWithBrowser(
browser: XULBrowserElement,
): { window: Window & typeof globalThis; tab: Tab } | null {
for (const window of this.registeredWindows.values()) {
const tab = (window as any).windowApi.tabs.tabs.find(
(t: Tab) => (t as any).getTabId() === browser.browserId,
)
if (tab) return { window, tab }
}
return null
},

focusWindow(id: number) {
this.activeWindow = id
this.events.emit('focus', this.getActiveWindow()!)
},

getActiveWindow(): (typeof window & typeof globalThis) | undefined {
return this.registeredWindows.get(this.activeWindow ?? -1)
},
}
1 change: 1 addition & 0 deletions src/modules/EPageActions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface PageActionOptions<PS = MatchPatternSet> {

export type PageActionEvents = {
updateIcon: Record<number, string> | undefined
click: { clickData: { modifiers: string[]; button: number } }
}

export class PageAction implements PageActionOptions {
Expand Down
Loading

0 comments on commit fc55569

Please sign in to comment.