Bug 118308, NS_THEME_TEXTFIELD_CARET implementation (GTK), r+sr=roc
authorMichael Ventnor <ventnor.bugzilla@yahoo.com.au>
Mon, 23 Jun 2008 11:50:52 +0300
changeset 15481 ba1a2dc688af3ffdc386d5ca509d643bc43f4df0
parent 15480 1fedf0fea98ffad264b63adf8db2f53c167a24ee
child 15482 c14ab4f6cec613f38a6ee49ca9a7a75d09ff1caf
push idunknown
push userunknown
push dateunknown
bugs118308
milestone1.9.1a1pre
Bug 118308, NS_THEME_TEXTFIELD_CARET implementation (GTK), r+sr=roc
layout/base/nsCaret.cpp
layout/base/nsCaret.h
layout/base/nsDisplayList.cpp
layout/base/nsICaret.h
widget/public/nsILookAndFeel.h
widget/src/gtk2/gtk2drawing.c
widget/src/gtk2/gtkdrawing.h
widget/src/gtk2/nsLookAndFeel.cpp
widget/src/gtk2/nsLookAndFeel.h
widget/src/gtk2/nsNativeThemeGTK.cpp
widget/src/xpwidgets/nsXPLookAndFeel.cpp
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -67,16 +67,17 @@
 #include "nsBlockFrame.h"
 #include "nsISelectionController.h"
 #include "nsDisplayList.h"
 #include "nsCaret.h"
 #include "nsTextFrame.h"
 #include "nsXULPopupManager.h"
 #include "nsMenuPopupFrame.h"
 #include "nsTextFragment.h"
+#include "nsThemeConstants.h"
 
 // The bidi indicator hangs off the caret to one side, to show which
 // direction the typing is in. It needs to be at least 2x2 to avoid looking like 
 // an insignificant dot
 static const PRInt32 kMinBidiIndicatorPixels = 2;
 
 #ifdef IBMBIDI
 #include "nsIBidiKeyboard.h"
@@ -118,20 +119,24 @@ NS_IMETHODIMP nsCaret::Init(nsIPresShell
 
   // get nsILookAndFeel from the pres context, which has one cached.
   nsILookAndFeel *lookAndFeel = nsnull;
   nsPresContext *presContext = inPresShell->GetPresContext();
   
   // XXX we should just do this nsILookAndFeel consultation every time
   // we need these values.
   mCaretWidthCSSPx = 1;
+  mCaretAspectRatio = 0;
   if (presContext && (lookAndFeel = presContext->LookAndFeel())) {
     PRInt32 tempInt;
+    float tempFloat;
     if (NS_SUCCEEDED(lookAndFeel->GetMetric(nsILookAndFeel::eMetric_CaretWidth, tempInt)))
       mCaretWidthCSSPx = (nscoord)tempInt;
+    if (NS_SUCCEEDED(lookAndFeel->GetMetric(nsILookAndFeel::eMetricFloat_CaretAspectRatio, tempFloat)))
+      mCaretAspectRatio = tempFloat;
     if (NS_SUCCEEDED(lookAndFeel->GetMetric(nsILookAndFeel::eMetric_CaretBlinkTime, tempInt)))
       mBlinkRate = (PRUint32)tempInt;
     if (NS_SUCCEEDED(lookAndFeel->GetMetric(nsILookAndFeel::eMetric_ShowCaretDuringSelection, tempInt)))
       mShowDuringSelection = tempInt ? PR_TRUE : PR_FALSE;
   }
   
   // get the selection from the pres shell, and set ourselves up as a selection
   // listener
@@ -172,20 +177,22 @@ DrawCJKCaret(nsIFrame* aFrame, PRInt32 a
   if (!frag)
     return PR_FALSE;
   if (aOffset < 0 || aOffset >= frag->GetLength())
     return PR_FALSE;
   PRUnichar ch = frag->CharAt(aOffset);
   return 0x2e80 <= ch && ch <= 0xd7ff;
 }
 
-nsCaret::Metrics nsCaret::ComputeMetrics(nsIFrame* aFrame, PRInt32 aOffset)
+nsCaret::Metrics nsCaret::ComputeMetrics(nsIFrame* aFrame, PRInt32 aOffset, nscoord aCaretHeight)
 {
   // Compute nominal sizes in appunits
-  nscoord caretWidth = nsPresContext::CSSPixelsToAppUnits(mCaretWidthCSSPx);
+  nscoord caretWidth = (aCaretHeight * mCaretAspectRatio) +
+                       nsPresContext::CSSPixelsToAppUnits(mCaretWidthCSSPx);
+
   if (DrawCJKCaret(aFrame, aOffset)) {
     caretWidth += nsPresContext::CSSPixelsToAppUnits(1);
   }
   nscoord bidiIndicatorSize = nsPresContext::CSSPixelsToAppUnits(kMinBidiIndicatorPixels);
   bidiIndicatorSize = PR_MAX(caretWidth, bidiIndicatorSize);
 
   // Round them to device pixels. Always round down, except that anything
   // between 0 and 1 goes up to 1 so we don't let the caret disappear.
@@ -362,17 +369,17 @@ NS_IMETHODIMP nsCaret::GetCaretCoordinat
     if (outView)
       *outView = drawingView;
   }
   // now add the frame offset to the view offset, and we're done
   viewOffset += framePos;
   outCoordinates->x = viewOffset.x;
   outCoordinates->y = viewOffset.y;
   outCoordinates->height = theFrame->GetSize().height;
-  outCoordinates->width = ComputeMetrics(theFrame, theFrameOffset).mCaretWidth;
+  outCoordinates->width = ComputeMetrics(theFrame, theFrameOffset, outCoordinates->height).mCaretWidth;
   
   return NS_OK;
 }
 
 void nsCaret::DrawCaretAfterBriefDelay()
 {
   // Make sure readonly caret gets drawn again if it needs to be
   if (!mBlinkTimer) {
@@ -468,23 +475,46 @@ void nsCaret::UpdateCaretPosition()
   // A trick! Make the DrawCaret code recalculate the caret's current
   // position.
   mDrawn = PR_FALSE;
   DrawCaret(PR_FALSE);
 }
 
 void nsCaret::PaintCaret(nsDisplayListBuilder *aBuilder,
                          nsIRenderingContext *aCtx,
-                         const nsPoint &aOffset,
-                         nscolor aColor)
+                         nsIFrame* aForFrame,
+                         const nsPoint &aOffset)
 {
   NS_ASSERTION(mDrawn, "The caret shouldn't be drawing");
 
-  aCtx->SetColor(aColor);
-  aCtx->FillRect(mCaretRect + aOffset);
+  const nsRect drawCaretRect = mCaretRect + aOffset;
+  nscolor cssColor = aForFrame->GetStyleColor()->mColor;
+
+  // 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) {
+        theme->DrawWidgetBackground(aCtx, aForFrame, NS_THEME_TEXTFIELD_CARET,
+                                    drawCaretRect, drawCaretRect);
+        return;
+      }
+    }
+  }
+
+  aCtx->SetColor(cssColor);
+  aCtx->FillRect(drawCaretRect);
   if (!GetHookRect().IsEmpty())
     aCtx->FillRect(GetHookRect() + aOffset);
 }
 
 
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP nsCaret::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aDomSel, PRInt16 aReason)
 {
@@ -1171,17 +1201,17 @@ nsresult nsCaret::UpdateCaretRects(nsIFr
                                                        framePos);
   if (NS_FAILED(rv))
   {
     mCaretRect.Empty();
     return rv;
   }
 
   mCaretRect += framePos;
-  Metrics metrics = ComputeMetrics(aFrame, aFrameOffset);
+  Metrics metrics = ComputeMetrics(aFrame, aFrameOffset, mCaretRect.height);
   mCaretRect.width = metrics.mCaretWidth;
 
   // Clamp our position to be within our scroll frame. If we don't, then it
   // clips us, and we don't appear at all. See bug 335560.
   nsIFrame *scrollFrame =
     nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::scrollFrame);
   if (scrollFrame)
   {
--- a/layout/base/nsCaret.h
+++ b/layout/base/nsCaret.h
@@ -99,18 +99,18 @@ class nsCaret : public nsICaret,
       return nsnull;
     }
 
     void      InvalidateOutsideCaret();
     void      UpdateCaretPosition();
 
     void      PaintCaret(nsDisplayListBuilder *aBuilder,
                          nsIRenderingContext *aCtx,
-                         const nsPoint &aOffset,
-                         nscolor aColor);
+                         nsIFrame *aForFrame,
+                         const nsPoint &aOffset);
 
     void SetIgnoreUserModify(PRBool aIgnoreUserModify);
 
     //nsISelectionListener interface
     NS_DECL_NSISELECTIONLISTENER
 
     static void   CaretBlinkCallback(nsITimer *aTimer, void *aClosure);
   
@@ -141,17 +141,17 @@ protected:
                                          nsFrameSelection::HINT aFrameHint,
                                          PRUint8 aBidiLevel,
                                          PRBool aInvalidate);
 
     struct Metrics {
       nscoord mBidiIndicatorSize; // width and height of bidi indicator
       nscoord mCaretWidth;        // full caret width including bidi indicator
     };
-    Metrics ComputeMetrics(nsIFrame* aFrame, PRInt32 aOffset);
+    Metrics ComputeMetrics(nsIFrame* aFrame, PRInt32 aOffset, nscoord aCaretHeight);
 
     // Returns true if the caret should be drawn. When |mDrawn| is true,
     // this returns true, so that we erase the drawn caret. If |aIgnoreDrawnState|
     // is true, we don't take into account whether the caret is currently
     // drawn or not. This can be used to determine if the caret is drawn when
     // it shouldn't be.
     PRBool        MustDrawCaret(PRBool aIgnoreDrawnState);
 
@@ -191,16 +191,17 @@ protected:
 
     nsCOMPtr<nsITimer>              mBlinkTimer;
     nsCOMPtr<nsIRenderingContext>   mRendContext;
 
     // XXX these fields should go away and the values be acquired as needed,
     // probably by ComputeMetrics.
     PRUint32              mBlinkRate;         // time for one cyle (off then on), in milliseconds
     nscoord               mCaretWidthCSSPx;   // caret width in CSS pixels
+    float                 mCaretAspectRatio;  // caret width/height aspect ratio
     
     PRPackedBool          mVisible;           // is the caret blinking
 
     PRPackedBool          mDrawn;             // Denotes when the caret is physically drawn on the screen.
 
     PRPackedBool          mReadOnly;          // it the caret in readonly state (draws differently)      
     PRPackedBool          mShowDuringSelection; // show when text is selected
 
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -592,18 +592,17 @@ nsDisplayOutline::OptimizeVisibility(nsD
   return PR_TRUE;
 }
 
 void
 nsDisplayCaret::Paint(nsDisplayListBuilder* aBuilder,
     nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
   // Note: Because we exist, we know that the caret is visible, so we don't
   // need to check for the caret's visibility.
-  mCaret->PaintCaret(aBuilder, aCtx, aBuilder->ToReferenceFrame(mFrame),
-                     mFrame->GetStyleColor()->mColor);
+  mCaret->PaintCaret(aBuilder, aCtx, mFrame, aBuilder->ToReferenceFrame(mFrame));
 }
 
 PRBool
 nsDisplayBorder::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
                                     nsRegion* aVisibleRegion) {
   if (!nsDisplayItem::OptimizeVisibility(aBuilder, aVisibleRegion))
     return PR_FALSE;
 
--- a/layout/base/nsICaret.h
+++ b/layout/base/nsICaret.h
@@ -53,18 +53,18 @@ class nsIRenderingContext;
 class nsIFrame;
 class nsIView;
 class nsIPresShell;
 class nsISelection;
 class nsIDOMNode;
 
 // IID for the nsICaret interface
 #define NS_ICARET_IID \
-{ 0x35d82f6b, 0x78f6, 0x4e8a, \
-  { 0xb9, 0x2a, 0x1a, 0x26, 0xac, 0x2c, 0xe8, 0x53 } }
+{ 0x48e23b7f, 0x264e, 0xab7d, \
+  { 0x20, 0x03, 0x2c, 0x79, 0x13, 0xe1, 0x09, 0x4d } }
 
 
 class nsICaret: public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICARET_IID)
 
   enum EViewCoordinates {
@@ -177,18 +177,18 @@ public:
    */
   virtual void UpdateCaretPosition() = 0;
 
   /** PaintCaret
    *  Actually paint the caret onto the given rendering context.
    */
   virtual void PaintCaret(nsDisplayListBuilder *aBuilder,
                           nsIRenderingContext *aCtx,
-                          const nsPoint &aOffset,
-                          nscolor aColor) = 0;
+                          nsIFrame *aForFrame,
+                          const nsPoint &aOffset) = 0;
 
   /**
    * Sets whether the caret should only be visible in nodes that are not
    * user-modify: read-only, or whether it should be visible in all nodes.
    *
    * @param aIgnoreUserModify PR_TRUE to have the cursor visible in all nodes,
    *                          PR_FALSE to have it visible in all nodes except
    *                          those with user-modify: read-only
--- a/widget/public/nsILookAndFeel.h
+++ b/widget/public/nsILookAndFeel.h
@@ -295,17 +295,21 @@ public:
     eMetricFloat_TextFieldVerticalInsidePadding,
     eMetricFloat_TextFieldHorizontalInsidePadding,
     eMetricFloat_TextAreaVerticalInsidePadding,
     eMetricFloat_TextAreaHorizontalInsidePadding,
     eMetricFloat_ListVerticalInsidePadding,
     eMetricFloat_ListHorizontalInsidePadding,
     eMetricFloat_ButtonVerticalInsidePadding,
     eMetricFloat_ButtonHorizontalInsidePadding,
-    eMetricFloat_IMEUnderlineRelativeSize
+    eMetricFloat_IMEUnderlineRelativeSize,
+
+    // The width/height ratio of the cursor. If used, the CaretWidth int metric
+    // should be added to the calculated caret width.
+    eMetricFloat_CaretAspectRatio
   } nsMetricFloatID;
 
   NS_IMETHOD GetColor(const nsColorID aID, nscolor &aColor) = 0;
   NS_IMETHOD GetMetric(const nsMetricID aID, PRInt32 & aMetric) = 0;
   NS_IMETHOD GetMetric(const nsMetricFloatID aID, float & aMetric) = 0;
   virtual PRUnichar GetPasswordCharacter()
   {
     return PRUnichar('*');
--- a/widget/src/gtk2/gtk2drawing.c
+++ b/widget/src/gtk2/gtk2drawing.c
@@ -1438,16 +1438,27 @@ moz_gtk_vpaned_paint(GdkDrawable* drawab
                      GTK_SHADOW_NONE, cliprect, gVPanedWidget, "paned",
                      rect->x, rect->y, rect->width, rect->height,
                      GTK_ORIENTATION_HORIZONTAL);
 
     return MOZ_GTK_SUCCESS;
 }
 
 static gint
+moz_gtk_caret_paint(GdkDrawable* drawable, GdkRectangle* rect,
+                    GdkRectangle* cliprect, GtkTextDirection direction)
+{
+    ensure_entry_widget();
+    gtk_draw_insertion_cursor(gEntryWidget, drawable, cliprect,
+                              rect, TRUE, direction, FALSE);
+
+    return MOZ_GTK_SUCCESS;
+}
+
+static gint
 moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect,
                     GdkRectangle* cliprect, GtkWidgetState* state,
                     GtkWidget* widget, GtkTextDirection direction)
 {
     GtkStateType bg_state = state->disabled ?
                                 GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL;
     gint x, y, width = rect->width, height = rect->height;
     GtkStyle* style;
@@ -2779,16 +2790,17 @@ moz_gtk_get_widget_border(GtkThemeWidget
     case MOZ_GTK_TOOLTIP:
     case MOZ_GTK_WINDOW:
     case MOZ_GTK_RESIZER:
     case MOZ_GTK_MENUARROW:
     case MOZ_GTK_TOOLBARBUTTON_ARROW:
     case MOZ_GTK_TOOLBAR:
     case MOZ_GTK_MENUBAR:
     case MOZ_GTK_TAB_SCROLLARROW:
+    case MOZ_GTK_ENTRY_CARET:
         *left = *top = *right = *bottom = 0;
         return MOZ_GTK_SUCCESS;
     default:
         g_warning("Unsupported widget type: %d", widget);
         return MOZ_GTK_UNKNOWN_WIDGET;
     }
 
     *right = *left = XTHICKNESS(w->style);
@@ -3047,16 +3059,19 @@ moz_gtk_widget_paint(GtkThemeWidgetType 
         return moz_gtk_expander_paint(drawable, rect, cliprect, state,
                                       (GtkExpanderStyle) flags, direction);
         break;
     case MOZ_GTK_ENTRY:
         ensure_entry_widget();
         return moz_gtk_entry_paint(drawable, rect, cliprect, state,
                                    gEntryWidget, direction);
         break;
+    case MOZ_GTK_ENTRY_CARET:
+        return moz_gtk_caret_paint(drawable, rect, cliprect, direction);
+        break;
     case MOZ_GTK_DROPDOWN:
         return moz_gtk_combo_box_paint(drawable, rect, cliprect, state,
                                        (gboolean) flags, direction);
         break;
     case MOZ_GTK_DROPDOWN_ARROW:
         return moz_gtk_combo_box_entry_button_paint(drawable, rect, cliprect,
                                                     state, flags, direction);
         break;
--- a/widget/src/gtk2/gtkdrawing.h
+++ b/widget/src/gtk2/gtkdrawing.h
@@ -139,16 +139,18 @@ typedef enum {
   MOZ_GTK_SPINBUTTON,
   MOZ_GTK_SPINBUTTON_UP,
   MOZ_GTK_SPINBUTTON_DOWN,
   MOZ_GTK_SPINBUTTON_ENTRY,
   /* Paints the gripper of a GtkHandleBox. */
   MOZ_GTK_GRIPPER,
   /* Paints a GtkEntry. */
   MOZ_GTK_ENTRY,
+  /* Paints the native caret (or in GTK-speak: insertion cursor) */
+  MOZ_GTK_ENTRY_CARET,
   /* Paints a GtkOptionMenu. */
   MOZ_GTK_DROPDOWN,
   /* Paints a dropdown arrow (a GtkButton containing a down GtkArrow). */
   MOZ_GTK_DROPDOWN_ARROW,
   /* Paints an entry in an editable option menu */
   MOZ_GTK_DROPDOWN_ENTRY,
   /* Paints the container part of a GtkCheckButton. */
   MOZ_GTK_CHECKBUTTON_CONTAINER,
--- a/widget/src/gtk2/nsLookAndFeel.cpp
+++ b/widget/src/gtk2/nsLookAndFeel.cpp
@@ -53,16 +53,17 @@ nscolor   nsLookAndFeel::sMenuHover = 0;
 nscolor   nsLookAndFeel::sMenuHoverText = 0;
 nscolor   nsLookAndFeel::sMenuBackground = 0;
 nscolor   nsLookAndFeel::sButtonBackground = 0;
 nscolor   nsLookAndFeel::sButtonText = 0;
 nscolor   nsLookAndFeel::sButtonOuterLightBorder = 0;
 nscolor   nsLookAndFeel::sButtonInnerDarkBorder = 0;
 nscolor   nsLookAndFeel::sOddCellBackground = 0;
 PRUnichar nsLookAndFeel::sInvisibleCharacter = PRUnichar('*');
+float     nsLookAndFeel::sCaretRatio = 0;
 
 //-------------------------------------------------------------------------
 //
 // Query interface implementation
 //
 //-------------------------------------------------------------------------
 nsLookAndFeel::nsLookAndFeel() : nsXPLookAndFeel()
 {
@@ -604,16 +605,19 @@ NS_IMETHODIMP nsLookAndFeel::GetMetric(c
         aMetric = 0.25f;
         break;
     case eMetricFloat_ButtonHorizontalInsidePadding:
         aMetric = 0.25f;
         break;
     case eMetricFloat_IMEUnderlineRelativeSize:
         aMetric = 1.0f;
         break;
+    case eMetricFloat_CaretAspectRatio:
+        aMetric = sCaretRatio;
+        break;
     default:
         aMetric = -1.0;
         res = NS_ERROR_FAILURE;
     }
     return res;
 }
 
 void
@@ -728,16 +732,22 @@ nsLookAndFeel::InitLookAndFeel()
 
     gtk_widget_destroy(window);
 
     // invisible character styles
     GtkWidget *entry = gtk_entry_new();
     guint value;
     g_object_get (entry, "invisible-char", &value, NULL);
     sInvisibleCharacter = PRUnichar(value);
+
+    // caret styles
+    gtk_widget_style_get(entry,
+                         "cursor-aspect-ratio", &sCaretRatio,
+                         NULL);
+
     gtk_widget_destroy(entry);
 }
 
 // virtual
 PRUnichar
 nsLookAndFeel::GetPasswordCharacter()
 {
     return sInvisibleCharacter;
--- a/widget/src/gtk2/nsLookAndFeel.h
+++ b/widget/src/gtk2/nsLookAndFeel.h
@@ -68,16 +68,17 @@ protected:
     static nscolor sMenuHover;
     static nscolor sMenuHoverText;
     static nscolor sButtonBackground;
     static nscolor sButtonText;
     static nscolor sButtonOuterLightBorder;
     static nscolor sButtonInnerDarkBorder;
     static nscolor sOddCellBackground;
     static PRUnichar sInvisibleCharacter;
+    static float   sCaretRatio;
 
     static void InitLookAndFeel();
     void InitWidget() {
         mWidget = gtk_invisible_new();
         gtk_object_ref(GTK_OBJECT(mWidget));
         gtk_object_sink(GTK_OBJECT(mWidget));
         gtk_widget_ensure_style(mWidget);
         mStyle = gtk_widget_get_style(mWidget);
--- a/widget/src/gtk2/nsNativeThemeGTK.cpp
+++ b/widget/src/gtk2/nsNativeThemeGTK.cpp
@@ -434,16 +434,19 @@ nsNativeThemeGTK::GetGtkWidgetAndState(P
     break;
   case NS_THEME_RESIZER:
     aGtkWidgetType = MOZ_GTK_RESIZER;
     break;
   case NS_THEME_TEXTFIELD:
   case NS_THEME_TEXTFIELD_MULTILINE:
     aGtkWidgetType = MOZ_GTK_ENTRY;
     break;
+  case NS_THEME_TEXTFIELD_CARET:
+    aGtkWidgetType = MOZ_GTK_ENTRY_CARET;
+    break;
   case NS_THEME_LISTBOX:
   case NS_THEME_TREEVIEW:
     aGtkWidgetType = MOZ_GTK_TREEVIEW;
     break;
   case NS_THEME_TREEVIEW_HEADER_CELL:
     if (aWidgetFlags) {
       // In this case, the flag denotes whether the header is the sorted one or not
       if (GetTreeSortDirection(aFrame) == eTreeSortDirection_Natural)
@@ -1321,17 +1324,17 @@ nsNativeThemeGTK::ThemeSupportsWidget(ns
   case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
   case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
   case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
   case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
     // case NS_THEME_SCROLLBAR_GRIPPER_HORIZONTAL:  (n/a for gtk)
     // case NS_THEME_SCROLLBAR_GRIPPER_VERTICAL:  (n/a for gtk)
   case NS_THEME_TEXTFIELD:
   case NS_THEME_TEXTFIELD_MULTILINE:
-    // case NS_THEME_TEXTFIELD_CARET:
+  case NS_THEME_TEXTFIELD_CARET:
   case NS_THEME_DROPDOWN_TEXTFIELD:
   case NS_THEME_SCALE_HORIZONTAL:
   case NS_THEME_SCALE_THUMB_HORIZONTAL:
   case NS_THEME_SCALE_VERTICAL:
   case NS_THEME_SCALE_THUMB_VERTICAL:
     // case NS_THEME_SCALE_THUMB_START:
     // case NS_THEME_SCALE_THUMB_END:
     // case NS_THEME_SCALE_TICK:
--- a/widget/src/xpwidgets/nsXPLookAndFeel.cpp
+++ b/widget/src/xpwidgets/nsXPLookAndFeel.cpp
@@ -138,16 +138,18 @@ nsLookAndFeelFloatPref nsXPLookAndFeel::
   { "ui.listHorizontalInsidePadding",
     eMetricFloat_ListHorizontalInsidePadding, PR_FALSE, nsLookAndFeelTypeFloat, 0 },
   { "ui.buttonVerticalInsidePadding", eMetricFloat_ButtonVerticalInsidePadding,
     PR_FALSE, nsLookAndFeelTypeFloat, 0 },
   { "ui.buttonHorizontalInsidePadding", eMetricFloat_ButtonHorizontalInsidePadding,
     PR_FALSE, nsLookAndFeelTypeFloat, 0 },
   { "ui.IMEUnderlineRelativeSize", eMetricFloat_IMEUnderlineRelativeSize,
     PR_FALSE, nsLookAndFeelTypeFloat, 0 },
+  { "ui.caretAspectRatio", eMetricFloat_CaretAspectRatio, PR_FALSE,
+    nsLookAndFeelTypeFloat, 0 },
 };
 
 
 // This array MUST be kept in the same order as the color list in nsILookAndFeel.h.
 /* XXX If you add any strings longer than
  * "ui.IMESelectedConvertedTextBackground"
  * to the following array then you MUST update the
  * sizes of the sColorPrefs array in nsXPLookAndFeel.h