Bug 554822 Caret should refer the actual text color instead of the value of CSS color property r=roc
authorMasayuki Nakano <masayuki@d-toybox.com>
Thu, 01 Apr 2010 11:35:48 +0900
changeset 40078 c746dd4dde4c38220763bb2a8aca2853602e809b
parent 40077 6e3f0fb61c62edf9da3bf0dbe38f46d1218e76f9
child 40079 3431c878b52bfd8c5cda81faa487f65a8f8f52ff
push id12542
push usermasayuki@d-toybox.com
push dateThu, 01 Apr 2010 02:36:19 +0000
treeherdermozilla-central@c746dd4dde4c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs554822
milestone1.9.3a4pre
Bug 554822 Caret should refer the actual text color instead of the value of CSS color property r=roc
layout/base/nsCaret.cpp
layout/base/nsCaret.h
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/generic/nsTextFrame.h
layout/generic/nsTextFrameThebes.cpp
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -492,32 +492,35 @@ nsresult nsCaret::DrawAtPosition(nsIDOMN
   nsresult rv = DrawAtPositionWithHint(aNode, aOffset,
                                        nsFrameSelection::HINTLEFT,
                                        bidiLevel, PR_TRUE)
     ?  NS_OK : NS_ERROR_FAILURE;
   ToggleDrawnStatus();
   return rv;
 }
 
-nsIFrame * nsCaret::GetCaretFrame()
+nsIFrame * nsCaret::GetCaretFrame(PRInt32 *aOffset)
 {
   // Return null if we're not drawn to prevent anybody from trying to draw us.
   if (!mDrawn)
     return nsnull;
 
   // Recompute the frame that we're supposed to draw in to guarantee that
   // we're not going to try to draw into a stale (dead) frame.
-  PRInt32 unused;
+  PRInt32 offset;
   nsIFrame *frame = nsnull;
   nsresult rv = GetCaretFrameForNodeOffset(mLastContent, mLastContentOffset,
                                            mLastHint, mLastBidiLevel, &frame,
-                                           &unused);
+                                           &offset);
   if (NS_FAILED(rv))
     return nsnull;
 
+  if (aOffset) {
+    *aOffset = offset;
+  }
   return frame;
 }
 
 void nsCaret::InvalidateOutsideCaret()
 {
   nsIFrame *frame = GetCaretFrame();
 
   // Only invalidate if we are not fully contained by our frame's rect.
@@ -540,39 +543,42 @@ void nsCaret::UpdateCaretPosition()
 void nsCaret::PaintCaret(nsDisplayListBuilder *aBuilder,
                          nsIRenderingContext *aCtx,
                          nsIFrame* aForFrame,
                          const nsPoint &aOffset)
 {
   NS_ASSERTION(mDrawn, "The caret shouldn't be drawing");
 
   const nsRect drawCaretRect = mCaretRect + aOffset;
-  nscolor cssColor = aForFrame->GetStyleColor()->mColor;
+  PRInt32 contentOffset;
+  nsIFrame* frame = GetCaretFrame(&contentOffset);
+  NS_ASSERTION(frame == aForFrame, "We're referring different frame");
+  nscolor foregroundColor = aForFrame->GetCaretColorAt(contentOffset);
 
   // Only draw the native caret if the foreground color matches that of
   // -moz-fieldtext (the color of the text in a textbox). If it doesn't match
   // we are likely in contenteditable or a custom widget and we risk being hard to see
   // against the background. In that case, fall back to the CSS color.
   nsPresContext* presContext = aForFrame->PresContext();
 
   if (GetHookRect().IsEmpty() && presContext) {
     nsITheme *theme = presContext->GetTheme();
     if (theme && theme->ThemeSupportsWidget(presContext, aForFrame, NS_THEME_TEXTFIELD_CARET)) {
       nsILookAndFeel* lookAndFeel = presContext->LookAndFeel();
       nscolor fieldText;
       if (NS_SUCCEEDED(lookAndFeel->GetColor(nsILookAndFeel::eColor__moz_fieldtext, fieldText)) &&
-          fieldText == cssColor) {
+          fieldText == foregroundColor) {
         theme->DrawWidgetBackground(aCtx, aForFrame, NS_THEME_TEXTFIELD_CARET,
                                     drawCaretRect, drawCaretRect);
         return;
       }
     }
   }
 
-  aCtx->SetColor(cssColor);
+  aCtx->SetColor(foregroundColor);
   aCtx->FillRect(drawCaretRect);
   if (!GetHookRect().IsEmpty())
     aCtx->FillRect(GetHookRect() + aOffset);
 }
 
 
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP nsCaret::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aDomSel, PRInt16 aReason)
--- a/layout/base/nsCaret.h
+++ b/layout/base/nsCaret.h
@@ -131,18 +131,20 @@ class nsCaret : public nsISelectionListe
      *  Note: This call breaks the caret's ability to blink at all.
      **/
     nsresult    DrawAtPosition(nsIDOMNode* aNode, PRInt32 aOffset);
 
     /** GetCaretFrame
      *  Get the current frame that the caret should be drawn in. If the caret is
      *  not currently visible (i.e., it is between blinks), then this will
      *  return null.
+     *
+     *  @param aOffset is result of the caret offset in the content.
      */
-    nsIFrame*     GetCaretFrame();
+    nsIFrame*     GetCaretFrame(PRInt32 *aOffset = nsnull);
 
     /** GetCaretRect
      *  Get the current caret rect. Only call this when GetCaretFrame returns
      *  non-null.
      */
     nsRect        GetCaretRect()
     {
       nsRect r;
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -968,16 +968,23 @@ nsIFrame::DisplayCaret(nsDisplayListBuil
 {
   if (!IsVisibleForPainting(aBuilder))
     return NS_OK;
 
   return aList->AppendNewToTop(
       new (aBuilder) nsDisplayCaret(this, aBuilder->GetCaret()));
 }
 
+nscolor
+nsIFrame::GetCaretColorAt(PRInt32 aOffset)
+{
+  // Use text color.
+  return GetStyleColor()->mColor;
+}
+
 PRBool
 nsIFrame::HasBorder() const
 {
   // Border images contribute to the background of the content area
   // even if there's no border proper.
   return (GetUsedBorder() != nsMargin(0,0,0,0) ||
           GetStyleBorder()->IsBorderImageLoaded());
 }
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -972,16 +972,23 @@ public:
    * painted on top of the rest of the display list items.
    *
    * @param aDirtyRect is the dirty rectangle that we're repainting.
    */
   nsresult DisplayCaret(nsDisplayListBuilder*       aBuilder,
                         const nsRect&               aDirtyRect,
                         nsDisplayList*              aList);
 
+  /**
+   * Get the preferred caret color at the offset.
+   *
+   * @param aOffset is offset of the content.
+   */
+  virtual nscolor GetCaretColorAt(PRInt32 aOffset);
+
   PRBool IsThemed(nsTransparencyMode* aTransparencyMode = nsnull) {
     return IsThemed(GetStyleDisplay(), aTransparencyMode);
   }
   PRBool IsThemed(const nsStyleDisplay* aDisp,
                   nsTransparencyMode* aTransparencyMode = nsnull) {
     if (!aDisp->mAppearance)
       return PR_FALSE;
     nsPresContext* pc = PresContext();
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -302,16 +302,18 @@ public:
                                      const gfxPoint& aFramePt,
                                      const gfxPoint& aTextBaselinePt,
                                      const gfxRect& aDirtyRect,
                                      PropertyProvider& aProvider,
                                      nsTextPaintStyle& aTextPaintStyle,
                                      SelectionDetails* aDetails,
                                      SelectionType aSelectionType);
 
+  virtual nscolor GetCaretColorAt(PRInt32 aOffset);
+
   PRInt16 GetSelectionStatus(PRInt16* aSelectionFlags);
 
 #ifdef DEBUG
   void ToCString(nsCString& aBuf, PRInt32* aTotalContentLength) const;
 #endif
 
   PRInt32 GetContentOffset() const { return mContentOffset; }
   PRInt32 GetContentLength() const
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -4712,16 +4712,58 @@ nsTextFrame::PaintTextWithSelection(gfxC
                                     aProvider, aTextPaintStyle, details, type);
     }
   }
 
   DestroySelectionDetails(details);
   return PR_TRUE;
 }
 
+nscolor
+nsTextFrame::GetCaretColorAt(PRInt32 aOffset)
+{
+  NS_PRECONDITION(aOffset >= 0, "aOffset must be positive");
+
+  gfxSkipCharsIterator iter = EnsureTextRun();
+  PropertyProvider provider(this, iter);
+  PRInt32 contentOffset = provider.GetStart().GetOriginalOffset();
+  PRInt32 contentLength = provider.GetOriginalLength();
+  NS_PRECONDITION(aOffset >= contentOffset &&
+                  aOffset <= contentOffset + contentLength,
+                  "aOffset must be in the frame's range");
+  PRInt32 offsetInFrame = aOffset - contentOffset;
+  if (offsetInFrame < 0 || offsetInFrame >= contentLength) {
+    return nsFrame::GetCaretColorAt(aOffset);
+  }
+
+  nsTextPaintStyle textPaintStyle(this);
+  SelectionDetails* details = GetSelectionDetails();
+  SelectionDetails* sdptr = details;
+  nscolor result = nsFrame::GetCaretColorAt(aOffset);
+  SelectionType type = 0;
+  while (sdptr) {
+    PRInt32 start = NS_MAX(0, sdptr->mStart - contentOffset);
+    PRInt32 end = NS_MIN(contentLength, sdptr->mEnd - contentOffset);
+    if (start <= offsetInFrame && offsetInFrame < end &&
+        (type == 0 || sdptr->mType < type)) {
+      nscolor foreground, background;
+      if (GetSelectionTextColors(sdptr->mType, textPaintStyle,
+                                 sdptr->mTextRangeStyle,
+                                 &foreground, &background)) {
+        result = foreground;
+        type = sdptr->mType;
+      }
+    }
+    sdptr = sdptr->mNext;
+  }
+
+  DestroySelectionDetails(details);
+  return result;
+}
+
 static PRUint32
 ComputeTransformedLength(PropertyProvider& aProvider)
 {
   gfxSkipCharsIterator iter(aProvider.GetStart());
   PRUint32 start = iter.GetSkippedOffset();
   iter.AdvanceOriginal(aProvider.GetOriginalLength());
   return iter.GetSkippedOffset() - start;
 }