2013-02-12 12:51:25 -08:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
|
|
/* Any copyright is dedicated to the Public Domain.
|
|
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
|
|
|
|
/*=============================================================================
|
|
|
|
Globals
|
|
|
|
=============================================================================*/
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/commonjs/sdk/core/promise.js");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
|
|
|
|
|
|
|
|
/*=============================================================================
|
|
|
|
Useful constants
|
|
|
|
=============================================================================*/
|
|
|
|
const serverRoot = "http://example.com/browser/metro/";
|
|
|
|
const baseURI = "http://mochi.test:8888/browser/metro/";
|
|
|
|
const chromeRoot = getRootDirectory(gTestPath);
|
2013-04-06 01:56:07 -07:00
|
|
|
const kDefaultWait = 2000;
|
2013-02-12 12:51:25 -08:00
|
|
|
const kDefaultInterval = 50;
|
2013-08-26 09:40:30 -07:00
|
|
|
|
|
|
|
/*=============================================================================
|
|
|
|
Load Helpers
|
|
|
|
=============================================================================*/
|
|
|
|
|
|
|
|
let splitPath = chromeRoot.split('/');
|
|
|
|
if (!splitPath[splitPath.length-1]) {
|
|
|
|
splitPath.pop();
|
|
|
|
}
|
|
|
|
// ../mochitest to make sure we're looking for the libs on the right path
|
|
|
|
// even for mochiperf tests.
|
|
|
|
splitPath.pop();
|
|
|
|
splitPath.push('mochitest');
|
|
|
|
|
|
|
|
const mochitestPath = splitPath.join('/') + '/';
|
|
|
|
|
|
|
|
[
|
2013-10-08 14:19:06 -07:00
|
|
|
"helpers/BookmarksHelper.js",
|
|
|
|
"helpers/HistoryHelper.js",
|
|
|
|
"helpers/ViewStateHelper.js"
|
2013-08-26 09:40:30 -07:00
|
|
|
].forEach(function(lib) {
|
|
|
|
Services.scriptloader.loadSubScript(mochitestPath + lib, this);
|
|
|
|
}, this);
|
2013-02-12 12:51:25 -08:00
|
|
|
|
2013-02-19 17:51:02 -08:00
|
|
|
/*=============================================================================
|
|
|
|
Metro ui helpers
|
|
|
|
=============================================================================*/
|
|
|
|
|
2013-04-19 08:01:00 -07:00
|
|
|
function isLandscapeMode()
|
|
|
|
{
|
2013-09-24 13:16:11 -07:00
|
|
|
return Elements.windowState.getAttribute("viewstate") == "landscape";
|
2013-04-19 08:01:00 -07:00
|
|
|
}
|
|
|
|
|
2013-06-25 12:06:27 -07:00
|
|
|
function setDevPixelEqualToPx()
|
|
|
|
{
|
|
|
|
todo(false, "test depends on devPixelsPerPx set to 1.0 - see bugs 886624 and 859742");
|
|
|
|
SpecialPowers.setCharPref("layout.css.devPixelsPerPx", "1.0");
|
|
|
|
registerCleanupFunction(function () {
|
|
|
|
SpecialPowers.clearUserPref("layout.css.devPixelsPerPx");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-02-19 17:51:02 -08:00
|
|
|
function checkContextUIMenuItemCount(aCount)
|
|
|
|
{
|
|
|
|
let visibleCount = 0;
|
2013-07-17 21:50:51 -07:00
|
|
|
for (let idx = 0; idx < ContextMenuUI.commands.childNodes.length; idx++) {
|
|
|
|
if (!ContextMenuUI.commands.childNodes[idx].hidden)
|
2013-02-19 17:51:02 -08:00
|
|
|
visibleCount++;
|
|
|
|
}
|
|
|
|
is(visibleCount, aCount, "command list count");
|
|
|
|
}
|
|
|
|
|
2013-02-19 17:51:02 -08:00
|
|
|
function checkContextUIMenuItemVisibility(aVisibleList)
|
|
|
|
{
|
|
|
|
let errors = 0;
|
2013-07-17 21:50:51 -07:00
|
|
|
for (let idx = 0; idx < ContextMenuUI.commands.childNodes.length; idx++) {
|
|
|
|
let item = ContextMenuUI.commands.childNodes[idx];
|
2013-02-19 17:51:02 -08:00
|
|
|
if (aVisibleList.indexOf(item.id) != -1 && item.hidden) {
|
|
|
|
// item should be visible
|
|
|
|
errors++;
|
|
|
|
info("should be visible:" + item.id);
|
|
|
|
} else if (aVisibleList.indexOf(item.id) == -1 && !item.hidden) {
|
|
|
|
// item should be hidden
|
|
|
|
errors++;
|
|
|
|
info("should be hidden:" + item.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
is(errors, 0, "context menu item list visibility");
|
|
|
|
}
|
|
|
|
|
2013-04-23 14:44:07 -07:00
|
|
|
function checkMonoclePositionRange(aMonocle, aMinX, aMaxX, aMinY, aMaxY)
|
|
|
|
{
|
|
|
|
let monocle = null;
|
|
|
|
if (aMonocle == "start")
|
|
|
|
monocle = SelectionHelperUI._startMark;
|
|
|
|
else if (aMonocle == "end")
|
|
|
|
monocle = SelectionHelperUI._endMark;
|
|
|
|
else if (aMonocle == "caret")
|
|
|
|
monocle = SelectionHelperUI._caretMark;
|
|
|
|
else
|
|
|
|
ok(false, "bad monocle id");
|
|
|
|
|
|
|
|
ok(monocle.xPos > aMinX && monocle.xPos < aMaxX,
|
|
|
|
"X position is " + monocle.xPos + ", expected between " + aMinX + " and " + aMaxX);
|
|
|
|
ok(monocle.yPos > aMinY && monocle.yPos < aMaxY,
|
|
|
|
"Y position is " + monocle.yPos + ", expected between " + aMinY + " and " + aMaxY);
|
|
|
|
}
|
|
|
|
|
2013-04-05 03:33:41 -07:00
|
|
|
/*
|
|
|
|
* showNotification - displays a test notification with the current
|
|
|
|
* browser and waits for the noticiation to be fully displayed.
|
|
|
|
*
|
|
|
|
* Usage: yield showNotification();
|
|
|
|
*/
|
|
|
|
function showNotification()
|
|
|
|
{
|
|
|
|
return Task.spawn(function() {
|
2013-08-27 07:58:43 -07:00
|
|
|
let strings = Strings.browser;
|
|
|
|
var buttons = [
|
|
|
|
{
|
|
|
|
isDefault: false,
|
|
|
|
label: strings.GetStringFromName("popupButtonAllowOnce2"),
|
|
|
|
accessKey: "",
|
|
|
|
callback: function() { }
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: strings.GetStringFromName("popupButtonAlwaysAllow3"),
|
|
|
|
accessKey: "",
|
|
|
|
callback: function() { }
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: strings.GetStringFromName("popupButtonNeverWarn3"),
|
|
|
|
accessKey: "",
|
|
|
|
callback: function() { }
|
|
|
|
}
|
|
|
|
];
|
|
|
|
let notificationBox = Browser.getNotificationBox();
|
|
|
|
const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
|
|
|
|
let note = notificationBox.appendNotification("test notification", "popup-blocked",
|
|
|
|
"chrome://browser/skin/images/infobar-popup.png",
|
|
|
|
priority, buttons);
|
|
|
|
yield waitForEvent(notificationBox, "transitionend");
|
|
|
|
throw new Task.Result(note);
|
2013-04-05 03:33:41 -07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-08-27 07:58:43 -07:00
|
|
|
function removeNotifications() {
|
|
|
|
Browser.getNotificationBox().removeAllNotifications(true);
|
|
|
|
}
|
|
|
|
|
2013-04-19 08:01:00 -07:00
|
|
|
function getSelection(aElement) {
|
|
|
|
if (!aElement)
|
|
|
|
return null;
|
2013-06-17 05:46:52 -07:00
|
|
|
|
|
|
|
// chrome text edit
|
|
|
|
if (aElement instanceof Ci.nsIDOMXULTextBoxElement) {
|
|
|
|
return aElement.QueryInterface(Components.interfaces.nsIDOMXULTextBoxElement)
|
|
|
|
.editor.selection;
|
|
|
|
}
|
|
|
|
|
|
|
|
// editable content element
|
2013-04-19 08:01:00 -07:00
|
|
|
if (aElement instanceof Ci.nsIDOMNSEditableElement) {
|
|
|
|
return aElement.QueryInterface(Ci.nsIDOMNSEditableElement)
|
2013-06-17 05:46:52 -07:00
|
|
|
.editor.selection;
|
2013-04-19 08:01:00 -07:00
|
|
|
}
|
2013-06-17 05:46:52 -07:00
|
|
|
|
2013-04-19 08:01:00 -07:00
|
|
|
// document or window
|
|
|
|
if (aElement instanceof HTMLDocument || aElement instanceof Window) {
|
|
|
|
return aElement.getSelection();
|
|
|
|
}
|
2013-06-17 05:46:52 -07:00
|
|
|
|
2013-04-19 08:01:00 -07:00
|
|
|
// browser
|
|
|
|
return aElement.contentWindow.getSelection();
|
2013-08-21 18:48:14 -07:00
|
|
|
}
|
2013-04-19 08:01:00 -07:00
|
|
|
|
|
|
|
function getTrimmedSelection(aElement) {
|
|
|
|
let sel = getSelection(aElement);
|
|
|
|
if (!sel)
|
|
|
|
return "";
|
|
|
|
return sel.toString().trim();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* clearSelection(aTarget) - clears the current selection in
|
|
|
|
* aTarget, shuts down the selection manager and purges all
|
|
|
|
* message manager events to insure a reset state for the ui.
|
|
|
|
*/
|
|
|
|
function clearSelection(aTarget) {
|
2013-04-19 08:01:01 -07:00
|
|
|
SelectionHelperUI.closeEditSession(true);
|
2013-04-19 08:01:00 -07:00
|
|
|
getSelection(aTarget).removeAllRanges();
|
|
|
|
purgeEventQueue();
|
|
|
|
}
|
|
|
|
|
2013-06-27 17:37:11 -07:00
|
|
|
// Hides the tab and context app bar if they are visible
|
2013-02-19 17:51:02 -08:00
|
|
|
function hideContextUI()
|
|
|
|
{
|
2013-02-19 17:51:02 -08:00
|
|
|
purgeEventQueue();
|
2013-05-14 23:30:24 -07:00
|
|
|
|
|
|
|
return Task.spawn(function() {
|
2013-06-27 17:37:11 -07:00
|
|
|
if (ContextUI.tabbarVisible) {
|
2013-05-14 23:30:24 -07:00
|
|
|
let promise = waitForEvent(Elements.tray, "transitionend", null, Elements.tray);
|
2013-06-27 17:37:11 -07:00
|
|
|
if (ContextUI.dismiss()) {
|
2013-05-14 23:30:24 -07:00
|
|
|
yield promise;
|
|
|
|
}
|
2013-04-16 23:16:34 -07:00
|
|
|
}
|
2013-05-14 23:30:24 -07:00
|
|
|
|
2013-06-27 17:37:11 -07:00
|
|
|
if (ContextUI.contextAppbarVisible) {
|
2013-05-14 23:30:24 -07:00
|
|
|
let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
|
2013-06-27 17:37:11 -07:00
|
|
|
ContextUI.dismissContextAppbar();
|
2013-05-14 23:30:24 -07:00
|
|
|
yield promise;
|
|
|
|
}
|
|
|
|
});
|
2013-02-19 17:51:02 -08:00
|
|
|
}
|
|
|
|
|
2013-04-19 08:01:00 -07:00
|
|
|
function showNavBar()
|
|
|
|
{
|
2013-06-27 17:37:11 -07:00
|
|
|
if (!ContextUI.navbarVisible) {
|
2014-01-27 15:45:03 -08:00
|
|
|
let promise = waitForEvent(Elements.navbar, "transitionend");
|
2013-04-19 08:01:00 -07:00
|
|
|
ContextUI.displayNavbar();
|
|
|
|
return promise;
|
|
|
|
}
|
2014-01-27 15:45:03 -08:00
|
|
|
return Promise.resolve(null);
|
|
|
|
}
|
|
|
|
|
|
|
|
function hideNavBar()
|
|
|
|
{
|
|
|
|
if (ContextUI.navbarVisible) {
|
|
|
|
let promise = waitForEvent(Elements.navbar, "transitionend");
|
|
|
|
ContextUI.dismissNavbar();
|
|
|
|
return promise;
|
|
|
|
}
|
|
|
|
return Promise.resolve(null);
|
2013-04-19 08:01:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
function fireAppBarDisplayEvent()
|
|
|
|
{
|
|
|
|
let promise = waitForEvent(Elements.tray, "transitionend");
|
|
|
|
let event = document.createEvent("Events");
|
2013-05-16 21:22:21 -07:00
|
|
|
event.initEvent("MozEdgeUICompleted", true, false);
|
2013-04-19 08:01:00 -07:00
|
|
|
gWindow.dispatchEvent(event);
|
|
|
|
purgeEventQueue();
|
|
|
|
return promise;
|
|
|
|
}
|
|
|
|
|
2013-02-12 12:51:25 -08:00
|
|
|
/*=============================================================================
|
2013-08-27 07:58:43 -07:00
|
|
|
General test helpers
|
2013-02-12 12:51:25 -08:00
|
|
|
=============================================================================*/
|
2013-04-29 20:42:44 -07:00
|
|
|
let gOpenedTabs = [];
|
|
|
|
|
2013-08-27 07:58:43 -07:00
|
|
|
function loadUriInActiveTab(aUri)
|
|
|
|
{
|
|
|
|
return Task.spawn(function() {
|
|
|
|
let promise = waitForEvent(getBrowser(), "pageshow");
|
|
|
|
BrowserUI.goToURI(aUri);
|
|
|
|
yield waitForCondition(function () {
|
|
|
|
return getBrowser().currentURI.spec == aUri
|
|
|
|
}, "getBrowser().currentURI.spec == " + aUri);
|
|
|
|
yield promise;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function navForward() {
|
|
|
|
return Task.spawn(function() {
|
|
|
|
let promise = waitForEvent(getBrowser(), "pageshow");
|
|
|
|
EventUtils.synthesizeKey("VK_RIGHT", { altKey: true }, window);
|
|
|
|
yield promise;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function navBackViaNavButton() {
|
|
|
|
return Task.spawn(function() {
|
|
|
|
let promise = waitForEvent(getBrowser(), "pageshow");
|
|
|
|
let backButton = document.getElementById("overlay-back");
|
|
|
|
sendElementTap(window, backButton);
|
|
|
|
yield promise;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-02-12 12:51:25 -08:00
|
|
|
/**
|
|
|
|
* Loads a URL in a new tab asynchronously.
|
|
|
|
*
|
|
|
|
* Usage:
|
|
|
|
* Task.spawn(function() {
|
|
|
|
* let tab = yield addTab("http://example.com/");
|
|
|
|
* ok(Browser.selectedTab == tab, "the new tab is selected");
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* @param aUrl the URL to load
|
|
|
|
* @returns a task that resolves to the new tab object after the URL is loaded.
|
|
|
|
*/
|
|
|
|
function addTab(aUrl) {
|
|
|
|
return Task.spawn(function() {
|
|
|
|
info("Opening "+aUrl+" in a new tab");
|
|
|
|
let tab = Browser.addTab(aUrl, true);
|
2013-04-23 06:51:03 -07:00
|
|
|
yield tab.pageShowPromise;
|
2013-02-12 12:51:25 -08:00
|
|
|
|
|
|
|
is(tab.browser.currentURI.spec, aUrl, aUrl + " is loaded");
|
2013-04-29 20:42:44 -07:00
|
|
|
|
|
|
|
yield hideContextUI();
|
|
|
|
|
|
|
|
gOpenedTabs.push(tab);
|
|
|
|
|
2013-02-12 12:51:25 -08:00
|
|
|
throw new Task.Result(tab);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-04-29 20:42:44 -07:00
|
|
|
/**
|
|
|
|
* Cleans up tabs left open by addTab().
|
|
|
|
* This is being called at runTests() after the test loop.
|
|
|
|
*/
|
|
|
|
function cleanUpOpenedTabs() {
|
|
|
|
let tab;
|
|
|
|
while(tab = gOpenedTabs.shift()) {
|
|
|
|
Browser.closeTab(Browser.getTabFromChrome(tab.chromeTab), { forceClose: true })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-12 12:51:25 -08:00
|
|
|
/**
|
|
|
|
* Waits a specified number of miliseconds for a specified event to be
|
|
|
|
* fired on a specified element.
|
|
|
|
*
|
|
|
|
* Usage:
|
|
|
|
* let receivedEvent = waitForEvent(element, "eventName");
|
|
|
|
* // Do some processing here that will cause the event to be fired
|
|
|
|
* // ...
|
|
|
|
* // Now yield until the Promise is fulfilled
|
|
|
|
* yield receivedEvent;
|
|
|
|
* if (receivedEvent && !(receivedEvent instanceof Error)) {
|
|
|
|
* receivedEvent.msg == "eventName";
|
|
|
|
* // ...
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* @param aSubject the element that should receive the event
|
|
|
|
* @param aEventName the event to wait for
|
|
|
|
* @param aTimeoutMs the number of miliseconds to wait before giving up
|
|
|
|
* @returns a Promise that resolves to the received event, or to an Error
|
|
|
|
*/
|
2013-04-16 23:16:34 -07:00
|
|
|
function waitForEvent(aSubject, aEventName, aTimeoutMs, aTarget) {
|
2013-02-12 12:51:25 -08:00
|
|
|
let eventDeferred = Promise.defer();
|
|
|
|
let timeoutMs = aTimeoutMs || kDefaultWait;
|
2013-05-14 23:30:24 -07:00
|
|
|
let stack = new Error().stack;
|
2013-02-12 12:51:25 -08:00
|
|
|
let timerID = setTimeout(function wfe_canceller() {
|
2013-08-27 07:58:43 -07:00
|
|
|
aSubject.removeEventListener(aEventName, listener);
|
2013-05-14 23:30:24 -07:00
|
|
|
eventDeferred.reject( new Error(aEventName+" event timeout at " + stack) );
|
2013-02-12 12:51:25 -08:00
|
|
|
}, timeoutMs);
|
|
|
|
|
2013-08-27 07:58:43 -07:00
|
|
|
var listener = function (aEvent) {
|
2013-04-16 23:16:34 -07:00
|
|
|
if (aTarget && aTarget !== aEvent.target)
|
|
|
|
return;
|
|
|
|
|
2013-02-12 12:51:25 -08:00
|
|
|
// stop the timeout clock and resume
|
|
|
|
clearTimeout(timerID);
|
|
|
|
eventDeferred.resolve(aEvent);
|
|
|
|
}
|
|
|
|
|
2013-09-17 11:12:40 -07:00
|
|
|
function cleanup(aEventOrError) {
|
2013-02-12 12:51:25 -08:00
|
|
|
// unhook listener in case of success or failure
|
2013-08-27 07:58:43 -07:00
|
|
|
aSubject.removeEventListener(aEventName, listener);
|
2013-09-17 11:12:40 -07:00
|
|
|
return aEventOrError;
|
2013-02-12 12:51:25 -08:00
|
|
|
}
|
2013-08-27 07:58:43 -07:00
|
|
|
aSubject.addEventListener(aEventName, listener, false);
|
2013-09-17 11:12:40 -07:00
|
|
|
return eventDeferred.promise.then(cleanup, cleanup);
|
2013-02-12 12:51:25 -08:00
|
|
|
}
|
|
|
|
|
2013-11-08 11:04:13 -08:00
|
|
|
/**
|
|
|
|
* Wait for an nsIMessageManager IPC message.
|
|
|
|
*/
|
|
|
|
function waitForMessage(aName, aMessageManager) {
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
let manager = aMessageManager || messageManager;
|
|
|
|
function listener(aMessage) {
|
|
|
|
deferred.resolve(aMessage);
|
|
|
|
}
|
|
|
|
manager.addMessageListener(aName, listener);
|
|
|
|
function cleanup(aEventOrError) {
|
|
|
|
manager.removeMessageListener(aName, listener);
|
|
|
|
}
|
|
|
|
return deferred.promise.then(cleanup, cleanup);
|
|
|
|
}
|
|
|
|
|
2013-02-12 12:51:25 -08:00
|
|
|
/**
|
|
|
|
* Waits a specified number of miliseconds.
|
|
|
|
*
|
|
|
|
* Usage:
|
|
|
|
* let wait = yield waitForMs(2000);
|
|
|
|
* ok(wait, "2 seconds should now have elapsed");
|
|
|
|
*
|
|
|
|
* @param aMs the number of miliseconds to wait for
|
|
|
|
* @returns a Promise that resolves to true after the time has elapsed
|
|
|
|
*/
|
|
|
|
function waitForMs(aMs) {
|
|
|
|
info("Wating for " + aMs + "ms");
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
let startTime = Date.now();
|
|
|
|
setTimeout(done, aMs);
|
|
|
|
|
|
|
|
function done() {
|
|
|
|
deferred.resolve(true);
|
|
|
|
info("waitForMs finished waiting, waited for "
|
|
|
|
+ (Date.now() - startTime)
|
|
|
|
+ "ms");
|
|
|
|
}
|
|
|
|
|
|
|
|
return deferred.promise;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Waits a specified number of miliseconds for a supplied callback to
|
|
|
|
* return a truthy value.
|
|
|
|
*
|
|
|
|
* Usage:
|
|
|
|
* let success = yield waitForCondition(myTestFunction);
|
|
|
|
* if (success && !(success instanceof Error)) {
|
|
|
|
* // ...
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* @param aCondition the callback that must return a truthy value
|
|
|
|
* @param aTimeoutMs the number of miliseconds to wait before giving up
|
|
|
|
* @param aIntervalMs the number of miliseconds between calls to aCondition
|
|
|
|
* @returns a Promise that resolves to true, or to an Error
|
|
|
|
*/
|
|
|
|
function waitForCondition(aCondition, aTimeoutMs, aIntervalMs) {
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
let timeoutMs = aTimeoutMs || kDefaultWait;
|
|
|
|
let intervalMs = aIntervalMs || kDefaultInterval;
|
|
|
|
let startTime = Date.now();
|
2013-09-16 12:57:11 -07:00
|
|
|
let stack = new Error().stack;
|
2013-02-12 12:51:25 -08:00
|
|
|
|
|
|
|
function testCondition() {
|
|
|
|
let now = Date.now();
|
|
|
|
if((now - startTime) > timeoutMs) {
|
2013-09-16 12:57:11 -07:00
|
|
|
deferred.reject( new Error("Timed out waiting for condition to be true at " + stack) );
|
2013-02-12 12:51:25 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let condition;
|
|
|
|
try {
|
|
|
|
condition = aCondition();
|
|
|
|
} catch (e) {
|
2013-04-06 01:56:07 -07:00
|
|
|
deferred.reject( new Error("Got exception while attempting to test condition: " + e) );
|
2013-02-12 12:51:25 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (condition) {
|
|
|
|
deferred.resolve(true);
|
|
|
|
} else {
|
|
|
|
setTimeout(testCondition, intervalMs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setTimeout(testCondition, 0);
|
|
|
|
return deferred.promise;
|
|
|
|
}
|
|
|
|
|
2013-08-27 07:58:43 -07:00
|
|
|
/**
|
2013-09-17 11:12:40 -07:00
|
|
|
* same as waitForCondition but with better test output.
|
2013-08-27 07:58:43 -07:00
|
|
|
*
|
|
|
|
* @param aCondition the callback that must return a truthy value
|
|
|
|
* @param aTestMsg test condition message printed when the test succeeds or
|
|
|
|
* fails. Defaults to the stringified version of aCondition.
|
|
|
|
* @param aTimeoutMs the number of miliseconds to wait before giving up
|
|
|
|
* @param aIntervalMs the number of miliseconds between calls to aCondition
|
|
|
|
* @returns a Promise that resolves to true, or to an Error
|
|
|
|
*/
|
|
|
|
function waitForCondition2(aCondition, aTestMsg, aTimeoutMs, aIntervalMs) {
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
let msg = aTestMsg || aCondition;
|
|
|
|
let timeoutMs = aTimeoutMs || kDefaultWait;
|
|
|
|
let intervalMs = aIntervalMs || kDefaultInterval;
|
|
|
|
let startTime = Date.now();
|
|
|
|
|
|
|
|
function testCondition() {
|
|
|
|
let now = Date.now();
|
|
|
|
if((now - startTime) > timeoutMs) {
|
|
|
|
deferred.reject( new Error("Timed out waiting for " + msg) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let condition;
|
|
|
|
try {
|
|
|
|
condition = aCondition();
|
|
|
|
} catch (e) {
|
|
|
|
deferred.reject( new Error("Got exception while attempting to test '" + msg + "': " + e) );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (condition) {
|
|
|
|
ok(true, msg);
|
|
|
|
deferred.resolve(true);
|
|
|
|
} else {
|
|
|
|
setTimeout(testCondition, intervalMs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setTimeout(testCondition, 0);
|
|
|
|
return deferred.promise;
|
|
|
|
}
|
|
|
|
|
2013-02-19 17:51:02 -08:00
|
|
|
/*
|
|
|
|
* Waits for an image in a page to load. Wrapper around waitForCondition.
|
|
|
|
*
|
|
|
|
* @param aWindow the tab or window that contains the image.
|
|
|
|
* @param aImageId the id of the image in the page.
|
|
|
|
* @returns a Promise that resolves to true, or to an Error
|
|
|
|
*/
|
|
|
|
function waitForImageLoad(aWindow, aImageId) {
|
|
|
|
let elem = aWindow.document.getElementById(aImageId);
|
|
|
|
return waitForCondition(function () {
|
|
|
|
let request = elem.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
|
|
|
|
if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}, 5000, 100);
|
|
|
|
}
|
|
|
|
|
2013-02-19 17:51:02 -08:00
|
|
|
/**
|
|
|
|
* Waits a specified number of miliseconds for an observer event.
|
|
|
|
*
|
|
|
|
* @param aObsEvent the observer event to wait for
|
|
|
|
* @param aTimeoutMs the number of miliseconds to wait before giving up
|
|
|
|
* @returns a Promise that resolves to true, or to an Error
|
|
|
|
*/
|
|
|
|
function waitForObserver(aObsEvent, aTimeoutMs) {
|
|
|
|
try {
|
2013-03-01 07:45:07 -08:00
|
|
|
|
2013-02-19 17:51:02 -08:00
|
|
|
let deferred = Promise.defer();
|
|
|
|
let timeoutMs = aTimeoutMs || kDefaultWait;
|
|
|
|
let timerID = 0;
|
|
|
|
|
|
|
|
var observeWatcher = {
|
|
|
|
onEvent: function () {
|
|
|
|
clearTimeout(timerID);
|
|
|
|
Services.obs.removeObserver(this, aObsEvent);
|
|
|
|
deferred.resolve();
|
|
|
|
},
|
|
|
|
|
|
|
|
onError: function () {
|
|
|
|
clearTimeout(timerID);
|
|
|
|
Services.obs.removeObserver(this, aObsEvent);
|
|
|
|
deferred.reject(new Error(aObsEvent + " event timeout"));
|
|
|
|
},
|
|
|
|
|
|
|
|
observe: function (aSubject, aTopic, aData) {
|
|
|
|
if (aTopic == aObsEvent) {
|
|
|
|
this.onEvent();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
QueryInterface: function (aIID) {
|
|
|
|
if (!aIID.equals(Ci.nsIObserver) &&
|
|
|
|
!aIID.equals(Ci.nsISupportsWeakReference) &&
|
|
|
|
!aIID.equals(Ci.nsISupports)) {
|
|
|
|
throw Components.results.NS_ERROR_NO_INTERFACE;
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
timerID = setTimeout(function wfo_canceller() {
|
|
|
|
observeWatcher.onError();
|
|
|
|
}, timeoutMs);
|
|
|
|
|
|
|
|
Services.obs.addObserver(observeWatcher, aObsEvent, true);
|
|
|
|
return deferred.promise;
|
2013-03-01 07:45:07 -08:00
|
|
|
|
2013-02-19 17:51:02 -08:00
|
|
|
} catch (ex) {
|
|
|
|
info(ex.message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-06 16:07:40 -08:00
|
|
|
|
|
|
|
/*=============================================================================
|
|
|
|
* Input mode helpers - these helpers notify observers to metro_precise_input
|
|
|
|
* and metro_imprecise_input respectively, triggering the same behaviour as user touch or mouse input
|
|
|
|
*
|
|
|
|
* Usage: let promise = waitForObservers("metro_imprecise_input");
|
|
|
|
* notifyImprecise();
|
|
|
|
* yield promise; // you are now in imprecise mode
|
|
|
|
*===========================================================================*/
|
|
|
|
function notifyPrecise()
|
|
|
|
{
|
|
|
|
Services.obs.notifyObservers(null, "metro_precise_input", null);
|
|
|
|
}
|
|
|
|
|
|
|
|
function notifyImprecise()
|
|
|
|
{
|
|
|
|
Services.obs.notifyObservers(null, "metro_imprecise_input", null);
|
|
|
|
}
|
|
|
|
|
2013-02-12 12:51:25 -08:00
|
|
|
/*=============================================================================
|
2013-12-14 12:40:56 -08:00
|
|
|
* Native input helpers - these helpers send input directly to the os
|
|
|
|
* generating os level input events that get processed by widget and
|
|
|
|
* apzc logic.
|
|
|
|
*===========================================================================*/
|
2013-02-12 12:51:25 -08:00
|
|
|
function synthesizeNativeMouse(aElement, aOffsetX, aOffsetY, aMsg) {
|
|
|
|
let x = aOffsetX;
|
|
|
|
let y = aOffsetY;
|
|
|
|
if (aElement) {
|
|
|
|
if (aElement.getBoundingClientRect) {
|
|
|
|
let rect = aElement.getBoundingClientRect();
|
|
|
|
x += rect.left;
|
|
|
|
y += rect.top;
|
|
|
|
} else if(aElement.left && aElement.top) {
|
|
|
|
x += aElement.left;
|
|
|
|
y += aElement.top;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Browser.windowUtils.sendNativeMouseEvent(x, y, aMsg, 0, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
function synthesizeNativeMouseMove(aElement, aOffsetX, aOffsetY) {
|
|
|
|
synthesizeNativeMouse(aElement,
|
|
|
|
aOffsetX,
|
|
|
|
aOffsetY,
|
|
|
|
0x0001); // MOUSEEVENTF_MOVE
|
|
|
|
}
|
|
|
|
|
|
|
|
function synthesizeNativeMouseLDown(aElement, aOffsetX, aOffsetY) {
|
|
|
|
synthesizeNativeMouse(aElement,
|
|
|
|
aOffsetX,
|
|
|
|
aOffsetY,
|
|
|
|
0x0002); // MOUSEEVENTF_LEFTDOWN
|
|
|
|
}
|
|
|
|
|
|
|
|
function synthesizeNativeMouseLUp(aElement, aOffsetX, aOffsetY) {
|
|
|
|
synthesizeNativeMouse(aElement,
|
|
|
|
aOffsetX,
|
|
|
|
aOffsetY,
|
|
|
|
0x0004); // MOUSEEVENTF_LEFTUP
|
|
|
|
}
|
|
|
|
|
|
|
|
function synthesizeNativeMouseRDown(aElement, aOffsetX, aOffsetY) {
|
|
|
|
synthesizeNativeMouse(aElement,
|
|
|
|
aOffsetX,
|
|
|
|
aOffsetY,
|
|
|
|
0x0008); // MOUSEEVENTF_RIGHTDOWN
|
|
|
|
}
|
|
|
|
|
|
|
|
function synthesizeNativeMouseRUp(aElement, aOffsetX, aOffsetY) {
|
|
|
|
synthesizeNativeMouse(aElement,
|
|
|
|
aOffsetX,
|
|
|
|
aOffsetY,
|
|
|
|
0x0010); // MOUSEEVENTF_RIGHTUP
|
|
|
|
}
|
|
|
|
|
|
|
|
function synthesizeNativeMouseMDown(aElement, aOffsetX, aOffsetY) {
|
|
|
|
synthesizeNativeMouse(aElement,
|
|
|
|
aOffsetX,
|
|
|
|
aOffsetY,
|
|
|
|
0x0020); // MOUSEEVENTF_MIDDLEDOWN
|
|
|
|
}
|
|
|
|
|
|
|
|
function synthesizeNativeMouseMUp(aElement, aOffsetX, aOffsetY) {
|
|
|
|
synthesizeNativeMouse(aElement,
|
|
|
|
aOffsetX,
|
|
|
|
aOffsetY,
|
|
|
|
0x0040); // MOUSEEVENTF_MIDDLEUP
|
|
|
|
}
|
|
|
|
|
2013-12-14 12:40:56 -08:00
|
|
|
// 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.
|
|
|
|
*===========================================================================*/
|
|
|
|
|
2013-06-25 15:35:50 -07:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
function logicalCoordsForElement (aElement, aX, aY) {
|
|
|
|
let coords = { x: null, y: null };
|
|
|
|
let rect = aElement.getBoundingClientRect();
|
|
|
|
|
|
|
|
coords.x = isNaN(aX) ? rect.left + (rect.width / 2) : rect.left + aX;
|
|
|
|
coords.y = isNaN(aY) ? rect.top + (rect.height / 2) : rect.top + aY;
|
|
|
|
|
|
|
|
return coords;
|
|
|
|
}
|
|
|
|
|
2013-07-17 21:50:51 -07:00
|
|
|
function sendContextMenuMouseClickToElement(aWindow, aElement, aX, aY) {
|
|
|
|
let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindowUtils);
|
|
|
|
let coords = logicalCoordsForElement(aElement, aX, aY);
|
|
|
|
|
|
|
|
utils.sendMouseEventToWindow("mousedown", coords.x, coords.y, 2, 1, 0);
|
|
|
|
utils.sendMouseEventToWindow("mouseup", coords.x, coords.y, 2, 1, 0);
|
|
|
|
utils.sendMouseEventToWindow("contextmenu", coords.x, coords.y, 2, 1, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
function sendMouseClick(aWindow, aX, aY) {
|
|
|
|
EventUtils.synthesizeMouseAtPoint(aX, aY, {}, aWindow);
|
|
|
|
}
|
|
|
|
|
2013-02-19 17:51:02 -08:00
|
|
|
/*
|
2013-04-05 03:33:41 -07:00
|
|
|
* sendContextMenuClick - simulates a press-hold touch input event. Event
|
|
|
|
* is delivered to the main window of the application through the top-level
|
|
|
|
* widget.
|
|
|
|
*
|
|
|
|
* @param aX, aY logical coordinates of the event.
|
|
|
|
*/
|
|
|
|
function sendContextMenuClick(aX, aY) {
|
|
|
|
let mediator = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
|
|
|
.getService(Components.interfaces.nsIWindowMediator);
|
|
|
|
let mainwin = mediator.getMostRecentWindow("navigator:browser");
|
|
|
|
let utils = mainwin.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
|
|
|
utils.sendMouseEvent("contextmenu", aX, aY, 2, 1, 0, true,
|
|
|
|
1, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
|
|
|
|
}
|
|
|
|
|
2013-08-19 02:25:58 -07:00
|
|
|
/*
|
|
|
|
* sendContextMenuClickToSelection - simulates a press-hold touch input event
|
|
|
|
* selected text in a window.
|
|
|
|
*/
|
|
|
|
function sendContextMenuClickToSelection(aWindow) {
|
|
|
|
let selection = aWindow.getSelection();
|
|
|
|
if (!selection || !selection.rangeCount) {
|
|
|
|
ok(false, "no selection to tap!");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let range = selection.getRangeAt(0);
|
|
|
|
let rect = range.getBoundingClientRect();
|
|
|
|
let x = rect.left + (rect.width / 2);
|
|
|
|
let y = rect.top + (rect.height / 2);
|
|
|
|
let utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
|
|
|
utils.sendMouseEventToWindow("contextmenu", x, y, 2, 1, 0, true,
|
|
|
|
1, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
|
|
|
|
}
|
|
|
|
|
2013-04-05 03:33:41 -07:00
|
|
|
/*
|
|
|
|
* sendContextMenuClickToWindow - simulates a press-hold touch input event.
|
|
|
|
*
|
|
|
|
* @param aWindow window used to retrieve dom window utils, and the
|
|
|
|
* target window for the event.
|
|
|
|
* @param aX, aY logical coordinates of the event relative to aWindow.
|
2013-02-19 17:51:02 -08:00
|
|
|
*/
|
2013-04-05 03:33:41 -07:00
|
|
|
function sendContextMenuClickToWindow(aWindow, aX, aY) {
|
2013-02-19 17:51:02 -08:00
|
|
|
let utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
|
|
|
|
|
|
|
utils.sendMouseEventToWindow("contextmenu", aX, aY, 2, 1, 0, true,
|
|
|
|
1, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
|
|
|
|
}
|
|
|
|
|
2013-02-19 17:51:02 -08:00
|
|
|
function sendContextMenuClickToElement(aWindow, aElement, aX, aY) {
|
|
|
|
let utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
2013-06-25 15:35:50 -07:00
|
|
|
let coords = logicalCoordsForElement(aElement, aX, aY);
|
|
|
|
utils.sendMouseEventToWindow("contextmenu", coords.x, coords.y, 2, 1, 0, true,
|
2013-02-19 17:51:02 -08:00
|
|
|
1, Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
|
|
|
|
}
|
|
|
|
|
2013-04-19 08:01:00 -07:00
|
|
|
/*
|
|
|
|
* sendDoubleTap - simulates a double click or double tap.
|
|
|
|
*/
|
|
|
|
function sendDoubleTap(aWindow, aX, aY) {
|
|
|
|
EventUtils.synthesizeMouseAtPoint(aX, aY, {
|
|
|
|
clickCount: 1,
|
|
|
|
inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
|
|
|
|
}, aWindow);
|
|
|
|
|
|
|
|
EventUtils.synthesizeMouseAtPoint(aX, aY, {
|
|
|
|
clickCount: 2,
|
|
|
|
inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
|
|
|
|
}, aWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
function sendTap(aWindow, aX, aY) {
|
|
|
|
EventUtils.synthesizeMouseAtPoint(aX, aY, {
|
|
|
|
clickCount: 1,
|
|
|
|
inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
|
|
|
|
}, aWindow);
|
|
|
|
}
|
|
|
|
|
2013-06-17 05:46:52 -07:00
|
|
|
function sendElementTap(aWindow, aElement, aX, aY) {
|
2013-06-25 15:35:50 -07:00
|
|
|
let coords = logicalCoordsForElement(aElement, aX, aY);
|
|
|
|
EventUtils.synthesizeMouseAtPoint(coords.x, coords.y, {
|
2013-06-17 05:46:52 -07:00
|
|
|
clickCount: 1,
|
|
|
|
inputSource: Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH
|
|
|
|
}, aWindow);
|
|
|
|
}
|
|
|
|
|
2013-04-19 08:01:00 -07:00
|
|
|
/*
|
|
|
|
* sendTouchDrag - sends a touch series composed of a touchstart,
|
|
|
|
* touchmove, and touchend w3c event.
|
|
|
|
*/
|
|
|
|
function sendTouchDrag(aWindow, aStartX, aStartY, aEndX, aEndY) {
|
|
|
|
EventUtils.synthesizeTouchAtPoint(aStartX, aStartY, { type: "touchstart" }, aWindow);
|
|
|
|
EventUtils.synthesizeTouchAtPoint(aEndX, aEndY, { type: "touchmove" }, aWindow);
|
|
|
|
EventUtils.synthesizeTouchAtPoint(aEndX, aEndY, { type: "touchend" }, aWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TouchDragAndHold - simulates a drag and hold sequence of events.
|
|
|
|
*/
|
|
|
|
function TouchDragAndHold() {
|
|
|
|
}
|
|
|
|
|
|
|
|
TouchDragAndHold.prototype = {
|
|
|
|
_timeoutStep: 2,
|
|
|
|
_numSteps: 50,
|
|
|
|
_debug: false,
|
2013-04-23 06:51:03 -07:00
|
|
|
_win: null,
|
2013-12-14 12:40:56 -08:00
|
|
|
_native: false,
|
|
|
|
_pointerId: 1,
|
|
|
|
_dui: Components.interfaces.nsIDOMWindowUtils,
|
|
|
|
|
|
|
|
set useNativeEvents(aValue) {
|
|
|
|
this._native = aValue;
|
|
|
|
},
|
|
|
|
|
2014-01-27 15:45:03 -08:00
|
|
|
set stepTimeout(aValue) {
|
|
|
|
this._timeoutStep = aValue;
|
|
|
|
},
|
|
|
|
|
|
|
|
set numSteps(aValue) {
|
|
|
|
this._numSteps = aValue;
|
|
|
|
},
|
|
|
|
|
2013-12-14 12:40:56 -08:00
|
|
|
set nativePointerId(aValue) {
|
|
|
|
this._pointerId = aValue;
|
|
|
|
},
|
2013-04-19 08:01:00 -07:00
|
|
|
|
|
|
|
callback: function callback() {
|
2013-04-23 06:51:03 -07:00
|
|
|
if (this._win == null)
|
|
|
|
return;
|
2013-11-17 04:12:52 -08:00
|
|
|
|
|
|
|
if (this._debug) {
|
|
|
|
SelectionHelperUI.debugDisplayDebugPoint(this._currentPoint.xPos,
|
|
|
|
this._currentPoint.yPos, 5, "#FF0000", true);
|
|
|
|
}
|
|
|
|
|
2013-04-19 08:01:00 -07:00
|
|
|
if (++this._step.steps >= this._numSteps) {
|
2013-12-14 12:40:56 -08:00
|
|
|
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);
|
|
|
|
}
|
2013-04-19 08:01:00 -07:00
|
|
|
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);
|
|
|
|
}
|
2013-12-14 12:40:56 -08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-04-19 08:01:00 -07:00
|
|
|
let self = this;
|
|
|
|
setTimeout(function () { self.callback(); }, this._timeoutStep);
|
|
|
|
},
|
|
|
|
|
|
|
|
start: function start(aWindow, aStartX, aStartY, aEndX, aEndY) {
|
|
|
|
this._defer = Promise.defer();
|
|
|
|
this._win = aWindow;
|
2013-12-14 12:40:56 -08:00
|
|
|
this._utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
|
|
.getInterface(Ci.nsIDOMWindowUtils);
|
2013-04-19 08:01:00 -07:00
|
|
|
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);
|
|
|
|
}
|
2013-10-22 05:13:26 -07:00
|
|
|
// flush layout, bug 914847
|
|
|
|
this._utils.elementFromPoint(aStartX, aStartY, false, true);
|
2013-12-14 12:40:56 -08:00
|
|
|
if (this._native) {
|
|
|
|
this._utils.sendNativeTouchPoint(this._pointerId, this._dui.TOUCH_CONTACT,
|
|
|
|
aStartX, aStartY, 1, 90);
|
|
|
|
} else {
|
|
|
|
EventUtils.synthesizeTouchAtPoint(aStartX, aStartY, { type: "touchstart" }, aWindow);
|
|
|
|
}
|
2013-04-19 08:01:00 -07:00
|
|
|
let self = this;
|
|
|
|
setTimeout(function () { self.callback(); }, this._timeoutStep);
|
2013-06-12 17:20:57 -07:00
|
|
|
return this._defer.promise;
|
|
|
|
},
|
|
|
|
|
|
|
|
move: function move(aEndX, aEndY) {
|
|
|
|
if (this._win == null)
|
|
|
|
return;
|
|
|
|
if (this._debug) {
|
|
|
|
info("[0] continuation to " + aEndX + " x " + aEndY);
|
|
|
|
}
|
|
|
|
this._defer = Promise.defer();
|
|
|
|
this._step = { steps: 0,
|
|
|
|
x: (aEndX - this._endPoint.xPos) / this._numSteps,
|
|
|
|
y: (aEndY - this._endPoint.yPos) / this._numSteps };
|
|
|
|
this._endPoint = { xPos: aEndX, yPos: aEndY };
|
|
|
|
let self = this;
|
|
|
|
setTimeout(function () { self.callback(); }, this._timeoutStep);
|
2013-04-19 08:01:00 -07:00
|
|
|
return this._defer.promise;
|
|
|
|
},
|
|
|
|
|
2013-10-22 05:13:26 -07:00
|
|
|
end: function end() {
|
2013-04-19 08:01:00 -07:00
|
|
|
if (this._debug) {
|
|
|
|
info("[" + this._step.steps + "] touchend " + this._endPoint.xPos + " x " + this._endPoint.yPos);
|
2013-11-17 04:12:52 -08:00
|
|
|
SelectionHelperUI.debugClearDebugPoints();
|
2013-04-19 08:01:00 -07:00
|
|
|
}
|
2013-12-14 12:40:56 -08:00
|
|
|
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);
|
|
|
|
}
|
2013-04-19 08:01:00 -07:00
|
|
|
this._win = null;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2013-02-19 17:51:02 -08:00
|
|
|
/*=============================================================================
|
|
|
|
System utilities
|
|
|
|
=============================================================================*/
|
|
|
|
|
2013-07-15 11:25:45 -07:00
|
|
|
/*
|
|
|
|
* emptyClipboard - clear the windows clipboard.
|
2013-04-19 08:01:00 -07:00
|
|
|
*/
|
|
|
|
function emptyClipboard() {
|
|
|
|
Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard)
|
|
|
|
.emptyClipboard(Ci.nsIClipboard.kGlobalClipboard);
|
|
|
|
}
|
|
|
|
|
2013-02-19 17:51:02 -08:00
|
|
|
/*
|
|
|
|
* purgeEventQueue - purges the event queue on the calling thread.
|
|
|
|
* Pumps latent in-process message manager events awaiting delivery.
|
|
|
|
*/
|
|
|
|
function purgeEventQueue() {
|
|
|
|
let thread = Services.tm.currentThread;
|
|
|
|
while (thread.hasPendingEvents()) {
|
|
|
|
if (!thread.processNextEvent(true))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-12 12:51:25 -08:00
|
|
|
/*=============================================================================
|
|
|
|
Test-running helpers
|
|
|
|
=============================================================================*/
|
|
|
|
let gCurrentTest = null;
|
|
|
|
let gTests = [];
|
|
|
|
|
|
|
|
function runTests() {
|
|
|
|
waitForExplicitFinish();
|
2013-04-29 20:42:44 -07:00
|
|
|
|
2013-02-12 12:51:25 -08:00
|
|
|
Task.spawn(function() {
|
|
|
|
while((gCurrentTest = gTests.shift())){
|
2013-04-06 01:56:07 -07:00
|
|
|
try {
|
|
|
|
if ('function' == typeof gCurrentTest.setUp) {
|
2013-04-10 23:06:19 -07:00
|
|
|
info("SETUP " + gCurrentTest.desc);
|
2013-04-06 01:56:07 -07:00
|
|
|
yield Task.spawn(gCurrentTest.setUp.bind(gCurrentTest));
|
|
|
|
}
|
2013-04-16 23:16:34 -07:00
|
|
|
try {
|
|
|
|
info("RUN " + gCurrentTest.desc);
|
|
|
|
yield Task.spawn(gCurrentTest.run.bind(gCurrentTest));
|
|
|
|
} finally {
|
|
|
|
if ('function' == typeof gCurrentTest.tearDown) {
|
|
|
|
info("TEARDOWN " + gCurrentTest.desc);
|
|
|
|
yield Task.spawn(gCurrentTest.tearDown.bind(gCurrentTest));
|
|
|
|
}
|
2013-04-06 01:56:07 -07:00
|
|
|
}
|
|
|
|
} catch (ex) {
|
2013-04-16 23:16:34 -07:00
|
|
|
ok(false, "runTests: Task failed - " + ex + ' at ' + ex.stack);
|
2013-04-06 01:56:07 -07:00
|
|
|
} finally {
|
2013-04-10 23:06:19 -07:00
|
|
|
info("END " + gCurrentTest.desc);
|
2013-03-01 07:45:07 -08:00
|
|
|
}
|
2013-02-12 12:51:25 -08:00
|
|
|
}
|
2013-04-29 20:42:44 -07:00
|
|
|
|
|
|
|
try {
|
|
|
|
cleanUpOpenedTabs();
|
|
|
|
|
|
|
|
let badTabs = [];
|
|
|
|
Browser.tabs.forEach(function(item, index, array) {
|
|
|
|
let location = item.browser.currentURI.spec;
|
2013-08-09 02:50:59 -07:00
|
|
|
if (index == 0 && location == "about:blank" || location == "about:start") {
|
2013-04-29 20:42:44 -07:00
|
|
|
return;
|
2013-08-09 02:50:59 -07:00
|
|
|
}
|
2013-04-29 20:42:44 -07:00
|
|
|
ok(false, "Left over tab after test: '" + location + "'");
|
|
|
|
badTabs.push(item);
|
|
|
|
});
|
|
|
|
|
|
|
|
badTabs.forEach(function(item, index, array) {
|
|
|
|
Browser.closeTab(item, { forceClose: true });
|
|
|
|
});
|
|
|
|
} catch (ex) {
|
|
|
|
ok(false, "Cleanup tabs failed - " + ex);
|
|
|
|
}
|
|
|
|
|
2013-02-12 12:51:25 -08:00
|
|
|
finish();
|
|
|
|
});
|
|
|
|
}
|
2013-03-18 09:30:41 -07:00
|
|
|
|
2013-06-26 21:54:06 -07:00
|
|
|
// wrap a method with a spy that records how and how many times it gets called
|
|
|
|
// the spy is returned; use spy.restore() to put the original back
|
|
|
|
function spyOnMethod(aObj, aMethod) {
|
|
|
|
let origFunc = aObj[aMethod];
|
|
|
|
let spy = function() {
|
|
|
|
spy.calledWith = Array.slice(arguments);
|
|
|
|
spy.callCount++;
|
|
|
|
return (spy.returnValue = origFunc.apply(aObj, arguments));
|
|
|
|
};
|
|
|
|
spy.callCount = 0;
|
|
|
|
spy.restore = function() {
|
|
|
|
return (aObj[aMethod] = origFunc);
|
|
|
|
};
|
|
|
|
return (aObj[aMethod] = spy);
|
|
|
|
}
|
|
|
|
|
|
|
|
// replace a method with a stub that records how and how many times it gets called
|
|
|
|
// the stub is returned; use stub.restore() to put the original back
|
2013-03-18 09:30:41 -07:00
|
|
|
function stubMethod(aObj, aMethod) {
|
|
|
|
let origFunc = aObj[aMethod];
|
|
|
|
let func = function() {
|
|
|
|
func.calledWith = Array.slice(arguments);
|
|
|
|
func.callCount++;
|
2013-08-21 18:48:14 -07:00
|
|
|
};
|
2013-03-18 09:30:41 -07:00
|
|
|
func.callCount = 0;
|
|
|
|
func.restore = function() {
|
|
|
|
return (aObj[aMethod] = origFunc);
|
|
|
|
};
|
|
|
|
aObj[aMethod] = func;
|
|
|
|
return func;
|
2013-05-16 21:22:21 -07:00
|
|
|
}
|