Bug 1347589: Implementation of Marionette Set Window Rect. r?ato
This implements Set Window Rect from the W3C WebDriver specification
to allow us to change the size and position of a window in one call
MozReview-Commit-ID: KxwJyyjg1VU
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -1465,18 +1465,41 @@ class Marionette(object):
"getWindowPosition", key="value" if self.protocol == 1 else None)
def set_window_position(self, x, y):
"""Set the position of the current window
:param x: x coordinate for the top left of the window
:param y: y coordinate for the top left of the window
"""
+ warnings.warn("set_window_position() has been deprecated, please use set_window_rect()",
+ DeprecationWarning)
self._send_message("setWindowPosition", {"x": x, "y": y})
+ def set_window_rect(self, x=None, y=None, height=None, width=None):
+ """Set the position and size of the current window.
+
+ The supplied width and height values refer to the window outerWidth
+ and outerHeight values, which include scroll bars, title bars, etc.
+
+ An error will be returned if the requested window size would result
+ in the window being in the maximised state.
+
+ :param x: x coordinate for the top left of the window
+ :param y: y coordinate for the top left of the window
+ :param width: The width to resize the window to.
+ :param height: The height to resize the window to.
+ """
+ if (x is None and y is None) and (height is None and width is None):
+ raise errors.InvalidArgumentException("x and y or height and width need values")
+
+ return self._send_message("setWindowRect", {"x": x, "y": y,
+ "height": height,
+ "width": width})
+
@property
def title(self):
"""Current title of the active window."""
return self._send_message("getTitle", key="value")
@property
def window_handles(self):
"""Get list of windows in the current context.
@@ -2157,26 +2180,28 @@ class Marionette(object):
:returns: dictionary representation of current window width and height
"""
return self._send_message("getWindowSize",
key="value" if self.protocol == 1 else None)
def set_window_size(self, width, height):
"""Resize the browser window currently in focus.
- The supplied width and height values refer to the window outerWidth
- and outerHeight values, which include scroll bars, title bars, etc.
+ The supplied ``width`` and ``height`` values refer to the window `outerWidth`
+ and `outerHeight` values, which include scroll bars, title bars, etc.
An error will be returned if the requested window size would result
in the window being in the maximised state.
:param width: The width to resize the window to.
:param height: The height to resize the window to.
"""
+ warnings.warn("set_window_size() has been deprecated, please use set_window_rect()",
+ DeprecationWarning)
body = {"width": width, "height": height}
return self._send_message("setWindowSize", body)
def maximize_window(self):
""" Resize the browser window currently receiving commands. The action
should be equivalent to the user pressing the the maximize button
"""
return self._send_message("maximizeWindow")
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -1257,49 +1257,82 @@ GeckoDriver.prototype.getWindowPosition
return {
x: win.screenX,
y: win.screenY,
};
};
/**
- * Set the window position of the browser on the OS Window Manager
+ * Set the window position and size of the browser on the OS Window Manager
*
+ * The supplied width and height values refer to the window outerWidth
+ * and outerHeight values, which include browser chrome and OS-level
+ * window borders.
* @param {number} x
* X coordinate of the top/left of the window that it will be
* moved to.
* @param {number} y
* Y coordinate of the top/left of the window that it will be
* moved to.
*
* @return {Object.<string, number>}
- * Object with |x| and |y| coordinates.
+ * Object with |x| and |y| coordinates
+ * and |width| and |height| dimensions
+ *
*/
-GeckoDriver.prototype.setWindowPosition = function* (cmd, resp) {
+GeckoDriver.prototype.setWindowRect = function* (cmd, resp) {
assert.firefox()
- let {x, y} = cmd.parameters;
- assert.integer(x);
- assert.integer(y);
-
- let win = this.getCurrentWindow();
- let orig = {screenX: win.screenX, screenY: win.screenY};
-
- win.moveTo(x, y);
- yield wait.until((resolve, reject) => {
- if ((x == win.screenX && y == win.screenY) ||
- (win.screenX != orig.screenX || win.screenY != orig.screenY)) {
- resolve();
- } else {
- reject();
- }
- });
-
- return this.curBrowser.position;
+ let win = assert.window(this.getCurrentWindow());
+
+ let {x, y, height, width} = cmd.parameters;
+
+ if (height != null && width != null) {
+ assert.positiveInteger(height);
+ assert.positiveInteger(width);
+ yield new Promise(resolve => {
+ // When the DOM resize event claims that it fires _after_ the document
+ // view has been resized, it is lying.
+ //
+ // Because resize events fire at a high rate, DOM modifications
+ // such as updates to outerWidth/outerHeight are not guaranteed to
+ // have processed. To overcome this... abomination... of the web
+ // platform, we throttle the event using setTimeout. If everything
+ // was well in this world we would use requestAnimationFrame, but
+ // it does not seem to like our particular flavour of XUL.
+ const fps15 = 66;
+ const synchronousResize = () => win.setTimeout(resolve, fps15);
+ win.addEventListener("resize", synchronousResize, {once: true});
+ win.resizeTo(width, height);
+ });
+ }
+
+ if (x != null && y != null) {
+ assert.integer(x);
+ assert.integer(y);
+ let orig = {screenX: win.screenX, screenY: win.screenY};
+ win.moveTo(x, y);
+ yield wait.until((resolve, reject) => {
+ if ((x == win.screenX && y == win.screenY) ||
+ (win.screenX != orig.screenX || win.screenY != orig.screenY)) {
+ resolve();
+ } else {
+ reject();
+ }
+ });
+ }
+
+ return {
+ "x": win.screenX,
+ "y": win.screenY,
+ "width": win.outerWidth,
+ "height": win.outerHeight,
+ };
+
};
/**
* Switch current top-level browsing context by name or server-assigned ID.
* Searches for windows by name, then ID. Content windows take precedence.
*
* @param {string} name
* Target name or ID of the window to switch to.
@@ -2559,59 +2592,16 @@ GeckoDriver.prototype.getWindowSize = fu
let win = assert.window(this.getCurrentWindow());
return {
width: win.outerWidth,
height: win.outerHeight,
};
};
/**
- * Set the size of the browser window currently in focus.
- *
- * The supplied width and height values refer to the window outerWidth
- * and outerHeight values, which include browser chrome and OS-level
- * window borders.
- *
- * @param {number} width
- * Requested window outer width.
- * @param {number} height
- * Requested window outer height.
- *
- * @return {Map.<string, number>}
- * New outerWidth/outerHeight dimensions.
- */
-GeckoDriver.prototype.setWindowSize = function* (cmd, resp) {
- assert.firefox()
- let win = assert.window(this.getCurrentWindow());
-
- const {width, height} = cmd.parameters;
-
- yield new Promise(resolve => {
- // When the DOM resize event claims that it fires _after_ the document
- // view has been resized, it is lying.
- //
- // Because resize events fire at a high rate, DOM modifications
- // such as updates to outerWidth/outerHeight are not guaranteed to
- // have processed. To overcome this... abomination... of the web
- // platform, we throttle the event using setTimeout. If everything
- // was well in this world we would use requestAnimationFrame, but
- // it does not seem to like our particular flavour of XUL.
- const fps15 = 66;
- const synchronousResize = () => win.setTimeout(resolve, fps15);
- win.addEventListener("resize", synchronousResize, {once: true});
- win.resizeTo(width, height);
- });
-
- return {
- width: win.outerWidth,
- height: win.outerHeight,
- };
-};
-
-/**
* Maximizes the user agent window as if the user pressed the maximise
* button.
*
* Not Supported on B2G or Fennec.
*/
GeckoDriver.prototype.maximizeWindow = function (cmd, resp) {
assert.firefox()
let win = assert.window(this.getCurrentWindow());
@@ -3021,17 +3011,18 @@ GeckoDriver.prototype.commands = {
"goForward": GeckoDriver.prototype.goForward,
"refresh": GeckoDriver.prototype.refresh,
"getWindowHandle": GeckoDriver.prototype.getWindowHandle,
"getChromeWindowHandle": GeckoDriver.prototype.getChromeWindowHandle,
"getCurrentChromeWindowHandle": GeckoDriver.prototype.getChromeWindowHandle,
"getWindowHandles": GeckoDriver.prototype.getWindowHandles,
"getChromeWindowHandles": GeckoDriver.prototype.getChromeWindowHandles,
"getWindowPosition": GeckoDriver.prototype.getWindowPosition,
- "setWindowPosition": GeckoDriver.prototype.setWindowPosition,
+ "setWindowPosition": GeckoDriver.prototype.setWindowRect, // Redirecting for compatibility
+ "setWindowRect": GeckoDriver.prototype.setWindowRect,
"getActiveFrame": GeckoDriver.prototype.getActiveFrame,
"switchToFrame": GeckoDriver.prototype.switchToFrame,
"switchToParentFrame": GeckoDriver.prototype.switchToParentFrame,
"switchToWindow": GeckoDriver.prototype.switchToWindow,
"switchToShadowRoot": GeckoDriver.prototype.switchToShadowRoot,
"deleteSession": GeckoDriver.prototype.deleteSession,
"importScript": GeckoDriver.prototype.importScript,
"clearImportedScripts": GeckoDriver.prototype.clearImportedScripts,
@@ -3043,17 +3034,17 @@ GeckoDriver.prototype.commands = {
"addCookie": GeckoDriver.prototype.addCookie,
"getCookies": GeckoDriver.prototype.getCookies,
"deleteAllCookies": GeckoDriver.prototype.deleteAllCookies,
"deleteCookie": GeckoDriver.prototype.deleteCookie,
"getActiveElement": GeckoDriver.prototype.getActiveElement,
"getScreenOrientation": GeckoDriver.prototype.getScreenOrientation,
"setScreenOrientation": GeckoDriver.prototype.setScreenOrientation,
"getWindowSize": GeckoDriver.prototype.getWindowSize,
- "setWindowSize": GeckoDriver.prototype.setWindowSize,
+ "setWindowSize": GeckoDriver.prototype.setWindowRect, // Redirecting for compatibility
"maximizeWindow": GeckoDriver.prototype.maximizeWindow,
"dismissDialog": GeckoDriver.prototype.dismissDialog,
"acceptDialog": GeckoDriver.prototype.acceptDialog,
"getTextFromDialog": GeckoDriver.prototype.getTextFromDialog,
"sendKeysToDialog": GeckoDriver.prototype.sendKeysToDialog,
"acceptConnections": GeckoDriver.prototype.acceptConnections,
"quitApplication": GeckoDriver.prototype.quit, // deprecated, can be removed in Firefox 56
"quit": GeckoDriver.prototype.quit,
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_position.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_position.py
@@ -24,16 +24,41 @@ class TestWindowPosition(MarionetteTestC
self.assertIsInstance(position["y"], int)
def test_set_types(self):
for x, y in (["a", "b"], [1.2, 3.4], [True, False], [[], []], [{}, {}]):
print("testing invalid type position ({},{})".format(x, y))
with self.assertRaises(InvalidArgumentException):
self.marionette.set_window_position(x, y)
+ def test_setting_window_rect_with_nulls_errors(self):
+ with self.assertRaises(InvalidArgumentException):
+ self.marionette.set_window_rect(height=None, width=None,
+ x=None, y=None)
+
+ def test_set_position_with_rect(self):
+ old_position = self.marionette.get_window_position()
+ wanted_position = {"x": old_position["x"] + 10, "y": old_position["y"] + 10}
+
+ new_position = self.marionette.set_window_rect(x=wanted_position["x"], y=wanted_position["y"])
+
+ self.assertNotEqual(old_position["x"], new_position["x"])
+ self.assertNotEqual(old_position["y"], new_position["y"])
+
+ def test_set_size_with_rect(self):
+ actual = self.marionette.window_size
+ width = actual["width"] - 50
+ height = actual["height"] - 50
+
+ size = self.marionette.set_window_rect(width=width, height=height)
+ self.assertEqual(size["width"], width,
+ "New width is {0} but should be {1}".format(size["width"], width))
+ self.assertEqual(size["height"], height,
+ "New height is {0} but should be {1}".format(size["height"], height))
+
def test_move_to_new_position(self):
old_position = self.marionette.get_window_position()
new_position = {"x": old_position["x"] + 10, "y": old_position["y"] + 10}
self.marionette.set_window_position(new_position["x"], new_position["y"])
self.assertNotEqual(old_position["x"], new_position["x"])
self.assertNotEqual(old_position["y"], new_position["y"])
def test_move_to_existing_position(self):