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