Bug 1077515 - part 6 - Create a new nsISelectionController::PhysicalMove command. r=roc
authorJonathan Kew <jkew@mozilla.com>
Sat, 22 Nov 2014 14:39:03 +0000
changeset 241314 b45361b7aab2976453598b289f355f2dd7ac25f4
parent 241313 c873b373dc1d5793a21a295c57738982d077c773
child 241315 e8d6b1cf145c6ccc3badd355f2ccebb76cb96bba
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1077515
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1077515 - part 6 - Create a new nsISelectionController::PhysicalMove command. r=roc
dom/base/nsISelectionController.idl
dom/html/nsTextEditorState.cpp
layout/base/nsPresShell.cpp
layout/base/nsPresShell.h
layout/generic/nsFrameSelection.h
layout/generic/nsSelection.cpp
--- a/dom/base/nsISelectionController.idl
+++ b/dom/base/nsISelectionController.idl
@@ -11,17 +11,17 @@ typedef short SelectionType;
 typedef short SelectionRegion;
 %}
 
 interface nsIContent;
 interface nsIDOMNode;
 interface nsISelection;
 interface nsISelectionDisplay;
 
-[scriptable, uuid(b1ff7faa-8097-431d-b7f1-b0615e3cd596)]
+[scriptable, uuid(7835DE46-DB36-4BB7-8684-1049A0C13049)]
 interface nsISelectionController : nsISelectionDisplay
 {
    const short SELECTION_NONE=0;
    const short SELECTION_NORMAL=1;
    const short SELECTION_SPELLCHECK=2;
    const short SELECTION_IME_RAWINPUT=4;
    const short SELECTION_IME_SELECTEDRAWTEXT=8;
    const short SELECTION_IME_CONVERTEDTEXT=16;
@@ -138,16 +138,36 @@ interface nsISelectionController : nsISe
    *  this will also have the effect of collapsing the selection if the aExtend = PR_FALSE
    *  the "point" of selection that is extended is considered the "focus" point. 
    *  or the last point adjusted by the selection.
    *  @param aForward forward or backward if PR_FALSE
    *  @param aExtend  should it collapse the selection of extend it?
    */
     void characterMove(in boolean forward, in boolean extend);
 
+   /** PhysicalMove will move the selection one "unit" in a given direction
+   *  within the document.
+   *  this will also have the effect of collapsing the selection if the aExtend = PR_FALSE
+   *  the "point" of selection that is extended is considered the "focus" point. 
+   *  or the last point adjusted by the selection.
+   *  @param aDirection 
+   *  @param aAmount    character/line; word/lineBoundary
+   *  @param aExtend    should it collapse the selection of extend it?
+   */
+    void physicalMove(in short direction, in short amount, in boolean extend);
+
+   /**
+    * nsFrameSelection::PhysicalMove depends on the ordering of these values;
+    * do not change without checking there!
+    */
+   const short MOVE_LEFT = 0;
+   const short MOVE_RIGHT = 1;
+   const short MOVE_UP = 2;
+   const short MOVE_DOWN = 3;
+
    /**
     * CharacterExtendForDelete will extend the selection one character cell
     * forward in the document.
     * this method is used internally for handling del key.
     */
     [noscript] void characterExtendForDelete();
 
    /**
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -222,16 +222,17 @@ public:
   NS_IMETHOD ScrollSelectionIntoView(int16_t aType, int16_t aRegion, int16_t aFlags);
   NS_IMETHOD RepaintSelection(int16_t type);
   NS_IMETHOD RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType);
   NS_IMETHOD SetCaretEnabled(bool enabled);
   NS_IMETHOD SetCaretReadOnly(bool aReadOnly);
   NS_IMETHOD GetCaretEnabled(bool *_retval);
   NS_IMETHOD GetCaretVisible(bool *_retval);
   NS_IMETHOD SetCaretVisibilityDuringSelection(bool aVisibility);
+  NS_IMETHOD PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend) MOZ_OVERRIDE;
   NS_IMETHOD CharacterMove(bool aForward, bool aExtend);
   NS_IMETHOD CharacterExtendForDelete();
   NS_IMETHOD CharacterExtendForBackspace();
   NS_IMETHOD WordMove(bool aForward, bool aExtend);
   NS_IMETHOD WordExtendForDelete(bool aForward);
   NS_IMETHOD LineMove(bool aForward, bool aExtend);
   NS_IMETHOD IntraLineMove(bool aForward, bool aExtend);
   NS_IMETHOD PageMove(bool aForward, bool aExtend);
@@ -441,16 +442,25 @@ nsTextInputSelectionImpl::SetCaretVisibi
         caret->SetVisibilityDuringSelection(aVisibility);
       return NS_OK;
     }
   }
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
+nsTextInputSelectionImpl::PhysicalMove(int16_t aDirection, int16_t aAmount,
+                                       bool aExtend)
+{
+  if (mFrameSelection)
+    return mFrameSelection->PhysicalMove(aDirection, aAmount, aExtend);
+  return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP
 nsTextInputSelectionImpl::CharacterMove(bool aForward, bool aExtend)
 {
   if (mFrameSelection)
     return mFrameSelection->CharacterMove(aForward, aExtend);
   return NS_ERROR_NULL_POINTER;
 }
 
 NS_IMETHODIMP
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -2299,16 +2299,22 @@ NS_IMETHODIMP PresShell::GetSelectionFla
     return NS_ERROR_INVALID_ARG;
   *aOutEnable = mSelectionFlags;
   return NS_OK;
 }
 
 //implementation of nsISelectionController
 
 NS_IMETHODIMP
+PresShell::PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend)
+{
+  return mSelection->PhysicalMove(aDirection, aAmount, aExtend);
+}
+
+NS_IMETHODIMP
 PresShell::CharacterMove(bool aForward, bool aExtend)
 {
   return mSelection->CharacterMove(aForward, aExtend);
 }
 
 NS_IMETHODIMP
 PresShell::CharacterExtendForDelete()
 {
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -244,16 +244,17 @@ public:
   virtual void SetCaret(nsCaret *aNewCaret) MOZ_OVERRIDE;
   virtual void RestoreCaret() MOZ_OVERRIDE;
 
   NS_IMETHOD SetSelectionFlags(int16_t aInEnable) MOZ_OVERRIDE;
   NS_IMETHOD GetSelectionFlags(int16_t *aOutEnable) MOZ_OVERRIDE;
 
   // nsISelectionController
 
+  NS_IMETHOD PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend) MOZ_OVERRIDE;
   NS_IMETHOD CharacterMove(bool aForward, bool aExtend) MOZ_OVERRIDE;
   NS_IMETHOD CharacterExtendForDelete() MOZ_OVERRIDE;
   NS_IMETHOD CharacterExtendForBackspace() MOZ_OVERRIDE;
   NS_IMETHOD WordMove(bool aForward, bool aExtend) MOZ_OVERRIDE;
   NS_IMETHOD WordExtendForDelete(bool aForward) MOZ_OVERRIDE;
   NS_IMETHOD LineMove(bool aForward, bool aExtend) MOZ_OVERRIDE;
   NS_IMETHOD IntraLineMove(bool aForward, bool aExtend) MOZ_OVERRIDE;
   NS_IMETHOD PageMove(bool aForward, bool aExtend) MOZ_OVERRIDE;
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -410,16 +410,26 @@ public:
    *  This method is virtual since it gets called from outside of layout.
    */
   virtual nsBidiLevel GetCaretBidiLevel() const;
   /** UndefineCaretBidiLevel sets the caret bidi level to "undefined"
    *  This method is virtual since it gets called from outside of layout.
    */
   virtual void UndefineCaretBidiLevel();
 
+  /** PhysicalMove will generally be called from the nsiselectioncontroller implementations.
+   *  the effect being the selection will move one unit 'aAmount' in the
+   *  given aDirection.
+   * @param aDirection  the direction to move the selection
+   * @param aAmount     amount of movement (char/line; word/page; eol/doc)
+   * @param aExtend     continue selection
+   */
+  /*unsafe*/
+  nsresult PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend);
+
   /** CharacterMove will generally be called from the nsiselectioncontroller implementations.
    *  the effect being the selection will move one character left or right.
    * @param aForward move forward in document.
    * @param aExtend continue selection
    */
   /*unsafe*/
   nsresult CharacterMove(bool aForward, bool aExtend);
 
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -1893,16 +1893,106 @@ nsFrameSelection::CommonPageMove(bool aF
                              nsIScrollableFrame::SMOOTH);
 
   // place the caret
   HandleClick(offsets.content, offsets.offset,
               offsets.offset, aExtend, false, CARET_ASSOCIATE_AFTER);
 }
 
 nsresult
+nsFrameSelection::PhysicalMove(int16_t aDirection, int16_t aAmount,
+                               bool aExtend)
+{
+  NS_ENSURE_STATE(mShell);
+  // Flush out layout, since we need it to be up to date to do caret
+  // positioning.
+  mShell->FlushPendingNotifications(Flush_Layout);
+
+  if (!mShell) {
+    return NS_OK;
+  }
+
+  // Check that parameters are safe
+  if (aDirection < 0 || aDirection > 3 || aAmount < 0 || aAmount > 1) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsPresContext *context = mShell->GetPresContext();
+  if (!context) {
+    return NS_ERROR_FAILURE;
+  }
+
+  int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+  nsRefPtr<Selection> sel = mDomSelections[index];
+  if (!sel) {
+    return NS_ERROR_NULL_POINTER;
+  }
+
+  // Map the abstract movement amounts (0-1) to direction-specific
+  // selection units.
+  static const nsSelectionAmount inlineAmount[] =
+    { eSelectCluster, eSelectWord };
+  static const nsSelectionAmount blockPrevAmount[] =
+    { eSelectLine, eSelectBeginLine };
+  static const nsSelectionAmount blockNextAmount[] =
+    { eSelectLine, eSelectEndLine };
+
+  struct PhysicalToLogicalMapping {
+    nsDirection direction;
+    const nsSelectionAmount *amounts;
+  };
+  static const PhysicalToLogicalMapping verticalLR[4] = {
+    { eDirPrevious, blockPrevAmount },  // left
+    { eDirNext, blockNextAmount },      // right
+    { eDirPrevious, inlineAmount }, // up
+    { eDirNext, inlineAmount }      // down
+  };
+  static const PhysicalToLogicalMapping verticalRL[4] = {
+    { eDirNext, blockNextAmount },
+    { eDirPrevious, blockPrevAmount },
+    { eDirPrevious, inlineAmount },
+    { eDirNext, inlineAmount }
+  };
+  static const PhysicalToLogicalMapping horizontal[4] = {
+    { eDirPrevious, inlineAmount },
+    { eDirNext, inlineAmount },
+    { eDirPrevious, blockPrevAmount },
+    { eDirNext, blockNextAmount }
+  };
+
+  WritingMode wm;
+  nsIFrame *frame = nullptr;
+  int32_t offsetused = 0;
+  if (NS_SUCCEEDED(sel->GetPrimaryFrameForFocusNode(&frame, &offsetused,
+                                                    true))) {
+    if (frame) {
+      wm = frame->GetWritingMode();
+    }
+  }
+
+  const PhysicalToLogicalMapping& mapping =
+    wm.IsVertical()
+      ? wm.IsVerticalLR() ? verticalLR[aDirection] : verticalRL[aDirection]
+      : horizontal[aDirection];
+
+  nsresult rv = MoveCaret(mapping.direction, aExtend, mapping.amounts[aAmount],
+                          eVisual);
+  if (NS_FAILED(rv)) {
+    // If we tried to do a line move, but couldn't move in the given direction,
+    // then we'll "promote" this to a line-edge move instead.
+    if (mapping.amounts[aAmount] == eSelectLine) {
+      rv = MoveCaret(mapping.direction, aExtend, mapping.amounts[aAmount + 1],
+                     eVisual);
+    }
+  }
+
+  return rv;
+}
+
+nsresult
 nsFrameSelection::CharacterMove(bool aForward, bool aExtend)
 {
   return MoveCaret(aForward ? eDirNext : eDirPrevious, aExtend, eSelectCluster,
                    eUsePrefStyle);
 }
 
 nsresult
 nsFrameSelection::CharacterExtendForDelete()