diff --git a/src/ZenActorsManager.mjs b/src/ZenActorsManager.mjs new file mode 100644 index 0000000..303a7ce --- /dev/null +++ b/src/ZenActorsManager.mjs @@ -0,0 +1,15 @@ + +// Utility to register JSWindowActors +var gZenActorsManager = { + _actors: new Set(), + + addJSWindowActor(...args) { + if (this._actors.has(args[0])) { + // Actor already registered, nothing to do + return; + } + + ChromeUtils.registerWindowActor(...args); + this._actors.add(args[0]); + }, +} diff --git a/src/ZenGlanceManager.mjs b/src/ZenGlanceManager.mjs index e69de29..371d266 100644 --- a/src/ZenGlanceManager.mjs +++ b/src/ZenGlanceManager.mjs @@ -0,0 +1,19 @@ + +{ + function registerWindowActors() { + // TODO: Only if the pref is enabled + gZenActorsManager.addJSWindowActor("ZenGlance", { + parent: { + esModuleURI: "chrome://browser/content/zen-components/actors/ZenGlanceParent.sys.mjs", + }, + child: { + esModuleURI: "chrome://browser/content/zen-components/actors/ZenGlanceChild.sys.mjs", + events: { + DOMContentLoaded: {}, + }, + }, + }); + } + + registerWindowActors(); +} diff --git a/src/ZenThemesImporter.mjs b/src/ZenThemesImporter.mjs index 0df876c..152d93b 100644 --- a/src/ZenThemesImporter.mjs +++ b/src/ZenThemesImporter.mjs @@ -304,3 +304,17 @@ var gZenThemesImporter = new (class { await gZenStylesheetManager.writeStylesheet(this.styleSheetPath, themes); } })(); + +gZenActorsManager.addJSWindowActor("ZenThemeMarketplace", { + parent: { + esModuleURI: "chrome://browser/content/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs", + }, + child: { + esModuleURI: "chrome://browser/content/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs", + events: { + DOMContentLoaded: {}, + }, + }, + matches: ["https://*.zen-browser.app/*", "about:preferences"], + allFrames: true, +}); diff --git a/src/actors/ZenGlanceChild.sys.mjs b/src/actors/ZenGlanceChild.sys.mjs index e69de29..93a88bd 100644 --- a/src/actors/ZenGlanceChild.sys.mjs +++ b/src/actors/ZenGlanceChild.sys.mjs @@ -0,0 +1,102 @@ +export class ZenGlanceChild extends JSWindowActorChild { + constructor() { + super(); + + this.mouseUpListener = this.handleMouseUp.bind(this); + this.mouseDownListener = this.handleMouseDown.bind(this); + this.clickListener = this.handleClick.bind(this); + } + + async handleEvent(event) { + switch (event.type) { + case 'DOMContentLoaded': + await this.initiateGlance(); + break; + default: + } + } + + async getActivationMethod() { + if (this._activationMethod === undefined) { + this._activationMethod = await this.sendQuery('ZenGlance:GetActivationMethod'); + } + return this._activationMethod; + } + + async getHoverActivationDelay() { + if (this._hoverActivationDelay === undefined) { + this._hoverActivationDelay = await this.sendQuery('ZenGlance:GetHoverActivationDelay'); + } + return this._hoverActivationDelay; + } + + async receiveMessage(message) { + switch (message.name) { + } + } + + async initiateGlance() { + this.mouseIsDown = false; + const activationMethod = await this.getActivationMethod(); + if (activationMethod === 'hover') { + this.contentWindow.addEventListener('mousedown', this.mouseDownListener); + this.contentWindow.addEventListener('mouseup', this.mouseUpListener); + + this.contentWindow.document.removeEventListener('click', this.clickListener); + } else if (activationMethod === 'ctrl' || activationMethod === 'alt' || activationMethod === 'shift') { + this.contentWindow.document.addEventListener('click', this.clickListener); + + this.contentWindow.removeEventListener('mousedown', this.mouseDownListener); + this.contentWindow.removeEventListener('mouseup', this.mouseUpListener); + } + } + + ensureOnlyKeyModifiers(event) { + return !(event.ctrlKey ^ event.altKey ^ event.shiftKey); + } + + openGlance(url) { + this.sendAsyncMessage('ZenGlance:OpenGlance', { url }); + } + + handleMouseUp(event) { + this.mouseIsDown = false; + } + + async handleMouseDown(event) { + if (event.target.tagName !== 'A') { + return; + } + this.mouseIsDown = true; + const hoverActivationDelay = await this.getHoverActivationDelay(); + setTimeout(() => { + if (this.mouseIsDown) { + this.openGlance(event.target.href); + } + }, hoverActivationDelay); + } + + handleClick(event) { + if (this.ensureOnlyKeyModifiers(event)) { + return; + } + const activationMethod = this._activationMethod; + if (activationMethod === 'ctrl' && !event.ctrlKey) { + return; + } else if (activationMethod === 'alt' && !event.altKey) { + return; + } else if (activationMethod === 'shift' && !event.shiftKey) { + return; + } else if (activationMethod === 'hover' || typeof activationMethod === 'undefined') { + return; + } + // get closest A element + const target = event.target.closest('A'); + if (target) { + event.preventDefault(); + event.stopPropagation(); + + this.openGlance(target.href); + } + } +} diff --git a/src/actors/ZenGlanceParent.sys.mjs b/src/actors/ZenGlanceParent.sys.mjs index e69de29..0f1a4e9 100644 --- a/src/actors/ZenGlanceParent.sys.mjs +++ b/src/actors/ZenGlanceParent.sys.mjs @@ -0,0 +1,24 @@ +export class ZenGlanceParent extends JSWindowActorParent { + constructor() { + super(); + } + + async receiveMessage(message) { + switch (message.name) { + case 'ZenGlance:GetActivationMethod': { + return Services.prefs.getStringPref('zen.glance.activation-method', 'ctrl'); + } + case 'ZenGlance:GetHoverActivationDelay': { + return Services.prefs.getIntPref('zen.glance.hold-duration', 500); + } + case 'ZenGlance:OpenGlance': { + this.openGlance(); + break; + } + } + } + + openGlance() { + console.log('Opening glance'); + } +}