Bug 1396017: Redact window titles without the appropriate tabs permissions. r?mixedpuppy
MozReview-Commit-ID: 2QJYvJlqt9l
--- a/browser/components/extensions/ext-browser.js
+++ b/browser/components/extensions/ext-browser.js
@@ -714,17 +714,17 @@ class Window extends WindowBase {
if (options.width !== null || options.height !== null) {
let width = options.width !== null ? options.width : window.outerWidth;
let height = options.height !== null ? options.height : window.outerHeight;
window.resizeTo(width, height);
}
}
- get title() {
+ get _title() {
return this.window.document.title;
}
setTitlePreface(titlePreface) {
this.window.document.documentElement.setAttribute("titlepreface", titlePreface);
}
get focused() {
@@ -819,16 +819,22 @@ class Window extends WindowBase {
* getTabs() {
let {tabManager} = this.extension;
for (let nativeTab of this.window.gBrowser.tabs) {
yield tabManager.getWrapper(nativeTab);
}
}
+ get activeTab() {
+ let {tabManager} = this.extension;
+
+ return tabManager.getWrapper(this.window.gBrowser.selectedTab);
+ }
+
/**
* Converts session store data to an object compatible with the return value
* of the convert() method, representing that data.
*
* @param {Extension} extension
* The extension for which to convert the data.
* @param {Object} windowData
* Session store data for a closed window, as returned by
--- a/browser/components/extensions/test/browser/browser_ext_windows.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows.js
@@ -59,16 +59,19 @@ add_task(async function testWindowTitle(
"Window has the expected title text after update.");
browser.test.sendMessage("updated", win);
}
});
}
let extension = ExtensionTestUtils.loadExtension({
background,
+ manifest: {
+ permissions: ["tabs"],
+ },
});
await extension.startup();
let {Management: {global: {windowTracker}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
async function createApiWin(options) {
let promiseLoaded = BrowserTestUtils.waitForNewWindow(true, START_URL);
extension.sendMessage("create", options);
@@ -134,16 +137,67 @@ add_task(async function testWindowTitle(
text: NEW_TITLE,
},
};
await updateWindow({titlePreface: PREFACE2}, apiWin, expected);
await extension.unload();
});
+// Test that the window title is only available with the correct tab
+// permissions.
+add_task(async function testWindowTitlePermissions() {
+ let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser, "http://example.com/")
+
+ let extension = ExtensionTestUtils.loadExtension({
+ async background() {
+ function awaitMessage(name) {
+ return new Promise(resolve => {
+ browser.test.onMessage.addListener(function listener(...msg) {
+ if (msg[0] === name) {
+ browser.test.onMessage.removeListener(listener);
+ resolve(msg[1]);
+ }
+ });
+ });
+ }
+
+ let window = await browser.windows.getCurrent();
+
+ browser.test.assertEq(undefined, window.title,
+ "Window title should be null without tab permission");
+
+ browser.test.sendMessage("grant-activeTab");
+ let expectedTitle = await awaitMessage("title");
+
+ window = await browser.windows.getCurrent();
+ browser.test.assertEq(expectedTitle, window.title,
+ "Window should have the expected title with tab permission granted");
+
+ await browser.test.notifyPass("window-title-permissions");
+ },
+ manifest: {
+ permissions: ["activeTab"],
+ browser_action: {},
+ },
+ });
+
+ await extension.startup();
+
+ await extension.awaitMessage("grant-activeTab");
+ await clickBrowserAction(extension);
+ extension.sendMessage("title", document.title);
+
+ await extension.awaitFinish("window-title-permissions");
+
+ await extension.unload();
+
+ await BrowserTestUtils.removeTab(tab);
+});
+
add_task(async function testInvalidWindowId() {
let extension = ExtensionTestUtils.loadExtension({
async background() {
await browser.test.assertRejects(
// Assuming that this windowId does not exist.
browser.windows.get(123456789),
/Invalid window/,
"Should receive invalid window");
--- a/mobile/android/components/extensions/ext-utils.js
+++ b/mobile/android/components/extensions/ext-utils.js
@@ -524,16 +524,22 @@ class Window extends WindowBase {
* getTabs() {
let {tabManager} = this.extension;
for (let nativeTab of this.window.BrowserApp.tabs) {
yield tabManager.getWrapper(nativeTab);
}
}
+
+ get activeTab() {
+ let {tabManager} = this.extension;
+
+ return tabManager.getWrapper(this.window.BrowserApp.selectedTab);
+ }
}
Object.assign(global, {Tab, TabContext, Window});
class TabManager extends TabManagerBase {
get(tabId, default_ = undefined) {
let nativeTab = tabTracker.getTab(tabId, default_);
--- a/toolkit/components/extensions/ext-tabs-base.js
+++ b/toolkit/components/extensions/ext-tabs-base.js
@@ -910,16 +910,28 @@ class WindowBase {
get state() {
throw new Error("Not implemented");
}
set state(state) {
throw new Error("Not implemented");
}
+ /**
+ * @property {nsIURI | null} title
+ * Returns the current title of this window if the extension has permission
+ * to read it, or null otherwise.
+ * @readonly
+ */
+ get title() {
+ if (this.activeTab.hasTabPermission) {
+ return this._title;
+ }
+ }
+
// The JSDoc validator does not support @returns tags in abstract functions or
// star functions without return statements.
/* eslint-disable valid-jsdoc */
/**
* Returns the window state of the given window.
*
* @param {DOMWindow} window
* The window for which to return a state.
@@ -937,16 +949,23 @@ class WindowBase {
/**
* Returns an iterator of TabBase objects for each tab in this window.
*
* @returns {Iterator<TabBase>}
*/
getTabs() {
throw new Error("Not implemented");
}
+
+ /**
+ * @property {TabBase} The window's currently active tab.
+ */
+ get activeTab() {
+ throw new Error("Not implemented");
+ }
/* eslint-enable valid-jsdoc */
}
Object.assign(WindowBase, {WINDOW_ID_NONE, WINDOW_ID_CURRENT});
/**
* The parameter type of "tab-attached" events, which are emitted when a
* pre-existing tab is attached to a new window.