layout/xul/nsTextBoxFrame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial