summaryrefslogtreecommitdiffstats
path: root/toolkit/components/webextensions/ext-webNavigation.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/webextensions/ext-webNavigation.js')
-rw-r--r--toolkit/components/webextensions/ext-webNavigation.js192
1 files changed, 192 insertions, 0 deletions
diff --git a/toolkit/components/webextensions/ext-webNavigation.js b/toolkit/components/webextensions/ext-webNavigation.js
new file mode 100644
index 000000000..904f3a4a7
--- /dev/null
+++ b/toolkit/components/webextensions/ext-webNavigation.js
@@ -0,0 +1,192 @@
+"use strict";
+
+var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
+ "resource://gre/modules/ExtensionManagement.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "MatchURLFilters",
+ "resource://gre/modules/MatchPattern.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "WebNavigation",
+ "resource://gre/modules/WebNavigation.jsm");
+
+Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+var {
+ SingletonEventManager,
+ ignoreEvent,
+} = 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"],
+ },
+};
+
+function isTopLevelFrame({frameId, parentFrameId}) {
+ return frameId == 0 && parentFrameId == -1;
+}
+
+function fillTransitionProperties(eventName, src, dst) {
+ if (eventName == "onCommitted" || eventName == "onHistoryStateUpdated") {
+ 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;
+ }
+}
+
+// Similar to WebRequestEventManager but for WebNavigation.
+function WebNavigationEventManager(context, eventName) {
+ let name = `webNavigation.${eventName}`;
+ let register = (callback, urlFilters) => {
+ // Don't create a MatchURLFilters instance if the listener does not include any filter.
+ let filters = urlFilters ?
+ new MatchURLFilters(urlFilters.url) : null;
+
+ let listener = data => {
+ if (!data.browser) {
+ return;
+ }
+
+ let data2 = {
+ url: data.url,
+ timeStamp: Date.now(),
+ frameId: ExtensionManagement.getFrameId(data.windowId),
+ parentFrameId: ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId),
+ };
+
+ if (eventName == "onErrorOccurred") {
+ data2.error = data.error;
+ }
+
+ // Fills in tabId typically.
+ extensions.emit("fill-browser-data", data.browser, data2);
+ if (data2.tabId < 0) {
+ return;
+ }
+
+ fillTransitionProperties(eventName, data, data2);
+
+ context.runSafe(callback, data2);
+ };
+
+ WebNavigation[eventName].addListener(listener, filters);
+ return () => {
+ WebNavigation[eventName].removeListener(listener);
+ };
+ };
+
+ return SingletonEventManager.call(this, context, name, register);
+}
+
+WebNavigationEventManager.prototype = Object.create(SingletonEventManager.prototype);
+
+function convertGetFrameResult(tabId, data) {
+ return {
+ errorOccurred: data.errorOccurred,
+ url: data.url,
+ tabId,
+ frameId: ExtensionManagement.getFrameId(data.windowId),
+ parentFrameId: ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId),
+ };
+}
+
+extensions.registerSchemaAPI("webNavigation", "addon_parent", context => {
+ return {
+ webNavigation: {
+ onTabReplaced: ignoreEvent(context, "webNavigation.onTabReplaced"),
+ onBeforeNavigate: new WebNavigationEventManager(context, "onBeforeNavigate").api(),
+ onCommitted: new WebNavigationEventManager(context, "onCommitted").api(),
+ onDOMContentLoaded: new WebNavigationEventManager(context, "onDOMContentLoaded").api(),
+ onCompleted: new WebNavigationEventManager(context, "onCompleted").api(),
+ onErrorOccurred: new WebNavigationEventManager(context, "onErrorOccurred").api(),
+ onReferenceFragmentUpdated: new WebNavigationEventManager(context, "onReferenceFragmentUpdated").api(),
+ onHistoryStateUpdated: new WebNavigationEventManager(context, "onHistoryStateUpdated").api(),
+ onCreatedNavigationTarget: ignoreEvent(context, "webNavigation.onCreatedNavigationTarget"),
+ getAllFrames(details) {
+ let tab = TabManager.getTab(details.tabId, context);
+
+ let {innerWindowID, messageManager} = tab.linkedBrowser;
+ let recipient = {innerWindowID};
+
+ return context.sendMessage(messageManager, "WebNavigation:GetAllFrames", {}, {recipient})
+ .then((results) => results.map(convertGetFrameResult.bind(null, details.tabId)));
+ },
+ getFrame(details) {
+ let tab = TabManager.getTab(details.tabId, context);
+
+ let recipient = {
+ innerWindowID: tab.linkedBrowser.innerWindowID,
+ };
+
+ let mm = tab.linkedBrowser.messageManager;
+ return context.sendMessage(mm, "WebNavigation:GetFrame", {options: details}, {recipient})
+ .then((result) => {
+ return result ?
+ convertGetFrameResult(details.tabId, result) :
+ Promise.reject({message: `No frame found with frameId: ${details.frameId}`});
+ });
+ },
+ },
+ };
+});