diff --git a/packages/addon-kit/docs/tabs.md b/packages/addon-kit/docs/tabs.md index ae0a29c83..66f268238 100644 --- a/packages/addon-kit/docs/tabs.md +++ b/packages/addon-kit/docs/tabs.md @@ -36,7 +36,8 @@ be correct at this point. Use `ready` event listener to be notified when the page has loaded. ### close ### -Event emitted when a tab is closed. +Event emitted when a tab is closed. In addition, when a window is closed, +this event will be emitted for each of the open tabs in that window. ### ready ### Event emitted when a tab's content's DOM is ready. diff --git a/packages/addon-kit/lib/windows.js b/packages/addon-kit/lib/windows.js index cb5bea759..0f82536ba 100644 --- a/packages/addon-kit/lib/windows.js +++ b/packages/addon-kit/lib/windows.js @@ -113,7 +113,6 @@ const BrowserWindowTrait = Trait.compose( this._emitOnObject(browserWindows, 'open', this._public); }, _onUnload: function() { - // Need to remove all the tabs before window listener are notified. this._destroyWindowTabTracker(); this._emitOnObject(browserWindows, 'close', this._public); this._window = null; diff --git a/packages/addon-kit/tests/test-tabs.js b/packages/addon-kit/tests/test-tabs.js index 8e8c4c6a2..4aafd5f19 100644 --- a/packages/addon-kit/tests/test-tabs.js +++ b/packages/addon-kit/tests/test-tabs.js @@ -380,6 +380,61 @@ exports.testTabsEvent_onClose = function(test) { }); }; +// onClose event handler when a window is closed +exports.testTabsEvent_onCloseWindow = function(test) { + test.waitUntilDone(); + + openBrowserWindow(function(window, browser) { + var tabs = require("tabs"); + + let closeCount = 0, individualCloseCount = 0; + function listener() { + closeCount++; + } + tabs.on('close', listener); + + // One tab is already open with the window + let openTabs = 1; + function testCasePossiblyLoaded() { + if (++openTabs == 4) { + beginCloseWindow(); + } + } + + tabs.open({ + url: "data:text/html,tab2", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + tabs.open({ + url: "data:text/html,tab3", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + tabs.open({ + url: "data:text/html,tab4", + onOpen: function() testCasePossiblyLoaded(), + onClose: function() individualCloseCount++ + }); + + function beginCloseWindow() { + closeBrowserWindow(window, function testFinished() { + tabs.removeListener("close", listener); + + test.assertEqual(closeCount, 4, "Correct number of close events received"); + test.assertEqual(individualCloseCount, 3, + "Each tab with an attached onClose listener received a close " + + "event when the window was closed"); + + test.done(); + }); + } + + }); +} + // onReady event handler exports.testTabsEvent_onReady = function(test) { test.waitUntilDone(); diff --git a/packages/api-utils/lib/list.js b/packages/api-utils/lib/list.js index 3beff2ede..e5ccecdf0 100644 --- a/packages/api-utils/lib/list.js +++ b/packages/api-utils/lib/list.js @@ -138,7 +138,7 @@ const List = Trait.resolve({ toString: null }).compose({ * @param {Boolean} onKeys */ __iterator__: function __iterator__(onKeys, onKeyValue) { - let array = this._keyValueMap, + let array = this._keyValueMap.slice(0), i = -1; for each(let element in array) yield onKeyValue ? [++i, element] : onKeys ? ++i : element; diff --git a/packages/api-utils/lib/windows/tabs.js b/packages/api-utils/lib/windows/tabs.js index 80bb2f80c..f0bc1e8af 100644 --- a/packages/api-utils/lib/windows/tabs.js +++ b/packages/api-utils/lib/windows/tabs.js @@ -93,8 +93,8 @@ const WindowTabTracker = Trait.compose({ } }, _destroyWindowTabTracker: function _destroyWindowTabTracker() { - for each (tab in this.tabs) - tab.close(); + for each (let tab in this.tabs) + this._emitEvent(EVENTS.close, tab); this._tabs._clear(); }, /** diff --git a/packages/api-utils/tests/test-list.js b/packages/api-utils/tests/test-list.js index 7a1c98621..3190fe7d7 100644 --- a/packages/api-utils/tests/test-list.js +++ b/packages/api-utils/tests/test-list.js @@ -144,3 +144,51 @@ exports['test:removing adding elements'] = function(test) { assertList(test, array, fixture); }; +exports['test:add list item from Iterator'] = function(test) { + let array = [1, 2, 3, 4], sum = 0, added = false; + + let fixture = List.compose({ + add: function() this._add.apply(this, arguments), + }).apply(null, array); + + for each (let item in fixture) { + sum += item; + + if (!added) { + fixture.add(5); + added = true; + } + } + + test.assertEqual(sum, 1 + 2 + 3 + 4); +}; + +exports['test:remove list item from Iterator'] = function(test) { + let array = [1, 2, 3, 4], sum = 0; + + let fixture = List.compose({ + remove: function() this._remove.apply(this, arguments), + }).apply(null, array); + + for each (let item in fixture) { + sum += item; + fixture.remove(item); + } + + test.assertEqual(sum, 1 + 2 + 3 + 4); +}; + +exports['test:clear list from Iterator'] = function(test) { + let array = [1, 2, 3, 4], sum = 0; + + let fixture = List.compose({ + clear: function() this._clear() + }).apply(null, array); + + for each (let item in fixture) { + sum += item; + fixture.clear(); + } + + test.assertEqual(sum, 1 + 2 + 3 + 4); +};