summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/parent/ext-webNavigation.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/components/extensions/parent/ext-webNavigation.js
parentInitial commit. (diff)
downloadfirefox-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/parent/ext-webNavigation.js')
-rw-r--r--toolkit/components/extensions/parent/ext-webNavigation.js276
1 files changed, 276 insertions, 0 deletions
diff --git a/toolkit/components/extensions/parent/ext-webNavigation.js b/toolkit/components/extensions/parent/ext-webNavigation.js
new file mode 100644
index 0000000000..c65b61041b
--- /dev/null
+++ b/toolkit/components/extensions/parent/ext-webNavigation.js
@@ -0,0 +1,276 @@
+/* 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/. */
+
+"use strict";
+
+// This file expects tabTracker to be defined in the global scope (e.g.
+// by ext-browser.js or ext-android.js).
+/* global tabTracker */
+
+ChromeUtils.defineESModuleGetters(this, {
+ MatchURLFilters: "resource://gre/modules/MatchURLFilters.sys.mjs",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
+ WebNavigation: "resource://gre/modules/WebNavigation.sys.mjs",
+ WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.sys.mjs",
+});
+
+var { ExtensionError } = ExtensionUtils;
+
+const defaultTransitionTypes = {
+ topFrame: "link",
+ subFrame: "auto_subframe",
+};
+
+const frameTransitions = {
+ anyFrame: {
+ qualifiers: ["server_redirect", "client_redirect", "forward_back"],
+ },
+ topFrame: {
+ types: ["reload", "form_submit"],
+ },
+};
+
+const tabTransitions = {
+ topFrame: {
+ qualifiers: ["from_address_bar"],
+ types: ["auto_bookmark", "typed", "keyword", "generated", "link"],
+ },
+ subFrame: {
+ types: ["manual_subframe"],
+ },
+};
+
+const isTopLevelFrame = ({ frameId, parentFrameId }) => {
+ return frameId == 0 && parentFrameId == -1;
+};
+
+const fillTransitionProperties = (eventName, src, dst) => {
+ if (
+ eventName == "onCommitted" ||
+ eventName == "onHistoryStateUpdated" ||
+ eventName == "onReferenceFragmentUpdated"
+ ) {
+ let frameTransitionData = src.frameTransitionData || {};
+ let tabTransitionData = src.tabTransitionData || {};
+
+ let transitionType,
+ transitionQualifiers = [];
+
+ // Fill transition properties for any frame.
+ for (let qualifier of frameTransitions.anyFrame.qualifiers) {
+ if (frameTransitionData[qualifier]) {
+ transitionQualifiers.push(qualifier);
+ }
+ }
+
+ if (isTopLevelFrame(dst)) {
+ for (let type of frameTransitions.topFrame.types) {
+ if (frameTransitionData[type]) {
+ transitionType = type;
+ }
+ }
+
+ for (let qualifier of tabTransitions.topFrame.qualifiers) {
+ if (tabTransitionData[qualifier]) {
+ transitionQualifiers.push(qualifier);
+ }
+ }
+
+ for (let type of tabTransitions.topFrame.types) {
+ if (tabTransitionData[type]) {
+ transitionType = type;
+ }
+ }
+
+ // If transitionType is not defined, defaults it to "link".
+ if (!transitionType) {
+ transitionType = defaultTransitionTypes.topFrame;
+ }
+ } else {
+ // If it is sub-frame, transitionType defaults it to "auto_subframe",
+ // "manual_subframe" is set only in case of a recent user interaction.
+ transitionType = tabTransitionData.link
+ ? "manual_subframe"
+ : defaultTransitionTypes.subFrame;
+ }
+
+ // Fill the transition properties in the webNavigation event object.
+ dst.transitionType = transitionType;
+ dst.transitionQualifiers = transitionQualifiers;
+ }
+};
+
+this.webNavigation = class extends ExtensionAPIPersistent {
+ makeEventHandler(event) {
+ let { extension } = this;
+ let { tabManager } = extension;
+ return ({ fire }, params) => {
+ // Don't create a MatchURLFilters instance if the listener does not include any filter.
+ let [urlFilters] = params;
+ let filters = urlFilters ? new MatchURLFilters(urlFilters.url) : null;
+
+ let listener = data => {
+ if (!data.browser) {
+ return;
+ }
+ if (
+ !extension.privateBrowsingAllowed &&
+ PrivateBrowsingUtils.isBrowserPrivate(data.browser)
+ ) {
+ return;
+ }
+ if (filters && !filters.matches(data.url)) {
+ return;
+ }
+
+ let data2 = {
+ url: data.url,
+ timeStamp: Date.now(),
+ };
+
+ if (event == "onErrorOccurred") {
+ data2.error = data.error;
+ }
+
+ if (data.frameId != undefined) {
+ data2.frameId = data.frameId;
+ data2.parentFrameId = data.parentFrameId;
+ }
+
+ if (data.sourceFrameId != undefined) {
+ data2.sourceFrameId = data.sourceFrameId;
+ }
+
+ // Do not send a webNavigation event when the data.browser is related to a tab from a
+ // new window opened to adopt an existent tab (See Bug 1443221 for a rationale).
+ const chromeWin = data.browser.ownerGlobal;
+
+ if (
+ chromeWin &&
+ chromeWin.gBrowser &&
+ chromeWin.gBrowserInit &&
+ chromeWin.gBrowserInit.isAdoptingTab() &&
+ chromeWin.gBrowser.selectedBrowser === data.browser
+ ) {
+ return;
+ }
+
+ // Fills in tabId typically.
+ Object.assign(data2, tabTracker.getBrowserData(data.browser));
+ if (data2.tabId < 0) {
+ return;
+ }
+ let tab = tabTracker.getTab(data2.tabId);
+ if (!tabManager.canAccessTab(tab)) {
+ return;
+ }
+
+ if (data.sourceTabBrowser) {
+ data2.sourceTabId = tabTracker.getBrowserData(
+ data.sourceTabBrowser
+ ).tabId;
+ }
+
+ fillTransitionProperties(event, data, data2);
+
+ fire.async(data2);
+ };
+
+ WebNavigation[event].addListener(listener);
+ return {
+ unregister() {
+ WebNavigation[event].removeListener(listener);
+ },
+ convert(_fire) {
+ fire = _fire;
+ },
+ };
+ };
+ }
+
+ makeEventManagerAPI(event, context) {
+ let self = this;
+ return new EventManager({
+ context,
+ module: "webNavigation",
+ event,
+ register(fire, ...params) {
+ let fn = self.makeEventHandler(event);
+ return fn({ fire }, params).unregister;
+ },
+ }).api();
+ }
+
+ PERSISTENT_EVENTS = {
+ onBeforeNavigate: this.makeEventHandler("onBeforeNavigate"),
+ onCommitted: this.makeEventHandler("onCommitted"),
+ onDOMContentLoaded: this.makeEventHandler("onDOMContentLoaded"),
+ onCompleted: this.makeEventHandler("onCompleted"),
+ onErrorOccurred: this.makeEventHandler("onErrorOccurred"),
+ onReferenceFragmentUpdated: this.makeEventHandler(
+ "onReferenceFragmentUpdated"
+ ),
+ onHistoryStateUpdated: this.makeEventHandler("onHistoryStateUpdated"),
+ onCreatedNavigationTarget: this.makeEventHandler(
+ "onCreatedNavigationTarget"
+ ),
+ };
+
+ getAPI(context) {
+ let { extension } = context;
+ let { tabManager } = extension;
+
+ return {
+ webNavigation: {
+ // onTabReplaced does nothing, it exists for compat.
+ onTabReplaced: new EventManager({
+ context,
+ name: "webNavigation.onTabReplaced",
+ register: fire => {
+ return () => {};
+ },
+ }).api(),
+ onBeforeNavigate: this.makeEventManagerAPI("onBeforeNavigate", context),
+ onCommitted: this.makeEventManagerAPI("onCommitted", context),
+ onDOMContentLoaded: this.makeEventManagerAPI(
+ "onDOMContentLoaded",
+ context
+ ),
+ onCompleted: this.makeEventManagerAPI("onCompleted", context),
+ onErrorOccurred: this.makeEventManagerAPI("onErrorOccurred", context),
+ onReferenceFragmentUpdated: this.makeEventManagerAPI(
+ "onReferenceFragmentUpdated",
+ context
+ ),
+ onHistoryStateUpdated: this.makeEventManagerAPI(
+ "onHistoryStateUpdated",
+ context
+ ),
+ onCreatedNavigationTarget: this.makeEventManagerAPI(
+ "onCreatedNavigationTarget",
+ context
+ ),
+ getAllFrames({ tabId }) {
+ let tab = tabManager.get(tabId);
+ if (tab.discarded) {
+ return null;
+ }
+ let frames = WebNavigationFrames.getAllFrames(tab.browsingContext);
+ return frames.map(fd => ({ tabId, ...fd }));
+ },
+ getFrame({ tabId, frameId }) {
+ let tab = tabManager.get(tabId);
+ if (tab.discarded) {
+ return null;
+ }
+ let fd = WebNavigationFrames.getFrame(tab.browsingContext, frameId);
+ if (!fd) {
+ throw new ExtensionError(`No frame found with frameId: ${frameId}`);
+ }
+ return { tabId, ...fd };
+ },
+ },
+ };
+ }
+};