diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /toolkit/components/extensions/ext-browser-content.js | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/extensions/ext-browser-content.js')
-rw-r--r-- | toolkit/components/extensions/ext-browser-content.js | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/toolkit/components/extensions/ext-browser-content.js b/toolkit/components/extensions/ext-browser-content.js new file mode 100644 index 0000000000..c431b6ccfd --- /dev/null +++ b/toolkit/components/extensions/ext-browser-content.js @@ -0,0 +1,275 @@ +/* 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-env mozilla/frame-script */ + +"use strict"; + +ChromeUtils.defineESModuleGetters(this, { + ExtensionCommon: "resource://gre/modules/ExtensionCommon.sys.mjs", + clearTimeout: "resource://gre/modules/Timer.sys.mjs", + setTimeout: "resource://gre/modules/Timer.sys.mjs", +}); + +// Minimum time between two resizes. +const RESIZE_TIMEOUT = 100; + +const BrowserListener = { + init({ + allowScriptsToClose, + blockParser, + fixedWidth, + maxHeight, + maxWidth, + stylesheets, + isInline, + }) { + this.fixedWidth = fixedWidth; + this.stylesheets = stylesheets || []; + + this.isInline = isInline; + this.maxWidth = maxWidth; + this.maxHeight = maxHeight; + + this.blockParser = blockParser; + this.needsResize = fixedWidth || maxHeight || maxWidth; + + this.oldBackground = null; + + if (allowScriptsToClose) { + content.windowUtils.allowScriptsToClose(); + } + + if (this.blockParser) { + this.blockingPromise = new Promise(resolve => { + this.unblockParser = resolve; + }); + addEventListener("DOMDocElementInserted", this, true); + } + + addEventListener("load", this, true); + addEventListener("DOMWindowCreated", this, true); + addEventListener("DOMContentLoaded", this, true); + addEventListener("MozScrolledAreaChanged", this, true); + }, + + destroy() { + if (this.blockParser) { + removeEventListener("DOMDocElementInserted", this, true); + } + + removeEventListener("load", this, true); + removeEventListener("DOMWindowCreated", this, true); + removeEventListener("DOMContentLoaded", this, true); + removeEventListener("MozScrolledAreaChanged", this, true); + }, + + receiveMessage({ name, data }) { + if (name === "Extension:InitBrowser") { + this.init(data); + } else if (name === "Extension:UnblockParser") { + if (this.unblockParser) { + this.unblockParser(); + this.blockingPromise = null; + } + } else if (name === "Extension:GrabFocus") { + content.window.requestAnimationFrame(() => { + Services.focus.focusedWindow = content.window; + }); + } + }, + + loadStylesheets() { + let { windowUtils } = content; + + for (let url of this.stylesheets) { + windowUtils.addSheet( + ExtensionCommon.stylesheetMap.get(url), + windowUtils.AUTHOR_SHEET + ); + } + }, + + handleEvent(event) { + switch (event.type) { + case "DOMDocElementInserted": + if (this.blockingPromise) { + const doc = event.target; + const policy = doc?.nodePrincipal?.addonPolicy; + event.target.blockParsing(this.blockingPromise).then(() => { + policy?.weakExtension?.get()?.untrackBlockedParsingDocument(doc); + }); + policy?.weakExtension?.get()?.trackBlockedParsingDocument(doc); + } + break; + + case "DOMWindowCreated": + if (event.target === content.document) { + this.loadStylesheets(); + } + break; + + case "DOMContentLoaded": + if (event.target === content.document) { + sendAsyncMessage("Extension:BrowserContentLoaded", { + url: content.location.href, + }); + + if (this.needsResize) { + this.handleDOMChange(true); + } + } + break; + + case "load": + if (event.target.contentWindow === content) { + // For about:addons inline <browser>s, we currently receive a load + // event on the <browser> element, but no load or DOMContentLoaded + // events from the content window. + + // Inline browsers don't receive the "DOMWindowCreated" event, so this + // is a workaround to load the stylesheets. + if (this.isInline) { + this.loadStylesheets(); + } + sendAsyncMessage("Extension:BrowserContentLoaded", { + url: content.location.href, + }); + } else if (event.target !== content.document) { + break; + } + + if (!this.needsResize) { + break; + } + + // We use a capturing listener, so we get this event earlier than any + // load listeners in the content page. Resizing after a timeout ensures + // that we calculate the size after the entire event cycle has completed + // (unless someone spins the event loop, anyway), and hopefully after + // the content has made any modifications. + Promise.resolve().then(() => { + this.handleDOMChange(true); + }); + + // Mutation observer to make sure the panel shrinks when the content does. + new content.MutationObserver(this.handleDOMChange.bind(this)).observe( + content.document.documentElement, + { + attributes: true, + characterData: true, + childList: true, + subtree: true, + } + ); + break; + + case "MozScrolledAreaChanged": + if (this.needsResize) { + this.handleDOMChange(); + } + break; + } + }, + + // Resizes the browser to match the preferred size of the content (debounced). + handleDOMChange(ignoreThrottling = false) { + if (ignoreThrottling && this.resizeTimeout) { + clearTimeout(this.resizeTimeout); + this.resizeTimeout = null; + } + + if (this.resizeTimeout == null) { + this.resizeTimeout = setTimeout(() => { + try { + if (content) { + this._handleDOMChange("delayed"); + } + } finally { + this.resizeTimeout = null; + } + }, RESIZE_TIMEOUT); + + this._handleDOMChange(); + } + }, + + _handleDOMChange(detail) { + let doc = content.document; + + let body = doc.body; + if (!body || doc.compatMode === "BackCompat") { + // In quirks mode, the root element is used as the scroll frame, and the + // body lies about its scroll geometry, and returns the values for the + // root instead. + body = doc.documentElement; + } + + let result; + const zoom = content.browsingContext.fullZoom; + if (this.fixedWidth) { + // If we're in a fixed-width area (namely a slide-in subview of the main + // menu panel), we need to calculate the view height based on the + // preferred height of the content document's root scrollable element at the + // current width, rather than the complete preferred dimensions of the + // content window. + + // Compensate for any offsets (margin, padding, ...) between the scroll + // area of the body and the outer height of the document. + // This calculation is hard to get right for all cases, so take the lower + // number of the combination of all padding and margins of the document + // and body elements, or the difference between their heights. + let getHeight = elem => elem.getBoundingClientRect(elem).height; + let bodyPadding = getHeight(doc.documentElement) - getHeight(body); + + if (body !== doc.documentElement) { + let bs = content.getComputedStyle(body); + let ds = content.getComputedStyle(doc.documentElement); + + let p = + parseFloat(bs.marginTop) + + parseFloat(bs.marginBottom) + + parseFloat(ds.marginTop) + + parseFloat(ds.marginBottom) + + parseFloat(ds.paddingTop) + + parseFloat(ds.paddingBottom); + bodyPadding = Math.min(p, bodyPadding); + } + + let height = Math.ceil((body.scrollHeight + bodyPadding) * zoom); + + result = { height, detail }; + } else { + let background = content.windowUtils.canvasBackgroundColor; + if (background !== this.oldBackground) { + sendAsyncMessage("Extension:BrowserBackgroundChanged", { background }); + } + this.oldBackground = background; + + // Adjust the size of the browser based on its content's preferred size. + let w = {}, + h = {}; + docShell.docViewer.getContentSize( + this.maxWidth, + this.maxHeight, + /* prefWidth = */ 0, + w, + h + ); + + let width = Math.ceil(w.value * zoom); + let height = Math.ceil(h.value * zoom); + result = { width, height, detail }; + } + + sendAsyncMessage("Extension:BrowserResized", result); + }, +}; + +addMessageListener("Extension:InitBrowser", BrowserListener); +addMessageListener("Extension:UnblockParser", BrowserListener); +addMessageListener("Extension:GrabFocus", BrowserListener); + +// This is a temporary hack to prevent regressions (bug 1471327). +void content; |