mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Main patch (original patch by mstange) - Bug 678392 - [10.7] Add support for swipe animation as in Safari. r=smichaud r=felipc
This commit is contained in:
parent
5e0f441d46
commit
e5e20d9c36
@ -532,6 +532,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", 20);
|
||||
|
||||
// 0: Nothing happens
|
||||
// 1: Scrolling contents
|
||||
// 2: Go back or go forward, in your history
|
||||
|
526
browser/base/content/browser-gestureSupport.js
Normal file
526
browser/base/content/browser-gestureSupport.js
Normal file
@ -0,0 +1,526 @@
|
||||
# -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
# 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/.
|
||||
|
||||
// History Swipe Animation Support (bug 678392)
|
||||
let gHistorySwipeAnimation = {
|
||||
|
||||
active: false,
|
||||
isLTR: false,
|
||||
|
||||
/**
|
||||
* Initializes the support for history swipe animations, if it is supported
|
||||
* by the platform/configuration.
|
||||
*/
|
||||
init: function HSA_init() {
|
||||
if (!this._isSupported())
|
||||
return;
|
||||
|
||||
gBrowser.addEventListener("pagehide", this, false);
|
||||
gBrowser.addEventListener("pageshow", this, false);
|
||||
gBrowser.addEventListener("popstate", this, false);
|
||||
gBrowser.tabContainer.addEventListener("TabClose", this, false);
|
||||
|
||||
this.active = true;
|
||||
this.isLTR = document.documentElement.mozMatchesSelector(
|
||||
":-moz-locale-dir(ltr)");
|
||||
this._trackedSnapshots = [];
|
||||
this._historyIndex = -1;
|
||||
this._boxWidth = -1;
|
||||
this._maxSnapshots = this._getMaxSnapshots();
|
||||
this._lastSwipeDir = "";
|
||||
},
|
||||
|
||||
/**
|
||||
* Uninitializes the support for history swipe animations.
|
||||
*/
|
||||
uninit: function HSA_uninit() {
|
||||
gBrowser.removeEventListener("pagehide", this, false);
|
||||
gBrowser.removeEventListener("pageshow", this, false);
|
||||
gBrowser.removeEventListener("popstate", this, false);
|
||||
gBrowser.tabContainer.removeEventListener("TabClose", this, false);
|
||||
|
||||
this.active = false;
|
||||
this.isLTR = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts the swipe animation and handles fast swiping (i.e. a swipe animation
|
||||
* is already in progress when a new one is initiated).
|
||||
*/
|
||||
startAnimation: function HSA_startAnimation() {
|
||||
if (this.isAnimationRunning()) {
|
||||
gBrowser.stop();
|
||||
this._lastSwipeDir = "RELOAD"; // just ensure that != ""
|
||||
this._canGoBack = this.canGoBack();
|
||||
this._canGoForward = this.canGoForward();
|
||||
this._handleFastSwiping();
|
||||
}
|
||||
else {
|
||||
this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
|
||||
this._canGoBack = this.canGoBack();
|
||||
this._canGoForward = this.canGoForward();
|
||||
this._takeSnapshot();
|
||||
this._installPrevAndNextSnapshots();
|
||||
this._addBoxes();
|
||||
this._lastSwipeDir = "";
|
||||
}
|
||||
this.updateAnimation(0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Stops the swipe animation.
|
||||
*/
|
||||
stopAnimation: function HSA_stopAnimation() {
|
||||
gHistorySwipeAnimation._removeBoxes();
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the animation between two pages in history.
|
||||
*
|
||||
* @param aVal
|
||||
* A floating point value that represents the progress of the
|
||||
* swipe gesture.
|
||||
*/
|
||||
updateAnimation: function HSA_updateAnimation(aVal) {
|
||||
if (!this.isAnimationRunning())
|
||||
return;
|
||||
|
||||
if ((aVal >= 0 && this.isLTR) ||
|
||||
(aVal <= 0 && !this.isLTR)) {
|
||||
if (aVal > 1)
|
||||
aVal = 1; // Cap value to avoid sliding the page further than allowed.
|
||||
|
||||
if (this._canGoBack)
|
||||
this._prevBox.collapsed = false;
|
||||
else
|
||||
this._prevBox.collapsed = true;
|
||||
|
||||
// The current page is pushed to the right (LTR) or left (RTL),
|
||||
// the intention is to go back.
|
||||
// If there is a page to go back to, it should show in the background.
|
||||
this._positionBox(this._curBox, aVal);
|
||||
|
||||
// The forward page should be pushed offscreen all the way to the right.
|
||||
this._positionBox(this._nextBox, 1);
|
||||
}
|
||||
else {
|
||||
if (aVal < -1)
|
||||
aVal = -1; // Cap value to avoid sliding the page further than allowed.
|
||||
|
||||
// The intention is to go forward. If there is a page to go forward to,
|
||||
// it should slide in from the right (LTR) or left (RTL).
|
||||
// Otherwise, the current page should slide to the left (LTR) or
|
||||
// right (RTL) and the backdrop should appear in the background.
|
||||
// For the backdrop to be visible in that case, the previous page needs
|
||||
// to be hidden (if it exists).
|
||||
if (this._canGoForward) {
|
||||
let offset = this.isLTR ? 1 : -1;
|
||||
this._positionBox(this._curBox, 0);
|
||||
this._positionBox(this._nextBox, offset + aVal); // aVal is negative
|
||||
}
|
||||
else {
|
||||
this._prevBox.collapsed = true;
|
||||
this._positionBox(this._curBox, aVal);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler for events relevant to the history swipe animation.
|
||||
*
|
||||
* @param aEvent
|
||||
* An event to process.
|
||||
*/
|
||||
handleEvent: function HSA_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "TabClose":
|
||||
let browser = gBrowser.getBrowserForTab(aEvent.target);
|
||||
this._removeTrackedSnapshot(-1, browser);
|
||||
break;
|
||||
case "pageshow":
|
||||
case "popstate":
|
||||
if (this.isAnimationRunning()) {
|
||||
if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
|
||||
break;
|
||||
this.stopAnimation();
|
||||
}
|
||||
this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
|
||||
break;
|
||||
case "pagehide":
|
||||
if (aEvent.target == gBrowser.selectedBrowser.contentDocument) {
|
||||
// Take a snapshot of a page whenever it's about to be navigated away
|
||||
// from.
|
||||
this._takeSnapshot();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether the history swipe animation is currently running or not.
|
||||
*
|
||||
* @return true if the animation is currently running, false otherwise.
|
||||
*/
|
||||
isAnimationRunning: function HSA_isAnimationRunning() {
|
||||
return !!this._container;
|
||||
},
|
||||
|
||||
/**
|
||||
* Process a swipe event based on the given direction.
|
||||
*
|
||||
* @param aEvent
|
||||
* The swipe event to handle
|
||||
* @param aDir
|
||||
* The direction for the swipe event
|
||||
*/
|
||||
processSwipeEvent: function HSA_processSwipeEvent(aEvent, aDir) {
|
||||
if (aDir == "RIGHT")
|
||||
this._historyIndex += this.isLTR ? 1 : -1;
|
||||
else if (aDir == "LEFT")
|
||||
this._historyIndex += this.isLTR ? -1 : 1;
|
||||
else
|
||||
return;
|
||||
this._lastSwipeDir = aDir;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if there is a page in the browser history to go back to.
|
||||
*
|
||||
* @return true if there is a previous page in history, false otherwise.
|
||||
*/
|
||||
canGoBack: function HSA_canGoBack() {
|
||||
if (this.isAnimationRunning())
|
||||
return this._doesIndexExistInHistory(this._historyIndex - 1);
|
||||
return gBrowser.webNavigation.canGoBack;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if there is a page in the browser history to go forward to.
|
||||
*
|
||||
* @return true if there is a next page in history, false otherwise.
|
||||
*/
|
||||
canGoForward: function HSA_canGoForward() {
|
||||
if (this.isAnimationRunning())
|
||||
return this._doesIndexExistInHistory(this._historyIndex + 1);
|
||||
return gBrowser.webNavigation.canGoForward;
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to notify the history swipe animation that the OS sent a swipe end
|
||||
* event and that we should navigate to the page that the user swiped to, if
|
||||
* any. This will also result in the animation overlay to be torn down.
|
||||
*/
|
||||
swipeEndEventReceived: function HSA_swipeEndEventReceived() {
|
||||
if (this._lastSwipeDir != "")
|
||||
this._navigateToHistoryIndex();
|
||||
else
|
||||
this.stopAnimation();
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether a particular index exists in the browser history or not.
|
||||
*
|
||||
* @param aIndex
|
||||
* The index to check for availability for in the history.
|
||||
* @return true if the index exists in the browser history, false otherwise.
|
||||
*/
|
||||
_doesIndexExistInHistory: function HSA__doesIndexExistInHistory(aIndex) {
|
||||
try {
|
||||
gBrowser.webNavigation.sessionHistory.getEntryAtIndex(aIndex, false);
|
||||
}
|
||||
catch(ex) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Navigates to the index in history that is currently being tracked by
|
||||
* |this|.
|
||||
*/
|
||||
_navigateToHistoryIndex: function HSA__navigateToHistoryIndex() {
|
||||
if (this._doesIndexExistInHistory(this._historyIndex)) {
|
||||
gBrowser.webNavigation.gotoIndex(this._historyIndex);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks to see if history swipe animations are supported by this
|
||||
* platform/configuration.
|
||||
*
|
||||
* return true if supported, false otherwise.
|
||||
*/
|
||||
_isSupported: function HSA__isSupported() {
|
||||
// Only activate on Lion.
|
||||
// TODO: Only if [NSEvent isSwipeTrackingFromScrollEventsEnabled]
|
||||
return window.matchMedia("(-moz-mac-lion-theme)").matches;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle fast swiping (i.e. a swipe animation is already in
|
||||
* progress when a new one is initiated). This will swap out the snapshots
|
||||
* used in the previous animation with the appropriate new ones.
|
||||
*/
|
||||
_handleFastSwiping: function HSA__handleFastSwiping() {
|
||||
this._installCurrentPageSnapshot(null);
|
||||
this._installPrevAndNextSnapshots();
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the boxes that contain the snapshots used during the swipe animation.
|
||||
*/
|
||||
_addBoxes: function HSA__addBoxes() {
|
||||
let browserStack =
|
||||
document.getAnonymousElementByAttribute(gBrowser.getNotificationBox(),
|
||||
"class", "browserStack");
|
||||
this._container = this._createElement("historySwipeAnimationContainer",
|
||||
"stack");
|
||||
browserStack.appendChild(this._container);
|
||||
|
||||
this._prevBox = this._createElement("historySwipeAnimationPreviousPage",
|
||||
"box");
|
||||
this._container.appendChild(this._prevBox);
|
||||
|
||||
this._curBox = this._createElement("historySwipeAnimationCurrentPage",
|
||||
"box");
|
||||
this._container.appendChild(this._curBox);
|
||||
|
||||
this._nextBox = this._createElement("historySwipeAnimationNextPage",
|
||||
"box");
|
||||
this._container.appendChild(this._nextBox);
|
||||
|
||||
this._boxWidth = this._curBox.getBoundingClientRect().width; // cache width
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the boxes.
|
||||
*/
|
||||
_removeBoxes: function HSA__removeBoxes() {
|
||||
this._curBox = null;
|
||||
this._prevBox = null;
|
||||
this._nextBox = null;
|
||||
if (this._container)
|
||||
this._container.parentNode.removeChild(this._container);
|
||||
this._container = null;
|
||||
this._boxWidth = -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates an element with a given identifier and tag name.
|
||||
*
|
||||
* @param aID
|
||||
* An identifier to create the element with.
|
||||
* @param aTagName
|
||||
* The name of the tag to create the element for.
|
||||
* @return the newly created element.
|
||||
*/
|
||||
_createElement: function HSA__createElement(aID, aTagName) {
|
||||
let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
let element = document.createElementNS(XULNS, aTagName);
|
||||
element.id = aID;
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Moves a given box to a given X coordinate position.
|
||||
*
|
||||
* @param aBox
|
||||
* The box element to position.
|
||||
* @param aPosition
|
||||
* The position (in X coordinates) to move the box element to.
|
||||
*/
|
||||
_positionBox: function HSA__positionBox(aBox, aPosition) {
|
||||
aBox.style.transform = "translateX(" + this._boxWidth * aPosition + "px)";
|
||||
},
|
||||
|
||||
/**
|
||||
* Takes a snapshot of the page the browser is currently on.
|
||||
*/
|
||||
_takeSnapshot: function HSA__takeSnapshot() {
|
||||
if ((this._maxSnapshots < 1) ||
|
||||
(gBrowser.webNavigation.sessionHistory.index < 0))
|
||||
return;
|
||||
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
let r = browser.getBoundingClientRect();
|
||||
let canvas = document.createElementNS("http://www.w3.org/1999/xhtml",
|
||||
"canvas");
|
||||
canvas.mozOpaque = true;
|
||||
canvas.width = r.width;
|
||||
canvas.height = r.height;
|
||||
let ctx = canvas.getContext("2d");
|
||||
let zoom = browser.markupDocumentViewer.fullZoom;
|
||||
ctx.scale(zoom, zoom);
|
||||
ctx.drawWindow(browser.contentWindow, 0, 0, r.width, r.height, "white",
|
||||
ctx.DRAWWINDOW_DO_NOT_FLUSH | ctx.DRAWWINDOW_DRAW_VIEW |
|
||||
ctx.DRAWWINDOW_ASYNC_DECODE_IMAGES |
|
||||
ctx.DRAWWINDOW_USE_WIDGET_LAYERS);
|
||||
|
||||
this._installCurrentPageSnapshot(canvas);
|
||||
this._assignSnapshotToCurrentBrowser(canvas);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves the maximum number of snapshots that should be kept in memory.
|
||||
* This limit is a global limit and is valid across all open tabs.
|
||||
*/
|
||||
_getMaxSnapshots: function HSA__getMaxSnapshots() {
|
||||
return gPrefService.getIntPref("browser.snapshots.limit");
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a snapshot to the list and initiates the compression of said snapshot.
|
||||
* Once the compression is completed, it will replace the uncompressed
|
||||
* snapshot in the list.
|
||||
*
|
||||
* @param aCanvas
|
||||
* The snapshot to add to the list and compress.
|
||||
*/
|
||||
_assignSnapshotToCurrentBrowser:
|
||||
function HSA__assignSnapshotToCurrentBrowser(aCanvas) {
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
let currIndex = browser.webNavigation.sessionHistory.index;
|
||||
|
||||
this._removeTrackedSnapshot(currIndex, browser);
|
||||
this._addSnapshotRefToArray(currIndex, browser);
|
||||
|
||||
if (!("snapshots" in browser))
|
||||
browser.snapshots = [];
|
||||
let snapshots = browser.snapshots;
|
||||
// Temporarily store the canvas as the compressed snapshot.
|
||||
// This avoids a blank page if the user swipes quickly
|
||||
// between pages before the compression could complete.
|
||||
snapshots[currIndex] = aCanvas;
|
||||
|
||||
// Kick off snapshot compression.
|
||||
aCanvas.toBlob(function(aBlob) {
|
||||
snapshots[currIndex] = aBlob;
|
||||
}, "image/png"
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes a snapshot identified by the browser and index in the array of
|
||||
* snapshots for that browser, if present. If no snapshot could be identified
|
||||
* the method simply returns without taking any action. If aIndex is negative,
|
||||
* all snapshots for a particular browser will be removed.
|
||||
*
|
||||
* @param aIndex
|
||||
* The index in history of the new snapshot, or negative value if all
|
||||
* snapshots for a browser should be removed.
|
||||
* @param aBrowser
|
||||
* The browser the new snapshot was taken in.
|
||||
*/
|
||||
_removeTrackedSnapshot: function HSA__removeTrackedSnapshot(aIndex, aBrowser) {
|
||||
let arr = this._trackedSnapshots;
|
||||
let requiresExactIndexMatch = aIndex >= 0;
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if ((arr[i].browser == aBrowser) &&
|
||||
(aIndex < 0 || aIndex == arr[i].index)) {
|
||||
delete aBrowser.snapshots[arr[i].index];
|
||||
arr.splice(i, 1);
|
||||
if (requiresExactIndexMatch)
|
||||
return; // Found and removed the only element.
|
||||
i--; // Make sure to revisit the index that we just removed an
|
||||
// element at.
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a new snapshot reference for a given index and browser to the array
|
||||
* of references to tracked snapshots.
|
||||
*
|
||||
* @param aIndex
|
||||
* The index in history of the new snapshot.
|
||||
* @param aBrowser
|
||||
* The browser the new snapshot was taken in.
|
||||
*/
|
||||
_addSnapshotRefToArray:
|
||||
function HSA__addSnapshotRefToArray(aIndex, aBrowser) {
|
||||
let id = { index: aIndex,
|
||||
browser: aBrowser };
|
||||
let arr = this._trackedSnapshots;
|
||||
arr.unshift(id);
|
||||
|
||||
while (arr.length > this._maxSnapshots) {
|
||||
let lastElem = arr[arr.length - 1];
|
||||
delete lastElem.browser.snapshots[lastElem.index];
|
||||
arr.splice(-1, 1);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a compressed blob to an Image object. In some situations
|
||||
* (especially during fast swiping) aBlob may still be a canvas, not a
|
||||
* compressed blob. In this case, we simply return the canvas.
|
||||
*
|
||||
* @param aBlob
|
||||
* The compressed blob to convert, or a canvas if a blob compression
|
||||
* couldn't complete before this method was called.
|
||||
* @return A new Image object representing the converted blob.
|
||||
*/
|
||||
_convertToImg: function HSA__convertToImg(aBlob) {
|
||||
if (!aBlob)
|
||||
return null;
|
||||
|
||||
// Return aBlob if it's still a canvas and not a compressed blob yet.
|
||||
if (aBlob instanceof HTMLCanvasElement)
|
||||
return aBlob;
|
||||
|
||||
let img = new Image();
|
||||
let url = URL.createObjectURL(aBlob);
|
||||
img.onload = function() {
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
img.src = url;
|
||||
return img;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the snapshot of the current page to the snapshot passed as parameter,
|
||||
* or to the one previously stored for the current index in history if the
|
||||
* parameter is null.
|
||||
*
|
||||
* @param aCanvas
|
||||
* The snapshot to set the current page to. If this parameter is null,
|
||||
* the previously stored snapshot for this index (if any) will be used.
|
||||
*/
|
||||
_installCurrentPageSnapshot:
|
||||
function HSA__installCurrentPageSnapshot(aCanvas) {
|
||||
let currSnapshot = aCanvas;
|
||||
if (!currSnapshot) {
|
||||
let snapshots = gBrowser.selectedBrowser.snapshots || {};
|
||||
let currIndex = this._historyIndex;
|
||||
if (currIndex in snapshots)
|
||||
currSnapshot = this._convertToImg(snapshots[currIndex]);
|
||||
}
|
||||
document.mozSetImageElement("historySwipeAnimationCurrentPageSnapshot",
|
||||
currSnapshot);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the snapshots of the previous and next pages to the snapshots
|
||||
* previously stored for their respective indeces.
|
||||
*/
|
||||
_installPrevAndNextSnapshots:
|
||||
function HSA__installPrevAndNextSnapshots() {
|
||||
let snapshots = gBrowser.selectedBrowser.snapshots || [];
|
||||
let currIndex = this._historyIndex;
|
||||
let prevIndex = currIndex - 1;
|
||||
let prevSnapshot = null;
|
||||
if (prevIndex in snapshots)
|
||||
prevSnapshot = this._convertToImg(snapshots[prevIndex]);
|
||||
document.mozSetImageElement("historySwipeAnimationPreviousPageSnapshot",
|
||||
prevSnapshot);
|
||||
|
||||
let nextIndex = currIndex + 1;
|
||||
let nextSnapshot = null;
|
||||
if (nextIndex in snapshots)
|
||||
nextSnapshot = this._convertToImg(snapshots[nextIndex]);
|
||||
document.mozSetImageElement("historySwipeAnimationNextPageSnapshot",
|
||||
nextSnapshot);
|
||||
},
|
||||
};
|
@ -366,6 +366,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 ,
|
||||
|
@ -156,6 +156,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
|
||||
@ -747,7 +748,8 @@ let gGestureSupport = {
|
||||
* True to add/init listeners and false to remove/uninit
|
||||
*/
|
||||
init: function GS_init(aAddListener) {
|
||||
const gestureEvents = ["SwipeGesture",
|
||||
const gestureEvents = ["SwipeGestureStart",
|
||||
"SwipeGestureUpdate", "SwipeGestureEnd", "SwipeGesture",
|
||||
"MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture",
|
||||
"RotateGestureStart", "RotateGestureUpdate", "RotateGesture",
|
||||
"TapGesture", "PressTapGesture"];
|
||||
@ -775,6 +777,18 @@ let gGestureSupport = {
|
||||
({ threshold: aThreshold, latched: !!aLatched });
|
||||
|
||||
switch (aEvent.type) {
|
||||
case "MozSwipeGestureStart":
|
||||
aEvent.preventDefault();
|
||||
this._setupSwipeGesture(aEvent);
|
||||
break;
|
||||
case "MozSwipeGestureUpdate":
|
||||
aEvent.preventDefault();
|
||||
this._doUpdate(aEvent);
|
||||
break;
|
||||
case "MozSwipeGestureEnd":
|
||||
aEvent.preventDefault();
|
||||
this._doEnd(aEvent);
|
||||
break;
|
||||
case "MozSwipeGesture":
|
||||
aEvent.preventDefault();
|
||||
this.onSwipe(aEvent);
|
||||
@ -861,6 +875,56 @@ let gGestureSupport = {
|
||||
this._doUpdate(aEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether a swipe gesture event can navigate the browser history or
|
||||
* not.
|
||||
*
|
||||
* @param aEvent
|
||||
* The swipe gesture event.
|
||||
* @return true if the swipe event may navigate the history, false othwerwise.
|
||||
*/
|
||||
_swipeNavigatesHistory: function GS__swipeNavigatesHistory(aEvent) {
|
||||
return this._getCommand(aEvent, ["swipe", "left"])
|
||||
== "Browser:BackOrBackDuplicate" &&
|
||||
this._getCommand(aEvent, ["swipe", "right"])
|
||||
== "Browser:ForwardOrForwardDuplicate";
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets up the history swipe animations for a swipe gesture event, if enabled.
|
||||
*
|
||||
* @param aEvent
|
||||
* The swipe gesture start event.
|
||||
*/
|
||||
_setupSwipeGesture: function GS__setupSwipeGesture(aEvent) {
|
||||
if (!this._swipeNavigatesHistory(aEvent) || !gHistorySwipeAnimation.active)
|
||||
return;
|
||||
|
||||
let canGoBack = gHistorySwipeAnimation.canGoBack();
|
||||
let canGoForward = gHistorySwipeAnimation.canGoForward();
|
||||
let isLTR = gHistorySwipeAnimation.isLTR;
|
||||
|
||||
if (canGoBack)
|
||||
aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_LEFT :
|
||||
aEvent.DIRECTION_RIGHT;
|
||||
if (canGoForward)
|
||||
aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_RIGHT :
|
||||
aEvent.DIRECTION_LEFT;
|
||||
|
||||
gHistorySwipeAnimation.startAnimation();
|
||||
|
||||
this._doUpdate = function GS__doUpdate(aEvent) {
|
||||
gHistorySwipeAnimation.updateAnimation(aEvent.delta);
|
||||
};
|
||||
|
||||
this._doEnd = function GS__doEnd(aEvent) {
|
||||
gHistorySwipeAnimation.swipeEndEventReceived();
|
||||
|
||||
this._doUpdate = function (aEvent) {};
|
||||
this._doEnd = function (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.
|
||||
@ -882,6 +946,22 @@ let gGestureSupport = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine what action to do for the gesture based on which keys are
|
||||
* pressed and which commands are set, and execute the command.
|
||||
*
|
||||
* @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)
|
||||
* @return Name of the executed command. Returns null if no command is
|
||||
* found.
|
||||
*/
|
||||
_doAction: function GS__doAction(aEvent, aGesture) {
|
||||
let command = this._getCommand(aEvent, aGesture);
|
||||
return command && this._doCommand(aEvent, command);
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine what action to do for the gesture based on which keys are
|
||||
* pressed and which commands are set
|
||||
@ -891,7 +971,7 @@ let gGestureSupport = {
|
||||
* @param aGesture
|
||||
* Array of gesture name parts (to be joined by periods)
|
||||
*/
|
||||
_doAction: function GS__doAction(aEvent, aGesture) {
|
||||
_getCommand: function GS__getCommand(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)
|
||||
@ -911,30 +991,40 @@ let gGestureSupport = {
|
||||
command = this._getPref(aGesture.concat(subCombo).join("."));
|
||||
} catch (e) {}
|
||||
|
||||
if (!command)
|
||||
continue;
|
||||
if (command)
|
||||
return command;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
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);
|
||||
/**
|
||||
* Execute the specified command.
|
||||
*
|
||||
* @param aEvent
|
||||
* The original gesture event to convert into a fake click event
|
||||
* @param aCommand
|
||||
* Name of the command found for the event's keys and gesture.
|
||||
*/
|
||||
_doCommand: function GS__doCommand(aEvent, aCommand) {
|
||||
let node = document.getElementById(aCommand);
|
||||
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, null);
|
||||
node.dispatchEvent(cmdEvent);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else {
|
||||
goDoCommand(aCommand);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Handle continual motion events. This function will be set by
|
||||
* _setupGesture or _setupSwipe.
|
||||
*
|
||||
* @param aEvent
|
||||
* The continual motion update event to handle
|
||||
@ -942,7 +1032,15 @@ let gGestureSupport = {
|
||||
_doUpdate: function(aEvent) {},
|
||||
|
||||
/**
|
||||
* Convert the swipe gesture into a browser action based on the direction
|
||||
* Handle gesture end events. This function will be set by _setupSwipe.
|
||||
*
|
||||
* @param aEvent
|
||||
* The gesture end event to handle
|
||||
*/
|
||||
_doEnd: function(aEvent) {},
|
||||
|
||||
/**
|
||||
* Convert the swipe gesture into a browser action based on the direction.
|
||||
*
|
||||
* @param aEvent
|
||||
* The swipe event to handle
|
||||
@ -951,12 +1049,46 @@ let gGestureSupport = {
|
||||
// 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()]);
|
||||
this._coordinateSwipeEventWithAnimation(aEvent, dir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Process a swipe event based on the given direction.
|
||||
*
|
||||
* @param aEvent
|
||||
* The swipe event to handle
|
||||
* @param aDir
|
||||
* The direction for the swipe event
|
||||
*/
|
||||
processSwipeEvent: function GS_processSwipeEvent(aEvent, aDir) {
|
||||
this._doAction(aEvent, ["swipe", aDir.toLowerCase()]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Coordinates the swipe event with the swipe animation, if any.
|
||||
* If an animation is currently running, the swipe event will be
|
||||
* processed once the animation stops. This will guarantee a fluid
|
||||
* motion of the animation.
|
||||
*
|
||||
* @param aEvent
|
||||
* The swipe event to handle
|
||||
* @param aDir
|
||||
* The direction for the swipe event
|
||||
*/
|
||||
_coordinateSwipeEventWithAnimation:
|
||||
function GS__coordinateSwipeEventWithAnimation(aEvent, aDir) {
|
||||
if ((gHistorySwipeAnimation.isAnimationRunning()) &&
|
||||
(aDir == "RIGHT" || aDir == "LEFT")) {
|
||||
gHistorySwipeAnimation.processSwipeEvent(aEvent, aDir);
|
||||
}
|
||||
else {
|
||||
this.processSwipeEvent(aEvent, aDir);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a gesture preference or use a default if it doesn't exist
|
||||
*
|
||||
@ -1196,6 +1328,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.
|
||||
@ -1656,6 +1791,8 @@ var gBrowserInit = {
|
||||
|
||||
gGestureSupport.init(false);
|
||||
|
||||
gHistorySwipeAnimation.uninit();
|
||||
|
||||
FullScreen.cleanup();
|
||||
|
||||
Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
|
||||
@ -1872,7 +2009,6 @@ var nonBrowserWindowDelayedStartup = gBrowserInit.nonBrowserWindowDelayedStartup
|
||||
var nonBrowserWindowShutdown = gBrowserInit.nonBrowserWindowShutdown.bind(gBrowserInit);
|
||||
#endif
|
||||
|
||||
|
||||
function HandleAppCommandEvent(evt) {
|
||||
switch (evt.command) {
|
||||
case "Back":
|
||||
|
@ -120,6 +120,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);
|
||||
@ -197,7 +205,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) {
|
||||
|
@ -2048,6 +2048,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,
|
||||
|
@ -40,6 +40,7 @@ browser.jar:
|
||||
skin/classic/browser/keyhole-circle.png
|
||||
skin/classic/browser/KUI-background.png
|
||||
skin/classic/browser/KUI-close.png
|
||||
skin/classic/browser/linen-pattern.png
|
||||
skin/classic/browser/menu-back.png
|
||||
skin/classic/browser/menu-forward.png
|
||||
skin/classic/browser/mixed-content-blocked-16.png
|
||||
|
BIN
browser/themes/pinstripe/linen-pattern.png
Normal file
BIN
browser/themes/pinstripe/linen-pattern.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
@ -1727,6 +1727,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")
|
||||
|
@ -755,6 +755,18 @@ NON_IDL_EVENT(MozScrolledAreaChanged,
|
||||
NS_SCROLLAREA_EVENT)
|
||||
|
||||
// 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,
|
||||
|
@ -39,6 +39,22 @@ 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)
|
||||
@ -82,6 +98,7 @@ nsDOMSimpleGestureEvent::InitSimpleGestureEvent(const nsAString& aTypeArg,
|
||||
bool aMetaKeyArg,
|
||||
uint16_t aButton,
|
||||
nsIDOMEventTarget* aRelatedTarget,
|
||||
uint32_t aAllowedDirectionsArg,
|
||||
uint32_t aDirectionArg,
|
||||
double aDeltaArg,
|
||||
uint32_t aClickCountArg)
|
||||
@ -104,6 +121,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;
|
||||
|
@ -1167,7 +1167,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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
@ -77,11 +107,28 @@ interface nsIDOMSimpleGestureEvent : nsIDOMMouseEvent
|
||||
const unsigned long DIRECTION_DOWN = 2;
|
||||
const unsigned long DIRECTION_LEFT = 4;
|
||||
const unsigned long DIRECTION_RIGHT = 8;
|
||||
|
||||
|
||||
/* Rotational direction constants */
|
||||
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,8 +141,8 @@ 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
|
||||
* rotation.
|
||||
@ -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);
|
||||
|
@ -55,7 +55,7 @@ function onDebugKeyPress(aEvent) {
|
||||
let evt = document.createEvent("SimpleGestureEvent");
|
||||
evt.initSimpleGestureEvent("MozSwipeGesture", true, true, window, null,
|
||||
0, 0, 0, 0, false, false, false, false, 0, null,
|
||||
aDirection, 0, 0);
|
||||
0, aDirection, 0, 0);
|
||||
Browser.selectedTab.inputHandler.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@ function onDebugKeyPress(aEvent) {
|
||||
function dispatchMagnifyEvent(aName, aDelta) {
|
||||
let evt = document.createEvent("SimpleGestureEvent");
|
||||
evt.initSimpleGestureEvent("MozMagnifyGesture" + aName, true, true, window, null,
|
||||
0, 0, 0, 0, false, false, false, false, 0, null, 0, aDelta, 0);
|
||||
0, 0, 0, 0, false, false, false, false, 0, null, 0, 0, aDelta, 0);
|
||||
Browser.selectedTab.inputHandler.dispatchEvent(evt);
|
||||
}
|
||||
dispatchMagnifyEvent("Start", 0);
|
||||
|
@ -260,9 +260,9 @@ typedef NSInteger NSEventGestureAxis;
|
||||
|
||||
BOOL mDidForceRefreshOpenGL;
|
||||
|
||||
// Support for fluid swipe tracking.
|
||||
#ifdef __LP64__
|
||||
BOOL *mSwipeAnimationCancelled;
|
||||
// Support for fluid swipe tracking.
|
||||
void (^mCancelSwipeAnimation)();
|
||||
#endif
|
||||
|
||||
// Whether this uses off-main-thread compositing.
|
||||
|
@ -2046,10 +2046,10 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
[self setFocusRingType:NSFocusRingTypeNone];
|
||||
|
||||
#ifdef __LP64__
|
||||
mSwipeAnimationCancelled = nil;
|
||||
mCancelSwipeAnimation = nil;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// register for things we'll take from other applications
|
||||
[ChildView registerViewForDraggedTypes:self];
|
||||
|
||||
@ -3029,6 +3029,45 @@ 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
|
||||
{
|
||||
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
|
||||
@ -3039,7 +3078,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
|
||||
{
|
||||
@ -3058,13 +3096,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
|
||||
@ -3097,6 +3128,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
|
||||
@ -3114,9 +3166,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
|
||||
@ -3125,48 +3177,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__
|
||||
|
||||
|
@ -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
|
||||
@ -1673,20 +1676,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
|
||||
|
Loading…
Reference in New Issue
Block a user