Bug 1164693 - Part 1: Directional caret should point in caret direction in bidi paragraphs. r=smontagu
authorTed Clancy <tclancy@mozilla.com>
Fri, 05 Jun 2015 21:44:54 -0400
changeset 268128 b355b20f61235e6a06cacca56161558ef8cf4390
parent 268127 ad46ffd4c50094b705573d37c01034504ef731de
child 268129 2a383ebb99d5a76b2fdd13b4248368feec57e639
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-esr52@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmontagu
bugs1164693
milestone41.0a1
Bug 1164693 - Part 1: Directional caret should point in caret direction in bidi paragraphs. r=smontagu
layout/base/nsCaret.cpp
layout/base/nsCaret.h
layout/base/tests/bug646382-1-ref.html
layout/base/tests/bug646382-2-ref.html
layout/generic/nsFrameSelection.h
layout/generic/nsSelection.cpp
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -110,19 +110,24 @@ AdjustCaretFrameForLineEnd(nsIFrame** aF
       NS_ASSERTION(r->GetType() == nsGkAtoms::textFrame, "Expected text frame");
       *aOffset = (static_cast<nsTextFrame*>(r))->GetContentEnd();
       return;
     }
   }
 }
 
 static bool
-IsBidiUI()
+IsKeyboardRTL()
 {
-  return Preferences::GetBool("bidi.browser.ui");
+  bool isKeyboardRTL = false;
+  nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
+  if (bidiKeyboard) {
+    bidiKeyboard->IsLangRTL(&isKeyboardRTL);
+  }
+  return isKeyboardRTL;
 }
 
 nsCaret::nsCaret()
 : mOverrideOffset(0)
 , mIsBlinkOn(false)
 , mBlinkCount(-1)
 , mVisible(false)
 , mReadOnly(false)
@@ -473,28 +478,40 @@ nsCaret::SetCaretPosition(nsIDOMNode* aN
 {
   mOverrideContent = do_QueryInterface(aNode);
   mOverrideOffset = aOffset;
 
   ResetBlinking();
   SchedulePaint();
 }
 
+bool
+nsCaret::IsBidiUI()
+{
+  nsIFrame* frame = nullptr;
+
+  if(Selection* selection = GetSelectionInternal()) {
+    int32_t contentOffset;
+    frame = GetFrameAndOffset(selection, mOverrideContent, mOverrideOffset,
+                              &contentOffset);
+  }
+
+  return (frame && frame->GetStateBits() & NS_FRAME_IS_BIDI) ||
+         Preferences::GetBool("bidi.browser.ui");
+}
+
 void
 nsCaret::CheckSelectionLanguageChange()
 {
   if (!IsBidiUI()) {
     return;
   }
 
-  bool isKeyboardRTL = false;
-  nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
-  if (bidiKeyboard) {
-    bidiKeyboard->IsLangRTL(&isKeyboardRTL);
-  }
+  bool isKeyboardRTL = IsKeyboardRTL();
+
   // Call SelectionLanguageChange on every paint. Mostly it will be a noop
   // but it should be fast anyway. This guarantees we never paint the caret
   // at the wrong place.
   Selection* selection = GetSelectionInternal();
   if (selection) {
     selection->SelectionLanguageChange(isKeyboardRTL);
   }
 }
@@ -673,18 +690,19 @@ nsCaret::GetCaretFrameForNodeOffset(nsFr
   //
   // Direction Style from visibility->mDirection
   // ------------------
   // NS_STYLE_DIRECTION_LTR : LTR or Default
   // NS_STYLE_DIRECTION_RTL
   if (theFrame->PresContext()->BidiEnabled())
   {
     // If there has been a reflow, take the caret Bidi level to be the level of the current frame
-    if (aBidiLevel & BIDI_LEVEL_UNDEFINED)
+    if (aBidiLevel & BIDI_LEVEL_UNDEFINED) {
       aBidiLevel = NS_GET_EMBEDDING_LEVEL(theFrame);
+    }
 
     int32_t start;
     int32_t end;
     nsIFrame* frameBefore;
     nsIFrame* frameAfter;
     nsBidiLevel levelBefore; // Bidi level of the character before the caret
     nsBidiLevel levelAfter;  // Bidi level of the character after the caret
 
@@ -897,31 +915,32 @@ nsCaret::ComputeCaretRects(nsIFrame* aFr
   if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
     if (isVertical) {
       aCaretRect->y -= aCaretRect->height;
     } else {
       aCaretRect->x -= aCaretRect->width;
     }
   }
 
-  // Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction
   aHookRect->SetEmpty();
-  if (!IsBidiUI()) {
+
+  Selection* selection = GetSelectionInternal();
+  if (!selection || !selection->GetFrameSelection()) {
     return;
   }
 
-  bool isCaretRTL;
-  nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
-  // if bidiKeyboard->IsLangRTL() fails, there is no way to tell the
-  // keyboard direction, or the user has no right-to-left keyboard
-  // installed, so we never draw the hook.
-  if (bidiKeyboard && NS_SUCCEEDED(bidiKeyboard->IsLangRTL(&isCaretRTL))) {
-    // If keyboard language is RTL, draw the hook on the left; if LTR, to the right
+  if (IsBidiUI() || IsKeyboardRTL()) {
+    // If caret level is RTL, draw the hook on the left; if LTR, to the right
     // The height of the hook rectangle is the same as the width of the caret
     // rectangle.
+    int caretBidiLevel = selection->GetFrameSelection()->GetCaretBidiLevel();
+    if (caretBidiLevel & BIDI_LEVEL_UNDEFINED) {
+      caretBidiLevel = NS_GET_EMBEDDING_LEVEL(aFrame);
+    }
+    bool isCaretRTL = caretBidiLevel % 2;
     if (isVertical) {
       aHookRect->SetRect(aCaretRect->XMost() - bidiIndicatorSize,
                          aCaretRect->y + (isCaretRTL ? bidiIndicatorSize * -1 :
                                                        aCaretRect->height),
                          aCaretRect->height,
                          bidiIndicatorSize);
     } else {
       aHookRect->SetRect(aCaretRect->x + (isCaretRTL ? bidiIndicatorSize * -1 :
--- a/layout/base/nsCaret.h
+++ b/layout/base/nsCaret.h
@@ -153,16 +153,17 @@ class nsCaret final : public nsISelectio
                                       int32_t   aFrameOffset,
                                       nscoord*  aBidiIndicatorSize);
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 protected:
     static void   CaretBlinkCallback(nsITimer *aTimer, void *aClosure);
 
+    bool          IsBidiUI();
     void          CheckSelectionLanguageChange();
 
     void          ResetBlinking();
     void          StopBlinking();
 
     mozilla::dom::Selection* GetSelectionInternal();
 
     struct Metrics {
--- a/layout/base/tests/bug646382-1-ref.html
+++ b/layout/base/tests/bug646382-1-ref.html
@@ -1,17 +1,18 @@
 <html class="reftest-wait">
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   </head>
   <body onload="start()">
-    <textarea onfocus="done()" style="-moz-appearance: none">س</textarea>
+    <textarea onfocus="done()" style="-moz-appearance: none">س&lrm;</textarea>
     <script>
       var textarea = document.querySelector("textarea");
       function start() {
         textarea.focus();
       }
       function done() {
+        textarea.selectionStart = textarea.selectionEnd = 2;
         document.documentElement.removeAttribute("class");
       }
     </script>
   </body>
 </html>
--- a/layout/base/tests/bug646382-2-ref.html
+++ b/layout/base/tests/bug646382-2-ref.html
@@ -1,14 +1,15 @@
 <html class="reftest-wait">
   <body onload="start()">
-    <textarea dir="rtl" onfocus="done()" style="-moz-appearance: none">s</textarea>
+    <textarea dir="rtl" onfocus="done()" style="-moz-appearance: none">s&rlm;</textarea>
     <script>
       var textarea = document.querySelector("textarea");
       function start() {
         textarea.focus();
       }
       function done() {
+        textarea.selectionStart = textarea.selectionEnd = 2;
         document.documentElement.removeAttribute("class");
       }
     </script>
   </body>
 </html>
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -616,17 +616,19 @@ private:
                      bool aContinueSelection,
                      bool aMultipleSelection);
 
   void BidiLevelFromMove(nsIPresShell* aPresShell,
                          nsIContent *aNode,
                          uint32_t aContentOffset,
                          nsSelectionAmount aAmount,
                          CaretAssociateHint aHint);
-  void BidiLevelFromClick(nsIContent *aNewFocus, uint32_t aContentOffset);
+  void BidiLevelFromClick(nsIContent *aNewFocus,
+                          uint32_t aContentOffset,
+                          CaretAssociateHint aHint);
   nsPrevNextBidiLevels GetPrevNextBidiLevels(nsIContent *aNode,
                                              uint32_t aContentOffset,
                                              CaretAssociateHint aHint,
                                              bool aJumpLines) const;
 
   bool AdjustForMaintainedSelection(nsIContent *aContent, int32_t aOffset);
 
 // post and pop reasons for notifications. we may stack these later
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -1426,22 +1426,23 @@ void nsFrameSelection::BidiLevelFromMove
 
 /**
  * BidiLevelFromClick is called when the caret is repositioned by clicking the mouse
  *
  * @param aNode is the content node
  * @param aContentOffset is the new caret position, as an offset into aNode
  */
 void nsFrameSelection::BidiLevelFromClick(nsIContent *aNode,
-                                          uint32_t    aContentOffset)
+                                          uint32_t    aContentOffset,
+                                          CaretAssociateHint aHint)
 {
   nsIFrame* clickInFrame=nullptr;
   int32_t OffsetNotUsed;
 
-  clickInFrame = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &OffsetNotUsed);
+  clickInFrame = GetFrameForNodeOffset(aNode, aContentOffset, aHint, &OffsetNotUsed);
   if (!clickInFrame)
     return;
 
   SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(clickInFrame));
 }
 
 
 bool
@@ -1510,17 +1511,17 @@ nsFrameSelection::HandleClick(nsIContent
     if (!IsValidSelectionPoint(this, aNewFocus)) {
       mAncestorLimiter = nullptr;
     }
   }
 
   // Don't take focus when dragging off of a table
   if (!mDragSelectingCells)
   {
-    BidiLevelFromClick(aNewFocus, aContentOffset);
+    BidiLevelFromClick(aNewFocus, aContentOffset, aHint);
     PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
     if (aContinueSelection &&
         AdjustForMaintainedSelection(aNewFocus, aContentOffset))
       return NS_OK; //shift clicked to maintained selection. rejected.
 
     int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
     AutoPrepareFocusRange prep(mDomSelections[index], aContinueSelection, aMultipleSelection);
     return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aHint,