mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
826 lines
23 KiB
JavaScript
826 lines
23 KiB
JavaScript
/* 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/. */
|
|
|
|
/*
|
|
* Markers
|
|
*/
|
|
|
|
// Y axis scroll distance that will disable this module and cancel selection
|
|
const kDisableOnScrollDistance = 25;
|
|
|
|
function MarkerDragger(aMarker) {
|
|
this._marker = aMarker;
|
|
}
|
|
|
|
MarkerDragger.prototype = {
|
|
_selectionHelperUI: null,
|
|
_marker: null,
|
|
_shutdown: false,
|
|
_dragging: false,
|
|
|
|
get marker() {
|
|
return this._marker;
|
|
},
|
|
|
|
set shutdown(aVal) {
|
|
this._shutdown = aVal;
|
|
},
|
|
|
|
get shutdown() {
|
|
return this._shutdown;
|
|
},
|
|
|
|
get dragging() {
|
|
return this._dragging;
|
|
},
|
|
|
|
freeDrag: function freeDrag() {
|
|
return true;
|
|
},
|
|
|
|
isDraggable: function isDraggable(aTarget, aContent) {
|
|
return { x: true, y: true };
|
|
},
|
|
|
|
dragStart: function dragStart(aX, aY, aTarget, aScroller) {
|
|
if (this._shutdown)
|
|
return false;
|
|
this._dragging = true;
|
|
this.marker.dragStart(aX, aY);
|
|
return true;
|
|
},
|
|
|
|
dragStop: function dragStop(aDx, aDy, aScroller) {
|
|
if (this._shutdown)
|
|
return false;
|
|
this._dragging = false;
|
|
this.marker.dragStop(aDx, aDy);
|
|
return true;
|
|
},
|
|
|
|
dragMove: function dragMove(aDx, aDy, aScroller, aIsKenetic, aClientX, aClientY) {
|
|
// Note if aIsKenetic is true this is synthetic movement,
|
|
// we don't want that so return false.
|
|
if (this._shutdown || aIsKenetic)
|
|
return false;
|
|
this.marker.moveBy(aDx, aDy, aClientX, aClientY);
|
|
// return true if we moved, false otherwise. The result
|
|
// is used in deciding if we should repaint between drags.
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function Marker(aParent, aTag, aElementId, xPos, yPos) {
|
|
this._xPos = xPos;
|
|
this._yPos = yPos;
|
|
this._selectionHelperUI = aParent;
|
|
this._element = document.getElementById(aElementId);
|
|
this._elementId = aElementId;
|
|
// These get picked in input.js and receives drag input
|
|
this._element.customDragger = new MarkerDragger(this);
|
|
this.tag = aTag;
|
|
}
|
|
|
|
Marker.prototype = {
|
|
_element: null,
|
|
_elementId: "",
|
|
_selectionHelperUI: null,
|
|
_xPos: 0,
|
|
_yPos: 0,
|
|
_tag: "",
|
|
_hPlane: 0,
|
|
_vPlane: 0,
|
|
|
|
// Tweak me if the monocle graphics change in any way
|
|
_monocleRadius: 8,
|
|
_monocleXHitTextAdjust: -2,
|
|
_monocleYHitTextAdjust: -10,
|
|
|
|
get xPos() {
|
|
return this._xPos;
|
|
},
|
|
|
|
get yPos() {
|
|
return this._yPos;
|
|
},
|
|
|
|
get tag() {
|
|
return this._tag;
|
|
},
|
|
|
|
set tag(aVal) {
|
|
this._tag = aVal;
|
|
},
|
|
|
|
get dragging() {
|
|
return this._element.customDragger.dragging;
|
|
},
|
|
|
|
shutdown: function shutdown() {
|
|
this._element.hidden = true;
|
|
this._element.customDragger.shutdown = true;
|
|
delete this._element.customDragger;
|
|
this._selectionHelperUI = null;
|
|
this._element = null;
|
|
},
|
|
|
|
setTrackBounds: function setTrackBounds(aVerticalPlane, aHorizontalPlane) {
|
|
// monocle boundaries
|
|
this._hPlane = aHorizontalPlane;
|
|
this._vPlane = aVerticalPlane;
|
|
},
|
|
|
|
show: function show() {
|
|
this._element.hidden = false;
|
|
},
|
|
|
|
hide: function hide() {
|
|
this._element.hidden = true;
|
|
},
|
|
|
|
get visible() {
|
|
return this._element.hidden == false;
|
|
},
|
|
|
|
position: function position(aX, aY) {
|
|
if (aX < 0) {
|
|
Util.dumpLn("Marker: aX is negative");
|
|
aX = 0;
|
|
}
|
|
if (aY < 0) {
|
|
Util.dumpLn("Marker: aY is negative");
|
|
aY = 0;
|
|
}
|
|
this._xPos = aX;
|
|
this._yPos = aY;
|
|
this._setPosition();
|
|
},
|
|
|
|
_setPosition: function _setPosition() {
|
|
this._element.left = this._xPos + "px";
|
|
this._element.top = this._yPos + "px";
|
|
},
|
|
|
|
dragStart: function dragStart(aX, aY) {
|
|
this._selectionHelperUI.markerDragStart(this);
|
|
},
|
|
|
|
dragStop: function dragStop(aDx, aDy) {
|
|
this._selectionHelperUI.markerDragStop(this);
|
|
},
|
|
|
|
moveBy: function moveBy(aDx, aDy, aClientX, aClientY) {
|
|
this._xPos -= aDx;
|
|
this._yPos -= aDy;
|
|
let direction = (aDx < 0 || aDy < 0 ? "end" : "start");
|
|
// We may swap markers in markerDragMove. If markerDragMove
|
|
// returns true keep processing, otherwise get out of here.
|
|
if (this._selectionHelperUI.markerDragMove(this, direction)) {
|
|
this._setPosition();
|
|
}
|
|
},
|
|
|
|
hitTest: function hitTest(aX, aY) {
|
|
// Gets the pointer of the arrow right in the middle of the
|
|
// monocle.
|
|
aY += this._monocleYHitTextAdjust;
|
|
aX += this._monocleXHitTextAdjust;
|
|
if (aX >= (this._xPos - this._monocleRadius) &&
|
|
aX <= (this._xPos + this._monocleRadius) &&
|
|
aY >= (this._yPos - this._monocleRadius) &&
|
|
aY <= (this._yPos + this._monocleRadius))
|
|
return true;
|
|
return false;
|
|
},
|
|
|
|
swapMonocle: function swapMonocle(aCaret) {
|
|
let targetElement = aCaret._element;
|
|
let targetElementId = aCaret._elementId;
|
|
|
|
aCaret._element = this._element;
|
|
aCaret._element.customDragger._marker = aCaret;
|
|
aCaret._elementId = this._elementId;
|
|
|
|
this._xPos = aCaret._xPos;
|
|
this._yPos = aCaret._yPos;
|
|
this._element = targetElement;
|
|
this._element.customDragger._marker = this;
|
|
this._elementId = targetElementId;
|
|
this._element.visible = true;
|
|
},
|
|
|
|
};
|
|
|
|
/*
|
|
* SelectionHelperUI
|
|
*/
|
|
|
|
var SelectionHelperUI = {
|
|
_debugEvents: false,
|
|
_msgTarget: null,
|
|
_startMark: null,
|
|
_endMark: null,
|
|
_caretMark: null,
|
|
_target: null,
|
|
_movement: { active: false, x:0, y: 0 },
|
|
_activeSelectionRect: null,
|
|
_selectionHandlerActive: false,
|
|
_selectionMarkIds: [],
|
|
|
|
/*
|
|
* Properties
|
|
*/
|
|
|
|
get startMark() {
|
|
if (this._startMark == null) {
|
|
this._startMark = new Marker(this, "start", this._selectionMarkIds.pop(), 0, 0);
|
|
}
|
|
return this._startMark;
|
|
},
|
|
|
|
get endMark() {
|
|
if (this._endMark == null) {
|
|
this._endMark = new Marker(this, "end", this._selectionMarkIds.pop(), 0, 0);
|
|
}
|
|
return this._endMark;
|
|
},
|
|
|
|
get caretMark() {
|
|
if (this._caretMark == null) {
|
|
this._caretMark = new Marker(this, "caret", this._selectionMarkIds.pop(), 0, 0);
|
|
}
|
|
return this._caretMark;
|
|
},
|
|
|
|
get overlay() {
|
|
return document.getElementById("selection-overlay");
|
|
},
|
|
|
|
/*
|
|
* isActive (prop)
|
|
*
|
|
* Determines if an edit session is currently active.
|
|
*/
|
|
get isActive() {
|
|
return (this._msgTarget &&
|
|
this._selectionHandlerActive);
|
|
},
|
|
|
|
/*
|
|
* Public apis
|
|
*/
|
|
|
|
/*
|
|
* openEditSession
|
|
*
|
|
* Attempts to select underlying text at a point and begins editing
|
|
* the section.
|
|
*/
|
|
openEditSession: function openEditSession(aContent, aClientX, aClientY) {
|
|
if (!aContent || this.isActive)
|
|
return;
|
|
this._init(aContent);
|
|
this._setupDebugOptions();
|
|
|
|
// Send this over to SelectionHandler in content, they'll message us
|
|
// back with information on the current selection. SelectionStart
|
|
// takes client coordinates.
|
|
this._selectionHandlerActive = false;
|
|
this._sendAsyncMessage("Browser:SelectionStart", {
|
|
xPos: aClientX,
|
|
yPos: aClientY
|
|
});
|
|
},
|
|
|
|
/*
|
|
* attachEditSession
|
|
*
|
|
* Attaches to existing selection and begins editing.
|
|
*/
|
|
attachEditSession: function attachEditSession(aContent, aClientX, aClientY) {
|
|
if (!aContent || this.isActive)
|
|
return;
|
|
this._init(aContent);
|
|
this._setupDebugOptions();
|
|
|
|
// Send this over to SelectionHandler in content, they'll message us
|
|
// back with information on the current selection. SelectionAttach
|
|
// takes client coordinates.
|
|
this._selectionHandlerActive = false;
|
|
this._sendAsyncMessage("Browser:SelectionAttach", {
|
|
xPos: aClientX,
|
|
yPos: aClientY
|
|
});
|
|
},
|
|
|
|
/*
|
|
* attachToCaret
|
|
*
|
|
* Initiates a touch caret selection session for a text input.
|
|
* Can be called multiple times to move the caret marker around.
|
|
*
|
|
* Note the caret marker is pretty limited in functionality. The
|
|
* only thing is can do is be displayed at the caret position.
|
|
* Once the user starts a drag, the caret marker is hidden, and
|
|
* the start and end markers take over.
|
|
*
|
|
* @param aClientX, aClientY client coordiates of the tap that
|
|
* initiated the session.
|
|
*/
|
|
attachToCaret: function attachToCaret(aContent, aClientX, aClientY) {
|
|
if (!aContent)
|
|
return;
|
|
if (!this.isActive)
|
|
this._init(aContent);
|
|
this._setupDebugOptions();
|
|
|
|
this._selectionHandlerActive = false;
|
|
this._sendAsyncMessage("Browser:CaretAttach", {
|
|
xPos: aClientX,
|
|
yPos: aClientY
|
|
});
|
|
},
|
|
|
|
/*
|
|
* canHandleContextMenuMsg
|
|
*
|
|
* Determines if we can handle a ContextMenuHandler message.
|
|
*/
|
|
canHandleContextMenuMsg: function canHandleContextMenuMsg(aMessage) {
|
|
if (aMessage.json.types.indexOf("content-text") != -1)
|
|
return true;
|
|
return false;
|
|
},
|
|
|
|
/*
|
|
* closeEditSession
|
|
*
|
|
* Closes an active edit session and shuts down. Does not clear existing
|
|
* selection regions if they exist.
|
|
*/
|
|
closeEditSession: function closeEditSession() {
|
|
this._sendAsyncMessage("Browser:SelectionClose");
|
|
this._shutdown();
|
|
},
|
|
|
|
/*
|
|
* closeEditSessionAndClear
|
|
*
|
|
* Closes an active edit session and shuts down. Clears any selection region
|
|
* associated with the edit session.
|
|
*/
|
|
closeEditSessionAndClear: function closeEditSessionAndClear() {
|
|
this._sendAsyncMessage("Browser:SelectionClear");
|
|
this.closeEditSession();
|
|
},
|
|
|
|
/*
|
|
* Init and shutdown
|
|
*/
|
|
|
|
_init: function _init(aMsgTarget) {
|
|
// store the target message manager
|
|
this._msgTarget = aMsgTarget;
|
|
|
|
// Init our list of available monocle ids
|
|
this._selectionMarkIds = ["selectionhandle-mark1",
|
|
"selectionhandle-mark2",
|
|
"selectionhandle-mark3"];
|
|
|
|
// SelectionHandler messages
|
|
messageManager.addMessageListener("Content:SelectionRange", this);
|
|
messageManager.addMessageListener("Content:SelectionCopied", this);
|
|
messageManager.addMessageListener("Content:SelectionFail", this);
|
|
messageManager.addMessageListener("Content:SelectionDebugRect", this);
|
|
|
|
// selection related events
|
|
window.addEventListener("click", this, false);
|
|
window.addEventListener("dblclick", this, false);
|
|
|
|
// Picking up scroll attempts
|
|
window.addEventListener("touchstart", this, true);
|
|
window.addEventListener("touchend", this, true);
|
|
window.addEventListener("touchmove", this, true);
|
|
|
|
// context ui display events
|
|
window.addEventListener("MozContextUIShow", this, true);
|
|
window.addEventListener("MozContextUIDismiss", this, true);
|
|
|
|
// cancellation related events
|
|
window.addEventListener("keypress", this, true);
|
|
Elements.browsers.addEventListener("URLChanged", this, true);
|
|
Elements.browsers.addEventListener("SizeChanged", this, true);
|
|
Elements.browsers.addEventListener("ZoomChanged", this, true);
|
|
|
|
window.addEventListener("MozPrecisePointer", this, true);
|
|
|
|
this.overlay.enabled = true;
|
|
},
|
|
|
|
_shutdown: function _shutdown() {
|
|
messageManager.removeMessageListener("Content:SelectionRange", this);
|
|
messageManager.removeMessageListener("Content:SelectionCopied", this);
|
|
messageManager.removeMessageListener("Content:SelectionFail", this);
|
|
messageManager.removeMessageListener("Content:SelectionDebugRect", this);
|
|
|
|
window.removeEventListener("click", this, false);
|
|
window.removeEventListener("dblclick", this, false);
|
|
|
|
window.removeEventListener("touchstart", this, true);
|
|
window.removeEventListener("touchend", this, true);
|
|
window.removeEventListener("touchmove", this, true);
|
|
|
|
window.removeEventListener("MozContextUIShow", this, true);
|
|
window.removeEventListener("MozContextUIDismiss", this, true);
|
|
|
|
window.removeEventListener("keypress", this, true);
|
|
Elements.browsers.removeEventListener("URLChanged", this, true);
|
|
Elements.browsers.removeEventListener("SizeChanged", this, true);
|
|
Elements.browsers.removeEventListener("ZoomChanged", this, true);
|
|
|
|
window.removeEventListener("MozPrecisePointer", this, true);
|
|
|
|
if (this.startMark != null)
|
|
this.startMark.shutdown();
|
|
if (this.endMark != null)
|
|
this.endMark.shutdown();
|
|
if (this._caretMark != null)
|
|
this._caretMark.shutdown();
|
|
|
|
this._startMark = null;
|
|
this._endMark = null;
|
|
this._caretMark = null;
|
|
|
|
this._selectionMarkIds = [];
|
|
this._msgTarget = null;
|
|
this._activeSelectionRect = null;
|
|
this._selectionHandlerActive = false;
|
|
|
|
this.overlay.displayDebugLayer = false;
|
|
this.overlay.enabled = false;
|
|
},
|
|
|
|
/*
|
|
* Utilities
|
|
*/
|
|
|
|
/*
|
|
* _swapCaretMarker
|
|
*
|
|
* Swap two drag markers - used when transitioning from caret mode
|
|
* to selection mode. We take the current caret marker (which is in a
|
|
* drag state) and swap it out with one of the selection markers.
|
|
*/
|
|
_swapCaretMarker: function _swapCaretMarker(aDirection) {
|
|
let targetMark = null;
|
|
if (aDirection == "start")
|
|
targetMark = this.startMark;
|
|
else
|
|
targetMark = this.endMark;
|
|
let caret = this.caretMark;
|
|
targetMark.swapMonocle(caret);
|
|
let id = caret._elementId;
|
|
caret.shutdown();
|
|
this._caretMark = null;
|
|
this._selectionMarkIds.push(id);
|
|
},
|
|
|
|
/*
|
|
* _transitionFromCaretToSelection
|
|
*
|
|
* Transitions from caret mode to text selection mode.
|
|
*/
|
|
_transitionFromCaretToSelection: function _transitionFromCaretToSelection(aDirection) {
|
|
// Get selection markers initialized if they aren't already
|
|
{ let mark = this.startMark; mark = this.endMark; }
|
|
|
|
// Swap the caret marker out for the start or end marker depending
|
|
// on direction.
|
|
this._swapCaretMarker(aDirection);
|
|
|
|
let targetMark = null;
|
|
if (aDirection == "start")
|
|
targetMark = this.startMark;
|
|
else
|
|
targetMark = this.endMark;
|
|
// Start the selection monocle drag. SelectionHandler relies on this
|
|
// for getting initialized. This will also trigger a message back for
|
|
// monocle positioning. Note, markerDragMove is still on the stack in
|
|
// this call!
|
|
targetMark._setPosition();
|
|
this.markerDragStart(targetMark);
|
|
this.markerDragMove(targetMark, aDirection);
|
|
},
|
|
|
|
/*
|
|
* _setupDebugOptions
|
|
*
|
|
* Sends a message over to content instructing it to
|
|
* turn on various debug features.
|
|
*/
|
|
_setupDebugOptions: function _setupDebugOptions() {
|
|
// Debug options for selection
|
|
let debugOpts = { dumpRanges: false, displayRanges: false, dumpEvents: false };
|
|
try {
|
|
if (Services.prefs.getBoolPref(kDebugSelectionDumpPref))
|
|
debugOpts.displayRanges = true;
|
|
} catch (ex) {}
|
|
try {
|
|
if (Services.prefs.getBoolPref(kDebugSelectionDisplayPref))
|
|
debugOpts.displayRanges = true;
|
|
} catch (ex) {}
|
|
try {
|
|
if (Services.prefs.getBoolPref(kDebugSelectionDumpEvents)) {
|
|
debugOpts.dumpEvents = true;
|
|
this._debugEvents = true;
|
|
}
|
|
} catch (ex) {}
|
|
|
|
if (debugOpts.displayRanges || debugOpts.dumpRanges || debugOpts.dumpEvents) {
|
|
// Turn on the debug layer
|
|
this.overlay.displayDebugLayer = true;
|
|
// Tell SelectionHandler what to do
|
|
this._sendAsyncMessage("Browser:SelectionDebug", debugOpts);
|
|
}
|
|
},
|
|
|
|
/*
|
|
* _sendAsyncMessage - helper for sending a message to
|
|
* SelectionHandler.
|
|
*/
|
|
_sendAsyncMessage: function _sendAsyncMessage(aMsg, aJson) {
|
|
if (!this._msgTarget) {
|
|
if (this._debugEvents)
|
|
Util.dumpLn("SelectionHelperUI sendAsyncMessage could not send", aMsg);
|
|
return;
|
|
}
|
|
this._msgTarget.messageManager.sendAsyncMessage(aMsg, aJson);
|
|
},
|
|
|
|
_checkForActiveDrag: function _checkForActiveDrag() {
|
|
return (this.startMark.dragging || this.endMark.dragging ||
|
|
this.caretMark.dragging);
|
|
},
|
|
|
|
_hitTestSelection: function _hitTestSelection(aEvent) {
|
|
// Ignore if the double tap isn't on our active selection rect.
|
|
if (this._activeSelectionRect &&
|
|
Util.pointWithinRect(aEvent.clientX, aEvent.clientY, this._activeSelectionRect)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
_setCaretPositionAtPoint: function _setCaretPositionAtPoint(aX, aY) {
|
|
let json = this._getMarkerBaseMessage();
|
|
json.change = "caret";
|
|
json.caret.xPos = aX;
|
|
json.caret.yPos = aY;
|
|
this._sendAsyncMessage("Browser:CaretUpdate", json);
|
|
},
|
|
|
|
/*
|
|
* Event handlers for document events
|
|
*/
|
|
|
|
/*
|
|
* _onTap
|
|
*
|
|
* Handles taps that move the current caret around text text
|
|
* edits. Also will clear active selection if it is present.
|
|
* Future: changing slection modes by tapping on a monocle.
|
|
*/
|
|
_onTap: function _onTap(aEvent) {
|
|
if (this.startMark.hitTest(aEvent.clientX, aEvent.clientY) ||
|
|
this.endMark.hitTest(aEvent.clientX, aEvent.clientY)) {
|
|
return;
|
|
}
|
|
|
|
// If the tap is within the target element and the caret monocle is
|
|
// active, update the caret.
|
|
if (this.caretMark.visible &&
|
|
Util.pointWithinRect(aEvent.clientX, aEvent.clientY,
|
|
this._targetElementRect)) {
|
|
// Move the caret
|
|
this._setCaretPositionAtPoint(aEvent.clientX, aEvent.clientY);
|
|
return;
|
|
}
|
|
|
|
if (this.caretMark.visible) {
|
|
// shutdown selection
|
|
this.closeEditSessionAndClear();
|
|
return;
|
|
}
|
|
|
|
// If we have active selection w/monocles, don't let random taps
|
|
// kill selection.
|
|
aEvent.stopPropagation();
|
|
aEvent.preventDefault();
|
|
},
|
|
|
|
/*
|
|
* Checks to see if the tap event was on our selection rect.
|
|
* If it is, we select the underlying text and shutdown.
|
|
*/
|
|
_onDblTap: function _onDblTap(aEvent) {
|
|
if (!this._hitTestSelection(aEvent)) {
|
|
// Clear and close
|
|
this.closeEditSessionAndClear();
|
|
return;
|
|
}
|
|
|
|
// Select and close
|
|
this._sendAsyncMessage("Browser:SelectionCopy", {
|
|
xPos: aEvent.clientX,
|
|
yPos: aEvent.clientY,
|
|
});
|
|
|
|
aEvent.stopPropagation();
|
|
aEvent.preventDefault();
|
|
},
|
|
|
|
_onSelectionCopied: function _onSelectionCopied(json) {
|
|
this.closeEditSessionAndClear();
|
|
},
|
|
|
|
_onSelectionRangeChange: function _onSelectionRangeChange(json) {
|
|
// start and end contain client coordinates.
|
|
if (json.updateStart) {
|
|
this.startMark.position(json.start.xPos, json.start.yPos);
|
|
this.startMark.show();
|
|
}
|
|
if (json.updateEnd) {
|
|
this.endMark.position(json.end.xPos, json.end.yPos);
|
|
this.endMark.show();
|
|
}
|
|
if (json.updateCaret) {
|
|
this.caretMark.position(json.caret.xPos, json.caret.yPos);
|
|
this.caretMark.show();
|
|
}
|
|
this._activeSelectionRect = json.selection;
|
|
this._targetElementRect = json.element;
|
|
},
|
|
|
|
_onSelectionFail: function _onSelectionFail() {
|
|
Util.dumpLn("failed to get a selection.");
|
|
this.closeEditSession();
|
|
},
|
|
|
|
_onKeypress: function _onKeypress() {
|
|
this.closeEditSession();
|
|
},
|
|
|
|
_onResize: function _onResize() {
|
|
this._sendAsyncMessage("Browser:SelectionUpdate", {});
|
|
},
|
|
|
|
_onContextUIVisibilityEvent: function _onContextUIVisibilityEvent(aType) {
|
|
// Manage display of monocles when the context ui is displayed.
|
|
if (!this.isActive)
|
|
return;
|
|
this.overlay.hidden = (aType == "MozContextUIShow");
|
|
},
|
|
|
|
_onDebugRectRequest: function _onDebugRectRequest(aMsg) {
|
|
this.overlay.addDebugRect(aMsg.left, aMsg.top, aMsg.right, aMsg.bottom,
|
|
aMsg.color, aMsg.fill, aMsg.id);
|
|
},
|
|
|
|
/*
|
|
* Events
|
|
*/
|
|
|
|
handleEvent: function handleEvent(aEvent) {
|
|
if (this._debugEvents && aEvent.type != "touchmove") {
|
|
Util.dumpLn("SelectionHelperUI:", aEvent.type);
|
|
}
|
|
switch (aEvent.type) {
|
|
case "click":
|
|
this._onTap(aEvent);
|
|
break;
|
|
|
|
case "dblclick":
|
|
this._onDblTap(aEvent);
|
|
break;
|
|
|
|
case "touchstart": {
|
|
if (aEvent.touches.length != 1)
|
|
break;
|
|
let touch = aEvent.touches[0];
|
|
this._movement.x = this._movement.y = 0;
|
|
this._movement.x = touch.clientX;
|
|
this._movement.y = touch.clientY;
|
|
this._movement.active = true;
|
|
break;
|
|
}
|
|
|
|
case "touchend":
|
|
if (aEvent.touches.length == 0)
|
|
this._movement.active = false;
|
|
break;
|
|
|
|
case "touchmove": {
|
|
if (aEvent.touches.length != 1)
|
|
break;
|
|
let touch = aEvent.touches[0];
|
|
// Clear our selection overlay when the user starts to pan the page
|
|
if (!this._checkForActiveDrag() && this._movement.active) {
|
|
let distanceY = touch.clientY - this._movement.y;
|
|
if (Math.abs(distanceY) > kDisableOnScrollDistance) {
|
|
this.closeEditSessionAndClear();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case "keypress":
|
|
this._onKeypress(aEvent);
|
|
break;
|
|
|
|
case "SizeChanged":
|
|
this._onResize(aEvent);
|
|
break;
|
|
|
|
case "ZoomChanged":
|
|
case "URLChanged":
|
|
case "MozPrecisePointer":
|
|
this.closeEditSessionAndClear();
|
|
break;
|
|
|
|
case "MozContextUIShow":
|
|
case "MozContextUIDismiss":
|
|
this._onContextUIVisibilityEvent(aEvent.type);
|
|
break;
|
|
}
|
|
},
|
|
|
|
receiveMessage: function sh_receiveMessage(aMessage) {
|
|
if (this._debugEvents) Util.dumpLn("SelectionHelperUI:", aMessage.name);
|
|
let json = aMessage.json;
|
|
switch (aMessage.name) {
|
|
case "Content:SelectionFail":
|
|
this._selectionHandlerActive = false;
|
|
this._onSelectionFail();
|
|
break;
|
|
case "Content:SelectionRange":
|
|
this._selectionHandlerActive = true;
|
|
this._onSelectionRangeChange(json);
|
|
break;
|
|
case "Content:SelectionCopied":
|
|
this._selectionHandlerActive = true;
|
|
this._onSelectionCopied(json);
|
|
break;
|
|
case "Content:SelectionDebugRect":
|
|
this._onDebugRectRequest(json);
|
|
break;
|
|
}
|
|
},
|
|
|
|
/*
|
|
* Callbacks from markers
|
|
*/
|
|
|
|
_getMarkerBaseMessage: function _getMarkerBaseMessage() {
|
|
return {
|
|
start: { xPos: this.startMark.xPos, yPos: this.startMark.yPos },
|
|
end: { xPos: this.endMark.xPos, yPos: this.endMark.yPos },
|
|
caret: { xPos: this.caretMark.xPos, yPos: this.caretMark.yPos },
|
|
};
|
|
},
|
|
|
|
markerDragStart: function markerDragStart(aMarker) {
|
|
let json = this._getMarkerBaseMessage();
|
|
json.change = aMarker.tag;
|
|
if (aMarker.tag == "caret") {
|
|
this._sendAsyncMessage("Browser:CaretMove", json);
|
|
return;
|
|
}
|
|
this._sendAsyncMessage("Browser:SelectionMoveStart", json);
|
|
},
|
|
|
|
markerDragStop: function markerDragStop(aMarker) {
|
|
let json = this._getMarkerBaseMessage();
|
|
json.change = aMarker.tag;
|
|
if (aMarker.tag == "caret") {
|
|
this._sendAsyncMessage("Browser:CaretUpdate", json);
|
|
return;
|
|
}
|
|
this._sendAsyncMessage("Browser:SelectionMoveEnd", json);
|
|
},
|
|
|
|
markerDragMove: function markerDragMove(aMarker, aDirection) {
|
|
let json = this._getMarkerBaseMessage();
|
|
json.change = aMarker.tag;
|
|
if (aMarker.tag == "caret") {
|
|
// We are going to transition from caret browsing mode to selection mode
|
|
// on a drag. So swap the caret monocle for a start or end monocle
|
|
// depending on the direction of the drag, and start selecting text.
|
|
this._transitionFromCaretToSelection(aDirection);
|
|
return false;
|
|
}
|
|
this._sendAsyncMessage("Browser:SelectionMove", json);
|
|
return true;
|
|
},
|
|
};
|