browser/metro/base/content/contenthandlers/SelectionHandler.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 let Ci = Components.interfaces;
michael@0 6 let Cc = Components.classes;
michael@0 7
michael@0 8 dump("### SelectionHandler.js loaded\n");
michael@0 9
michael@0 10 var SelectionHandler = {
michael@0 11 init: function init() {
michael@0 12 this.type = kContentSelector;
michael@0 13 this.snap = true;
michael@0 14 this.lastYPos = this.lastXPos = null;
michael@0 15 addMessageListener("Browser:SelectionStart", this);
michael@0 16 addMessageListener("Browser:SelectionAttach", this);
michael@0 17 addMessageListener("Browser:SelectionEnd", this);
michael@0 18 addMessageListener("Browser:SelectionMoveStart", this);
michael@0 19 addMessageListener("Browser:SelectionMove", this);
michael@0 20 addMessageListener("Browser:SelectionMoveEnd", this);
michael@0 21 addMessageListener("Browser:SelectionUpdate", this);
michael@0 22 addMessageListener("Browser:SelectionClose", this);
michael@0 23 addMessageListener("Browser:SelectionCopy", this);
michael@0 24 addMessageListener("Browser:SelectionDebug", this);
michael@0 25 addMessageListener("Browser:CaretAttach", this);
michael@0 26 addMessageListener("Browser:CaretMove", this);
michael@0 27 addMessageListener("Browser:CaretUpdate", this);
michael@0 28 addMessageListener("Browser:SelectionSwitchMode", this);
michael@0 29 addMessageListener("Browser:RepositionInfoRequest", this);
michael@0 30 addMessageListener("Browser:SelectionHandlerPing", this);
michael@0 31 addMessageListener("Browser:ResetLastPos", this);
michael@0 32 },
michael@0 33
michael@0 34 shutdown: function shutdown() {
michael@0 35 removeMessageListener("Browser:SelectionStart", this);
michael@0 36 removeMessageListener("Browser:SelectionAttach", this);
michael@0 37 removeMessageListener("Browser:SelectionEnd", this);
michael@0 38 removeMessageListener("Browser:SelectionMoveStart", this);
michael@0 39 removeMessageListener("Browser:SelectionMove", this);
michael@0 40 removeMessageListener("Browser:SelectionMoveEnd", this);
michael@0 41 removeMessageListener("Browser:SelectionUpdate", this);
michael@0 42 removeMessageListener("Browser:SelectionClose", this);
michael@0 43 removeMessageListener("Browser:SelectionCopy", this);
michael@0 44 removeMessageListener("Browser:SelectionDebug", this);
michael@0 45 removeMessageListener("Browser:CaretAttach", this);
michael@0 46 removeMessageListener("Browser:CaretMove", this);
michael@0 47 removeMessageListener("Browser:CaretUpdate", this);
michael@0 48 removeMessageListener("Browser:SelectionSwitchMode", this);
michael@0 49 removeMessageListener("Browser:RepositionInfoRequest", this);
michael@0 50 removeMessageListener("Browser:SelectionHandlerPing", this);
michael@0 51 removeMessageListener("Browser:ResetLastPos", this);
michael@0 52 },
michael@0 53
michael@0 54 sendAsync: function sendAsync(aMsg, aJson) {
michael@0 55 sendAsyncMessage(aMsg, aJson);
michael@0 56 },
michael@0 57
michael@0 58 /*************************************************
michael@0 59 * Browser event handlers
michael@0 60 */
michael@0 61
michael@0 62 /*
michael@0 63 * Selection start event handler
michael@0 64 */
michael@0 65 _onSelectionStart: function _onSelectionStart(aJson) {
michael@0 66 // Init content window information
michael@0 67 if (!this._initTargetInfo(aJson.xPos, aJson.yPos)) {
michael@0 68 this._onFail("failed to get target information");
michael@0 69 return;
michael@0 70 }
michael@0 71
michael@0 72 // for context menu select command, which doesn't trigger
michael@0 73 // form input focus changes.
michael@0 74 if (aJson.setFocus && this._targetIsEditable) {
michael@0 75 this._targetElement.focus();
michael@0 76 }
michael@0 77
michael@0 78 // Clear any existing selection from the document
michael@0 79 let selection = this._contentWindow.getSelection();
michael@0 80 selection.removeAllRanges();
michael@0 81
michael@0 82 // Set our initial selection, aX and aY should be in client coordinates.
michael@0 83 let framePoint = this._clientPointToFramePoint({ xPos: aJson.xPos, yPos: aJson.yPos });
michael@0 84 if (!this._domWinUtils.selectAtPoint(framePoint.xPos, framePoint.yPos,
michael@0 85 Ci.nsIDOMWindowUtils.SELECT_WORDNOSPACE)) {
michael@0 86 this._onFail("failed to set selection at point");
michael@0 87 return;
michael@0 88 }
michael@0 89
michael@0 90 // Update the position of our selection monocles
michael@0 91 this._updateSelectionUI("start", true, true);
michael@0 92 },
michael@0 93
michael@0 94 _onSelectionAttach: function _onSelectionAttach(aX, aY) {
michael@0 95 // Init content window information
michael@0 96 if (!this._initTargetInfo(aX, aY)) {
michael@0 97 this._onFail("failed to get frame offset");
michael@0 98 return;
michael@0 99 }
michael@0 100
michael@0 101 // Update the position of our selection monocles
michael@0 102 this._updateSelectionUI("start", true, true);
michael@0 103 },
michael@0 104
michael@0 105 /*
michael@0 106 * Switch selection modes. Currently we only support switching
michael@0 107 * from "caret" to "selection".
michael@0 108 */
michael@0 109 _onSwitchMode: function _onSwitchMode(aMode, aMarker, aX, aY) {
michael@0 110 if (aMode != "selection") {
michael@0 111 this._onFail("unsupported mode switch");
michael@0 112 return;
michael@0 113 }
michael@0 114
michael@0 115 // Sanity check to be sure we are initialized
michael@0 116 if (!this._targetElement) {
michael@0 117 this._onFail("not initialized");
michael@0 118 return;
michael@0 119 }
michael@0 120
michael@0 121 // Only use selectAtPoint for editable content and avoid that for inputs,
michael@0 122 // as we can expand caret to selection manually more precisely. We can use
michael@0 123 // selectAtPoint for inputs too though, but only once bug 881938 is fully
michael@0 124 // resolved.
michael@0 125 if(Util.isEditableContent(this._targetElement)) {
michael@0 126 // Similar to _onSelectionStart - we need to create initial selection
michael@0 127 // but without the initialization bits.
michael@0 128 let framePoint = this._clientPointToFramePoint({ xPos: aX, yPos: aY });
michael@0 129 if (!this._domWinUtils.selectAtPoint(framePoint.xPos, framePoint.yPos,
michael@0 130 Ci.nsIDOMWindowUtils.SELECT_CHARACTER)) {
michael@0 131 this._onFail("failed to set selection at point");
michael@0 132 return;
michael@0 133 }
michael@0 134 } else if (this._targetElement.selectionStart == 0 || aMarker == "end") {
michael@0 135 // Expand caret forward or backward depending on direction
michael@0 136 this._targetElement.selectionEnd++;
michael@0 137 } else {
michael@0 138 this._targetElement.selectionStart--;
michael@0 139 }
michael@0 140
michael@0 141 // We bail if things get out of sync here implying we missed a message.
michael@0 142 this._selectionMoveActive = true;
michael@0 143
michael@0 144 // Update the position of the selection marker that is *not*
michael@0 145 // being dragged.
michael@0 146 this._updateSelectionUI("update", aMarker == "end", aMarker == "start");
michael@0 147 },
michael@0 148
michael@0 149 /*
michael@0 150 * Selection monocle start move event handler
michael@0 151 */
michael@0 152 _onSelectionMoveStart: function _onSelectionMoveStart(aMsg) {
michael@0 153 if (!this._contentWindow) {
michael@0 154 this._onFail("_onSelectionMoveStart was called without proper view set up");
michael@0 155 return;
michael@0 156 }
michael@0 157
michael@0 158 if (this._selectionMoveActive) {
michael@0 159 this._onFail("mouse is already down on drag start?");
michael@0 160 return;
michael@0 161 }
michael@0 162
michael@0 163 // We bail if things get out of sync here implying we missed a message.
michael@0 164 this._selectionMoveActive = true;
michael@0 165
michael@0 166 if (this._targetIsEditable) {
michael@0 167 // If we're coming out of an out-of-bounds scroll, the node the user is
michael@0 168 // trying to drag may be hidden (the monocle will be pegged to the edge
michael@0 169 // of the edit). Make sure the node the user wants to move is visible
michael@0 170 // and has focus.
michael@0 171 this._updateInputFocus(aMsg.change);
michael@0 172 }
michael@0 173
michael@0 174 // Update the position of our selection monocles
michael@0 175 this._updateSelectionUI("update", true, true);
michael@0 176 },
michael@0 177
michael@0 178 /*
michael@0 179 * Selection monocle move event handler
michael@0 180 */
michael@0 181 _onSelectionMove: function _onSelectionMove(aMsg) {
michael@0 182 if (!this._contentWindow) {
michael@0 183 this._onFail("_onSelectionMove was called without proper view set up");
michael@0 184 return;
michael@0 185 }
michael@0 186
michael@0 187 if (!this._selectionMoveActive) {
michael@0 188 this._onFail("mouse isn't down for drag move?");
michael@0 189 return;
michael@0 190 }
michael@0 191
michael@0 192 this._handleSelectionPoint(aMsg, false);
michael@0 193 },
michael@0 194
michael@0 195 /*
michael@0 196 * Selection monocle move finished event handler
michael@0 197 */
michael@0 198 _onSelectionMoveEnd: function _onSelectionMoveComplete(aMsg) {
michael@0 199 if (!this._contentWindow) {
michael@0 200 this._onFail("_onSelectionMove was called without proper view set up");
michael@0 201 return;
michael@0 202 }
michael@0 203
michael@0 204 if (!this._selectionMoveActive) {
michael@0 205 this._onFail("mouse isn't down for drag move?");
michael@0 206 return;
michael@0 207 }
michael@0 208
michael@0 209 this._handleSelectionPoint(aMsg, true);
michael@0 210 this._selectionMoveActive = false;
michael@0 211
michael@0 212 // _handleSelectionPoint may set a scroll timer, so this must
michael@0 213 // be reset after the last call.
michael@0 214 this._clearTimers();
michael@0 215
michael@0 216 // Update the position of our selection monocles
michael@0 217 this._updateSelectionUI("end", true, true);
michael@0 218 },
michael@0 219
michael@0 220 /*
michael@0 221 * _onCaretAttach - called by SelectionHelperUI when the user taps in a
michael@0 222 * form input. Initializes SelectionHandler, updates the location of the
michael@0 223 * caret, and messages back with current monocle position information.
michael@0 224 *
michael@0 225 * @param aX, aY tap location in client coordinates.
michael@0 226 */
michael@0 227 _onCaretAttach: function _onCaretAttach(aX, aY) {
michael@0 228 // Init content window information
michael@0 229 if (!this._initTargetInfo(aX, aY)) {
michael@0 230 this._onFail("failed to get target information");
michael@0 231 return;
michael@0 232 }
michael@0 233
michael@0 234 // This should never happen, but we check to make sure
michael@0 235 if (!this._targetIsEditable) {
michael@0 236 this._onFail("Coordiates didn't find a text input element.");
michael@0 237 return;
michael@0 238 }
michael@0 239
michael@0 240 // Locate and sanity check the caret position
michael@0 241 let selection = this._getSelection();
michael@0 242 if (!selection || !selection.isCollapsed) {
michael@0 243 this._onFail("No selection or selection is not collapsed.");
michael@0 244 return;
michael@0 245 }
michael@0 246
michael@0 247 // Update the position of our selection monocles
michael@0 248 this._updateSelectionUI("caret", false, false, true);
michael@0 249 },
michael@0 250
michael@0 251 /*
michael@0 252 * Selection copy event handler
michael@0 253 *
michael@0 254 * Check to see if the incoming click was on our selection rect.
michael@0 255 * if it was, copy to the clipboard. Incoming coordinates are
michael@0 256 * content values.
michael@0 257 */
michael@0 258 _onSelectionCopy: function _onSelectionCopy(aMsg) {
michael@0 259 let tap = {
michael@0 260 xPos: aMsg.xPos,
michael@0 261 yPos: aMsg.yPos,
michael@0 262 };
michael@0 263
michael@0 264 let tapInSelection = (tap.xPos > this._cache.selection.left &&
michael@0 265 tap.xPos < this._cache.selection.right) &&
michael@0 266 (tap.yPos > this._cache.selection.top &&
michael@0 267 tap.yPos < this._cache.selection.bottom);
michael@0 268 // Util.dumpLn(tapInSelection,
michael@0 269 // tap.xPos, tap.yPos, "|", this._cache.selection.left,
michael@0 270 // this._cache.selection.right, this._cache.selection.top,
michael@0 271 // this._cache.selection.bottom);
michael@0 272 let success = false;
michael@0 273 let selectedText = this._getSelectedText();
michael@0 274 if (tapInSelection && selectedText.length) {
michael@0 275 let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
michael@0 276 .getService(Ci.nsIClipboardHelper);
michael@0 277 clipboard.copyString(selectedText, this._contentWindow.document);
michael@0 278 success = true;
michael@0 279 }
michael@0 280 sendSyncMessage("Content:SelectionCopied", { succeeded: success });
michael@0 281 },
michael@0 282
michael@0 283 /*
michael@0 284 * Selection close event handler
michael@0 285 *
michael@0 286 * @param aClearSelection requests that selection be cleared.
michael@0 287 */
michael@0 288 _onSelectionClose: function _onSelectionClose(aClearSelection) {
michael@0 289 if (aClearSelection) {
michael@0 290 this._clearSelection();
michael@0 291 }
michael@0 292 this.closeSelection();
michael@0 293 },
michael@0 294
michael@0 295 /*
michael@0 296 * Called any time SelectionHelperUI would like us to
michael@0 297 * recalculate the selection bounds.
michael@0 298 */
michael@0 299 _onSelectionUpdate: function _onSelectionUpdate(aMsg) {
michael@0 300 if (!this._contentWindow) {
michael@0 301 this._onFail("_onSelectionUpdate was called without proper view set up");
michael@0 302 return;
michael@0 303 }
michael@0 304
michael@0 305 if (aMsg && aMsg.isInitiatedByAPZC) {
michael@0 306 let {offset: offset} = Content.getCurrentWindowAndOffset(
michael@0 307 this._targetCoordinates.x, this._targetCoordinates.y);
michael@0 308 this._contentOffset = offset;
michael@0 309 }
michael@0 310
michael@0 311 // Update the position of our selection monocles
michael@0 312 this._updateSelectionUI("update", true, true);
michael@0 313 },
michael@0 314
michael@0 315 /*
michael@0 316 * Called if for any reason we fail during the selection
michael@0 317 * process. Cancels the selection.
michael@0 318 */
michael@0 319 _onFail: function _onFail(aDbgMessage) {
michael@0 320 if (aDbgMessage && aDbgMessage.length > 0)
michael@0 321 Util.dumpLn(aDbgMessage);
michael@0 322 this.sendAsync("Content:SelectionFail");
michael@0 323 this._clearSelection();
michael@0 324 this.closeSelection();
michael@0 325 },
michael@0 326
michael@0 327 /*
michael@0 328 * _repositionInfoRequest - fired at us by ContentAreaObserver when the
michael@0 329 * soft keyboard is being displayed. CAO wants to make a decision about
michael@0 330 * whether the browser deck needs repositioning.
michael@0 331 */
michael@0 332 _repositionInfoRequest: function _repositionInfoRequest(aJsonMsg) {
michael@0 333 let result = this._calcNewContentPosition(aJsonMsg.viewHeight);
michael@0 334
michael@0 335 // no repositioning needed
michael@0 336 if (result == 0) {
michael@0 337 this.sendAsync("Content:RepositionInfoResponse", { reposition: false });
michael@0 338 return;
michael@0 339 }
michael@0 340
michael@0 341 this.sendAsync("Content:RepositionInfoResponse", {
michael@0 342 reposition: true,
michael@0 343 raiseContent: result,
michael@0 344 });
michael@0 345 },
michael@0 346
michael@0 347 _onPing: function _onPing(aId) {
michael@0 348 this.sendAsync("Content:SelectionHandlerPong", { id: aId });
michael@0 349 },
michael@0 350
michael@0 351 onClickCoords: function (xPos, yPos) {
michael@0 352 this.lastXPos = xPos;
michael@0 353 this.lastYPos = yPos;
michael@0 354 },
michael@0 355
michael@0 356 /*************************************************
michael@0 357 * Selection helpers
michael@0 358 */
michael@0 359
michael@0 360 /*
michael@0 361 * _clearSelection
michael@0 362 *
michael@0 363 * Clear existing selection if it exists and reset our internla state.
michael@0 364 */
michael@0 365 _clearSelection: function _clearSelection() {
michael@0 366 this._clearTimers();
michael@0 367 if (this._contentWindow) {
michael@0 368 let selection = this._getSelection();
michael@0 369 if (selection)
michael@0 370 selection.removeAllRanges();
michael@0 371 } else {
michael@0 372 let selection = content.getSelection();
michael@0 373 if (selection)
michael@0 374 selection.removeAllRanges();
michael@0 375 }
michael@0 376 },
michael@0 377
michael@0 378 /*
michael@0 379 * closeSelection
michael@0 380 *
michael@0 381 * Shuts SelectionHandler down.
michael@0 382 */
michael@0 383 closeSelection: function closeSelection() {
michael@0 384 this._clearTimers();
michael@0 385 this._cache = null;
michael@0 386 this._contentWindow = null;
michael@0 387 this._targetElement = null;
michael@0 388 this._selectionMoveActive = false;
michael@0 389 this._contentOffset = null;
michael@0 390 this._domWinUtils = null;
michael@0 391 this._targetIsEditable = false;
michael@0 392 this._targetCoordinates = null;
michael@0 393 sendSyncMessage("Content:HandlerShutdown", {});
michael@0 394 },
michael@0 395
michael@0 396 /*
michael@0 397 * Find content within frames - cache the target nsIDOMWindow,
michael@0 398 * client coordinate offset, target element, and dom utils interface.
michael@0 399 */
michael@0 400 _initTargetInfo: function _initTargetInfo(aX, aY) {
michael@0 401 // getCurrentWindowAndOffset takes client coordinates
michael@0 402 let { element: element,
michael@0 403 contentWindow: contentWindow,
michael@0 404 offset: offset,
michael@0 405 utils: utils } =
michael@0 406 Content.getCurrentWindowAndOffset(aX, aY);
michael@0 407 if (!contentWindow) {
michael@0 408 return false;
michael@0 409 }
michael@0 410 this._targetElement = element;
michael@0 411 this._contentWindow = contentWindow;
michael@0 412 this._contentOffset = offset;
michael@0 413 this._domWinUtils = utils;
michael@0 414 this._targetIsEditable = Util.isEditable(this._targetElement);
michael@0 415 this._targetCoordinates = {
michael@0 416 x: aX,
michael@0 417 y: aY
michael@0 418 };
michael@0 419
michael@0 420 return true;
michael@0 421 },
michael@0 422
michael@0 423 /*
michael@0 424 * _calcNewContentPosition - calculates the distance the browser should be
michael@0 425 * raised to move the focused form input out of the way of the soft
michael@0 426 * keyboard.
michael@0 427 *
michael@0 428 * @param aNewViewHeight the new content view height
michael@0 429 * @return 0 if no positioning is required or a positive val equal to the
michael@0 430 * distance content should be raised to center the target element.
michael@0 431 */
michael@0 432 _calcNewContentPosition: function _calcNewContentPosition(aNewViewHeight) {
michael@0 433 // We have no target element but the keyboard is up
michael@0 434 // so lets not cover content that is below the keyboard
michael@0 435 if (!this._cache || !this._cache.element) {
michael@0 436 if (this.lastYPos != null && this.lastYPos > aNewViewHeight) {
michael@0 437 return Services.metro.keyboardHeight;
michael@0 438 }
michael@0 439 return 0;
michael@0 440 }
michael@0 441
michael@0 442 let position = Util.centerElementInView(aNewViewHeight, this._cache.element);
michael@0 443 if (position !== undefined) {
michael@0 444 return position;
michael@0 445 }
michael@0 446
michael@0 447 // Special case: we are dealing with an input that is taller than the
michael@0 448 // desired height of content. We need to center on the caret location.
michael@0 449 let rect =
michael@0 450 this._domWinUtils.sendQueryContentEvent(
michael@0 451 this._domWinUtils.QUERY_CARET_RECT,
michael@0 452 this._targetElement.selectionEnd,
michael@0 453 0, 0, 0,
michael@0 454 this._domWinUtils.QUERY_CONTENT_FLAG_USE_XP_LINE_BREAK);
michael@0 455 if (!rect || !rect.succeeded) {
michael@0 456 Util.dumpLn("no caret was present, unexpected.");
michael@0 457 return 0;
michael@0 458 }
michael@0 459
michael@0 460 // Note sendQueryContentEvent with QUERY_CARET_RECT is really buggy. If it
michael@0 461 // can't find the exact location of the caret position it will "guess".
michael@0 462 // Sometimes this can put the result in unexpected locations.
michael@0 463 let caretLocation = Math.max(Math.min(Math.round(rect.top + (rect.height * .5)),
michael@0 464 viewBottom), 0);
michael@0 465
michael@0 466 // Caret is above the bottom of the new view bounds, no need to shift.
michael@0 467 if (caretLocation <= aNewViewHeight) {
michael@0 468 return 0;
michael@0 469 }
michael@0 470
michael@0 471 // distance from the top of the keyboard down to the caret location
michael@0 472 return caretLocation - aNewViewHeight;
michael@0 473 },
michael@0 474
michael@0 475 /*************************************************
michael@0 476 * Events
michael@0 477 */
michael@0 478
michael@0 479 /*
michael@0 480 * Scroll + selection advancement timer when the monocle is
michael@0 481 * outside the bounds of an input control.
michael@0 482 */
michael@0 483 scrollTimerCallback: function scrollTimerCallback() {
michael@0 484 let result = SelectionHandler.updateTextEditSelection();
michael@0 485 // Update monocle position and speed if we've dragged off to one side
michael@0 486 if (result.trigger) {
michael@0 487 SelectionHandler._updateSelectionUI("update", result.start, result.end);
michael@0 488 }
michael@0 489 },
michael@0 490
michael@0 491 receiveMessage: function sh_receiveMessage(aMessage) {
michael@0 492 if (this._debugEvents && aMessage.name != "Browser:SelectionMove") {
michael@0 493 Util.dumpLn("SelectionHandler:", aMessage.name);
michael@0 494 }
michael@0 495 let json = aMessage.json;
michael@0 496 switch (aMessage.name) {
michael@0 497 case "Browser:SelectionStart":
michael@0 498 this._onSelectionStart(json);
michael@0 499 break;
michael@0 500
michael@0 501 case "Browser:SelectionAttach":
michael@0 502 this._onSelectionAttach(json.xPos, json.yPos);
michael@0 503 break;
michael@0 504
michael@0 505 case "Browser:CaretAttach":
michael@0 506 this._onCaretAttach(json.xPos, json.yPos);
michael@0 507 break;
michael@0 508
michael@0 509 case "Browser:CaretMove":
michael@0 510 this._onCaretMove(json.caret.xPos, json.caret.yPos);
michael@0 511 break;
michael@0 512
michael@0 513 case "Browser:CaretUpdate":
michael@0 514 this._onCaretPositionUpdate(json.caret.xPos, json.caret.yPos);
michael@0 515 break;
michael@0 516
michael@0 517 case "Browser:SelectionSwitchMode":
michael@0 518 this._onSwitchMode(json.newMode, json.change, json.xPos, json.yPos);
michael@0 519 break;
michael@0 520
michael@0 521 case "Browser:SelectionClose":
michael@0 522 this._onSelectionClose(json.clearSelection);
michael@0 523 break;
michael@0 524
michael@0 525 case "Browser:SelectionMoveStart":
michael@0 526 this._onSelectionMoveStart(json);
michael@0 527 break;
michael@0 528
michael@0 529 case "Browser:SelectionMove":
michael@0 530 this._onSelectionMove(json);
michael@0 531 break;
michael@0 532
michael@0 533 case "Browser:SelectionMoveEnd":
michael@0 534 this._onSelectionMoveEnd(json);
michael@0 535 break;
michael@0 536
michael@0 537 case "Browser:SelectionCopy":
michael@0 538 this._onSelectionCopy(json);
michael@0 539 break;
michael@0 540
michael@0 541 case "Browser:SelectionDebug":
michael@0 542 this._onSelectionDebug(json);
michael@0 543 break;
michael@0 544
michael@0 545 case "Browser:SelectionUpdate":
michael@0 546 this._onSelectionUpdate(json);
michael@0 547 break;
michael@0 548
michael@0 549 case "Browser:RepositionInfoRequest":
michael@0 550 // This message is sent simultaneously with a tap event.
michael@0 551 // Wait a bit to make sure we have the most up-to-date tap co-ordinates
michael@0 552 // before a call to _calcNewContentPosition() which accesses them.
michael@0 553 content.setTimeout (function () {
michael@0 554 SelectionHandler._repositionInfoRequest(json);
michael@0 555 }, 50);
michael@0 556 break;
michael@0 557
michael@0 558 case "Browser:SelectionHandlerPing":
michael@0 559 this._onPing(json.id);
michael@0 560 break;
michael@0 561
michael@0 562 case "Browser:ResetLastPos":
michael@0 563 this.onClickCoords(json.xPos, json.yPos);
michael@0 564 break;
michael@0 565 }
michael@0 566 },
michael@0 567
michael@0 568 /*************************************************
michael@0 569 * Utilities
michael@0 570 */
michael@0 571
michael@0 572 _getDocShell: function _getDocShell(aWindow) {
michael@0 573 if (aWindow == null)
michael@0 574 return null;
michael@0 575 return aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 576 .getInterface(Ci.nsIWebNavigation)
michael@0 577 .QueryInterface(Ci.nsIDocShell);
michael@0 578 },
michael@0 579
michael@0 580 _getSelectedText: function _getSelectedText() {
michael@0 581 let selection = this._getSelection();
michael@0 582 if (selection)
michael@0 583 return selection.toString();
michael@0 584 return "";
michael@0 585 },
michael@0 586
michael@0 587 _getSelection: function _getSelection() {
michael@0 588 if (this._targetElement instanceof Ci.nsIDOMNSEditableElement) {
michael@0 589 return this._targetElement
michael@0 590 .QueryInterface(Ci.nsIDOMNSEditableElement)
michael@0 591 .editor.selection;
michael@0 592 } else if (this._contentWindow)
michael@0 593 return this._contentWindow.getSelection();
michael@0 594 return null;
michael@0 595 },
michael@0 596
michael@0 597 _getSelectController: function _getSelectController() {
michael@0 598 if (this._targetElement instanceof Ci.nsIDOMNSEditableElement) {
michael@0 599 return this._targetElement
michael@0 600 .QueryInterface(Ci.nsIDOMNSEditableElement)
michael@0 601 .editor.selectionController;
michael@0 602 } else {
michael@0 603 let docShell = this._getDocShell(this._contentWindow);
michael@0 604 if (docShell == null)
michael@0 605 return null;
michael@0 606 return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 607 .getInterface(Ci.nsISelectionDisplay)
michael@0 608 .QueryInterface(Ci.nsISelectionController);
michael@0 609 }
michael@0 610 },
michael@0 611 };
michael@0 612 this.SelectionHandler = SelectionHandler;
michael@0 613
michael@0 614 SelectionHandler.__proto__ = new SelectionPrototype();
michael@0 615 SelectionHandler.init();
michael@0 616

mercurial