Bug 945765 - Add some basic front end apzc tests to catch regressions. r=mbrubeck a=lsblakk
authorJim Mathies <jmathies@mozilla.com>
Sat, 14 Dec 2013 14:40:56 -0600
changeset 175381 cb28a42ecacfa500ca92a6c54fff60c0de618f0f
parent 175380 f4bc4144c7e4f0517966ee5018d9f68f6a2e1325
child 175382 2ebab851cb335cd3502cdfaaf6f498f40000c485
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmbrubeck, lsblakk
bugs945765
milestone28.0a2
Bug 945765 - Add some basic front end apzc tests to catch regressions. r=mbrubeck a=lsblakk
browser/metro/base/tests/mochitest/browser_apzc_basic.js
browser/metro/base/tests/mochitest/head.js
browser/metro/base/tests/mochitest/metro.ini
browser/metro/base/tests/mochitest/res/textdivs01.html
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/browser_apzc_basic.js
@@ -0,0 +1,117 @@
+// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function test() {
+  if (!isLandscapeMode()) {
+    todo(false, "browser_snapped_tests need landscape mode to run.");
+    return;
+  }
+
+  runTests();
+}
+
+let kTransformTimeout = 5000;
+
+let gEdit = null;
+let tabAdded = false;
+
+function setUp() {
+  if (!tabAdded) {
+    yield addTab(chromeRoot + "res/textdivs01.html");
+    tabAdded = true;
+  }
+  yield hideContextUI();
+}
+
+/*
+gTests.push({
+  desc: "soft keyboard reliability",
+  setUp: setUp,
+  run: function() {
+    yield waitForMs(3000);
+
+    let edit = Browser.selectedBrowser.contentDocument.getElementById("textinput");
+    // show the soft keyboard
+    let keyboardPromise = waitForObserver("metro_softkeyboard_shown", 20000);
+    sendNativeTap(edit);
+    yield waitForMs(5000);
+    sendNativeTap(edit);
+    yield keyboardPromise;
+    yield waitForMs(5000);
+
+    // hide the soft keyboard / navbar
+    keyboardPromise = waitForObserver("metro_softkeyboard_hidden", 20000);
+    sendNativeTap(Browser.selectedBrowser.contentDocument.getElementById("first"));
+    yield keyboardPromise;
+    yield waitForMs(5000);
+  },
+  tearDown: function () {
+    clearNativeTouchSequence();
+  }
+});
+*/
+
+gTests.push({
+  desc: "native long tap works",
+  setUp: setUp,
+  run: function() {
+    let edit = Browser.selectedBrowser.contentDocument.getElementById("textinput");
+    let promise = waitForEvent(document, "popupshown");
+    sendNativeLongTap(edit);
+    yield promise;
+    ContextMenuUI.hide();
+  },
+  tearDown: function () {
+    clearNativeTouchSequence();
+  }
+});
+
+gTests.push({
+  desc: "double tap transforms",
+  setUp: setUp,
+  run: function() {
+    let beginPromise = waitForObserver("apzc-transform-begin", kTransformTimeout);
+    let endPromise = waitForObserver("apzc-transform-end", kTransformTimeout);
+
+    sendNativeDoubleTap(Browser.selectedBrowser.contentDocument.getElementById("second"));
+
+    yield beginPromise;
+    yield endPromise;
+
+    beginPromise = waitForObserver("apzc-transform-begin", kTransformTimeout);
+    endPromise = waitForObserver("apzc-transform-end", kTransformTimeout);
+
+    sendNativeDoubleTap(Browser.selectedBrowser.contentDocument.getElementById("second"));
+
+    yield beginPromise;
+    yield endPromise;
+  },
+  tearDown: function () {
+    clearNativeTouchSequence();
+  }
+});
+
+gTests.push({
+  desc: "scroll transforms",
+  setUp: setUp,
+  run: function() {
+    let beginPromise = waitForObserver("apzc-transform-begin", kTransformTimeout);
+    let endPromise = waitForObserver("apzc-transform-end", kTransformTimeout);
+
+    var touchdrag = new TouchDragAndHold();
+    touchdrag.useNativeEvents = true;
+    touchdrag.nativePointerId = 1;
+    yield touchdrag.start(Browser.selectedTab.browser.contentWindow,
+                          10, 100, 10, 10);
+    touchdrag.end();
+
+    yield beginPromise;
+    yield endPromise;
+  },
+  tearDown: function () {
+    clearNativeTouchSequence();
+  }
+});
--- a/browser/metro/base/tests/mochitest/head.js
+++ b/browser/metro/base/tests/mochitest/head.js
@@ -552,18 +552,21 @@ function waitForObserver(aObsEvent, aTim
   return deferred.promise;
 
   } catch (ex) {
     info(ex.message);
   }
 }
 
 /*=============================================================================
-  Native input synthesis helpers
-=============================================================================*/
+ * Native input helpers - these helpers send input directly to the os
+ * generating os level input events that get processed by widget and
+ * apzc logic.
+ *===========================================================================*/
+
 // Keyboard layouts for use with synthesizeNativeKey
 const usEnglish = 0x409;
 const arSpanish = 0x2C0A;
 
 // Modifiers for use with synthesizeNativeKey
 const leftShift = 0x100;
 const rightShift = 0x200;
 const leftControl = 0x400;
@@ -635,16 +638,46 @@ function synthesizeNativeMouseMDown(aEle
 
 function synthesizeNativeMouseMUp(aElement, aOffsetX, aOffsetY) {
   synthesizeNativeMouse(aElement,
                         aOffsetX,
                         aOffsetY,
                         0x0040);  // MOUSEEVENTF_MIDDLEUP
 }
 
+// WARNING: these calls can trigger the soft keyboard on tablets, but not
+// on test slaves (bug 947428).
+// WARNING: When testing the apzc, be careful of bug 933990. Events sent
+// shortly after loading a page may get ignored.
+
+function sendNativeLongTap(aElement, aX, aY) {
+  let coords = logicalCoordsForElement(aElement, aX, aY);
+  Browser.windowUtils.sendNativeTouchTap(coords.x, coords.y, true);
+}
+
+function sendNativeTap(aElement, aX, aY) {
+  let coords = logicalCoordsForElement(aElement, aX, aY);
+  Browser.windowUtils.sendNativeTouchTap(coords.x, coords.y, false);
+}
+
+function sendNativeDoubleTap(aElement, aX, aY) {
+  let coords = logicalCoordsForElement(aElement, aX, aY);
+  Browser.windowUtils.sendNativeTouchTap(coords.x, coords.y, false);
+  Browser.windowUtils.sendNativeTouchTap(coords.x, coords.y, false);
+}
+
+function clearNativeTouchSequence() {
+  Browser.windowUtils.clearNativeTouchSequence();
+}
+
+/*=============================================================================
+ * Synthesized event helpers - these helpers synthesize input events that get
+ * dispatched directly to the dom. As such widget and apzc logic is bypassed.
+ *===========================================================================*/
+
 /*
  * logicalCoordsForElement - given coordinates relative to top-left of
  * given element, returns logical coordinates for window. If a non-numeric
  * X or Y value is given, a value for the center of the element in that
  * dimension is used.
  *
  * @param aElement element coordinates are relative to.
  * @param aX, aY relative coordinates.
@@ -779,53 +812,85 @@ function sendTouchDrag(aWindow, aStartX,
 function TouchDragAndHold() {
 }
 
 TouchDragAndHold.prototype = {
   _timeoutStep: 2,
   _numSteps: 50,
   _debug: false,
   _win: null,
+  _native: false,
+  _pointerId: 1,
+  _dui: Components.interfaces.nsIDOMWindowUtils,
+
+  set useNativeEvents(aValue) {
+    this._native = aValue;
+  },
+
+  set nativePointerId(aValue) {
+    this._pointerId = aValue;
+  },
 
   callback: function callback() {
     if (this._win == null)
       return;
 
     if (this._debug) {
       SelectionHelperUI.debugDisplayDebugPoint(this._currentPoint.xPos,
         this._currentPoint.yPos, 5, "#FF0000", true);
     }
 
     if (++this._step.steps >= this._numSteps) {
-      EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos,
-                                        { type: "touchmove" }, this._win);
+      if (this._native) {
+        this._utils.sendNativeTouchPoint(this._pointerId, this._dui.TOUCH_CONTACT,
+                                         this._endPoint.xPos, this._endPoint.yPos,
+                                         1, 90);
+      } else {
+        EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos,
+                                          { type: "touchmove" }, this._win);
+      }
       this._defer.resolve();
       return;
     }
     this._currentPoint.xPos += this._step.x;
     this._currentPoint.yPos += this._step.y;
     if (this._debug) {
       info("[" + this._step.steps + "] touchmove " + this._currentPoint.xPos + " x " + this._currentPoint.yPos);
     }
-    EventUtils.synthesizeTouchAtPoint(this._currentPoint.xPos, this._currentPoint.yPos,
-                                      { type: "touchmove" }, this._win);
+
+    if (this._native) {
+      this._utils.sendNativeTouchPoint(this._pointerId, this._dui.TOUCH_CONTACT,
+                                       this._currentPoint.xPos, this._currentPoint.yPos,
+                                       1, 90);
+    } else {
+      EventUtils.synthesizeTouchAtPoint(this._currentPoint.xPos, this._currentPoint.yPos,
+                                        { type: "touchmove" }, this._win);
+    }
+
     let self = this;
     setTimeout(function () { self.callback(); }, this._timeoutStep);
   },
 
   start: function start(aWindow, aStartX, aStartY, aEndX, aEndY) {
     this._defer = Promise.defer();
     this._win = aWindow;
+    this._utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIDOMWindowUtils);
     this._endPoint = { xPos: aEndX, yPos: aEndY };
     this._currentPoint = { xPos: aStartX, yPos: aStartY };
     this._step = { steps: 0, x: (aEndX - aStartX) / this._numSteps, y: (aEndY - aStartY) / this._numSteps };
     if (this._debug) {
       info("[0] touchstart " + aStartX + " x " + aStartY);
     }
-    EventUtils.synthesizeTouchAtPoint(aStartX, aStartY, { type: "touchstart" }, aWindow);
+    if (this._native) {
+      this._utils.sendNativeTouchPoint(this._pointerId, this._dui.TOUCH_CONTACT,
+                                       aStartX, aStartY, 1, 90);
+    } else {
+      EventUtils.synthesizeTouchAtPoint(aStartX, aStartY, { type: "touchstart" }, aWindow);
+    }
     let self = this;
     setTimeout(function () { self.callback(); }, this._timeoutStep);
     return this._defer.promise;
   },
 
   move: function move(aEndX, aEndY) {
     if (this._win == null)
       return;
@@ -842,18 +907,24 @@ TouchDragAndHold.prototype = {
     return this._defer.promise;
   },
 
   end: function start() {
     if (this._debug) {
       info("[" + this._step.steps + "] touchend " + this._endPoint.xPos + " x " + this._endPoint.yPos);
       SelectionHelperUI.debugClearDebugPoints();
     }
-    EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos,
-                                      { type: "touchend" }, this._win);
+    if (this._native) {
+      this._utils.sendNativeTouchPoint(this._pointerId, this._dui.TOUCH_REMOVE,
+                                       this._endPoint.xPos, this._endPoint.yPos,
+                                       1, 90);
+    } else {
+      EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos,
+                                        { type: "touchend" }, this._win);
+    }
     this._win = null;
   },
 };
 
 /*=============================================================================
   System utilities
 =============================================================================*/
 
--- a/browser/metro/base/tests/mochitest/metro.ini
+++ b/browser/metro/base/tests/mochitest/metro.ini
@@ -19,16 +19,17 @@ support-files =
   browser_selection_textarea.html
   browser_tilegrid.xul
   head.js
   helpers/BookmarksHelper.js
   helpers/HistoryHelper.js
   helpers/ViewStateHelper.js
   res/image01.png
   res/textblock01.html
+  res/textdivs01.html
   res/textinput01.html
   res/textarea01.html
   res/testEngine.xml
   res/blankpage1.html
   res/blankpage2.html
   res/blankpage3.html
 
 [browser_bookmarks.js]
@@ -50,16 +51,17 @@ support-files =
 [browser_snappedState.js]
 [browser_tabs.js]
 [browser_test.js]
 [browser_tiles.js]
 [browser_topsites.js]
 [browser_urlbar.js]
 [browser_urlbar_highlightURLs.js]
 [browser_urlbar_trimURLs.js]
+[browser_apzc_basic.js]
 
 # These tests have known failures in debug builds
 [browser_selection_basic.js]
 skip-if = debug
 [browser_selection_textarea.js]
 skip-if = debug
 [browser_selection_frame_content.js]
 skip-if = debug
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/res/textdivs01.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8" />
+<style>
+div
+{
+  padding: 25px;
+  width: 300px;
+  display: block;
+}
+</style>
+</head>
+<body>
+<br />
+<input id="textinput" style="width:100px; height:25px;" value="The rabbit-hole went straight on like a tunnel for some way and then dipped suddenly down" type="text">
+<div id="first">
+    Alice was beginning to get very tired of sitting by her sister on the bank, and of having
+    nothing to do: once or twice she had peeped into the book her sister was reading 
+    but it had no pictures or conversations in it, `and what is the use of a book,' thought
+    Alice `without pictures or conversation?'
+</div>
+<div id="second">
+    Alice was beginning to get very tired of sitting by her sister on the bank, and of having
+    nothing to do: once or twice she had peeped into the book her sister was reading, but it
+    had no pictures or conversations in it, `and what is the use of a book,' thought Alice
+    `without pictures or conversation?'
+</div>
+<div id="third">
+    So she was considering in her own mind (as well as she could, for the hot day made her
+    feel very sleepy and stupid), whether the pleasure of making a daisy-chain would be worth
+    the trouble of getting up and picking the daisies, when suddenly a White Rabbit with pink
+    eyes ran close by her.
+</div>
+<div style="height:1000px;"></div>
+</body></html>
\ No newline at end of file