1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=4 sw=4 sts=4 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "nsTextBoxFrame.h"
8 
9 #include "gfx2DGlue.h"
10 #include "gfxUtils.h"
11 #include "mozilla/gfx/2D.h"
12 #include "nsFontMetrics.h"
13 #include "nsReadableUtils.h"
14 #include "nsCOMPtr.h"
15 #include "nsGkAtoms.h"
16 #include "nsPresContext.h"
17 #include "nsRenderingContext.h"
18 #include "nsStyleContext.h"
19 #include "nsIContent.h"
20 #include "nsNameSpaceManager.h"
21 #include "nsBoxLayoutState.h"
22 #include "nsMenuBarListener.h"
23 #include "nsXPIDLString.h"
24 #include "nsIServiceManager.h"
25 #include "nsIDOMElement.h"
26 #include "nsIDOMXULLabelElement.h"
27 #include "mozilla/EventStateManager.h"
28 #include "nsITheme.h"
29 #include "nsUnicharUtils.h"
30 #include "nsContentUtils.h"
31 #include "nsDisplayList.h"
32 #include "nsCSSRendering.h"
33 #include "nsIReflowCallback.h"
34 #include "nsBoxFrame.h"
NS_NewSprocketLayout(nsCOMPtr<nsBoxLayout> & aNewLayout)35 #include "mozilla/Preferences.h"
36 #include "nsLayoutUtils.h"
37 #include "mozilla/Attributes.h"
38 #include "nsUnicodeProperties.h"
39 
40 #ifdef ACCESSIBILITY
41 #include "nsAccessibilityService.h"
42 #endif
43 
44 #include "nsBidiUtils.h"
45 #include "nsBidiPresUtils.h"
46 
47 using namespace mozilla;
48 using namespace mozilla::gfx;
49 
50 class nsAccessKeyInfo
51 {
nsSprocketLayout()52 public:
53     int32_t mAccesskeyIndex;
54     nscoord mBeforeWidth, mAccessWidth, mAccessUnderlineSize, mAccessOffset;
55 };
56 
IsXULHorizontal(nsIFrame * aBox)57 
58 bool nsTextBoxFrame::gAlwaysAppendAccessKey          = false;
59 bool nsTextBoxFrame::gAccessKeyPrefInitialized       = false;
60 bool nsTextBoxFrame::gInsertSeparatorBeforeAccessKey = false;
61 bool nsTextBoxFrame::gInsertSeparatorPrefInitialized = false;
62 
GetFrameState(nsIFrame * aBox,nsFrameState & aState)63 nsIFrame*
64 NS_NewTextBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
65 {
66     return new (aPresShell) nsTextBoxFrame(aContext);
67 }
68 
GetFrameDirection(nsIFrame * aBox)69 NS_IMPL_FRAMEARENA_HELPERS(nsTextBoxFrame)
70 
71 NS_QUERYFRAME_HEAD(nsTextBoxFrame)
72   NS_QUERYFRAME_ENTRY(nsTextBoxFrame)
73 NS_QUERYFRAME_TAIL_INHERITING(nsLeafBoxFrame)
74 
75 nsresult
76 nsTextBoxFrame::AttributeChanged(int32_t         aNameSpaceID,
77                                  nsIAtom*        aAttribute,
78                                  int32_t         aModType)
79 {
80     bool aResize;
81     bool aRedraw;
82 
83     UpdateAttributes(aAttribute, aResize, aRedraw);
84 
85     if (aResize) {
86         PresContext()->PresShell()->
87             FrameNeedsReflow(this, nsIPresShell::eStyleChange,
88                              NS_FRAME_IS_DIRTY);
89     } else if (aRedraw) {
90         nsBoxLayoutState state(PresContext());
91         XULRedraw(state);
92     }
93 
94     // If the accesskey changed, register for the new value
95     // The old value has been unregistered in nsXULElement::SetAttr
96     if (aAttribute == nsGkAtoms::accesskey || aAttribute == nsGkAtoms::control)
97         RegUnregAccessKey(true);
98 
99     return NS_OK;
100 }
101 
102 nsTextBoxFrame::nsTextBoxFrame(nsStyleContext* aContext):
103   nsLeafBoxFrame(aContext), mAccessKeyInfo(nullptr), mCropType(CropRight),
104   mNeedsReflowCallback(false)
105 {
106     MarkIntrinsicISizesDirty();
107 }
108 
109 nsTextBoxFrame::~nsTextBoxFrame()
110 {
111     delete mAccessKeyInfo;
112 }
113 
114 
115 void
116 nsTextBoxFrame::Init(nsIContent*       aContent,
117                      nsContainerFrame* aParent,
118                      nsIFrame*         aPrevInFlow)
119 {
120     nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
121 
122     bool aResize;
123     bool aRedraw;
124     UpdateAttributes(nullptr, aResize, aRedraw); /* update all */
125 
126     // register access key
127     RegUnregAccessKey(true);
128 }
129 
130 void
131 nsTextBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
132 {
133     // unregister access key
134     RegUnregAccessKey(false);
135     nsLeafBoxFrame::DestroyFrom(aDestructRoot);
136 }
137 
138 bool
139 nsTextBoxFrame::AlwaysAppendAccessKey()
140 {
141   if (!gAccessKeyPrefInitialized)
142   {
143     gAccessKeyPrefInitialized = true;
144 
145     const char* prefName = "intl.menuitems.alwaysappendaccesskeys";
146     nsAdoptingString val = Preferences::GetLocalizedString(prefName);
147     gAlwaysAppendAccessKey = val.EqualsLiteral("true");
148   }
149   return gAlwaysAppendAccessKey;
150 }
151 
152 bool
153 nsTextBoxFrame::InsertSeparatorBeforeAccessKey()
154 {
155   if (!gInsertSeparatorPrefInitialized)
156   {
157     gInsertSeparatorPrefInitialized = true;
158 
159     const char* prefName = "intl.menuitems.insertseparatorbeforeaccesskeys";
160     nsAdoptingString val = Preferences::GetLocalizedString(prefName);
161     gInsertSeparatorBeforeAccessKey = val.EqualsLiteral("true");
162   }
163   return gInsertSeparatorBeforeAccessKey;
164 }
165 
166 class nsAsyncAccesskeyUpdate final : public nsIReflowCallback
167 {
168 public:
169     explicit nsAsyncAccesskeyUpdate(nsIFrame* aFrame) : mWeakFrame(aFrame)
170     {
XULLayout(nsIFrame * aBox,nsBoxLayoutState & aState)171     }
172 
173     virtual bool ReflowFinished() override
174     {
175         bool shouldFlush = false;
176         nsTextBoxFrame* frame =
177             static_cast<nsTextBoxFrame*>(mWeakFrame.GetFrame());
178         if (frame) {
179             shouldFlush = frame->UpdateAccesskey(mWeakFrame);
180         }
181         delete this;
182         return shouldFlush;
183     }
184 
185     virtual void ReflowCallbackCanceled() override
186     {
187         delete this;
188     }
189 
190     nsWeakFrame mWeakFrame;
191 };
192 
193 bool
194 nsTextBoxFrame::UpdateAccesskey(nsWeakFrame& aWeakThis)
195 {
196     nsAutoString accesskey;
197     nsCOMPtr<nsIDOMXULLabelElement> labelElement = do_QueryInterface(mContent);
198     NS_ENSURE_TRUE(aWeakThis.IsAlive(), false);
199     if (labelElement) {
200         // Accesskey may be stored on control.
201         labelElement->GetAccessKey(accesskey);
202         NS_ENSURE_TRUE(aWeakThis.IsAlive(), false);
203     }
204     else {
205         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accesskey);
206     }
207 
208     if (!accesskey.Equals(mAccessKey)) {
209         // Need to get clean mTitle.
210         RecomputeTitle();
211         mAccessKey = accesskey;
212         UpdateAccessTitle();
213         PresContext()->PresShell()->
214             FrameNeedsReflow(this, nsIPresShell::eStyleChange,
215                              NS_FRAME_IS_DIRTY);
216         return true;
217     }
218     return false;
219 }
220 
221 void
222 nsTextBoxFrame::UpdateAttributes(nsIAtom*         aAttribute,
223                                  bool&          aResize,
224                                  bool&          aRedraw)
225 {
226     bool doUpdateTitle = false;
227     aResize = false;
228     aRedraw = false;
229 
230     if (aAttribute == nullptr || aAttribute == nsGkAtoms::crop) {
231         static nsIContent::AttrValuesArray strings[] =
232           {&nsGkAtoms::left, &nsGkAtoms::start, &nsGkAtoms::center,
233            &nsGkAtoms::right, &nsGkAtoms::end, &nsGkAtoms::none, nullptr};
234         CroppingStyle cropType;
235         switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::crop,
236                                           strings, eCaseMatters)) {
237           case 0:
238           case 1:
239             cropType = CropLeft;
240             break;
241           case 2:
242             cropType = CropCenter;
243             break;
244           case 3:
245           case 4:
246             cropType = CropRight;
247             break;
248           case 5:
249             cropType = CropNone;
250             break;
251           default:
252             cropType = CropAuto;
253             break;
254         }
255 
256         if (cropType != mCropType) {
257             aResize = true;
258             mCropType = cropType;
259         }
260     }
261 
262     if (aAttribute == nullptr || aAttribute == nsGkAtoms::value) {
263         RecomputeTitle();
264         doUpdateTitle = true;
265     }
266 
267     if (aAttribute == nullptr || aAttribute == nsGkAtoms::accesskey) {
268         mNeedsReflowCallback = true;
269         // Ensure that layout is refreshed and reflow callback called.
270         aResize = true;
271     }
272 
273     if (doUpdateTitle) {
274         UpdateAccessTitle();
275         aResize = true;
276     }
277 
278 }
279 
280 class nsDisplayXULTextBox : public nsDisplayItem {
281 public:
282   nsDisplayXULTextBox(nsDisplayListBuilder* aBuilder,
283                       nsTextBoxFrame* aFrame) :
284     nsDisplayItem(aBuilder, aFrame),
285     mDisableSubpixelAA(false)
286   {
287     MOZ_COUNT_CTOR(nsDisplayXULTextBox);
288   }
289 #ifdef NS_BUILD_REFCNT_LOGGING
290   virtual ~nsDisplayXULTextBox() {
291     MOZ_COUNT_DTOR(nsDisplayXULTextBox);
292   }
293 #endif
294 
295   virtual void Paint(nsDisplayListBuilder* aBuilder,
296                      nsRenderingContext* aCtx) override;
297   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
298                            bool* aSnap) override;
299   NS_DISPLAY_DECL_NAME("XULTextBox", TYPE_XUL_TEXT_BOX)
300 
301   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override;
302 
303   virtual void DisableComponentAlpha() override {
304     mDisableSubpixelAA = true;
305   }
306 
307   void PaintTextToContext(nsRenderingContext* aCtx,
308                           nsPoint aOffset,
309                           const nscolor* aColor);
310 
311   bool mDisableSubpixelAA;
312 };
313 
314 static void
315 PaintTextShadowCallback(nsRenderingContext* aCtx,
316                         nsPoint aShadowOffset,
317                         const nscolor& aShadowColor,
318                         void* aData)
319 {
320   reinterpret_cast<nsDisplayXULTextBox*>(aData)->
321            PaintTextToContext(aCtx, aShadowOffset, &aShadowColor);
322 }
323 
324 void
325 nsDisplayXULTextBox::Paint(nsDisplayListBuilder* aBuilder,
326                            nsRenderingContext* aCtx)
327 {
328   DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
329                                                     mDisableSubpixelAA);
330 
331   // Paint the text shadow before doing any foreground stuff
332   nsRect drawRect = static_cast<nsTextBoxFrame*>(mFrame)->mTextDrawRect +
333                     ToReferenceFrame();
334   nsLayoutUtils::PaintTextShadow(mFrame, aCtx,
335                                  drawRect, mVisibleRect,
336                                  mFrame->StyleColor()->mColor,
337                                  PaintTextShadowCallback,
338                                  (void*)this);
339 
340   PaintTextToContext(aCtx, nsPoint(0, 0), nullptr);
341 }
342 
343 void
344 nsDisplayXULTextBox::PaintTextToContext(nsRenderingContext* aCtx,
345                                         nsPoint aOffset,
346                                         const nscolor* aColor)
347 {
348   static_cast<nsTextBoxFrame*>(mFrame)->
349     PaintTitle(*aCtx, mVisibleRect, ToReferenceFrame() + aOffset, aColor);
350 }
351 
352 nsRect
353 nsDisplayXULTextBox::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) {
354   *aSnap = false;
355   return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
356 }
357 
358 nsRect
359 nsDisplayXULTextBox::GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder)
360 {
361   return static_cast<nsTextBoxFrame*>(mFrame)->GetComponentAlphaBounds() +
362       ToReferenceFrame();
363 }
364 
365 void
366 nsTextBoxFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
367                                  const nsRect&           aDirtyRect,
368                                  const nsDisplayListSet& aLists)
369 {
370     if (!IsVisibleForPainting(aBuilder))
371         return;
372 
373     nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
374 
375     aLists.Content()->AppendNewToTop(new (aBuilder)
376         nsDisplayXULTextBox(aBuilder, this));
377 }
378 
379 void
380 nsTextBoxFrame::PaintTitle(nsRenderingContext& aRenderingContext,
381                            const nsRect&        aDirtyRect,
382                            nsPoint              aPt,
383                            const nscolor*       aOverrideColor)
384 {
385     if (mTitle.IsEmpty())
386         return;
387 
388     DrawText(aRenderingContext, aDirtyRect, mTextDrawRect + aPt, aOverrideColor);
389 }
390 
391 void
392 nsTextBoxFrame::DrawText(nsRenderingContext& aRenderingContext,
393                          const nsRect&       aDirtyRect,
394                          const nsRect&       aTextRect,
395                          const nscolor*      aOverrideColor)
396 {
397     nsPresContext* presContext = PresContext();
398     int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
399     DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
400 
401     // paint the title
402     nscolor overColor = 0;
403     nscolor underColor = 0;
404     nscolor strikeColor = 0;
405     uint8_t overStyle = 0;
406     uint8_t underStyle = 0;
407     uint8_t strikeStyle = 0;
408 
409     // Begin with no decorations
410     uint8_t decorations = NS_STYLE_TEXT_DECORATION_LINE_NONE;
411     // A mask of all possible decorations.
412     uint8_t decorMask = NS_STYLE_TEXT_DECORATION_LINE_LINES_MASK;
413 
414     WritingMode wm = GetWritingMode();
415     bool vertical = wm.IsVertical();
416 
417     nsIFrame* f = this;
418     do {  // find decoration colors
419       nsStyleContext* context = f->StyleContext();
420       if (!context->HasTextDecorationLines()) {
421         break;
422       }
423       const nsStyleTextReset* styleText = context->StyleTextReset();
424 
425       if (decorMask & styleText->mTextDecorationLine) {  // a decoration defined here
426         nscolor color;
427         if (aOverrideColor) {
428           color = *aOverrideColor;
429         } else {
430           color = context->StyleColor()->
431             CalcComplexColor(styleText->mTextDecorationColor);
432         }
433         uint8_t style = styleText->mTextDecorationStyle;
434 
435         if (NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE & decorMask &
436               styleText->mTextDecorationLine) {
437           underColor = color;
438           underStyle = style;
439           decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
440           decorations |= NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
441         }
442         if (NS_STYLE_TEXT_DECORATION_LINE_OVERLINE & decorMask &
443               styleText->mTextDecorationLine) {
444           overColor = color;
445           overStyle = style;
446           decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
447           decorations |= NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
448         }
449         if (NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH & decorMask &
450               styleText->mTextDecorationLine) {
451           strikeColor = color;
452           strikeStyle = style;
453           decorMask &= ~NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
454           decorations |= NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
455         }
456       }
457     } while (0 != decorMask &&
458              (f = nsLayoutUtils::GetParentOrPlaceholderFor(f)));
459 
460     RefPtr<nsFontMetrics> fontMet =
461       nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f);
462     fontMet->SetVertical(wm.IsVertical());
463     fontMet->SetTextOrientation(StyleVisibility()->mTextOrientation);
464 
465     nscoord offset;
466     nscoord size;
467     nscoord ascent = fontMet->MaxAscent();
468 
469     nsPoint baselinePt;
470     if (wm.IsVertical()) {
471       baselinePt.x =
472         presContext->RoundAppUnitsToNearestDevPixels(aTextRect.x +
473             (wm.IsVerticalRL() ? aTextRect.width - ascent : ascent));
474       baselinePt.y = aTextRect.y;
475     } else {
476       baselinePt.x = aTextRect.x;
477       baselinePt.y =
478         presContext->RoundAppUnitsToNearestDevPixels(aTextRect.y + ascent);
479     }
480 
481     nsCSSRendering::PaintDecorationLineParams params;
482     params.dirtyRect = ToRect(presContext->AppUnitsToGfxUnits(aDirtyRect));
483     params.pt = Point(presContext->AppUnitsToGfxUnits(aTextRect.x),
484                       presContext->AppUnitsToGfxUnits(aTextRect.y));
485     params.icoordInFrame =
486       Float(PresContext()->AppUnitsToGfxUnits(mTextDrawRect.x));
487     params.lineSize = Size(presContext->AppUnitsToGfxUnits(aTextRect.width), 0);
488     params.ascent = presContext->AppUnitsToGfxUnits(ascent);
489     params.vertical = vertical;
490 
491     // XXX todo: vertical-mode support for decorations not tested yet,
492     // probably won't be positioned correctly
493 
494     // Underlines are drawn before overlines, and both before the text
495     // itself, per http://www.w3.org/TR/CSS21/zindex.html point 7.2.1.4.1.1.
496     // (We don't apply this rule to the access-key underline because we only
497     // find out where that is as a side effect of drawing the text, in the
498     // general case -- see below.)
499     if (decorations & (NS_STYLE_TEXT_DECORATION_LINE_OVERLINE |
500                        NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE)) {
501       fontMet->GetUnderline(offset, size);
502       params.lineSize.height = presContext->AppUnitsToGfxUnits(size);
503       if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE) &&
504           underStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
505         params.color = underColor;
506         params.offset = presContext->AppUnitsToGfxUnits(offset);
507         params.decoration = NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE;
508         params.style = underStyle;
509         nsCSSRendering::PaintDecorationLine(this, *drawTarget, params);
510       }
511       if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_OVERLINE) &&
512           overStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
513         params.color = overColor;
514         params.offset = params.ascent;
515         params.decoration = NS_STYLE_TEXT_DECORATION_LINE_OVERLINE;
516         params.style = overStyle;
517         nsCSSRendering::PaintDecorationLine(this, *drawTarget, params);
518       }
519     }
520 
521     nsRenderingContext refContext(
522         PresContext()->PresShell()->CreateReferenceRenderingContext());
523     DrawTarget* refDrawTarget = refContext.GetDrawTarget();
524 
525     CalculateUnderline(refDrawTarget, *fontMet);
526 
527     nscolor c = aOverrideColor ? *aOverrideColor : StyleColor()->mColor;
528     ColorPattern color(ToDeviceColor(c));
529     aRenderingContext.ThebesContext()->SetColor(Color::FromABGR(c));
530 
531     nsresult rv = NS_ERROR_FAILURE;
532 
533     if (mState & NS_FRAME_IS_BIDI) {
534       presContext->SetBidiEnabled();
535       nsBidiLevel level = nsBidiPresUtils::BidiLevelFromStyle(StyleContext());
536       if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
537           // We let the RenderText function calculate the mnemonic's
538           // underline position for us.
539           nsBidiPositionResolve posResolve;
540           posResolve.logicalIndex = mAccessKeyInfo->mAccesskeyIndex;
541           rv = nsBidiPresUtils::RenderText(mCroppedTitle.get(), mCroppedTitle.Length(), level,
542                                            presContext, aRenderingContext,
543                                            refDrawTarget, *fontMet,
544                                            baselinePt.x, baselinePt.y,
545                                            &posResolve,
546                                            1);
547           mAccessKeyInfo->mBeforeWidth = posResolve.visualLeftTwips;
548           mAccessKeyInfo->mAccessWidth = posResolve.visualWidth;
549       }
550       else
551       {
552           rv = nsBidiPresUtils::RenderText(mCroppedTitle.get(), mCroppedTitle.Length(), level,
553                                            presContext, aRenderingContext,
554                                            refDrawTarget, *fontMet,
555                                            baselinePt.x, baselinePt.y);
556       }
557     }
558     if (NS_FAILED(rv)) {
559        fontMet->SetTextRunRTL(false);
560 
561        if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
562            // In the simple (non-BiDi) case, we calculate the mnemonic's
563            // underline position by getting the text metric.
564            // XXX are attribute values always two byte?
565            if (mAccessKeyInfo->mAccesskeyIndex > 0)
566                mAccessKeyInfo->mBeforeWidth = nsLayoutUtils::
567                    AppUnitWidthOfString(mCroppedTitle.get(),
568                                         mAccessKeyInfo->mAccesskeyIndex,
569                                         *fontMet, refDrawTarget);
570            else
571                mAccessKeyInfo->mBeforeWidth = 0;
572        }
573 
574        fontMet->DrawString(mCroppedTitle.get(), mCroppedTitle.Length(),
575                            baselinePt.x, baselinePt.y, &aRenderingContext,
576                            refDrawTarget);
577     }
578 
579     if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
580       nsRect r(aTextRect.x + mAccessKeyInfo->mBeforeWidth,
581                aTextRect.y + mAccessKeyInfo->mAccessOffset,
582                mAccessKeyInfo->mAccessWidth,
583                mAccessKeyInfo->mAccessUnderlineSize);
584       Rect devPxRect =
585         NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget);
586       drawTarget->FillRect(devPxRect, color);
587     }
588 
589     // Strikeout is drawn on top of the text, per
590     // http://www.w3.org/TR/CSS21/zindex.html point 7.2.1.4.1.1.
591     if ((decorations & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH) &&
592         strikeStyle != NS_STYLE_TEXT_DECORATION_STYLE_NONE) {
593       fontMet->GetStrikeout(offset, size);
594       params.color = strikeColor;
595       params.lineSize.height = presContext->AppUnitsToGfxUnits(size);
596       params.offset = presContext->AppUnitsToGfxUnits(offset);
597       params.decoration = NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH;
598       params.style = strikeStyle;
599       nsCSSRendering::PaintDecorationLine(this, *drawTarget, params);
600     }
601 }
602 
603 void
604 nsTextBoxFrame::CalculateUnderline(DrawTarget* aDrawTarget,
605                                    nsFontMetrics& aFontMetrics)
606 {
607     if (mAccessKeyInfo && mAccessKeyInfo->mAccesskeyIndex != kNotFound) {
608          // Calculate all fields of mAccessKeyInfo which
609          // are the same for both BiDi and non-BiDi frames.
610          const char16_t *titleString = mCroppedTitle.get();
611          aFontMetrics.SetTextRunRTL(false);
612          mAccessKeyInfo->mAccessWidth = nsLayoutUtils::
613              AppUnitWidthOfString(titleString[mAccessKeyInfo->mAccesskeyIndex],
614                                   aFontMetrics, aDrawTarget);
615 
616          nscoord offset, baseline;
617          aFontMetrics.GetUnderline(offset, mAccessKeyInfo->mAccessUnderlineSize);
618          baseline = aFontMetrics.MaxAscent();
619          mAccessKeyInfo->mAccessOffset = baseline - offset;
620     }
621 }
622 
623 nscoord
624 nsTextBoxFrame::CalculateTitleForWidth(nsRenderingContext& aRenderingContext,
625                                        nscoord              aWidth)
626 {
627     DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
628 
629     if (mTitle.IsEmpty()) {
630         mCroppedTitle.Truncate();
631         return 0;
632     }
633 
634     RefPtr<nsFontMetrics> fm =
635       nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f);
636 
637     // see if the text will completely fit in the width given
638     nscoord titleWidth =
639       nsLayoutUtils::AppUnitWidthOfStringBidi(mTitle, this, *fm,
640                                               aRenderingContext);
641     if (titleWidth <= aWidth) {
642         mCroppedTitle = mTitle;
643         if (HasRTLChars(mTitle) ||
644             StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
645             mState |= NS_FRAME_IS_BIDI;
646         }
PopulateBoxSizes(nsIFrame * aBox,nsBoxLayoutState & aState,nsBoxSize * & aBoxSizes,nscoord & aMinSize,nscoord & aMaxSize,int32_t & aFlexes)647         return titleWidth;  // fits, done.
648     }
649 
650     const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
651     if (mCropType != CropNone) {
652       // start with an ellipsis
653       mCroppedTitle.Assign(kEllipsis);
654 
655       // see if the width is even smaller than the ellipsis
656       // if so, clear the text (XXX set as many '.' as we can?).
657       fm->SetTextRunRTL(false);
658       titleWidth = nsLayoutUtils::AppUnitWidthOfString(kEllipsis, *fm,
659                                                        drawTarget);
660 
661       if (titleWidth > aWidth) {
662           mCroppedTitle.SetLength(0);
663           return 0;
664       }
665 
666       // if the ellipsis fits perfectly, no use in trying to insert
667       if (titleWidth == aWidth)
668           return titleWidth;
669 
670       aWidth -= titleWidth;
671     } else {
672       mCroppedTitle.Truncate(0);
673       titleWidth = 0;
674     }
675 
676     using mozilla::unicode::ClusterIterator;
677     using mozilla::unicode::ClusterReverseIterator;
678 
679     // ok crop things
680     switch (mCropType)
681     {
682         case CropAuto:
683         case CropNone:
684         case CropRight:
685         {
686             ClusterIterator iter(mTitle.Data(), mTitle.Length());
687             const char16_t* dataBegin = iter;
688             const char16_t* pos = dataBegin;
689             nscoord charWidth;
690             nscoord totalWidth = 0;
691 
692             while (!iter.AtEnd()) {
693                 iter.Next();
694                 const char16_t* nextPos = iter;
695                 ptrdiff_t length = nextPos - pos;
696                 charWidth = nsLayoutUtils::AppUnitWidthOfString(pos, length,
697                                                                 *fm,
698                                                                 drawTarget);
699                 if (totalWidth + charWidth > aWidth) {
700                     break;
701                 }
702 
703                 if (UCS2_CHAR_IS_BIDI(*pos)) {
704                     mState |= NS_FRAME_IS_BIDI;
705                 }
706                 pos = nextPos;
707                 totalWidth += charWidth;
708             }
709 
710             if (pos == dataBegin) {
711                 return titleWidth;
712             }
713 
714             // insert what character we can in.
715             nsAutoString title(mTitle);
716             title.Truncate(pos - dataBegin);
717             mCroppedTitle.Insert(title, 0);
718         }
719         break;
720 
721         case CropLeft:
722         {
723             ClusterReverseIterator iter(mTitle.Data(), mTitle.Length());
724             const char16_t* dataEnd = iter;
725             const char16_t* prevPos = dataEnd;
726             nscoord charWidth;
727             nscoord totalWidth = 0;
728 
729             while (!iter.AtEnd()) {
730                 iter.Next();
731                 const char16_t* pos = iter;
732                 ptrdiff_t length = prevPos - pos;
733                 charWidth = nsLayoutUtils::AppUnitWidthOfString(pos, length,
734                                                                 *fm,
735                                                                 drawTarget);
736                 if (totalWidth + charWidth > aWidth) {
737                     break;
738                 }
739 
740                 if (UCS2_CHAR_IS_BIDI(*pos)) {
741                     mState |= NS_FRAME_IS_BIDI;
742                 }
743                 prevPos = pos;
744                 totalWidth += charWidth;
745             }
746 
747             if (prevPos == dataEnd) {
748                 return titleWidth;
749             }
750 
751             nsAutoString copy;
752             mTitle.Right(copy, dataEnd - prevPos);
753             mCroppedTitle += copy;
754         }
755         break;
756 
757         case CropCenter:
758         {
759             nscoord stringWidth =
760                 nsLayoutUtils::AppUnitWidthOfStringBidi(mTitle, this, *fm,
761                                                         aRenderingContext);
762             if (stringWidth <= aWidth) {
763                 // the entire string will fit in the maximum width
764                 mCroppedTitle.Insert(mTitle, 0);
765                 break;
766             }
767 
768             // determine how much of the string will fit in the max width
769             nscoord charWidth = 0;
770             nscoord totalWidth = 0;
771             ClusterIterator leftIter(mTitle.Data(), mTitle.Length());
772             ClusterReverseIterator rightIter(mTitle.Data(), mTitle.Length());
773             const char16_t* dataBegin = leftIter;
774             const char16_t* dataEnd = rightIter;
775             const char16_t* leftPos = dataBegin;
776             const char16_t* rightPos = dataEnd;
777             const char16_t* pos;
778             ptrdiff_t length;
779             nsAutoString leftString, rightString;
780 
781             while (leftPos < rightPos) {
782                 leftIter.Next();
783                 pos = leftIter;
784                 length = pos - leftPos;
785                 charWidth = nsLayoutUtils::AppUnitWidthOfString(leftPos, length,
786                                                                 *fm,
787                                                                 drawTarget);
788                 if (totalWidth + charWidth > aWidth) {
789                     break;
790                 }
791 
792                 if (UCS2_CHAR_IS_BIDI(*leftPos)) {
793                     mState |= NS_FRAME_IS_BIDI;
794                 }
795 
796                 leftString.Append(leftPos, length);
797                 leftPos = pos;
798                 totalWidth += charWidth;
799 
800                 if (leftPos >= rightPos) {
801                     break;
802                 }
803 
804                 rightIter.Next();
805                 pos = rightIter;
806                 length = rightPos - pos;
807                 charWidth = nsLayoutUtils::AppUnitWidthOfString(pos, length,
808                                                                 *fm,
809                                                                 drawTarget);
810                 if (totalWidth + charWidth > aWidth) {
811                     break;
812                 }
813 
814                 if (UCS2_CHAR_IS_BIDI(*pos)) {
815                     mState |= NS_FRAME_IS_BIDI;
816                 }
817 
818                 rightString.Insert(pos, 0, length);
819                 rightPos = pos;
820                 totalWidth += charWidth;
821             }
822 
823             mCroppedTitle = leftString + kEllipsis + rightString;
824         }
825         break;
826     }
827 
828     return nsLayoutUtils::AppUnitWidthOfStringBidi(mCroppedTitle, this, *fm,
829                                                    aRenderingContext);
830 }
831 
832 #define OLD_ELLIPSIS NS_LITERAL_STRING("...")
833 
834 // the following block is to append the accesskey to mTitle if there is an accesskey
835 // but the mTitle doesn't have the character
836 void
837 nsTextBoxFrame::UpdateAccessTitle()
838 {
839     /*
840      * Note that if you change appending access key label spec,
841      * you need to maintain same logic in following methods. See bug 324159.
842      * toolkit/content/commonDialog.js (setLabelForNode)
843      * toolkit/content/widgets/text.xml (formatAccessKey)
844      */
845     int32_t menuAccessKey;
846     nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
847     if (!menuAccessKey || mAccessKey.IsEmpty())
848         return;
849 
850     if (!AlwaysAppendAccessKey() &&
851         FindInReadable(mAccessKey, mTitle, nsCaseInsensitiveStringComparator()))
852         return;
853 
854     nsAutoString accessKeyLabel;
855     accessKeyLabel += '(';
856     accessKeyLabel += mAccessKey;
857     ToUpperCase(accessKeyLabel);
858     accessKeyLabel += ')';
859 
860     if (mTitle.IsEmpty()) {
861         mTitle = accessKeyLabel;
862         return;
863     }
864 
865     const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
866     uint32_t offset = mTitle.Length();
867     if (StringEndsWith(mTitle, kEllipsis)) {
868         offset -= kEllipsis.Length();
869     } else if (StringEndsWith(mTitle, OLD_ELLIPSIS)) {
870         // Try to check with our old ellipsis (for old addons)
871         offset -= OLD_ELLIPSIS.Length();
872     } else {
873         // Try to check with
874         // our default ellipsis (for non-localized addons) or ':'
875         const char16_t kLastChar = mTitle.Last();
876         if (kLastChar == char16_t(0x2026) || kLastChar == char16_t(':'))
877             offset--;
878     }
879 
880     if (InsertSeparatorBeforeAccessKey() &&
881         offset > 0 && !NS_IS_SPACE(mTitle[offset - 1])) {
882         mTitle.Insert(' ', offset);
883         offset++;
884     }
885 
886     mTitle.Insert(accessKeyLabel, offset);
887 }
888 
889 void
890 nsTextBoxFrame::UpdateAccessIndex()
ComputeChildsNextPosition(nsIFrame * aBox,const nscoord & aCurX,const nscoord & aCurY,nscoord & aNextX,nscoord & aNextY,const nsRect & aCurrentChildSize)891 {
892     int32_t menuAccessKey;
893     nsMenuBarListener::GetMenuAccessKey(&menuAccessKey);
894     if (menuAccessKey) {
895         if (mAccessKey.IsEmpty()) {
896             if (mAccessKeyInfo) {
897                 delete mAccessKeyInfo;
898                 mAccessKeyInfo = nullptr;
899             }
900         } else {
901             if (!mAccessKeyInfo) {
902                 mAccessKeyInfo = new nsAccessKeyInfo();
903                 if (!mAccessKeyInfo)
904                     return;
905             }
906 
907             nsAString::const_iterator start, end;
908 
909             mCroppedTitle.BeginReading(start);
910             mCroppedTitle.EndReading(end);
911 
912             // remember the beginning of the string
913             nsAString::const_iterator originalStart = start;
914 
915             bool found;
916             if (!AlwaysAppendAccessKey()) {
917                 // not appending access key - do case-sensitive search
918                 // first
919                 found = FindInReadable(mAccessKey, start, end);
920                 if (!found) {
921                     // didn't find it - perform a case-insensitive search
922                     start = originalStart;
923                     found = FindInReadable(mAccessKey, start, end,
924                                            nsCaseInsensitiveStringComparator());
925                 }
926             } else {
927                 found = RFindInReadable(mAccessKey, start, end,
928                                         nsCaseInsensitiveStringComparator());
929             }
930 
931             if (found)
932                 mAccessKeyInfo->mAccesskeyIndex = Distance(originalStart, start);
933             else
934                 mAccessKeyInfo->mAccesskeyIndex = kNotFound;
935         }
936     }
937 }
938 
939 void
940 nsTextBoxFrame::RecomputeTitle()
941 {
942   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, mTitle);
943 
944   // This doesn't handle language-specific uppercasing/lowercasing
945   // rules, unlike textruns.
946   uint8_t textTransform = StyleText()->mTextTransform;
947   if (textTransform == NS_STYLE_TEXT_TRANSFORM_UPPERCASE) {
948     ToUpperCase(mTitle);
949   } else if (textTransform == NS_STYLE_TEXT_TRANSFORM_LOWERCASE) {
950     ToLowerCase(mTitle);
951   }
952   // We can't handle NS_STYLE_TEXT_TRANSFORM_CAPITALIZE because we
953   // have no clue about word boundaries here.  We also don't handle
954   // NS_STYLE_TEXT_TRANSFORM_FULL_WIDTH.
955 }
956 
957 void
958 nsTextBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
959 {
960   if (!aOldStyleContext) {
961     // We're just being initialized
962     return;
963   }
964 
965   const nsStyleText* oldTextStyle = aOldStyleContext->PeekStyleText();
966   // We should really have oldTextStyle here, since we asked for our
967   // nsStyleText during Init(), but if it's not there for some reason
968   // just assume the worst and recompute mTitle.
969   if (!oldTextStyle ||
970       oldTextStyle->mTextTransform != StyleText()->mTextTransform) {
971     RecomputeTitle();
972     UpdateAccessTitle();
973   }
974 }
975 
976 NS_IMETHODIMP
977 nsTextBoxFrame::DoXULLayout(nsBoxLayoutState& aBoxLayoutState)
978 {
979     if (mNeedsReflowCallback) {
980         nsIReflowCallback* cb = new nsAsyncAccesskeyUpdate(this);
981         if (cb) {
982             PresContext()->PresShell()->PostReflowCallback(cb);
983         }
984         mNeedsReflowCallback = false;
985     }
986 
987     nsresult rv = nsLeafBoxFrame::DoXULLayout(aBoxLayoutState);
988 
989     CalcDrawRect(*aBoxLayoutState.GetRenderingContext());
990 
991     const nsStyleText* textStyle = StyleText();
992 
993     nsRect scrollBounds(nsPoint(0, 0), GetSize());
994     nsRect textRect = mTextDrawRect;
995 
996     RefPtr<nsFontMetrics> fontMet =
997       nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f);
998     nsBoundingMetrics metrics =
999       fontMet->GetInkBoundsForVisualOverflow(mCroppedTitle.get(),
1000                                              mCroppedTitle.Length(),
1001                                              aBoxLayoutState.GetRenderingContext()->GetDrawTarget());
1002 
1003     WritingMode wm = GetWritingMode();
1004     LogicalRect tr(wm, textRect, GetSize());
1005 
1006     tr.IStart(wm) -= metrics.leftBearing;
1007     tr.ISize(wm) = metrics.width;
1008     // In DrawText() we always draw with the baseline at MaxAscent() (relative to mTextDrawRect),
1009     tr.BStart(wm) += fontMet->MaxAscent() - metrics.ascent;
1010     tr.BSize(wm) = metrics.ascent + metrics.descent;
1011 
1012     textRect = tr.GetPhysicalRect(wm, GetSize());
1013 
ChildResized(nsIFrame * aBox,nsBoxLayoutState & aState,nsIFrame * aChild,nsBoxSize * aChildBoxSize,nsComputedBoxSize * aChildComputedSize,nsBoxSize * aBoxSizes,nsComputedBoxSize * aComputedBoxSizes,const nsRect & aChildLayoutRect,nsRect & aChildActualRect,nsRect & aContainingRect,int32_t aFlexes,bool & aFinished)1014     // Our scrollable overflow is our bounds; our visual overflow may
1015     // extend beyond that.
1016     nsRect visualBounds;
1017     visualBounds.UnionRect(scrollBounds, textRect);
1018     nsOverflowAreas overflow(visualBounds, scrollBounds);
1019 
1020     if (textStyle->mTextShadow) {
1021       // text-shadow extends our visual but not scrollable bounds
1022       nsRect &vis = overflow.VisualOverflow();
1023       vis.UnionRect(vis, nsLayoutUtils::GetTextShadowRectsUnion(mTextDrawRect, this));
1024     }
1025     FinishAndStoreOverflow(overflow, GetSize());
1026 
1027     return rv;
1028 }
1029 
1030 nsRect
1031 nsTextBoxFrame::GetComponentAlphaBounds()
1032 {
1033   if (StyleText()->mTextShadow) {
1034     return GetVisualOverflowRectRelativeToSelf();
1035   }
1036   return mTextDrawRect;
1037 }
1038 
1039 bool
1040 nsTextBoxFrame::ComputesOwnOverflowArea()
1041 {
1042     return true;
1043 }
1044 
1045 /* virtual */ void
1046 nsTextBoxFrame::MarkIntrinsicISizesDirty()
1047 {
1048     mNeedsRecalc = true;
1049     nsLeafBoxFrame::MarkIntrinsicISizesDirty();
1050 }
1051 
1052 void
1053 nsTextBoxFrame::GetTextSize(nsRenderingContext& aRenderingContext,
1054                             const nsString& aString,
1055                             nsSize& aSize, nscoord& aAscent)
1056 {
1057     RefPtr<nsFontMetrics> fontMet =
1058       nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f);
1059     aSize.height = fontMet->MaxHeight();
1060     aSize.width =
1061       nsLayoutUtils::AppUnitWidthOfStringBidi(aString, this, *fontMet,
1062                                               aRenderingContext);
1063     aAscent = fontMet->MaxAscent();
1064 }
1065 
1066 void
1067 nsTextBoxFrame::CalcTextSize(nsBoxLayoutState& aBoxLayoutState)
1068 {
1069     if (mNeedsRecalc) {
1070         nsSize size;
1071         nsRenderingContext* rendContext = aBoxLayoutState.GetRenderingContext();
1072         if (rendContext) {
1073             GetTextSize(*rendContext, mTitle, size, mAscent);
1074             if (GetWritingMode().IsVertical()) {
1075                 Swap(size.width, size.height);
1076             }
1077             mTextSize = size;
1078             mNeedsRecalc = false;
1079         }
1080     }
1081 }
1082 
1083 void
1084 nsTextBoxFrame::CalcDrawRect(nsRenderingContext &aRenderingContext)
1085 {
1086     WritingMode wm = GetWritingMode();
1087 
1088     LogicalRect textRect(wm, LogicalPoint(wm, 0, 0), GetLogicalSize(wm));
1089     nsMargin borderPadding;
1090     GetXULBorderAndPadding(borderPadding);
1091     textRect.Deflate(wm, LogicalMargin(wm, borderPadding));
1092 
1093     // determine (cropped) title and underline position
1094     // determine (cropped) title which fits in aRect, and its width
1095     // (where "width" is the text measure along its baseline, i.e. actually
1096     // a physical height in vertical writing modes)
1097     nscoord titleWidth =
1098         CalculateTitleForWidth(aRenderingContext, textRect.ISize(wm));
1099 
1100 #ifdef ACCESSIBILITY
1101     // Make sure to update the accessible tree in case when cropped title is
1102     // changed.
1103     nsAccessibilityService* accService = GetAccService();
1104     if (accService) {
1105         accService->UpdateLabelValue(PresContext()->PresShell(), mContent,
1106                                      mCroppedTitle);
1107     }
1108 #endif
1109 
1110     // determine if and at which position to put the underline
1111     UpdateAccessIndex();
1112 
1113     // make the rect as small as our (cropped) text.
1114     nscoord outerISize = textRect.ISize(wm);
1115     textRect.ISize(wm) = titleWidth;
1116 
1117     // Align our text within the overall rect by checking our text-align property.
1118     const nsStyleText* textStyle = StyleText();
1119     if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_CENTER) {
1120       textRect.IStart(wm) += (outerISize - textRect.ISize(wm)) / 2;
1121     } else if (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_END ||
1122              (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_LEFT &&
1123               !wm.IsBidiLTR()) ||
1124              (textStyle->mTextAlign == NS_STYLE_TEXT_ALIGN_RIGHT &&
1125               wm.IsBidiLTR())) {
1126       textRect.IStart(wm) += (outerISize - textRect.ISize(wm));
1127     }
1128 
1129     mTextDrawRect = textRect.GetPhysicalRect(wm, GetSize());
1130 }
1131 
1132 /**
1133  * Ok return our dimensions
1134  */
1135 nsSize
1136 nsTextBoxFrame::GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState)
1137 {
1138     CalcTextSize(aBoxLayoutState);
1139 
1140     nsSize size = mTextSize;
1141     DISPLAY_PREF_SIZE(this, size);
1142 
1143     AddBorderAndPadding(size);
1144     bool widthSet, heightSet;
InvalidateComputedSizes(nsComputedBoxSize * aComputedBoxSizes)1145     nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet);
1146 
1147     return size;
1148 }
1149 
1150 /**
1151  * Ok return our dimensions
1152  */
1153 nsSize
ComputeChildSizes(nsIFrame * aBox,nsBoxLayoutState & aState,nscoord & aGivenSize,nsBoxSize * aBoxSizes,nsComputedBoxSize * & aComputedBoxSizes)1154 nsTextBoxFrame::GetXULMinSize(nsBoxLayoutState& aBoxLayoutState)
1155 {
1156     CalcTextSize(aBoxLayoutState);
1157 
1158     nsSize size = mTextSize;
1159     DISPLAY_MIN_SIZE(this, size);
1160 
1161     // if there is cropping our min width becomes our border and padding
1162     if (mCropType != CropNone && mCropType != CropAuto) {
1163         if (GetWritingMode().IsVertical()) {
1164             size.height = 0;
1165         } else {
1166             size.width = 0;
1167         }
1168     }
1169 
1170     AddBorderAndPadding(size);
1171     bool widthSet, heightSet;
1172     nsIFrame::AddXULMinSize(aBoxLayoutState, this, size, widthSet, heightSet);
1173 
1174     return size;
1175 }
1176 
1177 nscoord
1178 nsTextBoxFrame::GetXULBoxAscent(nsBoxLayoutState& aBoxLayoutState)
1179 {
1180     CalcTextSize(aBoxLayoutState);
1181 
1182     nscoord ascent = mAscent;
1183 
1184     nsMargin m(0,0,0,0);
1185     GetXULBorderAndPadding(m);
1186 
1187     WritingMode wm = GetWritingMode();
1188     ascent += LogicalMargin(wm, m).BStart(wm);
1189 
1190     return ascent;
1191 }
1192 
1193 #ifdef DEBUG_FRAME_DUMP
1194 nsresult
1195 nsTextBoxFrame::GetFrameName(nsAString& aResult) const
1196 {
1197     MakeFrameName(NS_LITERAL_STRING("TextBox"), aResult);
1198     aResult += NS_LITERAL_STRING("[value=") + mTitle + NS_LITERAL_STRING("]");
1199     return NS_OK;
1200 }
1201 #endif
1202 
1203 // If you make changes to this function, check its counterparts
1204 // in nsBoxFrame and nsXULLabelFrame
1205 nsresult
1206 nsTextBoxFrame::RegUnregAccessKey(bool aDoReg)
1207 {
1208     // if we have no content, we can't do anything
1209     if (!mContent)
1210         return NS_ERROR_FAILURE;
1211 
1212     // check if we have a |control| attribute
1213     // do this check first because few elements have control attributes, and we
1214     // can weed out most of the elements quickly.
1215 
1216     // XXXjag a side-effect is that we filter out anonymous <label>s
1217     // in e.g. <menu>, <menuitem>, <button>. These <label>s inherit
1218     // |accesskey| and would otherwise register themselves, overwriting
1219     // the content we really meant to be registered.
1220     if (!mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::control))
1221         return NS_OK;
1222 
1223     // see if we even have an access key
1224     nsAutoString accessKey;
1225     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
1226 
1227     if (accessKey.IsEmpty())
1228         return NS_OK;
1229 
1230     // With a valid PresContext we can get the ESM
1231     // and (un)register the access key
1232     EventStateManager* esm = PresContext()->EventStateManager();
1233 
1234     uint32_t key = accessKey.First();
1235     if (aDoReg)
1236         esm->RegisterAccessKey(mContent, key);
1237     else
1238         esm->UnregisterAccessKey(mContent, key);
1239 
1240     return NS_OK;
1241 }
1242