mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central to mozilla-inbound
This commit is contained in:
commit
335f4f23c8
@ -528,6 +528,9 @@ pref("layout.frame_rate.precise", true);
|
||||
// Handle hardware buttons in the b2g chrome package
|
||||
pref("b2g.keys.menu.enabled", true);
|
||||
|
||||
// Display simulator software buttons
|
||||
pref("b2g.software-buttons", false);
|
||||
|
||||
// Screen timeout in seconds
|
||||
pref("power.screen.timeout", 60);
|
||||
|
||||
|
@ -15,13 +15,24 @@ function enableTouch() {
|
||||
touchEventSimulator.start();
|
||||
}
|
||||
|
||||
// Some additional buttons are displayed on simulators to fake hardware buttons.
|
||||
function setupButtons() {
|
||||
let homeButton = document.getElementById('home-button');
|
||||
if (!homeButton) {
|
||||
// The toolbar only exists in b2g desktop build with
|
||||
// FXOS_SIMULATOR turned on.
|
||||
return;
|
||||
}
|
||||
let link = document.createElement('link');
|
||||
link.type = 'text/css';
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'chrome://b2g/content/desktop.css';
|
||||
document.head.appendChild(link);
|
||||
|
||||
let footer = document.createElement('footer');
|
||||
footer.id = 'controls';
|
||||
document.body.appendChild(footer);
|
||||
let homeButton = document.createElement('button');
|
||||
homeButton.id = 'home-button';
|
||||
footer.appendChild(homeButton);
|
||||
let rotateButton = document.createElement('button');
|
||||
rotateButton.id = 'rotate-button';
|
||||
footer.appendChild(rotateButton);
|
||||
|
||||
homeButton.addEventListener('mousedown', function() {
|
||||
let window = shell.contentBrowser.contentWindow;
|
||||
let e = new window.KeyboardEvent('keydown', {key: 'Home'});
|
||||
@ -36,7 +47,6 @@ function setupButtons() {
|
||||
});
|
||||
|
||||
Cu.import("resource://gre/modules/GlobalSimulatorScreen.jsm");
|
||||
let rotateButton = document.getElementById('rotate-button');
|
||||
rotateButton.addEventListener('mousedown', function() {
|
||||
rotateButton.classList.add('active');
|
||||
});
|
||||
@ -165,7 +175,9 @@ window.addEventListener('ContentStart', function() {
|
||||
if (!isMulet) {
|
||||
enableTouch();
|
||||
}
|
||||
setupButtons();
|
||||
if (Services.prefs.getBoolPref('b2g.software-buttons')) {
|
||||
setupButtons();
|
||||
}
|
||||
checkDebuggerPort();
|
||||
setupStorage();
|
||||
// On Firefox mulet, we automagically enable the responsive mode
|
||||
|
@ -18,9 +18,6 @@
|
||||
|
||||
<head>
|
||||
<link rel="stylesheet" href="shell.css" type="text/css">
|
||||
#ifdef FXOS_SIMULATOR
|
||||
<link rel="stylesheet" href="desktop.css" type="text/css">
|
||||
#endif
|
||||
<script type="text/javascript">
|
||||
<!-- Add raptor performance marker -->
|
||||
window.performance.mark('gecko-shell-html-load');
|
||||
@ -52,17 +49,6 @@
|
||||
src="chrome://b2g/content/devtools/debugger.js"> </script>
|
||||
</head>
|
||||
<body id="container">
|
||||
#ifdef FXOS_SIMULATOR
|
||||
#ifndef MOZ_MULET
|
||||
<!--
|
||||
Some additional buttons are displayed on desktop to fake hardware buttons.
|
||||
-->
|
||||
<footer id="controls">
|
||||
<button id="home-button"></button>
|
||||
<button id="rotate-button"></button>
|
||||
</footer>
|
||||
#endif
|
||||
#endif
|
||||
#ifndef MOZ_GRAPHENE
|
||||
#ifdef MOZ_WIDGET_COCOA
|
||||
<!--
|
||||
|
@ -22,13 +22,11 @@ chrome.jar:
|
||||
#endif
|
||||
* content/devtools/debugger.js (content/devtools/debugger.js)
|
||||
content/devtools/hud.js (content/devtools/hud.js)
|
||||
#ifdef FXOS_SIMULATOR
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
content/desktop.css (content/desktop.css)
|
||||
content/images/desktop/home-black.png (content/images/desktop/home-black.png)
|
||||
content/images/desktop/home-white.png (content/images/desktop/home-white.png)
|
||||
content/images/desktop/rotate.png (content/images/desktop/rotate.png)
|
||||
#endif
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
content/desktop.js (content/desktop.js)
|
||||
content/screen.js (content/screen.js)
|
||||
content/runapp.js (content/runapp.js)
|
||||
|
@ -1,6 +1,7 @@
|
||||
user_pref("devtools.debugger.prompt-connection", false);
|
||||
user_pref("devtools.debugger.forbid-certified-apps", false);
|
||||
user_pref("devtools.apps.forbidden-permissions", "");
|
||||
user_pref("b2g.software-buttons", true);
|
||||
|
||||
// Required for Mulet in order to run the debugger server from the command line
|
||||
user_pref("devtools.debugger.remote-enabled", true);
|
||||
|
@ -6,6 +6,8 @@ var loop = loop || {};
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var _slice = Array.prototype.slice;
|
||||
|
||||
var kMessageName = "Loop:Message";
|
||||
var kPushMessageName = "Loop:Message:Push";
|
||||
var kBatchMessage = "Batch";
|
||||
@ -51,7 +53,7 @@ var loop = loop || {};
|
||||
* script sent a reply. It never gets rejected.
|
||||
*/
|
||||
loop.request = function request() {
|
||||
var args = Array.slice(arguments);
|
||||
var args = _slice.call(arguments);
|
||||
|
||||
return new Promise(function(resolve) {
|
||||
var payload = buildRequestArray(args);
|
||||
@ -107,7 +109,7 @@ var loop = loop || {};
|
||||
throw new Error("loop.requestMulti: please pass in a list of calls to process in parallel.");
|
||||
}
|
||||
|
||||
var calls = Array.slice(arguments);
|
||||
var calls = _slice.call(arguments);
|
||||
calls.forEach(function(call) {
|
||||
if (!Array.isArray(call)) {
|
||||
throw new Error("loop.requestMulti: each call must be an array of options, " +
|
||||
|
@ -66,6 +66,8 @@ var gCurrentUser = null;
|
||||
var gRoomsCache = null;
|
||||
// Global variable that keeps track of the link clicker channel.
|
||||
var gLinkClickerChannel = null;
|
||||
// Global variable that keeps track of in-progress getAll requests.
|
||||
var gGetAllPromise = null;
|
||||
|
||||
/**
|
||||
* Extend a `target` object with the properties defined in `source`.
|
||||
@ -530,23 +532,19 @@ var LoopRoomsInternal = {
|
||||
*
|
||||
* @param {String} [version] If set, we will fetch a list of changed rooms since
|
||||
* `version`. Optional.
|
||||
* @param {Function} callback Function that will be invoked once the operation
|
||||
* finished. The first argument passed will be an
|
||||
* `Error` object or `null`. The second argument will
|
||||
* be the list of rooms, if it was fetched successfully.
|
||||
* @return {Promise} A promise that is resolved with a list of rooms
|
||||
* on success, or rejected with an error if it fails.
|
||||
*/
|
||||
getAll: function(version = null, callback = null) {
|
||||
if (!callback) {
|
||||
callback = version;
|
||||
version = null;
|
||||
getAll: function(version) {
|
||||
if (gGetAllPromise && !version) {
|
||||
return gGetAllPromise;
|
||||
}
|
||||
|
||||
Task.spawn(function* () {
|
||||
if (!gDirty) {
|
||||
callback(null, [...this.rooms.values()]);
|
||||
return;
|
||||
}
|
||||
if (!gDirty) {
|
||||
return Promise.resolve([...this.rooms.values()]);
|
||||
}
|
||||
|
||||
gGetAllPromise = Task.spawn(function* () {
|
||||
// Fetch the rooms from the server.
|
||||
let url = "/rooms" + (version ? "?version=" + encodeURIComponent(version) : "");
|
||||
let response = yield MozLoopService.hawkRequest(this.sessionType, url, "GET");
|
||||
@ -585,10 +583,15 @@ var LoopRoomsInternal = {
|
||||
|
||||
// Set the 'dirty' flag back to FALSE, since the list is as fresh as can be now.
|
||||
gDirty = false;
|
||||
callback(null, [...this.rooms.values()]);
|
||||
gGetAllPromise = null;
|
||||
return [...this.rooms.values()];
|
||||
}.bind(this)).catch(error => {
|
||||
callback(error);
|
||||
gGetAllPromise = null;
|
||||
// Re-throw error so callers get notified.
|
||||
throw error;
|
||||
});
|
||||
|
||||
return gGetAllPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -940,6 +943,7 @@ var LoopRoomsInternal = {
|
||||
|
||||
let oldDirty = gDirty;
|
||||
gDirty = true;
|
||||
|
||||
// If we were already dirty, then get the full set of rooms. For example,
|
||||
// we'd already be dirty if we had started up but not got the list of rooms
|
||||
// yet.
|
||||
@ -1037,7 +1041,13 @@ this.LoopRooms = {
|
||||
},
|
||||
|
||||
getAll: function(version, callback) {
|
||||
return LoopRoomsInternal.getAll(version, callback);
|
||||
if (!callback) {
|
||||
callback = version;
|
||||
version = null;
|
||||
}
|
||||
|
||||
LoopRoomsInternal.getAll(version).then(result => callback(null, result))
|
||||
.catch(error => callback(error));
|
||||
},
|
||||
|
||||
get: function(roomToken, callback) {
|
||||
@ -1107,6 +1117,10 @@ this.LoopRooms = {
|
||||
},
|
||||
|
||||
promise: function(method, ...params) {
|
||||
if (method == "getAll") {
|
||||
return LoopRoomsInternal.getAll(...params);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this[method](...params, (error, result) => {
|
||||
if (error) {
|
||||
@ -1140,6 +1154,7 @@ this.LoopRooms = {
|
||||
for (let [key, value] of roomsCache) {
|
||||
LoopRoomsInternal.rooms.set(key, value);
|
||||
}
|
||||
gGetAllPromise = null;
|
||||
gDirty = false;
|
||||
}
|
||||
}
|
||||
|
159
browser/components/loop/test/xpcshell/test_looprooms_getall.js
Normal file
159
browser/components/loop/test/xpcshell/test_looprooms_getall.js
Normal file
@ -0,0 +1,159 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
Cu.import("resource:///modules/loop/LoopRooms.jsm");
|
||||
Cu.import("resource:///modules/Chat.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
timerHandlers.startTimer = callback => callback();
|
||||
|
||||
var openChatOrig = Chat.open;
|
||||
|
||||
const kKey = "uGIs-kGbYt1hBBwjyW7MLQ";
|
||||
|
||||
// Rooms details as responded by the server.
|
||||
const kRoomsResponses = new Map([
|
||||
["_nxD4V4FflQ", {
|
||||
roomToken: "_nxD4V4FflQ",
|
||||
// Encrypted with roomKey "FliIGLUolW-xkKZVWstqKw".
|
||||
// roomKey is wrapped with kKey.
|
||||
context: {
|
||||
wrappedKey: "F3V27oPB+FgjFbVPML2PupONYqoIZ53XRU4BqG46Lr3eyIGumgCEqgjSe/MXAXiQ//8=",
|
||||
value: "df7B4SNxhOI44eJjQavCevADyCCxz6/DEZbkOkRUMVUxzS42FbzN6C2PqmCKDYUGyCJTwJ0jln8TLw==",
|
||||
alg: "AES-GCM"
|
||||
},
|
||||
roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ",
|
||||
maxSize: 2,
|
||||
ctime: 1405517546,
|
||||
participants: [{
|
||||
displayName: "Alexis",
|
||||
account: "alexis@example.com",
|
||||
roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
|
||||
}, {
|
||||
displayName: "Adam",
|
||||
roomConnectionId: "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7"
|
||||
}]
|
||||
}],
|
||||
["QzBbvGmIZWU", {
|
||||
roomToken: "QzBbvGmIZWU",
|
||||
roomName: "Second Room Name",
|
||||
roomUrl: "http://localhost:3000/rooms/QzBbvGmIZWU",
|
||||
maxSize: 2,
|
||||
ctime: 140551741
|
||||
}],
|
||||
["3jKS_Els9IU", {
|
||||
roomToken: "3jKS_Els9IU",
|
||||
roomName: "Third Room Name",
|
||||
roomUrl: "http://localhost:3000/rooms/3jKS_Els9IU",
|
||||
maxSize: 3,
|
||||
clientMaxSize: 2,
|
||||
ctime: 1405518241
|
||||
}]
|
||||
]);
|
||||
|
||||
// LoopRooms emits various events. Test if they work as expected here.
|
||||
var gQueuedResponses = [];
|
||||
|
||||
add_task(function* setup_server() {
|
||||
loopServer.registerPathHandler("/registration", (req, res) => {
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
});
|
||||
|
||||
loopServer.registerPathHandler("/rooms", (req, res) => {
|
||||
res.processAsync();
|
||||
|
||||
gQueuedResponses.push({
|
||||
req: req,
|
||||
res: res
|
||||
});
|
||||
});
|
||||
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
yield MozLoopService.promiseRegisteredWithServers();
|
||||
});
|
||||
|
||||
function handleQueuedResponses() {
|
||||
gQueuedResponses.forEach((xhr) => {
|
||||
xhr.res.setStatusLine(null, 200, "OK");
|
||||
xhr.res.write(JSON.stringify([...kRoomsResponses.values()]));
|
||||
xhr.res.finish();
|
||||
});
|
||||
gQueuedResponses = [];
|
||||
}
|
||||
|
||||
function rejectQueuedResponses() {
|
||||
gQueuedResponses.forEach((xhr) => {
|
||||
xhr.res.setStatusLine(null, 404, "Not found");
|
||||
xhr.res.finish();
|
||||
});
|
||||
gQueuedResponses = [];
|
||||
}
|
||||
|
||||
add_task(function* test_getAllRooms_failure() {
|
||||
let firstGetAllPromise = LoopRooms.promise("getAll");
|
||||
|
||||
// Check the first one hasn't been resolved yet.
|
||||
yield waitForCondition(() => gQueuedResponses.length == 1);
|
||||
|
||||
rejectQueuedResponses();
|
||||
|
||||
yield Assert.rejects(firstGetAllPromise, "Not found", "should throw failure");
|
||||
});
|
||||
|
||||
// Test fetching getAll with multiple requests.
|
||||
add_task(function* test_getAllRooms_multiple() {
|
||||
let resolvedFirstCall = false;
|
||||
let resolvedSecondCall = false;
|
||||
|
||||
let firstGetAllPromise = LoopRooms.promise("getAll").then(() => {
|
||||
resolvedFirstCall = true;
|
||||
});
|
||||
|
||||
// Check the first one hasn't been resolved yet.
|
||||
yield waitForCondition(() => gQueuedResponses.length == 1);
|
||||
Assert.equal(resolvedFirstCall, false);
|
||||
|
||||
// Queue a second one.
|
||||
let secondGetAllPromise = LoopRooms.promise("getAll").then(() => {
|
||||
resolvedSecondCall = true;
|
||||
});
|
||||
|
||||
// Check the second one hasn't been resolved yet.
|
||||
yield waitForCondition(() => gQueuedResponses.length == 1);
|
||||
Assert.equal(resolvedFirstCall, false);
|
||||
Assert.equal(resolvedSecondCall, false);
|
||||
|
||||
// Now resolve the requests, and check the calls returned.
|
||||
handleQueuedResponses();
|
||||
|
||||
// Ensure the promises have resolved.
|
||||
yield firstGetAllPromise;
|
||||
yield secondGetAllPromise;
|
||||
|
||||
yield waitForCondition(() => resolvedFirstCall == true);
|
||||
yield waitForCondition(() => resolvedSecondCall == true);
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
|
||||
Services.prefs.setCharPref("loop.key", kKey);
|
||||
|
||||
do_register_cleanup(function() {
|
||||
// Revert original Chat.open implementation
|
||||
Chat.open = openChatOrig;
|
||||
Services.prefs.clearUserPref("loop.key");
|
||||
Services.prefs.clearUserPref("loop.key.fxa");
|
||||
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
@ -11,6 +11,7 @@ skip-if = toolkit == 'gonk'
|
||||
[test_looprooms.js]
|
||||
[test_looprooms_encryption_in_fxa.js]
|
||||
[test_looprooms_first_notification.js]
|
||||
[test_looprooms_getall.js]
|
||||
[test_looprooms_upgrade_to_encryption.js]
|
||||
[test_loopservice_dnd.js]
|
||||
[test_loopservice_encryptionkey.js]
|
||||
|
@ -784,14 +784,17 @@ nsDefaultCommandLineHandler.prototype = {
|
||||
var params = new URLSearchParams(url.query);
|
||||
// We don't want to rewrite all Bing URLs coming from external apps. Look
|
||||
// for the magic URL parm that's present in searches from the task bar.
|
||||
// (Typed searches use "form=WNSGPH", Cortana voice searches use "FORM=WNSBOX"
|
||||
// for direct results, or "FORM=WNSFC2" for "see more results on
|
||||
// Bing.com")
|
||||
// * Typed searches use "form=WNSGPH"
|
||||
// * Cortana voice searches use "FORM=WNSBOX" or direct results, or "FORM=WNSFC2"
|
||||
// for "see more results on Bing.com")
|
||||
// * Cortana voice searches started from "Hey, Cortana" use "form=WNSHCO"
|
||||
// or "form=WNSSSV"
|
||||
var allowedParams = ["WNSGPH", "WNSBOX", "WNSFC2", "WNSHCO", "WNSSSV"];
|
||||
var formParam = params.get("form");
|
||||
if (!formParam) {
|
||||
formParam = params.get("FORM");
|
||||
}
|
||||
if (formParam == "WNSGPH" || formParam == "WNSBOX" || formParam == "WNSFC2") {
|
||||
if (allowedParams.indexOf(formParam) != -1) {
|
||||
var term = params.get("q");
|
||||
var ss = Components.classes["@mozilla.org/browser/search-service;1"]
|
||||
.getService(nsIBrowserSearchService);
|
||||
|
@ -78,7 +78,9 @@ const MESSAGES = [
|
||||
];
|
||||
|
||||
// The list of messages we accept from <xul:browser>s that have no tab
|
||||
// assigned. Those are for example the ones that preload about:newtab pages.
|
||||
// assigned, or whose windows have gone away. Those are for example the
|
||||
// ones that preload about:newtab pages, or from browsers where the window
|
||||
// has just been closed.
|
||||
const NOTAB_MESSAGES = new Set([
|
||||
// For a description see above.
|
||||
"SessionStore:setupSyncHandler",
|
||||
@ -126,6 +128,8 @@ const TAB_EVENTS = [
|
||||
"TabUnpinned"
|
||||
];
|
||||
|
||||
const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm", this);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
|
||||
Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", this);
|
||||
@ -396,6 +400,11 @@ var SessionStoreInternal = {
|
||||
// properly handle final update message.
|
||||
_closedTabs: new WeakMap(),
|
||||
|
||||
// A map (xul:browser -> object) that maps a browser associated with a
|
||||
// recently closed tab due to a window closure to the tab state information
|
||||
// that is being stored in _closedWindows for that tab.
|
||||
_closedWindowTabs: new WeakMap(),
|
||||
|
||||
// whether a setBrowserState call is in progress
|
||||
_browserSetState: false,
|
||||
|
||||
@ -652,14 +661,15 @@ var SessionStoreInternal = {
|
||||
// If we got here, that means we're dealing with a frame message
|
||||
// manager message, so the target will be a <xul:browser>.
|
||||
var browser = aMessage.target;
|
||||
var win = browser.ownerDocument.defaultView;
|
||||
let tab = win.gBrowser.getTabForBrowser(browser);
|
||||
let win = browser.ownerDocument.defaultView;
|
||||
let tab = win ? win.gBrowser.getTabForBrowser(browser) : null;
|
||||
|
||||
// Ensure we receive only specific messages from <xul:browser>s that
|
||||
// have no tab assigned, e.g. the ones that preload about:newtab pages.
|
||||
// have no tab or window assigned, e.g. the ones that preload
|
||||
// about:newtab pages, or windows that have closed.
|
||||
if (!tab && !NOTAB_MESSAGES.has(aMessage.name)) {
|
||||
throw new Error(`received unexpected message '${aMessage.name}' ` +
|
||||
`from a browser that has no tab`);
|
||||
`from a browser that has no tab or window`);
|
||||
}
|
||||
|
||||
let data = aMessage.data || {};
|
||||
@ -877,7 +887,10 @@ var SessionStoreInternal = {
|
||||
this.onBrowserCrashed(win, target);
|
||||
break;
|
||||
case "XULFrameLoaderCreated":
|
||||
if (target.tagName == "browser" && target.frameLoader && target.permanentKey) {
|
||||
if (target.namespaceURI == NS_XUL &&
|
||||
target.localName == "browser" &&
|
||||
target.frameLoader &&
|
||||
target.permanentKey) {
|
||||
this._lastKnownFrameLoader.set(target.permanentKey, target.frameLoader);
|
||||
this.resetEpoch(target);
|
||||
}
|
||||
@ -1202,11 +1215,14 @@ var SessionStoreInternal = {
|
||||
|
||||
// Collect window data only when *not* closed during shutdown.
|
||||
if (RunState.isRunning) {
|
||||
// Flush all data queued in the content script before the window is gone.
|
||||
TabState.flushWindow(aWindow);
|
||||
// Grab the most recent window data. The tab data will be updated
|
||||
// once we finish flushing all of the messages from the tabs.
|
||||
let tabMap = this._collectWindowData(aWindow);
|
||||
|
||||
// update all window data for a last time
|
||||
this._collectWindowData(aWindow);
|
||||
for (let [tab, tabData] of tabMap) {
|
||||
let permanentKey = tab.linkedBrowser.permanentKey;
|
||||
this._closedWindowTabs.set(permanentKey, tabData);
|
||||
}
|
||||
|
||||
if (isFullyLoaded) {
|
||||
winData.title = tabbrowser.selectedBrowser.contentTitle || tabbrowser.selectedTab.label;
|
||||
@ -1226,45 +1242,86 @@ var SessionStoreInternal = {
|
||||
// recently something was closed.
|
||||
winData.closedAt = Date.now();
|
||||
|
||||
// Save non-private windows if they have at
|
||||
// least one saveable tab or are the last window.
|
||||
// we don't want to save the busy state
|
||||
delete winData.busy;
|
||||
|
||||
// Now we have to figure out if this window is worth saving in the _closedWindows
|
||||
// Object.
|
||||
//
|
||||
// We're about to flush the tabs from this window, but it's possible that we
|
||||
// might never hear back from the content process(es) in time before the user
|
||||
// chooses to restore the closed window. So we do the following:
|
||||
//
|
||||
// 1) Use the tab state cache to determine synchronously if the window is
|
||||
// worth stashing in _closedWindows.
|
||||
// 2) Flush the window.
|
||||
// 3) When the flush is complete, revisit our decision to store the window
|
||||
// in _closedWindows, and add/remove as necessary.
|
||||
if (!winData.isPrivate) {
|
||||
// Remove any open private tabs the window may contain.
|
||||
PrivacyFilter.filterPrivateTabs(winData);
|
||||
|
||||
// Determine whether the window has any tabs worth saving.
|
||||
let hasSaveableTabs = winData.tabs.some(this._shouldSaveTabState);
|
||||
|
||||
// When closing windows one after the other until Firefox quits, we
|
||||
// will move those closed in series back to the "open windows" bucket
|
||||
// before writing to disk. If however there is only a single window
|
||||
// with tabs we deem not worth saving then we might end up with a
|
||||
// random closed or even a pop-up window re-opened. To prevent that
|
||||
// we explicitly allow saving an "empty" window state.
|
||||
let isLastWindow =
|
||||
Object.keys(this._windows).length == 1 &&
|
||||
!this._closedWindows.some(win => win._shouldRestore || false);
|
||||
|
||||
if (hasSaveableTabs || isLastWindow) {
|
||||
// we don't want to save the busy state
|
||||
delete winData.busy;
|
||||
|
||||
this._closedWindows.unshift(winData);
|
||||
this._capClosedWindows();
|
||||
}
|
||||
this.maybeSaveClosedWindow(winData);
|
||||
}
|
||||
|
||||
// clear this window from the list
|
||||
delete this._windows[aWindow.__SSi];
|
||||
// The tabbrowser binding will go away once the window is closed,
|
||||
// so we'll hold a reference to the browsers in the closure here.
|
||||
let browsers = tabbrowser.browsers;
|
||||
|
||||
// save the state without this window to disk
|
||||
this.saveStateDelayed();
|
||||
TabStateFlusher.flushWindow(aWindow).then(() => {
|
||||
// At this point, aWindow is closed! You should probably not try to
|
||||
// access any DOM elements from aWindow within this callback unless
|
||||
// you're holding on to them in the closure.
|
||||
|
||||
// We can still access tabbrowser.browsers, thankfully.
|
||||
for (let browser of browsers) {
|
||||
if (this._closedWindowTabs.has(browser.permanentKey)) {
|
||||
let tabData = this._closedWindowTabs.get(browser.permanentKey);
|
||||
TabState.copyFromCache(browser, tabData);
|
||||
this._closedWindowTabs.delete(browser.permanentKey);
|
||||
}
|
||||
}
|
||||
|
||||
// Save non-private windows if they have at
|
||||
// least one saveable tab or are the last window.
|
||||
if (!winData.isPrivate) {
|
||||
// It's possible that a tab switched its privacy state at some point
|
||||
// before our flush, so we need to filter again.
|
||||
PrivacyFilter.filterPrivateTabs(winData);
|
||||
this.maybeSaveClosedWindow(winData);
|
||||
}
|
||||
|
||||
// clear this window from the list
|
||||
delete this._windows[aWindow.__SSi];
|
||||
// Update the tabs data now that we've got the most
|
||||
// recent information.
|
||||
this.cleanUpWindow(aWindow, winData);
|
||||
|
||||
// save the state without this window to disk
|
||||
this.saveStateDelayed();
|
||||
});
|
||||
} else {
|
||||
this.cleanUpWindow(aWindow, winData);
|
||||
}
|
||||
|
||||
for (let i = 0; i < tabbrowser.tabs.length; i++) {
|
||||
this.onTabRemove(aWindow, tabbrowser.tabs[i], true);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Clean up the message listeners on a window that has finally
|
||||
* gone away. Call this once you're sure you don't want to hear
|
||||
* from any of this windows tabs from here forward.
|
||||
*
|
||||
* @param aWindow
|
||||
* The browser window we're cleaning up.
|
||||
* @param winData
|
||||
* The data for the window that we should hold in the
|
||||
* DyingWindowCache in case anybody is still holding a
|
||||
* reference to it.
|
||||
*/
|
||||
cleanUpWindow(aWindow, winData) {
|
||||
// Cache the window state until it is completely gone.
|
||||
DyingWindowCache.set(aWindow, winData);
|
||||
|
||||
@ -1274,6 +1331,57 @@ var SessionStoreInternal = {
|
||||
delete aWindow.__SSi;
|
||||
},
|
||||
|
||||
/**
|
||||
* Decides whether or not a closed window should be put into the
|
||||
* _closedWindows Object. This might be called multiple times per
|
||||
* window, and will do the right thing of moving the window data
|
||||
* in or out of _closedWindows if the winData indicates that our
|
||||
* need for saving it has changed.
|
||||
*
|
||||
* @param winData
|
||||
* The data for the closed window that we might save.
|
||||
*/
|
||||
maybeSaveClosedWindow(winData) {
|
||||
if (RunState.isRunning) {
|
||||
// Determine whether the window has any tabs worth saving.
|
||||
let hasSaveableTabs = winData.tabs.some(this._shouldSaveTabState);
|
||||
|
||||
// When closing windows one after the other until Firefox quits, we
|
||||
// will move those closed in series back to the "open windows" bucket
|
||||
// before writing to disk. If however there is only a single window
|
||||
// with tabs we deem not worth saving then we might end up with a
|
||||
// random closed or even a pop-up window re-opened. To prevent that
|
||||
// we explicitly allow saving an "empty" window state.
|
||||
let isLastWindow =
|
||||
Object.keys(this._windows).length == 1 &&
|
||||
!this._closedWindows.some(win => win._shouldRestore || false);
|
||||
|
||||
// Note that we might already have this window stored in
|
||||
// _closedWindows from a previous call to this function.
|
||||
let winIndex = this._closedWindows.indexOf(winData);
|
||||
let alreadyStored = (winIndex != -1);
|
||||
let shouldStore = (hasSaveableTabs || isLastWindow);
|
||||
|
||||
if (shouldStore && !alreadyStored) {
|
||||
let index = this._closedWindows.findIndex(win => {
|
||||
return win.closedAt < winData.closedAt;
|
||||
});
|
||||
|
||||
// If we found no tab closed before our
|
||||
// tab then just append it to the list.
|
||||
if (index == -1) {
|
||||
index = this._closedWindows.length;
|
||||
}
|
||||
|
||||
// Insert tabData at the right position.
|
||||
this._closedWindows.splice(index, 0, winData);
|
||||
this._capClosedWindows();
|
||||
} else if (!shouldStore && alreadyStored) {
|
||||
this._closedWindows.splice(winIndex, 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* On quit application requested
|
||||
*/
|
||||
@ -1620,6 +1728,7 @@ var SessionStoreInternal = {
|
||||
// not add it back to the list of closed tabs again.
|
||||
if (closedTab.permanentKey) {
|
||||
this._closedTabs.delete(closedTab.permanentKey);
|
||||
this._closedWindowTabs.delete(closedTab.permanentKey);
|
||||
delete closedTab.permanentKey;
|
||||
}
|
||||
|
||||
@ -2578,9 +2687,20 @@ var SessionStoreInternal = {
|
||||
return { windows: windows };
|
||||
},
|
||||
|
||||
/**
|
||||
* Gathers data about a window and its tabs, and updates its
|
||||
* entry in this._windows.
|
||||
*
|
||||
* @param aWindow
|
||||
* Window references.
|
||||
* @returns a Map mapping the browser tabs from aWindow to the tab
|
||||
* entry that was put into the window data in this._windows.
|
||||
*/
|
||||
_collectWindowData: function ssi_collectWindowData(aWindow) {
|
||||
let tabMap = new Map();
|
||||
|
||||
if (!this._isWindowLoaded(aWindow))
|
||||
return;
|
||||
return tabMap;
|
||||
|
||||
let tabbrowser = aWindow.gBrowser;
|
||||
let tabs = tabbrowser.tabs;
|
||||
@ -2589,7 +2709,9 @@ var SessionStoreInternal = {
|
||||
|
||||
// update the internal state data for this window
|
||||
for (let tab of tabs) {
|
||||
tabsData.push(TabState.collect(tab));
|
||||
let tabData = TabState.collect(tab);
|
||||
tabMap.set(tab, tabData);
|
||||
tabsData.push(tabData);
|
||||
}
|
||||
winData.selected = tabbrowser.mTabBox.selectedIndex + 1;
|
||||
|
||||
@ -2602,6 +2724,7 @@ var SessionStoreInternal = {
|
||||
aWindow.__SS_lastSessionWindowID;
|
||||
|
||||
DirtyWindows.remove(aWindow);
|
||||
return tabMap;
|
||||
},
|
||||
|
||||
/* ........ Restoring Functionality .............. */
|
||||
|
@ -27,6 +27,14 @@ this.TabStateFlusher = Object.freeze({
|
||||
return TabStateFlusherInternal.flush(browser);
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests an async flush for all browsers of a given window. Returns a Promise
|
||||
* that will resolve when we've heard back from all browsers.
|
||||
*/
|
||||
flushWindow(window) {
|
||||
return TabStateFlusherInternal.flushWindow(window);
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves the flush request with the given flush ID.
|
||||
*/
|
||||
@ -75,6 +83,16 @@ var TabStateFlusherInternal = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Requests an async flush for all browsers of a given window. Returns a Promise
|
||||
* that will resolve when we've heard back from all browsers.
|
||||
*/
|
||||
flushWindow(window) {
|
||||
let browsers = window.gBrowser.browsers;
|
||||
let promises = browsers.map((browser) => this.flush(browser));
|
||||
return Promise.all(promises);
|
||||
},
|
||||
|
||||
/**
|
||||
* Resolves the flush request with the given flush ID.
|
||||
*/
|
||||
|
@ -26,10 +26,10 @@ function test() {
|
||||
"//input[@type='file']": filePath
|
||||
};
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
windowsToClose.forEach(function(win) {
|
||||
win.close();
|
||||
});
|
||||
registerCleanupFunction(function* () {
|
||||
for (let win of windowsToClose) {
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
}
|
||||
});
|
||||
|
||||
function test(aLambda) {
|
||||
|
@ -4,54 +4,7 @@
|
||||
|
||||
/**
|
||||
* Checks that restoring the last browser window in session is actually
|
||||
* working:
|
||||
* 1.1) Open a new browser window
|
||||
* 1.2) Add some tabs
|
||||
* 1.3) Close that window
|
||||
* 1.4) Opening another window
|
||||
* --> State is restored
|
||||
*
|
||||
* 2.1) Open a new browser window
|
||||
* 2.2) Add some tabs
|
||||
* 2.3) Enter private browsing mode
|
||||
* 2.4) Close the window while still in private browsing mode
|
||||
* 2.5) Opening a new window
|
||||
* --> State is not restored, because private browsing mode is still active
|
||||
* 2.6) Leaving private browsing mode
|
||||
* 2.7) Open another window
|
||||
* --> State (that was before entering PBM) is restored
|
||||
*
|
||||
* 3.1) Open a new browser window
|
||||
* 3.2) Add some tabs
|
||||
* 3.4) Open some popups
|
||||
* 3.5) Add another tab to one popup (so that it gets stored) and close it again
|
||||
* 3.5) Close the browser window
|
||||
* 3.6) Open another browser window
|
||||
* --> State of the closed browser window, but not of the popup, is restored
|
||||
*
|
||||
* 4.1) Open a popup
|
||||
* 4.2) Add another tab to the popup (so that it gets stored) and close it again
|
||||
* 4.3) Open a window
|
||||
* --> Nothing at all should be restored
|
||||
*
|
||||
* 5.1) Open two browser windows and close them again
|
||||
* 5.2) undoCloseWindow() one
|
||||
* 5.3) Open another browser window
|
||||
* --> Nothing at all should be restored
|
||||
*
|
||||
* Checks the new notifications are correctly posted and processed, that is
|
||||
* for each successful -requested a -granted is received, but omitted if
|
||||
* -requested was cnceled
|
||||
* Said notifications are:
|
||||
* - browser-lastwindow-close-requested
|
||||
* - browser-lastwindow-close-granted
|
||||
* Tests are:
|
||||
* 6) Cancel closing when first observe a -requested
|
||||
* --> Window is kept open
|
||||
* 7) Count the number of notifications
|
||||
* --> count(-requested) == count(-granted) + 1
|
||||
* --> (The first -requested was canceled, so off-by-one)
|
||||
* 8) (Mac only) Mac version of Test 5 additionally preparing Test 6
|
||||
* working.
|
||||
*
|
||||
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=354894
|
||||
* @note It is implicitly tested that restoring the last window works when
|
||||
@ -71,48 +24,86 @@
|
||||
* notifications. The latter won't.
|
||||
*/
|
||||
|
||||
function browserWindowsCount(expected, msg) {
|
||||
if (typeof expected == "number")
|
||||
expected = [expected, expected];
|
||||
let count = 0;
|
||||
// Some urls that might be opened in tabs and/or popups
|
||||
// Do not use about:blank:
|
||||
// That one is reserved for special purposes in the tests
|
||||
const TEST_URLS = ["about:mozilla", "about:buildconfig"];
|
||||
|
||||
// Number of -request notifications to except
|
||||
// remember to adjust when adding new tests
|
||||
const NOTIFICATIONS_EXPECTED = 6;
|
||||
|
||||
// Window features of popup windows
|
||||
const POPUP_FEATURES = "toolbar=no,resizable=no,status=no";
|
||||
|
||||
// Window features of browser windows
|
||||
const CHROME_FEATURES = "chrome,all,dialog=no";
|
||||
|
||||
const IS_MAC = navigator.platform.match(/Mac/);
|
||||
|
||||
/**
|
||||
* Returns an Object with two properties:
|
||||
* open (int):
|
||||
* A count of how many non-closed navigator:browser windows there are.
|
||||
* winstates (int):
|
||||
* A count of how many windows there are in the SessionStore state.
|
||||
*/
|
||||
function getBrowserWindowsCount() {
|
||||
let open = 0;
|
||||
let e = Services.wm.getEnumerator("navigator:browser");
|
||||
while (e.hasMoreElements()) {
|
||||
if (!e.getNext().closed)
|
||||
++count;
|
||||
++open;
|
||||
}
|
||||
is(count, expected[0], msg + " (nsIWindowMediator)");
|
||||
let state = ss.getBrowserState();
|
||||
is(JSON.parse(state).windows.length, expected[1], msg + " (getBrowserState)");
|
||||
|
||||
let winstates = JSON.parse(ss.getBrowserState()).windows.length;
|
||||
|
||||
return { open, winstates };
|
||||
}
|
||||
|
||||
function test() {
|
||||
browserWindowsCount(1, "Only one browser window should be open initially");
|
||||
add_task(function* setup() {
|
||||
// Make sure we've only got one browser window to start with
|
||||
let { open, winstates } = getBrowserWindowsCount();
|
||||
is(open, 1, "Should only be one open window");
|
||||
is(winstates, 1, "Should only be one window state in SessionStore");
|
||||
|
||||
waitForExplicitFinish();
|
||||
// This test takes some time to run, and it could timeout randomly.
|
||||
// So we require a longer timeout. See bug 528219.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
// Some urls that might be opened in tabs and/or popups
|
||||
// Do not use about:blank:
|
||||
// That one is reserved for special purposes in the tests
|
||||
const TEST_URLS = ["about:mozilla", "about:buildconfig"];
|
||||
// Make the main test window not count as a browser window any longer
|
||||
let oldWinType = document.documentElement.getAttribute("windowtype");
|
||||
document.documentElement.setAttribute("windowtype", "navigator:testrunner");
|
||||
|
||||
// Number of -request notifications to except
|
||||
// remember to adjust when adding new tests
|
||||
const NOTIFICATIONS_EXPECTED = 6;
|
||||
registerCleanupFunction(() => {
|
||||
document.documentElement.setAttribute("windowtype", "navigator:browser");
|
||||
});
|
||||
});
|
||||
|
||||
// Window features of popup windows
|
||||
const POPUP_FEATURES = "toolbar=no,resizable=no,status=no";
|
||||
|
||||
// Window features of browser windows
|
||||
const CHROME_FEATURES = "chrome,all,dialog=no";
|
||||
|
||||
// Store the old window type for cleanup
|
||||
let oldWinType = "";
|
||||
// Store the old tabs.warnOnClose pref so that we may reset it during
|
||||
// cleanup
|
||||
let oldWarnTabsOnClose = gPrefService.getBoolPref("browser.tabs.warnOnClose");
|
||||
/**
|
||||
* Sets up one of our tests by setting the right preferences, and
|
||||
* then opening up a browser window preloaded with some tabs.
|
||||
*
|
||||
* @param options (Object)
|
||||
* An object that can contain the following properties:
|
||||
*
|
||||
* private:
|
||||
* Whether or not the opened window should be private.
|
||||
*
|
||||
* denyFirst:
|
||||
* Whether or not the first window that attempts to close
|
||||
* via closeWindowForRestoration should be denied.
|
||||
*
|
||||
* @param testFunction (Function*)
|
||||
* A generator function that yields Promises to be run
|
||||
* once the test has been set up.
|
||||
*
|
||||
* @returns Promise
|
||||
* Resolves once the test has been cleaned up.
|
||||
*/
|
||||
let setupTest = Task.async(function*(options, testFunction) {
|
||||
yield pushPrefs(["browser.startup.page", 3],
|
||||
["browser.tabs.warnOnClose", false]);
|
||||
|
||||
// Observe these, and also use to count the number of hits
|
||||
let observing = {
|
||||
@ -129,355 +120,355 @@ function test() {
|
||||
observing[aTopic]++;
|
||||
|
||||
// handle some tests
|
||||
if (++hitCount == 1) {
|
||||
// Test 6
|
||||
if (options.denyFirst && ++hitCount == 1) {
|
||||
aCancel.QueryInterface(Ci.nsISupportsPRBool).data = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Sets prefs as the testsuite requires
|
||||
* @note Will be reset in cleanTestSuite just before finishing the tests
|
||||
*/
|
||||
function setPrefs() {
|
||||
gPrefService.setIntPref("browser.startup.page", 3);
|
||||
gPrefService.setBoolPref("browser.tabs.warnOnClose", false);
|
||||
for (let o in observing) {
|
||||
Services.obs.addObserver(observer, o, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Sets up this testsuite
|
||||
*/
|
||||
function setupTestsuite(testFn) {
|
||||
// Register our observers
|
||||
for (let o in observing)
|
||||
Services.obs.addObserver(observer, o, false);
|
||||
let private = options.private || false;
|
||||
let newWin = yield promiseNewWindowLoaded({ private });
|
||||
|
||||
// Make the main test window not count as a browser window any longer
|
||||
oldWinType = document.documentElement.getAttribute("windowtype");
|
||||
document.documentElement.setAttribute("windowtype", "navigator:testrunner");
|
||||
injectTestTabs(newWin);
|
||||
|
||||
yield testFunction(newWin, observing);
|
||||
|
||||
let count = getBrowserWindowsCount();
|
||||
is(count.open, 0, "Got right number of open windows");
|
||||
is(count.winstates, 1, "Got right number of stored window states");
|
||||
|
||||
for (let o in observing) {
|
||||
Services.obs.removeObserver(observer, o);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: Cleans up behind the testsuite
|
||||
*/
|
||||
function cleanupTestsuite(callback) {
|
||||
// Finally remove observers again
|
||||
for (let o in observing)
|
||||
Services.obs.removeObserver(observer, o);
|
||||
yield popPrefs();
|
||||
});
|
||||
|
||||
// Reset the prefs we touched
|
||||
let pref = "browser.startup.page";
|
||||
if (gPrefService.prefHasUserValue(pref))
|
||||
gPrefService.clearUserPref(pref);
|
||||
gPrefService.setBoolPref("browser.tabs.warnOnClose", oldWarnTabsOnClose);
|
||||
/**
|
||||
* Loads a TEST_URLS into a browser window.
|
||||
*
|
||||
* @param win (Window)
|
||||
* The browser window to load the tabs in
|
||||
*/
|
||||
function injectTestTabs(win) {
|
||||
TEST_URLS.forEach(function (url) {
|
||||
win.gBrowser.addTab(url);
|
||||
});
|
||||
}
|
||||
|
||||
// Reset the window type
|
||||
document.documentElement.setAttribute("windowtype", oldWinType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: sets the prefs and a new window with our test tabs
|
||||
*/
|
||||
function setupTestAndRun(aIsPrivateWindow, testFn) {
|
||||
// Prepare the prefs
|
||||
setPrefs();
|
||||
|
||||
// Prepare a window; open it and add more tabs
|
||||
let options = {};
|
||||
if (aIsPrivateWindow) {
|
||||
options = {private: true};
|
||||
/**
|
||||
* Attempts to close a window via BrowserTryToCloseWindow so that
|
||||
* we get the browser-lastwindow-close-requested and
|
||||
* browser-lastwindow-close-granted observer notifications.
|
||||
*
|
||||
* @param win (Window)
|
||||
* The window to try to close
|
||||
* @returns Promise
|
||||
* Resolves to true if the window closed, or false if the window
|
||||
* was denied the ability to close.
|
||||
*/
|
||||
function closeWindowForRestoration(win) {
|
||||
return new Promise((resolve) => {
|
||||
let closePromise = BrowserTestUtils.windowClosed(win);
|
||||
win.BrowserTryToCloseWindow();
|
||||
if (!win.closed) {
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
|
||||
whenNewWindowLoaded(options, function (newWin) {
|
||||
TEST_URLS.forEach(function (url) {
|
||||
newWin.gBrowser.addTab(url);
|
||||
});
|
||||
|
||||
executeSoon(() => testFn(newWin));
|
||||
closePromise.then(() => {
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal in-session restore
|
||||
*
|
||||
* @note: Non-Mac only
|
||||
*
|
||||
* Should do the following:
|
||||
* 1. Open a new browser window
|
||||
* 2. Add some tabs
|
||||
* 3. Close that window
|
||||
* 4. Opening another window
|
||||
* 5. Checks that state is restored
|
||||
*/
|
||||
add_task(function* test_open_close_normal() {
|
||||
if (IS_MAC) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 1: Normal in-session restore
|
||||
* @note: Non-Mac only
|
||||
*/
|
||||
function testOpenCloseNormal(nextFn) {
|
||||
setupTestAndRun(false, function(newWin) {
|
||||
// Close the window
|
||||
// window.close doesn't push any close events,
|
||||
// so use BrowserTryToCloseWindow
|
||||
newWin.BrowserTryToCloseWindow();
|
||||
yield setupTest({ denyFirst: true }, function*(newWin, obs) {
|
||||
let closed = yield closeWindowForRestoration(newWin);
|
||||
ok(!closed, "First close request should have been denied");
|
||||
|
||||
// The first request to close is denied by our observer (Test 6)
|
||||
ok(!newWin.closed, "First close request was denied");
|
||||
if (!newWin.closed) {
|
||||
newWin.BrowserTryToCloseWindow();
|
||||
ok(newWin.closed, "Second close request was granted");
|
||||
}
|
||||
closed = yield closeWindowForRestoration(newWin);
|
||||
ok(closed, "Second close request should be accepted");
|
||||
|
||||
// Open a new window
|
||||
// The previously closed window should be restored
|
||||
whenNewWindowLoaded({}, function (newWin) {
|
||||
is(newWin.gBrowser.browsers.length, TEST_URLS.length + 2,
|
||||
"Restored window in-session with otherpopup windows around");
|
||||
newWin = yield promiseNewWindowLoaded();
|
||||
is(newWin.gBrowser.browsers.length, TEST_URLS.length + 2,
|
||||
"Restored window in-session with otherpopup windows around");
|
||||
|
||||
// Cleanup
|
||||
newWin.close();
|
||||
// Note that this will not result in the the browser-lastwindow-close
|
||||
// notifications firing for this other newWin.
|
||||
yield BrowserTestUtils.closeWindow(newWin);
|
||||
|
||||
// Next please
|
||||
executeSoon(nextFn);
|
||||
});
|
||||
});
|
||||
// setupTest gave us a window which was denied for closing once, and then
|
||||
// closed.
|
||||
is(obs["browser-lastwindow-close-requested"], 2,
|
||||
"Got expected browser-lastwindow-close-requested notifications");
|
||||
is(obs["browser-lastwindow-close-granted"], 1,
|
||||
"Got expected browser-lastwindow-close-granted notifications");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* PrivateBrowsing in-session restore
|
||||
*
|
||||
* @note: Non-Mac only
|
||||
*
|
||||
* Should do the following:
|
||||
* 1. Open a new browser window A
|
||||
* 2. Add some tabs
|
||||
* 3. Close the window A as the last window
|
||||
* 4. Open a private browsing window B
|
||||
* 5. Make sure that B didn't restore the tabs from A
|
||||
* 6. Close private browsing window B
|
||||
* 7. Open a new window C
|
||||
* 8. Make sure that new window C has restored tabs from A
|
||||
*/
|
||||
add_task(function* test_open_close_private_browsing() {
|
||||
if (IS_MAC) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 2: PrivateBrowsing in-session restore
|
||||
* @note: Non-Mac only
|
||||
*/
|
||||
function testOpenClosePrivateBrowsing(nextFn) {
|
||||
setupTestAndRun(false, function(newWin) {
|
||||
// Close the window
|
||||
newWin.BrowserTryToCloseWindow();
|
||||
yield setupTest({}, function*(newWin, obs) {
|
||||
let closed = yield closeWindowForRestoration(newWin);
|
||||
ok(closed, "Should be able to close the window");
|
||||
|
||||
// Enter private browsing mode
|
||||
// Open a new window.
|
||||
// The previously closed window should NOT be restored
|
||||
whenNewWindowLoaded({private: true}, function (newWin) {
|
||||
is(newWin.gBrowser.browsers.length, 1,
|
||||
"Did not restore in private browing mode");
|
||||
newWin = yield promiseNewWindowLoaded({private: true});
|
||||
is(newWin.gBrowser.browsers.length, 1,
|
||||
"Did not restore in private browing mode");
|
||||
|
||||
// Cleanup
|
||||
newWin.BrowserTryToCloseWindow();
|
||||
closed = yield closeWindowForRestoration(newWin);
|
||||
ok(closed, "Should be able to close the window");
|
||||
|
||||
// Exit private browsing mode again
|
||||
whenNewWindowLoaded({}, function (newWin) {
|
||||
is(newWin.gBrowser.browsers.length, TEST_URLS.length + 2,
|
||||
"Restored after leaving private browsing again");
|
||||
newWin = yield promiseNewWindowLoaded();
|
||||
is(newWin.gBrowser.browsers.length, TEST_URLS.length + 2,
|
||||
"Restored tabs in a new non-private window");
|
||||
|
||||
newWin.close();
|
||||
// Note that this will not result in the the browser-lastwindow-close
|
||||
// notifications firing for this other newWin.
|
||||
yield BrowserTestUtils.closeWindow(newWin);
|
||||
|
||||
// Next please
|
||||
executeSoon(nextFn);
|
||||
});
|
||||
});
|
||||
});
|
||||
// We closed two windows with closeWindowForRestoration, and both
|
||||
// should have been successful.
|
||||
is(obs["browser-lastwindow-close-requested"], 2,
|
||||
"Got expected browser-lastwindow-close-requested notifications");
|
||||
is(obs["browser-lastwindow-close-granted"], 2,
|
||||
"Got expected browser-lastwindow-close-granted notifications");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Open some popup windows to check those aren't restored, but the browser
|
||||
* window is.
|
||||
*
|
||||
* @note: Non-Mac only
|
||||
*
|
||||
* Should do the following:
|
||||
* 1. Open a new browser window
|
||||
* 2. Add some tabs
|
||||
* 3. Open some popups
|
||||
* 4. Add another tab to one popup (so that it gets stored) and close it again
|
||||
* 5. Close the browser window
|
||||
* 6. Open another browser window
|
||||
* 7. Make sure that the tabs of the closed browser window, but not the popup,
|
||||
* are restored
|
||||
*/
|
||||
add_task(function* test_open_close_window_and_popup() {
|
||||
if (IS_MAC) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 3: Open some popup windows to check those aren't restored, but
|
||||
* the browser window is
|
||||
* @note: Non-Mac only
|
||||
*/
|
||||
function testOpenCloseWindowAndPopup(nextFn) {
|
||||
setupTestAndRun(false, function(newWin) {
|
||||
// open some popups
|
||||
let popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[0]);
|
||||
let popup2 = openDialog(location, "popup2", POPUP_FEATURES, TEST_URLS[1]);
|
||||
popup2.addEventListener("load", function() {
|
||||
popup2.removeEventListener("load", arguments.callee, false);
|
||||
popup2.gBrowser.addEventListener("load", function() {
|
||||
popup2.gBrowser.removeEventListener("load", arguments.callee, true);
|
||||
popup2.gBrowser.addTab(TEST_URLS[0]);
|
||||
// close the window
|
||||
newWin.BrowserTryToCloseWindow();
|
||||
yield setupTest({}, function*(newWin, obs) {
|
||||
let popupPromise = BrowserTestUtils.waitForNewWindow();
|
||||
openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[0]);
|
||||
let popup = yield popupPromise;
|
||||
|
||||
// Close the popup window
|
||||
// The test is successful when not this popup window is restored
|
||||
// but instead newWin
|
||||
popup2.close();
|
||||
let popup2Promise = BrowserTestUtils.waitForNewWindow();
|
||||
openDialog(location, "popup2", POPUP_FEATURES, TEST_URLS[1]);
|
||||
let popup2 = yield popup2Promise;
|
||||
|
||||
// open a new window the previously closed window should be restored to
|
||||
whenNewWindowLoaded({}, function (newWin) {
|
||||
is(newWin.gBrowser.browsers.length, TEST_URLS.length + 2,
|
||||
"Restored window and associated tabs in session");
|
||||
popup2.gBrowser.addTab(TEST_URLS[0]);
|
||||
|
||||
// Cleanup
|
||||
newWin.close();
|
||||
popup.close();
|
||||
let closed = yield closeWindowForRestoration(newWin);
|
||||
ok(closed, "Should be able to close the window");
|
||||
|
||||
// Next please
|
||||
executeSoon(nextFn);
|
||||
});
|
||||
}, true);
|
||||
}, false);
|
||||
});
|
||||
yield BrowserTestUtils.closeWindow(popup2);
|
||||
|
||||
newWin = yield promiseNewWindowLoaded();
|
||||
|
||||
is(newWin.gBrowser.browsers.length, TEST_URLS.length + 2,
|
||||
"Restored window and associated tabs in session");
|
||||
|
||||
yield BrowserTestUtils.closeWindow(popup);
|
||||
yield BrowserTestUtils.closeWindow(newWin);
|
||||
|
||||
// We closed one window with closeWindowForRestoration, and it should
|
||||
// have been successful.
|
||||
is(obs["browser-lastwindow-close-requested"], 1,
|
||||
"Got expected browser-lastwindow-close-requested notifications");
|
||||
is(obs["browser-lastwindow-close-granted"], 1,
|
||||
"Got expected browser-lastwindow-close-granted notifications");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Open some popup window to check it isn't restored. Instead nothing at all
|
||||
* should be restored
|
||||
*
|
||||
* @note: Non-Mac only
|
||||
*
|
||||
* Should do the following:
|
||||
* 1. Open a popup
|
||||
* 2. Add another tab to the popup (so that it gets stored) and close it again
|
||||
* 3. Open a window
|
||||
* 4. Check that nothing at all is restored
|
||||
* 5. Open two browser windows and close them again
|
||||
* 6. undoCloseWindow() one
|
||||
* 7. Open another browser window
|
||||
* 8. Check that nothing at all is restored
|
||||
*/
|
||||
add_task(function* test_open_close_only_popup() {
|
||||
if (IS_MAC) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 4: Open some popup window to check it isn't restored.
|
||||
* Instead nothing at all should be restored
|
||||
* @note: Non-Mac only
|
||||
*/
|
||||
function testOpenCloseOnlyPopup(nextFn) {
|
||||
// prepare the prefs
|
||||
setPrefs();
|
||||
yield setupTest({}, function*(newWin, obs) {
|
||||
// We actually don't care about the initial window in this test.
|
||||
yield BrowserTestUtils.closeWindow(newWin);
|
||||
|
||||
// This will cause nsSessionStore to restore a window the next time it
|
||||
// gets a chance.
|
||||
let popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
|
||||
popup.addEventListener("load", function() {
|
||||
this.removeEventListener("load", arguments.callee, true);
|
||||
is(popup.gBrowser.browsers.length, 1,
|
||||
"Did not restore the popup window (1)");
|
||||
popup.BrowserTryToCloseWindow();
|
||||
let popupPromise = BrowserTestUtils.waitForNewWindow();
|
||||
openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
|
||||
let popup = yield popupPromise;
|
||||
|
||||
// Real tests
|
||||
popup = openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
|
||||
popup.addEventListener("load", function() {
|
||||
popup.removeEventListener("load", arguments.callee, false);
|
||||
popup.gBrowser.addEventListener("load", function() {
|
||||
popup.gBrowser.removeEventListener("load", arguments.callee, true);
|
||||
popup.gBrowser.addTab(TEST_URLS[0]);
|
||||
is(popup.gBrowser.browsers.length, 1,
|
||||
"Did not restore the popup window (1)");
|
||||
|
||||
is(popup.gBrowser.browsers.length, 2,
|
||||
"Did not restore to the popup window (2)");
|
||||
let closed = yield closeWindowForRestoration(popup);
|
||||
ok(closed, "Should be able to close the window");
|
||||
|
||||
// Close the popup window
|
||||
// The test is successful when not this popup window is restored
|
||||
// but instead a new window is opened without restoring anything
|
||||
popup.close();
|
||||
popupPromise = BrowserTestUtils.waitForNewWindow();
|
||||
openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
|
||||
popup = yield popupPromise;
|
||||
|
||||
whenNewWindowLoaded({}, function (newWin) {
|
||||
isnot(newWin.gBrowser.browsers.length, 2,
|
||||
"Did not restore the popup window");
|
||||
is(TEST_URLS.indexOf(newWin.gBrowser.browsers[0].currentURI.spec), -1,
|
||||
"Did not restore the popup window (2)");
|
||||
popup.gBrowser.addTab(TEST_URLS[0]);
|
||||
is(popup.gBrowser.browsers.length, 2,
|
||||
"Did not restore to the popup window (2)");
|
||||
|
||||
// Cleanup
|
||||
newWin.close();
|
||||
yield BrowserTestUtils.closeWindow(popup);
|
||||
|
||||
// Next please
|
||||
executeSoon(nextFn);
|
||||
});
|
||||
}, true);
|
||||
}, false);
|
||||
}, true);
|
||||
newWin = yield promiseNewWindowLoaded();
|
||||
isnot(newWin.gBrowser.browsers.length, 2,
|
||||
"Did not restore the popup window");
|
||||
is(TEST_URLS.indexOf(newWin.gBrowser.browsers[0].currentURI.spec), -1,
|
||||
"Did not restore the popup window (2)");
|
||||
yield BrowserTestUtils.closeWindow(newWin);
|
||||
|
||||
// We closed one popup window with closeWindowForRestoration, and popup
|
||||
// windows should never fire the browser-lastwindow notifications.
|
||||
is(obs["browser-lastwindow-close-requested"], 0,
|
||||
"Got expected browser-lastwindow-close-requested notifications");
|
||||
is(obs["browser-lastwindow-close-granted"], 0,
|
||||
"Got expected browser-lastwindow-close-granted notifications");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Open some windows and do undoCloseWindow. This should prevent any
|
||||
* restoring later in the test
|
||||
*
|
||||
* @note: Non-Mac only
|
||||
*
|
||||
* Should do the following:
|
||||
* 1. Open two browser windows and close them again
|
||||
* 2. undoCloseWindow() one
|
||||
* 3. Open another browser window
|
||||
* 4. Make sure nothing at all is restored
|
||||
*/
|
||||
add_task(function* test_open_close_restore_from_popup() {
|
||||
if (IS_MAC) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 5: Open some windows and do undoCloseWindow. This should prevent any
|
||||
* restoring later in the test
|
||||
* @note: Non-Mac only
|
||||
*/
|
||||
function testOpenCloseRestoreFromPopup(nextFn) {
|
||||
setupTestAndRun(false, function(newWin) {
|
||||
setupTestAndRun(false, function(newWin2) {
|
||||
newWin.BrowserTryToCloseWindow();
|
||||
newWin2.BrowserTryToCloseWindow();
|
||||
yield setupTest({}, function*(newWin, obs) {
|
||||
let newWin2 = yield promiseNewWindowLoaded();
|
||||
yield injectTestTabs(newWin2);
|
||||
|
||||
browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup");
|
||||
let closed = yield closeWindowForRestoration(newWin);
|
||||
ok(closed, "Should be able to close the window");
|
||||
closed = yield closeWindowForRestoration(newWin2);
|
||||
ok(closed, "Should be able to close the window");
|
||||
|
||||
newWin = undoCloseWindow(0);
|
||||
newWin.addEventListener("load", function whenloaded() {
|
||||
newWin.removeEventListener("load", whenloaded, false);
|
||||
let counts = getBrowserWindowsCount();
|
||||
is(counts.open, 0, "Got right number of open windows");
|
||||
is(counts.winstates, 1, "Got right number of window states");
|
||||
|
||||
newWin.gBrowser.tabContainer.addEventListener("SSTabRestored", function whenSSTabRestored() {
|
||||
newWin.gBrowser.tabContainer.removeEventListener("SSTabRestored", whenSSTabRestored, false);
|
||||
newWin = undoCloseWindow(0);
|
||||
yield BrowserTestUtils.waitForEvent(newWin, "load");
|
||||
|
||||
whenNewWindowLoaded({}, function (newWin2) {
|
||||
is(newWin2.gBrowser.browsers.length, 1,
|
||||
"Did not restore, as undoCloseWindow() was last called");
|
||||
is(TEST_URLS.indexOf(newWin2.gBrowser.browsers[0].currentURI.spec), -1,
|
||||
"Did not restore, as undoCloseWindow() was last called (2)");
|
||||
// Make sure we wait until this window is restored.
|
||||
yield BrowserTestUtils.waitForEvent(newWin.gBrowser.tabContainer,
|
||||
"SSTabRestored");
|
||||
|
||||
browserWindowsCount([2, 3], "browser windows while running testOpenCloseRestoreFromPopup");
|
||||
newWin2 = yield promiseNewWindowLoaded();
|
||||
|
||||
// Cleanup
|
||||
newWin.close();
|
||||
newWin2.close();
|
||||
is(newWin2.gBrowser.browsers.length, 1,
|
||||
"Did not restore, as undoCloseWindow() was last called");
|
||||
is(TEST_URLS.indexOf(newWin2.gBrowser.browsers[0].currentURI.spec), -1,
|
||||
"Did not restore, as undoCloseWindow() was last called (2)");
|
||||
|
||||
browserWindowsCount([0, 1], "browser windows while running testOpenCloseRestoreFromPopup");
|
||||
counts = getBrowserWindowsCount();
|
||||
is(counts.open, 2, "Got right number of open windows");
|
||||
is(counts.winstates, 3, "Got right number of window states");
|
||||
|
||||
// Next please
|
||||
executeSoon(nextFn);
|
||||
});
|
||||
}, false);
|
||||
}, false);
|
||||
});
|
||||
});
|
||||
yield BrowserTestUtils.closeWindow(newWin);
|
||||
yield BrowserTestUtils.closeWindow(newWin2);
|
||||
|
||||
counts = getBrowserWindowsCount();
|
||||
is(counts.open, 0, "Got right number of open windows");
|
||||
is(counts.winstates, 1, "Got right number of window states");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test if closing can be denied on Mac.
|
||||
* @note: Mac only
|
||||
*/
|
||||
add_task(function* test_mac_notifications() {
|
||||
if (!IS_MAC) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 7: Check whether the right number of notifications was received during
|
||||
* the tests
|
||||
*/
|
||||
function testNotificationCount(nextFn) {
|
||||
is(observing["browser-lastwindow-close-requested"], NOTIFICATIONS_EXPECTED,
|
||||
"browser-lastwindow-close-requested notifications observed");
|
||||
yield setupTest({ denyFirst: true }, function*(newWin, obs) {
|
||||
let closed = yield closeWindowForRestoration(newWin);
|
||||
ok(!closed, "First close attempt should be denied");
|
||||
closed = yield closeWindowForRestoration(newWin);
|
||||
ok(closed, "Second close attempt should be granted");
|
||||
|
||||
// -request must be one more as we cancel the first one we hit,
|
||||
// and hence won't produce a corresponding -grant
|
||||
// @see observer.observe
|
||||
is(observing["browser-lastwindow-close-requested"],
|
||||
observing["browser-lastwindow-close-granted"] + 1,
|
||||
"Notification count for -request and -grant matches");
|
||||
// We tried closing once, and got denied. Then we tried again and
|
||||
// succeeded. That means 2 close requests, and 1 close granted.
|
||||
is(obs["browser-lastwindow-close-requested"], 2,
|
||||
"Got expected browser-lastwindow-close-requested notifications");
|
||||
is(obs["browser-lastwindow-close-granted"], 1,
|
||||
"Got expected browser-lastwindow-close-granted notifications");
|
||||
});
|
||||
});
|
||||
|
||||
executeSoon(nextFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 8: Test if closing can be denied on Mac
|
||||
* Futhermore prepares the testNotificationCount test (Test 7)
|
||||
* @note: Mac only
|
||||
*/
|
||||
function testMacNotifications(nextFn, iteration) {
|
||||
iteration = iteration || 1;
|
||||
setupTestAndRun(false, function(newWin) {
|
||||
// close the window
|
||||
// window.close doesn't push any close events,
|
||||
// so use BrowserTryToCloseWindow
|
||||
newWin.BrowserTryToCloseWindow();
|
||||
if (iteration == 1) {
|
||||
ok(!newWin.closed, "First close attempt denied");
|
||||
if (!newWin.closed) {
|
||||
newWin.BrowserTryToCloseWindow();
|
||||
ok(newWin.closed, "Second close attempt granted");
|
||||
}
|
||||
}
|
||||
|
||||
if (iteration < NOTIFICATIONS_EXPECTED - 1) {
|
||||
executeSoon(() => testMacNotifications(nextFn, ++iteration));
|
||||
}
|
||||
else {
|
||||
executeSoon(nextFn);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Execution starts here
|
||||
|
||||
setupTestsuite();
|
||||
if (navigator.platform.match(/Mac/)) {
|
||||
// Mac tests
|
||||
testMacNotifications(function () {
|
||||
testNotificationCount(function () {
|
||||
cleanupTestsuite();
|
||||
browserWindowsCount(1, "Only one browser window should be open eventually");
|
||||
finish();
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
// Non-Mac Tests
|
||||
testOpenCloseNormal(function () {
|
||||
browserWindowsCount([0, 1], "browser windows after testOpenCloseNormal");
|
||||
testOpenClosePrivateBrowsing(function () {
|
||||
browserWindowsCount([0, 1], "browser windows after testOpenClosePrivateBrowsing");
|
||||
testOpenCloseWindowAndPopup(function () {
|
||||
browserWindowsCount([0, 1], "browser windows after testOpenCloseWindowAndPopup");
|
||||
testOpenCloseOnlyPopup(function () {
|
||||
browserWindowsCount([0, 1], "browser windows after testOpenCloseOnlyPopup");
|
||||
testOpenCloseRestoreFromPopup(function () {
|
||||
browserWindowsCount([0, 1], "browser windows after testOpenCloseRestoreFromPopup");
|
||||
testNotificationCount(function () {
|
||||
cleanupTestsuite();
|
||||
browserWindowsCount(1, "browser windows after testNotificationCount");
|
||||
finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -32,10 +32,7 @@ function test() {
|
||||
|
||||
let browser = newWin.gBrowser.selectedBrowser;
|
||||
setInputChecked(browser, {id: "chk", checked: true}).then(() => {
|
||||
newWin.close();
|
||||
|
||||
// Now give it time to close
|
||||
executeSoon(function() {
|
||||
BrowserTestUtils.closeWindow(newWin).then(() => {
|
||||
is(ss.getClosedWindowCount(), 1,
|
||||
"The closed window was added to Recently Closed Windows");
|
||||
let data = JSON.parse(ss.getClosedWindowData())[0];
|
||||
@ -75,8 +72,7 @@ function test() {
|
||||
"The window correctly restored the data associated with it");
|
||||
|
||||
// clean up
|
||||
newWin2.close();
|
||||
finish();
|
||||
BrowserTestUtils.closeWindow(newWin2).then(finish);
|
||||
}, true);
|
||||
});
|
||||
});
|
||||
|
@ -1,64 +1,65 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/**
|
||||
* Test helper function that opens a series of windows, closes them
|
||||
* and then checks the closed window data from SessionStore against
|
||||
* expected results.
|
||||
*
|
||||
* @param windowsToOpen (Array)
|
||||
* An array of Objects, where each object must define a single
|
||||
* property "isPopup" for whether or not the opened window should
|
||||
* be a popup.
|
||||
* @param expectedResults (Array)
|
||||
* An Object with two properies: mac and other, where each points
|
||||
* at yet another Object, with the following properties:
|
||||
*
|
||||
* popup (int):
|
||||
* The number of popup windows we expect to be in the closed window
|
||||
* data.
|
||||
* normal (int):
|
||||
* The number of normal windows we expect to be in the closed window
|
||||
* data.
|
||||
* @returns Promise
|
||||
*/
|
||||
function testWindows(windowsToOpen, expectedResults) {
|
||||
return Task.spawn(function*() {
|
||||
for (let winData of windowsToOpen) {
|
||||
let features = "chrome,dialog=no," +
|
||||
(winData.isPopup ? "all=no" : "all");
|
||||
let url = "http://example.com/?window=" + windowsToOpen.length;
|
||||
|
||||
function test() {
|
||||
let openWindowPromise = BrowserTestUtils.waitForNewWindow();
|
||||
openDialog(getBrowserURL(), "", features, url);
|
||||
let win = yield openWindowPromise;
|
||||
yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
|
||||
|
||||
if (win.gMultiProcessBrowser) {
|
||||
let tab = win.gBrowser.selectedTab;
|
||||
yield promiseTabRestored(tab);
|
||||
}
|
||||
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
}
|
||||
|
||||
let closedWindowData = JSON.parse(ss.getClosedWindowData());
|
||||
let numPopups = closedWindowData.filter(function(el, i, arr) {
|
||||
return el.isPopup;
|
||||
}).length;
|
||||
let numNormal = ss.getClosedWindowCount() - numPopups;
|
||||
// #ifdef doesn't work in browser-chrome tests, so do a simple regex on platform
|
||||
let oResults = navigator.platform.match(/Mac/) ? expectedResults.mac
|
||||
: expectedResults.other;
|
||||
is(numPopups, oResults.popup,
|
||||
"There were " + oResults.popup + " popup windows to reopen");
|
||||
is(numNormal, oResults.normal,
|
||||
"There were " + oResults.normal + " normal windows to repoen");
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* test_closed_window_states() {
|
||||
// This test takes quite some time, and timeouts frequently, so we require
|
||||
// more time to run.
|
||||
// See Bug 518970.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
// helper function that does the actual testing
|
||||
function openWindowRec(windowsToOpen, expectedResults, recCallback) {
|
||||
// do actual checking
|
||||
if (!windowsToOpen.length) {
|
||||
let closedWindowData = JSON.parse(ss.getClosedWindowData());
|
||||
let numPopups = closedWindowData.filter(function(el, i, arr) {
|
||||
return el.isPopup;
|
||||
}).length;
|
||||
let numNormal = ss.getClosedWindowCount() - numPopups;
|
||||
// #ifdef doesn't work in browser-chrome tests, so do a simple regex on platform
|
||||
let oResults = navigator.platform.match(/Mac/) ? expectedResults.mac
|
||||
: expectedResults.other;
|
||||
is(numPopups, oResults.popup,
|
||||
"There were " + oResults.popup + " popup windows to repoen");
|
||||
is(numNormal, oResults.normal,
|
||||
"There were " + oResults.normal + " normal windows to repoen");
|
||||
|
||||
// cleanup & return
|
||||
executeSoon(recCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
// hack to force window to be considered a popup (toolbar=no didn't work)
|
||||
let winData = windowsToOpen.shift();
|
||||
let settings = "chrome,dialog=no," +
|
||||
(winData.isPopup ? "all=no" : "all");
|
||||
let url = "http://example.com/?window=" + windowsToOpen.length;
|
||||
|
||||
provideWindow(function onTestURLLoaded(win) {
|
||||
let tabReady = () => {
|
||||
win.close();
|
||||
// Give it time to close
|
||||
executeSoon(function() {
|
||||
openWindowRec(windowsToOpen, expectedResults, recCallback);
|
||||
});
|
||||
};
|
||||
|
||||
if (win.gMultiProcessBrowser) {
|
||||
let tab = win.gBrowser.selectedTab;
|
||||
tab.addEventListener("SSTabRestored", function onTabRestored() {
|
||||
tab.removeEventListener("SSTabRestored", onTabRestored);
|
||||
tabReady();
|
||||
});
|
||||
} else {
|
||||
tabReady();
|
||||
}
|
||||
}, url, settings);
|
||||
}
|
||||
|
||||
let windowsToOpen = [{isPopup: false},
|
||||
{isPopup: false},
|
||||
{isPopup: true},
|
||||
@ -66,6 +67,10 @@ function test() {
|
||||
{isPopup: true}];
|
||||
let expectedResults = {mac: {popup: 3, normal: 0},
|
||||
other: {popup: 3, normal: 1}};
|
||||
|
||||
yield testWindows(windowsToOpen, expectedResults);
|
||||
|
||||
|
||||
let windowsToOpen2 = [{isPopup: false},
|
||||
{isPopup: false},
|
||||
{isPopup: false},
|
||||
@ -73,7 +78,6 @@ function test() {
|
||||
{isPopup: false}];
|
||||
let expectedResults2 = {mac: {popup: 0, normal: 3},
|
||||
other: {popup: 0, normal: 3}};
|
||||
openWindowRec(windowsToOpen, expectedResults, function() {
|
||||
openWindowRec(windowsToOpen2, expectedResults2, finish);
|
||||
});
|
||||
}
|
||||
|
||||
yield testWindows(windowsToOpen2, expectedResults2);
|
||||
});
|
@ -15,25 +15,27 @@ const TESTS = [
|
||||
|
||||
function promiseTestOpenCloseWindow(aIsPrivate, aTest) {
|
||||
return Task.spawn(function*() {
|
||||
let win = yield promiseNewWindowLoaded({ "private": aIsPrivate });
|
||||
let win = yield BrowserTestUtils.openNewBrowserWindow({ "private": aIsPrivate });
|
||||
win.gBrowser.selectedBrowser.loadURI(aTest.url);
|
||||
yield promiseBrowserLoaded(win.gBrowser.selectedBrowser);
|
||||
yield Promise.resolve();
|
||||
// Mark the window with some unique data to be restored later on.
|
||||
ss.setWindowValue(win, aTest.key, aTest.value);
|
||||
yield TabStateFlusher.flushWindow(win);
|
||||
// Close.
|
||||
yield promiseWindowClosed(win);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
}
|
||||
|
||||
function promiseTestOnWindow(aIsPrivate, aValue) {
|
||||
return Task.spawn(function*() {
|
||||
let win = yield promiseNewWindowLoaded({ "private": aIsPrivate });
|
||||
let win = yield BrowserTestUtils.openNewBrowserWindow({ "private": aIsPrivate });
|
||||
yield TabStateFlusher.flushWindow(win);
|
||||
let data = JSON.parse(ss.getClosedWindowData())[0];
|
||||
is(ss.getClosedWindowCount(), 1, "Check that the closed window count hasn't changed");
|
||||
ok(JSON.stringify(data).indexOf(aValue) > -1,
|
||||
"Check the closed window data was stored correctly");
|
||||
registerCleanupFunction(() => promiseWindowClosed(win));
|
||||
registerCleanupFunction(() => BrowserTestUtils.closeWindow(win));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -66,8 +66,7 @@ function test() {
|
||||
if (gPrefService.prefHasUserValue("browser.sessionstore.interval"))
|
||||
gPrefService.clearUserPref("browser.sessionstore.interval");
|
||||
cs.removeAll();
|
||||
newWin.close();
|
||||
finish();
|
||||
BrowserTestUtils.closeWindow(newWin).then(finish);
|
||||
};
|
||||
|
||||
function flushAndReady() {
|
||||
|
@ -67,8 +67,7 @@ function test() {
|
||||
"... and tabs not specifically forgetten weren't.");
|
||||
|
||||
// clean up
|
||||
newWin.close();
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
|
||||
finish();
|
||||
BrowserTestUtils.closeWindow(newWin).then(finish);
|
||||
});
|
||||
}
|
||||
|
@ -69,18 +69,17 @@ function test() {
|
||||
|
||||
ForgetAboutSite.removeDataFromDomain("example.net");
|
||||
waitForClearHistory(function() {
|
||||
closedTabs = JSON.parse(ss.getClosedTabData(newWin));
|
||||
is(closedTabs.length, remember_count,
|
||||
"The correct amout of tabs was removed");
|
||||
is(countByTitle(closedTabs, FORGET), 0,
|
||||
"All tabs to be forgotten were indeed removed");
|
||||
is(countByTitle(closedTabs, REMEMBER), remember_count,
|
||||
"... and tabs to be remembered weren't.");
|
||||
closedTabs = JSON.parse(ss.getClosedTabData(newWin));
|
||||
is(closedTabs.length, remember_count,
|
||||
"The correct amout of tabs was removed");
|
||||
is(countByTitle(closedTabs, FORGET), 0,
|
||||
"All tabs to be forgotten were indeed removed");
|
||||
is(countByTitle(closedTabs, REMEMBER), remember_count,
|
||||
"... and tabs to be remembered weren't.");
|
||||
|
||||
// clean up
|
||||
newWin.close();
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
|
||||
finish();
|
||||
// clean up
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
|
||||
BrowserTestUtils.closeWindow(newWin).then(finish);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ function test() {
|
||||
"window value was correctly overwritten");
|
||||
|
||||
// clean up
|
||||
newWin.close();
|
||||
finish();
|
||||
BrowserTestUtils.closeWindow(newWin).then(finish);
|
||||
});
|
||||
}
|
||||
|
@ -52,8 +52,7 @@ function test() {
|
||||
isnot(newWin.windowState, newWin.STATE_MAXIMIZED,
|
||||
"the window was explicitly unmaximized");
|
||||
|
||||
newWin.close();
|
||||
finish();
|
||||
BrowserTestUtils.closeWindow(newWin).then(finish);
|
||||
}, 0);
|
||||
}, 0);
|
||||
}, 0);
|
||||
|
@ -2,122 +2,64 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function test() {
|
||||
/** Test for Bug 490040 **/
|
||||
// Only windows with open tabs are restorable. Windows where a lone tab is
|
||||
// detached may have _closedTabs, but is left with just an empty tab.
|
||||
const STATES = [{
|
||||
shouldBeAdded: true,
|
||||
windowState: {
|
||||
windows: [{
|
||||
tabs: [{ entries: [{ url: "http://example.com", title: "example.com" }] }],
|
||||
selected: 1,
|
||||
_closedTabs: []
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
shouldBeAdded: false,
|
||||
windowState: {
|
||||
windows: [{
|
||||
tabs: [{ entries: [] }],
|
||||
_closedTabs: []
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
shouldBeAdded: false,
|
||||
windowState: {
|
||||
windows: [{
|
||||
tabs: [{ entries: [] }],
|
||||
_closedTabs: [{ state: { entries: [{ url: "http://example.com", index: 1 }] } }]
|
||||
}]
|
||||
}
|
||||
}, {
|
||||
shouldBeAdded: false,
|
||||
windowState: {
|
||||
windows: [{
|
||||
tabs: [{ entries: [] }],
|
||||
_closedTabs: [],
|
||||
extData: { keyname: "pi != " + Math.random() }
|
||||
}]
|
||||
}
|
||||
}];
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
function testWithState(aState) {
|
||||
add_task(function* test_bug_490040() {
|
||||
for (let state of STATES) {
|
||||
// Ensure we can store the window if needed.
|
||||
let startingClosedWindowCount = ss.getClosedWindowCount();
|
||||
yield pushPrefs(["browser.sessionstore.max_windows_undo",
|
||||
startingClosedWindowCount + 1]);
|
||||
|
||||
let curClosedWindowCount = ss.getClosedWindowCount();
|
||||
gPrefService.setIntPref("browser.sessionstore.max_windows_undo",
|
||||
curClosedWindowCount + 1);
|
||||
let win = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
|
||||
var origWin;
|
||||
function windowObserver(aSubject, aTopic, aData) {
|
||||
let theWin = aSubject.QueryInterface(Ci.nsIDOMWindow);
|
||||
if (origWin && theWin != origWin)
|
||||
return;
|
||||
|
||||
switch (aTopic) {
|
||||
case "domwindowopened":
|
||||
origWin = theWin;
|
||||
theWin.addEventListener("load", function () {
|
||||
theWin.removeEventListener("load", arguments.callee, false);
|
||||
executeSoon(function () {
|
||||
// Close the window as soon as the first tab loads, or
|
||||
// immediately if there are no tabs.
|
||||
if (aState.windowState.windows[0].tabs[0].entries.length) {
|
||||
theWin.gBrowser.addEventListener("load", function() {
|
||||
theWin.gBrowser.removeEventListener("load",
|
||||
arguments.callee, true);
|
||||
theWin.close();
|
||||
}, true);
|
||||
} else {
|
||||
executeSoon(function () {
|
||||
theWin.close();
|
||||
});
|
||||
}
|
||||
ss.setWindowState(theWin, JSON.stringify(aState.windowState),
|
||||
true);
|
||||
});
|
||||
}, false);
|
||||
break;
|
||||
|
||||
case "domwindowclosed":
|
||||
Services.ww.unregisterNotification(windowObserver);
|
||||
// Use executeSoon to ensure this happens after SS observer.
|
||||
executeSoon(function () {
|
||||
is(ss.getClosedWindowCount(),
|
||||
curClosedWindowCount + (aState.shouldBeAdded ? 1 : 0),
|
||||
"That window should " + (aState.shouldBeAdded ? "" : "not ") +
|
||||
"be restorable");
|
||||
executeSoon(runNextTest);
|
||||
});
|
||||
break;
|
||||
}
|
||||
ss.setWindowState(win, JSON.stringify(state.windowState), true);
|
||||
if (state.windowState.windows[0].tabs.length) {
|
||||
yield BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
|
||||
}
|
||||
Services.ww.registerNotification(windowObserver);
|
||||
Services.ww.openWindow(null,
|
||||
location,
|
||||
"_blank",
|
||||
"chrome,all,dialog=no",
|
||||
null);
|
||||
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
|
||||
is(ss.getClosedWindowCount(),
|
||||
curClosedWindowCount + (state.shouldBeAdded ? 1 : 0),
|
||||
"That window should " + (state.shouldBeAdded ? "" : "not ") +
|
||||
"be restorable");
|
||||
}
|
||||
|
||||
// Only windows with open tabs are restorable. Windows where a lone tab is
|
||||
// detached may have _closedTabs, but is left with just an empty tab.
|
||||
let states = [
|
||||
{
|
||||
shouldBeAdded: true,
|
||||
windowState: {
|
||||
windows: [{
|
||||
tabs: [{ entries: [{ url: "http://example.com", title: "example.com" }] }],
|
||||
selected: 1,
|
||||
_closedTabs: []
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
shouldBeAdded: false,
|
||||
windowState: {
|
||||
windows: [{
|
||||
tabs: [{ entries: [] }],
|
||||
_closedTabs: []
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
shouldBeAdded: false,
|
||||
windowState: {
|
||||
windows: [{
|
||||
tabs: [{ entries: [] }],
|
||||
_closedTabs: [{ state: { entries: [{ url: "http://example.com", index: 1 }] } }]
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
shouldBeAdded: false,
|
||||
windowState: {
|
||||
windows: [{
|
||||
tabs: [{ entries: [] }],
|
||||
_closedTabs: [],
|
||||
extData: { keyname: "pi != " + Math.random() }
|
||||
}]
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
function runNextTest() {
|
||||
if (states.length) {
|
||||
let state = states.shift();
|
||||
testWithState(state);
|
||||
}
|
||||
else {
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_windows_undo");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -114,8 +114,7 @@ function test() {
|
||||
"... and windows not specifically forgetten weren't.");
|
||||
|
||||
// clean up
|
||||
newWin.close();
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_windows_undo");
|
||||
finish();
|
||||
BrowserTestUtils.closeWindow(newWin).then(finish);
|
||||
});
|
||||
}
|
||||
|
@ -10,35 +10,36 @@ function test() {
|
||||
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no,toolbar=yes");
|
||||
promiseWindowLoaded(newWin).then(() => {
|
||||
let state1 = ss.getWindowState(newWin);
|
||||
newWin.close();
|
||||
BrowserTestUtils.closeWindow(newWin).then(() => {
|
||||
|
||||
newWin = openDialog(location, "_blank",
|
||||
"chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar=no,location,personal,directories,dialog=no");
|
||||
promiseWindowLoaded(newWin).then(() => {
|
||||
let state2 = ss.getWindowState(newWin);
|
||||
newWin.close();
|
||||
newWin = openDialog(location, "_blank",
|
||||
"chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar=no,location,personal,directories,dialog=no");
|
||||
promiseWindowLoaded(newWin).then(() => {
|
||||
let state2 = ss.getWindowState(newWin);
|
||||
|
||||
function testState(state, expected, callback) {
|
||||
let win = openDialog(location, "_blank", "chrome,all,dialog=no");
|
||||
promiseWindowLoaded(win).then(() => {
|
||||
function testState(state, expected, callback) {
|
||||
let win = openDialog(location, "_blank", "chrome,all,dialog=no");
|
||||
promiseWindowLoaded(win).then(() => {
|
||||
|
||||
is(win.gURLBar.readOnly, false,
|
||||
"URL bar should not be read-only before setting the state");
|
||||
is(win.gURLBar.getAttribute("enablehistory"), "true",
|
||||
"URL bar autocomplete should be enabled before setting the state");
|
||||
ss.setWindowState(win, state, true);
|
||||
is(win.gURLBar.readOnly, expected.readOnly,
|
||||
"URL bar read-only state should be restored correctly");
|
||||
is(win.gURLBar.getAttribute("enablehistory"), expected.enablehistory,
|
||||
"URL bar autocomplete state should be restored correctly");
|
||||
is(win.gURLBar.readOnly, false,
|
||||
"URL bar should not be read-only before setting the state");
|
||||
is(win.gURLBar.getAttribute("enablehistory"), "true",
|
||||
"URL bar autocomplete should be enabled before setting the state");
|
||||
ss.setWindowState(win, state, true);
|
||||
is(win.gURLBar.readOnly, expected.readOnly,
|
||||
"URL bar read-only state should be restored correctly");
|
||||
is(win.gURLBar.getAttribute("enablehistory"), expected.enablehistory,
|
||||
"URL bar autocomplete state should be restored correctly");
|
||||
|
||||
win.close();
|
||||
executeSoon(callback);
|
||||
BrowserTestUtils.closeWindow(win).then(callback);
|
||||
});
|
||||
}
|
||||
|
||||
BrowserTestUtils.closeWindow(newWin).then(() => {
|
||||
testState(state1, {readOnly: false, enablehistory: "true"}, function() {
|
||||
testState(state2, {readOnly: true, enablehistory: "false"}, finish);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
testState(state1, {readOnly: false, enablehistory: "true"}, function() {
|
||||
testState(state2, {readOnly: true, enablehistory: "false"}, finish);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -31,8 +31,7 @@ function test() {
|
||||
gotError = true;
|
||||
}
|
||||
ok(!gotError, "Didn't get a malformed URI error.");
|
||||
theWin.close();
|
||||
finish();
|
||||
BrowserTestUtils.closeWindow(theWin).then(finish);
|
||||
});
|
||||
}, false);
|
||||
}
|
||||
|
@ -35,8 +35,7 @@ function test() {
|
||||
|
||||
// Cleanup
|
||||
window.restore();
|
||||
window_B.close();
|
||||
finish();
|
||||
BrowserTestUtils.closeWindow(window_B).then(finish);
|
||||
});
|
||||
}, window_B);
|
||||
});
|
||||
|
@ -11,16 +11,11 @@ function browserWindowsCount(expected) {
|
||||
"number of open browser windows according to getBrowserState");
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
add_task(function() {
|
||||
browserWindowsCount(1);
|
||||
|
||||
var win = openDialog(location, "", "chrome,all,dialog=no");
|
||||
promiseWindowLoaded(win).then(() => {
|
||||
browserWindowsCount(2);
|
||||
win.close();
|
||||
browserWindowsCount(1);
|
||||
finish();
|
||||
});
|
||||
}
|
||||
let win = yield BrowserTestUtils.openNewBrowserWindow();
|
||||
browserWindowsCount(2);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
browserWindowsCount(1);
|
||||
});
|
||||
|
@ -22,10 +22,17 @@ function test() {
|
||||
function closeFirstWin(win) {
|
||||
win.gBrowser.pinTab(win.gBrowser.tabs[0]);
|
||||
win.gBrowser.pinTab(win.gBrowser.tabs[1]);
|
||||
|
||||
let winClosed = BrowserTestUtils.windowClosed(win);
|
||||
// We need to call BrowserTryToCloseWindow in order to trigger
|
||||
// the machinery that chooses whether or not to save the session
|
||||
// for the last window.
|
||||
win.BrowserTryToCloseWindow();
|
||||
ok(win.closed, "window closed");
|
||||
|
||||
openWinWithCb(checkSecondWin, URIS_NORMAL_B, URIS_PINNED.concat(URIS_NORMAL_B));
|
||||
winClosed.then(() => {
|
||||
openWinWithCb(checkSecondWin, URIS_NORMAL_B, URIS_PINNED.concat(URIS_NORMAL_B));
|
||||
});
|
||||
}
|
||||
|
||||
function checkSecondWin(win) {
|
||||
@ -33,11 +40,12 @@ function checkSecondWin(win) {
|
||||
is(win.gBrowser.browsers[1].currentURI.spec, URIS_PINNED[1], "second pinned tab restored");
|
||||
ok(win.gBrowser.tabs[0].pinned, "first pinned tab is still pinned");
|
||||
ok(win.gBrowser.tabs[1].pinned, "second pinned tab is still pinned");
|
||||
win.close();
|
||||
|
||||
// cleanup
|
||||
document.documentElement.setAttribute("windowtype", "navigator:browser");
|
||||
finish();
|
||||
BrowserTestUtils.closeWindow(win).then(() => {
|
||||
// cleanup
|
||||
document.documentElement.setAttribute("windowtype", "navigator:browser");
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
function openWinWithCb(cb, argURIs, expectedURIs) {
|
||||
|
@ -10,7 +10,7 @@ function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithState(state, function (win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
registerCleanupFunction(() => BrowserTestUtils.closeWindow(win));
|
||||
|
||||
is(win.gBrowser.tabs.length, 2, "two tabs were restored");
|
||||
is(win.gBrowser.visibleTabs.length, 1, "one tab is visible");
|
||||
|
@ -234,8 +234,9 @@ function onWindowUnloaded() {
|
||||
|
||||
function afterTestCleanup(aNewWin) {
|
||||
executeSoon(function() {
|
||||
aNewWin.close();
|
||||
document.documentElement.setAttribute("windowtype", originalWindowType);
|
||||
runNextTestOrFinish();
|
||||
BrowserTestUtils.closeWindow(aNewWin).then(() => {
|
||||
document.documentElement.setAttribute("windowtype", originalWindowType);
|
||||
runNextTestOrFinish();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
newWindowWithState(state, function (win) {
|
||||
registerCleanupFunction(() => win.close());
|
||||
registerCleanupFunction(() => BrowserTestUtils.closeWindow(win));
|
||||
|
||||
is(gBrowser.tabs.length, 1, "The total number of tabs should be 1");
|
||||
is(gBrowser.visibleTabs.length, 1, "The total number of visible tabs should be 1");
|
||||
|
@ -101,7 +101,7 @@ function newWindowWithState(state, callback) {
|
||||
let opts = "chrome,all,dialog=no,height=800,width=800";
|
||||
let win = window.openDialog(getBrowserURL(), "_blank", opts);
|
||||
|
||||
registerCleanupFunction(() => win.close());
|
||||
registerCleanupFunction(() => BrowserTestUtils.closeWindow(win));
|
||||
|
||||
whenWindowLoaded(win, function onWindowLoaded(aWin) {
|
||||
TabsProgressListener.init(aWin);
|
||||
|
@ -19,16 +19,16 @@ add_task(function test_close_last_nonpopup_window() {
|
||||
ss.setWindowState(window, JSON.stringify(popupState), true);
|
||||
|
||||
// Open a new window with a tab.
|
||||
let win = yield promiseNewWindowLoaded({private: false});
|
||||
let win = yield BrowserTestUtils.openNewBrowserWindow({private: false});
|
||||
let tab = win.gBrowser.addTab("http://example.com/");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
|
||||
// Make sure sessionstore sees this window.
|
||||
let state = JSON.parse(ss.getBrowserState());
|
||||
is(state.windows.length, 2, "sessionstore knows about this window");
|
||||
|
||||
// Closed the window and check the closed window count.
|
||||
yield promiseWindowClosed(win);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
is(ss.getClosedWindowCount(), 1, "correct closed window count");
|
||||
|
||||
// Cleanup.
|
||||
|
@ -66,13 +66,16 @@ function done() {
|
||||
// Enumerate windows and close everything but our primary window. We can't
|
||||
// use waitForFocus() because apparently it's buggy. See bug 599253.
|
||||
let windowsEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
let closeWinPromises = [];
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
let currentWindow = windowsEnum.getNext();
|
||||
if (currentWindow != window)
|
||||
currentWindow.close();
|
||||
closeWinPromises.push(BrowserTestUtils.closeWindow(currentWindow));
|
||||
}
|
||||
|
||||
waitForBrowserState(stateBackup, finish);
|
||||
Promise.all(closeWinPromises).then(() => {
|
||||
waitForBrowserState(stateBackup, finish);
|
||||
});
|
||||
}
|
||||
|
||||
// Count up the number of tabs in the state data
|
||||
|
@ -61,15 +61,15 @@ function runNextTest() {
|
||||
// Enumerate windows and close everything but our primary window. We can't
|
||||
// use waitForFocus() because apparently it's buggy. See bug 599253.
|
||||
var windowsEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
let closeWinPromises = [];
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
var currentWindow = windowsEnum.getNext();
|
||||
if (currentWindow != window) {
|
||||
currentWindow.close();
|
||||
closeWinPromises.push(BrowserTestUtils.closeWindow(currentWindow));
|
||||
}
|
||||
}
|
||||
|
||||
// If we closed a window, give it time to close
|
||||
executeSoon(function() {
|
||||
Promise.all(closeWinPromises).then(() => {
|
||||
let currentTest = tests.shift();
|
||||
info("prepping for " + currentTest.name);
|
||||
waitForBrowserState(testState, currentTest);
|
||||
@ -319,9 +319,8 @@ function test_undoCloseWindow() {
|
||||
|
||||
waitForBrowserState(lameMultiWindowState, function() {
|
||||
// Close the window which isn't window
|
||||
newWindow.close();
|
||||
// Now give it time to close
|
||||
executeSoon(function() {
|
||||
BrowserTestUtils.closeWindow(newWindow).then(() => {
|
||||
// Now give it time to close
|
||||
reopenedWindow = ss.undoCloseWindow(0);
|
||||
reopenedWindow.addEventListener("SSWindowStateBusy", onSSWindowStateBusy, false);
|
||||
reopenedWindow.addEventListener("SSWindowStateReady", onSSWindowStateReady, false);
|
||||
@ -357,9 +356,6 @@ function test_undoCloseWindow() {
|
||||
reopenedWindow.removeEventListener("SSWindowStateReady", onSSWindowStateReady, false);
|
||||
reopenedWindow.gBrowser.tabContainer.removeEventListener("SSTabRestored", onSSTabRestored, false);
|
||||
|
||||
reopenedWindow.close();
|
||||
|
||||
// Give it time to close
|
||||
executeSoon(runNextTest);
|
||||
BrowserTestUtils.closeWindow(reopenedWindow).then(runNextTest);
|
||||
}
|
||||
}
|
||||
|
@ -27,16 +27,19 @@ function runNextTest() {
|
||||
// Enumerate windows and close everything but our primary window. We can't
|
||||
// use waitForFocus() because apparently it's buggy. See bug 599253.
|
||||
var windowsEnum = Services.wm.getEnumerator("navigator:browser");
|
||||
let closeWinPromises = [];
|
||||
while (windowsEnum.hasMoreElements()) {
|
||||
var currentWindow = windowsEnum.getNext();
|
||||
if (currentWindow != window) {
|
||||
currentWindow.close();
|
||||
closeWinPromises.push(BrowserTestUtils.closeWindow(currentWindow));
|
||||
}
|
||||
}
|
||||
|
||||
let currentTest = tests.shift();
|
||||
info("running " + currentTest.name);
|
||||
waitForBrowserState(testState, currentTest);
|
||||
Promise.all(closeWinPromises).then(() => {
|
||||
let currentTest = tests.shift();
|
||||
info("running " + currentTest.name);
|
||||
waitForBrowserState(testState, currentTest);
|
||||
});
|
||||
}
|
||||
else {
|
||||
ss.setBrowserState(stateBackup);
|
||||
|
@ -31,7 +31,7 @@ add_task(function* new_window() {
|
||||
// Double check that we have no closed windows
|
||||
is(ss.getClosedWindowCount(), 0, "no closed windows on first save");
|
||||
|
||||
yield promiseWindowClosed(newWin);
|
||||
yield BrowserTestUtils.closeWindow(newWin);
|
||||
newWin = null;
|
||||
|
||||
let state = JSON.parse((yield promiseRecoveryFileContents()));
|
||||
@ -45,7 +45,7 @@ add_task(function* new_window() {
|
||||
"observe1: 1 closed window according to API");
|
||||
} finally {
|
||||
if (newWin) {
|
||||
yield promiseWindowClosed(newWin);
|
||||
yield BrowserTestUtils.closeWindow(newWin);
|
||||
}
|
||||
yield forceSaveState();
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ add_task(function* test() {
|
||||
checkWindows();
|
||||
|
||||
// Cleanup.
|
||||
yield promiseWindowClosed(win);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
yield promiseBrowserState(backupState);
|
||||
});
|
||||
|
||||
|
@ -47,8 +47,9 @@ function windowObserver(aSubject, aTopic, aData) {
|
||||
win.Scratchpad.removeObserver(this);
|
||||
|
||||
let state = win.Scratchpad.getState();
|
||||
win.close();
|
||||
addState(state);
|
||||
BrowserTestUtils.closeWindow(win).then(() => {
|
||||
addState(state);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -14,10 +14,11 @@ function test() {
|
||||
win.addEventListener("SSWindowClosing", function onWindowClosing() {
|
||||
win.removeEventListener("SSWindowClosing", onWindowClosing, false);
|
||||
eventReceived = true;
|
||||
waitForFocus(finish);
|
||||
}, false);
|
||||
|
||||
win.close();
|
||||
BrowserTestUtils.closeWindow(win).then(() => {
|
||||
waitForFocus(finish);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ function newWindowWithState(aState, aCallback) {
|
||||
let opts = "chrome,all,dialog=no,height=800,width=800";
|
||||
let win = window.openDialog(getBrowserURL(), "_blank", opts);
|
||||
|
||||
registerCleanupFunction(() => win.close());
|
||||
registerCleanupFunction(() => BrowserTestUtils.closeWindow(win));
|
||||
|
||||
whenWindowLoaded(win, function onWindowLoaded(aWin) {
|
||||
ss.setWindowState(aWin, JSON.stringify(aState), true);
|
||||
|
@ -82,7 +82,7 @@ add_task(function* test_3() {
|
||||
is(curState.windows[2].isPrivate, true, "Window 2 is private");
|
||||
is(curState.selectedWindow, 4, "Last window opened is the one selected");
|
||||
|
||||
yield promiseWindowClosed(normalWindow);
|
||||
yield BrowserTestUtils.closeWindow(normalWindow);
|
||||
|
||||
// Pin and unpin a tab before checking the written state so that
|
||||
// the list of restoring windows gets cleared. Otherwise the
|
||||
|
@ -30,7 +30,7 @@ add_task(function* () {
|
||||
|
||||
// Wait until the new window was restored.
|
||||
let win = yield waitForNewWindow();
|
||||
yield promiseWindowClosed(win);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
|
||||
let [{tabs: [{entries: [{url}]}]}] = JSON.parse(ss.getClosedWindowData());
|
||||
is(url, "about:mozilla", "session was restored correctly");
|
||||
|
@ -74,7 +74,7 @@ add_task(function flush_on_windowclose() {
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
yield modifySessionStorage(browser, {test: "on-window-close"});
|
||||
yield closeWindow(win);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
|
||||
let [{tabs: [_, {storage}]}] = JSON.parse(ss.getClosedWindowData());
|
||||
is(storage["http://example.com"].test, "on-window-close",
|
||||
@ -138,24 +138,6 @@ function promiseNewWindow() {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function closeWindow(win) {
|
||||
let deferred = Promise.defer();
|
||||
let outerID = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
|
||||
Services.obs.addObserver(function obs(subject, topic) {
|
||||
let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||
if (id == outerID) {
|
||||
Services.obs.removeObserver(obs, topic);
|
||||
deferred.resolve();
|
||||
}
|
||||
}, "outer-window-destroyed", false);
|
||||
|
||||
win.close();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function createTabWithStorageData(urls, win = window) {
|
||||
return Task.spawn(function task() {
|
||||
let tab = win.gBrowser.addTab();
|
||||
|
@ -55,7 +55,8 @@ add_task(function* test_open_and_close() {
|
||||
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
|
||||
|
||||
yield TabStateFlusher.flushWindow(window);
|
||||
yield TabStateFlusher.flushWindow(newWin);
|
||||
|
||||
info("1. Making sure that before closing, we don't have closedAt");
|
||||
// For the moment, no "closedAt"
|
||||
@ -65,12 +66,10 @@ add_task(function* test_open_and_close() {
|
||||
is(state.windows[0].tabs[0].closedAt || false, false, "1. First tab doesn't have closedAt");
|
||||
is(state.windows[0].tabs[1].closedAt || false, false, "1. Second tab doesn't have closedAt");
|
||||
|
||||
|
||||
|
||||
info("2. Making sure that after closing, we have closedAt");
|
||||
|
||||
// Now close stuff, this should add closeAt
|
||||
yield promiseWindowClosed(newWin);
|
||||
yield BrowserTestUtils.closeWindow(newWin);
|
||||
yield promiseRemoveTab(newTab1);
|
||||
yield promiseRemoveTab(newTab2);
|
||||
|
||||
@ -103,7 +102,7 @@ add_task(function* test_restore() {
|
||||
is(state.windows[0].tabs[0].closedAt || false, false, "3. First tab doesn't have closedAt");
|
||||
is(state.windows[0].tabs[1].closedAt || false, false, "3. Second tab doesn't have closedAt");
|
||||
|
||||
yield promiseWindowClosed(newWin);
|
||||
yield BrowserTestUtils.closeWindow(newWin);
|
||||
gBrowser.removeTab(newTab1);
|
||||
gBrowser.removeTab(newTab2);
|
||||
});
|
||||
|
@ -31,7 +31,7 @@ add_task(function* test() {
|
||||
let closedTabData = ss.getClosedTabData(win);
|
||||
|
||||
// Close our window.
|
||||
yield promiseWindowClosed(win);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
|
||||
// SessionStore should no longer track our window
|
||||
// but it should still report the same state.
|
||||
|
@ -65,7 +65,7 @@ add_task(function () {
|
||||
|
||||
// Clean up.
|
||||
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
|
||||
win.close();
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
||||
|
||||
|
@ -97,7 +97,7 @@ add_task(function () {
|
||||
|
||||
// Check that all private tabs are removed when the non-private
|
||||
// window is closed and we don't save windows without any tabs.
|
||||
yield promiseWindowClosed(win);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
is(ss.getClosedWindowCount(), 0, "no windows to restore");
|
||||
});
|
||||
|
||||
@ -123,7 +123,7 @@ add_task(function () {
|
||||
is(ss.getClosedTabCount(win), 1, "there is a single tab to restore");
|
||||
|
||||
// Ensure that closed private windows can never be restored.
|
||||
yield promiseWindowClosed(win);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
is(ss.getClosedWindowCount(), 0, "no windows to restore");
|
||||
});
|
||||
|
||||
|
@ -14,7 +14,7 @@ add_task(function* () {
|
||||
let browser = win.gBrowser.browsers[1];
|
||||
is(browser.currentURI.spec, "about:mozilla", "tab was duplicated");
|
||||
|
||||
yield promiseWindowClosed(win);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
||||
function promiseDelayedStartupFinished(win) {
|
||||
|
@ -17,11 +17,10 @@ function test() {
|
||||
win.addEventListener("SSWindowClosing", function onclosing() {
|
||||
win.removeEventListener("SSWindowClosing", onclosing, false);
|
||||
executeSoon(function () {
|
||||
is (ss.getClosedWindowCount(), 0,
|
||||
is(ss.getClosedWindowCount(), 0,
|
||||
"The private window should not have been stored");
|
||||
finish();
|
||||
});
|
||||
}, false);
|
||||
win.close();
|
||||
BrowserTestUtils.closeWindow(win).then(finish);
|
||||
});
|
||||
}
|
||||
|
@ -400,7 +400,7 @@ function promiseAllButPrimaryWindowClosed() {
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(windows.map(promiseWindowClosed));
|
||||
return Promise.all(windows.map(BrowserTestUtils.closeWindow));
|
||||
}
|
||||
|
||||
// Forget all closed windows.
|
||||
@ -438,7 +438,8 @@ function whenNewWindowLoaded(aOptions, aCallback) {
|
||||
|
||||
win.addEventListener("load", function onLoad() {
|
||||
win.removeEventListener("load", onLoad);
|
||||
resolve(promiseBrowserLoaded(win.gBrowser.selectedBrowser));
|
||||
let browser = win.gBrowser.selectedBrowser;
|
||||
promiseBrowserLoaded(browser).then(resolve);
|
||||
});
|
||||
});
|
||||
|
||||
@ -448,24 +449,6 @@ function promiseNewWindowLoaded(aOptions) {
|
||||
return new Promise(resolve => whenNewWindowLoaded(aOptions, resolve));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chrome windows aren't closed synchronously. Provide a helper method to close
|
||||
* a window and wait until we received the "domwindowclosed" notification for it.
|
||||
*/
|
||||
function promiseWindowClosed(win) {
|
||||
let promise = new Promise(resolve => {
|
||||
Services.obs.addObserver(function obs(subject, topic) {
|
||||
if (subject == win) {
|
||||
Services.obs.removeObserver(obs, topic);
|
||||
resolve();
|
||||
}
|
||||
}, "domwindowclosed", false);
|
||||
});
|
||||
|
||||
win.close();
|
||||
return promise;
|
||||
}
|
||||
|
||||
function runInContent(browser, func, arg, callback = null) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
@ -567,3 +550,15 @@ function modifySessionStorage(browser, data, options = {}) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function pushPrefs(...aPrefs) {
|
||||
return new Promise(resolve => {
|
||||
SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function popPrefs() {
|
||||
return new Promise(resolve => {
|
||||
SpecialPowers.popPrefEnv(resolve);
|
||||
});
|
||||
}
|
||||
|
@ -55,14 +55,14 @@ const ActorRegistryActor = protocol.ActorClass({
|
||||
},
|
||||
|
||||
registerActor: method(function (sourceText, fileName, options) {
|
||||
registerActor(sourceText, fileName, options);
|
||||
return registerActor(sourceText, fileName, options).then(() => {
|
||||
let { constructor, type } = options;
|
||||
|
||||
let { constructor, type } = options;
|
||||
|
||||
return ActorActor(this.conn, {
|
||||
name: constructor,
|
||||
tab: type.tab,
|
||||
global: type.global
|
||||
return ActorActor(this.conn, {
|
||||
name: constructor,
|
||||
tab: type.tab,
|
||||
global: type.global
|
||||
});
|
||||
});
|
||||
}, {
|
||||
request: {
|
||||
|
@ -9,6 +9,7 @@
|
||||
var { Cu, CC, Ci, Cc } = require("chrome");
|
||||
|
||||
const { DebuggerServer } = require("devtools/server/main");
|
||||
const promise = require("promise");
|
||||
|
||||
/**
|
||||
* Support for actor registration. Main used by ActorRegistryActor
|
||||
@ -45,12 +46,14 @@ exports.registerActor = function(sourceText, fileName, options) {
|
||||
// Also register in all child processes in case the current scope
|
||||
// is chrome parent process.
|
||||
if (!DebuggerServer.isInChildProcess) {
|
||||
DebuggerServer.setupInChild({
|
||||
return DebuggerServer.setupInChild({
|
||||
module: "devtools/server/actors/utils/actor-registry-utils",
|
||||
setupChild: "registerActor",
|
||||
args: [sourceText, fileName, options]
|
||||
args: [sourceText, fileName, options],
|
||||
waitForEval: true
|
||||
});
|
||||
}
|
||||
return promise.resolve();
|
||||
}
|
||||
|
||||
exports.unregisterActor = function(options) {
|
||||
|
@ -69,7 +69,6 @@ var chromeGlobal = this;
|
||||
|
||||
m[setupChild].apply(m, args);
|
||||
|
||||
return true;
|
||||
} catch(e) {
|
||||
let error_msg = "exception during actor module setup running in the child process: ";
|
||||
DevToolsUtils.reportException(error_msg + e);
|
||||
@ -78,6 +77,12 @@ var chromeGlobal = this;
|
||||
DevToolsUtils.safeErrorString(e));
|
||||
return false;
|
||||
}
|
||||
if (msg.data.id) {
|
||||
// Send a message back to know when it is processed
|
||||
sendAsyncMessage("debug:setup-in-child-response",
|
||||
{id: msg.data.id});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
addMessageListener("debug:setup-in-child", onSetupInChild);
|
||||
|
@ -29,6 +29,10 @@ DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => {
|
||||
DevToolsUtils.defineLazyGetter(this, "Authentication", () => {
|
||||
return require("devtools/shared/security/auth");
|
||||
});
|
||||
DevToolsUtils.defineLazyGetter(this, "generateUUID", () => {
|
||||
let { generateUUID } = Cc['@mozilla.org/uuid-generator;1'].getService(Ci.nsIUUIDGenerator);
|
||||
return generateUUID;
|
||||
});
|
||||
|
||||
// On B2G, `this` != Global scope, so `Ci` won't be binded on `this`
|
||||
// (i.e. this.Ci is undefined) Then later, when using loadSubScript,
|
||||
@ -389,8 +393,7 @@ var DebuggerServer = {
|
||||
type: { global: true }
|
||||
});
|
||||
}
|
||||
let win = Services.wm.getMostRecentWindow(DebuggerServer.chromeWindowType);
|
||||
if (win && win.navigator.mozSettings) {
|
||||
if (Services.prefs.getBoolPref("dom.mozSettings.enabled")) {
|
||||
this.registerModule("devtools/server/actors/settings", {
|
||||
prefix: "settings",
|
||||
constructor: "SettingsActor",
|
||||
@ -907,19 +910,51 @@ var DebuggerServer = {
|
||||
* @param setupChild
|
||||
* The name of the setup helper exported by the above module
|
||||
* (setup helper signature: function ({mm}) { ... })
|
||||
* @param waitForEval (optional)
|
||||
* If true, the returned promise only resolves once code in child
|
||||
* is evaluated
|
||||
*/
|
||||
setupInChild: function({ module, setupChild, args }) {
|
||||
if (this.isInChildProcess) {
|
||||
return;
|
||||
setupInChild: function({ module, setupChild, args, waitForEval }) {
|
||||
if (this.isInChildProcess || this._childMessageManagers.size == 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
let deferred = Promise.defer();
|
||||
|
||||
// If waitForEval is set, pass a unique id and expect child.js to send
|
||||
// a message back once the code in child is evaluated.
|
||||
if (typeof(waitForEval) != "boolean") {
|
||||
waitForEval = false;
|
||||
}
|
||||
let count = this._childMessageManagers.size;
|
||||
let id = waitForEval ? generateUUID().toString() : null;
|
||||
|
||||
this._childMessageManagers.forEach(mm => {
|
||||
if (waitForEval) {
|
||||
// Listen for the end of each child execution
|
||||
let evalListener = msg => {
|
||||
if (msg.data.id !== id) {
|
||||
return;
|
||||
}
|
||||
mm.removeMessageListener("debug:setup-in-child-response", evalListener);
|
||||
if (--count === 0) {
|
||||
deferred.resolve();
|
||||
}
|
||||
};
|
||||
mm.addMessageListener("debug:setup-in-child-response", evalListener);
|
||||
}
|
||||
mm.sendAsyncMessage("debug:setup-in-child", {
|
||||
module: module,
|
||||
setupChild: setupChild,
|
||||
args: args,
|
||||
id: id,
|
||||
});
|
||||
});
|
||||
|
||||
if (waitForEval) {
|
||||
return deferred.promise;
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1194,7 +1229,10 @@ var DebuggerServer = {
|
||||
(handler.id && handler.id == aActor.id)) {
|
||||
delete DebuggerServer.tabActorFactories[name];
|
||||
for (let connID of Object.getOwnPropertyNames(this._connections)) {
|
||||
this._connections[connID].rootActor.removeActorByName(name);
|
||||
// DebuggerServerConnection in child process don't have rootActor
|
||||
if (this._connections[connID].rootActor) {
|
||||
this._connections[connID].rootActor.removeActorByName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1698,9 +1736,10 @@ DebuggerServerConnection.prototype = {
|
||||
// Ignore this call if the connection is already closed.
|
||||
return;
|
||||
}
|
||||
this._actorPool = null;
|
||||
|
||||
events.emit(this, "closed", aStatus);
|
||||
|
||||
this._actorPool = null;
|
||||
this._extraPools.map(function(p) { p.destroy(); });
|
||||
this._extraPools = null;
|
||||
|
||||
|
@ -93,3 +93,4 @@ skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still di
|
||||
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
|
||||
[browser_directorscript_actors.js]
|
||||
skip-if = e10s # Bug 1183605 - devtools/server/tests/browser/ tests are still disabled in E10S
|
||||
[browser_register_actor.js]
|
||||
|
@ -1,30 +1,7 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Bug 895360 - [app manager] Device meta data actor
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Mozilla Bug</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script>
|
||||
var gClient;
|
||||
window.onload = function() {
|
||||
var Cu = Components.utils;
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
|
||||
var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
var {DebuggerClient} = require("devtools/shared/client/main");
|
||||
var {DebuggerServer} = require("devtools/server/main");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
var {ActorRegistryFront} = require("devtools/server/actors/actor-registry");
|
||||
var actorURL = "chrome://mochitests/content/chrome/devtools/server/tests/mochitest/hello-actor.js";
|
||||
|
||||
@ -56,7 +33,8 @@ window.onload = function() {
|
||||
actorFront.unregister().then(() => {
|
||||
gClient.close(() => {
|
||||
DebuggerServer.destroy();
|
||||
SimpleTest.finish();
|
||||
gClient = null;
|
||||
finish();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -96,7 +74,3 @@ function getCount(actor, callback) {
|
||||
type: "count"
|
||||
}, callback);
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -98,7 +98,6 @@ skip-if = buildapp == 'mulet'
|
||||
[test_memory_gc_events.html]
|
||||
[test_memprof.html]
|
||||
[test_preference.html]
|
||||
[test_registerActor.html]
|
||||
[test_settings.html]
|
||||
[test_setupInParentChild.html]
|
||||
[test_styles-applied.html]
|
||||
|
@ -228,8 +228,8 @@ public class GeckoMenu extends ListView
|
||||
mItems.add(menuItem);
|
||||
}
|
||||
} else if (actionEnum == GeckoMenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) {
|
||||
if (actionView instanceof MenuItemActionView) {
|
||||
final MenuItemActionView quickShareView = (MenuItemActionView) actionView;
|
||||
if (actionView instanceof MenuItemSwitcherLayout) {
|
||||
final MenuItemSwitcherLayout quickShareView = (MenuItemSwitcherLayout) actionView;
|
||||
|
||||
// We don't want to add the quick share bar if we don't have any quick share items.
|
||||
if (quickShareView.getActionButtonCount() > 0 &&
|
||||
@ -265,14 +265,14 @@ public class GeckoMenu extends ListView
|
||||
return false;
|
||||
}
|
||||
});
|
||||
} else if (actionView instanceof MenuItemActionView) {
|
||||
((MenuItemActionView) actionView).setMenuItemClickListener(new View.OnClickListener() {
|
||||
} else if (actionView instanceof MenuItemSwitcherLayout) {
|
||||
((MenuItemSwitcherLayout) actionView).setMenuItemClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
handleMenuItemClick(menuItem);
|
||||
}
|
||||
});
|
||||
((MenuItemActionView) actionView).setMenuItemLongClickListener(new View.OnLongClickListener() {
|
||||
((MenuItemSwitcherLayout) actionView).setMenuItemLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
if (handleMenuItemLongClick(menuItem)) {
|
||||
@ -607,7 +607,7 @@ public class GeckoMenu extends ListView
|
||||
if (actionView instanceof MenuItemActionBar) {
|
||||
((MenuItemActionBar) actionView).initialize(item);
|
||||
} else {
|
||||
((MenuItemActionView) actionView).initialize(item);
|
||||
((MenuItemSwitcherLayout) actionView).initialize(item);
|
||||
}
|
||||
} else {
|
||||
actionView.setVisibility(View.GONE);
|
||||
@ -738,8 +738,8 @@ public class GeckoMenu extends ListView
|
||||
params = new LinearLayout.LayoutParams(0, mRowHeight);
|
||||
}
|
||||
|
||||
if (actionItem instanceof MenuItemActionView) {
|
||||
params.weight = ((MenuItemActionView) actionItem).getChildCount();
|
||||
if (actionItem instanceof MenuItemSwitcherLayout) {
|
||||
params.weight = ((MenuItemSwitcherLayout) actionItem).getChildCount();
|
||||
} else {
|
||||
params.weight = 1.0f;
|
||||
}
|
||||
@ -818,17 +818,17 @@ public class GeckoMenu extends ListView
|
||||
view = (GeckoMenuItem.Layout) convertView;
|
||||
}
|
||||
|
||||
if (view == null || view instanceof MenuItemActionView) {
|
||||
if (view == null || view instanceof MenuItemSwitcherLayout) {
|
||||
// Always get from the menu item.
|
||||
// This will ensure that the default activity is refreshed.
|
||||
view = (MenuItemActionView) item.getActionView();
|
||||
view = (MenuItemSwitcherLayout) item.getActionView();
|
||||
|
||||
// ListView will not perform an item click if the row has a focusable view in it.
|
||||
// Hence, forward the click event on the menu item in the action-view to the ListView.
|
||||
final View actionView = (View) view;
|
||||
final int pos = position;
|
||||
final long id = getItemId(position);
|
||||
((MenuItemActionView) view).setMenuItemClickListener(new View.OnClickListener() {
|
||||
((MenuItemSwitcherLayout) view).setMenuItemClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
GeckoMenu listView = GeckoMenu.this;
|
||||
|
@ -20,27 +20,34 @@ import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
public class MenuItemActionView extends LinearLayout
|
||||
implements GeckoMenuItem.Layout,
|
||||
View.OnClickListener {
|
||||
/**
|
||||
* This class is a container view for menu items that:
|
||||
* * Shows text if there is enough space and there are
|
||||
* no action buttons ({@link #mActionButtons}).
|
||||
* * Shows an icon if there is not enough space for text,
|
||||
* or there are action buttons.
|
||||
*/
|
||||
public class MenuItemSwitcherLayout extends LinearLayout
|
||||
implements GeckoMenuItem.Layout,
|
||||
View.OnClickListener {
|
||||
private final MenuItemDefault mMenuItem;
|
||||
private final MenuItemActionBar mMenuButton;
|
||||
private final List<ImageButton> mActionButtons;
|
||||
private final List<View.OnClickListener> mActionButtonListeners = new ArrayList<View.OnClickListener>();
|
||||
|
||||
public MenuItemActionView(Context context) {
|
||||
public MenuItemSwitcherLayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public MenuItemActionView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.menuItemActionViewStyle);
|
||||
public MenuItemSwitcherLayout(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.menuItemSwitcherLayoutStyle);
|
||||
}
|
||||
|
||||
@TargetApi(14)
|
||||
public MenuItemActionView(Context context, AttributeSet attrs, int defStyle) {
|
||||
public MenuItemSwitcherLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.menu_item_action_view, this);
|
||||
LayoutInflater.from(context).inflate(R.layout.menu_item_switcher_layout, this);
|
||||
mMenuItem = (MenuItemDefault) findViewById(R.id.menu_item);
|
||||
mMenuButton = (MenuItemActionBar) findViewById(R.id.menu_item_button);
|
||||
mActionButtons = new ArrayList<ImageButton>();
|
||||
@ -170,7 +177,7 @@ public class MenuItemActionView extends LinearLayout
|
||||
* Update the styles if this view is being used in the context menus.
|
||||
*
|
||||
* Ideally, we just use different layout files and styles to set this, but
|
||||
* MenuItemActionView is too integrated into GeckoActionProvider to provide
|
||||
* MenuItemSwitcherLayout is too integrated into GeckoActionProvider to provide
|
||||
* an easy separation so instead I provide this hack. I'm sorry.
|
||||
*/
|
||||
public void initContextMenuStyles() {
|
@ -19,10 +19,10 @@ import org.mozilla.gecko.R;
|
||||
* uplifted, and this implementation will soon be replaced with the old implementation for adding
|
||||
* the share plane to the menu (see https://bug1122302.bugzilla.mozilla.org/attachment.cgi?id=8572126).
|
||||
*/
|
||||
public class QuickShareBarActionView extends MenuItemActionView {
|
||||
public class QuickShareBarActionView extends MenuItemSwitcherLayout {
|
||||
|
||||
public QuickShareBarActionView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.menuItemActionViewStyle);
|
||||
this(context, attrs, R.attr.menuItemSwitcherLayoutStyle);
|
||||
}
|
||||
|
||||
@TargetApi(14)
|
||||
|
@ -424,8 +424,8 @@ gbjar.sources += [
|
||||
'menu/GeckoMenuItem.java',
|
||||
'menu/GeckoSubMenu.java',
|
||||
'menu/MenuItemActionBar.java',
|
||||
'menu/MenuItemActionView.java',
|
||||
'menu/MenuItemDefault.java',
|
||||
'menu/MenuItemSwitcherLayout.java',
|
||||
'menu/MenuPanel.java',
|
||||
'menu/MenuPopup.java',
|
||||
'menu/QuickShareBarActionView.java',
|
||||
|
@ -104,7 +104,7 @@ public class SendTabDeviceListArrayAdapter extends ArrayAdapter<RemoteClient> {
|
||||
|
||||
if (currentState != State.LIST) {
|
||||
// If we're in a special "Button-like" state, use the override string and a generic icon.
|
||||
final Drawable sendTabIcon = context.getResources().getDrawable(R.drawable.overlay_send_tab_icon);
|
||||
final Drawable sendTabIcon = context.getResources().getDrawable(R.drawable.shareplane);
|
||||
row.setText(dummyRecordName);
|
||||
row.setDrawable(sendTabIcon);
|
||||
}
|
||||
|
@ -1,17 +1,11 @@
|
||||
package org.mozilla.gecko.prompts;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.gfx.BitmapUtils;
|
||||
import org.mozilla.gecko.menu.MenuItemActionView;
|
||||
import org.mozilla.gecko.menu.MenuItemSwitcherLayout;
|
||||
import org.mozilla.gecko.widget.GeckoActionProvider;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
@ -25,7 +19,6 @@ import android.widget.CheckedTextView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.AdapterView;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -179,7 +172,7 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
final GeckoActionProvider provider = GeckoActionProvider.getForType(item.getIntent().getType(), getContext());
|
||||
provider.setIntent(item.getIntent());
|
||||
|
||||
final MenuItemActionView view = (MenuItemActionView) provider.onCreateActionView(
|
||||
final MenuItemSwitcherLayout view = (MenuItemSwitcherLayout) provider.onCreateActionView(
|
||||
GeckoActionProvider.ActionViewType.CONTEXT_MENU);
|
||||
// If a quickshare button is clicked, we need to close the dialog.
|
||||
view.addActionButtonClickListener(new View.OnClickListener() {
|
||||
@ -195,7 +188,7 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
return view;
|
||||
}
|
||||
|
||||
private void updateActionView(final PromptListItem item, final MenuItemActionView view, final ListView list, final int position) {
|
||||
private void updateActionView(final PromptListItem item, final MenuItemSwitcherLayout view, final ListView list, final int position) {
|
||||
view.setTitle(item.label);
|
||||
view.setIcon(item.getIcon());
|
||||
view.setSubMenuIndicator(item.isParent);
|
||||
@ -260,7 +253,7 @@ public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
}
|
||||
|
||||
if (type == VIEW_TYPE_ACTIONS) {
|
||||
updateActionView(item, (MenuItemActionView) convertView, (ListView) parent, position);
|
||||
updateActionView(item, (MenuItemSwitcherLayout) convertView, (ListView) parent, position);
|
||||
} else {
|
||||
viewHolder.textView.setText(item.label);
|
||||
maybeUpdateCheckedState((ListView) parent, position, item, viewHolder);
|
||||
|
Before Width: | Height: | Size: 947 B After Width: | Height: | Size: 947 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@ -44,7 +44,7 @@
|
||||
<item name="android:panelBackground">@drawable/menu_panel_bg</item>
|
||||
<item name="android:spinnerDropDownItemStyle">@style/Widget.DropDownItem.Spinner</item>
|
||||
<item name="android:spinnerItemStyle">@style/Widget.TextView.SpinnerItem</item>
|
||||
<item name="menuItemActionViewStyle">@style/Widget.MenuItemActionView</item>
|
||||
<item name="menuItemSwitcherLayoutStyle">@style/Widget.MenuItemSwitcherLayout</item>
|
||||
<item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
|
||||
<item name="menuItemSecondaryActionBarStyle">@style/Widget.MenuItemSecondaryActionBar</item>
|
||||
<item name="tabGridLayoutViewStyle">@style/Widget.TabsGridLayout</item>
|
||||
|
@ -20,7 +20,7 @@
|
||||
<item name="android:panelBackground">@drawable/menu_panel_bg</item>
|
||||
<item name="android:spinnerDropDownItemStyle">@style/Widget.DropDownItem.Spinner</item>
|
||||
<item name="android:spinnerItemStyle">@style/Widget.TextView.SpinnerItem</item>
|
||||
<item name="menuItemActionViewStyle">@style/Widget.MenuItemActionView</item>
|
||||
<item name="menuItemSwitcherLayoutStyle">@style/Widget.MenuItemSwitcherLayout</item>
|
||||
<item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
|
||||
<item name="menuItemSecondaryActionBarStyle">@style/Widget.MenuItemSecondaryActionBar</item>
|
||||
</style>
|
||||
|
@ -40,7 +40,7 @@
|
||||
<item name="android:panelBackground">@drawable/menu_panel_bg</item>
|
||||
<item name="android:spinnerDropDownItemStyle">@style/Widget.DropDownItem.Spinner</item>
|
||||
<item name="android:spinnerItemStyle">@style/Widget.TextView.SpinnerItem</item>
|
||||
<item name="menuItemActionViewStyle">@style/Widget.MenuItemActionView</item>
|
||||
<item name="menuItemSwitcherLayoutStyle">@style/Widget.MenuItemSwitcherLayout</item>
|
||||
<item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
|
||||
<item name="menuItemSecondaryActionBarStyle">@style/Widget.MenuItemSecondaryActionBar</item>
|
||||
<item name="tabGridLayoutViewStyle">@style/Widget.TabsGridLayout</item>
|
||||
|
@ -16,8 +16,8 @@
|
||||
<!-- Style for MenuItemActionBar -->
|
||||
<attr name="menuItemActionModeStyle" format="reference"/>
|
||||
|
||||
<!-- Style for MenuItemActionView -->
|
||||
<attr name="menuItemActionViewStyle" format="reference"/>
|
||||
<!-- Style for MenuItemSwitcherLayout -->
|
||||
<attr name="menuItemSwitcherLayoutStyle" format="reference"/>
|
||||
|
||||
<!-- Style for MenuItemDefault -->
|
||||
<attr name="menuItemDefaultStyle" format="reference"/>
|
||||
@ -25,7 +25,7 @@
|
||||
<!-- Style for MenuItemActionBar when shown in SecondaryActionBar -->
|
||||
<attr name="menuItemSecondaryActionBarStyle" format="reference"/>
|
||||
|
||||
<!-- Style for MenuItemActionView's ShareActionButton -->
|
||||
<!-- Style for MenuItemSwitcherLayout's ShareActionButton -->
|
||||
<attr name="menuItemShareActionButtonStyle" format="reference"/>
|
||||
|
||||
<!-- Default style for the BookmarksListView -->
|
||||
|
@ -99,7 +99,7 @@
|
||||
<item name="drawableTintList">@color/action_bar_secondary_menu_item_colors</item>
|
||||
</style>
|
||||
|
||||
<style name="Widget.MenuItemActionView">
|
||||
<style name="Widget.MenuItemSwitcherLayout">
|
||||
<item name="android:gravity">left</item>
|
||||
</style>
|
||||
|
||||
|
@ -10,9 +10,9 @@ import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.menu.MenuItemActionView;
|
||||
import org.mozilla.gecko.menu.QuickShareBarActionView;
|
||||
import org.mozilla.gecko.overlays.ui.ShareDialog;
|
||||
import org.mozilla.gecko.menu.MenuItemSwitcherLayout;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.content.Context;
|
||||
@ -97,10 +97,10 @@ public class GeckoActionProvider {
|
||||
public View onCreateActionView(final int maxHistorySize, final ActionViewType viewType) {
|
||||
// Create the view and set its data model.
|
||||
ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName);
|
||||
final MenuItemActionView view;
|
||||
final MenuItemSwitcherLayout view;
|
||||
switch (viewType) {
|
||||
case DEFAULT:
|
||||
view = new MenuItemActionView(mContext, null);
|
||||
view = new MenuItemSwitcherLayout(mContext, null);
|
||||
break;
|
||||
|
||||
case QUICK_SHARE_ICON:
|
||||
@ -108,7 +108,7 @@ public class GeckoActionProvider {
|
||||
break;
|
||||
|
||||
case CONTEXT_MENU:
|
||||
view = new MenuItemActionView(mContext, null);
|
||||
view = new MenuItemSwitcherLayout(mContext, null);
|
||||
view.initContextMenuStyles();
|
||||
break;
|
||||
|
||||
|
@ -257,17 +257,42 @@ this.BrowserTestUtils = {
|
||||
}),
|
||||
|
||||
/**
|
||||
* @param win (optional)
|
||||
* The window we should wait to have "domwindowopened" sent through
|
||||
* the observer service for. If this is not supplied, we'll just
|
||||
* resolve when the first "domwindowopened" notification is seen.
|
||||
* @return {Promise}
|
||||
* A Promise which resolves when a "domwindowopened" notification
|
||||
* has been fired by the window watcher.
|
||||
*/
|
||||
domWindowOpened() {
|
||||
domWindowOpened(win) {
|
||||
return new Promise(resolve => {
|
||||
function observer(subject, topic, data) {
|
||||
if (topic != "domwindowopened") { return; }
|
||||
if (topic == "domwindowopened" && (!win || subject === win)) {
|
||||
Services.ww.unregisterNotification(observer);
|
||||
resolve(subject.QueryInterface(Ci.nsIDOMWindow));
|
||||
}
|
||||
}
|
||||
Services.ww.registerNotification(observer);
|
||||
});
|
||||
},
|
||||
|
||||
Services.ww.unregisterNotification(observer);
|
||||
resolve(subject.QueryInterface(Ci.nsIDOMWindow));
|
||||
/**
|
||||
* @param win (optional)
|
||||
* The window we should wait to have "domwindowclosed" sent through
|
||||
* the observer service for. If this is not supplied, we'll just
|
||||
* resolve when the first "domwindowclosed" notification is seen.
|
||||
* @return {Promise}
|
||||
* A Promise which resolves when a "domwindowclosed" notification
|
||||
* has been fired by the window watcher.
|
||||
*/
|
||||
domWindowClosed(win) {
|
||||
return new Promise((resolve) => {
|
||||
function observer(subject, topic, data) {
|
||||
if (topic == "domwindowclosed" && (!win || subject === win)) {
|
||||
Services.ww.unregisterNotification(observer);
|
||||
resolve(subject.QueryInterface(Ci.nsIDOMWindow));
|
||||
}
|
||||
}
|
||||
Services.ww.registerNotification(observer);
|
||||
});
|
||||
@ -318,19 +343,55 @@ this.BrowserTestUtils = {
|
||||
* A window to close.
|
||||
*
|
||||
* @return {Promise}
|
||||
* Resolves when the provided window has been closed.
|
||||
* Resolves when the provided window has been closed. For browser
|
||||
* windows, the Promise will also wait until all final SessionStore
|
||||
* messages have been sent up from all browser tabs.
|
||||
*/
|
||||
closeWindow(win) {
|
||||
return new Promise(resolve => {
|
||||
function observer(subject, topic, data) {
|
||||
if (topic == "domwindowclosed" && subject === win) {
|
||||
Services.ww.unregisterNotification(observer);
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
Services.ww.registerNotification(observer);
|
||||
win.close();
|
||||
});
|
||||
let closedPromise = BrowserTestUtils.windowClosed(win);
|
||||
win.close();
|
||||
return closedPromise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a Promise that resolves when a window has finished closing.
|
||||
*
|
||||
* @param {Window}
|
||||
* The closing window.
|
||||
*
|
||||
* @return {Promise}
|
||||
* Resolves when the provided window has been fully closed. For
|
||||
* browser windows, the Promise will also wait until all final
|
||||
* SessionStore messages have been sent up from all browser tabs.
|
||||
*/
|
||||
windowClosed(win) {
|
||||
let domWinClosedPromise = BrowserTestUtils.domWindowClosed(win);
|
||||
let promises = [domWinClosedPromise];
|
||||
let winType = win.document.documentElement.getAttribute("windowtype");
|
||||
|
||||
if (winType == "navigator:browser") {
|
||||
let finalMsgsPromise = new Promise((resolve) => {
|
||||
let browserSet = new Set(win.gBrowser.browsers);
|
||||
let mm = win.getGroupMessageManager("browsers");
|
||||
|
||||
mm.addMessageListener("SessionStore:update", function onMessage(msg) {
|
||||
if (browserSet.has(msg.target) && msg.data.isFinal) {
|
||||
browserSet.delete(msg.target);
|
||||
if (!browserSet.size) {
|
||||
mm.removeMessageListener("SessionStore:update", onMessage);
|
||||
// Give the TabStateFlusher a chance to react to this final
|
||||
// update and for the TabStateFlusher.flushWindow promise
|
||||
// to resolve before we resolve.
|
||||
TestUtils.executeSoon(resolve);
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
|
||||
promises.push(finalMsgsPromise);
|
||||
}
|
||||
|
||||
return Promise.all(promises);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -44,7 +44,7 @@ elif [[ $MULET =~ 'linux' ]] ; then
|
||||
PLATFORM=linux
|
||||
elif [[ $MULET =~ 'win32' ]] ; then
|
||||
PLATFORM=win32
|
||||
elif [[ $MULET =~ 'mac64' ]] ; then
|
||||
elif [[ $MULET =~ 'mac' ]] ; then
|
||||
PLATFORM=mac64
|
||||
fi
|
||||
if [ -z $PLATFORM ]; then
|
||||
@ -110,14 +110,24 @@ if [[ $MULET =~ .dmg$ ]]; then
|
||||
done
|
||||
# Now we can copy everything out of the $MOUNTPOINT directory into the target directory
|
||||
mkdir -p $ADDON_DIR/firefox
|
||||
cp -r $WORK_DIR/dmg/FirefoxNightly.app $ADDON_DIR/firefox/
|
||||
# Try builds are not branded as Firefox
|
||||
if [ -d $WORK_DIR/dmg/Nightly.app ]; then
|
||||
cp -r $WORK_DIR/dmg/Nightly.app $ADDON_DIR/firefox/FirefoxNightly.app
|
||||
else
|
||||
cp -r $WORK_DIR/dmg/FirefoxNightly.app $ADDON_DIR/firefox/
|
||||
fi
|
||||
hdiutil detach $WORK_DIR/DMG
|
||||
else
|
||||
7z x -o$WORK_DIR/dmg $MULET
|
||||
mkdir -p $WORK_DIR/dmg/hfs
|
||||
sudo mount -o loop,ro -t hfsplus $WORK_DIR/dmg/2.hfs $WORK_DIR/dmg/hfs
|
||||
mkdir -p $ADDON_DIR/firefox
|
||||
cp -r $WORK_DIR/dmg/hfs/FirefoxNightly.app $ADDON_DIR/firefox/
|
||||
# Try builds are not branded as Firefox
|
||||
if [ -d $WORK_DIR/dmg/hfs/Nightly.app ]; then
|
||||
cp -r $WORK_DIR/dmg/hfs/Nightly.app $ADDON_DIR/firefox/FirefoxNightly.app
|
||||
else
|
||||
cp -r $WORK_DIR/dmg/hfs/FirefoxNightly.app $ADDON_DIR/firefox/
|
||||
fi
|
||||
sudo umount $WORK_DIR/dmg/hfs
|
||||
fi
|
||||
rm -rf $WORK_DIR/dmg
|
||||
|
@ -29,6 +29,7 @@
|
||||
const SAMPLE_URL = "http://www.mozilla.org/";
|
||||
const SAMPLE_TEXT = "Some text in a text field.";
|
||||
const SEARCH_TEXT = "Text Test";
|
||||
const NOT_FOUND_TEXT = "This text is not on the page."
|
||||
|
||||
var gFindBar = null;
|
||||
var gBrowser;
|
||||
@ -122,6 +123,7 @@
|
||||
yield testFailedStringReset();
|
||||
gFindBar.close();
|
||||
yield testQuickFindClose();
|
||||
yield Task.spawn(testFindAgainNotFound);
|
||||
finish();
|
||||
});
|
||||
|
||||
@ -544,6 +546,38 @@
|
||||
"testClipboardSearchString: search string not set to '" + aExpected +
|
||||
"', instead found '" + searchStr + "'");
|
||||
}
|
||||
|
||||
// See bug 967982.
|
||||
function* testFindAgainNotFound() {
|
||||
yield openFindbar();
|
||||
yield findAndWaitForResult(NOT_FOUND_TEXT);
|
||||
gFindBar.close();
|
||||
ok(gFindBar.hidden, "The findbar is closed.");
|
||||
yield findAndWaitForResult();
|
||||
ok(!gFindBar.hidden, "Unsuccessful Find Again opens the find bar.");
|
||||
|
||||
yield findAndWaitForResult(SEARCH_TEXT);
|
||||
gFindBar.close();
|
||||
ok(gFindBar.hidden, "The findbar is closed.");
|
||||
yield findAndWaitForResult();
|
||||
ok(gFindBar.hidden, "Successful Find Again leaves the find bar closed.");
|
||||
}
|
||||
|
||||
function findAndWaitForResult(aText) {
|
||||
return new Promise(resolve => {
|
||||
gFindBar.browser.finder.addResultListener({
|
||||
onFindResult: function(aData) {
|
||||
gFindBar.browser.finder.removeResultListener(this);
|
||||
resolve(aData);
|
||||
}
|
||||
});
|
||||
if (aText) {
|
||||
enterStringIntoFindField(aText);
|
||||
} else {
|
||||
gFindBar.onFindAgainCommand();
|
||||
}
|
||||
});
|
||||
}
|
||||
]]></script>
|
||||
|
||||
<commandset>
|
||||
|
@ -1152,14 +1152,20 @@
|
||||
<method name="onFindResult">
|
||||
<parameter name="aData"/>
|
||||
<body><![CDATA[
|
||||
if (aData.result == this.nsITypeAheadFind.FIND_NOTFOUND) {
|
||||
// If an explicit Find Again command fails, re-open the toolbar.
|
||||
if (aData.storeResult && this.open()) {
|
||||
this._findField.select();
|
||||
this._findField.focus();
|
||||
}
|
||||
this._findFailedString = aData.searchString;
|
||||
} else {
|
||||
this._findFailedString = null;
|
||||
}
|
||||
|
||||
this._updateStatusUI(aData.result, aData.findBackwards);
|
||||
this._updateStatusUIBar(aData.linkURL);
|
||||
|
||||
if (aData.result == this.nsITypeAheadFind.FIND_NOTFOUND)
|
||||
this._findFailedString = aData.searchString;
|
||||
else
|
||||
this._findFailedString = null;
|
||||
|
||||
if (this._findMode != this.FIND_NORMAL)
|
||||
this._setFindCloseTimeout();
|
||||
]]></body>
|
||||
|
Loading…
Reference in New Issue
Block a user