Misc popup refactoring/documentation

This commit is contained in:
hensm
2022-04-17 11:17:24 +01:00
parent 62d95df803
commit 3a488a6530
2 changed files with 49 additions and 70 deletions

View File

@@ -5,7 +5,6 @@ import messaging, { Port, Message } from "../../messaging";
import options from "../../lib/options"; import options from "../../lib/options";
import { TypedEventTarget } from "../../lib/TypedEventTarget"; import { TypedEventTarget } from "../../lib/TypedEventTarget";
import { getWindowCenteredProps, WindowCenteredProps } from "../../lib/utils";
import { ReceiverDevice } from "../../types"; import { ReceiverDevice } from "../../types";
import { import {
@@ -29,6 +28,10 @@ export interface PageInfo {
frameId: number; frameId: number;
} }
/**
* Manages the receiver selector popup window and communication with the
* extension page hosted within.
*/
export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorEvents> { export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorEvents> {
private windowId?: number; private windowId?: number;
@@ -49,7 +52,6 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
constructor() { constructor() {
super(); super();
// Bind methods to pass to addListener
this.onConnect = this.onConnect.bind(this); this.onConnect = this.onConnect.bind(this);
this.onPopupMessage = this.onPopupMessage.bind(this); this.onPopupMessage = this.onPopupMessage.bind(this);
this.onWindowsRemoved = this.onWindowsRemoved.bind(this); this.onWindowsRemoved = this.onWindowsRemoved.bind(this);
@@ -68,13 +70,16 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
return this.#isOpen; return this.#isOpen;
} }
/**
* Creates and opens a receiver selector window.
*/
public async open( public async open(
receiverDevices: ReceiverDevice[], receiverDevices: ReceiverDevice[],
defaultMediaType: ReceiverSelectorMediaType, defaultMediaType: ReceiverSelectorMediaType,
availableMediaTypes: ReceiverSelectorMediaType, availableMediaTypes: ReceiverSelectorMediaType,
appId?: string, appId?: string,
pageInfo?: PageInfo pageInfo?: PageInfo
): Promise<void> { ) {
this.appId = appId; this.appId = appId;
this.pageInfo = pageInfo; this.pageInfo = pageInfo;
@@ -87,54 +92,59 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
this.defaultMediaType = defaultMediaType; this.defaultMediaType = defaultMediaType;
this.availableMediaTypes = availableMediaTypes; this.availableMediaTypes = availableMediaTypes;
let centeredProps: WindowCenteredProps = { const popupSizePosition = {
left: 100,
top: 100,
width: 350, width: 350,
height: 200 height: 200,
left: 100,
top: 100
}; };
try { /**
// Calculate centered size/position based on current window * Get current browser window and calculate relative centered
centeredProps = getWindowCenteredProps( * left/top positions for the popup.
await browser.windows.getCurrent(), */
centeredProps.width, const refWin = await browser.windows.getCurrent();
centeredProps.height if (refWin.width && refWin.height && refWin.left && refWin.top) {
const centerX = refWin.left + refWin.width / 2;
const centerY = refWin.top + refWin.height / 3;
popupSizePosition.left = Math.floor(
centerX - popupSizePosition.width / 2
); );
} catch { popupSizePosition.top = Math.floor(
// Shouldn't ever hit this, but defaults are provided in case centerY - popupSizePosition.height / 2
);
} else {
logger.log("Reference window missing positional properties.");
} }
// Create popup window
const popup = await browser.windows.create({ const popup = await browser.windows.create({
url: POPUP_URL, url: POPUP_URL,
type: "popup", type: "popup",
...centeredProps ...popupSizePosition
}); });
if (popup?.id === undefined) { if (popup?.id === undefined) {
throw logger.error("Failed to create receiver selector popup."); throw logger.error("Failed to create receiver selector popup.");
} }
// Size/position not set correctly on creation (bug 1396881)
await browser.windows.update(popup.id, {
...popupSizePosition
});
this.#isOpen = true; this.#isOpen = true;
this.windowId = popup.id; this.windowId = popup.id;
// Size/position not set correctly on creation (bug?) // Add focus listener
await browser.windows.update(this.windowId, { if (await options.get("receiverSelectorCloseIfFocusLost")) {
...centeredProps
});
const closeIfFocusLost = await options.get(
"receiverSelectorCloseIfFocusLost"
);
if (closeIfFocusLost) {
// Add focus listener
browser.windows.onFocusChanged.addListener( browser.windows.onFocusChanged.addListener(
this.onWindowsFocusChanged this.onWindowsFocusChanged
); );
} }
} }
/** Updates receiver devices displayed in the receiver selector. */
public update(receiverDevices: ReceiverDevice[]) { public update(receiverDevices: ReceiverDevice[]) {
this.receiverDevices = receiverDevices; this.receiverDevices = receiverDevices;
this.messagePort?.postMessage({ this.messagePort?.postMessage({
@@ -145,7 +155,8 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
}); });
} }
public async close(): Promise<void> { /** Closes the receiver selector (if open). */
public async close() {
if (this.windowId) { if (this.windowId) {
await browser.windows.remove(this.windowId); await browser.windows.remove(this.windowId);
} }
@@ -158,16 +169,19 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
} }
} }
/**
* Handles incoming port connection from the extension page and
* sends init data.
*/
private onConnect(port: Port) { private onConnect(port: Port) {
// Keep history state clean
browser.history.deleteUrl({ url: POPUP_URL }); browser.history.deleteUrl({ url: POPUP_URL });
if (port.name !== "popup") { if (port.name !== "popup") {
return; return;
} }
if (this.messagePort) { this.messagePort?.disconnect();
this.messagePort.disconnect();
}
this.messagePort = port; this.messagePort = port;
this.messagePort.onMessage.addListener(this.onPopupMessage); this.messagePort.onMessage.addListener(this.onPopupMessage);
@@ -180,7 +194,8 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
this.defaultMediaType === undefined || this.defaultMediaType === undefined ||
this.availableMediaTypes === undefined this.availableMediaTypes === undefined
) { ) {
throw logger.error("Popup receiver data not found."); logger.error("Popup receiver data not found.");
return;
} }
this.messagePort.postMessage({ this.messagePort.postMessage({
@@ -196,13 +211,9 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
availableMediaTypes: this.availableMediaTypes availableMediaTypes: this.availableMediaTypes
} }
}); });
messaging.onConnect.removeListener(this.onConnect);
} }
/** /** Handles messages from the popup extension page. */
* Handles popup messages.
*/
private onPopupMessage(message: Message) { private onPopupMessage(message: Message) {
switch (message.subject) { switch (message.subject) {
case "receiverSelector:selected": { case "receiverSelector:selected": {

View File

@@ -79,38 +79,6 @@ export function getMediaTypesForPageUrl(
return availableMediaTypes; return availableMediaTypes;
} }
export interface WindowCenteredProps {
width: number;
height: number;
left: number;
top: number;
}
export function getWindowCenteredProps(
refWin: browser.windows.Window,
width: number,
height: number
): WindowCenteredProps {
if (
refWin.left === undefined ||
refWin.width === undefined ||
refWin.top === undefined ||
refWin.height === undefined
) {
throw logger.error("refWin missing positional attributes.");
}
const centerX = refWin.left + refWin.width / 2;
const centerY = refWin.top + refWin.height / 3;
return {
width,
height,
left: Math.floor(centerX - width / 2),
top: Math.floor(centerY - height / 2)
};
}
export function loadScript( export function loadScript(
scriptUrl: string, scriptUrl: string,
doc: Document = document doc: Document = document