--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -5734,24 +5734,29 @@ function handleLinkClick(event, href, li
linkNode) {
let referrerAttrValue = Services.netUtils.parseAttributePolicyString(linkNode.
getAttribute("referrerpolicy"));
if (referrerAttrValue != Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
referrerPolicy = referrerAttrValue;
}
}
+ let frameOuterWindowID = doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .outerWindowID;
+
urlSecurityCheck(href, doc.nodePrincipal);
let params = {
charset: doc.characterSet,
allowMixedContent: persistAllowMixedContentInChildTab,
referrerURI,
referrerPolicy,
noReferrer: BrowserUtils.linkHasNoReferrer(linkNode),
originPrincipal: doc.nodePrincipal,
+ frameOuterWindowID,
};
// The new tab/window must use the same userContextId
if (doc.nodePrincipal.originAttributes.userContextId) {
params.userContextId = doc.nodePrincipal.originAttributes.userContextId;
}
openLinkIn(href, where, params);
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -496,20 +496,24 @@ var ClickEventHandler = {
node) {
let referrerAttrValue = Services.netUtils.parseAttributePolicyString(node.
getAttribute("referrerpolicy"));
if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
referrerPolicy = referrerAttrValue;
}
}
+ let frameOuterWindowID = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .outerWindowID;
+
let json = { button: event.button, shiftKey: event.shiftKey,
ctrlKey: event.ctrlKey, metaKey: event.metaKey,
altKey: event.altKey, href: null, title: null,
- bookmark: false, referrerPolicy,
+ bookmark: false, frameOuterWindowID, referrerPolicy,
triggeringPrincipal: principal,
originAttributes: principal ? principal.originAttributes : {},
isContentWindowPrivate: PrivateBrowsingUtils.isContentWindowPrivate(ownerDoc.defaultView)};
if (href) {
try {
BrowserUtils.urlSecurityCheck(href, principal);
} catch (e) {
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -946,21 +946,27 @@ nsContextMenu.prototype = {
this.target.mediaKeys.keySystem != "org.w3.clearkey";
},
_openLinkInParameters : function (extra) {
let params = { charset: gContextMenuContentData.charSet,
originPrincipal: this.principal,
referrerURI: gContextMenuContentData.documentURIObject,
referrerPolicy: gContextMenuContentData.referrerPolicy,
+ frameOuterWindowID: gContextMenuContentData.frameOuterWindowID,
noReferrer: this.linkHasNoReferrer };
for (let p in extra) {
params[p] = extra[p];
}
+ if (!this.isRemote) {
+ params.frameOuterWindowID = this.target.ownerGlobal
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
+ }
// If we want to change userContextId, we must be sure that we don't
// propagate the referrer.
if ("userContextId" in params &&
params.userContextId != gContextMenuContentData.userContextId) {
params.noReferrer = true;
}
return params;
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -299,17 +299,40 @@ function openLinkIn(url, where, params)
sa.appendElement(userContextIdSupports, /* weak =*/ false);
sa.appendElement(aPrincipal, /* weak =*/ false);
let features = "chrome,dialog=no,all";
if (aIsPrivate) {
features += ",private";
}
- Services.ww.openWindow(w || window, getBrowserURL(), null, features, sa);
+ const sourceWindow = (w || window);
+ let win;
+ if (params.frameOuterWindowID && sourceWindow) {
+ // Only notify it as a WebExtensions' webNavigation.onCreatedNavigationTarget
+ // event if it contains the expected frameOuterWindowID params.
+ // (e.g. we should not notify it as a onCreatedNavigationTarget if the user is
+ // opening a new window using the keyboard shortcut).
+ const sourceTabBrowser = sourceWindow.gBrowser.selectedBrowser;
+ let delayedStartupObserver = aSubject => {
+ if (aSubject == win) {
+ Services.obs.removeObserver(delayedStartupObserver, "browser-delayed-startup-finished");
+ Services.obs.notifyObservers({
+ wrappedJSObject: {
+ url,
+ createdTabBrowser: win.gBrowser.selectedBrowser,
+ sourceTabBrowser,
+ sourceFrameOuterWindowID: params.frameOuterWindowID,
+ },
+ }, "webNavigation-createdNavigationTarget", null);
+ }
+ };
+ Services.obs.addObserver(delayedStartupObserver, "browser-delayed-startup-finished", false);
+ }
+ win = Services.ww.openWindow(sourceWindow, getBrowserURL(), null, features, sa);
return;
}
// We're now committed to loading the link in an existing browser window.
// Raise the target window before loading the URI, since loading it may
// result in a new frontmost window (e.g. "javascript:window.open('');").
w.focus();
@@ -401,16 +424,31 @@ function openLinkIn(url, where, params)
skipAnimation: aSkipTabAnimation,
allowMixedContent: aAllowMixedContent,
noReferrer: aNoReferrer,
userContextId: aUserContextId,
originPrincipal: aPrincipal,
triggeringPrincipal: aPrincipal,
});
targetBrowser = tabUsedForLoad.linkedBrowser;
+
+ if (params.frameOuterWindowID && w) {
+ // Only notify it as a WebExtensions' webNavigation.onCreatedNavigationTarget
+ // event if it contains the expected frameOuterWindowID params.
+ // (e.g. we should not notify it as a onCreatedNavigationTarget if the user is
+ // opening a new tab using the keyboard shortcut).
+ Services.obs.notifyObservers({
+ wrappedJSObject: {
+ url,
+ createdTabBrowser: targetBrowser,
+ sourceTabBrowser: w.gBrowser.selectedBrowser,
+ sourceFrameOuterWindowID: params.frameOuterWindowID,
+ },
+ }, "webNavigation-createdNavigationTarget", null);
+ }
break;
}
// Focus the content, but only if the browser used for the load is selected.
if (targetBrowser == w.gBrowser.selectedBrowser) {
targetBrowser.focus();
}
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -14,16 +14,19 @@ support-files =
file_iframe_document.sjs
file_bypass_cache.sjs
file_language_fr_en.html
file_language_ja.html
file_language_tlh.html
file_dummy.html
file_inspectedwindow_reload_target.sjs
file_serviceWorker.html
+ webNav_createdTarget.html
+ webNav_createdTargetSource.html
+ webNav_createdTargetSource_subframe.html
serviceWorker.js
searchSuggestionEngine.xml
searchSuggestionEngine.sjs
../../../../../toolkit/components/extensions/test/mochitest/head_webrequest.js
[browser_ext_browserAction_area.js]
[browser_ext_browserAction_context.js]
[browser_ext_browserAction_disabled.js]
@@ -118,16 +121,17 @@ support-files =
[browser_ext_themes_lwtsupport.js]
[browser_ext_topwindowid.js]
[browser_ext_url_overrides_all.js]
[browser_ext_url_overrides_home.js]
[browser_ext_url_overrides_newtab.js]
[browser_ext_webRequest.js]
[browser_ext_webNavigation_frameId0.js]
[browser_ext_webNavigation_getFrames.js]
+[browser_ext_webNavigation_onCreatedNavigationTarget.js]
[browser_ext_webNavigation_urlbar_transitions.js]
[browser_ext_windows.js]
[browser_ext_windows_create.js]
tags = fullscreen
[browser_ext_windows_create_params.js]
[browser_ext_windows_create_tabId.js]
[browser_ext_windows_create_url.js]
[browser_ext_windows_events.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
@@ -0,0 +1,285 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
+const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
+const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+
+async function background() {
+ const tabs = await browser.tabs.query({active: true, currentWindow: true});
+ const sourceTabId = tabs[0].id;
+
+ const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
+
+ browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
+ browser.test.sendMessage("webNavOnCreated", msg);
+ });
+
+ browser.webNavigation.onCompleted.addListener(async (msg) => {
+ // NOTE: checking the url is currently necessary because of Bug 1252129
+ // ( Filter out webNavigation events related to new window initialization phase).
+ if (msg.tabId !== sourceTabId && msg.url !== "about:blank") {
+ await browser.tabs.remove(msg.tabId);
+ browser.test.sendMessage("webNavOnCompleted", msg);
+ }
+ });
+
+ browser.tabs.onCreated.addListener((tab) => {
+ browser.test.sendMessage("tabsOnCreated", tab.id);
+ });
+
+ browser.test.sendMessage("expectedSourceTab", {
+ sourceTabId, sourceTabFrames,
+ });
+}
+
+async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
+ await openNavTarget();
+
+ const webNavMsg = await extension.awaitMessage("webNavOnCreated");
+ const createdTabId = await extension.awaitMessage("tabsOnCreated");
+ const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
+
+ let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
+
+ is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
+ is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
+ is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
+ is(webNavMsg.url, url, "Got the expected url property");
+
+ is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
+ is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
+}
+
+async function clickContextMenuItem({pageElementSelector, contextMenuItemLabel}) {
+ const contentAreaContextMenu = await openContextMenu(pageElementSelector);
+ const item = contentAreaContextMenu.getElementsByAttribute("label", contextMenuItemLabel);
+ is(item.length, 1, `found contextMenu item for "${contextMenuItemLabel}"`);
+ item[0].click();
+ await closeContextMenu();
+}
+
+add_task(function* test_on_created_navigation_target_from_mouse_click() {
+ const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+
+ const extension = ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ permissions: ["webNavigation"],
+ },
+ });
+
+ yield extension.startup();
+
+ const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
+
+ info("Open link in a new tab using Ctrl-click");
+
+ yield runTestCase({
+ extension,
+ openNavTarget() {
+ BrowserTestUtils.synthesizeMouseAtCenter("#test-create-new-tab-from-mouse-click",
+ {ctrlKey: true, metaKey: true},
+ tab.linkedBrowser);
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: 0,
+ url: `${OPENED_PAGE}#new-tab-from-mouse-click`,
+ },
+ });
+
+ info("Open link in a new window using Shift-click");
+
+ yield runTestCase({
+ extension,
+ openNavTarget() {
+ BrowserTestUtils.synthesizeMouseAtCenter("#test-create-new-window-from-mouse-click",
+ {shiftKey: true},
+ tab.linkedBrowser);
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: 0,
+ url: `${OPENED_PAGE}#new-window-from-mouse-click`,
+ },
+ });
+
+ yield BrowserTestUtils.removeTab(tab);
+
+ yield extension.unload();
+});
+
+add_task(function* test_on_created_navigation_target_from_context_menu() {
+ const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+
+ const extension = ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ permissions: ["webNavigation"],
+ },
+ });
+
+ yield extension.startup();
+
+ const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
+
+ info("Open link in a new tab from the context menu");
+
+ yield runTestCase({
+ extension,
+ async openNavTarget() {
+ await clickContextMenuItem({
+ pageElementSelector: "#test-create-new-tab-from-context-menu",
+ contextMenuItemLabel: "Open Link in New Tab",
+ });
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: 0,
+ url: `${OPENED_PAGE}#new-tab-from-context-menu`,
+ },
+ });
+
+ info("Open link in a new window from the context menu");
+
+ yield runTestCase({
+ extension,
+ async openNavTarget() {
+ await clickContextMenuItem({
+ pageElementSelector: "#test-create-new-window-from-context-menu",
+ contextMenuItemLabel: "Open Link in New Window",
+ });
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: 0,
+ url: `${OPENED_PAGE}#new-window-from-context-menu`,
+ },
+ });
+
+ yield BrowserTestUtils.removeTab(tab);
+
+ yield extension.unload();
+});
+
+add_task(function* test_on_created_navigation_target_from_mouse_click_subframe() {
+ const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+
+ const extension = ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ permissions: ["webNavigation"],
+ },
+ });
+
+ yield extension.startup();
+
+ const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
+
+ info("Open a subframe link in a new tab using Ctrl-click");
+
+ yield runTestCase({
+ extension,
+ openNavTarget() {
+ BrowserTestUtils.synthesizeMouseAtCenter(function() {
+ // This code runs as a framescript in the child process and it returns the
+ // target link in the subframe.
+ return this.content.frames[0].document // eslint-disable-line mozilla/no-cpows-in-tests
+ .querySelector("#test-create-new-tab-from-mouse-click-subframe");
+ }, {ctrlKey: true, metaKey: true}, tab.linkedBrowser);
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+ url: `${OPENED_PAGE}#new-tab-from-mouse-click-subframe`,
+ },
+ });
+
+ info("Open a subframe link in a new window using Shift-click");
+
+ yield runTestCase({
+ extension,
+ openNavTarget() {
+ BrowserTestUtils.synthesizeMouseAtCenter(function() {
+ // This code runs as a framescript in the child process and it returns the
+ // target link in the subframe.
+ return this.content.frames[0].document // eslint-disable-line mozilla/no-cpows-in-tests
+ .querySelector("#test-create-new-window-from-mouse-click-subframe");
+ }, {shiftKey: true}, tab.linkedBrowser);
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+ url: `${OPENED_PAGE}#new-window-from-mouse-click-subframe`,
+ },
+ });
+
+ yield BrowserTestUtils.removeTab(tab);
+
+ yield extension.unload();
+});
+
+add_task(function* test_on_created_navigation_target_from_context_menu_subframe() {
+ const tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+
+ const extension = ExtensionTestUtils.loadExtension({
+ background,
+ manifest: {
+ permissions: ["webNavigation"],
+ },
+ });
+
+ yield extension.startup();
+
+ const expectedSourceTab = yield extension.awaitMessage("expectedSourceTab");
+
+ info("Open a subframe link in a new tab from the context menu");
+
+ yield runTestCase({
+ extension,
+ async openNavTarget() {
+ await clickContextMenuItem({
+ pageElementSelector() {
+ // This code runs as a framescript in the child process and it returns the
+ // target link in the subframe.
+ return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
+ .document.querySelector("#test-create-new-tab-from-context-menu-subframe");
+ },
+ contextMenuItemLabel: "Open Link in New Tab",
+ });
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+ url: `${OPENED_PAGE}#new-tab-from-context-menu-subframe`,
+ },
+ });
+
+ info("Open a subframe link in a new window from the context menu");
+
+ yield runTestCase({
+ extension,
+ async openNavTarget() {
+ await clickContextMenuItem({
+ pageElementSelector() {
+ // This code runs as a framescript in the child process and it returns the
+ // target link in the subframe.
+ return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
+ .document.querySelector("#test-create-new-window-from-context-menu-subframe");
+ },
+ contextMenuItemLabel: "Open Link in New Window",
+ });
+ },
+ expectedWebNavProps: {
+ sourceTabId: expectedSourceTab.sourceTabId,
+ sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+ url: `${OPENED_PAGE}#new-window-from-context-menu-subframe`,
+ },
+ });
+
+ yield BrowserTestUtils.removeTab(tab);
+
+ yield extension.unload();
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/webNav_createdTarget.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>WebNavigatio onCreatedNavigationTarget target</title>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <a id="other-link" href="webNav_createdTarget_source.html">Go back to the source page</a>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/webNav_createdTargetSource.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>WebNavigatio onCreatedNavigationTarget source</title>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <ul>
+ <li>
+ <a id="test-create-new-tab-from-context-menu"
+ href="webNav_createdTarget.html#new-tab-from-context-menu">
+ Open a target page in a new tab from the context menu
+ </a>
+ </li>
+ <li>
+ <a id="test-create-new-window-from-context-menu"
+ href="webNav_createdTarget.html#new-window-from-context-menu">
+ Open a target page in a new window from the context menu
+ </a>
+ </li>
+ <li>
+ <a id="test-create-new-tab-from-mouse-click"
+ href="webNav_createdTarget.html#new-tab-from-mouse-click">
+ Open a target page in a new tab from mouse click
+ </a>
+ </li>
+ <li>
+ <a id="test-create-new-window-from-mouse-click"
+ href="webNav_createdTarget.html#new-window-from-mouse-click">
+ Open a target page in a new window from mouse click
+ </a>
+ </li>
+ </ul>
+
+ <iframe src="webNav_createdTargetSource_subframe.html" style="width: 100%; height: 100%;">
+ </iframe>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/webNav_createdTargetSource_subframe.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>WebNavigatio onCreatedNavigationTarget source subframe</title>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <ul>
+ <li>
+ <a id="test-create-new-tab-from-context-menu-subframe"
+ href="webNav_createdTarget.html#new-tab-from-context-menu-subframe">
+ Open a target page in a new tab from the context menu (subframe)
+ </a>
+ </li>
+ <li>
+ <a id="test-create-new-window-from-context-menu-subframe"
+ href="webNav_createdTarget.html#new-window-from-context-menu-subframe">
+ Open a target page in a new window from the context menu (subframe)
+ </a>
+ </li>
+ <li>
+ <a id="test-create-new-tab-from-mouse-click-subframe"
+ href="webNav_createdTarget.html#new-tab-from-mouse-click-subframe">
+ Open a target page in a new tab from mouse click (subframe)
+ </a>
+ </li>
+ <li>
+ <a id="test-create-new-window-from-mouse-click-subframe"
+ href="webNav_createdTarget.html#new-window-from-mouse-click-subframe">
+ Open a target page in a new window from mouse click (subframe)
+ </a>
+ </li>
+ </ul>
+ </body>
+</html>
--- a/browser/modules/ContentClick.jsm
+++ b/browser/modules/ContentClick.jsm
@@ -80,16 +80,17 @@ var ContentClick = {
let params = {
charset: browser.characterSet,
referrerURI: browser.documentURI,
referrerPolicy: json.referrerPolicy,
noReferrer: json.noReferrer,
allowMixedContent: json.allowMixedContent,
isContentWindowPrivate: json.isContentWindowPrivate,
originPrincipal: json.originPrincipal,
+ frameOuterWindowID: json.frameOuterWindowID,
};
// The new tab/window must use the same userContextId.
if (json.originAttributes.userContextId) {
params.userContextId = json.originAttributes.userContextId;
}
window.openLinkIn(json.href, where, params);
--- a/toolkit/components/extensions/ext-webNavigation.js
+++ b/toolkit/components/extensions/ext-webNavigation.js
@@ -108,30 +108,41 @@ function WebNavigationEventManager(conte
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;
}
+ if (data.windowId) {
+ data2.frameId = ExtensionManagement.getFrameId(data.windowId);
+ data2.parentFrameId = ExtensionManagement.getParentFrameId(data.parentWindowId, data.windowId);
+ }
+
+ if (data.sourceWindowId) {
+ data2.sourceFrameId = ExtensionManagement.getFrameId(data.sourceWindowId);
+ }
+
// Fills in tabId typically.
Object.assign(data2, tabTracker.getBrowserData(data.browser));
if (data2.tabId < 0) {
return;
}
+ if (data.sourceTabBrowser) {
+ data2.sourceTabId = tabTracker.getBrowserData(data.sourceTabBrowser).tabId;
+ }
+
fillTransitionProperties(eventName, data, data2);
fire.async(data2);
};
WebNavigation[eventName].addListener(listener, filters);
return () => {
WebNavigation[eventName].removeListener(listener);
@@ -161,17 +172,17 @@ extensions.registerSchemaAPI("webNavigat
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"),
+ onCreatedNavigationTarget: new WebNavigationEventManager(context, "onCreatedNavigationTarget").api(),
getAllFrames(details) {
let tab = tabManager.get(details.tabId);
let {innerWindowID, messageManager} = tab.browser;
let recipient = {innerWindowID};
return context.sendMessage(messageManager, "WebNavigation:GetAllFrames", {}, {recipient})
.then((results) => results.map(convertGetFrameResult.bind(null, details.tabId)));
--- a/toolkit/components/extensions/schemas/web_navigation.json
+++ b/toolkit/components/extensions/schemas/web_navigation.json
@@ -279,17 +279,16 @@
"optional": true,
"$ref": "EventUrlFilters",
"description": "Conditions that the URL being navigated to must satisfy. The 'schemes' and 'ports' fields of UrlFilter are ignored for this event."
}
]
},
{
"name": "onCreatedNavigationTarget",
- "unsupported": true,
"type": "function",
"description": "Fired when a new window, or a new tab in an existing window, is created to host a navigation.",
"parameters": [
{
"type": "object",
"name": "details",
"properties": {
"sourceTabId": {"type": "integer", "description": "The ID of the tab in which the navigation is triggered."},
--- a/toolkit/modules/addons/WebNavigation.jsm
+++ b/toolkit/modules/addons/WebNavigation.jsm
@@ -16,43 +16,44 @@ Cu.import("resource://gre/modules/Servic
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
"resource:///modules/RecentWindow.jsm");
// Maximum amount of time that can be passed and still consider
// the data recent (similar to how is done in nsNavHistory,
// e.g. nsNavHistory::CheckIsRecentEvent, but with a lower threshold value).
const RECENT_DATA_THRESHOLD = 5 * 1000000;
-// TODO:
-// onCreatedNavigationTarget
-
var Manager = {
// Map[string -> Map[listener -> URLFilter]]
listeners: new Map(),
init() {
// Collect recent tab transition data in a WeakMap:
// browser -> tabTransitionData
this.recentTabTransitionData = new WeakMap();
Services.obs.addObserver(this, "autocomplete-did-enter-text", true);
+ Services.obs.addObserver(this, "webNavigation-createdNavigationTarget", false);
+
Services.mm.addMessageListener("Content:Click", this);
Services.mm.addMessageListener("Extension:DOMContentLoaded", this);
Services.mm.addMessageListener("Extension:StateChange", this);
Services.mm.addMessageListener("Extension:DocumentChange", this);
Services.mm.addMessageListener("Extension:HistoryChange", this);
Services.mm.loadFrameScript("resource://gre/modules/WebNavigationContent.js", true);
},
uninit() {
// Stop collecting recent tab transition data and reset the WeakMap.
Services.obs.removeObserver(this, "autocomplete-did-enter-text");
this.recentTabTransitionData = new WeakMap();
+ Services.obs.removeObserver(this, "webNavigation-createdNavigationTarget");
+
Services.mm.removeMessageListener("Content:Click", this);
Services.mm.removeMessageListener("Extension:StateChange", this);
Services.mm.removeMessageListener("Extension:DocumentChange", this);
Services.mm.removeMessageListener("Extension:HistoryChange", this);
Services.mm.removeMessageListener("Extension:DOMContentLoaded", this);
Services.mm.removeDelayedFrameScript("resource://gre/modules/WebNavigationContent.js");
Services.mm.broadcastAsyncMessage("Extension:DisableWebNavigation");
@@ -87,26 +88,43 @@ var Manager = {
/**
* Support nsIObserver interface to observe the urlbar autocomplete events used
* to keep track of the urlbar user interaction.
*/
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
/**
- * Observe autocomplete-did-enter-text topic to track the user interaction with
- * the awesome bar.
+ * Observe autocomplete-did-enter-text (to track the user interaction with the awesomebar)
+ * and webNavigation-createdNavigationTarget (to fire the onCreatedNavigationTarget
+ * related to windows or tabs opened from the main process) topics.
*
- * @param {nsIAutoCompleteInput} subject
+ * @param {nsIAutoCompleteInput|Object} subject
* @param {string} topic
- * @param {string} data
+ * @param {string|undefined} data
*/
observe: function(subject, topic, data) {
if (topic == "autocomplete-did-enter-text") {
this.onURLBarAutoCompletion(subject);
+ } else if (topic == "webNavigation-createdNavigationTarget") {
+ // The observed notification is coming from privileged JavaScript components running
+ // in the main process (e.g. when a new tab or window is opened using the context menu
+ // or Ctrl/Shift + click on a link).
+ const {
+ createdTabBrowser,
+ url,
+ sourceFrameOuterWindowID,
+ sourceTabBrowser,
+ } = subject.wrappedJSObject;
+
+ this.fire("onCreatedNavigationTarget", createdTabBrowser, {}, {
+ sourceTabBrowser,
+ sourceWindowId: sourceFrameOuterWindowID,
+ url,
+ });
}
},
/**
* Recognize the type of urlbar user interaction (e.g. typing a new url,
* clicking on an url generated from a searchengine or a keyword, or a
* bookmark found by the urlbar autocompletion).
*
@@ -352,17 +370,17 @@ var Manager = {
const EVENTS = [
"onBeforeNavigate",
"onCommitted",
"onDOMContentLoaded",
"onCompleted",
"onErrorOccurred",
"onReferenceFragmentUpdated",
"onHistoryStateUpdated",
- // "onCreatedNavigationTarget",
+ "onCreatedNavigationTarget",
];
var WebNavigation = {};
for (let event of EVENTS) {
WebNavigation[event] = {
addListener: Manager.addListener.bind(Manager, event),
removeListener: Manager.removeListener.bind(Manager, event),