diff --git a/mobile/chrome/tests/Makefile.in b/mobile/chrome/tests/Makefile.in
index 382df88a0c77..5af0d4c74463 100644
--- a/mobile/chrome/tests/Makefile.in
+++ b/mobile/chrome/tests/Makefile.in
@@ -68,6 +68,8 @@ _BROWSER_FILES = \
browser_select.js \
browser_sessionstore.js \
browser_tabs.js \
+ browser_tapping.js \
+ browser_tap_content.html \
browser_viewport_00.html \
browser_viewport_01.html \
browser_viewport_02.html \
diff --git a/mobile/chrome/tests/browser_tap_content.html b/mobile/chrome/tests/browser_tap_content.html
new file mode 100644
index 000000000000..95fe8d3c5063
--- /dev/null
+++ b/mobile/chrome/tests/browser_tap_content.html
@@ -0,0 +1,22 @@
+
+
Browser Tap Page 01
+
+ Here are some images and links. We use them to test context menu (long taps)
+
+
+
+
+
A plain image
+
+
+
+
+
A nested image inside a link
+
+
+
+
+
diff --git a/mobile/chrome/tests/browser_tapping.js b/mobile/chrome/tests/browser_tapping.js
new file mode 100644
index 000000000000..1432bc3784bc
--- /dev/null
+++ b/mobile/chrome/tests/browser_tapping.js
@@ -0,0 +1,276 @@
+/*
+ * Testing the tapping interactions:
+ * single tap, double tap & long tap
+ */
+
+let testURL = "chrome://mochikit/content/browser/mobile/chrome/browser_tap_content.html";
+
+let gTests = [];
+let gCurrentTest = null;
+let gCurrentTab;
+
+let gEvents = [];
+function dumpEvents(aEvent) {
+ gEvents.push(aEvent.type);
+}
+
+function clearEvents() {
+ gEvents = [];
+}
+
+function checkEvents(aEvents) {
+ if (aEvents.length != gEvents.length) {
+ dump("---- event check: failed length (" + aEvents.length + " != " + gEvents.length + ")\n");
+ dump("---- expected: [" + aEvents.join(",") + "] actual: [" + gEvents.join(",") + "]\n");
+ return false;
+ }
+
+ for (let i=0; i 0) {
+ gCurrentTest = gTests.shift();
+ info(gCurrentTest.desc);
+ gCurrentTest.run();
+ }
+ else {
+ window.removeEventListener("TapSingle", dumpEvents, true);
+ window.removeEventListener("TapDouble", dumpEvents, true);
+ window.removeEventListener("TapLong", dumpEvents, true);
+
+ Browser.closeTab(gCurrentTab);
+
+ finish();
+ }
+}
+
+//------------------------------------------------------------------------------
+// Case: Test the double tap behavior
+gTests.push({
+ desc: "Test the double tap behavior",
+
+ run: function() {
+ let browser = gCurrentTab.browser;
+ let width = browser.getBoundingClientRect().width;
+ let height = browser.getBoundingClientRect().height;
+
+ // Should fire "TapSingle"
+ // XXX not working? WTF?
+ info("Test good single tap");
+ clearEvents();
+ EventUtils.synthesizeMouse(browser, width / 2, height / 2, {});
+ todo(checkEvents(["TapSingle"]), "Fired a good single tap");
+ clearEvents();
+
+ setTimeout(function() { gCurrentTest.doubleTapTest(); }, 500);
+ },
+
+ doubleTapTest: function() {
+ let browser = gCurrentTab.browser;
+ let width = browser.getBoundingClientRect().width;
+ let height = browser.getBoundingClientRect().height;
+
+ // Should fire "TapDouble"
+ info("Test good double tap");
+ clearEvents();
+ EventUtils.synthesizeMouse(browser, width / 2, height / 2, {});
+ EventUtils.synthesizeMouse(browser, width / 2, height / 2, {});
+ ok(checkEvents(["TapDouble"]), "Fired a good double tap");
+ clearEvents();
+
+ setTimeout(function() { gCurrentTest.doubleTapFailTest(); }, 500);
+ },
+
+ doubleTapFailTest: function() {
+ let browser = gCurrentTab.browser;
+ let width = browser.getBoundingClientRect().width;
+ let height = browser.getBoundingClientRect().height;
+
+ // Should fire "TapSingle", "TapSingle"
+ info("Test two single taps in different locations");
+ clearEvents();
+ EventUtils.synthesizeMouse(browser, width / 4, height / 4, {});
+ EventUtils.synthesizeMouse(browser, width * 3 / 4, height * 3 / 4, {});
+ ok(checkEvents(["TapSingle", "TapSingle"]), "Fired two single taps in different places, not a double tap");
+ clearEvents();
+
+ setTimeout(function() { gCurrentTest.tapPanTest(); }, 500);
+ },
+
+ tapPanTest: function() {
+ let browser = gCurrentTab.browser;
+ let width = browser.getBoundingClientRect().width;
+ let height = browser.getBoundingClientRect().height;
+
+ info("Test a pan - non-tap event");
+ clearEvents();
+ EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" });
+ EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mousemove" });
+ EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" });
+ ok(checkEvents([]), "Fired a pan which should be seen as a non event");
+ clearEvents();
+
+ setTimeout(function() { gCurrentTest.longTapFailTest(); }, 500);
+ },
+
+ longTapFailTest: function() {
+ let browser = gCurrentTab.browser;
+ let width = browser.getBoundingClientRect().width;
+ let height = browser.getBoundingClientRect().height;
+
+ info("Test a long pan - non-tap event");
+ clearEvents();
+ EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" });
+ EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mousemove" });
+ setTimeout(function() {
+ EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" });
+ ok(checkEvents([]), "Fired a pan + delay which should be seen as a non-event");
+ clearEvents();
+
+ gCurrentTest.longTapPassTest();
+ }, 500);
+ },
+
+ longTapPassTest: function() {
+ let browser = gCurrentTab.browser;
+ let width = browser.getBoundingClientRect().width;
+ let height = browser.getBoundingClientRect().height;
+
+ info("Test a good long pan");
+ clearEvents();
+ EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mousedown" });
+ setTimeout(function() {
+ EventUtils.synthesizeMouse(browser, width / 2, height / 4, { type: "mouseup" });
+ ok(checkEvents(["TapLong"]), "Fired a good long tap");
+ clearEvents();
+
+ gCurrentTest.contextPlainLinkTest();
+ }, 500);
+ },
+
+ contextPlainLinkTest: function() {
+ let browser = gCurrentTab.browser;
+ browser.messageManager.addMessageListener("Browser:ContextMenu", dumpMessages);
+
+ let link = browser.contentDocument.getElementById("link-single");
+ let linkRect = link.getBoundingClientRect();
+
+ clearContextTypes();
+ EventUtils.synthesizeMouseForContent(link, 1, 1, { type: "mousedown" }, window);
+ setTimeout(function() {
+ EventUtils.synthesizeMouseForContent(link, 1, 1, { type: "mouseup" }, window);
+ ok(checkContextTypes(["link","link-saveable"]), "Plain link context types");
+ clearContextTypes();
+
+ gCurrentTest.contextPlainImageTest();
+ }, 500);
+ },
+
+ contextPlainImageTest: function() {
+ let browser = gCurrentTab.browser;
+ browser.messageManager.addMessageListener("Browser:ContextMenu", dumpMessages);
+
+ let img = browser.contentDocument.getElementById("img-single");
+ let imgRect = img.getBoundingClientRect();
+
+ clearContextTypes();
+ EventUtils.synthesizeMouseForContent(img, 1, 1, { type: "mousedown" }, window);
+ setTimeout(function() {
+ EventUtils.synthesizeMouseForContent(img, 1, 1, { type: "mouseup" }, window);
+ ok(checkContextTypes(["image","image-shareable","image-loaded"]), "Plain image context types");
+ clearContextTypes();
+
+ gCurrentTest.contextNestedImageTest();
+ }, 500);
+ },
+
+ contextNestedImageTest: function() {
+ let browser = gCurrentTab.browser;
+ browser.messageManager.addMessageListener("Browser:ContextMenu", dumpMessages);
+
+ let img = browser.contentDocument.getElementById("img-nested");
+ let imgRect = img.getBoundingClientRect();
+
+ clearContextTypes();
+ EventUtils.synthesizeMouseForContent(img, 1, 1, { type: "mousedown" }, window);
+ setTimeout(function() {
+ EventUtils.synthesizeMouseForContent(img, 1, 1, { type: "mouseup" }, window);
+ ok(checkContextTypes(["link","link-saveable","image","image-shareable","image-loaded"]), "Nested image context types");
+ clearContextTypes();
+
+ gCurrentTest.lastTest();
+ }, 500);
+ },
+
+ lastTest: function() {
+ gCurrentTab.browser.messageManager.removeMessageListener("Browser:ContextMenu", dumpMessages);
+
+ runNextTest();
+ }
+});
+
diff --git a/mobile/chrome/tests/head.js b/mobile/chrome/tests/head.js
index c16f12c4632d..18d9505aab43 100644
--- a/mobile/chrome/tests/head.js
+++ b/mobile/chrome/tests/head.js
@@ -1,6 +1,9 @@
/*=============================================================================
Common Helpers functions
=============================================================================*/
+
+// Wait for a condition and call a supplied callback if condition is met within
+// alloted time. If condition is not met, cause a hard failure, stopping the test.
function waitFor(callback, test, timeout) {
if (test()) {
callback();
@@ -13,6 +16,21 @@ function waitFor(callback, test, timeout) {
setTimeout(waitFor, 50, callback, test, timeout);
};
+// Wait for a condition and call a supplied callback if condition is met within
+// alloted time. If condition is not met, continue anyway. Use this helper if the
+// callback will test for the outcome, but not stop the entire test.
+function waitForAndContinue(callback, test, timeout) {
+ if (test()) {
+ callback();
+ return;
+ }
+
+ timeout = timeout || Date.now();
+ if (Date.now() - timeout > 1000)
+ callback();
+ setTimeout(waitFor, 50, callback, test, timeout);
+};
+
function makeURI(spec) {
return Services.io.newURI(spec, null, null);
};