layout/xul/nsTextBoxFrame.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsTextBoxFrame.h"
     8 #include "nsReadableUtils.h"
     9 #include "nsCOMPtr.h"
    10 #include "nsGkAtoms.h"
    11 #include "nsPresContext.h"
    12 #include "nsRenderingContext.h"
    13 #include "nsStyleContext.h"
    14 #include "nsIContent.h"
    15 #include "nsNameSpaceManager.h"
    16 #include "nsBoxLayoutState.h"
    17 #include "nsMenuBarListener.h"
    18 #include "nsXPIDLString.h"
    19 #include "nsIServiceManager.h"
    20 #include "nsIDOMElement.h"
    21 #include "nsIDOMXULLabelElement.h"
    22 #include "mozilla/EventStateManager.h"
    23 #include "nsITheme.h"
    24 #include "nsUnicharUtils.h"
    25 #include "nsContentUtils.h"
    26 #include "nsCxPusher.h"
    27 #include "nsDisplayList.h"
    28 #include "nsCSSRendering.h"
    29 #include "nsIReflowCallback.h"
    30 #include "nsBoxFrame.h"
    31 #include "mozilla/Preferences.h"
    32 #include "nsLayoutUtils.h"
    33 #include "mozilla/Attributes.h"
    35 #ifdef ACCESSIBILITY
    36 #include "nsAccessibilityService.h"
    37 #endif
    39 #include "nsBidiUtils.h"
    40 #include "nsBidiPresUtils.h"
    42 using namespace mozilla;
    44 class nsAccessKeyInfo
    45 {
    46 public:
    47     int32_t mAccesskeyIndex;
    48     nscoord mBeforeWidth, mAccessWidth, mAccessUnderlineSize, mAccessOffset;
    49 };
    52 bool nsTextBoxFrame::gAlwaysAppendAccessKey          = false;
    53 bool nsTextBoxFrame::gAccessKeyPrefInitialized       = false;
    54 bool nsTextBoxFrame::gInsertSeparatorBeforeAccessKey = false;
    55 bool nsTextBoxFrame::gInsertSeparatorPrefInitialized = false;
    57 nsIFrame*
    58 NS_NewTextBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
    59 {
    60     return new (aPresShell) nsTextBoxFrame (aPresShell, aContext);
    61 }
    63 NS_IMPL_FRAMEARENA_HELPERS(nsTextBoxFrame)
    65 NS_QUERYFRAME_HEAD(nsTextBoxFrame)
    66   NS_QUERYFRAME_ENTRY(nsTextBoxFrame)
    67 NS_QUERYFRAME_TAIL_INHERITING(nsTextBoxFrameSuper)
    69 nsresult
    70 nsTextBoxFrame::AttributeChanged(int32_t         aNameSpaceID,
    71                                  nsIAtom*        aAttribute,
    72                                  int32_t         aModType)
    73 {
    74     bool aResize;
    75     bool aRedraw;
    77     UpdateAttributes(aAttribute, aResize, aRedraw);
    79     if (aResize) {
    80         PresContext()->PresShell()->
    81             FrameNeedsReflow(this, nsIPresShell::eStyleChange,
    82                              NS_FRAME_IS_DIRTY);
    83     } else if (aRedraw) {
    84         nsBoxLayoutState state(PresContext());
    85         Redraw(state);
    86     }
    88     // If the accesskey changed, register for the new value
    89     // The old value has been unregistered in nsXULElement::SetAttr
    90     if (aAttribute == nsGkAtoms::accesskey || aAttribute == nsGkAtoms::control)
    91         RegUnregAccessKey(true);
    93     return NS_OK;
    94 }
    96 nsTextBoxFrame::nsTextBoxFrame(nsIPresShell* aShell, nsStyleContext* aContext):
    97   nsLeafBoxFrame(aShell, aContext), mAccessKeyInfo(nullptr), mCropType(CropRight),
    98   mNeedsReflowCallback(false)
    99 {
   100     MarkIntrinsicWidthsDirty();
   101 }
   103 nsTextBoxFrame::~nsTextBoxFrame()
   104 {
   105     delete mAccessKeyInfo;
   106 }
   109 void
   110 nsTextBoxFrame::Init(nsIContent*      aContent,
   111                      nsIFrame*        aParent,
   112                      nsIFrame*        aPrevInFlow)
   113 {
   114     nsTextBoxFrameSuper::Init(aContent, aParent, aPrevInFlow);
   116     bool aResize;
   117     bool aRedraw;
   118     UpdateAttributes(nullptr, aResize, aRedraw); /* update all */
   120     // register access key
   121     RegUnregAccessKey(true);
   122 }
   124 void
   125 nsTextBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
   126 {
   127     // unregister access key
   128     RegUnregAccessKey(false);
   129     nsTextBoxFrameSuper::DestroyFrom(aDestructRoot);
   130 }
   132 bool
   133 nsTextBoxFrame::AlwaysAppendAccessKey()
   134 {
   135   if (!gAccessKeyPrefInitialized) 
   136   {
   137     gAccessKeyPrefInitialized = true;
   139     const char* prefName = "intl.menuitems.alwaysappendaccesskeys";
   140     nsAdoptingString val = Preferences::GetLocalizedString(prefName);
   141     gAlwaysAppendAccessKey = val.Equals(NS_LITERAL_STRING("true"));
   142   }
   143   return gAlwaysAppendAccessKey;
   144 }
   146 bool
   147 nsTextBoxFrame::InsertSeparatorBeforeAccessKey()
   148 {
   149   if (!gInsertSeparatorPrefInitialized)
   150   {
   151     gInsertSeparatorPrefInitialized = true;
   153     const char* prefName = "intl.menuitems.insertseparatorbeforeaccesskeys";
   154     nsAdoptingString val = Preferences::GetLocalizedString(prefName);
   155     gInsertSeparatorBeforeAccessKey = val.EqualsLiteral("true");
   156   }
   157   return gInsertSeparatorBeforeAccessKey;
   158 }
   160 class nsAsyncAccesskeyUpdate MOZ_FINAL : public nsIReflowCallback
   161 {
   162 public:
   163     nsAsyncAccesskeyUpdate(nsIFrame* aFrame) : mWeakFrame(aFrame)
   164     {
   165     }
   167     virtual bool ReflowFinished() MOZ_OVERRIDE
   168     {
   169         bool shouldFlush = false;
   170         nsTextBoxFrame* frame =
   171             static_cast<nsTextBoxFrame*>(mWeakFrame.GetFrame());
   172         if (frame) {
   173             shouldFlush = frame->UpdateAccesskey(mWeakFrame);
   174         }
   175         delete this;
   176         return shouldFlush;
   177     }
   179     virtual void ReflowCallbackCanceled() MOZ_OVERRIDE
   180     {
   181         delete this;
   182     }
   184     nsWeakFrame mWeakFrame;
   185 };
   187 bool
   188 nsTextBoxFrame::UpdateAccesskey(nsWeakFrame& aWeakThis)
   189 {
   190     nsAutoString accesskey;
   191     nsCOMPtr<nsIDOMXULLabelElement> labelElement = do_QueryInterface(mContent);
   192     NS_ENSURE_TRUE(aWeakThis.IsAlive(), false);
   193     if (labelElement) {
   194         // Accesskey may be stored on control.
   195         // Because this method is called by the reflow callback, current context
   196         // may not be the right one. Pushing the context of mContent so that
   197         // if nsIDOMXULLabelElement is implemented in XBL, we don't get a
   198         // security exception.
   199         nsCxPusher cx;
   200         if (cx.Push(mContent)) {
   201           labelElement->GetAccessKey(accesskey);
   202           NS_ENSURE_TRUE(aWeakThis.IsAlive(), false);
   203         }
   204     }
   205     else {
   206         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accesskey);
   207     }
   209     if (!accesskey.Equals(mAccessKey)) {
   210         // Need to get clean mTitle.
   211         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, mTitle);
   212         mAccessKey = accesskey;
   213         UpdateAccessTitle();
   214         PresContext()->PresShell()->
   215             FrameNeedsReflow(this, nsIPresShell::eStyleChange,
   216                              NS_FRAME_IS_DIRTY);
   217         return true;
   218     }
   219     return false;
   220 }
   222 void
   223 nsTextBoxFrame::UpdateAttributes(nsIAtom*         aAttribute,
   224                                  bool&          aResize,
   225                                  bool&          aRedraw)
   226 {
   227     bool doUpdateTitle = false;
   228     aResize = false;
   229     aRedraw = false;
   231     if (aAttribute == nullptr || aAttribute == nsGkAtoms::crop) {
   232         static nsIContent::AttrValuesArray strings[] =
   233           {&nsGkAtoms::left, &nsGkAtoms::start, &nsGkAtoms::center,
   234            &nsGkAtoms::right, &nsGkAtoms::end, nullptr};
   235         CroppingStyle cropType;
   236         switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::crop,
   237                                           strings, eCaseMatters)) {
   238           case 0:
   239           case 1:
   240             cropType = CropLeft;
   241             break;
   242           case 2:
   243             cropType = CropCenter;
   244             break;
   245           case 3:
   246           case 4:
   247             cropType = CropRight;
   248             break;
   249           default:
   250             cropType = CropNone;
   251             break;
   252         }
   254         if (cropType != mCropType) {
   255             aResize = true;
   256             mCropType = cropType;
   257         }
   258     }
   260     if (aAttribute == nullptr || aAttribute == nsGkAtoms::value) {
   261         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, mTitle);
   262         doUpdateTitle = true;
   263     }
   265     if (aAttribute == nullptr || aAttribute == nsGkAtoms::accesskey) {
   266         mNeedsReflowCallback = true;
   267         // Ensure that layout is refreshed and reflow callback called.
   268         aResize = true;
   269     }
   271     if (doUpdateTitle) {
   272         UpdateAccessTitle();
   273         aResize = true;
   274     }
   276 }
   278 class nsDisplayXULTextBox : public nsDisplayItem {
   279 public:
   280   nsDisplayXULTextBox(nsDisplayListBuilder* aBuilder,
   281                       nsTextBoxFrame* aFrame) :
   282     nsDisplayItem(aBuilder, aFrame),
   283     mDisableSubpixelAA(false)
   284   {
   285     MOZ_COUNT_CTOR(nsDisplayXULTextBox);
   286   }
   287 #ifdef NS_BUILD_REFCNT_LOGGING
   288   virtual ~nsDisplayXULTextBox() {
   289     MOZ_COUNT_DTOR(nsDisplayXULTextBox);
   290   }
   291 #endif
   293   virtual void Paint(nsDisplayListBuilder* aBuilder,
   294                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
   295   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
   296                            bool* aSnap) MOZ_OVERRIDE;
   297   NS_DISPLAY_DECL_NAME("XULTextBox", TYPE_XUL_TEXT_BOX)
   299   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
   301   virtual void DisableComponentAlpha() MOZ_OVERRIDE {
   302     mDisableSubpixelAA = true;
   303   }
   305   void PaintTextToContext(nsRenderingContext* aCtx,
   306                           nsPoint aOffset,
   307                           const nscolor* aColor);
   309   bool mDisableSubpixelAA;
   310 };
   312 static void
   313 PaintTextShadowCallback(nsRenderingContext* aCtx,
   314                         nsPoint aShadowOffset,
   315                         const nscolor& aShadowColor,
   316                         void* aData)
   317 {
   318   reinterpret_cast<nsDisplayXULTextBox*>(aData)->
   319            PaintTextToContext(aCtx, aShadowOffset, &aShadowColor);
   320 }
   322 void
   323 nsDisplayXULTextBox::Paint(nsDisplayListBuilder* aBuilder,
   324                            nsRenderingContext* aCtx)
   325 {
   326   gfxContextAutoDisableSubpixelAntialiasing disable(aCtx->ThebesContext(),
   327                                                     mDisableSubpixelAA);
   329   // Paint the text shadow before doing any foreground stuff
   330   nsRect drawRect = static_cast<nsTextBoxFrame*>(mFrame)->mTextDrawRect +
   331                     ToReferenceFrame();
   332   nsLayoutUtils::PaintTextShadow(mFrame, aCtx,
   333                                  drawRect, mVisibleRect,
   334                                  mFrame->StyleColor()->mColor,
   335                                  PaintTextShadowCallback,
   336                                  (void*)this);
   338   PaintTextToContext(aCtx, nsPoint(0, 0), nullptr);
   339 }
   341 void
   342 nsDisplayXULTextBox::PaintTextToContext(nsRenderingContext* aCtx,
   343                                         nsPoint aOffset,
   344                                         const nscolor* aColor)
   345 {
   346   static_cast<nsTextBoxFrame*>(mFrame)->
   347     PaintTitle(*aCtx, mVisibleRect, ToReferenceFrame() + aOffset, aColor);
   348 }
   350 nsRect
   351 nsDisplayXULTextBox::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
   352   *aSnap = false;
   353   return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
   354 }
   356 nsRect
   357 nsDisplayXULTextBox::GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder)
   358 {
   359   return static_cast<nsTextBoxFrame*>(mFrame)->GetComponentAlphaBounds() +
   360       ToReferenceFrame();
   361 }
   363 void
   364 nsTextBoxFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   365                                  const nsRect&           aDirtyRect,
   366                                  const nsDisplayListSet& aLists)
   367 {
   368     if (!IsVisibleForPainting(aBuilder))
   369         return;
   371     nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
   373     aLists.Content()->AppendNewToTop(new (aBuilder)
   374         nsDisplayXULTextBox(aBuilder, this));
   375 }
   377 void
   378 nsTextBoxFrame::PaintTitle(nsRenderingContext& aRenderingContext,
   379                            const nsRect&        aDirtyRect,
   380                            nsPoint              aPt,
   381                            const nscolor*       aOverrideColor)
   382 {
   383     if (mTitle.IsEmpty())
   384         return;
   386     DrawText(aRenderingContext, aDirtyRect, mTextDrawRect + aPt, aOverrideColor);
   387 }
   389 void
   390 nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext,
   391                          const nsRect&       aDirtyRect,
   392                          const nsRect&       aTextRect,
   393                          const nscolor*      aOverrideColor)
   394 {
   395     nsPresContext* presContext = PresContext();
   397     // paint the title
   398     nscolor overColor;
   399     nscolor underColor;
   400     nscolor strikeColor;
   401     uint8_t overStyle;
   402     uint8_t underStyle;
   403     uint8_t strikeStyle;
   405     // Begin with no decorations
   406     uint8_t decorations = NS_STYLE_TEXT_DECORATION_LINE_NONE;
   407     // A mask of all possible decorations.
   408     uint8_t decorMask = NS_STYLE_TEXT_DECORATION_LINE_LINES_MASK;
   410     nsIFrame* f = this;
   411     do {  // find decoration colors
   412       nsStyleContext* context = f->StyleContext();
   413       if (!context->HasTextDecorationLines()) {
   414         break;
   415       }
   416       const nsStyleTextReset* styleText = context->StyleTextReset();
   418       if (decorMask & styleText->mTextDecorationLine) {  // a decoration defined here
   419         nscolor color;
   420         if (aOverrideColor) {
   421           color = *aOverrideColor;
   422         } else {
   423           bool isForeground;
   424           styleText->GetDecorationColor(color, isForeground);
   425           if (isForeground) {
   426             color = nsLayoutUtils::GetColor(f, eCSSProperty_color);
   427           }
   428         }
   429         uint8_t style = styleText->GetDecorationStyle();
   431         if (NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE & decorMask &
   432               styleText->mTextDecorationLine) {
   433           underColor = color;
   434           underStyle = style;
   435           decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
   436           decorations |= NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
   437         }
   438         if (NS_STYLE_TEXT_DECORATION_LINE_OVERLINE & decorMask &
   439               styleText->mTextDecorationLine) {
   440           overColor = color;
   441           overStyle = style;
   442           decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
   443           decorations |= NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
   444         }
   445         if (NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH & decorMask &
   446               styleText->mTextDecorationLine) {
   447           strikeColor = color;
   448           strikeStyle = style;
   449           decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
   450           decorations |= NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
   451         }
   452       }
   453     } while (0 != decorMask &&
   454              (f = nsLayoutUtils::GetParentOrPlaceholderFor(f)));
   456     nsRefPtr<nsFontMetrics> fontMet;
   457     nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet));
   459     nscoord offset;
   460     nscoord size;
   461     nscoord ascent = fontMet->MaxAscent();
   463     nscoord baseline =
   464       presContext->RoundAppUnitsToNearestDevPixels(aTextRect.y + ascent);
   465     nsRefPtr<gfxContext> ctx = aRenderingContext.ThebesContext();
   466     gfxPoint pt(presContext->AppUnitsToGfxUnits(aTextRect.x),
   467                 presContext->AppUnitsToGfxUnits(aTextRect.y));
   468     gfxFloat width = presContext->AppUnitsToGfxUnits(aTextRect.width);
   469     gfxFloat ascentPixel = presContext->AppUnitsToGfxUnits(ascent);
   470     gfxFloat xInFrame = PresContext()->AppUnitsToGfxUnits(mTextDrawRect.x);
   471     gfxRect dirtyRect(presContext->AppUnitsToGfxUnits(aDirtyRect));
   473     // Underlines are drawn before overlines, and both before the text
   474     // itself, per http://www.w3.org/TR/CSS21/zindex.html point 7.2.1.4.1.1.
   475     // (We don't apply this rule to the access-key underline because we only
   476     // find out where that is as a side effect of drawing the text, in the
   477     // general case -- see below.)
   478     if (decorations & (NS_FONT_DECORATION_OVERLINE |
   479                        NS_FONT_DECORATION_UNDERLINE)) {
   480       fontMet->GetUnderline(offset, size);
   481       gfxFloat offsetPixel = presContext->AppUnitsToGfxUnits(offset);
   482       gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size);
   483       if ((decorations & NS_FONT_DECORATION_UNDERLINE) &&
   484           underStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
   485         nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, underColor,
   486                           pt, xInFrame, gfxSize(width, sizePixel),
   487                           ascentPixel, offsetPixel,
   488                           NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE, underStyle);
   489       }
   490       if ((decorations & NS_FONT_DECORATION_OVERLINE) &&
   491           overStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
   492         nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, overColor,
   493                           pt, xInFrame, gfxSize(width, sizePixel),
   494                           ascentPixel, ascentPixel,
   495                           NS_STYLE_TEXT_DECORATION_LINE_OVERLINE, overStyle);
   496       }
   497     }
   499     nsRefPtr<nsRenderingContext> refContext =
   500         PresContext()->PresShell()->CreateReferenceRenderingContext();
   502     aRenderingContext.SetFont(fontMet);
   503     refContext->SetFont(fontMet);
   505     CalculateUnderline(*refContext);
   507     aRenderingContext.SetColor(aOverrideColor ? *aOverrideColor : StyleColor()->mColor);
   509     nsresult rv = NS_ERROR_FAILURE;
   511     if (mState & NS_FRAME_IS_BIDI) {
   512       presContext->SetBidiEnabled();
   513       nsBidiLevel level = nsBidiPresUtils::BidiLevelFromStyle(StyleContext());
   514       if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
   515           // We let the RenderText function calculate the mnemonic's
   516           // underline position for us.
   517           nsBidiPositionResolve posResolve;
   518           posResolve.logicalIndex = mAccessKeyInfo->mAccesskeyIndex;
   519           rv = nsBidiPresUtils::RenderText(mCroppedTitle.get(), mCroppedTitle.Length(), level,
   520                                            presContext, aRenderingContext,
   521                                            *refContext,
   522                                            aTextRect.x, baseline,
   523                                            &posResolve,
   524                                            1);
   525           mAccessKeyInfo->mBeforeWidth = posResolve.visualLeftTwips;
   526           mAccessKeyInfo->mAccessWidth = posResolve.visualWidth;
   527       }
   528       else
   529       {
   530           rv = nsBidiPresUtils::RenderText(mCroppedTitle.get(), mCroppedTitle.Length(), level,
   531                                            presContext, aRenderingContext,
   532                                            *refContext,
   533                                            aTextRect.x, baseline);
   534       }
   535     }
   536     if (NS_FAILED(rv)) {
   537        aRenderingContext.SetTextRunRTL(false);
   539        if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
   540            // In the simple (non-BiDi) case, we calculate the mnemonic's
   541            // underline position by getting the text metric.
   542            // XXX are attribute values always two byte?
   543            if (mAccessKeyInfo->mAccesskeyIndex > 0)
   544                mAccessKeyInfo->mBeforeWidth =
   545                    refContext->GetWidth(mCroppedTitle.get(),
   546                                         mAccessKeyInfo->mAccesskeyIndex);
   547            else
   548                mAccessKeyInfo->mBeforeWidth = 0;
   549        }
   551        fontMet->DrawString(mCroppedTitle.get(), mCroppedTitle.Length(),
   552                            aTextRect.x, baseline, &aRenderingContext,
   553                            refContext.get());
   554     }
   556     if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
   557         aRenderingContext.FillRect(aTextRect.x + mAccessKeyInfo->mBeforeWidth,
   558                                    aTextRect.y + mAccessKeyInfo->mAccessOffset,
   559                                    mAccessKeyInfo->mAccessWidth,
   560                                    mAccessKeyInfo->mAccessUnderlineSize);
   561     }
   563     // Strikeout is drawn on top of the text, per
   564     // http://www.w3.org/TR/CSS21/zindex.html point 7.2.1.4.1.1.
   565     if ((decorations & NS_FONT_DECORATION_LINE_THROUGH) &&
   566         strikeStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
   567       fontMet->GetStrikeout(offset, size);
   568       gfxFloat offsetPixel = presContext->AppUnitsToGfxUnits(offset);
   569       gfxFloat sizePixel = presContext->AppUnitsToGfxUnits(size);
   570       nsCSSRendering::PaintDecorationLine(this, ctx, dirtyRect, strikeColor,
   571                         pt, xInFrame, gfxSize(width, sizePixel), ascentPixel,
   572                         offsetPixel, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH,
   573                         strikeStyle);
   574     }
   575 }
   577 void
   578 nsTextBoxFrame::CalculateUnderline(nsRenderingContext& aRenderingContext)
   579 {
   580     if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
   581          // Calculate all fields of mAccessKeyInfo which
   582          // are the same for both BiDi and non-BiDi frames.
   583          const char16_t *titleString = mCroppedTitle.get();
   584          aRenderingContext.SetTextRunRTL(false);
   585          mAccessKeyInfo->mAccessWidth =
   586              aRenderingContext.GetWidth(titleString[mAccessKeyInfo->
   587                                                     mAccesskeyIndex]);
   589          nscoord offset, baseline;
   590          nsFontMetrics* metrics = aRenderingContext.FontMetrics();
   591          metrics->GetUnderline(offset, mAccessKeyInfo->mAccessUnderlineSize);
   592          baseline = metrics->MaxAscent();
   593          mAccessKeyInfo->mAccessOffset = baseline - offset;
   594     }
   595 }
   597 nscoord
   598 nsTextBoxFrame::CalculateTitleForWidth(nsPresContext*      aPresContext,
   599                                        nsRenderingContext& aRenderingContext,
   600                                        nscoord              aWidth)
   601 {
   602     if (mTitle.IsEmpty()) {
   603         mCroppedTitle.Truncate();
   604         return 0;
   605     }
   607     nsRefPtr<nsFontMetrics> fm;
   608     nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   609     aRenderingContext.SetFont(fm);
   611     // see if the text will completely fit in the width given
   612     nscoord titleWidth = nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
   613                                                        mTitle.get(), mTitle.Length());
   615     if (titleWidth <= aWidth) {
   616         mCroppedTitle = mTitle;
   617         if (HasRTLChars(mTitle)) {
   618             mState |= NS_FRAME_IS_BIDI;
   619         }
   620         return titleWidth;  // fits, done.
   621     }
   623     const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
   624     // start with an ellipsis
   625     mCroppedTitle.Assign(kEllipsis);
   627     // see if the width is even smaller than the ellipsis
   628     // if so, clear the text (XXX set as many '.' as we can?).
   629     aRenderingContext.SetTextRunRTL(false);
   630     titleWidth = aRenderingContext.GetWidth(kEllipsis);
   632     if (titleWidth > aWidth) {
   633         mCroppedTitle.SetLength(0);
   634         return 0;
   635     }
   637     // if the ellipsis fits perfectly, no use in trying to insert
   638     if (titleWidth == aWidth)
   639         return titleWidth;
   641     aWidth -= titleWidth;
   643     // XXX: This whole block should probably take surrogates into account
   644     // XXX and clusters!
   645     // ok crop things
   646     switch (mCropType)
   647     {
   648         case CropNone:
   649         case CropRight:
   650         {
   651             nscoord cwidth;
   652             nscoord twidth = 0;
   653             int length = mTitle.Length();
   654             int i;
   655             for (i = 0; i < length; ++i) {
   656                 char16_t ch = mTitle.CharAt(i);
   657                 // still in LTR mode
   658                 cwidth = aRenderingContext.GetWidth(ch);
   659                 if (twidth + cwidth > aWidth)
   660                     break;
   662                 twidth += cwidth;
   663                 if (UCS2_CHAR_IS_BIDI(ch) ) {
   664                   mState |= NS_FRAME_IS_BIDI;
   665                 }
   666             }
   668             if (i == 0)
   669                 return titleWidth;
   671             // insert what character we can in.
   672             nsAutoString title( mTitle );
   673             title.Truncate(i);
   674             mCroppedTitle.Insert(title, 0);
   675         }
   676         break;
   678         case CropLeft:
   679         {
   680             nscoord cwidth;
   681             nscoord twidth = 0;
   682             int length = mTitle.Length();
   683             int i;
   684             for (i=length-1; i >= 0; --i) {
   685                 char16_t ch = mTitle.CharAt(i);
   686                 cwidth = aRenderingContext.GetWidth(ch);
   687                 if (twidth + cwidth > aWidth)
   688                     break;
   690                 twidth += cwidth;
   691                 if (UCS2_CHAR_IS_BIDI(ch) ) {
   692                   mState |= NS_FRAME_IS_BIDI;
   693                 }
   694             }
   696             if (i == length-1)
   697                 return titleWidth;
   699             nsAutoString copy;
   700             mTitle.Right(copy, length-1-i);
   701             mCroppedTitle += copy;
   702         }
   703         break;
   705         case CropCenter:
   706         {
   707             nscoord stringWidth =
   708                 nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
   709                                               mTitle.get(), mTitle.Length());
   710             if (stringWidth <= aWidth) {
   711                 // the entire string will fit in the maximum width
   712                 mCroppedTitle.Insert(mTitle, 0);
   713                 break;
   714             }
   716             // determine how much of the string will fit in the max width
   717             nscoord charWidth = 0;
   718             nscoord totalWidth = 0;
   719             char16_t ch;
   720             int leftPos, rightPos;
   721             nsAutoString leftString, rightString;
   723             rightPos = mTitle.Length() - 1;
   724             aRenderingContext.SetTextRunRTL(false);
   725             for (leftPos = 0; leftPos <= rightPos;) {
   726                 // look at the next character on the left end
   727                 ch = mTitle.CharAt(leftPos);
   728                 charWidth = aRenderingContext.GetWidth(ch);
   729                 totalWidth += charWidth;
   730                 if (totalWidth > aWidth)
   731                     // greater than the allowable width
   732                     break;
   733                 leftString.Insert(ch, leftString.Length());
   735                 if (UCS2_CHAR_IS_BIDI(ch))
   736                     mState |= NS_FRAME_IS_BIDI;
   738                 // look at the next character on the right end
   739                 if (rightPos > leftPos) {
   740                     // haven't looked at this character yet
   741                     ch = mTitle.CharAt(rightPos);
   742                     charWidth = aRenderingContext.GetWidth(ch);
   743                     totalWidth += charWidth;
   744                     if (totalWidth > aWidth)
   745                         // greater than the allowable width
   746                         break;
   747                     rightString.Insert(ch, 0);
   749                     if (UCS2_CHAR_IS_BIDI(ch))
   750                         mState |= NS_FRAME_IS_BIDI;
   751                 }
   753                 // look at the next two characters
   754                 leftPos++;
   755                 rightPos--;
   756             }
   758             mCroppedTitle = leftString + kEllipsis + rightString;
   759         }
   760         break;
   761     }
   763     return nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
   764                                          mCroppedTitle.get(), mCroppedTitle.Length());
   765 }
   767 #define OLD_ELLIPSIS NS_LITERAL_STRING("...")
   769 // the following block is to append the accesskey to mTitle if there is an accesskey
   770 // but the mTitle doesn't have the character
   771 void
   772 nsTextBoxFrame::UpdateAccessTitle()
   773 {
   774     /*
   775      * Note that if you change appending access key label spec,
   776      * you need to maintain same logic in following methods. See bug 324159.
   777      * toolkit/content/commonDialog.js (setLabelForNode)
   778      * toolkit/content/widgets/text.xml (formatAccessKey)
   779      */
   780     int32_t menuAccessKey;
   781     nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
   782     if (!menuAccessKey || mAccessKey.IsEmpty())
   783         return;
   785     if (!AlwaysAppendAccessKey() &&
   786         FindInReadable(mAccessKey, mTitle, nsCaseInsensitiveStringComparator()))
   787         return;
   789     nsAutoString accessKeyLabel;
   790     accessKeyLabel += '(';
   791     accessKeyLabel += mAccessKey;
   792     ToUpperCase(accessKeyLabel);
   793     accessKeyLabel += ')';
   795     if (mTitle.IsEmpty()) {
   796         mTitle = accessKeyLabel;
   797         return;
   798     }
   800     const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
   801     uint32_t offset = mTitle.Length();
   802     if (StringEndsWith(mTitle, kEllipsis)) {
   803         offset -= kEllipsis.Length();
   804     } else if (StringEndsWith(mTitle, OLD_ELLIPSIS)) {
   805         // Try to check with our old ellipsis (for old addons)
   806         offset -= OLD_ELLIPSIS.Length();
   807     } else {
   808         // Try to check with
   809         // our default ellipsis (for non-localized addons) or ':'
   810         const char16_t kLastChar = mTitle.Last();
   811         if (kLastChar == char16_t(0x2026) || kLastChar == char16_t(':'))
   812             offset--;
   813     }
   815     if (InsertSeparatorBeforeAccessKey() &&
   816         offset > 0 && !NS_IS_SPACE(mTitle[offset - 1])) {
   817         mTitle.Insert(' ', offset);
   818         offset++;
   819     }
   821     mTitle.Insert(accessKeyLabel, offset);
   822 }
   824 void
   825 nsTextBoxFrame::UpdateAccessIndex()
   826 {
   827     int32_t menuAccessKey;
   828     nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
   829     if (menuAccessKey) {
   830         if (mAccessKey.IsEmpty()) {
   831             if (mAccessKeyInfo) {
   832                 delete mAccessKeyInfo;
   833                 mAccessKeyInfo = nullptr;
   834             }
   835         } else {
   836             if (!mAccessKeyInfo) {
   837                 mAccessKeyInfo = new nsAccessKeyInfo();
   838                 if (!mAccessKeyInfo)
   839                     return;
   840             }
   842             nsAString::const_iterator start, end;
   844             mCroppedTitle.BeginReading(start);
   845             mCroppedTitle.EndReading(end);
   847             // remember the beginning of the string
   848             nsAString::const_iterator originalStart = start;
   850             bool found;
   851             if (!AlwaysAppendAccessKey()) {
   852                 // not appending access key - do case-sensitive search
   853                 // first
   854                 found = FindInReadable(mAccessKey, start, end);
   855                 if (!found) {
   856                     // didn't find it - perform a case-insensitive search
   857                     start = originalStart;
   858                     found = FindInReadable(mAccessKey, start, end,
   859                                            nsCaseInsensitiveStringComparator());
   860                 }
   861             } else {
   862                 found = RFindInReadable(mAccessKey, start, end,
   863                                         nsCaseInsensitiveStringComparator());
   864             }
   866             if (found)
   867                 mAccessKeyInfo->mAccesskeyIndex = Distance(originalStart, start);
   868             else
   869                 mAccessKeyInfo->mAccesskeyIndex = kNotFound;
   870         }
   871     }
   872 }
   874 NS_IMETHODIMP
   875 nsTextBoxFrame::DoLayout(nsBoxLayoutState& aBoxLayoutState)
   876 {
   877     if (mNeedsReflowCallback) {
   878         nsIReflowCallback* cb = new nsAsyncAccesskeyUpdate(this);
   879         if (cb) {
   880             PresContext()->PresShell()->PostReflowCallback(cb);
   881         }
   882         mNeedsReflowCallback = false;
   883     }
   885     nsresult rv = nsLeafBoxFrame::DoLayout(aBoxLayoutState);
   887     CalcDrawRect(*aBoxLayoutState.GetRenderingContext());
   889     const nsStyleText* textStyle = StyleText();
   891     nsRect scrollBounds(nsPoint(0, 0), GetSize());
   892     nsRect textRect = mTextDrawRect;
   894     nsRefPtr<nsFontMetrics> fontMet;
   895     nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet));
   896     nsBoundingMetrics metrics = 
   897       fontMet->GetInkBoundsForVisualOverflow(mCroppedTitle.get(),
   898                                              mCroppedTitle.Length(),
   899                                              aBoxLayoutState.GetRenderingContext());
   901     textRect.x -= metrics.leftBearing;
   902     textRect.width = metrics.width;
   903     // In DrawText() we always draw with the baseline at MaxAscent() (relative to mTextDrawRect), 
   904     textRect.y += fontMet->MaxAscent() - metrics.ascent;
   905     textRect.height = metrics.ascent + metrics.descent;
   907     // Our scrollable overflow is our bounds; our visual overflow may
   908     // extend beyond that.
   909     nsRect visualBounds;
   910     visualBounds.UnionRect(scrollBounds, textRect);
   911     nsOverflowAreas overflow(visualBounds, scrollBounds);
   913     if (textStyle->mTextShadow) {
   914       // text-shadow extends our visual but not scrollable bounds
   915       nsRect &vis = overflow.VisualOverflow();
   916       vis.UnionRect(vis, nsLayoutUtils::GetTextShadowRectsUnion(mTextDrawRect, this));
   917     }
   918     FinishAndStoreOverflow(overflow, GetSize());
   920     return rv;
   921 }
   923 nsRect
   924 nsTextBoxFrame::GetComponentAlphaBounds()
   925 {
   926   if (StyleText()->mTextShadow) {
   927     return GetVisualOverflowRectRelativeToSelf();
   928   }
   929   return mTextDrawRect;
   930 }
   932 bool
   933 nsTextBoxFrame::ComputesOwnOverflowArea()
   934 {
   935     return true;
   936 }
   938 /* virtual */ void
   939 nsTextBoxFrame::MarkIntrinsicWidthsDirty()
   940 {
   941     mNeedsRecalc = true;
   942     nsTextBoxFrameSuper::MarkIntrinsicWidthsDirty();
   943 }
   945 void
   946 nsTextBoxFrame::GetTextSize(nsPresContext* aPresContext,
   947                             nsRenderingContext& aRenderingContext,
   948                             const nsString& aString,
   949                             nsSize& aSize, nscoord& aAscent)
   950 {
   951     nsRefPtr<nsFontMetrics> fontMet;
   952     nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet));
   953     aSize.height = fontMet->MaxHeight();
   954     aRenderingContext.SetFont(fontMet);
   955     aSize.width =
   956       nsLayoutUtils::GetStringWidth(this, &aRenderingContext,
   957                                     aString.get(), aString.Length());
   958     aAscent = fontMet->MaxAscent();
   959 }
   961 void
   962 nsTextBoxFrame::CalcTextSize(nsBoxLayoutState& aBoxLayoutState)
   963 {
   964     if (mNeedsRecalc)
   965     {
   966         nsSize size;
   967         nsPresContext* presContext = aBoxLayoutState.PresContext();
   968         nsRenderingContext* rendContext = aBoxLayoutState.GetRenderingContext();
   969         if (rendContext) {
   970             GetTextSize(presContext, *rendContext,
   971                         mTitle, size, mAscent);
   972             mTextSize = size;
   973             mNeedsRecalc = false;
   974         }
   975     }
   976 }
   978 void
   979 nsTextBoxFrame::CalcDrawRect(nsRenderingContext &aRenderingContext)
   980 {
   981     nsRect textRect(nsPoint(0, 0), GetSize());
   982     nsMargin borderPadding;
   983     GetBorderAndPadding(borderPadding);
   984     textRect.Deflate(borderPadding);
   986     // determine (cropped) title and underline position
   987     nsPresContext* presContext = PresContext();
   988     // determine (cropped) title which fits in aRect.width and its width
   989     nscoord titleWidth =
   990         CalculateTitleForWidth(presContext, aRenderingContext, textRect.width);
   992 #ifdef ACCESSIBILITY
   993     // Make sure to update the accessible tree in case when cropped title is
   994     // changed.
   995     nsAccessibilityService* accService = GetAccService();
   996     if (accService) {
   997         accService->UpdateLabelValue(PresContext()->PresShell(), mContent,
   998                                      mCroppedTitle);
   999     }
  1000 #endif
  1002     // determine if and at which position to put the underline
  1003     UpdateAccessIndex();
  1005     // make the rect as small as our (cropped) text.
  1006     nscoord outerWidth = textRect.width;
  1007     textRect.width = titleWidth;
  1009     // Align our text within the overall rect by checking our text-align property.
  1010     const nsStyleVisibility* vis = StyleVisibility();
  1011     const nsStyleText* textStyle = StyleText();
  1013     if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_CENTER)
  1014       textRect.x += (outerWidth - textRect.width)/2;
  1015     else if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_RIGHT ||
  1016              (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_DEFAULT &&
  1017               vis->mDirection == NS_STYLE_DIRECTION_RTL) ||
  1018              (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_END &&
  1019               vis->mDirection == NS_STYLE_DIRECTION_LTR)) {
  1020       textRect.x += (outerWidth - textRect.width);
  1023     mTextDrawRect = textRect;
  1026 /**
  1027  * Ok return our dimensions
  1028  */
  1029 nsSize
  1030 nsTextBoxFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState)
  1032     CalcTextSize(aBoxLayoutState);
  1034     nsSize size = mTextSize;
  1035     DISPLAY_PREF_SIZE(this, size);
  1037     AddBorderAndPadding(size);
  1038     bool widthSet, heightSet;
  1039     nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet);
  1041     return size;
  1044 /**
  1045  * Ok return our dimensions
  1046  */
  1047 nsSize
  1048 nsTextBoxFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState)
  1050     CalcTextSize(aBoxLayoutState);
  1052     nsSize size = mTextSize;
  1053     DISPLAY_MIN_SIZE(this, size);
  1055     // if there is cropping our min width becomes our border and padding
  1056     if (mCropType != CropNone)
  1057         size.width = 0;
  1059     AddBorderAndPadding(size);
  1060     bool widthSet, heightSet;
  1061     nsIFrame::AddCSSMinSize(aBoxLayoutState, this, size, widthSet, heightSet);
  1063     return size;
  1066 nscoord
  1067 nsTextBoxFrame::GetBoxAscent(nsBoxLayoutState& aBoxLayoutState)
  1069     CalcTextSize(aBoxLayoutState);
  1071     nscoord ascent = mAscent;
  1073     nsMargin m(0,0,0,0);
  1074     GetBorderAndPadding(m);
  1075     ascent += m.top;
  1077     return ascent;
  1080 #ifdef DEBUG_FRAME_DUMP
  1081 nsresult
  1082 nsTextBoxFrame::GetFrameName(nsAString& aResult) const
  1084     MakeFrameName(NS_LITERAL_STRING("TextBox"), aResult);
  1085     aResult += NS_LITERAL_STRING("[value=") + mTitle + NS_LITERAL_STRING("]");
  1086     return NS_OK;
  1088 #endif
  1090 // If you make changes to this function, check its counterparts 
  1091 // in nsBoxFrame and nsXULLabelFrame
  1092 nsresult
  1093 nsTextBoxFrame::RegUnregAccessKey(bool aDoReg)
  1095     // if we have no content, we can't do anything
  1096     if (!mContent)
  1097         return NS_ERROR_FAILURE;
  1099     // check if we have a |control| attribute
  1100     // do this check first because few elements have control attributes, and we
  1101     // can weed out most of the elements quickly.
  1103     // XXXjag a side-effect is that we filter out anonymous <label>s
  1104     // in e.g. <menu>, <menuitem>, <button>. These <label>s inherit
  1105     // |accesskey| and would otherwise register themselves, overwriting
  1106     // the content we really meant to be registered.
  1107     if (!mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::control))
  1108         return NS_OK;
  1110     // see if we even have an access key
  1111     nsAutoString accessKey;
  1112     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
  1114     if (accessKey.IsEmpty())
  1115         return NS_OK;
  1117     // With a valid PresContext we can get the ESM 
  1118     // and (un)register the access key
  1119     EventStateManager* esm = PresContext()->EventStateManager();
  1121     uint32_t key = accessKey.First();
  1122     if (aDoReg)
  1123         esm->RegisterAccessKey(mContent, key);
  1124     else
  1125         esm->UnregisterAccessKey(mContent, key);
  1127     return NS_OK;

mercurial