1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsTextBoxFrame.h"
10 #include "mozilla/gfx/2D.h"
11 #include "nsFontMetrics.h"
12 #include "nsReadableUtils.h"
14 #include "nsGkAtoms.h"
15 #include "nsPresContext.h"
16 #include "nsRenderingContext.h"
17 #include "nsStyleContext.h"
18 #include "nsIContent.h"
19 #include "nsNameSpaceManager.h"
20 #include "nsBoxLayoutState.h"
21 #include "nsMenuBarListener.h"
22 #include "nsXPIDLString.h"
23 #include "nsIServiceManager.h"
24 #include "nsIDOMElement.h"
25 #include "nsIDOMXULLabelElement.h"
26 #include "mozilla/EventStateManager.h"
28 #include "nsUnicharUtils.h"
29 #include "nsContentUtils.h"
30 #include "nsDisplayList.h"
31 #include "nsCSSRendering.h"
32 #include "nsIReflowCallback.h"
33 #include "nsBoxFrame.h"
34 #include "mozilla/Preferences.h"
35 #include "nsLayoutUtils.h"
36 #include "mozilla/Attributes.h"
39 #include "nsAccessibilityService.h"
42 #include "nsBidiUtils.h"
43 #include "nsBidiPresUtils.h"
45 using namespace mozilla
;
46 using namespace mozilla::gfx
;
51 int32_t mAccesskeyIndex
;
52 nscoord mBeforeWidth
, mAccessWidth
, mAccessUnderlineSize
, mAccessOffset
;
56 bool nsTextBoxFrame::gAlwaysAppendAccessKey
= false;
57 bool nsTextBoxFrame::gAccessKeyPrefInitialized
= false;
58 bool nsTextBoxFrame::gInsertSeparatorBeforeAccessKey
= false;
59 bool nsTextBoxFrame::gInsertSeparatorPrefInitialized
= false;
62 NS_NewTextBoxFrame (nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
64 return new (aPresShell
) nsTextBoxFrame(aContext
);
67 NS_IMPL_FRAMEARENA_HELPERS(nsTextBoxFrame
)
69 NS_QUERYFRAME_HEAD(nsTextBoxFrame
)
70 NS_QUERYFRAME_ENTRY(nsTextBoxFrame
)
71 NS_QUERYFRAME_TAIL_INHERITING(nsTextBoxFrameSuper
)
74 nsTextBoxFrame::AttributeChanged(int32_t aNameSpaceID
,
81 UpdateAttributes(aAttribute
, aResize
, aRedraw
);
84 PresContext()->PresShell()->
85 FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
88 nsBoxLayoutState
state(PresContext());
92 // If the accesskey changed, register for the new value
93 // The old value has been unregistered in nsXULElement::SetAttr
94 if (aAttribute
== nsGkAtoms::accesskey
|| aAttribute
== nsGkAtoms::control
)
95 RegUnregAccessKey(true);
100 nsTextBoxFrame::nsTextBoxFrame(nsStyleContext
* aContext
):
101 nsLeafBoxFrame(aContext
), mAccessKeyInfo(nullptr), mCropType(CropRight
),
102 mNeedsReflowCallback(false)
104 MarkIntrinsicISizesDirty();
107 nsTextBoxFrame::~nsTextBoxFrame()
109 delete mAccessKeyInfo
;
114 nsTextBoxFrame::Init(nsIContent
* aContent
,
115 nsContainerFrame
* aParent
,
116 nsIFrame
* aPrevInFlow
)
118 nsTextBoxFrameSuper::Init(aContent
, aParent
, aPrevInFlow
);
122 UpdateAttributes(nullptr, aResize
, aRedraw
); /* update all */
124 // register access key
125 RegUnregAccessKey(true);
129 nsTextBoxFrame::DestroyFrom(nsIFrame
* aDestructRoot
)
131 // unregister access key
132 RegUnregAccessKey(false);
133 nsTextBoxFrameSuper::DestroyFrom(aDestructRoot
);
137 nsTextBoxFrame::AlwaysAppendAccessKey()
139 if (!gAccessKeyPrefInitialized
)
141 gAccessKeyPrefInitialized
= true;
143 const char* prefName
= "intl.menuitems.alwaysappendaccesskeys";
144 nsAdoptingString val
= Preferences::GetLocalizedString(prefName
);
145 gAlwaysAppendAccessKey
= val
.EqualsLiteral("true");
147 return gAlwaysAppendAccessKey
;
151 nsTextBoxFrame::InsertSeparatorBeforeAccessKey()
153 if (!gInsertSeparatorPrefInitialized
)
155 gInsertSeparatorPrefInitialized
= true;
157 const char* prefName
= "intl.menuitems.insertseparatorbeforeaccesskeys";
158 nsAdoptingString val
= Preferences::GetLocalizedString(prefName
);
159 gInsertSeparatorBeforeAccessKey
= val
.EqualsLiteral("true");
161 return gInsertSeparatorBeforeAccessKey
;
164 class nsAsyncAccesskeyUpdate MOZ_FINAL
: public nsIReflowCallback
167 explicit nsAsyncAccesskeyUpdate(nsIFrame
* aFrame
) : mWeakFrame(aFrame
)
171 virtual bool ReflowFinished() MOZ_OVERRIDE
173 bool shouldFlush
= false;
174 nsTextBoxFrame
* frame
=
175 static_cast<nsTextBoxFrame
*>(mWeakFrame
.GetFrame());
177 shouldFlush
= frame
->UpdateAccesskey(mWeakFrame
);
183 virtual void ReflowCallbackCanceled() MOZ_OVERRIDE
188 nsWeakFrame mWeakFrame
;
192 nsTextBoxFrame::UpdateAccesskey(nsWeakFrame
& aWeakThis
)
194 nsAutoString accesskey
;
195 nsCOMPtr
<nsIDOMXULLabelElement
> labelElement
= do_QueryInterface(mContent
);
196 NS_ENSURE_TRUE(aWeakThis
.IsAlive(), false);
198 // Accesskey may be stored on control.
199 labelElement
->GetAccessKey(accesskey
);
200 NS_ENSURE_TRUE(aWeakThis
.IsAlive(), false);
203 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::accesskey
, accesskey
);
206 if (!accesskey
.Equals(mAccessKey
)) {
207 // Need to get clean mTitle.
209 mAccessKey
= accesskey
;
211 PresContext()->PresShell()->
212 FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
220 nsTextBoxFrame::UpdateAttributes(nsIAtom
* aAttribute
,
224 bool doUpdateTitle
= false;
228 if (aAttribute
== nullptr || aAttribute
== nsGkAtoms::crop
) {
229 static nsIContent::AttrValuesArray strings
[] =
230 {&nsGkAtoms::left
, &nsGkAtoms::start
, &nsGkAtoms::center
,
231 &nsGkAtoms::right
, &nsGkAtoms::end
, nullptr};
232 CroppingStyle cropType
;
233 switch (mContent
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::crop
,
234 strings
, eCaseMatters
)) {
240 cropType
= CropCenter
;
244 cropType
= CropRight
;
251 if (cropType
!= mCropType
) {
253 mCropType
= cropType
;
257 if (aAttribute
== nullptr || aAttribute
== nsGkAtoms::value
) {
259 doUpdateTitle
= true;
262 if (aAttribute
== nullptr || aAttribute
== nsGkAtoms::accesskey
) {
263 mNeedsReflowCallback
= true;
264 // Ensure that layout is refreshed and reflow callback called.
275 class nsDisplayXULTextBox
: public nsDisplayItem
{
277 nsDisplayXULTextBox(nsDisplayListBuilder
* aBuilder
,
278 nsTextBoxFrame
* aFrame
) :
279 nsDisplayItem(aBuilder
, aFrame
),
280 mDisableSubpixelAA(false)
282 MOZ_COUNT_CTOR(nsDisplayXULTextBox
);
284 #ifdef NS_BUILD_REFCNT_LOGGING
285 virtual ~nsDisplayXULTextBox() {
286 MOZ_COUNT_DTOR(nsDisplayXULTextBox
);
290 virtual void Paint(nsDisplayListBuilder
* aBuilder
,
291 nsRenderingContext
* aCtx
) MOZ_OVERRIDE
;
292 virtual nsRect
GetBounds(nsDisplayListBuilder
* aBuilder
,
293 bool* aSnap
) MOZ_OVERRIDE
;
294 NS_DISPLAY_DECL_NAME("XULTextBox", TYPE_XUL_TEXT_BOX
)
296 virtual nsRect
GetComponentAlphaBounds(nsDisplayListBuilder
* aBuilder
) MOZ_OVERRIDE
;
298 virtual void DisableComponentAlpha() MOZ_OVERRIDE
{
299 mDisableSubpixelAA
= true;
302 void PaintTextToContext(nsRenderingContext
* aCtx
,
304 const nscolor
* aColor
);
306 bool mDisableSubpixelAA
;
310 PaintTextShadowCallback(nsRenderingContext
* aCtx
,
311 nsPoint aShadowOffset
,
312 const nscolor
& aShadowColor
,
315 reinterpret_cast<nsDisplayXULTextBox
*>(aData
)->
316 PaintTextToContext(aCtx
, aShadowOffset
, &aShadowColor
);
320 nsDisplayXULTextBox::Paint(nsDisplayListBuilder
* aBuilder
,
321 nsRenderingContext
* aCtx
)
323 gfxContextAutoDisableSubpixelAntialiasing
disable(aCtx
->ThebesContext(),
326 // Paint the text shadow before doing any foreground stuff
327 nsRect drawRect
= static_cast<nsTextBoxFrame
*>(mFrame
)->mTextDrawRect
+
329 nsLayoutUtils::PaintTextShadow(mFrame
, aCtx
,
330 drawRect
, mVisibleRect
,
331 mFrame
->StyleColor()->mColor
,
332 PaintTextShadowCallback
,
335 PaintTextToContext(aCtx
, nsPoint(0, 0), nullptr);
339 nsDisplayXULTextBox::PaintTextToContext(nsRenderingContext
* aCtx
,
341 const nscolor
* aColor
)
343 static_cast<nsTextBoxFrame
*>(mFrame
)->
344 PaintTitle(*aCtx
, mVisibleRect
, ToReferenceFrame() + aOffset
, aColor
);
348 nsDisplayXULTextBox::GetBounds(nsDisplayListBuilder
* aBuilder
, bool* aSnap
) {
350 return mFrame
->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
354 nsDisplayXULTextBox::GetComponentAlphaBounds(nsDisplayListBuilder
* aBuilder
)
356 return static_cast<nsTextBoxFrame
*>(mFrame
)->GetComponentAlphaBounds() +
361 nsTextBoxFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
362 const nsRect
& aDirtyRect
,
363 const nsDisplayListSet
& aLists
)
365 if (!IsVisibleForPainting(aBuilder
))
368 nsLeafBoxFrame::BuildDisplayList(aBuilder
, aDirtyRect
, aLists
);
370 aLists
.Content()->AppendNewToTop(new (aBuilder
)
371 nsDisplayXULTextBox(aBuilder
, this));
375 nsTextBoxFrame::PaintTitle(nsRenderingContext
& aRenderingContext
,
376 const nsRect
& aDirtyRect
,
378 const nscolor
* aOverrideColor
)
380 if (mTitle
.IsEmpty())
383 DrawText(aRenderingContext
, aDirtyRect
, mTextDrawRect
+ aPt
, aOverrideColor
);
387 nsTextBoxFrame::DrawText(nsRenderingContext
& aRenderingContext
,
388 const nsRect
& aDirtyRect
,
389 const nsRect
& aTextRect
,
390 const nscolor
* aOverrideColor
)
392 nsPresContext
* presContext
= PresContext();
393 int32_t appUnitsPerDevPixel
= presContext
->AppUnitsPerDevPixel();
394 DrawTarget
* drawTarget
= aRenderingContext
.GetDrawTarget();
404 // Begin with no decorations
405 uint8_t decorations
= NS_STYLE_TEXT_DECORATION_LINE_NONE
;
406 // A mask of all possible decorations.
407 uint8_t decorMask
= NS_STYLE_TEXT_DECORATION_LINE_LINES_MASK
;
409 bool vertical
= GetWritingMode().IsVertical();
412 do { // find decoration colors
413 nsStyleContext
* context
= f
->StyleContext();
414 if (!context
->HasTextDecorationLines()) {
417 const nsStyleTextReset
* styleText
= context
->StyleTextReset();
419 if (decorMask
& styleText
->mTextDecorationLine
) { // a decoration defined here
421 if (aOverrideColor
) {
422 color
= *aOverrideColor
;
425 styleText
->GetDecorationColor(color
, isForeground
);
427 color
= nsLayoutUtils::GetColor(f
, eCSSProperty_color
);
430 uint8_t style
= styleText
->GetDecorationStyle();
432 if (NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE
& decorMask
&
433 styleText
->mTextDecorationLine
) {
436 decorMask
&= ~NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE
;
437 decorations
|= NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE
;
439 if (NS_STYLE_TEXT_DECORATION_LINE_OVERLINE
& decorMask
&
440 styleText
->mTextDecorationLine
) {
443 decorMask
&= ~NS_STYLE_TEXT_DECORATION_LINE_OVERLINE
;
444 decorations
|= NS_STYLE_TEXT_DECORATION_LINE_OVERLINE
;
446 if (NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH
& decorMask
&
447 styleText
->mTextDecorationLine
) {
450 decorMask
&= ~NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH
;
451 decorations
|= NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH
;
454 } while (0 != decorMask
&&
455 (f
= nsLayoutUtils::GetParentOrPlaceholderFor(f
)));
457 nsRefPtr
<nsFontMetrics
> fontMet
;
458 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet
));
462 nscoord ascent
= fontMet
->MaxAscent();
465 presContext
->RoundAppUnitsToNearestDevPixels(aTextRect
.y
+ ascent
);
466 nsRefPtr
<gfxContext
> ctx
= aRenderingContext
.ThebesContext();
467 gfxPoint
pt(presContext
->AppUnitsToGfxUnits(aTextRect
.x
),
468 presContext
->AppUnitsToGfxUnits(aTextRect
.y
));
469 gfxFloat width
= presContext
->AppUnitsToGfxUnits(aTextRect
.width
);
470 gfxFloat ascentPixel
= presContext
->AppUnitsToGfxUnits(ascent
);
471 Float xInFrame
= Float(PresContext()->AppUnitsToGfxUnits(mTextDrawRect
.x
));
472 gfxRect
dirtyRect(presContext
->AppUnitsToGfxUnits(aDirtyRect
));
474 // Underlines are drawn before overlines, and both before the text
475 // itself, per http://www.w3.org/TR/CSS21/zindex.html point 7.2.1.4.1.1.
476 // (We don't apply this rule to the access-key underline because we only
477 // find out where that is as a side effect of drawing the text, in the
478 // general case -- see below.)
479 if (decorations
& (NS_FONT_DECORATION_OVERLINE
|
480 NS_FONT_DECORATION_UNDERLINE
)) {
481 fontMet
->GetUnderline(offset
, size
);
482 gfxFloat offsetPixel
= presContext
->AppUnitsToGfxUnits(offset
);
483 gfxFloat sizePixel
= presContext
->AppUnitsToGfxUnits(size
);
484 if ((decorations
& NS_FONT_DECORATION_UNDERLINE
) &&
485 underStyle
!= NS_STYLE_TEXT_DECORATION_STYLE_NONE
) {
486 nsCSSRendering::PaintDecorationLine(this, *drawTarget
,
487 ToRect(dirtyRect
), underColor
,
488 pt
, xInFrame
, gfxSize(width
, sizePixel
),
489 ascentPixel
, offsetPixel
,
490 NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE
, underStyle
,
493 if ((decorations
& NS_FONT_DECORATION_OVERLINE
) &&
494 overStyle
!= NS_STYLE_TEXT_DECORATION_STYLE_NONE
) {
495 nsCSSRendering::PaintDecorationLine(this, *drawTarget
,
496 ToRect(dirtyRect
), overColor
,
497 pt
, xInFrame
, gfxSize(width
, sizePixel
),
498 ascentPixel
, ascentPixel
,
499 NS_STYLE_TEXT_DECORATION_LINE_OVERLINE
, overStyle
,
504 nsRenderingContext
refContext(
505 PresContext()->PresShell()->CreateReferenceRenderingContext());
507 CalculateUnderline(refContext
, *fontMet
);
509 nscolor c
= aOverrideColor
? *aOverrideColor
: StyleColor()->mColor
;
510 ColorPattern
color(ToDeviceColor(c
));
511 aRenderingContext
.ThebesContext()->SetColor(c
);
513 nsresult rv
= NS_ERROR_FAILURE
;
515 if (mState
& NS_FRAME_IS_BIDI
) {
516 presContext
->SetBidiEnabled();
517 nsBidiLevel level
= nsBidiPresUtils::BidiLevelFromStyle(StyleContext());
518 if (mAccessKeyInfo
&& mAccessKeyInfo
->mAccesskeyIndex
!= kNotFound
) {
519 // We let the RenderText function calculate the mnemonic's
520 // underline position for us.
521 nsBidiPositionResolve posResolve
;
522 posResolve
.logicalIndex
= mAccessKeyInfo
->mAccesskeyIndex
;
523 rv
= nsBidiPresUtils::RenderText(mCroppedTitle
.get(), mCroppedTitle
.Length(), level
,
524 presContext
, aRenderingContext
,
525 refContext
, *fontMet
,
526 aTextRect
.x
, baseline
,
529 mAccessKeyInfo
->mBeforeWidth
= posResolve
.visualLeftTwips
;
530 mAccessKeyInfo
->mAccessWidth
= posResolve
.visualWidth
;
534 rv
= nsBidiPresUtils::RenderText(mCroppedTitle
.get(), mCroppedTitle
.Length(), level
,
535 presContext
, aRenderingContext
,
536 refContext
, *fontMet
,
537 aTextRect
.x
, baseline
);
541 fontMet
->SetTextRunRTL(false);
543 if (mAccessKeyInfo
&& mAccessKeyInfo
->mAccesskeyIndex
!= kNotFound
) {
544 // In the simple (non-BiDi) case, we calculate the mnemonic's
545 // underline position by getting the text metric.
546 // XXX are attribute values always two byte?
547 if (mAccessKeyInfo
->mAccesskeyIndex
> 0)
548 mAccessKeyInfo
->mBeforeWidth
= nsLayoutUtils::
549 AppUnitWidthOfString(mCroppedTitle
.get(),
550 mAccessKeyInfo
->mAccesskeyIndex
,
551 *fontMet
, refContext
);
553 mAccessKeyInfo
->mBeforeWidth
= 0;
556 fontMet
->DrawString(mCroppedTitle
.get(), mCroppedTitle
.Length(),
557 aTextRect
.x
, baseline
, &aRenderingContext
,
561 if (mAccessKeyInfo
&& mAccessKeyInfo
->mAccesskeyIndex
!= kNotFound
) {
562 nsRect
r(aTextRect
.x
+ mAccessKeyInfo
->mBeforeWidth
,
563 aTextRect
.y
+ mAccessKeyInfo
->mAccessOffset
,
564 mAccessKeyInfo
->mAccessWidth
,
565 mAccessKeyInfo
->mAccessUnderlineSize
);
567 NSRectToSnappedRect(r
, appUnitsPerDevPixel
, *drawTarget
);
568 drawTarget
->FillRect(devPxRect
, color
);
571 // Strikeout is drawn on top of the text, per
572 // http://www.w3.org/TR/CSS21/zindex.html point 7.2.1.4.1.1.
573 if ((decorations
& NS_FONT_DECORATION_LINE_THROUGH
) &&
574 strikeStyle
!= NS_STYLE_TEXT_DECORATION_STYLE_NONE
) {
575 fontMet
->GetStrikeout(offset
, size
);
576 gfxFloat offsetPixel
= presContext
->AppUnitsToGfxUnits(offset
);
577 gfxFloat sizePixel
= presContext
->AppUnitsToGfxUnits(size
);
578 nsCSSRendering::PaintDecorationLine(this, *drawTarget
, ToRect(dirtyRect
),
580 pt
, xInFrame
, gfxSize(width
, sizePixel
), ascentPixel
,
581 offsetPixel
, NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH
,
582 strikeStyle
, vertical
);
587 nsTextBoxFrame::CalculateUnderline(nsRenderingContext
& aRenderingContext
,
588 nsFontMetrics
& aFontMetrics
)
590 if (mAccessKeyInfo
&& mAccessKeyInfo
->mAccesskeyIndex
!= kNotFound
) {
591 // Calculate all fields of mAccessKeyInfo which
592 // are the same for both BiDi and non-BiDi frames.
593 const char16_t
*titleString
= mCroppedTitle
.get();
594 aFontMetrics
.SetTextRunRTL(false);
595 mAccessKeyInfo
->mAccessWidth
= nsLayoutUtils::
596 AppUnitWidthOfString(titleString
[mAccessKeyInfo
->mAccesskeyIndex
],
597 aFontMetrics
, aRenderingContext
);
599 nscoord offset
, baseline
;
600 aFontMetrics
.GetUnderline(offset
, mAccessKeyInfo
->mAccessUnderlineSize
);
601 baseline
= aFontMetrics
.MaxAscent();
602 mAccessKeyInfo
->mAccessOffset
= baseline
- offset
;
607 nsTextBoxFrame::CalculateTitleForWidth(nsPresContext
* aPresContext
,
608 nsRenderingContext
& aRenderingContext
,
611 if (mTitle
.IsEmpty()) {
612 mCroppedTitle
.Truncate();
616 nsRefPtr
<nsFontMetrics
> fm
;
617 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm
));
619 // see if the text will completely fit in the width given
621 nsLayoutUtils::AppUnitWidthOfStringBidi(mTitle
, this, *fm
,
623 if (titleWidth
<= aWidth
) {
624 mCroppedTitle
= mTitle
;
625 if (HasRTLChars(mTitle
)) {
626 mState
|= NS_FRAME_IS_BIDI
;
628 return titleWidth
; // fits, done.
631 const nsDependentString
& kEllipsis
= nsContentUtils::GetLocalizedEllipsis();
632 // start with an ellipsis
633 mCroppedTitle
.Assign(kEllipsis
);
635 // see if the width is even smaller than the ellipsis
636 // if so, clear the text (XXX set as many '.' as we can?).
637 fm
->SetTextRunRTL(false);
638 titleWidth
= nsLayoutUtils::AppUnitWidthOfString(kEllipsis
, *fm
,
641 if (titleWidth
> aWidth
) {
642 mCroppedTitle
.SetLength(0);
646 // if the ellipsis fits perfectly, no use in trying to insert
647 if (titleWidth
== aWidth
)
650 aWidth
-= titleWidth
;
652 // XXX: This whole block should probably take surrogates into account
662 int length
= mTitle
.Length();
664 for (i
= 0; i
< length
; ++i
) {
665 char16_t ch
= mTitle
.CharAt(i
);
667 cwidth
= nsLayoutUtils::AppUnitWidthOfString(ch
, *fm
,
669 if (twidth
+ cwidth
> aWidth
)
673 if (UCS2_CHAR_IS_BIDI(ch
) ) {
674 mState
|= NS_FRAME_IS_BIDI
;
681 // insert what character we can in.
682 nsAutoString
title( mTitle
);
684 mCroppedTitle
.Insert(title
, 0);
692 int length
= mTitle
.Length();
694 for (i
=length
-1; i
>= 0; --i
) {
695 char16_t ch
= mTitle
.CharAt(i
);
696 cwidth
= nsLayoutUtils::AppUnitWidthOfString(ch
, *fm
,
698 if (twidth
+ cwidth
> aWidth
)
702 if (UCS2_CHAR_IS_BIDI(ch
) ) {
703 mState
|= NS_FRAME_IS_BIDI
;
711 mTitle
.Right(copy
, length
-1-i
);
712 mCroppedTitle
+= copy
;
718 nscoord stringWidth
=
719 nsLayoutUtils::AppUnitWidthOfStringBidi(mTitle
, this, *fm
,
721 if (stringWidth
<= aWidth
) {
722 // the entire string will fit in the maximum width
723 mCroppedTitle
.Insert(mTitle
, 0);
727 // determine how much of the string will fit in the max width
728 nscoord charWidth
= 0;
729 nscoord totalWidth
= 0;
731 int leftPos
, rightPos
;
732 nsAutoString leftString
, rightString
;
734 rightPos
= mTitle
.Length() - 1;
735 fm
->SetTextRunRTL(false);
736 for (leftPos
= 0; leftPos
<= rightPos
;) {
737 // look at the next character on the left end
738 ch
= mTitle
.CharAt(leftPos
);
739 charWidth
= nsLayoutUtils::AppUnitWidthOfString(ch
, *fm
,
741 totalWidth
+= charWidth
;
742 if (totalWidth
> aWidth
)
743 // greater than the allowable width
745 leftString
.Insert(ch
, leftString
.Length());
747 if (UCS2_CHAR_IS_BIDI(ch
))
748 mState
|= NS_FRAME_IS_BIDI
;
750 // look at the next character on the right end
751 if (rightPos
> leftPos
) {
752 // haven't looked at this character yet
753 ch
= mTitle
.CharAt(rightPos
);
755 nsLayoutUtils::AppUnitWidthOfString(ch
, *fm
,
757 totalWidth
+= charWidth
;
758 if (totalWidth
> aWidth
)
759 // greater than the allowable width
761 rightString
.Insert(ch
, 0);
763 if (UCS2_CHAR_IS_BIDI(ch
))
764 mState
|= NS_FRAME_IS_BIDI
;
767 // look at the next two characters
772 mCroppedTitle
= leftString
+ kEllipsis
+ rightString
;
777 return nsLayoutUtils::AppUnitWidthOfStringBidi(mCroppedTitle
, this, *fm
,
781 #define OLD_ELLIPSIS NS_LITERAL_STRING("...")
783 // the following block is to append the accesskey to mTitle if there is an accesskey
784 // but the mTitle doesn't have the character
786 nsTextBoxFrame::UpdateAccessTitle()
789 * Note that if you change appending access key label spec,
790 * you need to maintain same logic in following methods. See bug 324159.
791 * toolkit/content/commonDialog.js (setLabelForNode)
792 * toolkit/content/widgets/text.xml (formatAccessKey)
794 int32_t menuAccessKey
;
795 nsMenuBarListener::GetMenuAccessKey(&menuAccessKey
);
796 if (!menuAccessKey
|| mAccessKey
.IsEmpty())
799 if (!AlwaysAppendAccessKey() &&
800 FindInReadable(mAccessKey
, mTitle
, nsCaseInsensitiveStringComparator()))
803 nsAutoString accessKeyLabel
;
804 accessKeyLabel
+= '(';
805 accessKeyLabel
+= mAccessKey
;
806 ToUpperCase(accessKeyLabel
);
807 accessKeyLabel
+= ')';
809 if (mTitle
.IsEmpty()) {
810 mTitle
= accessKeyLabel
;
814 const nsDependentString
& kEllipsis
= nsContentUtils::GetLocalizedEllipsis();
815 uint32_t offset
= mTitle
.Length();
816 if (StringEndsWith(mTitle
, kEllipsis
)) {
817 offset
-= kEllipsis
.Length();
818 } else if (StringEndsWith(mTitle
, OLD_ELLIPSIS
)) {
819 // Try to check with our old ellipsis (for old addons)
820 offset
-= OLD_ELLIPSIS
.Length();
823 // our default ellipsis (for non-localized addons) or ':'
824 const char16_t kLastChar
= mTitle
.Last();
825 if (kLastChar
== char16_t(0x2026) || kLastChar
== char16_t(':'))
829 if (InsertSeparatorBeforeAccessKey() &&
830 offset
> 0 && !NS_IS_SPACE(mTitle
[offset
- 1])) {
831 mTitle
.Insert(' ', offset
);
835 mTitle
.Insert(accessKeyLabel
, offset
);
839 nsTextBoxFrame::UpdateAccessIndex()
841 int32_t menuAccessKey
;
842 nsMenuBarListener::GetMenuAccessKey(&menuAccessKey
);
844 if (mAccessKey
.IsEmpty()) {
845 if (mAccessKeyInfo
) {
846 delete mAccessKeyInfo
;
847 mAccessKeyInfo
= nullptr;
850 if (!mAccessKeyInfo
) {
851 mAccessKeyInfo
= new nsAccessKeyInfo();
856 nsAString::const_iterator start
, end
;
858 mCroppedTitle
.BeginReading(start
);
859 mCroppedTitle
.EndReading(end
);
861 // remember the beginning of the string
862 nsAString::const_iterator originalStart
= start
;
865 if (!AlwaysAppendAccessKey()) {
866 // not appending access key - do case-sensitive search
868 found
= FindInReadable(mAccessKey
, start
, end
);
870 // didn't find it - perform a case-insensitive search
871 start
= originalStart
;
872 found
= FindInReadable(mAccessKey
, start
, end
,
873 nsCaseInsensitiveStringComparator());
876 found
= RFindInReadable(mAccessKey
, start
, end
,
877 nsCaseInsensitiveStringComparator());
881 mAccessKeyInfo
->mAccesskeyIndex
= Distance(originalStart
, start
);
883 mAccessKeyInfo
->mAccesskeyIndex
= kNotFound
;
889 nsTextBoxFrame::RecomputeTitle()
891 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::value
, mTitle
);
893 // This doesn't handle language-specific uppercasing/lowercasing
894 // rules, unlike textruns.
895 uint8_t textTransform
= StyleText()->mTextTransform
;
896 if (textTransform
== NS_STYLE_TEXT_TRANSFORM_UPPERCASE
) {
898 } else if (textTransform
== NS_STYLE_TEXT_TRANSFORM_LOWERCASE
) {
901 // We can't handle NS_STYLE_TEXT_TRANSFORM_CAPITALIZE because we
902 // have no clue about word boundaries here. We also don't handle
903 // NS_STYLE_TEXT_TRANSFORM_FULLWIDTH.
907 nsTextBoxFrame::DidSetStyleContext(nsStyleContext
* aOldStyleContext
)
909 if (!aOldStyleContext
) {
910 // We're just being initialized
914 const nsStyleText
* oldTextStyle
= aOldStyleContext
->PeekStyleText();
915 // We should really have oldTextStyle here, since we asked for our
916 // nsStyleText during Init(), but if it's not there for some reason
917 // just assume the worst and recompute mTitle.
919 oldTextStyle
->mTextTransform
!= StyleText()->mTextTransform
) {
926 nsTextBoxFrame::DoLayout(nsBoxLayoutState
& aBoxLayoutState
)
928 if (mNeedsReflowCallback
) {
929 nsIReflowCallback
* cb
= new nsAsyncAccesskeyUpdate(this);
931 PresContext()->PresShell()->PostReflowCallback(cb
);
933 mNeedsReflowCallback
= false;
936 nsresult rv
= nsLeafBoxFrame::DoLayout(aBoxLayoutState
);
938 CalcDrawRect(*aBoxLayoutState
.GetRenderingContext());
940 const nsStyleText
* textStyle
= StyleText();
942 nsRect
scrollBounds(nsPoint(0, 0), GetSize());
943 nsRect textRect
= mTextDrawRect
;
945 nsRefPtr
<nsFontMetrics
> fontMet
;
946 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet
));
947 nsBoundingMetrics metrics
=
948 fontMet
->GetInkBoundsForVisualOverflow(mCroppedTitle
.get(),
949 mCroppedTitle
.Length(),
950 aBoxLayoutState
.GetRenderingContext());
952 textRect
.x
-= metrics
.leftBearing
;
953 textRect
.width
= metrics
.width
;
954 // In DrawText() we always draw with the baseline at MaxAscent() (relative to mTextDrawRect),
955 textRect
.y
+= fontMet
->MaxAscent() - metrics
.ascent
;
956 textRect
.height
= metrics
.ascent
+ metrics
.descent
;
958 // Our scrollable overflow is our bounds; our visual overflow may
959 // extend beyond that.
961 visualBounds
.UnionRect(scrollBounds
, textRect
);
962 nsOverflowAreas
overflow(visualBounds
, scrollBounds
);
964 if (textStyle
->mTextShadow
) {
965 // text-shadow extends our visual but not scrollable bounds
966 nsRect
&vis
= overflow
.VisualOverflow();
967 vis
.UnionRect(vis
, nsLayoutUtils::GetTextShadowRectsUnion(mTextDrawRect
, this));
969 FinishAndStoreOverflow(overflow
, GetSize());
975 nsTextBoxFrame::GetComponentAlphaBounds()
977 if (StyleText()->mTextShadow
) {
978 return GetVisualOverflowRectRelativeToSelf();
980 return mTextDrawRect
;
984 nsTextBoxFrame::ComputesOwnOverflowArea()
990 nsTextBoxFrame::MarkIntrinsicISizesDirty()
993 nsTextBoxFrameSuper::MarkIntrinsicISizesDirty();
997 nsTextBoxFrame::GetTextSize(nsPresContext
* aPresContext
,
998 nsRenderingContext
& aRenderingContext
,
999 const nsString
& aString
,
1000 nsSize
& aSize
, nscoord
& aAscent
)
1002 nsRefPtr
<nsFontMetrics
> fontMet
;
1003 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet
));
1004 aSize
.height
= fontMet
->MaxHeight();
1006 nsLayoutUtils::AppUnitWidthOfStringBidi(aString
, this, *fontMet
,
1008 aAscent
= fontMet
->MaxAscent();
1012 nsTextBoxFrame::CalcTextSize(nsBoxLayoutState
& aBoxLayoutState
)
1017 nsPresContext
* presContext
= aBoxLayoutState
.PresContext();
1018 nsRenderingContext
* rendContext
= aBoxLayoutState
.GetRenderingContext();
1020 GetTextSize(presContext
, *rendContext
,
1021 mTitle
, size
, mAscent
);
1023 mNeedsRecalc
= false;
1029 nsTextBoxFrame::CalcDrawRect(nsRenderingContext
&aRenderingContext
)
1031 nsRect
textRect(nsPoint(0, 0), GetSize());
1032 nsMargin borderPadding
;
1033 GetBorderAndPadding(borderPadding
);
1034 textRect
.Deflate(borderPadding
);
1036 // determine (cropped) title and underline position
1037 nsPresContext
* presContext
= PresContext();
1038 // determine (cropped) title which fits in aRect.width and its width
1039 nscoord titleWidth
=
1040 CalculateTitleForWidth(presContext
, aRenderingContext
, textRect
.width
);
1042 #ifdef ACCESSIBILITY
1043 // Make sure to update the accessible tree in case when cropped title is
1045 nsAccessibilityService
* accService
= GetAccService();
1047 accService
->UpdateLabelValue(PresContext()->PresShell(), mContent
,
1052 // determine if and at which position to put the underline
1053 UpdateAccessIndex();
1055 // make the rect as small as our (cropped) text.
1056 nscoord outerWidth
= textRect
.width
;
1057 textRect
.width
= titleWidth
;
1059 // Align our text within the overall rect by checking our text-align property.
1060 const nsStyleVisibility
* vis
= StyleVisibility();
1061 const nsStyleText
* textStyle
= StyleText();
1063 if (textStyle
->mTextAlign
== NS_STYLE_TEXT_ALIGN_CENTER
)
1064 textRect
.x
+= (outerWidth
- textRect
.width
)/2;
1065 else if (textStyle
->mTextAlign
== NS_STYLE_TEXT_ALIGN_RIGHT
||
1066 (textStyle
->mTextAlign
== NS_STYLE_TEXT_ALIGN_DEFAULT
&&
1067 vis
->mDirection
== NS_STYLE_DIRECTION_RTL
) ||
1068 (textStyle
->mTextAlign
== NS_STYLE_TEXT_ALIGN_END
&&
1069 vis
->mDirection
== NS_STYLE_DIRECTION_LTR
)) {
1070 textRect
.x
+= (outerWidth
- textRect
.width
);
1073 mTextDrawRect
= textRect
;
1077 * Ok return our dimensions
1080 nsTextBoxFrame::GetPrefSize(nsBoxLayoutState
& aBoxLayoutState
)
1082 CalcTextSize(aBoxLayoutState
);
1084 nsSize size
= mTextSize
;
1085 DISPLAY_PREF_SIZE(this, size
);
1087 AddBorderAndPadding(size
);
1088 bool widthSet
, heightSet
;
1089 nsIFrame::AddCSSPrefSize(this, size
, widthSet
, heightSet
);
1095 * Ok return our dimensions
1098 nsTextBoxFrame::GetMinSize(nsBoxLayoutState
& aBoxLayoutState
)
1100 CalcTextSize(aBoxLayoutState
);
1102 nsSize size
= mTextSize
;
1103 DISPLAY_MIN_SIZE(this, size
);
1105 // if there is cropping our min width becomes our border and padding
1106 if (mCropType
!= CropNone
)
1109 AddBorderAndPadding(size
);
1110 bool widthSet
, heightSet
;
1111 nsIFrame::AddCSSMinSize(aBoxLayoutState
, this, size
, widthSet
, heightSet
);
1117 nsTextBoxFrame::GetBoxAscent(nsBoxLayoutState
& aBoxLayoutState
)
1119 CalcTextSize(aBoxLayoutState
);
1121 nscoord ascent
= mAscent
;
1123 nsMargin
m(0,0,0,0);
1124 GetBorderAndPadding(m
);
1130 #ifdef DEBUG_FRAME_DUMP
1132 nsTextBoxFrame::GetFrameName(nsAString
& aResult
) const
1134 MakeFrameName(NS_LITERAL_STRING("TextBox"), aResult
);
1135 aResult
+= NS_LITERAL_STRING("[value=") + mTitle
+ NS_LITERAL_STRING("]");
1140 // If you make changes to this function, check its counterparts
1141 // in nsBoxFrame and nsXULLabelFrame
1143 nsTextBoxFrame::RegUnregAccessKey(bool aDoReg
)
1145 // if we have no content, we can't do anything
1147 return NS_ERROR_FAILURE
;
1149 // check if we have a |control| attribute
1150 // do this check first because few elements have control attributes, and we
1151 // can weed out most of the elements quickly.
1153 // XXXjag a side-effect is that we filter out anonymous <label>s
1154 // in e.g. <menu>, <menuitem>, <button>. These <label>s inherit
1155 // |accesskey| and would otherwise register themselves, overwriting
1156 // the content we really meant to be registered.
1157 if (!mContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::control
))
1160 // see if we even have an access key
1161 nsAutoString accessKey
;
1162 mContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::accesskey
, accessKey
);
1164 if (accessKey
.IsEmpty())
1167 // With a valid PresContext we can get the ESM
1168 // and (un)register the access key
1169 EventStateManager
* esm
= PresContext()->EventStateManager();
1171 uint32_t key
= accessKey
.First();
1173 esm
->RegisterAccessKey(mContent
, key
);
1175 esm
->UnregisterAccessKey(mContent
, key
);