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