Coverage Report

Created: 2018-09-25 14:53

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