Bug 1271616 - Make BrowserObj into a module; r?automatedtester
No changes to the functionality of BrowserObj whatsoever.
MozReview-Commit-ID: JGg7eqil0qd
new file mode 100644
--- /dev/null
+++ b/testing/marionette/browser.js
@@ -0,0 +1,262 @@
+/* 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/. */
+
+"use strict";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+Cu.import("resource://gre/modules/Log.jsm");
+
+Cu.import("chrome://marionette/content/element.js");
+Cu.import("chrome://marionette/content/frame.js");
+
+this.EXPORTED_SYMBOLS = ["browser"];
+
+const logger = Log.repository.getLogger("Marionette");
+
+this.browser = {};
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+/**
+ * Creates a browsing context wrapper.
+ *
+ * Browsing contexts handle interactions with the browser, according to
+ * the current environment (desktop, B2G, Fennec, &c).
+ *
+ * @param {nsIDOMWindow} win
+ * The window whose browser needs to be accessed.
+ * @param {GeckoDriver} driver
+ * Reference to the driver the browser is attached to.
+ */
+browser.Context = class {
+
+ constructor(win, driver) {
+ logger.info("Context ctor");
+ this.browser = undefined;
+ this.window = win;
+ this.driver = driver;
+ this.knownFrames = [];
+ this.startPage = "about:blank";
+ // used in B2G to identify the homescreen content page
+ this.mainContentId = null;
+ // used to set curFrameId upon new session
+ this.newSession = true;
+ this.elementManager = new ElementManager([
+ element.Strategy.Name,
+ element.Strategy.LinkText,
+ element.Strategy.PartialLinkText,
+ ]);
+ this.setBrowser(win);
+
+ // A reference to the tab corresponding to the current window handle, if any.
+ this.tab = null;
+ this.pendingCommands = [];
+
+ // we should have one FM per BO so that we can handle modals in each Browser
+ this.frameManager = new frame.Manager(driver);
+ this.frameRegsPending = 0;
+
+ // register all message listeners
+ this.frameManager.addMessageManagerListeners(driver.mm);
+ this.getIdForBrowser = driver.getIdForBrowser.bind(driver);
+ this.updateIdForBrowser = driver.updateIdForBrowser.bind(driver);
+ this._curFrameId = null;
+ this._browserWasRemote = null;
+ this._hasRemotenessChange = false;
+ }
+
+ get browserForTab() {
+ return this.browser.getBrowserForTab(this.tab);
+ }
+
+ /**
+ * The current frame ID is managed per browser element on desktop in
+ * case the ID needs to be refreshed. The currently selected window is
+ * identified by a tab.
+ */
+ get curFrameId() {
+ let rv = null;
+ if (this.driver.appName != "Firefox") {
+ rv = this._curFrameId;
+ } else if (this.tab) {
+ rv = this.getIdForBrowser(this.browserForTab);
+ }
+ return rv;
+ }
+
+ set curFrameId(id) {
+ if (this.driver.appName != "Firefox") {
+ this._curFrameId = id;
+ }
+ }
+
+ /**
+ * Retrieves the current tabmodal UI object. According to the browser
+ * associated with the currently selected tab.
+ */
+ getTabModalUI() {
+ let br = this.browserForTab;
+ if (!br.hasAttribute("tabmodalPromptShowing")) {
+ return null;
+ }
+
+ // The modal is a direct sibling of the browser element.
+ // See tabbrowser.xml's getTabModalPromptBox.
+ let modals = br.parentNode.getElementsByTagNameNS(
+ XUL_NS, "tabmodalprompt");
+ return modals[0].ui;
+ }
+
+ /**
+ * Set the browser if the application is not B2G.
+ *
+ * @param {nsIDOMWindow} win
+ * Current window reference.
+ */
+ setBrowser(win) {
+ switch (this.driver.appName) {
+ case "Firefox":
+ this.browser = win.gBrowser;
+ break;
+
+ case "Fennec":
+ this.browser = win.BrowserApp;
+ break;
+ }
+ }
+
+ /** Called when we start a session with this browser. */
+ startSession(newSession, win, callback) {
+ callback(win, newSession);
+ }
+
+ /** Closes current tab. */
+ closeTab() {
+ if (this.browser &&
+ this.browser.removeTab &&
+ this.tab !== null && (this.driver.appName != "B2G")) {
+ this.browser.removeTab(this.tab);
+ }
+ }
+
+ /**
+ * Opens a tab with given URI.
+ *
+ * @param {string} uri
+ * URI to open.
+ */
+ addTab(uri) {
+ return this.browser.addTab(uri, true);
+ }
+
+ /**
+ * Re-sets current tab and updates remoteness tracking.
+ *
+ * If a window is provided, the internal reference is updated before
+ * proceeding.
+ */
+ switchToTab(ind, win) {
+ if (win) {
+ this.window = win;
+ this.setBrowser(win);
+ }
+
+ this.browser.selectTabAtIndex(ind);
+ this.tab = this.browser.selectedTab;
+ this._browserWasRemote = this.browserForTab.isRemoteBrowser;
+ this._hasRemotenessChange = false;
+ }
+
+ /**
+ * Registers a new frame, and sets its current frame id to this frame
+ * if it is not already assigned, and if a) we already have a session
+ * or b) we're starting a new session and it is the right start frame.
+ *
+ * @param {string} uid
+ * Frame uid for use by Marionette.
+ * @param 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.browser) {
+ // 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(this.browser.selectedIndex);
+ }
+
+ if (target == this.browserForTab) {
+ this.updateIdForBrowser(this.browserForTab, uid);
+ this.mainContentId = uid;
+ }
+ } else {
+ this._curFrameId = uid;
+ this.mainContentId = uid;
+ }
+ }
+
+ // 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 on b2g or if we don't have a tab yet,
+ // and may not apply on Fennec.
+ if (this.driver.appName != "Firefox" || this.tab === null) {
+ return false;
+ }
+
+ if (this._hasRemotenessChange) {
+ return true;
+ }
+
+ // this.tab can potentially get stale and cause problems, see bug 1227252
+ let currentTab = this.browser.selectedTab;
+ let currentIsRemote = this.browser.getBrowserForTab(currentTab).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.
+ */
+ flushPendingCommands() {
+ if (!this._hasRemotenessChange) {
+ return;
+ }
+
+ this._hasRemotenessChange = false;
+ this.pendingCommands.forEach(cb => cb());
+ this.pendingCommands = [];
+ }
+
+ /**
+ * This function intercepts commands interacting with content and queues
+ * or executes them as needed.
+ *
+ * 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
+ * are subsequently posted to the server.
+ */
+ executeWhenReady(cb) {
+ if (this.hasRemotenessChange()) {
+ this.pendingCommands.push(cb);
+ } else {
+ cb();
+ }
+ }
+
+};
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -14,33 +14,32 @@ Cu.import("resource://gre/modules/Prefer
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(
this, "cookieManager", "@mozilla.org/cookiemanager;1", "nsICookieManager2");
Cu.import("chrome://marionette/content/action.js");
Cu.import("chrome://marionette/content/atom.js");
+Cu.import("chrome://marionette/content/browser.js");
Cu.import("chrome://marionette/content/element.js");
Cu.import("chrome://marionette/content/emulator.js");
Cu.import("chrome://marionette/content/error.js");
Cu.import("chrome://marionette/content/evaluate.js");
Cu.import("chrome://marionette/content/event.js");
-Cu.import("chrome://marionette/content/frame.js");
Cu.import("chrome://marionette/content/interaction.js");
Cu.import("chrome://marionette/content/logging.js");
Cu.import("chrome://marionette/content/modal.js");
Cu.import("chrome://marionette/content/proxy.js");
Cu.import("chrome://marionette/content/simpletest.js");
this.EXPORTED_SYMBOLS = ["GeckoDriver", "Context"];
var FRAME_SCRIPT = "chrome://marionette/content/listener.js";
const BROWSER_STARTUP_FINISHED = "browser-delayed-startup-finished";
-const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const CLICK_TO_START_PREF = "marionette.debugging.clicktostart";
const CONTENT_LISTENER_PREF = "marionette.contentListener";
const logger = Log.repository.getLogger("Marionette");
const globalMessageManager = Cc["@mozilla.org/globalmessagemanager;1"]
.getService(Ci.nsIMessageBroadcaster);
// This is used to prevent newSession from returning before the telephony
@@ -94,17 +93,17 @@ this.Context.fromString = function(s) {
this.GeckoDriver = function(appName, device, stopSignal, emulator) {
this.appName = appName;
this.stopSignal_ = stopSignal;
this.emulator = emulator;
// TODO(ato): hack
this.emulator.sendToListener = this.sendAsync.bind(this);
this.sessionId = null;
- // holds list of BrowserObjs
+ // holds list of browser.Context's
this.browsers = {};
// points to current browser
this.curBrowser = null;
this.context = Context.CONTENT;
this.scriptTimeout = null;
this.searchTimeout = null;
this.pageTimeout = null;
this.timer = null;
@@ -268,30 +267,32 @@ GeckoDriver.prototype.addFrameCloseListe
this.switchToGlobalMessageManager();
throw new NoSuchWindowError("The window closed during action: " + action);
}
};
win.addEventListener("mozbrowserclose", this.mozBrowserClose, true);
};
/**
- * Create a new BrowserObj for window and add to known browsers.
+ * Create a new browsing context for window and add to known browsers.
*
* @param {nsIDOMWindow} win
- * Window for which we will create a BrowserObj.
+ * Window for which we will create a browsing context.
*
* @return {string}
* Returns the unique server-assigned ID of the window.
*/
GeckoDriver.prototype.addBrowser = function(win) {
- let browser = new BrowserObj(win, this);
+ logger.info("addBrowser");
+ let bc = new browser.Context(win, this);
+ logger.info("bc=" + bc);
let winId = win.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).outerWindowID;
winId = winId + ((this.appName == "B2G") ? "-b2g" : "");
- this.browsers[winId] = browser;
+ this.browsers[winId] = bc;
this.curBrowser = this.browsers[winId];
if (typeof this.curBrowser.elementManager.seenItems[winId] == "undefined") {
// add this to seenItems so we can guarantee
// the user will get winId as this window's id
this.curBrowser.elementManager.seenItems[winId] = Cu.getWeakReference(win);
}
};
@@ -2772,246 +2773,8 @@ GeckoDriver.prototype.commands = {
"setWindowSize": GeckoDriver.prototype.setWindowSize,
"maximizeWindow": GeckoDriver.prototype.maximizeWindow,
"dismissDialog": GeckoDriver.prototype.dismissDialog,
"acceptDialog": GeckoDriver.prototype.acceptDialog,
"getTextFromDialog": GeckoDriver.prototype.getTextFromDialog,
"sendKeysToDialog": GeckoDriver.prototype.sendKeysToDialog,
"quitApplication": GeckoDriver.prototype.quitApplication,
};
-
-/**
- * Creates a BrowserObj. BrowserObjs handle interactions with the
- * browser, according to the current environment (desktop, b2g, etc.).
- *
- * @param {nsIDOMWindow} win
- * The window whose browser needs to be accessed.
- * @param {GeckoDriver} driver
- * Reference to the driver the browser is attached to.
- */
-var BrowserObj = function(win, driver) {
- this.browser = undefined;
- this.window = win;
- this.driver = driver;
- this.knownFrames = [];
- this.startPage = "about:blank";
- // used in B2G to identify the homescreen content page
- this.mainContentId = null;
- // used to set curFrameId upon new session
- this.newSession = true;
- this.elementManager = new ElementManager([
- element.Strategy.Name,
- element.Strategy.LinkText,
- element.Strategy.PartialLinkText,
- ]);
- this.setBrowser(win);
-
- // A reference to the tab corresponding to the current window handle, if any.
- this.tab = null;
- this.pendingCommands = [];
-
- // we should have one FM per BO so that we can handle modals in each Browser
- this.frameManager = new frame.Manager(driver);
- this.frameRegsPending = 0;
-
- // register all message listeners
- this.frameManager.addMessageManagerListeners(driver.mm);
- this.getIdForBrowser = driver.getIdForBrowser.bind(driver);
- this.updateIdForBrowser = driver.updateIdForBrowser.bind(driver);
- this._curFrameId = null;
- this._browserWasRemote = null;
- this._hasRemotenessChange = false;
-};
-
-Object.defineProperty(BrowserObj.prototype, "browserForTab", {
- get() {
- return this.browser.getBrowserForTab(this.tab);
- }
-});
-
-/**
- * The current frame ID is managed per browser element on desktop in
- * case the ID needs to be refreshed. The currently selected window is
- * identified within BrowserObject by a tab.
- */
-Object.defineProperty(BrowserObj.prototype, "curFrameId", {
- get() {
- let rv = null;
- if (this.driver.appName != "Firefox") {
- rv = this._curFrameId;
- } else if (this.tab) {
- rv = this.getIdForBrowser(this.browserForTab);
- }
- return rv;
- },
-
- set(id) {
- if (this.driver.appName != "Firefox") {
- this._curFrameId = id;
- }
- }
-});
-
-/**
- * Retrieves the current tabmodal UI object. According to the browser
- * associated with the currently selected tab.
- */
-BrowserObj.prototype.getTabModalUI = function() {
- let br = this.browserForTab;
- if (!br.hasAttribute("tabmodalPromptShowing")) {
- return null;
- }
-
- // The modal is a direct sibling of the browser element.
- // See tabbrowser.xml's getTabModalPromptBox.
- let modals = br.parentNode.getElementsByTagNameNS(
- XUL_NS, "tabmodalprompt");
- return modals[0].ui;
-};
-
-/**
- * Set the browser if the application is not B2G.
- *
- * @param {nsIDOMWindow} win
- * Current window reference.
- */
-BrowserObj.prototype.setBrowser = function(win) {
- switch (this.driver.appName) {
- case "Firefox":
- this.browser = win.gBrowser;
- break;
-
- case "Fennec":
- this.browser = win.BrowserApp;
- break;
- }
-};
-
-/** Called when we start a session with this browser. */
-BrowserObj.prototype.startSession = function(newSession, win, callback) {
- callback(win, newSession);
-};
-
-/** Closes current tab. */
-BrowserObj.prototype.closeTab = function() {
- if (this.browser &&
- this.browser.removeTab &&
- this.tab !== null && (this.driver.appName != "B2G")) {
- this.browser.removeTab(this.tab);
- }
-};
-
-/**
- * Opens a tab with given URI.
- *
- * @param {string} uri
- * URI to open.
- */
-BrowserObj.prototype.addTab = function(uri) {
- return this.browser.addTab(uri, true);
-};
-
-/**
- * Re-sets this BrowserObject's current tab and updates remoteness tracking.
- * If a window is provided, this BrowserObj's internal reference is updated
- * before proceeding.
- */
-BrowserObj.prototype.switchToTab = function(ind, win) {
- if (win) {
- this.window = win;
- this.setBrowser(win);
- }
-
- this.browser.selectTabAtIndex(ind);
- this.tab = this.browser.selectedTab;
- this._browserWasRemote = this.browserForTab.isRemoteBrowser;
- this._hasRemotenessChange = false;
-};
-
-/**
- * Registers a new frame, and sets its current frame id to this frame
- * if it is not already assigned, and if a) we already have a session
- * or b) we're starting a new session and it is the right start frame.
- *
- * @param {string} uid
- * Frame uid for use by Marionette.
- * @param the XUL <browser> that was the target of the originating message.
- */
-BrowserObj.prototype.register = function(uid, target) {
- let remotenessChange = this.hasRemotenessChange();
- if (this.curFrameId === null || remotenessChange) {
- if (this.browser) {
- // 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(this.browser.selectedIndex);
- }
-
- if (target == this.browserForTab) {
- this.updateIdForBrowser(this.browserForTab, uid);
- this.mainContentId = uid;
- }
- } else {
- this._curFrameId = uid;
- this.mainContentId = uid;
- }
- }
-
- // 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.
- */
-BrowserObj.prototype.hasRemotenessChange = function() {
- // None of these checks are relevant on b2g or if we don't have a tab yet,
- // and may not apply on Fennec.
- if (this.driver.appName != "Firefox" || this.tab === null) {
- return false;
- }
-
- if (this._hasRemotenessChange) {
- return true;
- }
-
- // this.tab can potentially get stale and cause problems, see bug 1227252
- let currentTab = this.browser.selectedTab;
- let currentIsRemote = this.browser.getBrowserForTab(currentTab).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.
- */
-BrowserObj.prototype.flushPendingCommands = function() {
- if (!this._hasRemotenessChange) {
- return;
- }
-
- this._hasRemotenessChange = false;
- this.pendingCommands.forEach(cb => cb());
- this.pendingCommands = [];
-};
-
-/**
- * This function intercepts commands interacting with content and queues
- * or executes them as needed.
- *
- * 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
- * are subsequently posted to the server.
- */
-BrowserObj.prototype.executeWhenReady = function(cb) {
- if (this.hasRemotenessChange()) {
- this.pendingCommands.push(cb);
- } else {
- cb();
- }
-};
--- a/testing/marionette/jar.mn
+++ b/testing/marionette/jar.mn
@@ -2,16 +2,17 @@
# 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/.
marionette.jar:
% content marionette %content/
content/server.js (server.js)
content/driver.js (driver.js)
content/action.js (action.js)
+ content/browser.js (browser.js)
content/interaction.js (interaction.js)
content/accessibility.js (accessibility.js)
content/listener.js (listener.js)
content/element.js (element.js)
content/simpletest.js (simpletest.js)
content/frame.js (frame.js)
content/event.js (event.js)
content/error.js (error.js)
--- a/testing/marionette/modal.js
+++ b/testing/marionette/modal.js
@@ -71,18 +71,18 @@ modal.removeHandler = function(toRemove)
}
}
}
};
/**
* Represents the current modal dialogue.
*
- * @param {function(): BrowserObj} curBrowserFn
- * Function that returns the current |BrowserObj|.
+ * @param {function(): browser.Context} curBrowserFn
+ * Function that returns the current |browser.Context|.
* @param {nsIWeakReference=} winRef
* A weak reference to the current |ChromeWindow|.
*/
modal.Dialog = class {
constructor(curBrowserFn, winRef = undefined) {
this.curBrowserFn_ = curBrowserFn;
this.win_ = winRef;
}