Bug 1320306 - Implement sessions.onChanged WebExtensions API, r?aswan
MozReview-Commit-ID: 14si74CKswB
--- a/browser/components/extensions/ext-sessions.js
+++ b/browser/components/extensions/ext-sessions.js
@@ -1,20 +1,23 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
promiseObserved,
+ SingletonEventManager,
} = ExtensionUtils;
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
"resource:///modules/sessionstore/SessionStore.jsm");
+const ssOnChangedTopic = "sessionstore-closed-objects-changed";
+
function getRecentlyClosed(maxResults, extension) {
let recentlyClosed = [];
// Get closed windows
let closedWindowData = SessionStore.getClosedWindowData(false);
for (let window of closedWindowData) {
recentlyClosed.push({
lastModified: window.closedAt,
@@ -82,11 +85,38 @@ extensions.registerSchemaAPI("sessions",
recentlyClosedTabs.sort((a, b) => b.closedAt - a.closedAt);
// Use the closedId of the most recently closed tab to restore it.
closedId = recentlyClosedTabs[0].closedId;
session = SessionStore.undoCloseById(closedId);
}
return createSession(session, extension, closedId);
},
+ onChanged: new SingletonEventManager(context, "sessions.onChanged", fire => {
+ let listenerCount = 0;
+
+ let observer = {
+ observe: function() {
+ this.emit("changed");
+ },
+ };
+ EventEmitter.decorate(observer);
+
+ let listener = (event) => {
+ context.runSafe(fire);
+ };
+
+ observer.on("changed", listener);
+ listenerCount++;
+ if (listenerCount == 1) {
+ Services.obs.addObserver(observer, ssOnChangedTopic, false);
+ }
+ return () => {
+ observer.off("changed", listener);
+ listenerCount -= 1;
+ if (!listenerCount) {
+ Services.obs.removeObserver(observer, ssOnChangedTopic);
+ }
+ };
+ }).api(),
},
};
});
--- a/browser/components/extensions/schemas/sessions.json
+++ b/browser/components/extensions/schemas/sessions.json
@@ -126,17 +126,16 @@
]
}
]
}
],
"events": [
{
"name": "onChanged",
- "unsupported": true,
"description": "Fired when recently closed tabs and/or windows are changed. This event does not monitor synced sessions changes.",
"type": "function"
}
],
"properties": {
"MAX_SESSION_RESULTS": {
"value": 25,
"description": "The maximum number of $(ref:sessions.Session) that will be included in a requested list."
--- a/browser/components/extensions/test/browser/browser_ext_sessions_restore.js
+++ b/browser/components/extensions/test/browser/browser_ext_sessions_restore.js
@@ -1,19 +1,26 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
SimpleTest.requestCompleteLog();
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
"resource:///modules/sessionstore/SessionStore.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "TabStateFlusher",
+ "resource:///modules/sessionstore/TabStateFlusher.jsm");
add_task(function* test_sessions_restore() {
function background() {
+ let notificationCount = 0;
+ browser.sessions.onChanged.addListener(() => {
+ notificationCount++;
+ browser.test.sendMessage("notificationCount", notificationCount);
+ });
browser.test.onMessage.addListener((msg, data) => {
if (msg == "check-sessions") {
browser.sessions.getRecentlyClosed().then(recentlyClosed => {
browser.test.sendMessage("recentlyClosed", recentlyClosed);
});
} else if (msg == "restore") {
browser.sessions.restore(data).then(sessions => {
browser.test.sendMessage("restored", sessions);
@@ -26,109 +33,129 @@ add_task(function* test_sessions_restore
error => {
browser.test.assertTrue(
error.message.includes("Could not restore object using sessionId not-a-valid-session-id."));
browser.test.sendMessage("restore-rejected");
}
);
}
});
+ browser.test.sendMessage("ready");
}
let extension = ExtensionTestUtils.loadExtension({
manifest: {
permissions: ["sessions", "tabs"],
},
background,
});
+ function* assertNotificationCount(expected) {
+ let notificationCount = yield extension.awaitMessage("notificationCount");
+ is(notificationCount, expected, "the expected number of notifications was fired");
+ }
+
yield extension.startup();
let {Management: {global: {WindowManager, TabManager}}} = Cu.import("resource://gre/modules/Extension.jsm", {});
function checkLocalTab(tab, expectedUrl) {
let realTab = TabManager.getTab(tab.id);
let tabState = JSON.parse(SessionStore.getTabState(realTab));
is(tabState.entries[0].url, expectedUrl, "restored tab has the expected url");
}
+ yield extension.awaitMessage("ready");
+
let win = yield BrowserTestUtils.openNewBrowserWindow();
yield BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, "about:config");
yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
for (let url of ["about:robots", "about:mozilla"]) {
yield BrowserTestUtils.openNewForegroundTab(win.gBrowser, url);
}
yield BrowserTestUtils.closeWindow(win);
+ yield assertNotificationCount(1);
extension.sendMessage("check-sessions");
let recentlyClosed = yield extension.awaitMessage("recentlyClosed");
// Check that our expected window is the most recently closed.
is(recentlyClosed[0].window.tabs.length, 3, "most recently closed window has the expected number of tabs");
// Restore the window.
extension.sendMessage("restore");
+ yield assertNotificationCount(2);
let restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
is(restored[0].window.tabs.length, 3, "restore returned a window with the expected number of tabs");
checkLocalTab(restored[0].window.tabs[0], "about:config");
checkLocalTab(restored[0].window.tabs[1], "about:robots");
checkLocalTab(restored[0].window.tabs[2], "about:mozilla");
// Close the window again.
let window = WindowManager.getWindow(restored[0].window.id);
yield BrowserTestUtils.closeWindow(window);
+ yield assertNotificationCount(3);
// Restore the window using the sessionId.
extension.sendMessage("check-sessions");
recentlyClosed = yield extension.awaitMessage("recentlyClosed");
extension.sendMessage("restore", recentlyClosed[0].window.sessionId);
+ yield assertNotificationCount(4);
restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
is(restored[0].window.tabs.length, 3, "restore returned a window with the expected number of tabs");
checkLocalTab(restored[0].window.tabs[0], "about:config");
checkLocalTab(restored[0].window.tabs[1], "about:robots");
checkLocalTab(restored[0].window.tabs[2], "about:mozilla");
// Close the window again.
window = WindowManager.getWindow(restored[0].window.id);
yield BrowserTestUtils.closeWindow(window);
+ // notificationCount = yield extension.awaitMessage("notificationCount");
+ yield assertNotificationCount(5);
// Open and close a tab.
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
+ yield TabStateFlusher.flush(tab.linkedBrowser);
yield BrowserTestUtils.removeTab(tab);
+ yield assertNotificationCount(6);
// Restore the most recently closed item.
extension.sendMessage("restore");
+ yield assertNotificationCount(7);
restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
tab = restored[0].tab;
ok(tab, "restore returned a tab");
checkLocalTab(tab, "about:robots");
// Close the tab again.
let realTab = TabManager.getTab(tab.id);
yield BrowserTestUtils.removeTab(realTab);
+ yield assertNotificationCount(8);
// Restore the tab using the sessionId.
extension.sendMessage("check-sessions");
recentlyClosed = yield extension.awaitMessage("recentlyClosed");
extension.sendMessage("restore", recentlyClosed[0].tab.sessionId);
+ yield assertNotificationCount(9);
restored = yield extension.awaitMessage("restored");
is(restored.length, 1, "restore returned the expected number of sessions");
tab = restored[0].tab;
ok(tab, "restore returned a tab");
checkLocalTab(tab, "about:robots");
// Close the tab again.
realTab = TabManager.getTab(tab.id);
yield BrowserTestUtils.removeTab(realTab);
+ yield assertNotificationCount(10);
// Try to restore something with an invalid sessionId.
extension.sendMessage("restore-reject");
restored = yield extension.awaitMessage("restore-rejected");
yield extension.unload();
});