mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-22 01:35:35 +00:00
Bug 1332122 - Re-register the browser for frame script reloads. r=automatedtester
With multi-processes for content reloads of the frame script can happen at any time, and not only for remoteness changes. As such the current browser has to be re-registered with the new id of the listener. MozReview-Commit-ID: 48MOZfuPTR9 --HG-- extra : rebase_source : 60e91ae7e1cdc942d0ac9a9dd3aac3baeccc5ddf
This commit is contained in:
parent
d373a550b4
commit
618082fc0c
@ -78,8 +78,8 @@ browser.getTabBrowser = function(win) {
|
||||
browser.Context = class {
|
||||
|
||||
/**
|
||||
* @param {<xul:browser>} win
|
||||
* Frame that is expected to contain the view of the web document.
|
||||
* @param {ChromeWindow} win
|
||||
* ChromeWindow that contains the top-level browsing context.
|
||||
* @param {GeckoDriver} driver
|
||||
* Reference to driver instance.
|
||||
*/
|
||||
@ -106,7 +106,14 @@ browser.Context = class {
|
||||
// browser window, this.tab will still point to tab A, despite tab B
|
||||
// being the currently selected tab.
|
||||
this.tab = null;
|
||||
|
||||
// Commands which trigger a page load can cause the frame script to be
|
||||
// reloaded. To not loose the currently active command, or any other
|
||||
// already pushed following command, store them as long as they haven't
|
||||
// been fully processed. The commands get flushed after a new browser
|
||||
// has been registered.
|
||||
this.pendingCommands = [];
|
||||
this._needsFlushPendingCommands = false;
|
||||
|
||||
// We should have one frame.Manager per browser.Context so that we
|
||||
// can handle modals in each <xul:browser>.
|
||||
@ -117,8 +124,6 @@ browser.Context = class {
|
||||
this.frameManager.addMessageManagerListeners(driver.mm);
|
||||
this.getIdForBrowser = driver.getIdForBrowser.bind(driver);
|
||||
this.updateIdForBrowser = driver.updateIdForBrowser.bind(driver);
|
||||
this._browserWasRemote = null;
|
||||
this._hasRemotenessChange = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -286,8 +291,7 @@ browser.Context = class {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current tab and update remoteness tracking if a tabbrowser
|
||||
* is available.
|
||||
* Set the current tab.
|
||||
*
|
||||
* @param {number=} index
|
||||
* Tab index to switch to. If the parameter is undefined,
|
||||
@ -330,11 +334,6 @@ browser.Context = class {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.driver.appName == "Firefox") {
|
||||
this._browserWasRemote = this.contentBrowser.isRemoteBrowser;
|
||||
this._hasRemotenessChange = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -344,65 +343,38 @@ browser.Context = class {
|
||||
*
|
||||
* @param {string} uid
|
||||
* Frame uid for use by Marionette.
|
||||
* @param the XUL <browser> that was the target of the originating message.
|
||||
* @param {xul:browser} target
|
||||
* The <xul:browser> that was the target of the originating message.
|
||||
*/
|
||||
register(uid, target) {
|
||||
let remotenessChange = this.hasRemotenessChange();
|
||||
if (this.curFrameId === null || remotenessChange) {
|
||||
if (this.tabBrowser) {
|
||||
// If we're setting up a new session on Firefox, we only process the
|
||||
// registration for this frame if it belongs to the current tab.
|
||||
if (!this.tab) {
|
||||
this.switchToTab();
|
||||
}
|
||||
if (this.tabBrowser) {
|
||||
// If we're setting up a new session on Firefox, we only process the
|
||||
// registration for this frame if it belongs to the current tab.
|
||||
if (!this.tab) {
|
||||
this.switchToTab();
|
||||
}
|
||||
|
||||
if (target === this.contentBrowser) {
|
||||
this.updateIdForBrowser(this.contentBrowser, uid);
|
||||
}
|
||||
if (target === this.contentBrowser) {
|
||||
this.updateIdForBrowser(this.contentBrowser, uid);
|
||||
this._needsFlushPendingCommands = true;
|
||||
}
|
||||
}
|
||||
|
||||
// used to delete sessions
|
||||
this.knownFrames.push(uid);
|
||||
return remotenessChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* When navigating between pages results in changing a browser's
|
||||
* process, we need to take measures not to lose contact with a listener
|
||||
* script. This function does the necessary bookkeeping.
|
||||
*/
|
||||
hasRemotenessChange() {
|
||||
// None of these checks are relevant if we don't have a tab yet,
|
||||
// and may not apply on Fennec.
|
||||
if (this.driver.appName != "Firefox" ||
|
||||
this.tab === null ||
|
||||
this.contentBrowser === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._hasRemotenessChange) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let currentIsRemote = this.contentBrowser.isRemoteBrowser;
|
||||
this._hasRemotenessChange = this._browserWasRemote !== currentIsRemote;
|
||||
this._browserWasRemote = currentIsRemote;
|
||||
return this._hasRemotenessChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes any pending commands queued when a remoteness change is being
|
||||
* processed and mark this remotenessUpdate as complete.
|
||||
* Flushes any queued pending commands after a reload of the frame script.
|
||||
*/
|
||||
flushPendingCommands() {
|
||||
if (!this._hasRemotenessChange) {
|
||||
if (!this._needsFlushPendingCommands) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._hasRemotenessChange = false;
|
||||
this.pendingCommands.forEach(cb => cb());
|
||||
this.pendingCommands = [];
|
||||
this._needsFlushPendingCommands = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -412,11 +384,11 @@ browser.Context = class {
|
||||
* No commands interacting with content are safe to process until
|
||||
* the new listener script is loaded and registers itself.
|
||||
* This occurs when a command whose effect is asynchronous (such
|
||||
* as goBack) results in a remoteness change and new commands
|
||||
* as goBack) results in a reload of the frame script and new commands
|
||||
* are subsequently posted to the server.
|
||||
*/
|
||||
executeWhenReady(cb) {
|
||||
if (this.hasRemotenessChange()) {
|
||||
if (this._needsFlushPendingCommands) {
|
||||
this.pendingCommands.push(cb);
|
||||
} else {
|
||||
cb();
|
||||
|
@ -571,8 +571,6 @@ GeckoDriver.prototype.registerBrowser = function(id, be) {
|
||||
this.curBrowser.frameManager.currentRemoteFrame.targetFrameId = id;
|
||||
}
|
||||
|
||||
let reg = {};
|
||||
|
||||
// We want to ignore frames that are XUL browsers that aren't in the "main"
|
||||
// tabbrowser, but accept things on Fennec (which doesn't have a
|
||||
// xul:tabbrowser), and accept HTML iframes (because tests depend on it),
|
||||
@ -581,11 +579,10 @@ GeckoDriver.prototype.registerBrowser = function(id, be) {
|
||||
if (this.appName != "Firefox" || be.namespaceURI != XUL_NS ||
|
||||
be.nodeName != "browser" || be.getTabBrowser()) {
|
||||
// curBrowser holds all the registered frames in knownFrames
|
||||
reg.id = id;
|
||||
reg.remotenessChange = this.curBrowser.register(id, be);
|
||||
this.curBrowser.register(id, be);
|
||||
}
|
||||
|
||||
this.wins.set(reg.id, listenerWindow);
|
||||
this.wins.set(id, listenerWindow);
|
||||
if (nullPrevious && (this.curBrowser.curFrameId !== null)) {
|
||||
this.sendAsync(
|
||||
"newSession",
|
||||
@ -596,7 +593,7 @@ GeckoDriver.prototype.registerBrowser = function(id, be) {
|
||||
}
|
||||
}
|
||||
|
||||
return [reg, this.capabilities.toJSON()];
|
||||
return [id, this.capabilities.toJSON()];
|
||||
};
|
||||
|
||||
GeckoDriver.prototype.registerPromise = function() {
|
||||
@ -626,10 +623,13 @@ GeckoDriver.prototype.registerPromise = function() {
|
||||
|
||||
GeckoDriver.prototype.listeningPromise = function() {
|
||||
const li = "Marionette:listenersAttached";
|
||||
|
||||
return new Promise(resolve => {
|
||||
let cb = () => {
|
||||
this.mm.removeMessageListener(li, cb);
|
||||
resolve();
|
||||
let cb = msg => {
|
||||
if (msg.json.listenerId === this.curBrowser.curFrameId) {
|
||||
this.mm.removeMessageListener(li, cb);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
this.mm.addMessageListener(li, cb);
|
||||
});
|
||||
@ -978,9 +978,9 @@ GeckoDriver.prototype.get = function* (cmd, resp) {
|
||||
|
||||
let get = this.listener.get({url, pageTimeout: this.timeouts.pageLoad});
|
||||
|
||||
// If a remoteness update interrupts our page load, this will never return
|
||||
// We need to re-issue this request to correctly poll for readyState and
|
||||
// send errors.
|
||||
// If a reload of the frame script interrupts our page load, this will
|
||||
// never return. We need to re-issue this request to correctly poll for
|
||||
// readyState and send errors.
|
||||
this.curBrowser.pendingCommands.push(() => {
|
||||
let parameters = {
|
||||
// TODO(ato): Bug 1242595
|
||||
@ -1097,9 +1097,9 @@ GeckoDriver.prototype.goBack = function* (cmd, resp) {
|
||||
let lastURL = this.currentURL;
|
||||
let goBack = this.listener.goBack({pageTimeout: this.timeouts.pageLoad});
|
||||
|
||||
// If a remoteness update interrupts our page load, this will never return
|
||||
// We need to re-issue this request to correctly poll for readyState and
|
||||
// send errors.
|
||||
// If a reload of the frame script interrupts our page load, this will
|
||||
// never return. We need to re-issue this request to correctly poll for
|
||||
// readyState and send errors.
|
||||
this.curBrowser.pendingCommands.push(() => {
|
||||
let parameters = {
|
||||
// TODO(ato): Bug 1242595
|
||||
@ -1141,9 +1141,9 @@ GeckoDriver.prototype.goForward = function* (cmd, resp) {
|
||||
let goForward = this.listener.goForward(
|
||||
{pageTimeout: this.timeouts.pageLoad});
|
||||
|
||||
// If a remoteness update interrupts our page load, this will never return
|
||||
// We need to re-issue this request to correctly poll for readyState and
|
||||
// send errors.
|
||||
// If a reload of the frame script interrupts our page load, this will
|
||||
// never return. We need to re-issue this request to correctly poll for
|
||||
// readyState and send errors.
|
||||
this.curBrowser.pendingCommands.push(() => {
|
||||
let parameters = {
|
||||
// TODO(ato): Bug 1242595
|
||||
@ -1176,7 +1176,25 @@ GeckoDriver.prototype.refresh = function* (cmd, resp) {
|
||||
assert.window(this.getCurrentWindow());
|
||||
assert.noUserPrompt(this.dialog);
|
||||
|
||||
yield this.listener.refresh({pageTimeout: this.timeouts.pageLoad});
|
||||
let refresh = this.listener.refresh(
|
||||
{pageTimeout: this.timeouts.pageLoad})
|
||||
|
||||
// If a reload of the frame script interrupts our page load, this will
|
||||
// never return. We need to re-issue this request to correctly poll for
|
||||
// readyState and send errors.
|
||||
this.curBrowser.pendingCommands.push(() => {
|
||||
let parameters = {
|
||||
// TODO(ato): Bug 1242595
|
||||
command_id: this.listener.activeMessageId,
|
||||
pageTimeout: this.timeouts.pageLoad,
|
||||
startTime: new Date().getTime(),
|
||||
};
|
||||
this.mm.broadcastAsyncMessage(
|
||||
"Marionette:waitForPageLoaded" + this.curBrowser.curFrameId,
|
||||
parameters);
|
||||
});
|
||||
|
||||
yield refresh;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -2069,9 +2087,9 @@ GeckoDriver.prototype.clickElement = function* (cmd, resp) {
|
||||
let click = this.listener.clickElement(
|
||||
{id, pageTimeout: this.timeouts.pageLoad});
|
||||
|
||||
// If a remoteness update interrupts our page load, this will
|
||||
// never return We need to re-issue this request to correctly poll
|
||||
// for readyState and send errors.
|
||||
// If a reload of the frame script interrupts our page load, this will
|
||||
// never return. We need to re-issue this request to correctly poll for
|
||||
// readyState and send errors.
|
||||
this.curBrowser.pendingCommands.push(() => {
|
||||
let parameters = {
|
||||
// TODO(ato): Bug 1242595
|
||||
@ -3178,9 +3196,9 @@ GeckoDriver.prototype.receiveMessage = function(message) {
|
||||
|
||||
case "Marionette:listenersAttached":
|
||||
if (message.json.listenerId === this.curBrowser.curFrameId) {
|
||||
// If remoteness gets updated we need to call newSession. In the case
|
||||
// of desktop this just sets up a small amount of state that doesn't
|
||||
// change over the course of a session.
|
||||
// If the frame script gets reloaded we need to call newSession.
|
||||
// In the case of desktop this just sets up a small amount of state
|
||||
// that doesn't change over the course of a session.
|
||||
this.sendAsync("newSession", this.capabilities.toJSON());
|
||||
this.curBrowser.flushPendingCommands();
|
||||
}
|
||||
|
@ -54,7 +54,6 @@ var winUtil = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
var listenerId = null; // unique ID of this listener
|
||||
var curContainer = {frame: content, shadowRoot: null};
|
||||
var isRemoteBrowser = () => curContainer.frame.contentWindow !== null;
|
||||
var previousContainer = null;
|
||||
|
||||
var seenEls = new element.Store();
|
||||
@ -118,10 +117,10 @@ var sandboxes = new Sandboxes(() => curContainer.frame);
|
||||
var sandboxName = "default";
|
||||
|
||||
/**
|
||||
* The load listener singleton helps to keep track of active page
|
||||
* load activities, and can be used by any command which might cause a
|
||||
* navigation to happen. In the specific case of remoteness changes it
|
||||
* allows to continue observing the current page load.
|
||||
* The load listener singleton helps to keep track of active page load
|
||||
* activities, and can be used by any command which might cause a navigation
|
||||
* to happen. In the specific case of a reload of the frame script it allows
|
||||
* to continue observing the current page load.
|
||||
*/
|
||||
var loadListener = {
|
||||
command_id: null,
|
||||
@ -157,7 +156,7 @@ var loadListener = {
|
||||
.createInstance(Ci.nsITimer);
|
||||
this.timerPageUnload = null;
|
||||
|
||||
// In case of a remoteness change, only wait the remaining time
|
||||
// In case the frame script has been reloaded, wait the remaining time
|
||||
timeout = startTime + timeout - new Date().getTime();
|
||||
|
||||
if (timeout <= 0) {
|
||||
@ -212,8 +211,8 @@ var loadListener = {
|
||||
}
|
||||
}
|
||||
|
||||
// In the case when the observer was added before a remoteness change,
|
||||
// it will no longer be available. Exceptions can be silently ignored.
|
||||
// In case the observer was added before the frame script has been
|
||||
// reloaded, it will no longer be available. Exceptions can be ignored.
|
||||
try {
|
||||
Services.obs.removeObserver(this, "outer-window-destroyed");
|
||||
} catch (e) {}
|
||||
@ -344,8 +343,8 @@ var loadListener = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Continue to listen for page load events after a remoteness change
|
||||
* happened.
|
||||
* Continue to listen for page load events after the frame script has been
|
||||
* reloaded.
|
||||
*
|
||||
* @param {number} command_id
|
||||
* ID of the currently handled message between the driver and
|
||||
@ -356,7 +355,7 @@ var loadListener = {
|
||||
* @param {number} startTime
|
||||
* Unix timestap when the navitation request got triggered.
|
||||
*/
|
||||
waitForLoadAfterRemotenessChange(command_id, timeout, startTime) {
|
||||
waitForLoadAfterFramescriptReload(command_id, timeout, startTime) {
|
||||
this.start(command_id, timeout, startTime, false);
|
||||
},
|
||||
|
||||
@ -430,18 +429,13 @@ function registerSelf() {
|
||||
// register will have the ID and a boolean describing if this is the
|
||||
// main process or not
|
||||
let register = sendSyncMessage("Marionette:register", msg);
|
||||
|
||||
if (register[0]) {
|
||||
let {id, remotenessChange} = register[0][0];
|
||||
listenerId = register[0][0];
|
||||
capabilities = session.Capabilities.fromJSON(register[0][1]);
|
||||
listenerId = id;
|
||||
if (typeof id != "undefined") {
|
||||
if (typeof listenerId != "undefined") {
|
||||
startListeners();
|
||||
let rv = {};
|
||||
if (remotenessChange) {
|
||||
rv.listenerId = id;
|
||||
}
|
||||
sendAsyncMessage("Marionette:listenersAttached", rv);
|
||||
sendAsyncMessage("Marionette:listenersAttached",
|
||||
{"listenerId": listenerId});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1105,8 +1099,8 @@ function cancelRequest() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This implements the latter part of a get request (for the case we
|
||||
* need to resume one when a remoteness update happens in the middle of a
|
||||
* This implements the latter part of a get request (for the case we need
|
||||
* to resume one when the frame script has been reloaded in the middle of a
|
||||
* navigate request). This is most of of the work of a navigate request,
|
||||
* but doesn't assume DOMContentLoaded is yet to fire.
|
||||
*
|
||||
@ -1122,7 +1116,7 @@ function cancelRequest() {
|
||||
function waitForPageLoaded(msg) {
|
||||
let {command_id, pageTimeout, startTime} = msg.json;
|
||||
|
||||
loadListener.waitForLoadAfterRemotenessChange(
|
||||
loadListener.waitForLoadAfterFramescriptReload(
|
||||
command_id, pageTimeout, startTime);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user