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