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