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