Bug 1396866 - Relax WindowRectResponse typing. r?jgraham draft
authorAndreas Tolfsen <ato@sny.no>
Sat, 09 Sep 2017 12:21:50 +0100
changeset 663136 1c4b69d415ecaab3819a9a6d1014eeaa23ada7a5
parent 663135 efc535007e5c0084fb1e08bf9901ac6f36d60e3c
child 663137 49bc0716a649b3ea629abddebc654cbc0f1be723
push id79339
push userbmo:ato@sny.no
push dateTue, 12 Sep 2017 18:05:14 +0000
reviewersjgraham
bugs1396866
milestone57.0a1
Bug 1396866 - Relax WindowRectResponse typing. r?jgraham The WindowProxy screenX/screenY/outerWidth/outerHeight fields are platform-independent longs, meaning the bitness of the system is not taken into account. This is defined in WebIDL as "a signed integer type that has values in the range [−2147483648, 2147483647]". This can be represented with an i32 internally. Additionally, the WebDriver specification says that we should accept JSON Numbers as input. This means the input value can be either a float or an integer. rustc_serialize handles this very badly and we first need to extract the input as f64 because this is the most liberal type (i.e. an integer can be coerced to a float without problem). We then we cast the number to an i64 which trims off any decimals without worrying that the number wraps. We then run bounds checks of an i32 and error if the number will not fit within the set range. This patch expects the output from Marionette to be conforming. MozReview-Commit-ID: CVdUZWh2t8X
testing/geckodriver/CHANGES.md
testing/geckodriver/src/marionette.rs
testing/web-platform/tests/webdriver/tests/contexts/maximize_window.py
testing/web-platform/tests/webdriver/tests/fullscreen_window.py
testing/web-platform/tests/webdriver/tests/minimize_window.py
testing/web-platform/tests/webdriver/tests/set_window_rect.py
testing/webdriver/src/command.rs
testing/webdriver/src/response.rs
--- a/testing/geckodriver/CHANGES.md
+++ b/testing/geckodriver/CHANGES.md
@@ -2,30 +2,24 @@
 
 All notable changes to this program is documented in this file.
 
 ## Unreleased
 
 ### Added
 - Added crashreporter environment variables to better control the browser in case of crashes.
 - Added preference `dom.file.createInChild` set to true to allow file object creation in content processes.
-- New window `state` field on the window rect response object, returned from [`GetWindowRect`], [`SetWindowRect`], [`MinimizeWindow`], [`MaximizeWindow`], and [`FullscreenWindow`] commands
-
-[`FullscreenWindow`]: https://docs.rs/webdriver/0.29.0/webdriver/command/enum.WebDriverCommand.html#variant.FullscreenWindow
-[`GetWindowRect`]: https://docs.rs/webdriver/0.29.0/webdriver/command/enum.WebDriverCommand.html#variant.GetWindowRect
-[`MaximizeWindow`]: https://docs.rs/webdriver/0.29.0/webdriver/command/enum.WebDriverCommand.html#variant.MaximizeWindow
-[`MinimizeWindow`]: https://docs.rs/webdriver/0.29.0/webdriver/command/enum.WebDriverCommand.html#variant.MinimizeWindow
-[`SetWindowRect`]: https://docs.rs/webdriver/0.29.0/webdriver/command/enum.WebDriverCommand.html#variant.SetWindowRect
 
 ### Changed
 - Removed deprecated `socksProxyVersion` in favor of `socksVersion`.
 - Removed `ftpProxyPort`, `httpProxyPort`, `sslProxyPort`, and `socksProxyPort` because _ports_ have to be set for `ftpProxy`, `httpProxy`, `sslProxy`, and `socksProxy` using ":<PORT>".
 - To make sure no browser process is left behind when the [`New Session` command](https://docs.rs/webdriver/0.27.0/webdriver/command/enum.WebDriverCommand.html#variant.NewSession) fails, the process is closed immediately now.
 - The `proxyType` `noproxy` has been replaced with `direct` in accordance with recent WebDriver specification changes
 - `/moz/addon/install` command accepts an `addon` parameter, in lieu of `path`, containing an addon as a Base64 string
+- The [`WindowRectParameters`](https://docs.rs/webdriver/0.30.0/webdriver/command/struct.WindowRectParameters.html have been updated to return signed 32-bit integers in accordance with the CSS and WebDriver specifications, and to be more liberal with the input types
 - [webdriver crate](https://crates.io/crates/webdriver) upgraded to version 0.30.0
 - [mozrunner crate](https://crates.io/crates/mozrunner) upgraded to version 0.4.2
 
 ## 0.18.0 (2017-07-10)
 
 ### Changed
 - [`RectResponse`](https://docs.rs/webdriver/0.27.0/webdriver/response/struct.RectResponse.html) permits returning floats for `width` and `height` fields
 - New type [`CookieResponse`](https://docs.rs/webdriver/0.27.0/webdriver/response/struct.CookieResponse.html) for the [`GetNamedCookie` command](https://docs.rs/webdriver/0.27.0/webdriver/command/enum.WebDriverCommand.html#variant.GetNamedCookie) returns a single cookie, as opposed to an array of a single cookie
--- a/testing/geckodriver/src/marionette.rs
+++ b/testing/geckodriver/src/marionette.rs
@@ -774,42 +774,47 @@ impl MarionetteSession {
                 let rect = ElementRectResponse { x, y, width, height };
                 WebDriverResponse::ElementRect(rect)
             },
             FullscreenWindow | MinimizeWindow | MaximizeWindow | GetWindowRect |
             SetWindowRect(_) => {
                 let width = try_opt!(
                     try_opt!(resp.result.find("width"),
                              ErrorStatus::UnknownError,
-                             "Failed to find width field").as_f64(),
+                             "Failed to find width field").as_u64(),
                     ErrorStatus::UnknownError,
-                    "Failed to interpret width as float");
+                    "Failed to interpret width as positive integer");
 
                 let height = try_opt!(
                     try_opt!(resp.result.find("height"),
                              ErrorStatus::UnknownError,
-                             "Failed to find height field").as_f64(),
+                             "Failed to find heigenht field").as_u64(),
                     ErrorStatus::UnknownError,
-                    "Failed to interpret height as float");
+                    "Failed to interpret height as positive integer");
 
                 let x = try_opt!(
                     try_opt!(resp.result.find("x"),
                              ErrorStatus::UnknownError,
-                             "Failed to find x field").as_f64(),
+                             "Failed to find x field").as_i64(),
                     ErrorStatus::UnknownError,
-                    "Failed to interpret x as float");
+                    "Failed to interpret x as integer");
 
                 let y = try_opt!(
                     try_opt!(resp.result.find("y"),
                              ErrorStatus::UnknownError,
-                             "Failed to find y field").as_f64(),
+                             "Failed to find y field").as_i64(),
                     ErrorStatus::UnknownError,
-                    "Failed to interpret y as float");
+                    "Failed to interpret y as integer");
 
-                let rect = WindowRectResponse { x, y, width, height };
+                let rect = WindowRectResponse {
+                    x: x as i32,
+                    y: y as i32,
+                    width: width as i32,
+                    height: height as i32,
+                };
                 WebDriverResponse::WindowRect(rect)
             },
             GetCookies => {
                 let cookies = try!(self.process_cookies(&resp.result));
                 WebDriverResponse::Cookies(CookiesResponse { value: cookies })
             },
             GetNamedCookie(ref name) => {
                 let mut cookies = try!(self.process_cookies(&resp.result));
--- a/testing/web-platform/tests/webdriver/tests/contexts/maximize_window.py
+++ b/testing/web-platform/tests/webdriver/tests/contexts/maximize_window.py
@@ -186,33 +186,60 @@ def test_maximize(session):
 
     response = maximize(session)
     assert_success(response)
 
     assert before_size != session.window.size
 
 
 def test_payload(session):
+    """
+    7. Return success with the JSON serialization of the current top-level
+    browsing context's window rect.
+
+    [...]
+
+    A top-level browsing context's window rect is defined as a
+    dictionary of the screenX, screenY, width and height attributes of
+    the WindowProxy. Its JSON representation is the following:
+
+    "x"
+        WindowProxy's screenX attribute.
+
+    "y"
+        WindowProxy's screenY attribute.
+
+    "width"
+        Width of the top-level browsing context's outer dimensions,
+        including any browser chrome and externally drawn window
+        decorations in CSS reference pixels.
+
+    "height"
+        Height of the top-level browsing context's outer dimensions,
+        including any browser chrome and externally drawn window
+        decorations in CSS reference pixels.
+
+    """
     before_size = session.window.size
 
     response = maximize(session)
 
     # step 5
     assert response.status == 200
     assert isinstance(response.body["value"], dict)
 
     value = response.body["value"]
     assert "width" in value
     assert "height" in value
     assert "x" in value
     assert "y" in value
-    assert isinstance(value["width"], (int, float))
-    assert isinstance(value["height"], (int, float))
-    assert isinstance(value["x"], (int, float))
-    assert isinstance(value["y"], (int, float))
+    assert isinstance(value["width"], int)
+    assert isinstance(value["height"], int)
+    assert isinstance(value["x"], int)
+    assert isinstance(value["y"], int)
 
     assert before_size != session.window.size
 
 
 def test_maximize_twice_is_idempotent(session):
     first_response = maximize(session)
     assert_success(first_response)
     max_size = session.window.size
--- a/testing/web-platform/tests/webdriver/tests/fullscreen_window.py
+++ b/testing/web-platform/tests/webdriver/tests/fullscreen_window.py
@@ -146,81 +146,59 @@ def test_fullscreen(session):
     context's active document's document element.
 
     """
     response = fullscreen(session)
     assert_success(response)
     assert session.execute_script("return window.fullScreen") is True
 
 
-# 5. Return success with the JSON serialization of the current top-level
-# browsing context's window rect.
-#
-# [...]
-#
-# A top-level browsing context's window rect is defined as a
-# dictionary of the screenX, screenY, width and height attributes of the
-# WindowProxy. Its JSON representation is the following:
-#
-#   x
-#     WindowProxy's screenX attribute.
-#
-#   y
-#     WindowProxy's screenY attribute.
-#
-#   width
-#     Width of the top-level browsing context's outer dimensions,
-#     including any browser chrome and externally drawn window
-#     decorations in CSS reference pixels.
-#
-#   height
-#     Height of the top-level browsing context's outer dimensions,
-#     including any browser chrome and externally drawn window decorations
-#     in CSS reference pixels.
-#
-#   state
-#     The top-level browsing context's window state.
-#
-# [...]
-#
-# The top-level browsing context has an associated window state which
-# describes what visibility state its OS widget window is in. It can be
-# in one of the following states:
-#
-#   "maximized"
-#     The window is maximized.
-#
-#   "minimized"
-#     The window is iconified.
-#
-#   "normal"
-#     The window is shown normally.
-#
-#   "fullscreen"
-#     The window is in full screen mode.
-#
-# If for whatever reason the top-level browsing context's OS window
-# cannot enter either of the window states, or if this concept is not
-# applicable on the current system, the default state must be normal.
 def test_payload(session):
+    """
+    5. Return success with the JSON serialization of the current top-level
+    browsing context's window rect.
+
+    [...]
+
+    A top-level browsing context's window rect is defined as a
+    dictionary of the screenX, screenY, width and height attributes of
+    the WindowProxy. Its JSON representation is the following:
+
+    "x"
+        WindowProxy's screenX attribute.
+
+    "y"
+        WindowProxy's screenY attribute.
+
+    "width"
+        Width of the top-level browsing context's outer dimensions,
+        including any browser chrome and externally drawn window
+        decorations in CSS reference pixels.
+
+    "height"
+        Height of the top-level browsing context's outer dimensions,
+        including any browser chrome and externally drawn window
+        decorations in CSS reference pixels.
+
+    """
     response = fullscreen(session)
 
     # step 5
     assert response.status == 200
     assert isinstance(response.body["value"], dict)
 
-    rect = response.body["value"]
-    assert "width" in rect
-    assert "height" in rect
-    assert "x" in rect
-    assert "y" in rect
-    assert isinstance(rect["width"], (int, float))
-    assert isinstance(rect["height"], (int, float))
-    assert isinstance(rect["x"], (int, float))
-    assert isinstance(rect["y"], (int, float))
+    value = response.body["value"]
+    assert "width" in value
+    assert "height" in value
+    assert "x" in value
+    assert "y" in value
+    assert isinstance(value["width"], int)
+    assert isinstance(value["height"], int)
+    assert isinstance(value["x"], int)
+    assert isinstance(value["y"], int)
 
 
 def test_fullscreen_twice_is_idempotent(session):
     assert session.execute_script("return window.fullScreen") is False
 
     first_response = fullscreen(session)
     assert_success(first_response)
     assert session.execute_script("return window.fullScreen") is True
--- a/testing/web-platform/tests/webdriver/tests/minimize_window.py
+++ b/testing/web-platform/tests/webdriver/tests/minimize_window.py
@@ -161,33 +161,59 @@ def test_minimize(session):
 
     response = minimize(session)
     assert_success(response)
 
     assert session.execute_script("return document.hidden")
 
 
 def test_payload(session):
+    """
+    6. Return success with the JSON serialization of the current top-level
+    browsing context's window rect.
+
+    [...]
+
+    A top-level browsing context's window rect is defined as a
+    dictionary of the screenX, screenY, width and height attributes of
+    the WindowProxy. Its JSON representation is the following:
+
+    "x"
+        WindowProxy's screenX attribute.
+
+    "y"
+        WindowProxy's screenY attribute.
+
+    "width"
+        Width of the top-level browsing context's outer dimensions,
+        including any browser chrome and externally drawn window
+        decorations in CSS reference pixels.
+
+    "height"
+        Height of the top-level browsing context's outer dimensions,
+        including any browser chrome and externally drawn window
+        decorations in CSS reference pixels.
+
+    """
     assert not session.execute_script("return document.hidden")
 
     response = minimize(session)
 
-    # step 5
     assert response.status == 200
     assert isinstance(response.body["value"], dict)
 
     value = response.body["value"]
     assert "width" in value
     assert "height" in value
     assert "x" in value
     assert "y" in value
-    assert isinstance(value["width"], (int, float))
-    assert isinstance(value["height"], (int, float))
-    assert isinstance(value["x"], (int, float))
-    assert isinstance(value["y"], (int, float))
+    assert isinstance(value["width"], int)
+    assert isinstance(value["height"], int)
+    assert isinstance(value["x"], int)
+    assert isinstance(value["y"], int)
 
     assert session.execute_script("return document.hidden")
 
 
 def test_minimize_twice_is_idempotent(session):
     assert not session.execute_script("return document.hidden")
 
     first_response = minimize(session)
--- a/testing/web-platform/tests/webdriver/tests/set_window_rect.py
+++ b/testing/web-platform/tests/webdriver/tests/set_window_rect.py
@@ -1,31 +1,33 @@
+# META: timeout=long
+
 import pytest
 
-from support.inline import inline
-from support.fixtures import create_dialog
-from support.asserts import assert_error, assert_dialog_handled, assert_success
+from tests.support.asserts import assert_error, assert_dialog_handled, assert_success
+from tests.support.fixtures import create_dialog
+from tests.support.inline import inline
 
 
 alert_doc = inline("<script>window.alert()</script>")
 
 
 def set_window_rect(session, rect):
     return session.transport.send("POST", "session/%s/window/rect" % session.session_id, rect)
 
 
 # 10.7.2 Set Window Rect
 
 
 def test_current_top_level_browsing_context_no_longer_open(session, create_window):
     """
     1. If the current top-level browsing context is no longer open,
     return error with error code no such window.
+
     """
-
     session.window_handle = create_window()
     session.close()
     response = set_window_rect(session, {})
     assert_error(response, "no such window")
 
 
 def test_handle_prompt_dismiss():
     """TODO"""
@@ -46,37 +48,36 @@ def test_handle_prompt_accept(new_sessio
       user prompt handler:
 
         [...]
 
         - accept state
            Accept the current user prompt.
 
     """
-
     _, session = new_session(
         {"alwaysMatch": {"unhandledPromptBehavior": "accept"}})
     original = session.window.rect
 
     # step 2
     create_dialog(session)("alert", text="dismiss #1", result_var="dismiss1")
-    result = set_window_rect(session, {"x": int(original["x"]),
-                                       "y": int(original["y"])})
+    result = set_window_rect(session, {"x": original["x"],
+                                       "y": original["y"]})
     assert result.status == 200
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog(session)("confirm", text="dismiss #2", result_var="dismiss2")
-    result = set_window_rect(session, {"x": int(original["x"]),
-                                       "y": int(original["y"])})
+    result = set_window_rect(session, {"x": original["x"],
+                                       "y": original["y"]})
     assert result.status == 200
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog(session)("prompt", text="dismiss #3", result_var="dismiss3")
-    result = set_window_rect(session, {"x": int(original["x"]),
-                                       "y": int(original["y"])})
+    result = set_window_rect(session, {"x": original["x"],
+                                       "y": original["y"]})
     assert_success(result)
     assert_dialog_handled(session, "dismiss #3")
 
 
 def test_handle_prompt_dismiss_and_notify():
     """TODO"""
 
 
@@ -110,32 +111,32 @@ def test_handle_prompt_missing_value(ses
 
     """
 
     original = session.window.rect
 
     # step 2
     create_dialog("alert", text="dismiss #1", result_var="dismiss1")
 
-    result = set_window_rect(session, {"x": int(original["x"]),
-                                       "y": int(original["y"])})
+    result = set_window_rect(session, {"x": original["x"],
+                                       "y": original["y"]})
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #1")
 
     create_dialog("confirm", text="dismiss #2", result_var="dismiss2")
 
-    result = set_window_rect(session, {"x": int(original["x"]),
-                                       "y": int(original["y"])})
+    result = set_window_rect(session, {"x": original["x"],
+                                       "y": original["y"]})
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #2")
 
     create_dialog("prompt", text="dismiss #3", result_var="dismiss3")
 
-    result = set_window_rect(session, {"x": int(original["x"]),
-                                       "y": int(original["y"])})
+    result = set_window_rect(session, {"x": original["x"],
+                                       "y": original["y"]})
     assert_error(result, "unexpected alert open")
     assert_dialog_handled(session, "dismiss #3")
 
 
 @pytest.mark.parametrize("rect", [
     {"width": "a"},
     {"height": "b"},
     {"width": "a", "height": "b"},
@@ -165,75 +166,75 @@ def test_handle_prompt_missing_value(ses
     {"height": {}, "width": {}},
     {"x": {}},
     {"y": {}},
     {"x": {}, "y": {}},
     {"width": {}, "height": {}, "x": {}, "y": {}},
 ])
 def test_invalid_types(session, rect):
     """
-    8. If width or height is neither null nor a Number from 0 to 2^64 -
+    8. If width or height is neither null nor a Number from 0 to 2^31 -
     1, return error with error code invalid argument.
 
-    9. If x or y is neither null nor a Number from -(263) to 263 - 1,
+    9. If x or y is neither null nor a Number from -(2^31) to 2^31 - 1,
     return error with error code invalid argument.
     """
     response = set_window_rect(session, rect)
     assert_error(response, "invalid argument")
 
 
 @pytest.mark.parametrize("rect", [
     {"width": -1},
     {"height": -2},
     {"width": -1, "height": -2},
 ])
 def test_out_of_bounds(session, rect):
     """
-    8. If width or height is neither null nor a Number from 0 to 2^64 -
+    8. If width or height is neither null nor a Number from 0 to 2^31 -
     1, return error with error code invalid argument.
 
-    9. If x or y is neither null nor a Number from -(263) to 263 - 1,
+    9. If x or y is neither null nor a Number from -(2^31) to 2^31 - 1,
     return error with error code invalid argument.
     """
     response = set_window_rect(session, rect)
     assert_error(response, "invalid argument")
 
 
 def test_width_height_floats(session):
     """
-    8. If width or height is neither null nor a Number from 0 to 2^64 -
+    8. If width or height is neither null nor a Number from 0 to 2^31 -
     1, return error with error code invalid argument.
     """
 
-    response = set_window_rect(session, {"width": 200.5, "height": 400})
+    response = set_window_rect(session, {"width": 500.5, "height": 420})
     value = assert_success(response)
-    assert value["width"] == 200.2
-    assert value["height"] == 400
+    assert value["width"] == 500
+    assert value["height"] == 420
 
-    response = set_window_rect(session, {"width": 300, "height": 450.5})
+    response = set_window_rect(session, {"width": 500, "height": 450.5})
     value = assert_success(response)
-    assert value["width"] == 300
-    assert value["height"] == 450.5
+    assert value["width"] == 500
+    assert value["height"] == 450
 
 
 def test_x_y_floats(session):
     """
-    9. If x or y is neither null nor a Number from -(263) to 263 - 1,
+    9. If x or y is neither null nor a Number from -(2^31) to 2^31 - 1,
     return error with error code invalid argument.
     """
 
-    response = set_window_rect(session, {"x": 200.5, "y": 400})
+    response = set_window_rect(session, {"x": 0.5, "y": 420})
     value = assert_success(response)
-    assert value["x"] == 200.2
-    assert value["y"] == 400
+    assert value["x"] == 0
+    assert value["y"] == 420
 
-    response = set_window_rect(session, {"x": 300, "y": 450.5})
+    response = set_window_rect(session, {"x": 100, "y": 450.5})
     value = assert_success(response)
-    assert value["x"] == 300
-    assert value["y"] == 450.5
+    assert value["x"] == 100
+    assert value["y"] == 450
 
 
 @pytest.mark.parametrize("rect", [
     {},
 
     {"width": None},
     {"height": None},
     {"width": None, "height": None},
@@ -389,32 +390,32 @@ def test_height_width_larger_than_max(se
     assert rect["width"] >= max["width"]
     assert rect["height"] >= max["height"]
 
 
 def test_height_width_as_current(session):
     original = session.window.rect
 
     # step 12
-    response = set_window_rect(session, {"width": int(original["width"]),
-                                         "height": int(original["height"])})
+    response = set_window_rect(session, {"width": original["width"],
+                                         "height": original["height"]})
 
     # step 14
     assert_success(response, {"x": original["x"],
                               "y": original["y"],
                               "width": original["width"],
                               "height": original["height"]})
 
 
 def test_x_y(session):
     original = session.window.rect
 
     # step 13
-    response = set_window_rect(session, {"x": int(original["x"]) + 10,
-                                         "y": int(original["y"]) + 10})
+    response = set_window_rect(session, {"x": original["x"] + 10,
+                                         "y": original["y"] + 10})
 
     # step 14
     assert_success(response, {"x": original["x"] + 10,
                               "y": original["y"] + 10,
                               "width": original["width"],
                               "height": original["height"]})
 
 
@@ -449,57 +450,57 @@ def test_negative_x_y(session):
         assert_success(response, {"x": -8,
                                   "y": -8,
                                   "width": original["width"],
                                   "height": original["height"]})
 
 
 def test_move_to_same_position(session):
     original_position = session.window.position
-    position = session.window.position = (int(original_position[0]), int(original_position[1]))
+    position = session.window.position = original_position
     assert position == original_position
 
 
 def test_move_to_same_x(session):
     original_x = session.window.position[0]
-    position = session.window.position = (int(original_x), 345)
+    position = session.window.position = (original_x, 345)
     assert position == (original_x, 345)
 
 
 def test_move_to_same_y(session):
     original_y = session.window.position[1]
-    position = session.window.position = (456, int(original_y))
+    position = session.window.position = (456, original_y)
     assert position == (456, original_y)
 
 
 def test_resize_to_same_size(session):
     original_size = session.window.size
-    size = session.window.size = (int(original_size[0]), int(original_size[1]))
+    size = session.window.size = original_size
     assert size == original_size
 
 
 def test_resize_to_same_width(session):
     original_width = session.window.size[0]
-    size = session.window.size = (int(original_width), 345)
+    size = session.window.size = (original_width, 345)
     assert size == (original_width, 345)
 
 
 def test_resize_to_same_height(session):
     original_height = session.window.size[1]
-    size = session.window.size = (456, int(original_height))
+    size = session.window.size = (456, original_height)
     assert size == (456, original_height)
 
 
 def test_payload(session):
     # step 14
     response = set_window_rect(session, {"x": 400, "y": 400})
 
     assert response.status == 200
     assert isinstance(response.body["value"], dict)
-    rect = response.body["value"]
-    assert "width" in rect
-    assert "height" in rect
-    assert "x" in rect
-    assert "y" in rect
-    assert isinstance(rect["width"], (int, float))
-    assert isinstance(rect["height"], (int, float))
-    assert isinstance(rect["x"], (int, float))
-    assert isinstance(rect["y"], (int, float))
+    value = response.body["value"]
+    assert "width" in value
+    assert "height" in value
+    assert "x" in value
+    assert "y" in value
+    assert isinstance(value["width"], int)
+    assert isinstance(value["height"], int)
+    assert isinstance(value["x"], int)
+    assert isinstance(value["y"], int)
--- a/testing/webdriver/src/command.rs
+++ b/testing/webdriver/src/command.rs
@@ -578,77 +578,112 @@ impl ToJson for TimeoutsParameters {
         }
         if let Some(ms) = self.implicit {
             data.insert("implicit".into(), ms.to_json());
         }
         Json::Object(data)
     }
 }
 
+/// A top-level browsing context’s window rect is a dictionary of the
+/// [`screenX`], [`screenY`], `width`, and `height` attributes of the
+/// `WindowProxy`.
+///
+/// In some user agents the operating system’s window dimensions, including
+/// decorations, are provided by the proprietary `window.outerWidth` and
+/// `window.outerHeight` DOM properties.
+///
+/// [`screenX`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screenx
+/// [`screenY`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screeny
 #[derive(Debug, PartialEq)]
 pub struct WindowRectParameters {
-    pub x: Nullable<i64>,
-    pub y: Nullable<i64>,
-    pub width: Nullable<u64>,
-    pub height: Nullable<u64>,
+    pub x: Nullable<i32>,
+    pub y: Nullable<i32>,
+    pub width: Nullable<i32>,
+    pub height: Nullable<i32>,
 }
 
 impl Parameters for WindowRectParameters {
     fn from_json(body: &Json) -> WebDriverResult<WindowRectParameters> {
         let data = try_opt!(body.as_object(),
-                            ErrorStatus::UnknownError,
-                            "Message body was not an object");
+            ErrorStatus::InvalidArgument, "Message body was not an object");
 
         let x = match data.get("x") {
-            Some(json) => {
-                try!(Nullable::from_json(json, |n| {
-                    Ok((try_opt!(n.as_i64(),
-                                 ErrorStatus::InvalidArgument,
-                                 "'x' is not an integer")))
-                }))
-            }
+            Some(json) => try!(Nullable::from_json(json, |n| {
+                let x = try_opt!(
+                    n.as_f64(),
+                    ErrorStatus::InvalidArgument,
+                    "'x' is not a number"
+                ) as i64;
+                if x < i32::min_value() as i64 || x > i32::max_value() as i64 {
+                    return Err(WebDriverError::new(
+                        ErrorStatus::InvalidArgument,
+                        "'x' is larger than i32",
+                    ));
+                }
+                Ok(x as i32)
+            })),
             None => Nullable::Null,
         };
+
         let y = match data.get("y") {
-            Some(json) => {
-                try!(Nullable::from_json(json, |n| {
-                    Ok((try_opt!(n.as_i64(),
-                                 ErrorStatus::InvalidArgument,
-                                 "'y' is not an integer")))
-                }))
-            }
-            None => Nullable::Null,
-        };
-        let width = match data.get("width") {
-            Some(json) => {
-                try!(Nullable::from_json(json, |n| {
-                    Ok((try_opt!(n.as_u64(),
-                                 ErrorStatus::InvalidArgument,
-                                 "'width' is not a positive integer")))
-                }))
-            }
-            None => Nullable::Null,
-        };
-        let height = match data.get("height") {
-            Some(json) => {
-                try!(Nullable::from_json(json, |n| {
-                    Ok((try_opt!(n.as_u64(),
-                                 ErrorStatus::InvalidArgument,
-                                 "'height' is not a positive integer")))
-                }))
-            }
+            Some(json) => try!(Nullable::from_json(json, |n| {
+                let y = try_opt!(
+                    n.as_f64(),
+                    ErrorStatus::InvalidArgument,
+                    "'y' is not a number"
+                ) as i64;
+                if y < i32::min_value() as i64 || y > i32::max_value() as i64 {
+                    return Err(WebDriverError::new(
+                        ErrorStatus::InvalidArgument,
+                        "'y' is larger than i32",
+                    ));
+                }
+                Ok(y as i32)
+            })),
             None => Nullable::Null,
         };
 
-        Ok(WindowRectParameters {
-               x: x,
-               y: y,
-               width: width,
-               height: height,
-           })
+        let width = match data.get("width") {
+            Some(json) => try!(Nullable::from_json(json, |n| {
+                let width = try_opt!(
+                    n.as_f64(),
+                    ErrorStatus::InvalidArgument,
+                    "'width' is not a number"
+                ) as i64;
+                if width < 0 || width > i32::max_value() as i64 {
+                    return Err(WebDriverError::new(
+                        ErrorStatus::InvalidArgument,
+                        "'width' is larger than i32",
+                    ));
+                }
+                Ok(width as i32)
+            })),
+            None => Nullable::Null,
+        };
+
+        let height = match data.get("height") {
+            Some(json) => try!(Nullable::from_json(json, |n| {
+                let height = try_opt!(
+                    n.as_f64(),
+                    ErrorStatus::InvalidArgument,
+                    "'height' is not a positive integer"
+                ) as i64;
+                if height < 0 || height > i32::max_value() as i64 {
+                    return Err(WebDriverError::new(
+                        ErrorStatus::InvalidArgument,
+                        "'height' is larger than i32",
+                    ));
+                }
+                Ok(height as i32)
+            })),
+            None => Nullable::Null,
+        };
+
+        Ok(WindowRectParameters { x, y, width, height })
     }
 }
 
 impl ToJson for WindowRectParameters {
     fn to_json(&self) -> Json {
         let mut data = BTreeMap::new();
         data.insert("x".to_string(), self.x.to_json());
         data.insert("y".to_string(), self.y.to_json());
@@ -1703,41 +1738,53 @@ impl ToJson for PointerMoveAction {
 #[cfg(test)]
 mod tests {
     use rustc_serialize::json::Json;
     use super::{Nullable, Parameters, WindowRectParameters};
 
     #[test]
     fn test_window_rect() {
         let expected = WindowRectParameters {
-            x: Nullable::Value(0i64),
-            y: Nullable::Value(1i64),
-            width: Nullable::Value(2u64),
-            height: Nullable::Value(3u64),
+            x: Nullable::Value(0i32),
+            y: Nullable::Value(1i32),
+            width: Nullable::Value(2i32),
+            height: Nullable::Value(3i32),
         };
         let actual = Json::from_str(r#"{"x": 0, "y": 1, "width": 2, "height": 3}"#).unwrap();
         assert_eq!(expected, Parameters::from_json(&actual).unwrap());
     }
 
     #[test]
     fn test_window_rect_nullable() {
         let expected = WindowRectParameters {
-            x: Nullable::Value(0i64),
+            x: Nullable::Value(0i32),
             y: Nullable::Null,
-            width: Nullable::Value(2u64),
+            width: Nullable::Value(2i32),
             height: Nullable::Null,
         };
         let actual = Json::from_str(r#"{"x": 0, "y": null, "width": 2, "height": null}"#).unwrap();
         assert_eq!(expected, Parameters::from_json(&actual).unwrap());
     }
 
     #[test]
     fn test_window_rect_missing_fields() {
         let expected = WindowRectParameters {
-            x: Nullable::Value(0i64),
+            x: Nullable::Value(0i32),
             y: Nullable::Null,
-            width: Nullable::Value(2u64),
+            width: Nullable::Value(2i32),
             height: Nullable::Null,
         };
         let actual = Json::from_str(r#"{"x": 0, "width": 2}"#).unwrap();
         assert_eq!(expected, Parameters::from_json(&actual).unwrap());
     }
+
+    #[test]
+    fn test_window_rect_floats() {
+        let expected = WindowRectParameters {
+            x: Nullable::Value(1i32),
+            y: Nullable::Value(2i32),
+            width: Nullable::Value(3i32),
+            height: Nullable::Value(4i32),
+        };
+        let actual = Json::from_str(r#"{"x": 1.1, "y": 2.2, "width": 3.3, "height": 4.4}"#).unwrap();
+        assert_eq!(expected, Parameters::from_json(&actual).unwrap());
+    }
 }
--- a/testing/webdriver/src/response.rs
+++ b/testing/webdriver/src/response.rs
@@ -138,32 +138,32 @@ pub struct ElementRectResponse {
     pub height: f64,
 }
 
 #[derive(Debug)]
 pub struct WindowRectResponse {
     /// `WindowProxy`’s [screenX] attribute.
     ///
     /// [screenX]: https://drafts.csswg.org/cssom-view/#dom-window-screenx
-    pub x: f64,
+    pub x: i32,
 
     /// `WindowProxy`’s [screenY] attribute.
     ///
     /// [screenY]: https://drafts.csswg.org/cssom-view/#dom-window-screeny
-    pub y: f64,
+    pub y: i32,
 
     /// Width of the top-level browsing context’s outer dimensions, including
     /// any browser chrome and externally drawn window decorations in CSS
     /// reference pixels.
-    pub width: f64,
+    pub width: i32,
 
     /// Height of the top-level browsing context’s outer dimensions, including
     /// any browser chrome and externally drawn window decorations in CSS
     /// reference pixels.
-    pub height: f64,
+    pub height: i32,
 }
 
 impl ToJson for WindowRectResponse {
     fn to_json(&self) -> Json {
         let mut body = BTreeMap::new();
         body.insert("x".to_owned(), self.x.to_json());
         body.insert("y".to_owned(), self.y.to_json());
         body.insert("width".to_owned(), self.width.to_json());
@@ -286,23 +286,23 @@ mod tests {
         let resp = WebDriverResponse::ElementRect(rect);
         let expected = r#"{"value": {"x": 0.0, "y": 1.0, "width": 2.0, "height": 3.0}}"#;
         test(resp, expected);
     }
 
     #[test]
     fn test_window_rect() {
         let rect = WindowRectResponse {
-            x: 0f64,
-            y: 1f64,
-            width: 2f64,
-            height: 3f64,
+            x: 0i32,
+            y: 1i32,
+            width: 2i32,
+            height: 3i32,
         };
         let resp = WebDriverResponse::WindowRect(rect);
-        let expected = r#"{"value": {"x": 0.0, "y": 1.0, "width": 2.0, "height": 3.0}}"#;
+        let expected = r#"{"value": {"x": 0, "y": 1, "width": 2, "height": 3}}"#;
         test(resp, expected);
     }
 
     #[test]
     fn test_new_session() {
         let resp = WebDriverResponse::NewSession(
             NewSessionResponse::new("test".into(),
                                     Json::Object(BTreeMap::new())));