Bug 678392 - Add support for swipe animation as in Safari. r=smichaud,jaws,felipc

This commit is contained in:
Stephen Pohl 2013-04-09 14:44:01 -05:00
parent 216694c961
commit 28ee11a080
32 changed files with 1618 additions and 476 deletions

View File

@ -17,4 +17,4 @@
#
# Modifying this file will now automatically clobber the buildbot machines \o/
#
Bug 856349 broke Windows b2g builds.
Bug 678392 - Added new attribute to nsIDOMSimpleGestureEvent interface.

View File

@ -540,6 +540,8 @@ pref("browser.gesture.twist.left", "cmd_gestureRotateLeft");
pref("browser.gesture.twist.end", "cmd_gestureRotateEnd");
pref("browser.gesture.tap", "cmd_fullZoomReset");
pref("browser.snapshots.limit", 0);
// 0: Nothing happens
// 1: Scrolling contents
// 2: Go back or go forward, in your history

File diff suppressed because it is too large Load Diff

View File

@ -358,6 +358,30 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
}
%endif
/* History Swipe Animation */
#historySwipeAnimationContainer {
overflow: hidden;
}
#historySwipeAnimationPreviousPage,
#historySwipeAnimationCurrentPage,
#historySwipeAnimationNextPage {
background: none top left no-repeat white;
}
#historySwipeAnimationPreviousPage {
background-image: -moz-element(#historySwipeAnimationPreviousPageSnapshot);
}
#historySwipeAnimationCurrentPage {
background-image: -moz-element(#historySwipeAnimationCurrentPageSnapshot);
}
#historySwipeAnimationNextPage {
background-image: -moz-element(#historySwipeAnimationNextPageSnapshot);
}
/* Identity UI */
#identity-popup-content-box.unknownIdentity > #identity-popup-connectedToLabel ,
#identity-popup-content-box.unknownIdentity > #identity-popup-runByLabel ,

View File

@ -157,6 +157,7 @@ let gInitialPages = [
#include browser-tabview.js
#include browser-thumbnails.js
#include browser-webrtcUI.js
#include browser-gestureSupport.js
#ifdef MOZ_DATA_REPORTING
#include browser-data-submission-info-bar.js
@ -724,396 +725,6 @@ const gFormSubmitObserver = {
}
};
// Simple gestures support
//
// As per bug #412486, web content must not be allowed to receive any
// simple gesture events. Multi-touch gesture APIs are in their
// infancy and we do NOT want to be forced into supporting an API that
// will probably have to change in the future. (The current Mac OS X
// API is undocumented and was reverse-engineered.) Until support is
// implemented in the event dispatcher to keep these events as
// chrome-only, we must listen for the simple gesture events during
// the capturing phase and call stopPropagation on every event.
let gGestureSupport = {
_currentRotation: 0,
_lastRotateDelta: 0,
_rotateMomentumThreshold: .75,
/**
* Add or remove mouse gesture event listeners
*
* @param aAddListener
* True to add/init listeners and false to remove/uninit
*/
init: function GS_init(aAddListener) {
const gestureEvents = ["SwipeGesture",
"MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture",
"RotateGestureStart", "RotateGestureUpdate", "RotateGesture",
"TapGesture", "PressTapGesture"];
let addRemove = aAddListener ? window.addEventListener :
window.removeEventListener;
gestureEvents.forEach(function (event) addRemove("Moz" + event, this, true),
this);
},
/**
* Dispatch events based on the type of mouse gesture event. For now, make
* sure to stop propagation of every gesture event so that web content cannot
* receive gesture events.
*
* @param aEvent
* The gesture event to handle
*/
handleEvent: function GS_handleEvent(aEvent) {
if (!Services.prefs.getBoolPref(
"dom.debug.propagate_gesture_events_through_content")) {
aEvent.stopPropagation();
}
// Create a preference object with some defaults
let def = function(aThreshold, aLatched)
({ threshold: aThreshold, latched: !!aLatched });
switch (aEvent.type) {
case "MozSwipeGesture":
aEvent.preventDefault();
this.onSwipe(aEvent);
break;
case "MozMagnifyGestureStart":
aEvent.preventDefault();
#ifdef XP_WIN
this._setupGesture(aEvent, "pinch", def(25, 0), "out", "in");
#else
this._setupGesture(aEvent, "pinch", def(150, 1), "out", "in");
#endif
break;
case "MozRotateGestureStart":
aEvent.preventDefault();
this._setupGesture(aEvent, "twist", def(25, 0), "right", "left");
break;
case "MozMagnifyGestureUpdate":
case "MozRotateGestureUpdate":
aEvent.preventDefault();
this._doUpdate(aEvent);
break;
case "MozTapGesture":
aEvent.preventDefault();
this._doAction(aEvent, ["tap"]);
break;
case "MozRotateGesture":
aEvent.preventDefault();
this._doAction(aEvent, ["twist", "end"]);
break;
/* case "MozPressTapGesture":
break; */
}
},
/**
* Called at the start of "pinch" and "twist" gestures to setup all of the
* information needed to process the gesture
*
* @param aEvent
* The continual motion start event to handle
* @param aGesture
* Name of the gesture to handle
* @param aPref
* Preference object with the names of preferences and defaults
* @param aInc
* Command to trigger for increasing motion (without gesture name)
* @param aDec
* Command to trigger for decreasing motion (without gesture name)
*/
_setupGesture: function GS__setupGesture(aEvent, aGesture, aPref, aInc, aDec) {
// Try to load user-set values from preferences
for (let [pref, def] in Iterator(aPref))
aPref[pref] = this._getPref(aGesture + "." + pref, def);
// Keep track of the total deltas and latching behavior
let offset = 0;
let latchDir = aEvent.delta > 0 ? 1 : -1;
let isLatched = false;
// Create the update function here to capture closure state
this._doUpdate = function GS__doUpdate(aEvent) {
// Update the offset with new event data
offset += aEvent.delta;
// Check if the cumulative deltas exceed the threshold
if (Math.abs(offset) > aPref["threshold"]) {
// Trigger the action if we don't care about latching; otherwise, make
// sure either we're not latched and going the same direction of the
// initial motion; or we're latched and going the opposite way
let sameDir = (latchDir ^ offset) >= 0;
if (!aPref["latched"] || (isLatched ^ sameDir)) {
this._doAction(aEvent, [aGesture, offset > 0 ? aInc : aDec]);
// We must be getting latched or leaving it, so just toggle
isLatched = !isLatched;
}
// Reset motion counter to prepare for more of the same gesture
offset = 0;
}
};
// The start event also contains deltas, so handle an update right away
this._doUpdate(aEvent);
},
/**
* Generator producing the powerset of the input array where the first result
* is the complete set and the last result (before StopIteration) is empty.
*
* @param aArray
* Source array containing any number of elements
* @yield Array that is a subset of the input array from full set to empty
*/
_power: function GS__power(aArray) {
// Create a bitmask based on the length of the array
let num = 1 << aArray.length;
while (--num >= 0) {
// Only select array elements where the current bit is set
yield aArray.reduce(function (aPrev, aCurr, aIndex) {
if (num & 1 << aIndex)
aPrev.push(aCurr);
return aPrev;
}, []);
}
},
/**
* Determine what action to do for the gesture based on which keys are
* pressed and which commands are set
*
* @param aEvent
* The original gesture event to convert into a fake click event
* @param aGesture
* Array of gesture name parts (to be joined by periods)
*/
_doAction: function GS__doAction(aEvent, aGesture) {
// Create an array of pressed keys in a fixed order so that a command for
// "meta" is preferred over "ctrl" when both buttons are pressed (and a
// command for both don't exist)
let keyCombos = [];
["shift", "alt", "ctrl", "meta"].forEach(function (key) {
if (aEvent[key + "Key"])
keyCombos.push(key);
});
// Try each combination of key presses in decreasing order for commands
for (let subCombo of this._power(keyCombos)) {
// Convert a gesture and pressed keys into the corresponding command
// action where the preference has the gesture before "shift" before
// "alt" before "ctrl" before "meta" all separated by periods
let command;
try {
command = this._getPref(aGesture.concat(subCombo).join("."));
} catch (e) {}
if (!command)
continue;
let node = document.getElementById(command);
if (node) {
if (node.getAttribute("disabled") != "true") {
let cmdEvent = document.createEvent("xulcommandevent");
cmdEvent.initCommandEvent("command", true, true, window, 0,
aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
aEvent.metaKey, aEvent);
node.dispatchEvent(cmdEvent);
}
} else {
goDoCommand(command);
}
break;
}
},
/**
* Convert continual motion events into an action if it exceeds a threshold
* in a given direction. This function will be set by _setupGesture to
* capture state that needs to be shared across multiple gesture updates.
*
* @param aEvent
* The continual motion update event to handle
*/
_doUpdate: function(aEvent) {},
/**
* Convert the swipe gesture into a browser action based on the direction
*
* @param aEvent
* The swipe event to handle
*/
onSwipe: function GS_onSwipe(aEvent) {
// Figure out which one (and only one) direction was triggered
for (let dir of ["UP", "RIGHT", "DOWN", "LEFT"]) {
if (aEvent.direction == aEvent["DIRECTION_" + dir]) {
this._doAction(aEvent, ["swipe", dir.toLowerCase()]);
break;
}
}
},
/**
* Get a gesture preference or use a default if it doesn't exist
*
* @param aPref
* Name of the preference to load under the gesture branch
* @param aDef
* Default value if the preference doesn't exist
*/
_getPref: function GS__getPref(aPref, aDef) {
// Preferences branch under which all gestures preferences are stored
const branch = "browser.gesture.";
try {
// Determine what type of data to load based on default value's type
let type = typeof aDef;
let getFunc = "get" + (type == "boolean" ? "Bool" :
type == "number" ? "Int" : "Char") + "Pref";
return gPrefService[getFunc](branch + aPref);
}
catch (e) {
return aDef;
}
},
/**
* Perform rotation for ImageDocuments
*
* @param aEvent
* The MozRotateGestureUpdate event triggering this call
*/
rotate: function(aEvent) {
if (!(content.document instanceof ImageDocument))
return;
let contentElement = content.document.body.firstElementChild;
if (!contentElement)
return;
// If we're currently snapping, cancel that snap
if (contentElement.classList.contains("completeRotation"))
this._clearCompleteRotation();
this.rotation = Math.round(this.rotation + aEvent.delta);
contentElement.style.transform = "rotate(" + this.rotation + "deg)";
this._lastRotateDelta = aEvent.delta;
},
/**
* Perform a rotation end for ImageDocuments
*/
rotateEnd: function() {
if (!(content.document instanceof ImageDocument))
return;
let contentElement = content.document.body.firstElementChild;
if (!contentElement)
return;
let transitionRotation = 0;
// The reason that 360 is allowed here is because when rotating between
// 315 and 360, setting rotate(0deg) will cause it to rotate the wrong
// direction around--spinning wildly.
if (this.rotation <= 45)
transitionRotation = 0;
else if (this.rotation > 45 && this.rotation <= 135)
transitionRotation = 90;
else if (this.rotation > 135 && this.rotation <= 225)
transitionRotation = 180;
else if (this.rotation > 225 && this.rotation <= 315)
transitionRotation = 270;
else
transitionRotation = 360;
// If we're going fast enough, and we didn't already snap ahead of rotation,
// then snap ahead of rotation to simulate momentum
if (this._lastRotateDelta > this._rotateMomentumThreshold &&
this.rotation > transitionRotation)
transitionRotation += 90;
else if (this._lastRotateDelta < -1 * this._rotateMomentumThreshold &&
this.rotation < transitionRotation)
transitionRotation -= 90;
// Only add the completeRotation class if it is is necessary
if (transitionRotation != this.rotation) {
contentElement.classList.add("completeRotation");
contentElement.addEventListener("transitionend", this._clearCompleteRotation);
}
contentElement.style.transform = "rotate(" + transitionRotation + "deg)";
this.rotation = transitionRotation;
},
/**
* Gets the current rotation for the ImageDocument
*/
get rotation() {
return this._currentRotation;
},
/**
* Sets the current rotation for the ImageDocument
*
* @param aVal
* The new value to take. Can be any value, but it will be bounded to
* 0 inclusive to 360 exclusive.
*/
set rotation(aVal) {
this._currentRotation = aVal % 360;
if (this._currentRotation < 0)
this._currentRotation += 360;
return this._currentRotation;
},
/**
* When the location/tab changes, need to reload the current rotation for the
* image
*/
restoreRotationState: function() {
if (!(content.document instanceof ImageDocument))
return;
let contentElement = content.document.body.firstElementChild;
let transformValue = content.window.getComputedStyle(contentElement, null)
.transform;
if (transformValue == "none") {
this.rotation = 0;
return;
}
// transformValue is a rotation matrix--split it and do mathemagic to
// obtain the real rotation value
transformValue = transformValue.split("(")[1]
.split(")")[0]
.split(",");
this.rotation = Math.round(Math.atan2(transformValue[1], transformValue[0]) *
(180 / Math.PI));
},
/**
* Removes the transition rule by removing the completeRotation class
*/
_clearCompleteRotation: function() {
let contentElement = content.document &&
content.document instanceof ImageDocument &&
content.document.body &&
content.document.body.firstElementChild;
if (!contentElement)
return;
contentElement.classList.remove("completeRotation");
contentElement.removeEventListener("transitionend", this._clearCompleteRotation);
},
};
var gBrowserInit = {
onLoad: function() {
// window.arguments[0]: URI to load (string), or an nsISupportsArray of
@ -1212,6 +823,9 @@ var gBrowserInit = {
// setup simple gestures support
gGestureSupport.init(true);
// setup history swipe animation
gHistorySwipeAnimation.init();
if (window.opener && !window.opener.closed) {
let openerSidebarBox = window.opener.document.getElementById("sidebar-box");
// If the opener had a sidebar, open the same sidebar in our window.
@ -1689,6 +1303,8 @@ var gBrowserInit = {
gGestureSupport.init(false);
gHistorySwipeAnimation.uninit();
FullScreen.cleanup();
Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
@ -1908,7 +1524,6 @@ var nonBrowserWindowDelayedStartup = gBrowserInit.nonBrowserWindowDelayedStartup
var nonBrowserWindowShutdown = gBrowserInit.nonBrowserWindowShutdown.bind(gBrowserInit);
#endif
function HandleAppCommandEvent(evt) {
switch (evt.command) {
case "Back":

View File

@ -145,6 +145,9 @@ _BROWSER_FILES = \
browser_bug647886.js \
browser_bug655584.js \
browser_bug664672.js \
browser_bug678392.js \
browser_bug678392-1.html \
browser_bug678392-2.html \
browser_bug710878.js \
browser_bug719271.js \
browser_bug724239.js \

View File

@ -0,0 +1,12 @@
<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<title>bug678392 - 1</title>
</head>
<body>
bug 678392 test page 1
</body>
</html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<title>bug678392 - 2</title>
</head>
<body>
bug 678392 test page 2
</body>
</html>

View File

@ -0,0 +1,192 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let HTTPROOT = "http://example.com/browser/browser/base/content/test/";
function maxSnapshotOverride() {
return 5;
}
function test() {
waitForExplicitFinish();
BrowserOpenTab();
let tab = gBrowser.selectedTab;
registerCleanupFunction(function () { gBrowser.removeTab(tab); });
ok(gHistorySwipeAnimation, "gHistorySwipeAnimation exists.");
if (!gHistorySwipeAnimation._isSupported()) {
is(gHistorySwipeAnimation.active, false, "History swipe animation is not " +
"active when not supported by the platform.");
finish();
return;
}
gHistorySwipeAnimation._getMaxSnapshots = maxSnapshotOverride;
gHistorySwipeAnimation.init();
is(gHistorySwipeAnimation.active, true, "History swipe animation support " +
"was successfully initialized when supported.");
cleanupArray();
load(gBrowser.selectedTab, HTTPROOT + "browser_bug678392-2.html", test0);
}
function load(aTab, aUrl, aCallback) {
aTab.linkedBrowser.addEventListener("pageshow", function onpageshow(aEvent) {
aEvent.currentTarget.removeEventListener("pageshow", onpageshow, false);
waitForFocus(aCallback, content);
}, false);
aTab.linkedBrowser.loadURI(aUrl);
}
function cleanupArray() {
let arr = gHistorySwipeAnimation._trackedSnapshots;
while (arr.length > 0) {
delete arr[0].browser.snapshots[arr[0].index]; // delete actual snapshot
arr.splice(0, 1);
}
}
function testArrayCleanup() {
// Test cleanup of array of tracked snapshots.
let arr = gHistorySwipeAnimation._trackedSnapshots;
is(arr.length, 0, "Snapshots were removed correctly from the array of " +
"tracked snapshots.");
}
function test0() {
// Test growing of array of tracked snapshots.
let tab = gBrowser.selectedTab;
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
ok(gHistorySwipeAnimation._trackedSnapshots, "Array for snapshot " +
"tracking is initialized.");
is(gHistorySwipeAnimation._trackedSnapshots.length, 1, "Snapshot array " +
"has correct length of 1 after loading one page.");
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
is(gHistorySwipeAnimation._trackedSnapshots.length, 2, "Snapshot array " +
" has correct length of 2 after loading two pages.");
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
is(gHistorySwipeAnimation._trackedSnapshots.length, 3, "Snapshot " +
"array has correct length of 3 after loading three pages.");
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
is(gHistorySwipeAnimation._trackedSnapshots.length, 4, "Snapshot " +
"array has correct length of 4 after loading four pages.");
cleanupArray();
testArrayCleanup();
test1();
});
});
});
});
}
function verifyRefRemoved(aIndex, aBrowser) {
let wasFound = false;
let arr = gHistorySwipeAnimation._trackedSnapshots;
for (let i = 0; i < arr.length; i++) {
if (arr[i].index == aIndex && arr[i].browser == aBrowser)
wasFound = true;
}
is(wasFound, false, "The reference that was previously removed was " +
"still found in the array of tracked snapshots.");
}
function test1() {
// Test presence of snpashots in per-tab array of snapshots and removal of
// individual snapshots (and corresponding references in the array of
// tracked snapshots).
let tab = gBrowser.selectedTab;
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
var historyIndex = gBrowser.webNavigation.sessionHistory.index - 1;
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
let browser = gBrowser.selectedBrowser;
ok(browser.snapshots, "Array of snapshots exists in browser.");
ok(browser.snapshots[historyIndex], "First page exists in snapshot " +
"array.");
ok(browser.snapshots[historyIndex + 1], "Second page exists in " +
"snapshot array.");
ok(browser.snapshots[historyIndex + 2], "Third page exists in " +
"snapshot array.");
ok(browser.snapshots[historyIndex + 3], "Fourth page exists in " +
"snapshot array.");
is(gHistorySwipeAnimation._trackedSnapshots.length, 4, "Length of " +
"array of tracked snapshots is equal to 4 after loading four " +
"pages.");
// Test removal of reference in the middle of the array.
gHistorySwipeAnimation._removeTrackedSnapshot(historyIndex + 1,
browser);
verifyRefRemoved(historyIndex + 1, browser);
is(gHistorySwipeAnimation._trackedSnapshots.length, 3, "Length of " +
"array of tracked snapshots is equal to 3 after removing one" +
"reference from the array with length 4.");
// Test removal of reference at end of array.
gHistorySwipeAnimation._removeTrackedSnapshot(historyIndex + 3,
browser);
verifyRefRemoved(historyIndex + 3, browser);
is(gHistorySwipeAnimation._trackedSnapshots.length, 2, "Length of " +
"array of tracked snapshots is equal to 2 after removing two" +
"references from the array with length 4.");
// Test removal of reference at head of array.
gHistorySwipeAnimation._removeTrackedSnapshot(historyIndex,
browser);
verifyRefRemoved(historyIndex, browser);
is(gHistorySwipeAnimation._trackedSnapshots.length, 1, "Length of " +
"array of tracked snapshots is equal to 1 after removing three" +
"references from the array with length 4.");
cleanupArray();
test2();
});
});
});
});
}
function test2() {
// Test growing of snapshot array across tabs.
let tab = gBrowser.selectedTab;
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
var historyIndex = gBrowser.webNavigation.sessionHistory.index - 1;
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
is(gHistorySwipeAnimation._trackedSnapshots.length, 2, "Length of " +
"snapshot array is equal to 2 after loading two pages");
let prevTab = tab;
tab = gBrowser.addTab("about:newtab");
gBrowser.selectedTab = tab;
load(tab, HTTPROOT + "browser_bug678392-2.html" /* initial page */,
function() {
load(tab, HTTPROOT + "browser_bug678392-1.html", function() {
load(tab, HTTPROOT + "browser_bug678392-2.html", function() {
is(gHistorySwipeAnimation._trackedSnapshots.length, 4, "Length " +
"of snapshot array is equal to 4 after loading two pages in " +
"two tabs each.");
gBrowser.removeCurrentTab();
gBrowser.selectedTab = prevTab;
cleanupArray();
test3();
});
});
});
});
});
}
function test3() {
// Test uninit of gHistorySwipeAnimation.
// This test MUST be the last one to execute.
gHistorySwipeAnimation.uninit();
is(gHistorySwipeAnimation.active, false, "History swipe animation support " +
"was successfully uninitialized");
finish();
}

View File

@ -122,6 +122,14 @@ function test_TestEventListeners()
{
let e = test_helper1; // easier to type this name
// Swipe gesture animation events
e("MozSwipeGestureStart", 0, -0.7, 0);
e("MozSwipeGestureUpdate", 0, -0.4, 0);
e("MozSwipeGestureEnd", 0, 0, 0);
e("MozSwipeGestureStart", 0, 0.6, 0);
e("MozSwipeGestureUpdate", 0, 0.3, 0);
e("MozSwipeGestureEnd", 0, 1, 0);
// Swipe gesture event
e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_LEFT, 0.0, 0);
e("MozSwipeGesture", SimpleGestureEvent.DIRECTION_RIGHT, 0.0, 0);
@ -199,7 +207,7 @@ function test_helper2(type, direction, delta, altKey, ctrlKey, shiftKey, metaKey
10, 10, 10, 10,
ctrlKey, altKey, shiftKey, metaKey,
1, window,
direction, delta, 0);
0, direction, delta, 0);
successful = true;
}
catch (ex) {

View File

@ -2056,6 +2056,17 @@ window[tabsontop="false"] richlistitem[type~="action"][actiontype="switchtab"][s
}
}
/* History Swipe Animation */
#historySwipeAnimationCurrentPage,
#historySwipeAnimationNextPage {
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.6);
}
#historySwipeAnimationContainer {
background: url("chrome://browser/skin/linen-pattern.png") #B3B9C1;
}
/* ----- SIDEBAR ELEMENTS ----- */
#sidebar,

View File

@ -39,6 +39,7 @@ browser.jar:
skin/classic/browser/Info.png
skin/classic/browser/keyhole-circle.png
skin/classic/browser/KUI-background.png
skin/classic/browser/linen-pattern.png
skin/classic/browser/menu-back.png
skin/classic/browser/menu-forward.png
skin/classic/browser/notification-16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -1723,6 +1723,9 @@ GK_ATOM(seconds, "seconds")
GK_ATOM(secondsFromDateTime, "seconds-from-dateTime")
// Simple gestures support
GK_ATOM(onMozSwipeGestureStart, "onMozSwipeGestureStart")
GK_ATOM(onMozSwipeGestureUpdate, "onMozSwipeGestureUpdate")
GK_ATOM(onMozSwipeGestureEnd, "onMozSwipeGestureEnd")
GK_ATOM(onMozSwipeGesture, "onMozSwipeGesture")
GK_ATOM(onMozMagnifyGestureStart, "onMozMagnifyGestureStart")
GK_ATOM(onMozMagnifyGestureUpdate, "onMozMagnifyGestureUpdate")
@ -1984,6 +1987,7 @@ GK_ATOM(windows_glass, "windows-glass")
GK_ATOM(touch_enabled, "touch-enabled")
GK_ATOM(maemo_classic, "maemo-classic")
GK_ATOM(menubar_drag, "menubar-drag")
GK_ATOM(swipe_animation_enabled, "swipe-animation-enabled")
// windows theme selector metrics
GK_ATOM(windows_classic, "windows-classic")
@ -2016,6 +2020,7 @@ GK_ATOM(_moz_menubar_drag, "-moz-menubar-drag")
GK_ATOM(_moz_device_pixel_ratio, "-moz-device-pixel-ratio")
GK_ATOM(_moz_device_orientation, "-moz-device-orientation")
GK_ATOM(_moz_is_resource_document, "-moz-is-resource-document")
GK_ATOM(_moz_swipe_animation_enabled, "-moz-swipe-animation-enabled")
// application commands
GK_ATOM(Back, "Back")

View File

@ -778,6 +778,18 @@ NON_IDL_EVENT(gamepaddisconnected,
#endif
// Simple gesture events
NON_IDL_EVENT(MozSwipeGestureStart,
NS_SIMPLE_GESTURE_SWIPE_START,
EventNameType_None,
NS_SIMPLE_GESTURE_EVENT)
NON_IDL_EVENT(MozSwipeGestureUpdate,
NS_SIMPLE_GESTURE_SWIPE_UPDATE,
EventNameType_None,
NS_SIMPLE_GESTURE_EVENT)
NON_IDL_EVENT(MozSwipeGestureEnd,
NS_SIMPLE_GESTURE_SWIPE_END,
EventNameType_None,
NS_SIMPLE_GESTURE_EVENT)
NON_IDL_EVENT(MozSwipeGesture,
NS_SIMPLE_GESTURE_SWIPE,
EventNameType_None,

View File

@ -43,6 +43,24 @@ NS_INTERFACE_MAP_BEGIN(nsDOMSimpleGestureEvent)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SimpleGestureEvent)
NS_INTERFACE_MAP_END_INHERITING(nsDOMMouseEvent)
/* attribute unsigned long allowedDirections; */
NS_IMETHODIMP
nsDOMSimpleGestureEvent::GetAllowedDirections(PRUint32 *aAllowedDirections)
{
NS_ENSURE_ARG_POINTER(aAllowedDirections);
*aAllowedDirections =
static_cast<nsSimpleGestureEvent*>(mEvent)->allowedDirections;
return NS_OK;
}
NS_IMETHODIMP
nsDOMSimpleGestureEvent::SetAllowedDirections(PRUint32 aAllowedDirections)
{
static_cast<nsSimpleGestureEvent*>(mEvent)->allowedDirections =
aAllowedDirections;
return NS_OK;
}
/* readonly attribute unsigned long direction; */
NS_IMETHODIMP
nsDOMSimpleGestureEvent::GetDirection(uint32_t *aDirection)
@ -86,6 +104,7 @@ nsDOMSimpleGestureEvent::InitSimpleGestureEvent(const nsAString& aTypeArg,
bool aMetaKeyArg,
uint16_t aButton,
nsIDOMEventTarget* aRelatedTarget,
uint32_t aAllowedDirectionsArg,
uint32_t aDirectionArg,
double aDeltaArg,
uint32_t aClickCountArg)
@ -108,6 +127,7 @@ nsDOMSimpleGestureEvent::InitSimpleGestureEvent(const nsAString& aTypeArg,
NS_ENSURE_SUCCESS(rv, rv);
nsSimpleGestureEvent* simpleGestureEvent = static_cast<nsSimpleGestureEvent*>(mEvent);
simpleGestureEvent->allowedDirections = aAllowedDirectionsArg;
simpleGestureEvent->direction = aDirectionArg;
simpleGestureEvent->delta = aDeltaArg;
simpleGestureEvent->clickCount = aClickCountArg;

View File

@ -31,6 +31,11 @@ public:
return mozilla::dom::SimpleGestureEventBinding::Wrap(aCx, aScope, this);
}
uint32_t AllowedDirections()
{
return static_cast<nsSimpleGestureEvent*>(mEvent)->allowedDirections;
}
uint32_t Direction()
{
return static_cast<nsSimpleGestureEvent*>(mEvent)->direction;
@ -61,6 +66,7 @@ public:
bool aMetaKey,
uint16_t aButton,
mozilla::dom::EventTarget* aRelatedTarget,
uint32_t aAllowedDirections,
uint32_t aDirection,
double aDelta,
uint32_t aClickCount,
@ -70,8 +76,8 @@ public:
aView, aDetail, aScreenX, aScreenY,
aClientX, aClientY, aCtrlKey, aAltKey,
aShiftKey, aMetaKey, aButton,
aRelatedTarget, aDirection,
aDelta, aClickCount);
aRelatedTarget, aAllowedDirections,
aDirection, aDelta, aClickCount);
}
};

View File

@ -1190,7 +1190,13 @@ nsDOMWindowUtils::SendSimpleGestureEvent(const nsAString& aType,
return NS_ERROR_FAILURE;
int32_t msg;
if (aType.EqualsLiteral("MozSwipeGesture"))
if (aType.EqualsLiteral("MozSwipeGestureStart"))
msg = NS_SIMPLE_GESTURE_SWIPE_START;
else if (aType.EqualsLiteral("MozSwipeGestureUpdate"))
msg = NS_SIMPLE_GESTURE_SWIPE_UPDATE;
else if (aType.EqualsLiteral("MozSwipeGestureEnd"))
msg = NS_SIMPLE_GESTURE_SWIPE_END;
else if (aType.EqualsLiteral("MozSwipeGesture"))
msg = NS_SIMPLE_GESTURE_SWIPE;
else if (aType.EqualsLiteral("MozMagnifyGestureStart"))
msg = NS_SIMPLE_GESTURE_MAGNIFY_START;

View File

@ -539,7 +539,8 @@ interface nsIDOMWindowUtils : nsISupports {
[optional] in long aExtraForgetSkippableCalls);
/** Synthesize a simple gesture event for a window. The event types
* supported are: MozSwipeGesture, MozMagnifyGestureStart,
* supported are: MozSwipeGestureStart, MozSwipeGestureUpdate,
* MozSwipeGestureEnd, MozSwipeGesture, MozMagnifyGestureStart,
* MozMagnifyGestureUpdate, MozMagnifyGesture, MozRotateGestureStart,
* MozRotateGestureUpdate, MozRotateGesture, MozPressTapGesture,
* MozTapGesture, and MozEdgeUIGesture.

View File

@ -12,8 +12,38 @@
*
* The following events are generated:
*
* MozSwipeGesture - Generated when the user completes a swipe across
* across the input device.
* MozSwipeGestureStart - Generated when the user starts a horizontal
* swipe across the input device. This event not only acts as a signal,
* but also asks two questions: Should a swipe really be started, and
* in which directions should the user be able to swipe? The first
* question is answered by event listeners by calling or not calling
* preventDefault() on the event. Since a swipe swallows all scroll
* events, the default action of the swipe start event is *not* to
* start a swipe. Call preventDefault() if you want a swipe to be
* started.
* The second question (swipe-able directions) is answered in the
* allowedDirections field.
* If this event has preventDefault() called on it (and thus starts
* a swipe), it guarantees a future MozSwipeGestureEnd event that
* will signal the end of a swipe animation.
*
* MozSwipeGestureUpdate - Generated periodically while the user is
* continuing a horizontal swipe gesture. The "delta" value represents
* the current absolute gesture amount. This event may even be sent
* after a MozSwipeGesture event fired in order to allow for fluid
* completion of a swipe animation. The direction value is meaningless
* on swipe update events.
*
* MozSwipeGestureEnd - Generated when the swipe animation is completed.
*
* MozSwipeGesture - Generated when the user releases a swipe across
* across the input device. This event signals that the actual swipe
* operation is complete, even though the animation might not be finished
* yet. This event can be sent without accompanying start / update / end
* events, and it can also be handled on its own if the consumer doesn't
* want to handle swipe animation events.
* Only the direction value has any significance, the delta value is
* meaningless.
*
* MozMagnifyGestureStart - Generated when the user begins the magnify
* ("pinch") gesture. The "delta" value represents the initial
@ -69,7 +99,7 @@
* consuming events.
*/
[scriptable, builtinclass, uuid(0cd3fde1-0c99-49cc-a74e-9a9348864307)]
[scriptable, builtinclass, uuid(d78656ab-9d68-4f03-83f9-7c7bee071aa7)]
interface nsIDOMSimpleGestureEvent : nsIDOMMouseEvent
{
/* Swipe direction constants */
@ -82,6 +112,23 @@ interface nsIDOMSimpleGestureEvent : nsIDOMMouseEvent
const unsigned long ROTATION_COUNTERCLOCKWISE = 1;
const unsigned long ROTATION_CLOCKWISE = 2;
/* Read-write value for swipe events.
*
* Reports the directions that can be swiped to; multiple directions
* should be OR'ed together.
*
* The allowedDirections field is designed to be set on SwipeGestureStart
* events by event listeners. Its value after event dispatch determines
* the behavior of the swipe animation that is about to begin.
* Specifically, if the user swipes in a direction that can't be swiped
* to, the animation will have a bounce effect.
* Future SwipeGestureUpdate, SwipeGesture and SwipeGestureEnd events
* will carry the allowDirections value that was set on the SwipeStart
* event. Changing this field on non-SwipeGestureStart events doesn't
* have any effect.
*/
attribute unsigned long allowedDirections;
/* Direction of a gesture. Diagonals are indicated by OR'ing the
* applicable constants together.
*
@ -94,7 +141,7 @@ interface nsIDOMSimpleGestureEvent : nsIDOMMouseEvent
*/
readonly attribute unsigned long direction;
/* Delta value for magnify and rotate gestures.
/* Delta value for magnify, rotate and swipe gestures.
*
* For rotation, the value is in degrees and is positive for
* clockwise rotation and negative for counterclockwise
@ -111,6 +158,14 @@ interface nsIDOMSimpleGestureEvent : nsIDOMMouseEvent
* 100.0, but it is only safe currently to rely on the delta being
* positive or negative.
*
* For swipe start, update and end events, the value is a fraction
* of one "page". If the resulting swipe will have DIRECTION_LEFT, the
* delta value will be positive; for DIRECTION_RIGHT, delta is negative.
* If this seems backwards to you, look at it this way: If the current
* page is pushed to the right during the animation (positive delta),
* the page left to the current page will be visible after the swipe
* (DIRECTION_LEFT).
*
* Units on Windows represent the difference between the initial
* and current/final width between the two touch points on the input
* device and are measured in pixels.
@ -135,6 +190,7 @@ interface nsIDOMSimpleGestureEvent : nsIDOMMouseEvent
in boolean metaKeyArg,
in unsigned short buttonArg,
in nsIDOMEventTarget relatedTargetArg,
in unsigned long allowedDirectionsArg,
in unsigned long directionArg,
in double deltaArg,
in unsigned long clickCount);

View File

@ -18,6 +18,8 @@ interface SimpleGestureEvent : MouseEvent
const unsigned long ROTATION_COUNTERCLOCKWISE = 1;
const unsigned long ROTATION_CLOCKWISE = 2;
attribute unsigned long allowedDirections;
readonly attribute unsigned long direction;
readonly attribute double delta;
@ -40,6 +42,7 @@ interface SimpleGestureEvent : MouseEvent
boolean metaKeyArg,
unsigned short buttonArg,
EventTarget? relatedTargetArg,
unsigned long allowedDirectionsArg,
unsigned long directionArg,
double deltaArg,
unsigned long clickCount);

View File

@ -1180,6 +1180,12 @@ InitSystemMetrics()
sSystemMetrics->AppendElement(nsGkAtoms::maemo_classic);
}
rv = LookAndFeel::GetInt(LookAndFeel::eIntID_SwipeAnimationEnabled,
&metricResult);
if (NS_SUCCEEDED(rv) && metricResult) {
sSystemMetrics->AppendElement(nsGkAtoms::swipe_animation_enabled);
}
#ifdef XP_WIN
if (NS_SUCCEEDED(
LookAndFeel::GetInt(LookAndFeel::eIntID_WindowsThemeIdentifier,

View File

@ -579,6 +579,14 @@ nsMediaFeatures::features[] = {
GetWindowsTheme
},
{
&nsGkAtoms::_moz_swipe_animation_enabled,
nsMediaFeature::eMinMaxNotAllowed,
nsMediaFeature::eBoolInteger,
{ &nsGkAtoms::swipe_animation_enabled },
GetSystemMetric
},
// Internal -moz-is-glyph media feature: applies only inside SVG glyphs.
// Internal because it is really only useful in the user agent anyway
// and therefore not worth standardizing.

View File

@ -569,6 +569,7 @@ function run() {
expression_should_be_parseable("-moz-windows-glass");
expression_should_be_parseable("-moz-touch-enabled");
expression_should_be_parseable("-moz-maemo-classic");
expression_should_be_parseable("-moz-swipe-animation-enabled");
expression_should_be_parseable("-moz-scrollbar-start-backward: 0");
expression_should_be_parseable("-moz-scrollbar-start-forward: 0");
@ -585,6 +586,7 @@ function run() {
expression_should_be_parseable("-moz-windows-glass: 0");
expression_should_be_parseable("-moz-touch-enabled: 0");
expression_should_be_parseable("-moz-maemo-classic: 0");
expression_should_be_parseable("-moz-swipe-animation-enabled: 0");
expression_should_be_parseable("-moz-scrollbar-start-backward: 1");
expression_should_be_parseable("-moz-scrollbar-start-forward: 1");
@ -601,6 +603,7 @@ function run() {
expression_should_be_parseable("-moz-windows-glass: 1");
expression_should_be_parseable("-moz-touch-enabled: 1");
expression_should_be_parseable("-moz-maemo-classic: 1");
expression_should_be_parseable("-moz-swipe-animation-enabled: 1");
expression_should_not_be_parseable("-moz-scrollbar-start-backward: -1");
expression_should_not_be_parseable("-moz-scrollbar-start-forward: -1");
@ -617,6 +620,7 @@ function run() {
expression_should_not_be_parseable("-moz-windows-glass: -1");
expression_should_not_be_parseable("-moz-touch-enabled: -1");
expression_should_not_be_parseable("-moz-maemo-classic: -1");
expression_should_not_be_parseable("-moz-swipe-animation-enabled: -1");
expression_should_not_be_parseable("-moz-scrollbar-start-backward: true");
expression_should_not_be_parseable("-moz-scrollbar-start-forward: true");
@ -633,6 +637,7 @@ function run() {
expression_should_not_be_parseable("-moz-windows-glass: true");
expression_should_not_be_parseable("-moz-touch-enabled: true");
expression_should_not_be_parseable("-moz-maemo-classic: true");
expression_should_not_be_parseable("-moz-swipe-animation-enabled: true");
// windows theme media queries
expression_should_be_parseable("-moz-windows-theme: aero");

View File

@ -355,7 +355,13 @@ public:
/**
* Dealy before showing a tooltip.
*/
eIntID_TooltipDelay
eIntID_TooltipDelay,
/*
* A Boolean value to determine whether Mac OS X Lion style swipe animations
* should be used.
*/
eIntID_SwipeAnimationEnabled
};
/**

View File

@ -261,9 +261,9 @@ typedef NSInteger NSEventGestureAxis;
BOOL mDidForceRefreshOpenGL;
BOOL mWaitingForPaint;
// Support for fluid swipe tracking.
#ifdef __LP64__
BOOL *mSwipeAnimationCancelled;
// Support for fluid swipe tracking.
void (^mCancelSwipeAnimation)();
#endif
// Whether this uses off-main-thread compositing.

View File

@ -2205,7 +2205,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
[self setFocusRingType:NSFocusRingTypeNone];
#ifdef __LP64__
mSwipeAnimationCancelled = nil;
mCancelSwipeAnimation = nil;
#endif
}
@ -3292,6 +3292,48 @@ NSEvent* gLastDragMouseDownEvent = nil;
NS_OBJC_END_TRY_ABORT_BLOCK;
}
#ifdef __LP64__
- (bool)sendSwipeEvent:(NSEvent*)aEvent
withKind:(PRUint32)aMsg
allowedDirections:(PRUint32*)aAllowedDirections
direction:(PRUint32)aDirection
delta:(PRFloat64)aDelta
{
if (!mGeckoChild)
return false;
nsSimpleGestureEvent geckoEvent(true, aMsg, mGeckoChild, aDirection, aDelta);
geckoEvent.allowedDirections = *aAllowedDirections;
[self convertCocoaMouseEvent:aEvent toGeckoEvent:&geckoEvent];
bool eventCancelled = mGeckoChild->DispatchWindowEvent(geckoEvent);
*aAllowedDirections = geckoEvent.allowedDirections;
return eventCancelled; // event cancelled == swipe should start
}
- (void)cancelSwipeIfRunning
{
// Clear gesture state.
mGestureState = eGestureState_None;
if (mCancelSwipeAnimation) {
mCancelSwipeAnimation();
[mCancelSwipeAnimation release];
mCancelSwipeAnimation = nil;
}
}
- (void)sendSwipeEndEvent:(NSEvent *)anEvent
allowedDirections:(PRUint32)aAllowedDirections
{
// Tear down animation overlay by sending a swipe end event.
PRUint32 allowedDirectionsCopy = aAllowedDirections;
[self sendSwipeEvent:anEvent
withKind:NS_SIMPLE_GESTURE_SWIPE_END
allowedDirections:&allowedDirectionsCopy
direction:0
delta:0.0];
}
// Support fluid swipe tracking on OS X 10.7 and higher. We must be careful
// to only invoke this support on a horizontal two-finger gesture that really
// is a swipe (and not a scroll) -- in other words, the app is responsible
@ -3302,7 +3344,6 @@ NSEvent* gLastDragMouseDownEvent = nil;
// partly based on Apple sample code available at
// http://developer.apple.com/library/mac/#releasenotes/Cocoa/AppKit.html
// (under Fluid Swipe Tracking API).
#ifdef __LP64__
- (void)maybeTrackScrollEventAsSwipe:(NSEvent *)anEvent
scrollOverflow:(double)overflow
{
@ -3321,13 +3362,6 @@ NSEvent* gLastDragMouseDownEvent = nil;
return;
}
// If a swipe is currently being tracked kill it -- it's been interrupted by
// another gesture or legacy scroll wheel event.
if (mSwipeAnimationCancelled && (*mSwipeAnimationCancelled == NO)) {
*mSwipeAnimationCancelled = YES;
mSwipeAnimationCancelled = nil;
}
// Only initiate tracking if the user has tried to scroll past the edge of
// the current page (as indicated by 'overflow' being non-zero). Gecko only
// sets nsMouseScrollEvent.scrollOverflow when it's processing
@ -3360,6 +3394,27 @@ NSEvent* gLastDragMouseDownEvent = nil;
return;
}
// If a swipe is currently being tracked kill it -- it's been interrupted by
// another gesture or legacy scroll wheel event.
[self cancelSwipeIfRunning];
// We're ready to start the animation. Tell Gecko about it, and at the same
// time ask it if it really wants to start an animation for this event.
// This event also reports back the directions that we can swipe in.
PRUint32 allowedDirections = 0;
bool shouldStartSwipe = [self sendSwipeEvent:anEvent
withKind:NS_SIMPLE_GESTURE_SWIPE_START
allowedDirections:&allowedDirections
direction:0
delta:0.0];
if (!shouldStartSwipe) {
return;
}
double min = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_RIGHT) ? -1 : 0;
double max = (allowedDirections & nsIDOMSimpleGestureEvent::DIRECTION_LEFT) ? 1 : 0;
__block BOOL animationCancelled = NO;
__block BOOL geckoSwipeEventSent = NO;
// At this point, anEvent is the first scroll wheel event in a two-finger
@ -3377,9 +3432,9 @@ NSEvent* gLastDragMouseDownEvent = nil;
// the anEvent object because it's retained by the block, see bug 682445.
// The block will release it when the block goes away at the end of the
// animation, or when the animation is canceled.
[anEvent trackSwipeEventWithOptions:0
dampenAmountThresholdMin:-1
max:1
[anEvent trackSwipeEventWithOptions:NSEventSwipeTrackingLockDirection
dampenAmountThresholdMin:min
max:max
usingHandler:^(CGFloat gestureAmount, NSEventPhase phase, BOOL isComplete, BOOL *stop) {
// Since this tracking handler can be called asynchronously, mGeckoChild
// might have become NULL here (our child widget might have been
@ -3388,48 +3443,56 @@ NSEvent* gLastDragMouseDownEvent = nil;
*stop = YES;
return;
}
// gestureAmount is documented to be '-1', '0' or '1' when isComplete
// is TRUE, but the docs don't say anything about its value at other
// times. However, tests show that, when phase == NSEventPhaseEnded,
// gestureAmount is negative when it will be '-1' at isComplete, and
// positive when it will be '1'. And phase is never equal to
// NSEventPhaseEnded when gestureAmount will be '0' at isComplete.
// Not waiting until isComplete is TRUE substantially reduces the
// time it takes to change pages after a swipe, and helps resolve
// bug 678891.
PRUint32 allowedDirectionsCopy = allowedDirections;
// Update animation overlay to match gestureAmount.
[self sendSwipeEvent:anEvent
withKind:NS_SIMPLE_GESTURE_SWIPE_UPDATE
allowedDirections:&allowedDirectionsCopy
direction:0
delta:gestureAmount];
if (phase == NSEventPhaseEnded && !geckoSwipeEventSent) {
if (gestureAmount) {
nsSimpleGestureEvent geckoEvent(true, NS_SIMPLE_GESTURE_SWIPE, mGeckoChild, 0, 0.0);
[self convertCocoaMouseEvent:anEvent toGeckoEvent:&geckoEvent];
if (gestureAmount > 0) {
geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_LEFT;
} else {
geckoEvent.direction |= nsIDOMSimpleGestureEvent::DIRECTION_RIGHT;
}
// If DispatchWindowEvent() does something to trigger a modal dialog
// (which spins the event loop), the OS gets confused and makes
// several re-entrant calls to this handler, all of which have
// 'phase' set to NSEventPhaseEnded. Unless we do something about
// it, this results in an equal number of re-entrant calls to
// DispatchWindowEvent(), and to our modal-event handling code.
// Probably because of bug 478703, this really messes things up,
// and requires a force quit to get out of. We avoid this by
// avoiding re-entrant calls to DispatchWindowEvent(). See bug
// 770626.
geckoSwipeEventSent = YES;
mGeckoChild->DispatchWindowEvent(geckoEvent);
}
mSwipeAnimationCancelled = nil;
} else if (phase == NSEventPhaseCancelled) {
mSwipeAnimationCancelled = nil;
// The result of the swipe is now known, so the main event can be sent.
// The animation might continue even after this event was sent, so
// don't tear down the animation overlay yet.
// gestureAmount is documented to be '-1', '0' or '1' when isComplete
// is TRUE, but the docs don't say anything about its value at other
// times. However, tests show that, when phase == NSEventPhaseEnded,
// gestureAmount is negative when it will be '-1' at isComplete, and
// positive when it will be '1'. And phase is never equal to
// NSEventPhaseEnded when gestureAmount will be '0' at isComplete.
PRUint32 direction = gestureAmount > 0 ?
(PRUint32)nsIDOMSimpleGestureEvent::DIRECTION_LEFT :
(PRUint32)nsIDOMSimpleGestureEvent::DIRECTION_RIGHT;
// If DispatchWindowEvent() does something to trigger a modal dialog
// (which spins the event loop), the OS gets confused and makes
// several re-entrant calls to this handler, all of which have
// 'phase' set to NSEventPhaseEnded. Unless we do something about
// it, this results in an equal number of re-entrant calls to
// DispatchWindowEvent(), and to our modal-event handling code.
// Probably because of bug 478703, this really messes things up,
// and requires a force quit to get out of. We avoid this by
// avoiding re-entrant calls to DispatchWindowEvent(). See bug
// 770626.
geckoSwipeEventSent = YES;
[self sendSwipeEvent:anEvent
withKind:NS_SIMPLE_GESTURE_SWIPE
allowedDirections:&allowedDirectionsCopy
direction:direction
delta:0.0];
}
if (isComplete) {
[self cancelSwipeIfRunning];
[self sendSwipeEndEvent:anEvent allowedDirections:allowedDirections];
}
}];
// We keep a pointer to the __block variable (animationCanceled) so we
// can cancel our block handler at any time. Note: We must assign
// &animationCanceled after our block creation and copy -- its address
// isn't resolved until then!
mSwipeAnimationCancelled = &animationCancelled;
mCancelSwipeAnimation = [^{
animationCancelled = YES;
} copy];
}
#endif // #ifdef __LP64__

View File

@ -414,6 +414,13 @@ nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult)
case eIntID_ScrollbarButtonAutoRepeatBehavior:
aResult = 0;
break;
case eIntID_SwipeAnimationEnabled:
aResult = 0;
if ([NSEvent respondsToSelector:@selector(
isSwipeTrackingFromScrollEventsEnabled)]) {
aResult = [NSEvent isSwipeTrackingFromScrollEventsEnabled] ? 1 : 0;
}
break;
default:
aResult = 0;
res = NS_ERROR_FAILURE;

View File

@ -566,6 +566,9 @@ nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult)
case eIntID_ScrollbarButtonAutoRepeatBehavior:
aResult = 1;
break;
case eIntID_SwipeAnimationEnabled:
aResult = 0;
break;
default:
aResult = 0;
res = NS_ERROR_FAILURE;

View File

@ -337,16 +337,19 @@ enum nsEventStructType {
// Simple gesture events
#define NS_SIMPLE_GESTURE_EVENT_START 3500
#define NS_SIMPLE_GESTURE_SWIPE (NS_SIMPLE_GESTURE_EVENT_START)
#define NS_SIMPLE_GESTURE_MAGNIFY_START (NS_SIMPLE_GESTURE_EVENT_START+1)
#define NS_SIMPLE_GESTURE_MAGNIFY_UPDATE (NS_SIMPLE_GESTURE_EVENT_START+2)
#define NS_SIMPLE_GESTURE_MAGNIFY (NS_SIMPLE_GESTURE_EVENT_START+3)
#define NS_SIMPLE_GESTURE_ROTATE_START (NS_SIMPLE_GESTURE_EVENT_START+4)
#define NS_SIMPLE_GESTURE_ROTATE_UPDATE (NS_SIMPLE_GESTURE_EVENT_START+5)
#define NS_SIMPLE_GESTURE_ROTATE (NS_SIMPLE_GESTURE_EVENT_START+6)
#define NS_SIMPLE_GESTURE_TAP (NS_SIMPLE_GESTURE_EVENT_START+7)
#define NS_SIMPLE_GESTURE_PRESSTAP (NS_SIMPLE_GESTURE_EVENT_START+8)
#define NS_SIMPLE_GESTURE_EDGEUI (NS_SIMPLE_GESTURE_EVENT_START+9)
#define NS_SIMPLE_GESTURE_SWIPE_START (NS_SIMPLE_GESTURE_EVENT_START)
#define NS_SIMPLE_GESTURE_SWIPE_UPDATE (NS_SIMPLE_GESTURE_EVENT_START+1)
#define NS_SIMPLE_GESTURE_SWIPE_END (NS_SIMPLE_GESTURE_EVENT_START+2)
#define NS_SIMPLE_GESTURE_SWIPE (NS_SIMPLE_GESTURE_EVENT_START+3)
#define NS_SIMPLE_GESTURE_MAGNIFY_START (NS_SIMPLE_GESTURE_EVENT_START+4)
#define NS_SIMPLE_GESTURE_MAGNIFY_UPDATE (NS_SIMPLE_GESTURE_EVENT_START+5)
#define NS_SIMPLE_GESTURE_MAGNIFY (NS_SIMPLE_GESTURE_EVENT_START+6)
#define NS_SIMPLE_GESTURE_ROTATE_START (NS_SIMPLE_GESTURE_EVENT_START+7)
#define NS_SIMPLE_GESTURE_ROTATE_UPDATE (NS_SIMPLE_GESTURE_EVENT_START+8)
#define NS_SIMPLE_GESTURE_ROTATE (NS_SIMPLE_GESTURE_EVENT_START+9)
#define NS_SIMPLE_GESTURE_TAP (NS_SIMPLE_GESTURE_EVENT_START+10)
#define NS_SIMPLE_GESTURE_PRESSTAP (NS_SIMPLE_GESTURE_EVENT_START+11)
#define NS_SIMPLE_GESTURE_EDGEUI (NS_SIMPLE_GESTURE_EVENT_START+12)
// These are used to send native events to plugins.
#define NS_PLUGIN_EVENT_START 3600
@ -1701,20 +1704,22 @@ public:
nsSimpleGestureEvent(bool isTrusted, uint32_t msg, nsIWidget* w,
uint32_t directionArg, double deltaArg)
: nsMouseEvent_base(isTrusted, msg, w, NS_SIMPLE_GESTURE_EVENT),
direction(directionArg), delta(deltaArg), clickCount(0)
allowedDirections(0), direction(directionArg), delta(deltaArg),
clickCount(0)
{
}
nsSimpleGestureEvent(const nsSimpleGestureEvent& other)
: nsMouseEvent_base(other.mFlags.mIsTrusted,
other.message, other.widget, NS_SIMPLE_GESTURE_EVENT),
direction(other.direction), delta(other.delta), clickCount(0)
allowedDirections(other.allowedDirections), direction(other.direction),
delta(other.delta), clickCount(0)
{
}
uint32_t direction; // See nsIDOMSimpleGestureEvent for values
double delta; // Delta for magnify and rotate events
uint32_t clickCount; // The number of taps for tap events
uint32_t allowedDirections; // See nsIDOMSimpleGestureEvent for values
uint32_t direction; // See nsIDOMSimpleGestureEvent for values
double delta; // Delta for magnify and rotate events
uint32_t clickCount; // The number of taps for tap events
};
class nsTransitionEvent : public nsEvent

View File

@ -329,6 +329,9 @@ nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult)
case eIntID_ScrollbarButtonAutoRepeatBehavior:
aResult = 0;
break;
case eIntID_SwipeAnimationEnabled:
aResult = 0;
break;
default:
aResult = 0;

View File

@ -455,6 +455,9 @@ nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult)
case eIntID_ScrollbarButtonAutoRepeatBehavior:
aResult = 0;
break;
case eIntID_SwipeAnimationEnabled:
aResult = 0;
break;
default:
aResult = 0;
res = NS_ERROR_FAILURE;