diff options
Diffstat (limited to 'ext/js/app')
-rw-r--r-- | ext/js/app/frontend.js | 53 | ||||
-rw-r--r-- | ext/js/app/popup-factory.js | 32 | ||||
-rw-r--r-- | ext/js/app/popup-proxy.js | 136 | ||||
-rw-r--r-- | ext/js/app/popup-window.js | 125 | ||||
-rw-r--r-- | ext/js/app/popup.js | 127 |
5 files changed, 473 insertions, 0 deletions
diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js index bbc0ff85..9fa6009b 100644 --- a/ext/js/app/frontend.js +++ b/ext/js/app/frontend.js @@ -21,7 +21,26 @@ * TextSourceRange */ +/** + * This is the main class responsible for scanning and handling webpage content. + */ class Frontend { + /** + * Creates a new instance. + * @param {object} details + * @param {string} details.pageType The type of page, one of 'web', 'popup', or 'search'. + * @param {PopupFactory} details.popupFactory A PopupFactory instance to use for generating popups. + * @param {number} details.depth The nesting depth value of the popup. + * @param {number} details.tabId The tab ID of the host tab. + * @param {number} details.frameId The frame ID of the host frame. + * @param {?string} details.parentPopupId The popup ID of the parent popup if one exists, otherwise null. + * @param {?number} details.parentFrameId The frame ID of the parent popup if one exists, otherwise null. + * @param {boolean} details.useProxyPopup Whether or not proxy popups should be used. + * @param {boolean} details.canUseWindowPopup Whether or not window popups can be used. + * @param {boolean} details.allowRootFramePopupProxy Whether or not popups can be hosted in the root frame. + * @param {boolean} details.childrenSupported Whether popups can create child popups or not. + * @param {HotkeyHandler} details.hotkeyHandler A HotkeyHandler instance. + */ constructor({ pageType, popupFactory, @@ -83,18 +102,33 @@ class Frontend { ]); } + /** + * Get whether or not the text selection can be cleared. + * @type {boolean} + */ get canClearSelection() { return this._textScanner.canClearSelection; } + /** + * Set whether or not the text selection can be cleared. + * @param {boolean} value The new value to assign. + */ set canClearSelection(value) { this._textScanner.canClearSelection = value; } + /** + * Gets the popup instance. + * @type {Popup} + */ get popup() { return this._popup; } + /** + * Prepares the instance for use. + */ async prepare() { await this.updateOptions(); try { @@ -135,20 +169,35 @@ class Frontend { this._signalFrontendReady(); } + /** + * Set whether or not the instance is disabled. + * @param {boolean} disabled Whether or not the instance is disabled. + */ setDisabledOverride(disabled) { this._disabledOverride = disabled; this._updateTextScannerEnabled(); } + /** + * Set or clear an override options context object. + * @param {?object} optionsContext An options context object to use as the override, or `null` to clear the override. + */ setOptionsContextOverride(optionsContext) { this._optionsContextOverride = optionsContext; } + /** + * Performs a new search on a specific source. + * @param {TextSourceRange|TextSourceElement} textSource The text source to search. + */ async setTextSource(textSource) { this._textScanner.setCurrentTextSource(null); await this._textScanner.search(textSource); } + /** + * Updates the internal options representation. + */ async updateOptions() { try { await this._updateOptionsInternal(); @@ -159,6 +208,10 @@ class Frontend { } } + /** + * Waits for the previous `showContent` call to be completed. + * @returns {Promise} A promise which is resolved when the previous `showContent` call has completed. + */ showContentCompleted() { return this._lastShowPromise; } diff --git a/ext/js/app/popup-factory.js b/ext/js/app/popup-factory.js index 096d145e..05ab1942 100644 --- a/ext/js/app/popup-factory.js +++ b/ext/js/app/popup-factory.js @@ -22,7 +22,14 @@ * PopupWindow */ +/** + * A class which is used to generate and manage popups. + */ class PopupFactory { + /** + * Creates a new instance. + * @param {number} frameId The frame ID of the host frame. + */ constructor(frameId) { this._frameId = frameId; this._frameOffsetForwarder = new FrameOffsetForwarder(frameId); @@ -32,6 +39,9 @@ class PopupFactory { // Public functions + /** + * Prepares the instance for use. + */ prepare() { this._frameOffsetForwarder.prepare(); yomichan.crossFrame.registerHandlers([ @@ -53,6 +63,16 @@ class PopupFactory { ]); } + /** + * Gets or creates a popup based on a set of parameters + * @param {object} details + * @param {?number} [details.frameId] The ID of the frame that should host the popup. + * @param {?string} [details.id] A specific ID used to find an existing popup, or to assign to the new popup. + * @param {?string} [details.parentPopupId] The ID of the parent popup. + * @param {?number} [details.depth] A specific depth value to assign to the popup. + * @param {boolean} [details.popupWindow] Whether or not a separate popup window should be used, rather than an iframe. + * @param {boolean} [details.childrenSupported] Whether or not the popup is able to show child popups. + */ async getOrCreatePopup({ frameId=null, id=null, @@ -148,6 +168,13 @@ class PopupFactory { } } + /** + * Force all popups to have a specific visibility value. + * @param {boolean} value Whether or not the popups should be visible. + * @param {number} priority The priority of the override. + * @returns {string} A token which can be passed to clearAllVisibleOverride. + * @throws An exception is thrown if any popup fails to have its visibiltiy overridden. + */ async setAllVisibleOverride(value, priority) { const promises = []; const errors = []; @@ -173,6 +200,11 @@ class PopupFactory { throw errors[0]; } + /** + * Clears a visibility override that was generated by `setAllVisibleOverride`. + * @param {string} token The token returned from `setAllVisibleOverride`. + * @returns {boolean} `true` if the override existed and was removed, `false` otherwise. + */ async clearAllVisibleOverride(token) { const results = this._allPopupVisibilityTokenMap.get(token); if (typeof results === 'undefined') { return false; } diff --git a/ext/js/app/popup-proxy.js b/ext/js/app/popup-proxy.js index c46821d5..d9e1af8d 100644 --- a/ext/js/app/popup-proxy.js +++ b/ext/js/app/popup-proxy.js @@ -15,7 +15,19 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/** + * This class is a proxy for a Popup that is hosted in a different frame. + * It effectively forwards all API calls to the underlying Popup. + */ class PopupProxy extends EventDispatcher { + /** + * Creates a new instance. + * @param {object} details + * @param {string} details.id The ID of the popup. + * @param {number} details.depth The depth of the popup. + * @param {number} details.frameId The ID of the host frame. + * @param {FrameOffsetForwarder} details.frameOffsetForwarder A `FrameOffsetForwarder` instance which is used to determine frame positioning. + */ constructor({ id, depth, @@ -36,64 +48,137 @@ class PopupProxy extends EventDispatcher { // Public properties + /** + * The ID of the popup. + * @type {string} + */ get id() { return this._id; } + /** + * The parent of the popup, which is always `null` for `PopupProxy` instances, + * since any potential parent popups are in a different frame. + * @type {Popup} + */ get parent() { return null; } + /** + * Attempts to set the parent popup. + * @param {Popup} value + * @throws Throws an error, since this class doesn't support a direct parent. + */ set parent(value) { throw new Error('Not supported on PopupProxy'); } + /** + * The popup child popup, which is always null for `PopupProxy` instances, + * since any potential child popups are in a different frame. + * @type {Popup} + */ get child() { return null; } + /** + * Attempts to set the child popup. + * @param {Popup} value + * @throws Throws an error, since this class doesn't support children. + */ set child(value) { throw new Error('Not supported on PopupProxy'); } + /** + * The depth of the popup. + * @type {numer} + */ get depth() { return this._depth; } + /** + * Gets the content window of the frame. This value is null, + * since the window is hosted in a different frame. + * @type {Window} + */ get frameContentWindow() { return null; } + /** + * Gets the DOM node that contains the frame. + * @type {Element} + */ get container() { return null; } + /** + * Gets the ID of the frame. + * @type {number} + */ get frameId() { return this._frameId; } // Public functions + /** + * Sets the options context for the popup. + * @param {object} optionsContext The options context object. + * @returns {Promise<void>} + */ setOptionsContext(optionsContext, source) { return this._invokeSafe('setOptionsContext', {id: this._id, optionsContext, source}); } + /** + * Hides the popup. + * @param {boolean} changeFocus Whether or not the parent popup or host frame should be focused. + * @returns {Promise<void>} + */ hide(changeFocus) { return this._invokeSafe('hide', {id: this._id, changeFocus}); } + /** + * Returns whether or not the popup is currently visible. + * @returns {Promise<boolean>} `true` if the popup is visible, `false` otherwise. + */ isVisible() { return this._invokeSafe('isVisible', {id: this._id}, false); } + /** + * Force assigns the visibility of the popup. + * @param {boolean} value Whether or not the popup should be visible. + * @param {number} priority The priority of the override. + * @returns {Promise<string?>} A token used which can be passed to `clearVisibleOverride`, + * or null if the override wasn't assigned. + */ setVisibleOverride(value, priority) { return this._invokeSafe('setVisibleOverride', {id: this._id, value, priority}, null); } + /** + * Clears a visibility override that was generated by `setVisibleOverride`. + * @param {string} token The token returned from `setVisibleOverride`. + * @returns {Promise<boolean>} `true` if the override existed and was removed, `false` otherwise. + */ clearVisibleOverride(token) { return this._invokeSafe('clearVisibleOverride', {id: this._id, token}, false); } + /** + * Checks whether a point is contained within the popup's rect. + * @param {number} x The x coordinate. + * @param {number} y The y coordinate. + * @returns {Promise<boolean>} `true` if the point is contained within the popup's rect, `false` otherwise. + */ async containsPoint(x, y) { if (this._frameOffsetForwarder !== null) { await this._updateFrameOffset(); @@ -102,6 +187,12 @@ class PopupProxy extends EventDispatcher { return await this._invokeSafe('containsPoint', {id: this._id, x, y}, false); } + /** + * Shows and updates the positioning and content of the popup. + * @param {{optionsContext: object, elementRect: {x: number, y: number, width: number, height: number}, writingMode: string}} details Settings for the outer popup. + * @param {object} displayDetails The details parameter passed to `Display.setContent`; see that function for details. + * @returns {Promise<void>} + */ async showContent(details, displayDetails) { const {elementRect} = details; if (typeof elementRect !== 'undefined' && this._frameOffsetForwarder !== null) { @@ -111,38 +202,83 @@ class PopupProxy extends EventDispatcher { return await this._invokeSafe('showContent', {id: this._id, details, displayDetails}); } + /** + * Sets the custom styles for the popup content. + * @param {string} css The CSS rules. + * @returns {Promise<void>} + */ setCustomCss(css) { return this._invokeSafe('setCustomCss', {id: this._id, css}); } + /** + * Stops the audio auto-play timer, if one has started. + * @returns {Promise<void>} + */ clearAutoPlayTimer() { return this._invokeSafe('clearAutoPlayTimer', {id: this._id}); } + /** + * Sets the scaling factor of the popup content. + * @param {number} scale The scaling factor. + * @returns {Promise<void>} + */ setContentScale(scale) { return this._invokeSafe('setContentScale', {id: this._id, scale}); } + /** + * Returns whether or not the popup is currently visible, synchronously. + * @returns {boolean} `true` if the popup is visible, `false` otherwise. + * @throws An exception is thrown for `PopupProxy` since it cannot synchronously detect visibility. + */ isVisibleSync() { throw new Error('Not supported on PopupProxy'); } + /** + * Updates the outer theme of the popup. + * @returns {Promise<void>} + */ updateTheme() { return this._invokeSafe('updateTheme', {id: this._id}); } + /** + * Sets the custom styles for the outer popup container. + * @param {string} css The CSS rules. + * @param {boolean} useWebExtensionApi Whether or not web extension APIs should be used to inject the rules. + * When web extension APIs are used, a DOM node is not generated, making it harder to detect the changes. + * @returns {Promise<void>} + */ setCustomOuterCss(css, useWebExtensionApi) { return this._invokeSafe('setCustomOuterCss', {id: this._id, css, useWebExtensionApi}); } + /** + * Gets the rectangle of the DOM frame, synchronously. + * @returns {{x: number, y: number, width: number, height: number, valid: boolean}} The rect. + * `valid` is `false` for `PopupProxy`, since the DOM node is hosted in a different frame. + */ getFrameRect() { return {x: 0, y: 0, width: 0, height: 0, valid: false}; } + /** + * Gets the size of the DOM frame. + * @returns {Promise<{width: number, height: number, valid: boolean}>} The size and whether or not it is valid. + */ getFrameSize() { return this._invokeSafe('popup.getFrameSize', {id: this._id}, {width: 0, height: 0, valid: false}); } + /** + * Sets the size of the DOM frame. + * @param {number} width The desired width of the popup. + * @param {number} height The desired height of the popup. + * @returns {Promise<boolean>} `true` if the size assignment was successful, `false` otherwise. + */ setFrameSize(width, height) { return this._invokeSafe('popup.setFrameSize', {id: this._id, width, height}); } diff --git a/ext/js/app/popup-window.js b/ext/js/app/popup-window.js index 6f86c61e..61c627b8 100644 --- a/ext/js/app/popup-window.js +++ b/ext/js/app/popup-window.js @@ -15,7 +15,17 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ +/** + * This class represents a popup that is hosted in a new native window. + */ class PopupWindow extends EventDispatcher { + /** + * Creates a new instance. + * @param {object} details + * @param {string} details.id The ID of the popup. + * @param {number} details.depth The depth of the popup. + * @param {number} details.frameId The ID of the host frame. + */ constructor({ id, depth, @@ -30,6 +40,10 @@ class PopupWindow extends EventDispatcher { // Public properties + /** + * The ID of the popup. + * @type {string} + */ get id() { return this._id; } @@ -38,30 +52,63 @@ class PopupWindow extends EventDispatcher { return null; } + /** + * The parent of the popup, which is always `null` for `PopupWindow` instances, + * since any potential parent popups are in a different frame. + * @param {Popup} value + * @type {Popup} + */ set parent(value) { throw new Error('Not supported on PopupProxy'); } + /** + * The popup child popup, which is always null for `PopupWindow` instances, + * since any potential child popups are in a different frame. + * @type {Popup} + */ get child() { return null; } + /** + * Attempts to set the child popup. + * @param {Popup} value + * @throws Throws an error, since this class doesn't support children. + */ set child(value) { throw new Error('Not supported on PopupProxy'); } + /** + * The depth of the popup. + * @type {numer} + */ get depth() { return this._depth; } + /** + * Gets the content window of the frame. This value is null, + * since the window is hosted in a different frame. + * @type {Window} + */ get frameContentWindow() { return null; } + /** + * Gets the DOM node that contains the frame. + * @type {Element} + */ get container() { return null; } + /** + * Gets the ID of the frame. + * @type {number} + */ get frameId() { return this._frameId; } @@ -69,67 +116,145 @@ class PopupWindow extends EventDispatcher { // Public functions + /** + * Sets the options context for the popup. + * @param {object} optionsContext The options context object. + * @returns {Promise<void>} + */ setOptionsContext(optionsContext, source) { return this._invoke(false, 'setOptionsContext', {id: this._id, optionsContext, source}); } + /** + * Hides the popup. This does nothing for `PopupWindow`. + * @param {boolean} _changeFocus Whether or not the parent popup or host frame should be focused. + */ hide(_changeFocus) { // NOP } + /** + * Returns whether or not the popup is currently visible. + * @returns {Promise<boolean>} `true` if the popup is visible, `false` otherwise. + */ async isVisible() { return (this._popupTabId !== null && await yomichan.api.isTabSearchPopup(this._popupTabId)); } + /** + * Force assigns the visibility of the popup. + * @param {boolean} _value Whether or not the popup should be visible. + * @param {number} _priority The priority of the override. + * @returns {Promise<string?>} A token used which can be passed to `clearVisibleOverride`, + * or null if the override wasn't assigned. + */ async setVisibleOverride(_value, _priority) { return null; } + /** + * Clears a visibility override that was generated by `setVisibleOverride`. + * @param {string} _token The token returned from `setVisibleOverride`. + * @returns {boolean} `true` if the override existed and was removed, `false` otherwise. + */ clearVisibleOverride(_token) { return false; } + /** + * Checks whether a point is contained within the popup's rect. + * @param {number} _x The x coordinate. + * @param {number} _y The y coordinate. + * @returns {Promise<boolean>} `true` if the point is contained within the popup's rect, `false` otherwise. + */ async containsPoint(_x, _y) { return false; } + /** + * Shows and updates the positioning and content of the popup. + * @param {{optionsContext: object, elementRect: {x: number, y: number, width: number, height: number}, writingMode: string}} details Settings for the outer popup. + * @param {object} displayDetails The details parameter passed to `Display.setContent`; see that function for details. + * @returns {Promise<void>} + */ async showContent(_details, displayDetails) { if (displayDetails === null) { return; } await this._invoke(true, 'setContent', {id: this._id, details: displayDetails}); } + /** + * Sets the custom styles for the popup content. + * @param {string} css The CSS rules. + */ setCustomCss(css) { return this._invoke(false, 'setCustomCss', {id: this._id, css}); } + /** + * Stops the audio auto-play timer, if one has started. + */ clearAutoPlayTimer() { return this._invoke(false, 'clearAutoPlayTimer', {id: this._id}); } + /** + * Sets the scaling factor of the popup content. + * @param {number} scale The scaling factor. + */ setContentScale(_scale) { // NOP } + /** + * Returns whether or not the popup is currently visible, synchronously. + * @returns {boolean} `true` if the popup is visible, `false` otherwise. + * @throws An exception is thrown for `PopupWindow` since it cannot synchronously detect visibility. + */ isVisibleSync() { throw new Error('Not supported on PopupWindow'); } + /** + * Updates the outer theme of the popup. + */ updateTheme() { // NOP } + /** + * Sets the custom styles for the outer popup container. + * This does nothing for `PopupWindow`. + * @param {string} _css The CSS rules. + * @param {boolean} _useWebExtensionApi Whether or not web extension APIs should be used to inject the rules. + * When web extension APIs are used, a DOM node is not generated, making it harder to detect the changes. + */ async setCustomOuterCss(_css, _useWebExtensionApi) { // NOP } + /** + * Gets the rectangle of the DOM frame, synchronously. + * @returns {{x: number, y: number, width: number, height: number, valid: boolean}} The rect. + * `valid` is `false` for `PopupProxy`, since the DOM node is hosted in a different frame. + */ getFrameRect() { return {x: 0, y: 0, width: 0, height: 0, valid: false}; } + /** + * Gets the size of the DOM frame. + * @returns {Promise<{width: number, height: number, valid: boolean}>} The size and whether or not it is valid. + */ async getFrameSize() { return {width: 0, height: 0, valid: false}; } + /** + * Sets the size of the DOM frame. + * @param {number} _width The desired width of the popup. + * @param {number} _height The desired height of the popup. + * @returns {Promise<boolean>} `true` if the size assignment was successful, `false` otherwise. + */ async setFrameSize(_width, _height) { return false; } diff --git a/ext/js/app/popup.js b/ext/js/app/popup.js index 3a29563d..7581c8d7 100644 --- a/ext/js/app/popup.js +++ b/ext/js/app/popup.js @@ -21,7 +21,18 @@ * dynamicLoader */ +/** + * This class is the container which hosts the display of search results. + */ class Popup extends EventDispatcher { + /** + * Creates a new instance. + * @param {object} details + * @param {string} details.id The ID of the popup. + * @param {number} details.depth The depth of the popup. + * @param {number} details.frameId The ID of the host frame. + * @param {boolean} details.childrenSupported Whether or not the popup is able to show child popups. + */ constructor({ id, depth, @@ -59,44 +70,84 @@ class Popup extends EventDispatcher { // Public properties + /** + * The ID of the popup. + * @type {string} + */ get id() { return this._id; } + /** + * The parent of the popup. + * @type {Popup} + */ get parent() { return this._parent; } + /** + * Sets the parent popup. + * @param {Popup} value The parent popup to assign. + */ set parent(value) { this._parent = value; } + /** + * The child of the popup. + * @type {Popup} + */ get child() { return this._child; } + /** + * Sets the child popup. + * @param {Popup} value The child popup to assign. + */ set child(value) { this._child = value; } + /** + * The depth of the popup. + * @type {numer} + */ get depth() { return this._depth; } + /** + * Gets the content window of the frame, which can be `null` + * depending on the current state of the frame. + * @type {?Window} + */ get frameContentWindow() { return this._frame.contentWindow; } + /** + * Gets the DOM node that contains the frame. + * @type {Element} + */ get container() { return this._container; } + /** + * Gets the ID of the frame. + * @type {number} + */ get frameId() { return this._frameId; } // Public functions + /** + * Prepares the popup for use. + */ prepare() { this._frame.addEventListener('mouseover', this._onFrameMouseOver.bind(this)); this._frame.addEventListener('mouseout', this._onFrameMouseOut.bind(this)); @@ -108,11 +159,19 @@ class Popup extends EventDispatcher { this._onVisibleChange({value: this.isVisibleSync()}); } + /** + * Sets the options context for the popup. + * @param {object} optionsContext The options context object. + */ async setOptionsContext(optionsContext) { await this._setOptionsContext(optionsContext); await this._invokeSafe('setOptionsContext', {optionsContext}); } + /** + * Hides the popup. + * @param {boolean} changeFocus Whether or not the parent popup or host frame should be focused. + */ hide(changeFocus) { if (!this.isVisibleSync()) { return; @@ -127,18 +186,40 @@ class Popup extends EventDispatcher { } } + /** + * Returns whether or not the popup is currently visible. + * @returns {Promise<boolean>} `true` if the popup is visible, `false` otherwise. + */ async isVisible() { return this.isVisibleSync(); } + /** + * Force assigns the visibility of the popup. + * @param {boolean} value Whether or not the popup should be visible. + * @param {number} priority The priority of the override. + * @returns {Promise<string?>} A token used which can be passed to `clearVisibleOverride`, + * or null if the override wasn't assigned. + */ async setVisibleOverride(value, priority) { return this._visible.setOverride(value, priority); } + /** + * Clears a visibility override that was generated by `setVisibleOverride`. + * @param {string} token The token returned from `setVisibleOverride`. + * @returns {Promise<boolean>} `true` if the override existed and was removed, `false` otherwise. + */ async clearVisibleOverride(token) { return this._visible.clearOverride(token); } + /** + * Checks whether a point is contained within the popup's rect. + * @param {number} x The x coordinate. + * @param {number} y The y coordinate. + * @returns {Promise<boolean>} `true` if the point is contained within the popup's rect, `false` otherwise. + */ async containsPoint(x, y) { for (let popup = this; popup !== null && popup.isVisibleSync(); popup = popup.child) { const rect = popup.getFrameRect(); @@ -149,6 +230,12 @@ class Popup extends EventDispatcher { return false; } + /** + * Shows and updates the positioning and content of the popup. + * @param {{optionsContext: object, elementRect: {x: number, y: number, width: number, height: number}, writingMode: string}} details Settings for the outer popup. + * @param {object} displayDetails The details parameter passed to `Display.setContent`; see that function for details. + * @returns {Promise<void>} + */ async showContent(details, displayDetails) { if (this._options === null) { throw new Error('Options not assigned'); } @@ -166,24 +253,43 @@ class Popup extends EventDispatcher { } } + /** + * Sets the custom styles for the popup content. + * @param {string} css The CSS rules. + */ setCustomCss(css) { this._invokeSafe('setCustomCss', {css}); } + /** + * Stops the audio auto-play timer, if one has started. + */ clearAutoPlayTimer() { this._invokeSafe('clearAutoPlayTimer'); } + /** + * Sets the scaling factor of the popup content. + * @param {number} scale The scaling factor. + */ setContentScale(scale) { this._contentScale = scale; this._frame.style.fontSize = `${scale}px`; this._invokeSafe('setContentScale', {scale}); } + /** + * Returns whether or not the popup is currently visible, synchronously. + * @returns {boolean} `true` if the popup is visible, `false` otherwise. + */ isVisibleSync() { return this._visible.value; } + /** + * Updates the outer theme of the popup. + * @returns {Promise<void>} + */ updateTheme() { const {popupTheme, popupOuterTheme} = this._options.general; this._frame.dataset.theme = popupTheme; @@ -191,6 +297,12 @@ class Popup extends EventDispatcher { this._frame.dataset.siteColor = this._getSiteColor(); } + /** + * Sets the custom styles for the outer popup container. + * @param {string} css The CSS rules. + * @param {boolean} useWebExtensionApi Whether or not web extension APIs should be used to inject the rules. + * When web extension APIs are used, a DOM node is not generated, making it harder to detect the changes. + */ async setCustomOuterCss(css, useWebExtensionApi) { let parentNode = null; const inShadow = (this._shadow !== null); @@ -202,16 +314,31 @@ class Popup extends EventDispatcher { this.trigger('customOuterCssChanged', {node, useWebExtensionApi, inShadow}); } + /** + * Gets the rectangle of the DOM frame, synchronously. + * @returns {{x: number, y: number, width: number, height: number, valid: boolean}} The rect. + * `valid` is `false` for `PopupProxy`, since the DOM node is hosted in a different frame. + */ getFrameRect() { const {left, top, width, height} = this._frame.getBoundingClientRect(); return {x: left, y: top, width, height, valid: true}; } + /** + * Gets the size of the DOM frame. + * @returns {Promise<{width: number, height: number, valid: boolean}>} The size and whether or not it is valid. + */ async getFrameSize() { const {width, height} = this._frame.getBoundingClientRect(); return {width, height, valid: true}; } + /** + * Sets the size of the DOM frame. + * @param {number} width The desired width of the popup. + * @param {number} height The desired height of the popup. + * @returns {Promise<boolean>} `true` if the size assignment was successful, `false` otherwise. + */ async setFrameSize(width, height) { this._setFrameSize(width, height); return true; |