Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / base / nsCaret.cpp
blob263c3be284b242ca3f394288475d46c8aa84352f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Mats Palmgren <mats.palmgren@bredband.net>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
41 /* the caret is the text cursor used, e.g., when editing */
43 #include "nsCOMPtr.h"
45 #include "nsITimer.h"
47 #include "nsIComponentManager.h"
48 #include "nsIServiceManager.h"
49 #include "nsFrameSelection.h"
50 #include "nsIFrame.h"
51 #include "nsIScrollableFrame.h"
52 #include "nsIDOMNode.h"
53 #include "nsIDOMRange.h"
54 #include "nsIFontMetrics.h"
55 #include "nsISelection.h"
56 #include "nsISelectionPrivate.h"
57 #include "nsIDOMCharacterData.h"
58 #include "nsIContent.h"
59 #include "nsIPresShell.h"
60 #include "nsIRenderingContext.h"
61 #include "nsIDeviceContext.h"
62 #include "nsIView.h"
63 #include "nsIScrollableView.h"
64 #include "nsIViewManager.h"
65 #include "nsPresContext.h"
66 #include "nsILookAndFeel.h"
67 #include "nsBlockFrame.h"
68 #include "nsISelectionController.h"
69 #include "nsDisplayList.h"
70 #include "nsCaret.h"
71 #include "nsTextFrame.h"
72 #include "nsXULPopupManager.h"
73 #include "nsMenuPopupFrame.h"
74 #include "nsTextFragment.h"
75 #include "nsThemeConstants.h"
77 // The bidi indicator hangs off the caret to one side, to show which
78 // direction the typing is in. It needs to be at least 2x2 to avoid looking like
79 // an insignificant dot
80 static const PRInt32 kMinBidiIndicatorPixels = 2;
82 #ifdef IBMBIDI
83 #include "nsIBidiKeyboard.h"
84 #include "nsContentUtils.h"
85 #endif //IBMBIDI
87 //-----------------------------------------------------------------------------
89 nsCaret::nsCaret()
90 : mPresShell(nsnull)
91 , mBlinkRate(500)
92 , mVisible(PR_FALSE)
93 , mDrawn(PR_FALSE)
94 , mReadOnly(PR_FALSE)
95 , mShowDuringSelection(PR_FALSE)
96 , mLastContentOffset(0)
97 , mLastHint(nsFrameSelection::HINTLEFT)
98 , mIgnoreUserModify(PR_TRUE)
99 #ifdef IBMBIDI
100 , mLastBidiLevel(0)
101 , mKeyboardRTL(PR_FALSE)
102 #endif
106 //-----------------------------------------------------------------------------
107 nsCaret::~nsCaret()
109 KillTimer();
112 //-----------------------------------------------------------------------------
113 nsresult nsCaret::Init(nsIPresShell *inPresShell)
115 NS_ENSURE_ARG(inPresShell);
117 mPresShell = do_GetWeakReference(inPresShell); // the presshell owns us, so no addref
118 NS_ASSERTION(mPresShell, "Hey, pres shell should support weak refs");
120 // get nsILookAndFeel from the pres context, which has one cached.
121 nsILookAndFeel *lookAndFeel = nsnull;
122 nsPresContext *presContext = inPresShell->GetPresContext();
124 // XXX we should just do this nsILookAndFeel consultation every time
125 // we need these values.
126 mCaretWidthCSSPx = 1;
127 mCaretAspectRatio = 0;
128 if (presContext && (lookAndFeel = presContext->LookAndFeel())) {
129 PRInt32 tempInt;
130 float tempFloat;
131 if (NS_SUCCEEDED(lookAndFeel->GetMetric(nsILookAndFeel::eMetric_CaretWidth, tempInt)))
132 mCaretWidthCSSPx = (nscoord)tempInt;
133 if (NS_SUCCEEDED(lookAndFeel->GetMetric(nsILookAndFeel::eMetricFloat_CaretAspectRatio, tempFloat)))
134 mCaretAspectRatio = tempFloat;
135 if (NS_SUCCEEDED(lookAndFeel->GetMetric(nsILookAndFeel::eMetric_CaretBlinkTime, tempInt)))
136 mBlinkRate = (PRUint32)tempInt;
137 if (NS_SUCCEEDED(lookAndFeel->GetMetric(nsILookAndFeel::eMetric_ShowCaretDuringSelection, tempInt)))
138 mShowDuringSelection = tempInt ? PR_TRUE : PR_FALSE;
141 // get the selection from the pres shell, and set ourselves up as a selection
142 // listener
144 nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mPresShell);
145 if (!selCon)
146 return NS_ERROR_FAILURE;
148 nsCOMPtr<nsISelection> domSelection;
149 nsresult rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
150 getter_AddRefs(domSelection));
151 if (NS_FAILED(rv))
152 return rv;
153 if (!domSelection)
154 return NS_ERROR_FAILURE;
156 nsCOMPtr<nsISelectionPrivate> privateSelection = do_QueryInterface(domSelection);
157 if (privateSelection)
158 privateSelection->AddSelectionListener(this);
159 mDomSelectionWeak = do_GetWeakReference(domSelection);
161 // set up the blink timer
162 if (mVisible)
164 StartBlinking();
166 #ifdef IBMBIDI
167 mBidiUI = nsContentUtils::GetBoolPref("bidi.browser.ui");
168 #endif
170 return NS_OK;
173 static PRBool
174 DrawCJKCaret(nsIFrame* aFrame, PRInt32 aOffset)
176 nsIContent* content = aFrame->GetContent();
177 const nsTextFragment* frag = content->GetText();
178 if (!frag)
179 return PR_FALSE;
180 if (aOffset < 0 || aOffset >= frag->GetLength())
181 return PR_FALSE;
182 PRUnichar ch = frag->CharAt(aOffset);
183 return 0x2e80 <= ch && ch <= 0xd7ff;
186 nsCaret::Metrics nsCaret::ComputeMetrics(nsIFrame* aFrame, PRInt32 aOffset, nscoord aCaretHeight)
188 // Compute nominal sizes in appunits
189 nscoord caretWidth = (aCaretHeight * mCaretAspectRatio) +
190 nsPresContext::CSSPixelsToAppUnits(mCaretWidthCSSPx);
192 if (DrawCJKCaret(aFrame, aOffset)) {
193 caretWidth += nsPresContext::CSSPixelsToAppUnits(1);
195 nscoord bidiIndicatorSize = nsPresContext::CSSPixelsToAppUnits(kMinBidiIndicatorPixels);
196 bidiIndicatorSize = PR_MAX(caretWidth, bidiIndicatorSize);
198 // Round them to device pixels. Always round down, except that anything
199 // between 0 and 1 goes up to 1 so we don't let the caret disappear.
200 PRUint32 tpp = aFrame->PresContext()->AppUnitsPerDevPixel();
201 Metrics result;
202 result.mCaretWidth = NS_ROUND_BORDER_TO_PIXELS(caretWidth, tpp);
203 result.mBidiIndicatorSize = NS_ROUND_BORDER_TO_PIXELS(bidiIndicatorSize, tpp);
204 return result;
207 //-----------------------------------------------------------------------------
208 void nsCaret::Terminate()
210 // this doesn't erase the caret if it's drawn. Should it? We might not have
211 // a good drawing environment during teardown.
213 KillTimer();
214 mBlinkTimer = nsnull;
216 // unregiser ourselves as a selection listener
217 nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
218 nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSelection));
219 if (privateSelection)
220 privateSelection->RemoveSelectionListener(this);
221 mDomSelectionWeak = nsnull;
222 mPresShell = nsnull;
224 mLastContent = nsnull;
227 //-----------------------------------------------------------------------------
228 NS_IMPL_ISUPPORTS1(nsCaret, nsISelectionListener)
230 //-----------------------------------------------------------------------------
231 nsISelection* nsCaret::GetCaretDOMSelection()
233 nsCOMPtr<nsISelection> sel(do_QueryReferent(mDomSelectionWeak));
234 return sel;
237 //-----------------------------------------------------------------------------
238 nsresult nsCaret::SetCaretDOMSelection(nsISelection *aDOMSel)
240 NS_ENSURE_ARG_POINTER(aDOMSel);
241 mDomSelectionWeak = do_GetWeakReference(aDOMSel); // weak reference to pres shell
242 if (mVisible)
244 // Stop the caret from blinking in its previous location.
245 StopBlinking();
246 // Start the caret blinking in the new location.
247 StartBlinking();
249 return NS_OK;
253 //-----------------------------------------------------------------------------
254 void nsCaret::SetCaretVisible(PRBool inMakeVisible)
256 mVisible = inMakeVisible;
257 if (mVisible) {
258 StartBlinking();
259 SetIgnoreUserModify(PR_TRUE);
260 } else {
261 StopBlinking();
262 SetIgnoreUserModify(PR_FALSE);
267 //-----------------------------------------------------------------------------
268 nsresult nsCaret::GetCaretVisible(PRBool *outMakeVisible)
270 NS_ENSURE_ARG_POINTER(outMakeVisible);
271 *outMakeVisible = (mVisible && MustDrawCaret(PR_TRUE));
272 return NS_OK;
276 //-----------------------------------------------------------------------------
277 void nsCaret::SetCaretReadOnly(PRBool inMakeReadonly)
279 mReadOnly = inMakeReadonly;
283 //-----------------------------------------------------------------------------
284 nsresult nsCaret::GetCaretCoordinates(EViewCoordinates aRelativeToType,
285 nsISelection *aDOMSel,
286 nsRect *outCoordinates,
287 PRBool *outIsCollapsed,
288 nsIView **outView)
290 if (!mPresShell)
291 return NS_ERROR_NOT_INITIALIZED;
292 if (!outCoordinates || !outIsCollapsed)
293 return NS_ERROR_NULL_POINTER;
295 nsCOMPtr<nsISelection> domSelection = aDOMSel;
297 if (outView)
298 *outView = nsnull;
300 // fill in defaults for failure
301 outCoordinates->x = -1;
302 outCoordinates->y = -1;
303 outCoordinates->width = -1;
304 outCoordinates->height = -1;
305 *outIsCollapsed = PR_FALSE;
307 nsresult err = domSelection->GetIsCollapsed(outIsCollapsed);
308 if (NS_FAILED(err))
309 return err;
311 nsCOMPtr<nsIDOMNode> focusNode;
313 err = domSelection->GetFocusNode(getter_AddRefs(focusNode));
314 if (NS_FAILED(err))
315 return err;
316 if (!focusNode)
317 return NS_ERROR_FAILURE;
319 PRInt32 focusOffset;
320 err = domSelection->GetFocusOffset(&focusOffset);
321 if (NS_FAILED(err))
322 return err;
324 nsCOMPtr<nsIContent> contentNode = do_QueryInterface(focusNode);
325 if (!contentNode)
326 return NS_ERROR_FAILURE;
328 // find the frame that contains the content node that has focus
329 nsIFrame* theFrame = nsnull;
330 PRInt32 theFrameOffset = 0;
332 nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection();
333 if (!frameSelection)
334 return NS_ERROR_FAILURE;
335 PRUint8 bidiLevel = frameSelection->GetCaretBidiLevel();
337 err = GetCaretFrameForNodeOffset(contentNode, focusOffset,
338 frameSelection->GetHint(), bidiLevel,
339 &theFrame, &theFrameOffset);
340 if (NS_FAILED(err) || !theFrame)
341 return err;
343 nsPoint viewOffset(0, 0);
344 nsIView *drawingView; // views are not refcounted
346 GetViewForRendering(theFrame, aRelativeToType, viewOffset, &drawingView, outView);
347 if (!drawingView)
348 return NS_ERROR_UNEXPECTED;
350 nsPoint framePos(0, 0);
351 err = theFrame->GetPointFromOffset(theFrameOffset, &framePos);
352 if (NS_FAILED(err))
353 return err;
355 // we don't need drawingView anymore so reuse that; reset viewOffset values for our purposes
356 if (aRelativeToType == eClosestViewCoordinates)
358 theFrame->GetOffsetFromView(viewOffset, &drawingView);
359 if (outView)
360 *outView = drawingView;
362 // now add the frame offset to the view offset, and we're done
363 viewOffset += framePos;
364 outCoordinates->x = viewOffset.x;
365 outCoordinates->y = viewOffset.y;
366 outCoordinates->height = theFrame->GetSize().height;
367 outCoordinates->width = ComputeMetrics(theFrame, theFrameOffset, outCoordinates->height).mCaretWidth;
369 return NS_OK;
372 void nsCaret::DrawCaretAfterBriefDelay()
374 // Make sure readonly caret gets drawn again if it needs to be
375 if (!mBlinkTimer) {
376 nsresult err;
377 mBlinkTimer = do_CreateInstance("@mozilla.org/timer;1", &err);
378 if (NS_FAILED(err))
379 return;
382 mBlinkTimer->InitWithFuncCallback(CaretBlinkCallback, this, 0,
383 nsITimer::TYPE_ONE_SHOT);
386 void nsCaret::EraseCaret()
388 if (mDrawn) {
389 DrawCaret(PR_TRUE);
390 if (mReadOnly && mBlinkRate) {
391 // If readonly we don't have a blink timer set, so caret won't
392 // be redrawn automatically. We need to force the caret to get
393 // redrawn right after the paint
394 DrawCaretAfterBriefDelay();
399 void nsCaret::SetVisibilityDuringSelection(PRBool aVisibility)
401 mShowDuringSelection = aVisibility;
404 nsresult nsCaret::DrawAtPosition(nsIDOMNode* aNode, PRInt32 aOffset)
406 NS_ENSURE_ARG(aNode);
408 PRUint8 bidiLevel;
409 nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection();
410 if (!frameSelection)
411 return NS_ERROR_FAILURE;
412 bidiLevel = frameSelection->GetCaretBidiLevel();
414 // DrawAtPosition is used by consumers who want us to stay drawn where they
415 // tell us. Setting mBlinkRate to 0 tells us to not set a timer to erase
416 // ourselves, our consumer will take care of that.
417 mBlinkRate = 0;
419 // XXX we need to do more work here to get the correct hint.
420 nsresult rv = DrawAtPositionWithHint(aNode, aOffset,
421 nsFrameSelection::HINTLEFT,
422 bidiLevel, PR_TRUE)
423 ? NS_OK : NS_ERROR_FAILURE;
424 ToggleDrawnStatus();
425 return rv;
428 nsIFrame * nsCaret::GetCaretFrame()
430 // Return null if we're not drawn to prevent anybody from trying to draw us.
431 if (!mDrawn)
432 return nsnull;
434 // Recompute the frame that we're supposed to draw in to guarantee that
435 // we're not going to try to draw into a stale (dead) frame.
436 PRInt32 unused;
437 nsIFrame *frame = nsnull;
438 nsresult rv = GetCaretFrameForNodeOffset(mLastContent, mLastContentOffset,
439 mLastHint, mLastBidiLevel, &frame,
440 &unused);
441 if (NS_FAILED(rv))
442 return nsnull;
444 return frame;
447 void nsCaret::InvalidateOutsideCaret()
449 nsIFrame *frame = GetCaretFrame();
451 // Only invalidate if we are not fully contained by our frame's rect.
452 if (frame && !frame->GetOverflowRect().Contains(GetCaretRect()))
453 InvalidateRects(mCaretRect, GetHookRect(), frame);
456 void nsCaret::UpdateCaretPosition()
458 // We'll recalculate anyway if we're not drawn right now.
459 if (!mDrawn)
460 return;
462 // A trick! Make the DrawCaret code recalculate the caret's current
463 // position.
464 mDrawn = PR_FALSE;
465 DrawCaret(PR_FALSE);
468 void nsCaret::PaintCaret(nsDisplayListBuilder *aBuilder,
469 nsIRenderingContext *aCtx,
470 nsIFrame* aForFrame,
471 const nsPoint &aOffset)
473 NS_ASSERTION(mDrawn, "The caret shouldn't be drawing");
475 const nsRect drawCaretRect = mCaretRect + aOffset;
476 nscolor cssColor = aForFrame->GetStyleColor()->mColor;
478 // Only draw the native caret if the foreground color matches that of
479 // -moz-fieldtext (the color of the text in a textbox). If it doesn't match
480 // we are likely in contenteditable or a custom widget and we risk being hard to see
481 // against the background. In that case, fall back to the CSS color.
482 nsPresContext* presContext = aForFrame->PresContext();
484 if (GetHookRect().IsEmpty() && presContext) {
485 nsITheme *theme = presContext->GetTheme();
486 if (theme && theme->ThemeSupportsWidget(presContext, aForFrame, NS_THEME_TEXTFIELD_CARET)) {
487 nsILookAndFeel* lookAndFeel = presContext->LookAndFeel();
488 nscolor fieldText;
489 if (NS_SUCCEEDED(lookAndFeel->GetColor(nsILookAndFeel::eColor__moz_fieldtext, fieldText)) &&
490 fieldText == cssColor) {
491 theme->DrawWidgetBackground(aCtx, aForFrame, NS_THEME_TEXTFIELD_CARET,
492 drawCaretRect, drawCaretRect);
493 return;
498 aCtx->SetColor(cssColor);
499 aCtx->FillRect(drawCaretRect);
500 if (!GetHookRect().IsEmpty())
501 aCtx->FillRect(GetHookRect() + aOffset);
505 //-----------------------------------------------------------------------------
506 NS_IMETHODIMP nsCaret::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aDomSel, PRInt16 aReason)
508 if (aReason & nsISelectionListener::MOUSEUP_REASON)//this wont do
509 return NS_OK;
511 nsCOMPtr<nsISelection> domSel(do_QueryReferent(mDomSelectionWeak));
513 // The same caret is shared amongst the document and any text widgets it
514 // may contain. This means that the caret could get notifications from
515 // multiple selections.
517 // If this notification is for a selection that is not the one the
518 // the caret is currently interested in (mDomSelectionWeak), then there
519 // is nothing to do!
521 if (domSel != aDomSel)
522 return NS_OK;
524 if (mVisible)
526 // Stop the caret from blinking in its previous location.
527 StopBlinking();
529 // Start the caret blinking in the new location.
530 StartBlinking();
533 return NS_OK;
537 //-----------------------------------------------------------------------------
538 void nsCaret::KillTimer()
540 if (mBlinkTimer)
542 mBlinkTimer->Cancel();
547 //-----------------------------------------------------------------------------
548 nsresult nsCaret::PrimeTimer()
550 // set up the blink timer
551 if (!mReadOnly && mBlinkRate > 0)
553 if (!mBlinkTimer) {
554 nsresult err;
555 mBlinkTimer = do_CreateInstance("@mozilla.org/timer;1", &err);
556 if (NS_FAILED(err))
557 return err;
560 mBlinkTimer->InitWithFuncCallback(CaretBlinkCallback, this, mBlinkRate,
561 nsITimer::TYPE_REPEATING_SLACK);
564 return NS_OK;
568 //-----------------------------------------------------------------------------
569 void nsCaret::StartBlinking()
571 if (mReadOnly) {
572 // Make sure the one draw command we use for a readonly caret isn't
573 // done until the selection is set
574 DrawCaretAfterBriefDelay();
575 return;
577 PrimeTimer();
579 // If we are currently drawn, then the second call to DrawCaret below will
580 // actually erase the caret. That would cause the caret to spend an "off"
581 // cycle before it appears, which is not really what we want. This first
582 // call to DrawCaret makes sure that the first cycle after a call to
583 // StartBlinking is an "on" cycle.
584 if (mDrawn)
585 DrawCaret(PR_TRUE);
587 DrawCaret(PR_TRUE); // draw it right away
591 //-----------------------------------------------------------------------------
592 void nsCaret::StopBlinking()
594 if (mDrawn) // erase the caret if necessary
595 DrawCaret(PR_TRUE);
597 NS_ASSERTION(!mDrawn, "Caret still drawn after StopBlinking().");
598 KillTimer();
601 PRBool
602 nsCaret::DrawAtPositionWithHint(nsIDOMNode* aNode,
603 PRInt32 aOffset,
604 nsFrameSelection::HINT aFrameHint,
605 PRUint8 aBidiLevel,
606 PRBool aInvalidate)
608 nsCOMPtr<nsIContent> contentNode = do_QueryInterface(aNode);
609 if (!contentNode)
610 return PR_FALSE;
612 nsIFrame* theFrame = nsnull;
613 PRInt32 theFrameOffset = 0;
615 nsresult rv = GetCaretFrameForNodeOffset(contentNode, aOffset, aFrameHint, aBidiLevel,
616 &theFrame, &theFrameOffset);
617 if (NS_FAILED(rv) || !theFrame)
618 return PR_FALSE;
620 // now we have a frame, check whether it's appropriate to show the caret here
621 const nsStyleUserInterface* userinterface = theFrame->GetStyleUserInterface();
622 if ((!mIgnoreUserModify &&
623 userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) ||
624 (userinterface->mUserInput == NS_STYLE_USER_INPUT_NONE) ||
625 (userinterface->mUserInput == NS_STYLE_USER_INPUT_DISABLED))
627 return PR_FALSE;
630 if (!mDrawn)
632 // save stuff so we can figure out what frame we're in later.
633 mLastContent = contentNode;
634 mLastContentOffset = aOffset;
635 mLastHint = aFrameHint;
636 mLastBidiLevel = aBidiLevel;
638 // If there has been a reflow, set the caret Bidi level to the level of the current frame
639 if (aBidiLevel & BIDI_LEVEL_UNDEFINED) {
640 nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection();
641 if (!frameSelection)
642 return PR_FALSE;
643 frameSelection->SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(theFrame));
646 // Only update the caret's rect when we're not currently drawn.
647 rv = UpdateCaretRects(theFrame, theFrameOffset);
648 if (NS_FAILED(rv))
649 return PR_FALSE;
652 if (aInvalidate)
653 InvalidateRects(mCaretRect, mHookRect, theFrame);
655 return PR_TRUE;
659 * Find the first frame in an in-order traversal of the frame subtree rooted
660 * at aFrame which is either a text frame logically at the end of a line,
661 * or which is aStopAtFrame. Return null if no such frame is found. We don't
662 * descend into the children of non-eLineParticipant frames.
664 static nsIFrame*
665 CheckForTrailingTextFrameRecursive(nsIFrame* aFrame, nsIFrame* aStopAtFrame)
667 if (aFrame == aStopAtFrame ||
668 ((aFrame->GetType() == nsGkAtoms::textFrame &&
669 (static_cast<nsTextFrame*>(aFrame))->IsAtEndOfLine())))
670 return aFrame;
671 if (!aFrame->IsFrameOfType(nsIFrame::eLineParticipant))
672 return nsnull;
674 for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling())
676 nsIFrame* r = CheckForTrailingTextFrameRecursive(f, aStopAtFrame);
677 if (r)
678 return r;
680 return nsnull;
683 static nsLineBox*
684 FindContainingLine(nsIFrame* aFrame)
686 while (aFrame && aFrame->IsFrameOfType(nsIFrame::eLineParticipant))
688 nsIFrame* parent = aFrame->GetParent();
689 nsBlockFrame* blockParent = nsLayoutUtils::GetAsBlock(parent);
690 if (blockParent)
692 PRBool isValid;
693 nsBlockInFlowLineIterator iter(blockParent, aFrame, &isValid);
694 return isValid ? iter.GetLine().get() : nsnull;
696 aFrame = parent;
698 return nsnull;
701 static void
702 AdjustCaretFrameForLineEnd(nsIFrame** aFrame, PRInt32* aOffset)
704 nsLineBox* line = FindContainingLine(*aFrame);
705 if (!line)
706 return;
707 PRInt32 count = line->GetChildCount();
708 for (nsIFrame* f = line->mFirstChild; count > 0; --count, f = f->GetNextSibling())
710 nsIFrame* r = CheckForTrailingTextFrameRecursive(f, *aFrame);
711 if (r == *aFrame)
712 return;
713 if (r)
715 *aFrame = r;
716 NS_ASSERTION(r->GetType() == nsGkAtoms::textFrame, "Expected text frame");
717 *aOffset = (static_cast<nsTextFrame*>(r))->GetContentEnd();
718 return;
723 nsresult
724 nsCaret::GetCaretFrameForNodeOffset(nsIContent* aContentNode,
725 PRInt32 aOffset,
726 nsFrameSelection::HINT aFrameHint,
727 PRUint8 aBidiLevel,
728 nsIFrame** aReturnFrame,
729 PRInt32* aReturnOffset)
732 //get frame selection and find out what frame to use...
733 nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
734 if (!presShell)
735 return NS_ERROR_FAILURE;
737 nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection();
738 if (!frameSelection)
739 return NS_ERROR_FAILURE;
741 nsIFrame* theFrame = nsnull;
742 PRInt32 theFrameOffset = 0;
744 theFrame = frameSelection->GetFrameForNodeOffset(aContentNode, aOffset,
745 aFrameHint, &theFrameOffset);
746 if (!theFrame)
747 return NS_ERROR_FAILURE;
749 // if theFrame is after a text frame that's logically at the end of the line
750 // (e.g. if theFrame is a <br> frame), then put the caret at the end of
751 // that text frame instead. This way, the caret will be positioned as if
752 // trailing whitespace was not trimmed.
753 AdjustCaretFrameForLineEnd(&theFrame, &theFrameOffset);
755 // Mamdouh : modification of the caret to work at rtl and ltr with Bidi
757 // Direction Style from this->GetStyleData()
758 // now in (visibility->mDirection)
759 // ------------------
760 // NS_STYLE_DIRECTION_LTR : LTR or Default
761 // NS_STYLE_DIRECTION_RTL
762 // NS_STYLE_DIRECTION_INHERIT
763 if (mBidiUI)
765 // If there has been a reflow, take the caret Bidi level to be the level of the current frame
766 if (aBidiLevel & BIDI_LEVEL_UNDEFINED)
767 aBidiLevel = NS_GET_EMBEDDING_LEVEL(theFrame);
769 PRInt32 start;
770 PRInt32 end;
771 nsIFrame* frameBefore;
772 nsIFrame* frameAfter;
773 PRUint8 levelBefore; // Bidi level of the character before the caret
774 PRUint8 levelAfter; // Bidi level of the character after the caret
776 theFrame->GetOffsets(start, end);
777 if (start == 0 || end == 0 || start == theFrameOffset || end == theFrameOffset)
779 nsPrevNextBidiLevels levels = frameSelection->
780 GetPrevNextBidiLevels(aContentNode, aOffset, PR_FALSE);
782 /* Boundary condition, we need to know the Bidi levels of the characters before and after the caret */
783 if (levels.mFrameBefore || levels.mFrameAfter)
785 frameBefore = levels.mFrameBefore;
786 frameAfter = levels.mFrameAfter;
787 levelBefore = levels.mLevelBefore;
788 levelAfter = levels.mLevelAfter;
790 if ((levelBefore != levelAfter) || (aBidiLevel != levelBefore))
792 aBidiLevel = PR_MAX(aBidiLevel, PR_MIN(levelBefore, levelAfter)); // rule c3
793 aBidiLevel = PR_MIN(aBidiLevel, PR_MAX(levelBefore, levelAfter)); // rule c4
794 if (aBidiLevel == levelBefore // rule c1
795 || (aBidiLevel > levelBefore && aBidiLevel < levelAfter && !((aBidiLevel ^ levelBefore) & 1)) // rule c5
796 || (aBidiLevel < levelBefore && aBidiLevel > levelAfter && !((aBidiLevel ^ levelBefore) & 1))) // rule c9
798 if (theFrame != frameBefore)
800 if (frameBefore) // if there is a frameBefore, move into it
802 theFrame = frameBefore;
803 theFrame->GetOffsets(start, end);
804 theFrameOffset = end;
806 else
808 // if there is no frameBefore, we must be at the beginning of the line
809 // so we stay with the current frame.
810 // Exception: when the first frame on the line has a different Bidi level from the paragraph level, there is no
811 // real frame for the caret to be in. We have to find the visually first frame on the line.
812 PRUint8 baseLevel = NS_GET_BASE_LEVEL(frameAfter);
813 if (baseLevel != levelAfter)
815 nsPeekOffsetStruct pos;
816 pos.SetData(eSelectBeginLine, eDirPrevious, 0, 0, PR_FALSE, PR_TRUE, PR_FALSE, PR_TRUE);
817 if (NS_SUCCEEDED(frameAfter->PeekOffset(&pos))) {
818 theFrame = pos.mResultFrame;
819 theFrameOffset = pos.mContentOffset;
825 else if (aBidiLevel == levelAfter // rule c2
826 || (aBidiLevel > levelBefore && aBidiLevel < levelAfter && !((aBidiLevel ^ levelAfter) & 1)) // rule c6
827 || (aBidiLevel < levelBefore && aBidiLevel > levelAfter && !((aBidiLevel ^ levelAfter) & 1))) // rule c10
829 if (theFrame != frameAfter)
831 if (frameAfter)
833 // if there is a frameAfter, move into it
834 theFrame = frameAfter;
835 theFrame->GetOffsets(start, end);
836 theFrameOffset = start;
838 else
840 // if there is no frameAfter, we must be at the end of the line
841 // so we stay with the current frame.
842 // Exception: when the last frame on the line has a different Bidi level from the paragraph level, there is no
843 // real frame for the caret to be in. We have to find the visually last frame on the line.
844 PRUint8 baseLevel = NS_GET_BASE_LEVEL(frameBefore);
845 if (baseLevel != levelBefore)
847 nsPeekOffsetStruct pos;
848 pos.SetData(eSelectEndLine, eDirNext, 0, 0, PR_FALSE, PR_TRUE, PR_FALSE, PR_TRUE);
849 if (NS_SUCCEEDED(frameBefore->PeekOffset(&pos))) {
850 theFrame = pos.mResultFrame;
851 theFrameOffset = pos.mContentOffset;
857 else if (aBidiLevel > levelBefore && aBidiLevel < levelAfter // rule c7/8
858 && !((levelBefore ^ levelAfter) & 1) // before and after have the same parity
859 && ((aBidiLevel ^ levelAfter) & 1)) // caret has different parity
861 if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(frameAfter, eDirNext, aBidiLevel, &theFrame)))
863 theFrame->GetOffsets(start, end);
864 levelAfter = NS_GET_EMBEDDING_LEVEL(theFrame);
865 if (aBidiLevel & 1) // c8: caret to the right of the rightmost character
866 theFrameOffset = (levelAfter & 1) ? start : end;
867 else // c7: caret to the left of the leftmost character
868 theFrameOffset = (levelAfter & 1) ? end : start;
871 else if (aBidiLevel < levelBefore && aBidiLevel > levelAfter // rule c11/12
872 && !((levelBefore ^ levelAfter) & 1) // before and after have the same parity
873 && ((aBidiLevel ^ levelAfter) & 1)) // caret has different parity
875 if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(frameBefore, eDirPrevious, aBidiLevel, &theFrame)))
877 theFrame->GetOffsets(start, end);
878 levelBefore = NS_GET_EMBEDDING_LEVEL(theFrame);
879 if (aBidiLevel & 1) // c12: caret to the left of the leftmost character
880 theFrameOffset = (levelBefore & 1) ? end : start;
881 else // c11: caret to the right of the rightmost character
882 theFrameOffset = (levelBefore & 1) ? start : end;
889 *aReturnFrame = theFrame;
890 *aReturnOffset = theFrameOffset;
891 return NS_OK;
895 //-----------------------------------------------------------------------------
896 void nsCaret::GetViewForRendering(nsIFrame *caretFrame,
897 EViewCoordinates coordType,
898 nsPoint &viewOffset,
899 nsIView **outRenderingView,
900 nsIView **outRelativeView)
902 if (!caretFrame || !outRenderingView)
903 return;
905 *outRenderingView = nsnull;
906 if (outRelativeView)
907 *outRelativeView = nsnull;
909 NS_ASSERTION(caretFrame, "Should have a frame here");
911 viewOffset.x = 0;
912 viewOffset.y = 0;
914 nsPoint withinViewOffset(0, 0);
915 // get the offset of this frame from its parent view (walks up frame hierarchy)
916 nsIView* theView = nsnull;
917 caretFrame->GetOffsetFromView(withinViewOffset, &theView);
918 if (!theView)
919 return;
921 if (outRelativeView && coordType == eClosestViewCoordinates)
922 *outRelativeView = theView;
924 // Note: views are not refcounted.
925 nsIView* returnView = nsIView::GetViewFor(theView->GetNearestWidget(nsnull));
927 // This gets uses the first view with a widget
928 if (coordType == eRenderingViewCoordinates) {
929 if (returnView) {
930 // Now adjust the view offset for this view.
931 withinViewOffset += theView->GetOffsetTo(returnView);
933 // Account for the view's origin not lining up with the widget's
934 // (bug 190290)
935 withinViewOffset += returnView->GetPosition() -
936 returnView->GetBounds().TopLeft();
937 viewOffset = withinViewOffset;
939 if (outRelativeView)
940 *outRelativeView = returnView;
943 else {
944 // window-relative coordinates. Done for us by the view.
945 withinViewOffset += theView->GetOffsetTo(nsnull);
946 viewOffset = withinViewOffset;
948 // Get the relative view for top level window coordinates
949 if (outRelativeView && coordType == eTopLevelWindowCoordinates) {
950 nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
951 if (presShell) {
952 nsIViewManager* vm = presShell->GetViewManager();
953 if (vm) {
954 vm->GetRootView(*outRelativeView);
960 *outRenderingView = returnView;
963 nsresult nsCaret::CheckCaretDrawingState()
965 // If the caret's drawn when it shouldn't be, erase it.
966 if (mDrawn && (!mVisible || !MustDrawCaret(PR_TRUE)))
967 EraseCaret();
968 return NS_OK;
971 /*-----------------------------------------------------------------------------
973 MustDrawCaret
975 Find out if we need to do any caret drawing. This returns true if
976 either:
977 a) The caret has been drawn, and we need to erase it.
978 b) The caret is not drawn, and the selection is collapsed.
979 c) The caret is not hidden due to open XUL popups
980 (see IsMenuPopupHidingCaret()).
982 ----------------------------------------------------------------------------- */
983 PRBool nsCaret::MustDrawCaret(PRBool aIgnoreDrawnState)
985 nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
986 if (presShell) {
987 PRBool isPaintingSuppressed;
988 presShell->IsPaintingSuppressed(&isPaintingSuppressed);
989 if (isPaintingSuppressed)
990 return PR_FALSE;
993 if (!aIgnoreDrawnState && mDrawn)
994 return PR_TRUE;
996 nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
997 if (!domSelection)
998 return PR_FALSE;
999 PRBool isCollapsed;
1001 if (NS_FAILED(domSelection->GetIsCollapsed(&isCollapsed)))
1002 return PR_FALSE;
1004 if (mShowDuringSelection)
1005 return PR_TRUE; // show the caret even in selections
1007 if (IsMenuPopupHidingCaret())
1008 return PR_FALSE;
1010 return isCollapsed;
1013 PRBool nsCaret::IsMenuPopupHidingCaret()
1015 #ifdef MOZ_XUL
1016 // Check if there are open popups.
1017 nsXULPopupManager *popMgr = nsXULPopupManager::GetInstance();
1018 nsTArray<nsIFrame*> popups = popMgr->GetOpenPopups();
1020 if (popups.Length() == 0)
1021 return PR_FALSE; // No popups, so caret can't be hidden by them.
1023 // Get the selection focus content, that's where the caret would
1024 // go if it was drawn.
1025 nsCOMPtr<nsIDOMNode> node;
1026 nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
1027 if (!domSelection)
1028 return PR_TRUE; // No selection/caret to draw.
1029 domSelection->GetFocusNode(getter_AddRefs(node));
1030 if (!node)
1031 return PR_TRUE; // No selection/caret to draw.
1032 nsCOMPtr<nsIContent> caretContent = do_QueryInterface(node);
1033 if (!caretContent)
1034 return PR_TRUE; // No selection/caret to draw.
1036 // If there's a menu popup open before the popup with
1037 // the caret, don't show the caret.
1038 for (PRUint32 i=0; i<popups.Length(); i++) {
1039 nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame*>(popups[i]);
1040 nsIContent* popupContent = popupFrame->GetContent();
1042 if (nsContentUtils::ContentIsDescendantOf(caretContent, popupContent)) {
1043 // The caret is in this popup. There were no menu popups before this
1044 // popup, so don't hide the caret.
1045 return PR_FALSE;
1048 if (popupFrame->PopupType() == ePopupTypeMenu && !popupFrame->IsContextMenu()) {
1049 // This is an open menu popup. It does not contain the caret (else we'd
1050 // have returned above). Even if the caret is in a subsequent popup,
1051 // or another document/frame, it should be hidden.
1052 return PR_TRUE;
1055 #endif
1057 // There are no open menu popups, no need to hide the caret.
1058 return PR_FALSE;
1061 /*-----------------------------------------------------------------------------
1063 DrawCaret
1065 ----------------------------------------------------------------------------- */
1067 void nsCaret::DrawCaret(PRBool aInvalidate)
1069 // do we need to draw the caret at all?
1070 if (!MustDrawCaret(PR_FALSE))
1071 return;
1073 nsCOMPtr<nsIDOMNode> node;
1074 PRInt32 offset;
1075 nsFrameSelection::HINT hint;
1076 PRUint8 bidiLevel;
1078 if (!mDrawn)
1080 nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
1081 nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSelection));
1082 if (!privateSelection) return;
1084 PRBool isCollapsed = PR_FALSE;
1085 domSelection->GetIsCollapsed(&isCollapsed);
1086 if (!mShowDuringSelection && !isCollapsed)
1087 return;
1089 PRBool hintRight;
1090 privateSelection->GetInterlinePosition(&hintRight);//translate hint.
1091 hint = hintRight ? nsFrameSelection::HINTRIGHT : nsFrameSelection::HINTLEFT;
1093 // get the node and offset, which is where we want the caret to draw
1094 domSelection->GetFocusNode(getter_AddRefs(node));
1095 if (!node)
1096 return;
1098 if (NS_FAILED(domSelection->GetFocusOffset(&offset)))
1099 return;
1101 nsCOMPtr<nsFrameSelection> frameSelection = GetFrameSelection();
1102 if (!frameSelection)
1103 return;
1104 bidiLevel = frameSelection->GetCaretBidiLevel();
1106 else
1108 if (!mLastContent)
1110 mDrawn = PR_FALSE;
1111 return;
1113 if (!mLastContent->IsInDoc())
1115 mLastContent = nsnull;
1116 mDrawn = PR_FALSE;
1117 return;
1119 node = do_QueryInterface(mLastContent);
1120 offset = mLastContentOffset;
1121 hint = mLastHint;
1122 bidiLevel = mLastBidiLevel;
1125 DrawAtPositionWithHint(node, offset, hint, bidiLevel, aInvalidate);
1126 ToggleDrawnStatus();
1129 nsresult nsCaret::UpdateCaretRects(nsIFrame* aFrame, PRInt32 aFrameOffset)
1131 NS_ASSERTION(aFrame, "Should have a frame here");
1133 nsRect frameRect = aFrame->GetRect();
1134 frameRect.x = 0;
1135 frameRect.y = 0;
1137 nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
1138 if (!presShell) return NS_ERROR_FAILURE;
1140 nsPresContext *presContext = presShell->GetPresContext();
1142 // if we got a zero-height frame, it's probably a BR frame at the end of a non-empty line
1143 // (see BRFrame::Reflow). In that case, figure out a height. We have to do this
1144 // after we've got an RC.
1145 if (frameRect.height == 0)
1147 nsCOMPtr<nsIFontMetrics> fm;
1148 nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm));
1150 if (fm)
1152 nscoord ascent, descent;
1153 fm->GetMaxAscent(ascent);
1154 fm->GetMaxDescent(descent);
1155 frameRect.height = ascent + descent;
1156 frameRect.y -= ascent; // BR frames sit on the baseline of the text, so we need to subtract
1157 // the ascent to account for the frame height.
1161 mCaretRect = frameRect;
1162 nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
1163 nsCOMPtr<nsISelectionPrivate> privateSelection = do_QueryInterface(domSelection);
1165 nsPoint framePos;
1167 // if cache in selection is available, apply it, else refresh it
1168 nsresult rv = privateSelection->GetCachedFrameOffset(aFrame, aFrameOffset,
1169 framePos);
1170 if (NS_FAILED(rv))
1172 mCaretRect.Empty();
1173 return rv;
1176 mCaretRect += framePos;
1177 Metrics metrics = ComputeMetrics(aFrame, aFrameOffset, mCaretRect.height);
1178 mCaretRect.width = metrics.mCaretWidth;
1180 // Clamp our position to be within our scroll frame. If we don't, then it
1181 // clips us, and we don't appear at all. See bug 335560.
1182 nsIFrame *scrollFrame =
1183 nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::scrollFrame);
1184 if (scrollFrame)
1186 // First, use the scrollFrame to get at the scrollable view that we're in.
1187 nsIScrollableFrame *scrollable;
1188 CallQueryInterface(scrollFrame, &scrollable);
1189 nsIScrollableView *scrollView = scrollable->GetScrollableView();
1190 nsIView *view;
1191 scrollView->GetScrolledView(view);
1193 // Compute the caret's coordinates in the enclosing view's coordinate
1194 // space. To do so, we need to correct for both the original frame's
1195 // offset from the scrollframe, and the scrollable view's offset from the
1196 // scrolled frame's view.
1197 nsPoint toScroll = aFrame->GetOffsetTo(scrollFrame) -
1198 view->GetOffsetTo(scrollFrame->GetView());
1199 nsRect caretInScroll = mCaretRect + toScroll;
1201 // Now see if thet caret extends beyond the view's bounds. If it does,
1202 // then snap it back, put it as close to the edge as it can.
1203 nscoord overflow = caretInScroll.XMost() - view->GetBounds().width;
1204 if (overflow > 0)
1205 mCaretRect.x -= overflow;
1208 // on RTL frames the right edge of mCaretRect must be equal to framePos
1209 const nsStyleVisibility* vis = aFrame->GetStyleVisibility();
1210 if (NS_STYLE_DIRECTION_RTL == vis->mDirection)
1211 mCaretRect.x -= mCaretRect.width;
1213 return UpdateHookRect(presContext, metrics);
1216 nsresult nsCaret::UpdateHookRect(nsPresContext* aPresContext,
1217 const Metrics& aMetrics)
1219 mHookRect.Empty();
1221 #ifdef IBMBIDI
1222 // Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction
1223 PRBool isCaretRTL=PR_FALSE;
1224 nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
1225 if (!bidiKeyboard || NS_FAILED(bidiKeyboard->IsLangRTL(&isCaretRTL)))
1226 // if bidiKeyboard->IsLangRTL() failed, there is no way to tell the
1227 // keyboard direction, or the user has no right-to-left keyboard
1228 // installed, so we never draw the hook.
1229 return NS_OK;
1230 if (mBidiUI)
1232 if (isCaretRTL != mKeyboardRTL)
1234 /* if the caret bidi level and the keyboard language direction are not in
1235 * synch, the keyboard language must have been changed by the
1236 * user, and if the caret is in a boundary condition (between left-to-right and
1237 * right-to-left characters) it may have to change position to
1238 * reflect the location in which the next character typed will
1239 * appear. We will call |SelectionLanguageChange| and exit
1240 * without drawing the caret in the old position.
1242 mKeyboardRTL = isCaretRTL;
1243 nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
1244 if (domSelection)
1246 if (NS_SUCCEEDED(domSelection->SelectionLanguageChange(mKeyboardRTL)))
1248 return NS_ERROR_FAILURE;
1252 // If keyboard language is RTL, draw the hook on the left; if LTR, to the right
1253 // The height of the hook rectangle is the same as the width of the caret
1254 // rectangle.
1255 nscoord bidiIndicatorSize = aMetrics.mBidiIndicatorSize;
1256 mHookRect.SetRect(mCaretRect.x + ((isCaretRTL) ?
1257 bidiIndicatorSize * -1 :
1258 mCaretRect.width),
1259 mCaretRect.y + bidiIndicatorSize,
1260 bidiIndicatorSize,
1261 mCaretRect.width);
1263 #endif //IBMBIDI
1265 return NS_OK;
1268 // static
1269 void nsCaret::InvalidateRects(const nsRect &aRect, const nsRect &aHook,
1270 nsIFrame *aFrame)
1272 NS_ASSERTION(aFrame, "Must have a frame to invalidate");
1273 nsRect rect;
1274 rect.UnionRect(aRect, aHook);
1275 aFrame->Invalidate(rect);
1278 //-----------------------------------------------------------------------------
1279 /* static */
1280 void nsCaret::CaretBlinkCallback(nsITimer *aTimer, void *aClosure)
1282 nsCaret *theCaret = reinterpret_cast<nsCaret*>(aClosure);
1283 if (!theCaret) return;
1285 theCaret->DrawCaret(PR_TRUE);
1289 //-----------------------------------------------------------------------------
1290 already_AddRefed<nsFrameSelection>
1291 nsCaret::GetFrameSelection()
1293 nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryReferent(mDomSelectionWeak));
1294 if (!privateSelection)
1295 return nsnull;
1296 nsFrameSelection* frameSelection = nsnull;
1297 privateSelection->GetFrameSelection(&frameSelection);
1298 return frameSelection;
1301 void
1302 nsCaret::SetIgnoreUserModify(PRBool aIgnoreUserModify)
1304 if (!aIgnoreUserModify && mIgnoreUserModify && mDrawn) {
1305 // We're turning off mIgnoreUserModify. If the caret's drawn
1306 // in a read-only node we must erase it, else the next call
1307 // to DrawCaret() won't erase the old caret, due to the new
1308 // mIgnoreUserModify value.
1309 nsIFrame *frame = GetCaretFrame();
1310 if (frame) {
1311 const nsStyleUserInterface* userinterface = frame->GetStyleUserInterface();
1312 if (userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) {
1313 StopBlinking();
1317 mIgnoreUserModify = aIgnoreUserModify;
1320 //-----------------------------------------------------------------------------
1321 nsresult NS_NewCaret(nsCaret** aInstancePtrResult)
1323 NS_PRECONDITION(aInstancePtrResult, "null ptr");
1325 nsCaret* caret = new nsCaret();
1326 if (nsnull == caret)
1327 return NS_ERROR_OUT_OF_MEMORY;
1328 NS_ADDREF(caret);
1329 *aInstancePtrResult = caret;
1330 return NS_OK;