mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 788000 - Add support for chrome selection. r=mbrubeck
This commit is contained in:
parent
686bb00322
commit
9a8815b1ac
@ -110,6 +110,7 @@ let ScriptContexts = {};
|
||||
["SelectHelperUI", "chrome://browser/content/helperui/SelectHelperUI.js"],
|
||||
["SelectionHelperUI", "chrome://browser/content/helperui/SelectionHelperUI.js"],
|
||||
["SelectionPrototype", "chrome://browser/content/library/SelectionPrototype.js"],
|
||||
["ChromeSelectionHandler", "chrome://browser/content/helperui/ChromeSelectionHandler.js"],
|
||||
["AnimatedZoom", "chrome://browser/content/AnimatedZoom.js"],
|
||||
["CommandUpdater", "chrome://browser/content/commandUtil.js"],
|
||||
["ContextCommands", "chrome://browser/content/ContextCommands.js"],
|
||||
|
@ -783,21 +783,35 @@ var BrowserUI = {
|
||||
onEvent: function(aEventName) {}
|
||||
},
|
||||
|
||||
_urlbarClicked: function _urlbarClicked() {
|
||||
_urlbarClicked: function _urlbarClicked(aEvent) {
|
||||
let touchEvent = aEvent.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH;
|
||||
|
||||
// If the urlbar is not already focused, focus it and select the contents.
|
||||
if (Elements.urlbarState.getAttribute("mode") != "edit")
|
||||
this._editURI(true);
|
||||
if (Elements.urlbarState.getAttribute("mode") != "edit") {
|
||||
this._editURI(true, touchEvent);
|
||||
if (touchEvent) {
|
||||
SelectionHelperUI.attachEditSession(ChromeSelectionHandler,
|
||||
aEvent.clientX, aEvent.clientY);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// tap caret handling
|
||||
if (touchEvent) {
|
||||
SelectionHelperUI.attachToCaret(ChromeSelectionHandler,
|
||||
aEvent.clientX, aEvent.clientY);
|
||||
}
|
||||
},
|
||||
|
||||
_editURI: function _editURI(aShouldDismiss) {
|
||||
this._clearURIFormatting();
|
||||
_editURI: function _editURI(aEvent, aShouldDismiss) {
|
||||
this._edit.focus();
|
||||
this._edit.select();
|
||||
|
||||
Elements.urlbarState.setAttribute("mode", "edit");
|
||||
StartUI.show();
|
||||
if (aShouldDismiss)
|
||||
if (aShouldDismiss) {
|
||||
ContextUI.dismissTabs();
|
||||
}
|
||||
},
|
||||
|
||||
formatURI: function formatURI() {
|
||||
|
364
browser/metro/base/content/helperui/ChromeSelectionHandler.js
Normal file
364
browser/metro/base/content/helperui/ChromeSelectionHandler.js
Normal file
@ -0,0 +1,364 @@
|
||||
/* 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/. */
|
||||
|
||||
/*
|
||||
* Selection handler for chrome text inputs
|
||||
*/
|
||||
|
||||
const kCaretMode = 1;
|
||||
const kSelectionMode = 2;
|
||||
|
||||
var ChromeSelectionHandler = {
|
||||
_mode: kSelectionMode,
|
||||
|
||||
/*************************************************
|
||||
* Messaging wrapper
|
||||
*/
|
||||
|
||||
sendAsync: function sendAsync(aMsg, aJson) {
|
||||
SelectionHelperUI.receiveMessage({
|
||||
name: aMsg,
|
||||
json: aJson
|
||||
});
|
||||
},
|
||||
|
||||
/*************************************************
|
||||
* Browser event handlers
|
||||
*/
|
||||
|
||||
/*
|
||||
* General selection start method for both caret and selection mode.
|
||||
*/
|
||||
_onSelectionAttach: function _onSelectionAttach(aJson) {
|
||||
this._domWinUtils = Util.getWindowUtils(window);
|
||||
this._contentWindow = window;
|
||||
this._targetElement = this._domWinUtils.elementFromPoint(aJson.xPos, aJson.yPos, true, false);
|
||||
|
||||
this._targetIsEditable = this._targetElement instanceof Components.interfaces.nsIDOMXULTextBoxElement;
|
||||
if (!this._targetIsEditable) {
|
||||
this._onFail("not an editable?");
|
||||
return;
|
||||
}
|
||||
|
||||
let selection = this._getSelection();
|
||||
if (!selection) {
|
||||
this._onFail("no selection.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selection.isCollapsed) {
|
||||
this._mode = kSelectionMode;
|
||||
this._updateSelectionUI("start", true, true);
|
||||
} else {
|
||||
this._mode = kCaretMode;
|
||||
this._updateSelectionUI("caret", false, false, true);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
/*
|
||||
* Selection monocle start move event handler
|
||||
*/
|
||||
_onSelectionMoveStart: function _onSelectionMoveStart(aMsg) {
|
||||
if (!this.targetIsEditable) {
|
||||
this._onFail("_onSelectionMoveStart with bad targetElement.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._selectionMoveActive) {
|
||||
this._onFail("mouse is already down on drag start?");
|
||||
return;
|
||||
}
|
||||
|
||||
// We bail if things get out of sync here implying we missed a message.
|
||||
this._selectionMoveActive = true;
|
||||
|
||||
if (this._targetIsEditable) {
|
||||
// If we're coming out of an out-of-bounds scroll, the node the user is
|
||||
// trying to drag may be hidden (the monocle will be pegged to the edge
|
||||
// of the edit). Make sure the node the user wants to move is visible
|
||||
// and has focus.
|
||||
this._updateInputFocus(aMsg.change);
|
||||
}
|
||||
|
||||
// Update the position of our selection monocles
|
||||
this._updateSelectionUI("update", true, true);
|
||||
},
|
||||
|
||||
/*
|
||||
* Selection monocle move event handler
|
||||
*/
|
||||
_onSelectionMove: function _onSelectionMove(aMsg) {
|
||||
if (!this.targetIsEditable) {
|
||||
this._onFail("_onSelectionMove with bad targetElement.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._selectionMoveActive) {
|
||||
this._onFail("mouse isn't down for drag move?");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update selection in the doc
|
||||
let pos = null;
|
||||
if (aMsg.change == "start") {
|
||||
pos = aMsg.start;
|
||||
} else {
|
||||
pos = aMsg.end;
|
||||
}
|
||||
this._handleSelectionPoint(aMsg.change, pos, false);
|
||||
},
|
||||
|
||||
/*
|
||||
* Selection monocle move finished event handler
|
||||
*/
|
||||
_onSelectionMoveEnd: function _onSelectionMoveComplete(aMsg) {
|
||||
if (!this.targetIsEditable) {
|
||||
this._onFail("_onSelectionMoveEnd with bad targetElement.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._selectionMoveActive) {
|
||||
this._onFail("mouse isn't down for drag move?");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update selection in the doc
|
||||
let pos = null;
|
||||
if (aMsg.change == "start") {
|
||||
pos = aMsg.start;
|
||||
} else {
|
||||
pos = aMsg.end;
|
||||
}
|
||||
|
||||
this._handleSelectionPoint(aMsg.change, pos, true);
|
||||
this._selectionMoveActive = false;
|
||||
|
||||
// Update the position of our selection monocles
|
||||
this._updateSelectionUI("end", true, true);
|
||||
},
|
||||
|
||||
/*
|
||||
* Switch selection modes. Currently we only support switching
|
||||
* from "caret" to "selection".
|
||||
*/
|
||||
_onSwitchMode: function _onSwitchMode(aMode, aMarker, aX, aY) {
|
||||
if (aMode != "selection") {
|
||||
this._onFail("unsupported mode switch");
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check to be sure we are initialized
|
||||
if (!this._targetElement) {
|
||||
this._onFail("not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
// Similar to _onSelectionStart - we need to create initial selection
|
||||
// but without the initialization bits.
|
||||
let framePoint = this._clientPointToFramePoint({ xPos: aX, yPos: aY });
|
||||
if (!this._domWinUtils.selectAtPoint(framePoint.xPos, framePoint.yPos,
|
||||
Ci.nsIDOMWindowUtils.SELECT_CHARACTER)) {
|
||||
this._onFail("failed to set selection at point");
|
||||
return;
|
||||
}
|
||||
|
||||
// We bail if things get out of sync here implying we missed a message.
|
||||
this._selectionMoveActive = true;
|
||||
this._mode = kSelectionMode;
|
||||
|
||||
// Update the position of the selection marker that is *not*
|
||||
// being dragged.
|
||||
this._updateSelectionUI("update", aMarker == "end", aMarker == "start");
|
||||
},
|
||||
|
||||
/*
|
||||
* Selection close event handler
|
||||
*
|
||||
* @param aClearSelection requests that selection be cleared.
|
||||
*/
|
||||
_onSelectionClose: function _onSelectionClose(aClearSelection) {
|
||||
if (aClearSelection) {
|
||||
this._clearSelection();
|
||||
}
|
||||
this._closeSelection();
|
||||
},
|
||||
|
||||
/*
|
||||
* Called if for any reason we fail during the selection
|
||||
* process. Cancels the selection.
|
||||
*/
|
||||
_onFail: function _onFail(aDbgMessage) {
|
||||
if (aDbgMessage && aDbgMessage.length > 0)
|
||||
Util.dumpLn(aDbgMessage);
|
||||
this.sendAsync("Content:SelectionFail");
|
||||
this._clearSelection();
|
||||
this._closeSelection();
|
||||
},
|
||||
|
||||
/*
|
||||
* Empty conversion routines to match those in
|
||||
* browser. Called by SelectionHelperUI when
|
||||
* sending and receiving coordinates in messages.
|
||||
*/
|
||||
|
||||
ptClientToBrowser: function ptClientToBrowser(aX, aY, aIgnoreScroll, aIgnoreScale) {
|
||||
return { x: aX, y: aY }
|
||||
},
|
||||
|
||||
rectBrowserToClient: function rectBrowserToClient(aRect, aIgnoreScroll, aIgnoreScale) {
|
||||
return {
|
||||
left: aRect.left,
|
||||
right: aRect.right,
|
||||
top: aRect.top,
|
||||
bottom: aRect.bottom
|
||||
}
|
||||
},
|
||||
|
||||
ptBrowserToClient: function ptBrowserToClient(aX, aY, aIgnoreScroll, aIgnoreScale) {
|
||||
return { x: aX, y: aY }
|
||||
},
|
||||
|
||||
ctobx: function ctobx(aCoord) {
|
||||
return aCoord;
|
||||
},
|
||||
|
||||
ctoby: function ctoby(aCoord) {
|
||||
return aCoord;
|
||||
},
|
||||
|
||||
btocx: function btocx(aCoord) {
|
||||
return aCoord;
|
||||
},
|
||||
|
||||
btocy: function btocy(aCoord) {
|
||||
return aCoord;
|
||||
},
|
||||
|
||||
|
||||
/*************************************************
|
||||
* Selection helpers
|
||||
*/
|
||||
|
||||
/*
|
||||
* _clearSelection
|
||||
*
|
||||
* Clear existing selection if it exists and reset our internla state.
|
||||
*/
|
||||
_clearSelection: function _clearSelection() {
|
||||
let selection = this._getSelection();
|
||||
if (selection) {
|
||||
selection.removeAllRanges();
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* _closeSelection
|
||||
*
|
||||
* Shuts SelectionHandler down.
|
||||
*/
|
||||
_closeSelection: function _closeSelection() {
|
||||
this._clearTimers();
|
||||
this._cache = null;
|
||||
this._contentWindow = null;
|
||||
this._targetElement = null;
|
||||
this._selectionMoveActive = false;
|
||||
this._domWinUtils = null;
|
||||
this._targetIsEditable = false;
|
||||
this.sendAsync("Content:HandlerShutdown", {});
|
||||
},
|
||||
|
||||
/*************************************************
|
||||
* Events
|
||||
*/
|
||||
|
||||
/*
|
||||
* Scroll + selection advancement timer when the monocle is
|
||||
* outside the bounds of an input control.
|
||||
*/
|
||||
scrollTimerCallback: function scrollTimerCallback() {
|
||||
let result = ChromeSelectionHandler.updateTextEditSelection();
|
||||
// Update monocle position and speed if we've dragged off to one side
|
||||
if (result.trigger) {
|
||||
ChromeSelectionHandler._updateSelectionUI("update", result.start, result.end);
|
||||
}
|
||||
},
|
||||
|
||||
msgHandler: function msgHandler(aMsg, aJson) {
|
||||
if (this._debugEvents && "Browser:SelectionMove" != aMsg) {
|
||||
Util.dumpLn("ChromeSelectionHandler:", aMsg);
|
||||
}
|
||||
switch(aMsg) {
|
||||
case "Browser:SelectionDebug":
|
||||
this._onSelectionDebug(aJson);
|
||||
break;
|
||||
|
||||
case "Browser:SelectionAttach":
|
||||
this._onSelectionAttach(aJson);
|
||||
break;
|
||||
|
||||
case "Browser:CaretAttach":
|
||||
this._onSelectionAttach(aJson);
|
||||
break;
|
||||
|
||||
case "Browser:SelectionClose":
|
||||
this._onSelectionClose(aJson.clearSelection);
|
||||
break;
|
||||
|
||||
case "Browser:SelectionUpdate":
|
||||
this._updateSelectionUI("update",
|
||||
this._mode == kSelectionMode,
|
||||
this._mode == kSelectionMode,
|
||||
this._mode == kCaretMode);
|
||||
break;
|
||||
|
||||
case "Browser:SelectionMoveStart":
|
||||
this._onSelectionMoveStart(aJson);
|
||||
break;
|
||||
|
||||
case "Browser:SelectionMove":
|
||||
this._onSelectionMove(aJson);
|
||||
break;
|
||||
|
||||
case "Browser:SelectionMoveEnd":
|
||||
this._onSelectionMoveEnd(aJson);
|
||||
break;
|
||||
|
||||
case "Browser:CaretUpdate":
|
||||
this._onCaretPositionUpdate(aJson.caret.xPos, aJson.caret.yPos);
|
||||
break;
|
||||
|
||||
case "Browser:CaretMove":
|
||||
this._onCaretMove(aJson.caret.xPos, aJson.caret.yPos);
|
||||
break;
|
||||
|
||||
case "Browser:SelectionSwitchMode":
|
||||
this._onSwitchMode(aJson.newMode, aJson.change, aJson.xPos, aJson.yPos);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/*************************************************
|
||||
* Utilities
|
||||
*/
|
||||
|
||||
_getSelection: function _getSelection() {
|
||||
if (this._targetElement instanceof Ci.nsIDOMXULTextBoxElement) {
|
||||
return this._targetElement
|
||||
.QueryInterface(Components.interfaces.nsIDOMXULTextBoxElement)
|
||||
.editor.selection;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
_getSelectController: function _getSelectController() {
|
||||
return this._targetElement
|
||||
.QueryInterface(Components.interfaces.nsIDOMXULTextBoxElement)
|
||||
.editor.selectionController;
|
||||
},
|
||||
};
|
||||
|
||||
ChromeSelectionHandler.__proto__ = new SelectionPrototype();
|
||||
ChromeSelectionHandler.type = 1; // kChromeSelector
|
||||
|
@ -289,6 +289,8 @@ var SelectionHelperUI = {
|
||||
},
|
||||
|
||||
get layerMode() {
|
||||
if (this._msgTarget && this._msgTarget instanceof SelectionPrototype)
|
||||
return kChromeLayer;
|
||||
return kContentLayer;
|
||||
},
|
||||
|
||||
@ -522,6 +524,8 @@ var SelectionHelperUI = {
|
||||
Elements.browsers.addEventListener("SizeChanged", this, true);
|
||||
Elements.browsers.addEventListener("ZoomChanged", this, true);
|
||||
|
||||
Elements.navbar.addEventListener("transitionend", this, true);
|
||||
|
||||
this.overlay.enabled = true;
|
||||
},
|
||||
|
||||
@ -546,6 +550,8 @@ var SelectionHelperUI = {
|
||||
Elements.browsers.removeEventListener("SizeChanged", this, true);
|
||||
Elements.browsers.removeEventListener("ZoomChanged", this, true);
|
||||
|
||||
Elements.navbar.removeEventListener("transitionend", this, true);
|
||||
|
||||
this._shutdownAllMarkers();
|
||||
|
||||
this._selectionMarkIds = [];
|
||||
@ -625,9 +631,6 @@ var SelectionHelperUI = {
|
||||
* tap that initiates the change.
|
||||
*/
|
||||
_transitionFromSelectionToCaret: function _transitionFromSelectionToCaret(aClientX, aClientY) {
|
||||
// clear existing selection and shutdown SelectionHandler
|
||||
this.closeEditSession(true);
|
||||
|
||||
// Reset some of our state
|
||||
this._activeSelectionRect = null;
|
||||
|
||||
@ -694,7 +697,11 @@ var SelectionHelperUI = {
|
||||
Util.dumpLn("SelectionHelperUI sendAsyncMessage could not send", aMsg);
|
||||
return;
|
||||
}
|
||||
this._msgTarget.messageManager.sendAsyncMessage(aMsg, aJson);
|
||||
if (this._msgTarget && this._msgTarget instanceof SelectionPrototype) {
|
||||
this._msgTarget.msgHandler(aMsg, aJson);
|
||||
} else {
|
||||
this._msgTarget.messageManager.sendAsyncMessage(aMsg, aJson);
|
||||
}
|
||||
},
|
||||
|
||||
_checkForActiveDrag: function _checkForActiveDrag() {
|
||||
@ -831,8 +838,15 @@ var SelectionHelperUI = {
|
||||
// since we always get a single tap before a double, and double tap
|
||||
// copies selected text.
|
||||
if (selectionTap) {
|
||||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
if (!this._targetIsEditable) {
|
||||
this.closeEditSession(false);
|
||||
return;
|
||||
}
|
||||
// Attach to the newly placed caret position
|
||||
this._sendAsyncMessage("Browser:CaretAttach", {
|
||||
xPos: aEvent.clientX,
|
||||
yPos: aEvent.clientY
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -877,6 +891,24 @@ var SelectionHelperUI = {
|
||||
this.attachToCaret(null, this._lastPoint.xPos, this._lastPoint.yPos);
|
||||
},
|
||||
|
||||
/*
|
||||
* Detects when the nav bar hides or shows, so we can enable
|
||||
* selection at the appropriate location once the transition is
|
||||
* complete, or shutdown selection down when the nav bar is hidden.
|
||||
*/
|
||||
_onNavBarTransitionEvent: function _onNavBarTransitionEvent(aEvent) {
|
||||
if (this.layerMode == kContentLayer) {
|
||||
return;
|
||||
}
|
||||
if (aEvent.propertyName == "bottom" && !Elements.navbar.isShowing) {
|
||||
this.closeEditSession(false);
|
||||
return;
|
||||
}
|
||||
if (aEvent.propertyName == "bottom" && Elements.navbar.isShowing) {
|
||||
this._sendAsyncMessage("Browser:SelectionUpdate", {});
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* Event handlers for message manager
|
||||
*/
|
||||
@ -1023,6 +1055,10 @@ var SelectionHelperUI = {
|
||||
case "MozDeckOffsetChanged":
|
||||
this._onDeckOffsetChanged(aEvent);
|
||||
break;
|
||||
|
||||
case "transitionend":
|
||||
this._onNavBarTransitionEvent(aEvent);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -49,7 +49,7 @@ dump("### SelectionPrototype.js loaded\n");
|
||||
var SelectionPrototype = function() { }
|
||||
|
||||
SelectionPrototype.prototype = {
|
||||
_debugEvents: true,
|
||||
_debugEvents: false,
|
||||
_cache: {},
|
||||
_targetElement: null,
|
||||
_targetIsEditable: true,
|
||||
@ -307,7 +307,8 @@ SelectionPrototype.prototype = {
|
||||
let cp =
|
||||
this._contentWindow.document.caretPositionFromPoint(constrainedPoint.xPos,
|
||||
constrainedPoint.yPos);
|
||||
if (!cp || cp.offsetNode != this._targetElement) {
|
||||
if (!cp || (cp.offsetNode != this._targetElement &&
|
||||
this._contentWindow.document.getBindingParent(cp.offsetNode) != this._targetElement)) {
|
||||
return;
|
||||
}
|
||||
if (aMarker == "start") {
|
||||
|
@ -42,6 +42,7 @@ chrome.jar:
|
||||
content/helperui/OfflineApps.js (content/helperui/OfflineApps.js)
|
||||
content/helperui/SelectHelperUI.js (content/helperui/SelectHelperUI.js)
|
||||
content/helperui/SelectionHelperUI.js (content/helperui/SelectionHelperUI.js)
|
||||
content/helperui/ChromeSelectionHandler.js (content/helperui/ChromeSelectionHandler.js)
|
||||
content/helperui/FormHelperUI.js (content/helperui/FormHelperUI.js)
|
||||
content/helperui/FindHelperUI.js (content/helperui/FindHelperUI.js)
|
||||
content/helperui/ItemPinHelper.js (content/helperui/ItemPinHelper.js)
|
||||
|
Loading…
Reference in New Issue
Block a user