browser/metro/base/content/helperui/SelectionHelperUI.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /*
michael@0 6 * selection management
michael@0 7 */
michael@0 8
michael@0 9 /*
michael@0 10 * Current monocle image:
michael@0 11 * dimensions: 32 x 24
michael@0 12 * circle center: 16 x 14
michael@0 13 * padding top: 6
michael@0 14 */
michael@0 15
michael@0 16 // Y axis scroll distance that will disable this module and cancel selection
michael@0 17 const kDisableOnScrollDistance = 25;
michael@0 18
michael@0 19 // Drag hysteresis programmed into monocle drag moves
michael@0 20 const kDragHysteresisDistance = 10;
michael@0 21
michael@0 22 // selection layer id returned from SelectionHandlerUI's layerMode.
michael@0 23 const kChromeLayer = 1;
michael@0 24 const kContentLayer = 2;
michael@0 25
michael@0 26 /*
michael@0 27 * Markers
michael@0 28 */
michael@0 29
michael@0 30 function MarkerDragger(aMarker) {
michael@0 31 this._marker = aMarker;
michael@0 32 }
michael@0 33
michael@0 34 MarkerDragger.prototype = {
michael@0 35 _selectionHelperUI: null,
michael@0 36 _marker: null,
michael@0 37 _shutdown: false,
michael@0 38 _dragging: false,
michael@0 39
michael@0 40 get marker() {
michael@0 41 return this._marker;
michael@0 42 },
michael@0 43
michael@0 44 set shutdown(aVal) {
michael@0 45 this._shutdown = aVal;
michael@0 46 },
michael@0 47
michael@0 48 get shutdown() {
michael@0 49 return this._shutdown;
michael@0 50 },
michael@0 51
michael@0 52 get dragging() {
michael@0 53 return this._dragging;
michael@0 54 },
michael@0 55
michael@0 56 freeDrag: function freeDrag() {
michael@0 57 return true;
michael@0 58 },
michael@0 59
michael@0 60 isDraggable: function isDraggable(aTarget, aContent) {
michael@0 61 return { x: true, y: true };
michael@0 62 },
michael@0 63
michael@0 64 dragStart: function dragStart(aX, aY, aTarget, aScroller) {
michael@0 65 if (this._shutdown)
michael@0 66 return false;
michael@0 67 this._dragging = true;
michael@0 68 this.marker.dragStart(aX, aY);
michael@0 69 return true;
michael@0 70 },
michael@0 71
michael@0 72 dragStop: function dragStop(aDx, aDy, aScroller) {
michael@0 73 if (this._shutdown)
michael@0 74 return false;
michael@0 75 this._dragging = false;
michael@0 76 this.marker.dragStop(aDx, aDy);
michael@0 77 return true;
michael@0 78 },
michael@0 79
michael@0 80 dragMove: function dragMove(aDx, aDy, aScroller, aIsKenetic, aClientX, aClientY) {
michael@0 81 // Note if aIsKenetic is true this is synthetic movement,
michael@0 82 // we don't want that so return false.
michael@0 83 if (this._shutdown || aIsKenetic)
michael@0 84 return false;
michael@0 85 this.marker.moveBy(aDx, aDy, aClientX, aClientY);
michael@0 86 // return true if we moved, false otherwise. The result
michael@0 87 // is used in deciding if we should repaint between drags.
michael@0 88 return true;
michael@0 89 }
michael@0 90 }
michael@0 91
michael@0 92 function Marker(aParent, aTag, aElementId, xPos, yPos) {
michael@0 93 this._xPos = xPos;
michael@0 94 this._yPos = yPos;
michael@0 95 this._selectionHelperUI = aParent;
michael@0 96 this._element = aParent.overlay.getMarker(aElementId);
michael@0 97 this._elementId = aElementId;
michael@0 98 // These get picked in input.js and receives drag input
michael@0 99 this._element.customDragger = new MarkerDragger(this);
michael@0 100 this.tag = aTag;
michael@0 101 }
michael@0 102
michael@0 103 Marker.prototype = {
michael@0 104 _element: null,
michael@0 105 _elementId: "",
michael@0 106 _selectionHelperUI: null,
michael@0 107 _xPos: 0,
michael@0 108 _yPos: 0,
michael@0 109 _xDrag: 0,
michael@0 110 _yDrag: 0,
michael@0 111 _tag: "",
michael@0 112 _hPlane: 0,
michael@0 113 _vPlane: 0,
michael@0 114 _restrictedToBounds: false,
michael@0 115
michael@0 116 // Tweak me if the monocle graphics change in any way
michael@0 117 _monocleRadius: 8,
michael@0 118 _monocleXHitTextAdjust: -2,
michael@0 119 _monocleYHitTextAdjust: -10,
michael@0 120
michael@0 121 get xPos() {
michael@0 122 return this._xPos;
michael@0 123 },
michael@0 124
michael@0 125 get yPos() {
michael@0 126 return this._yPos;
michael@0 127 },
michael@0 128
michael@0 129 get tag() {
michael@0 130 return this._tag;
michael@0 131 },
michael@0 132
michael@0 133 set tag(aVal) {
michael@0 134 this._tag = aVal;
michael@0 135 },
michael@0 136
michael@0 137 get dragging() {
michael@0 138 return this._element.customDragger.dragging;
michael@0 139 },
michael@0 140
michael@0 141 // Indicates that marker's position doesn't reflect real selection boundary
michael@0 142 // but rather boundary of input control while actual selection boundaries are
michael@0 143 // not visible (ex. due scrolled content).
michael@0 144 get restrictedToBounds() {
michael@0 145 return this._restrictedToBounds;
michael@0 146 },
michael@0 147
michael@0 148 shutdown: function shutdown() {
michael@0 149 this._element.hidden = true;
michael@0 150 this._element.customDragger.shutdown = true;
michael@0 151 delete this._element.customDragger;
michael@0 152 this._selectionHelperUI = null;
michael@0 153 this._element = null;
michael@0 154 },
michael@0 155
michael@0 156 setTrackBounds: function setTrackBounds(aVerticalPlane, aHorizontalPlane) {
michael@0 157 // monocle boundaries
michael@0 158 this._hPlane = aHorizontalPlane;
michael@0 159 this._vPlane = aVerticalPlane;
michael@0 160 },
michael@0 161
michael@0 162 show: function show() {
michael@0 163 this._element.hidden = false;
michael@0 164 },
michael@0 165
michael@0 166 hide: function hide() {
michael@0 167 this._element.hidden = true;
michael@0 168 },
michael@0 169
michael@0 170 get visible() {
michael@0 171 return this._element.hidden == false;
michael@0 172 },
michael@0 173
michael@0 174 position: function position(aX, aY, aRestrictedToBounds) {
michael@0 175 this._xPos = aX;
michael@0 176 this._yPos = aY;
michael@0 177 this._restrictedToBounds = !!aRestrictedToBounds;
michael@0 178 this._setPosition();
michael@0 179 },
michael@0 180
michael@0 181 _setPosition: function _setPosition() {
michael@0 182 this._element.left = this._xPos + "px";
michael@0 183 this._element.top = this._yPos + "px";
michael@0 184 },
michael@0 185
michael@0 186 dragStart: function dragStart(aX, aY) {
michael@0 187 this._xDrag = 0;
michael@0 188 this._yDrag = 0;
michael@0 189 this._selectionHelperUI.markerDragStart(this);
michael@0 190 },
michael@0 191
michael@0 192 dragStop: function dragStop(aDx, aDy) {
michael@0 193 this._selectionHelperUI.markerDragStop(this);
michael@0 194 },
michael@0 195
michael@0 196 moveBy: function moveBy(aDx, aDy, aClientX, aClientY) {
michael@0 197 this._xPos -= aDx;
michael@0 198 this._yPos -= aDy;
michael@0 199 this._xDrag -= aDx;
michael@0 200 this._yDrag -= aDy;
michael@0 201 // Add a bit of hysteresis to our directional detection so "big fingers"
michael@0 202 // are detected accurately.
michael@0 203 let direction = "tbd";
michael@0 204 if (Math.abs(this._xDrag) > kDragHysteresisDistance ||
michael@0 205 Math.abs(this._yDrag) > kDragHysteresisDistance) {
michael@0 206 direction = (this._xDrag <= 0 && this._yDrag <= 0 ? "start" : "end");
michael@0 207 }
michael@0 208 // We may swap markers in markerDragMove. If markerDragMove
michael@0 209 // returns true keep processing, otherwise get out of here.
michael@0 210 if (this._selectionHelperUI.markerDragMove(this, direction)) {
michael@0 211 this._setPosition();
michael@0 212 }
michael@0 213 },
michael@0 214
michael@0 215 hitTest: function hitTest(aX, aY) {
michael@0 216 // Gets the pointer of the arrow right in the middle of the
michael@0 217 // monocle.
michael@0 218 aY += this._monocleYHitTextAdjust;
michael@0 219 aX += this._monocleXHitTextAdjust;
michael@0 220 if (aX >= (this._xPos - this._monocleRadius) &&
michael@0 221 aX <= (this._xPos + this._monocleRadius) &&
michael@0 222 aY >= (this._yPos - this._monocleRadius) &&
michael@0 223 aY <= (this._yPos + this._monocleRadius))
michael@0 224 return true;
michael@0 225 return false;
michael@0 226 },
michael@0 227
michael@0 228 swapMonocle: function swapMonocle(aCaret) {
michael@0 229 let targetElement = aCaret._element;
michael@0 230 let targetElementId = aCaret._elementId;
michael@0 231
michael@0 232 aCaret._element = this._element;
michael@0 233 aCaret._element.customDragger._marker = aCaret;
michael@0 234 aCaret._elementId = this._elementId;
michael@0 235
michael@0 236 this._xPos = aCaret._xPos;
michael@0 237 this._yPos = aCaret._yPos;
michael@0 238 this._element = targetElement;
michael@0 239 this._element.customDragger._marker = this;
michael@0 240 this._elementId = targetElementId;
michael@0 241 this._element.visible = true;
michael@0 242 },
michael@0 243
michael@0 244 };
michael@0 245
michael@0 246 /*
michael@0 247 * SelectionHelperUI
michael@0 248 */
michael@0 249
michael@0 250 var SelectionHelperUI = {
michael@0 251 _debugEvents: false,
michael@0 252 _msgTarget: null,
michael@0 253 _startMark: null,
michael@0 254 _endMark: null,
michael@0 255 _caretMark: null,
michael@0 256 _target: null,
michael@0 257 _showAfterUpdate: false,
michael@0 258 _activeSelectionRect: null,
michael@0 259 _selectionMarkIds: [],
michael@0 260 _targetIsEditable: false,
michael@0 261
michael@0 262 /*
michael@0 263 * Properties
michael@0 264 */
michael@0 265
michael@0 266 get startMark() {
michael@0 267 if (this._startMark == null) {
michael@0 268 this._startMark = new Marker(this, "start", this._selectionMarkIds.pop(), 0, 0);
michael@0 269 }
michael@0 270 return this._startMark;
michael@0 271 },
michael@0 272
michael@0 273 get endMark() {
michael@0 274 if (this._endMark == null) {
michael@0 275 this._endMark = new Marker(this, "end", this._selectionMarkIds.pop(), 0, 0);
michael@0 276 }
michael@0 277 return this._endMark;
michael@0 278 },
michael@0 279
michael@0 280 get caretMark() {
michael@0 281 if (this._caretMark == null) {
michael@0 282 this._caretMark = new Marker(this, "caret", this._selectionMarkIds.pop(), 0, 0);
michael@0 283 }
michael@0 284 return this._caretMark;
michael@0 285 },
michael@0 286
michael@0 287 get overlay() {
michael@0 288 return document.getElementById(this.layerMode == kChromeLayer ?
michael@0 289 "chrome-selection-overlay" : "content-selection-overlay");
michael@0 290 },
michael@0 291
michael@0 292 get layerMode() {
michael@0 293 if (this._msgTarget && this._msgTarget instanceof SelectionPrototype)
michael@0 294 return kChromeLayer;
michael@0 295 return kContentLayer;
michael@0 296 },
michael@0 297
michael@0 298 /*
michael@0 299 * isActive (prop)
michael@0 300 *
michael@0 301 * Determines if a selection edit session is currently active.
michael@0 302 */
michael@0 303 get isActive() {
michael@0 304 return this._msgTarget ? true : false;
michael@0 305 },
michael@0 306
michael@0 307 /*
michael@0 308 * isSelectionUIVisible (prop)
michael@0 309 *
michael@0 310 * Determines if edit session monocles are visible. Useful
michael@0 311 * in checking if selection handler is setup for tests.
michael@0 312 */
michael@0 313 get isSelectionUIVisible() {
michael@0 314 if (!this._msgTarget || !this._startMark)
michael@0 315 return false;
michael@0 316 return this._startMark.visible;
michael@0 317 },
michael@0 318
michael@0 319 /*
michael@0 320 * isCaretUIVisible (prop)
michael@0 321 *
michael@0 322 * Determines if caret browsing monocle is visible. Useful
michael@0 323 * in checking if selection handler is setup for tests.
michael@0 324 */
michael@0 325 get isCaretUIVisible() {
michael@0 326 if (!this._msgTarget || !this._caretMark)
michael@0 327 return false;
michael@0 328 return this._caretMark.visible;
michael@0 329 },
michael@0 330
michael@0 331 /*
michael@0 332 * hasActiveDrag (prop)
michael@0 333 *
michael@0 334 * Determines if a marker is actively being dragged (missing call
michael@0 335 * to markerDragStop). Useful in checking if selection handler is
michael@0 336 * setup for tests.
michael@0 337 */
michael@0 338 get hasActiveDrag() {
michael@0 339 if (!this._msgTarget)
michael@0 340 return false;
michael@0 341 if ((this._caretMark && this._caretMark.dragging) ||
michael@0 342 (this._startMark && this._startMark.dragging) ||
michael@0 343 (this._endMark && this._endMark.dragging))
michael@0 344 return true;
michael@0 345 return false;
michael@0 346 },
michael@0 347
michael@0 348
michael@0 349 /*
michael@0 350 * Observers
michael@0 351 */
michael@0 352
michael@0 353 observe: function (aSubject, aTopic, aData) {
michael@0 354 switch (aTopic) {
michael@0 355 case "attach_edit_session_to_content":
michael@0 356 // We receive this from text input bindings when this module
michael@0 357 // isn't accessible.
michael@0 358 this.chromeTextboxClick(aSubject);
michael@0 359 break;
michael@0 360
michael@0 361 case "apzc-transform-begin":
michael@0 362 if (this.isActive && this.layerMode == kContentLayer) {
michael@0 363 this._hideMonocles();
michael@0 364 }
michael@0 365 break;
michael@0 366
michael@0 367 case "apzc-transform-end":
michael@0 368 // The selection range callback will check to see if the new
michael@0 369 // position is off the screen, in which case it shuts down and
michael@0 370 // clears the selection.
michael@0 371 if (this.isActive && this.layerMode == kContentLayer) {
michael@0 372 this._showAfterUpdate = true;
michael@0 373 this._sendAsyncMessage("Browser:SelectionUpdate", {
michael@0 374 isInitiatedByAPZC: true
michael@0 375 });
michael@0 376 }
michael@0 377 break;
michael@0 378 }
michael@0 379 },
michael@0 380
michael@0 381 /*
michael@0 382 * Public apis
michael@0 383 */
michael@0 384
michael@0 385 /*
michael@0 386 * pingSelectionHandler
michael@0 387 *
michael@0 388 * Ping the SelectionHandler and wait for the right response. Insures
michael@0 389 * all previous messages have been received. Useful in checking if
michael@0 390 * selection handler is setup for tests.
michael@0 391 *
michael@0 392 * @return a promise
michael@0 393 */
michael@0 394 pingSelectionHandler: function pingSelectionHandler() {
michael@0 395 if (!this.isActive)
michael@0 396 return null;
michael@0 397
michael@0 398 if (this._pingCount == undefined) {
michael@0 399 this._pingCount = 0;
michael@0 400 this._pingArray = [];
michael@0 401 }
michael@0 402
michael@0 403 this._pingCount++;
michael@0 404
michael@0 405 let deferred = Promise.defer();
michael@0 406 this._pingArray.push({
michael@0 407 id: this._pingCount,
michael@0 408 deferred: deferred
michael@0 409 });
michael@0 410
michael@0 411 this._sendAsyncMessage("Browser:SelectionHandlerPing", { id: this._pingCount });
michael@0 412 return deferred.promise;
michael@0 413 },
michael@0 414
michael@0 415 /*
michael@0 416 * openEditSession
michael@0 417 *
michael@0 418 * Attempts to select underlying text at a point and begins editing
michael@0 419 * the section.
michael@0 420 *
michael@0 421 * @param aMsgTarget - Browser or chrome message target
michael@0 422 * @param aX, aY - Browser relative client coordinates.
michael@0 423 * @param aSetFocus - (optional) For form inputs, requests that the focus
michael@0 424 * be set to the element.
michael@0 425 */
michael@0 426 openEditSession: function openEditSession(aMsgTarget, aX, aY, aSetFocus) {
michael@0 427 if (!aMsgTarget || this.isActive)
michael@0 428 return;
michael@0 429 this._init(aMsgTarget);
michael@0 430 this._setupDebugOptions();
michael@0 431 let setFocus = aSetFocus || false;
michael@0 432 // Send this over to SelectionHandler in content, they'll message us
michael@0 433 // back with information on the current selection. SelectionStart
michael@0 434 // takes client coordinates.
michael@0 435 this._sendAsyncMessage("Browser:SelectionStart", {
michael@0 436 setFocus: setFocus,
michael@0 437 xPos: aX,
michael@0 438 yPos: aY
michael@0 439 });
michael@0 440 },
michael@0 441
michael@0 442 /*
michael@0 443 * attachEditSession
michael@0 444 *
michael@0 445 * Attaches to existing selection and begins editing.
michael@0 446 *
michael@0 447 * @param aMsgTarget - Browser or chrome message target.
michael@0 448 * @param aX Tap browser relative client X coordinate.
michael@0 449 * @param aY Tap browser relative client Y coordinate.
michael@0 450 * @param aTarget Actual tap target (optional).
michael@0 451 */
michael@0 452 attachEditSession: function attachEditSession(aMsgTarget, aX, aY, aTarget) {
michael@0 453 if (!aMsgTarget || this.isActive)
michael@0 454 return;
michael@0 455 this._init(aMsgTarget);
michael@0 456 this._setupDebugOptions();
michael@0 457
michael@0 458 // Send this over to SelectionHandler in content, they'll message us
michael@0 459 // back with information on the current selection. SelectionAttach
michael@0 460 // takes client coordinates.
michael@0 461 this._sendAsyncMessage("Browser:SelectionAttach", {
michael@0 462 target: aTarget,
michael@0 463 xPos: aX,
michael@0 464 yPos: aY
michael@0 465 });
michael@0 466 },
michael@0 467
michael@0 468 /*
michael@0 469 * attachToCaret
michael@0 470 *
michael@0 471 * Initiates a touch caret selection session for a text input.
michael@0 472 * Can be called multiple times to move the caret marker around.
michael@0 473 *
michael@0 474 * Note the caret marker is pretty limited in functionality. The
michael@0 475 * only thing is can do is be displayed at the caret position.
michael@0 476 * Once the user starts a drag, the caret marker is hidden, and
michael@0 477 * the start and end markers take over.
michael@0 478 *
michael@0 479 * @param aMsgTarget - Browser or chrome message target.
michael@0 480 * @param aX Tap browser relative client X coordinate.
michael@0 481 * @param aY Tap browser relative client Y coordinate.
michael@0 482 * @param aTarget Actual tap target (optional).
michael@0 483 */
michael@0 484 attachToCaret: function attachToCaret(aMsgTarget, aX, aY, aTarget) {
michael@0 485 if (!this.isActive) {
michael@0 486 this._init(aMsgTarget);
michael@0 487 this._setupDebugOptions();
michael@0 488 } else {
michael@0 489 this._hideMonocles();
michael@0 490 }
michael@0 491
michael@0 492 this._lastCaretAttachment = {
michael@0 493 target: aTarget,
michael@0 494 xPos: aX,
michael@0 495 yPos: aY
michael@0 496 };
michael@0 497
michael@0 498 this._sendAsyncMessage("Browser:CaretAttach", {
michael@0 499 target: aTarget,
michael@0 500 xPos: aX,
michael@0 501 yPos: aY
michael@0 502 });
michael@0 503 },
michael@0 504
michael@0 505 /*
michael@0 506 * canHandleContextMenuMsg
michael@0 507 *
michael@0 508 * Determines if we can handle a ContextMenuHandler message.
michael@0 509 */
michael@0 510 canHandleContextMenuMsg: function canHandleContextMenuMsg(aMessage) {
michael@0 511 if (aMessage.json.types.indexOf("content-text") != -1)
michael@0 512 return true;
michael@0 513 return false;
michael@0 514 },
michael@0 515
michael@0 516 /*
michael@0 517 * closeEditSession(aClearSelection)
michael@0 518 *
michael@0 519 * Closes an active edit session and shuts down. Does not clear existing
michael@0 520 * selection regions if they exist.
michael@0 521 *
michael@0 522 * @param aClearSelection bool indicating if the selection handler should also
michael@0 523 * clear any selection. optional, the default is false.
michael@0 524 */
michael@0 525 closeEditSession: function closeEditSession(aClearSelection) {
michael@0 526 if (!this.isActive) {
michael@0 527 return;
michael@0 528 }
michael@0 529 // This will callback in _selectionHandlerShutdown in
michael@0 530 // which we will call _shutdown().
michael@0 531 let clearSelection = aClearSelection || false;
michael@0 532 this._sendAsyncMessage("Browser:SelectionClose", {
michael@0 533 clearSelection: clearSelection
michael@0 534 });
michael@0 535 },
michael@0 536
michael@0 537 /*
michael@0 538 * Click handler for chrome pages loaded into the browser (about:config).
michael@0 539 * Called from the text input bindings via the attach_edit_session_to_content
michael@0 540 * observer.
michael@0 541 */
michael@0 542 chromeTextboxClick: function (aEvent) {
michael@0 543 this.attachEditSession(Browser.selectedTab.browser, aEvent.clientX,
michael@0 544 aEvent.clientY, aEvent.target);
michael@0 545 },
michael@0 546
michael@0 547 /*
michael@0 548 * Handy debug routines that work independent of selection. They
michael@0 549 * make use of the selection overlay for drawing points.
michael@0 550 */
michael@0 551
michael@0 552 debugDisplayDebugPoint: function (aLeft, aTop, aSize, aCssColorStr, aFill) {
michael@0 553 this.overlay.enabled = true;
michael@0 554 this.overlay.displayDebugLayer = true;
michael@0 555 this.overlay.addDebugRect(aLeft, aTop, aLeft + aSize, aTop + aSize,
michael@0 556 aCssColorStr, aFill);
michael@0 557 },
michael@0 558
michael@0 559 debugClearDebugPoints: function () {
michael@0 560 this.overlay.displayDebugLayer = false;
michael@0 561 if (!this._msgTarget) {
michael@0 562 this.overlay.enabled = false;
michael@0 563 }
michael@0 564 },
michael@0 565
michael@0 566 /*
michael@0 567 * Init and shutdown
michael@0 568 */
michael@0 569
michael@0 570 init: function () {
michael@0 571 let os = Services.obs;
michael@0 572 os.addObserver(this, "attach_edit_session_to_content", false);
michael@0 573 os.addObserver(this, "apzc-transform-begin", false);
michael@0 574 os.addObserver(this, "apzc-transform-end", false);
michael@0 575 },
michael@0 576
michael@0 577 _init: function _init(aMsgTarget) {
michael@0 578 // store the target message manager
michael@0 579 this._msgTarget = aMsgTarget;
michael@0 580
michael@0 581 // Init our list of available monocle ids
michael@0 582 this._setupMonocleIdArray();
michael@0 583
michael@0 584 // Init selection rect info
michael@0 585 this._activeSelectionRect = Util.getCleanRect();
michael@0 586 this._targetElementRect = Util.getCleanRect();
michael@0 587
michael@0 588 // SelectionHandler messages
michael@0 589 messageManager.addMessageListener("Content:SelectionRange", this);
michael@0 590 messageManager.addMessageListener("Content:SelectionCopied", this);
michael@0 591 messageManager.addMessageListener("Content:SelectionFail", this);
michael@0 592 messageManager.addMessageListener("Content:SelectionDebugRect", this);
michael@0 593 messageManager.addMessageListener("Content:HandlerShutdown", this);
michael@0 594 messageManager.addMessageListener("Content:SelectionHandlerPong", this);
michael@0 595 messageManager.addMessageListener("Content:SelectionSwap", this);
michael@0 596
michael@0 597 // capture phase
michael@0 598 window.addEventListener("keypress", this, true);
michael@0 599 window.addEventListener("MozPrecisePointer", this, true);
michael@0 600 window.addEventListener("MozDeckOffsetChanging", this, true);
michael@0 601 window.addEventListener("MozDeckOffsetChanged", this, true);
michael@0 602 window.addEventListener("KeyboardChanged", this, true);
michael@0 603
michael@0 604 // bubble phase
michael@0 605 window.addEventListener("click", this, false);
michael@0 606 window.addEventListener("touchstart", this, false);
michael@0 607
michael@0 608 Elements.browsers.addEventListener("URLChanged", this, true);
michael@0 609 Elements.browsers.addEventListener("SizeChanged", this, true);
michael@0 610
michael@0 611 Elements.tabList.addEventListener("TabSelect", this, true);
michael@0 612
michael@0 613 Elements.navbar.addEventListener("transitionend", this, true);
michael@0 614
michael@0 615 this.overlay.enabled = true;
michael@0 616 },
michael@0 617
michael@0 618 _shutdown: function _shutdown() {
michael@0 619 messageManager.removeMessageListener("Content:SelectionRange", this);
michael@0 620 messageManager.removeMessageListener("Content:SelectionCopied", this);
michael@0 621 messageManager.removeMessageListener("Content:SelectionFail", this);
michael@0 622 messageManager.removeMessageListener("Content:SelectionDebugRect", this);
michael@0 623 messageManager.removeMessageListener("Content:HandlerShutdown", this);
michael@0 624 messageManager.removeMessageListener("Content:SelectionHandlerPong", this);
michael@0 625 messageManager.removeMessageListener("Content:SelectionSwap", this);
michael@0 626
michael@0 627 window.removeEventListener("keypress", this, true);
michael@0 628 window.removeEventListener("MozPrecisePointer", this, true);
michael@0 629 window.removeEventListener("MozDeckOffsetChanging", this, true);
michael@0 630 window.removeEventListener("MozDeckOffsetChanged", this, true);
michael@0 631 window.removeEventListener("KeyboardChanged", this, true);
michael@0 632
michael@0 633 window.removeEventListener("click", this, false);
michael@0 634 window.removeEventListener("touchstart", this, false);
michael@0 635
michael@0 636 Elements.browsers.removeEventListener("URLChanged", this, true);
michael@0 637 Elements.browsers.removeEventListener("SizeChanged", this, true);
michael@0 638
michael@0 639 Elements.tabList.removeEventListener("TabSelect", this, true);
michael@0 640
michael@0 641 Elements.navbar.removeEventListener("transitionend", this, true);
michael@0 642
michael@0 643 this._shutdownAllMarkers();
michael@0 644
michael@0 645 this._selectionMarkIds = [];
michael@0 646 this._msgTarget = null;
michael@0 647 this._activeSelectionRect = null;
michael@0 648
michael@0 649 this.overlay.displayDebugLayer = false;
michael@0 650 this.overlay.enabled = false;
michael@0 651 },
michael@0 652
michael@0 653 /*
michael@0 654 * Utilities
michael@0 655 */
michael@0 656
michael@0 657 /*
michael@0 658 * _swapCaretMarker
michael@0 659 *
michael@0 660 * Swap two drag markers - used when transitioning from caret mode
michael@0 661 * to selection mode. We take the current caret marker (which is in a
michael@0 662 * drag state) and swap it out with one of the selection markers.
michael@0 663 */
michael@0 664 _swapCaretMarker: function _swapCaretMarker(aDirection) {
michael@0 665 let targetMark = null;
michael@0 666 if (aDirection == "start")
michael@0 667 targetMark = this.startMark;
michael@0 668 else
michael@0 669 targetMark = this.endMark;
michael@0 670 let caret = this.caretMark;
michael@0 671 targetMark.swapMonocle(caret);
michael@0 672 let id = caret._elementId;
michael@0 673 caret.shutdown();
michael@0 674 this._caretMark = null;
michael@0 675 this._selectionMarkIds.push(id);
michael@0 676 },
michael@0 677
michael@0 678 /*
michael@0 679 * _transitionFromCaretToSelection
michael@0 680 *
michael@0 681 * Transitions from caret mode to text selection mode.
michael@0 682 */
michael@0 683 _transitionFromCaretToSelection: function _transitionFromCaretToSelection(aDirection) {
michael@0 684 // Get selection markers initialized if they aren't already
michael@0 685 { let mark = this.startMark; mark = this.endMark; }
michael@0 686
michael@0 687 // Swap the caret marker out for the start or end marker depending
michael@0 688 // on direction.
michael@0 689 this._swapCaretMarker(aDirection);
michael@0 690
michael@0 691 let targetMark = null;
michael@0 692 if (aDirection == "start")
michael@0 693 targetMark = this.startMark;
michael@0 694 else
michael@0 695 targetMark = this.endMark;
michael@0 696
michael@0 697 // Position both in the same starting location.
michael@0 698 this.startMark.position(targetMark.xPos, targetMark.yPos);
michael@0 699 this.endMark.position(targetMark.xPos, targetMark.yPos);
michael@0 700
michael@0 701 // We delay transitioning until we know which direction the user is dragging
michael@0 702 // based on a hysteresis value in the drag marker code. Down in our caller, we
michael@0 703 // cache the first drag position in _cachedCaretPos so we can select from the
michael@0 704 // initial caret drag position. Use those values if we have them. (Note
michael@0 705 // _cachedCaretPos has already been translated in _getMarkerBaseMessage.)
michael@0 706 let xpos = this._cachedCaretPos ? this._cachedCaretPos.xPos :
michael@0 707 this._msgTarget.ctobx(targetMark.xPos, true);
michael@0 708 let ypos = this._cachedCaretPos ? this._cachedCaretPos.yPos :
michael@0 709 this._msgTarget.ctoby(targetMark.yPos, true);
michael@0 710
michael@0 711 // Start the selection monocle drag. SelectionHandler relies on this
michael@0 712 // for getting initialized. This will also trigger a message back for
michael@0 713 // monocle positioning. Note, markerDragMove is still on the stack in
michael@0 714 // this call!
michael@0 715 this._sendAsyncMessage("Browser:SelectionSwitchMode", {
michael@0 716 newMode: "selection",
michael@0 717 change: targetMark.tag,
michael@0 718 xPos: xpos,
michael@0 719 yPos: ypos,
michael@0 720 });
michael@0 721 },
michael@0 722
michael@0 723 /*
michael@0 724 * _setupDebugOptions
michael@0 725 *
michael@0 726 * Sends a message over to content instructing it to
michael@0 727 * turn on various debug features.
michael@0 728 */
michael@0 729 _setupDebugOptions: function _setupDebugOptions() {
michael@0 730 // Debug options for selection
michael@0 731 let debugOpts = { dumpRanges: false, displayRanges: false, dumpEvents: false };
michael@0 732 try {
michael@0 733 if (Services.prefs.getBoolPref(kDebugSelectionDumpPref))
michael@0 734 debugOpts.displayRanges = true;
michael@0 735 } catch (ex) {}
michael@0 736 try {
michael@0 737 if (Services.prefs.getBoolPref(kDebugSelectionDisplayPref))
michael@0 738 debugOpts.displayRanges = true;
michael@0 739 } catch (ex) {}
michael@0 740 try {
michael@0 741 if (Services.prefs.getBoolPref(kDebugSelectionDumpEvents)) {
michael@0 742 debugOpts.dumpEvents = true;
michael@0 743 this._debugEvents = true;
michael@0 744 }
michael@0 745 } catch (ex) {}
michael@0 746
michael@0 747 if (debugOpts.displayRanges || debugOpts.dumpRanges || debugOpts.dumpEvents) {
michael@0 748 // Turn on the debug layer
michael@0 749 this.overlay.displayDebugLayer = true;
michael@0 750 // Tell SelectionHandler what to do
michael@0 751 this._sendAsyncMessage("Browser:SelectionDebug", debugOpts);
michael@0 752 }
michael@0 753 },
michael@0 754
michael@0 755 /*
michael@0 756 * _sendAsyncMessage
michael@0 757 *
michael@0 758 * Helper for sending a message to SelectionHandler.
michael@0 759 */
michael@0 760 _sendAsyncMessage: function _sendAsyncMessage(aMsg, aJson) {
michael@0 761 if (!this._msgTarget) {
michael@0 762 if (this._debugEvents)
michael@0 763 Util.dumpLn("SelectionHelperUI sendAsyncMessage could not send", aMsg);
michael@0 764 return;
michael@0 765 }
michael@0 766 if (this._msgTarget && this._msgTarget instanceof SelectionPrototype) {
michael@0 767 this._msgTarget.msgHandler(aMsg, aJson);
michael@0 768 } else {
michael@0 769 this._msgTarget.messageManager.sendAsyncMessage(aMsg, aJson);
michael@0 770 }
michael@0 771 },
michael@0 772
michael@0 773 _checkForActiveDrag: function _checkForActiveDrag() {
michael@0 774 return (this.startMark.dragging || this.endMark.dragging ||
michael@0 775 this.caretMark.dragging);
michael@0 776 },
michael@0 777
michael@0 778 _hitTestSelection: function _hitTestSelection(aEvent) {
michael@0 779 // Ignore if the double tap isn't on our active selection rect.
michael@0 780 if (this._activeSelectionRect &&
michael@0 781 Util.pointWithinRect(aEvent.clientX, aEvent.clientY, this._activeSelectionRect)) {
michael@0 782 return true;
michael@0 783 }
michael@0 784 return false;
michael@0 785 },
michael@0 786
michael@0 787 /*
michael@0 788 * _setCaretPositionAtPoint - sets the current caret position.
michael@0 789 *
michael@0 790 * @param aX, aY - browser relative client coordinates
michael@0 791 */
michael@0 792 _setCaretPositionAtPoint: function _setCaretPositionAtPoint(aX, aY) {
michael@0 793 let json = this._getMarkerBaseMessage("caret");
michael@0 794 json.caret.xPos = aX;
michael@0 795 json.caret.yPos = aY;
michael@0 796 this._sendAsyncMessage("Browser:CaretUpdate", json);
michael@0 797 },
michael@0 798
michael@0 799 /*
michael@0 800 * _shutdownAllMarkers
michael@0 801 *
michael@0 802 * Helper for shutting down all markers and
michael@0 803 * freeing the objects associated with them.
michael@0 804 */
michael@0 805 _shutdownAllMarkers: function _shutdownAllMarkers() {
michael@0 806 if (this._startMark)
michael@0 807 this._startMark.shutdown();
michael@0 808 if (this._endMark)
michael@0 809 this._endMark.shutdown();
michael@0 810 if (this._caretMark)
michael@0 811 this._caretMark.shutdown();
michael@0 812
michael@0 813 this._startMark = null;
michael@0 814 this._endMark = null;
michael@0 815 this._caretMark = null;
michael@0 816 },
michael@0 817
michael@0 818 /*
michael@0 819 * _setupMonocleIdArray
michael@0 820 *
michael@0 821 * Helper for initing the array of monocle anon ids.
michael@0 822 */
michael@0 823 _setupMonocleIdArray: function _setupMonocleIdArray() {
michael@0 824 this._selectionMarkIds = ["selectionhandle-mark1",
michael@0 825 "selectionhandle-mark2",
michael@0 826 "selectionhandle-mark3"];
michael@0 827 },
michael@0 828
michael@0 829 _hideMonocles: function _hideMonocles() {
michael@0 830 if (this._startMark) {
michael@0 831 this.startMark.hide();
michael@0 832 }
michael@0 833 if (this._endMark) {
michael@0 834 this.endMark.hide();
michael@0 835 }
michael@0 836 if (this._caretMark) {
michael@0 837 this.caretMark.hide();
michael@0 838 }
michael@0 839 },
michael@0 840
michael@0 841 _showMonocles: function _showMonocles(aSelection) {
michael@0 842 if (!aSelection) {
michael@0 843 if (this._checkMonocleVisibility(this.caretMark.xPos, this.caretMark.yPos)) {
michael@0 844 this.caretMark.show();
michael@0 845 }
michael@0 846 } else {
michael@0 847 if (this._checkMonocleVisibility(this.endMark.xPos, this.endMark.yPos)) {
michael@0 848 this.endMark.show();
michael@0 849 }
michael@0 850 if (this._checkMonocleVisibility(this.startMark.xPos, this.startMark.yPos)) {
michael@0 851 this.startMark.show();
michael@0 852 }
michael@0 853 }
michael@0 854 },
michael@0 855
michael@0 856 _checkMonocleVisibility: function(aX, aY) {
michael@0 857 let viewport = Browser.selectedBrowser.contentViewportBounds;
michael@0 858 aX = this._msgTarget.ctobx(aX);
michael@0 859 aY = this._msgTarget.ctoby(aY);
michael@0 860 if (aX < viewport.x || aY < viewport.y ||
michael@0 861 aX > (viewport.x + viewport.width) ||
michael@0 862 aY > (viewport.y + viewport.height)) {
michael@0 863 return false;
michael@0 864 }
michael@0 865 return true;
michael@0 866 },
michael@0 867
michael@0 868 /*
michael@0 869 * Event handlers for document events
michael@0 870 */
michael@0 871
michael@0 872 /*
michael@0 873 * Handles taps that move the current caret around in text edits,
michael@0 874 * clear active selection and focus when necessary, or change
michael@0 875 * modes. Only active after SelectionHandlerUI is initialized.
michael@0 876 */
michael@0 877 _onClick: function(aEvent) {
michael@0 878 if (this.layerMode == kChromeLayer && this._targetIsEditable) {
michael@0 879 this.attachToCaret(this._msgTarget, aEvent.clientX, aEvent.clientY,
michael@0 880 aEvent.target);
michael@0 881 }
michael@0 882 },
michael@0 883
michael@0 884 _onKeypress: function _onKeypress() {
michael@0 885 this.closeEditSession();
michael@0 886 },
michael@0 887
michael@0 888 _onResize: function _onResize() {
michael@0 889 this._sendAsyncMessage("Browser:SelectionUpdate", {});
michael@0 890 },
michael@0 891
michael@0 892 /*
michael@0 893 * _onDeckOffsetChanging - fired by ContentAreaObserver before the browser
michael@0 894 * deck is shifted for form input access in response to a soft keyboard
michael@0 895 * display.
michael@0 896 */
michael@0 897 _onDeckOffsetChanging: function _onDeckOffsetChanging(aEvent) {
michael@0 898 // Hide the monocles temporarily
michael@0 899 this._hideMonocles();
michael@0 900 },
michael@0 901
michael@0 902 /*
michael@0 903 * _onDeckOffsetChanged - fired by ContentAreaObserver after the browser
michael@0 904 * deck is shifted for form input access in response to a soft keyboard
michael@0 905 * display.
michael@0 906 */
michael@0 907 _onDeckOffsetChanged: function _onDeckOffsetChanged(aEvent) {
michael@0 908 // Update the monocle position and display
michael@0 909 this.attachToCaret(null, this._lastCaretAttachment.xPos,
michael@0 910 this._lastCaretAttachment.yPos, this._lastCaretAttachment.target);
michael@0 911 },
michael@0 912
michael@0 913 /*
michael@0 914 * Detects when the nav bar transitions, so we can enable selection at the
michael@0 915 * appropriate location once the transition is complete, or shutdown
michael@0 916 * selection down when the nav bar is hidden.
michael@0 917 */
michael@0 918 _onNavBarTransitionEvent: function _onNavBarTransitionEvent(aEvent) {
michael@0 919 // Ignore when selection is in content
michael@0 920 if (this.layerMode == kContentLayer) {
michael@0 921 return;
michael@0 922 }
michael@0 923
michael@0 924 // After tansitioning up, show the monocles
michael@0 925 if (Elements.navbar.isShowing) {
michael@0 926 this._showAfterUpdate = true;
michael@0 927 this._sendAsyncMessage("Browser:SelectionUpdate", {});
michael@0 928 }
michael@0 929 },
michael@0 930
michael@0 931 _onKeyboardChangedEvent: function _onKeyboardChangedEvent() {
michael@0 932 if (!this.isActive || this.layerMode == kContentLayer) {
michael@0 933 return;
michael@0 934 }
michael@0 935 this._sendAsyncMessage("Browser:SelectionUpdate", {});
michael@0 936 },
michael@0 937
michael@0 938 /*
michael@0 939 * Event handlers for message manager
michael@0 940 */
michael@0 941
michael@0 942 _onDebugRectRequest: function _onDebugRectRequest(aMsg) {
michael@0 943 this.overlay.addDebugRect(aMsg.left, aMsg.top, aMsg.right, aMsg.bottom,
michael@0 944 aMsg.color, aMsg.fill, aMsg.id);
michael@0 945 },
michael@0 946
michael@0 947 _selectionHandlerShutdown: function _selectionHandlerShutdown() {
michael@0 948 this._shutdown();
michael@0 949 },
michael@0 950
michael@0 951 _selectionSwap: function _selectionSwap() {
michael@0 952 [this.startMark.tag, this.endMark.tag] = [this.endMark.tag,
michael@0 953 this.startMark.tag];
michael@0 954 [this._startMark, this._endMark] = [this.endMark, this.startMark];
michael@0 955 },
michael@0 956
michael@0 957 /*
michael@0 958 * Message handlers
michael@0 959 */
michael@0 960
michael@0 961 _onSelectionCopied: function _onSelectionCopied(json) {
michael@0 962 this.closeEditSession(true);
michael@0 963 },
michael@0 964
michael@0 965 _onSelectionRangeChange: function _onSelectionRangeChange(json) {
michael@0 966 let haveSelectionRect = true;
michael@0 967
michael@0 968 if (json.updateStart) {
michael@0 969 let x = this._msgTarget.btocx(json.start.xPos, true);
michael@0 970 let y = this._msgTarget.btocy(json.start.yPos, true);
michael@0 971 this.startMark.position(x, y, json.start.restrictedToBounds);
michael@0 972 }
michael@0 973
michael@0 974 if (json.updateEnd) {
michael@0 975 let x = this._msgTarget.btocx(json.end.xPos, true);
michael@0 976 let y = this._msgTarget.btocy(json.end.yPos, true);
michael@0 977 this.endMark.position(x, y, json.end.restrictedToBounds);
michael@0 978 }
michael@0 979
michael@0 980 if (json.updateCaret) {
michael@0 981 let x = this._msgTarget.btocx(json.caret.xPos, true);
michael@0 982 let y = this._msgTarget.btocy(json.caret.yPos, true);
michael@0 983 // If selectionRangeFound is set SelectionHelper found a range we can
michael@0 984 // attach to. If not, there's no text in the control, and hence no caret
michael@0 985 // position information we can use.
michael@0 986 haveSelectionRect = json.selectionRangeFound;
michael@0 987 if (json.selectionRangeFound) {
michael@0 988 this.caretMark.position(x, y);
michael@0 989 this._showMonocles(false);
michael@0 990 }
michael@0 991 }
michael@0 992
michael@0 993 if (this._showAfterUpdate) {
michael@0 994 this._showAfterUpdate = false;
michael@0 995 this._showMonocles(!json.updateCaret);
michael@0 996 }
michael@0 997
michael@0 998 this._targetIsEditable = json.targetIsEditable;
michael@0 999 this._activeSelectionRect = haveSelectionRect ?
michael@0 1000 this._msgTarget.rectBrowserToClient(json.selection, true) :
michael@0 1001 this._activeSelectionRect = Util.getCleanRect();
michael@0 1002 this._targetElementRect =
michael@0 1003 this._msgTarget.rectBrowserToClient(json.element, true);
michael@0 1004
michael@0 1005 // If this is the end of a selection move show the appropriate
michael@0 1006 // monocle images. src=(start, update, end, caret)
michael@0 1007 if (json.src == "start" || json.src == "end") {
michael@0 1008 this._showMonocles(true);
michael@0 1009 }
michael@0 1010 },
michael@0 1011
michael@0 1012 _onSelectionFail: function _onSelectionFail() {
michael@0 1013 Util.dumpLn("failed to get a selection.");
michael@0 1014 this.closeEditSession();
michael@0 1015 },
michael@0 1016
michael@0 1017 /*
michael@0 1018 * _onPong
michael@0 1019 *
michael@0 1020 * Handles the closure of promise we return when we send a ping
michael@0 1021 * to SelectionHandler in pingSelectionHandler. Testing use.
michael@0 1022 */
michael@0 1023 _onPong: function _onPong(aId) {
michael@0 1024 let ping = this._pingArray.pop();
michael@0 1025 if (ping.id != aId) {
michael@0 1026 ping.deferred.reject(
michael@0 1027 new Error("Selection module's pong doesn't match our last ping."));
michael@0 1028 }
michael@0 1029 ping.deferred.resolve();
michael@0 1030 },
michael@0 1031
michael@0 1032 /*
michael@0 1033 * Events
michael@0 1034 */
michael@0 1035
michael@0 1036 handleEvent: function handleEvent(aEvent) {
michael@0 1037 if (this._debugEvents && aEvent.type != "touchmove") {
michael@0 1038 Util.dumpLn("SelectionHelperUI:", aEvent.type);
michael@0 1039 }
michael@0 1040 switch (aEvent.type) {
michael@0 1041 case "click":
michael@0 1042 this._onClick(aEvent);
michael@0 1043 break;
michael@0 1044
michael@0 1045 case "touchstart": {
michael@0 1046 if (aEvent.touches.length != 1)
michael@0 1047 break;
michael@0 1048 // Only prevent default if we're dragging so that
michael@0 1049 // APZC doesn't scroll.
michael@0 1050 if (this._checkForActiveDrag()) {
michael@0 1051 aEvent.preventDefault();
michael@0 1052 }
michael@0 1053 break;
michael@0 1054 }
michael@0 1055
michael@0 1056 case "keypress":
michael@0 1057 this._onKeypress(aEvent);
michael@0 1058 break;
michael@0 1059
michael@0 1060 case "SizeChanged":
michael@0 1061 this._onResize(aEvent);
michael@0 1062 break;
michael@0 1063
michael@0 1064 case "URLChanged":
michael@0 1065 case "TabSelect":
michael@0 1066 this._shutdown();
michael@0 1067 break;
michael@0 1068
michael@0 1069 case "MozPrecisePointer":
michael@0 1070 this.closeEditSession(true);
michael@0 1071 break;
michael@0 1072
michael@0 1073 case "MozDeckOffsetChanging":
michael@0 1074 this._onDeckOffsetChanging(aEvent);
michael@0 1075 break;
michael@0 1076
michael@0 1077 case "MozDeckOffsetChanged":
michael@0 1078 this._onDeckOffsetChanged(aEvent);
michael@0 1079 break;
michael@0 1080
michael@0 1081 case "transitionend":
michael@0 1082 this._onNavBarTransitionEvent(aEvent);
michael@0 1083 break;
michael@0 1084
michael@0 1085 case "KeyboardChanged":
michael@0 1086 this._onKeyboardChangedEvent();
michael@0 1087 break;
michael@0 1088 }
michael@0 1089 },
michael@0 1090
michael@0 1091 receiveMessage: function sh_receiveMessage(aMessage) {
michael@0 1092 if (this._debugEvents) Util.dumpLn("SelectionHelperUI:", aMessage.name);
michael@0 1093 let json = aMessage.json;
michael@0 1094 switch (aMessage.name) {
michael@0 1095 case "Content:SelectionFail":
michael@0 1096 this._onSelectionFail();
michael@0 1097 break;
michael@0 1098 case "Content:SelectionRange":
michael@0 1099 this._onSelectionRangeChange(json);
michael@0 1100 break;
michael@0 1101 case "Content:SelectionCopied":
michael@0 1102 this._onSelectionCopied(json);
michael@0 1103 break;
michael@0 1104 case "Content:SelectionDebugRect":
michael@0 1105 this._onDebugRectRequest(json);
michael@0 1106 break;
michael@0 1107 case "Content:HandlerShutdown":
michael@0 1108 this._selectionHandlerShutdown();
michael@0 1109 break;
michael@0 1110 case "Content:SelectionSwap":
michael@0 1111 this._selectionSwap();
michael@0 1112 break;
michael@0 1113 case "Content:SelectionHandlerPong":
michael@0 1114 this._onPong(json.id);
michael@0 1115 break;
michael@0 1116 }
michael@0 1117 },
michael@0 1118
michael@0 1119 /*
michael@0 1120 * Callbacks from markers
michael@0 1121 */
michael@0 1122
michael@0 1123 _getMarkerBaseMessage: function _getMarkerBaseMessage(aMarkerTag) {
michael@0 1124 return {
michael@0 1125 change: aMarkerTag,
michael@0 1126 start: {
michael@0 1127 xPos: this._msgTarget.ctobx(this.startMark.xPos, true),
michael@0 1128 yPos: this._msgTarget.ctoby(this.startMark.yPos, true),
michael@0 1129 restrictedToBounds: this.startMark.restrictedToBounds
michael@0 1130 },
michael@0 1131 end: {
michael@0 1132 xPos: this._msgTarget.ctobx(this.endMark.xPos, true),
michael@0 1133 yPos: this._msgTarget.ctoby(this.endMark.yPos, true),
michael@0 1134 restrictedToBounds: this.endMark.restrictedToBounds
michael@0 1135 },
michael@0 1136 caret: {
michael@0 1137 xPos: this._msgTarget.ctobx(this.caretMark.xPos, true),
michael@0 1138 yPos: this._msgTarget.ctoby(this.caretMark.yPos, true)
michael@0 1139 },
michael@0 1140 };
michael@0 1141 },
michael@0 1142
michael@0 1143 markerDragStart: function markerDragStart(aMarker) {
michael@0 1144 let json = this._getMarkerBaseMessage(aMarker.tag);
michael@0 1145 if (aMarker.tag == "caret") {
michael@0 1146 // Cache for when we start the drag in _transitionFromCaretToSelection.
michael@0 1147 if (!this._cachedCaretPos) {
michael@0 1148 this._cachedCaretPos = this._getMarkerBaseMessage(aMarker.tag).caret;
michael@0 1149 }
michael@0 1150 return;
michael@0 1151 }
michael@0 1152 this._sendAsyncMessage("Browser:SelectionMoveStart", json);
michael@0 1153 },
michael@0 1154
michael@0 1155 markerDragStop: function markerDragStop(aMarker) {
michael@0 1156 let json = this._getMarkerBaseMessage(aMarker.tag);
michael@0 1157 if (aMarker.tag == "caret") {
michael@0 1158 this._cachedCaretPos = null;
michael@0 1159 return;
michael@0 1160 }
michael@0 1161 this._sendAsyncMessage("Browser:SelectionMoveEnd", json);
michael@0 1162 },
michael@0 1163
michael@0 1164 markerDragMove: function markerDragMove(aMarker, aDirection) {
michael@0 1165 if (aMarker.tag == "caret") {
michael@0 1166 // If direction is "tbd" the drag monocle hasn't determined which
michael@0 1167 // direction the user is dragging.
michael@0 1168 if (aDirection != "tbd") {
michael@0 1169 // We are going to transition from caret browsing mode to selection
michael@0 1170 // mode on drag. So swap the caret monocle for a start or end monocle
michael@0 1171 // depending on the direction of the drag, and start selecting text.
michael@0 1172 this._transitionFromCaretToSelection(aDirection);
michael@0 1173 return false;
michael@0 1174 }
michael@0 1175 return true;
michael@0 1176 }
michael@0 1177 this._cachedCaretPos = null;
michael@0 1178
michael@0 1179 // We'll re-display these after the drag is complete.
michael@0 1180 this._hideMonocles();
michael@0 1181
michael@0 1182 let json = this._getMarkerBaseMessage(aMarker.tag);
michael@0 1183 this._sendAsyncMessage("Browser:SelectionMove", json);
michael@0 1184 return true;
michael@0 1185 },
michael@0 1186 };

mercurial