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