diff options
Diffstat (limited to 'browser/components/pagedata/PageDataChild.sys.mjs')
-rw-r--r-- | browser/components/pagedata/PageDataChild.sys.mjs | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/browser/components/pagedata/PageDataChild.sys.mjs b/browser/components/pagedata/PageDataChild.sys.mjs new file mode 100644 index 0000000000..8a68e65e4a --- /dev/null +++ b/browser/components/pagedata/PageDataChild.sys.mjs @@ -0,0 +1,122 @@ +/* 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + PageDataSchema: "resource:///modules/pagedata/PageDataSchema.sys.mjs", + PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs", +}); + +// We defer any attempt to check for page data for a short time after a page +// loads to allow JS to operate. +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "READY_DELAY", + "browser.pagedata.readyDelay", + 500 +); + +/** + * The actor responsible for monitoring a page for page data. + */ +export class PageDataChild extends JSWindowActorChild { + #isContentWindowPrivate = true; + /** + * Used to debounce notifications about a page being ready. + * + * @type {Timer | null} + */ + #deferTimer = null; + + /** + * Called when the actor is created for a new page. + */ + actorCreated() { + this.#isContentWindowPrivate = lazy.PrivateBrowsingUtils.isContentWindowPrivate( + this.contentWindow + ); + } + + /** + * Called when the page is destroyed. + */ + didDestroy() { + if (this.#deferTimer) { + this.#deferTimer.cancel(); + } + } + + /** + * Called when the page has signalled it is done loading. This signal is + * debounced by READY_DELAY. + */ + #deferReady() { + if (!this.#deferTimer) { + this.#deferTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + } + + // If the timer was already running this re-starts it. + this.#deferTimer.initWithCallback( + () => { + this.#deferTimer = null; + this.sendAsyncMessage("PageData:DocumentReady", { + url: this.document.documentURI, + }); + }, + lazy.READY_DELAY, + Ci.nsITimer.TYPE_ONE_SHOT_LOW_PRIORITY + ); + } + + /** + * Called when a message is received from the parent process. + * + * @param {ReceiveMessageArgument} msg + * The received message. + * + * @returns {Promise | undefined} + * A promise for the requested data or undefined if no data was requested. + */ + receiveMessage(msg) { + if (this.#isContentWindowPrivate) { + return undefined; + } + + switch (msg.name) { + case "PageData:CheckLoaded": + // The service just started in the parent. Check if this document is + // already loaded. + if (this.document.readystate == "complete") { + this.#deferReady(); + } + break; + case "PageData:Collect": + return lazy.PageDataSchema.collectPageData(this.document); + } + + return undefined; + } + + /** + * DOM event handler. + * + * @param {Event} event + * The DOM event. + */ + handleEvent(event) { + if (this.#isContentWindowPrivate) { + return; + } + + switch (event.type) { + case "DOMContentLoaded": + case "pageshow": + this.#deferReady(); + break; + } + } +} |