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 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* the caret is the text cursor used, e.g., when editing */
15 #include "nsFrameSelection.h"
17 #include "nsIScrollableFrame.h"
18 #include "nsIDOMNode.h"
19 #include "nsISelection.h"
20 #include "nsISelectionPrivate.h"
21 #include "nsIContent.h"
22 #include "nsIPresShell.h"
23 #include "nsRenderingContext.h"
24 #include "nsPresContext.h"
25 #include "nsBlockFrame.h"
26 #include "nsISelectionController.h"
27 #include "nsTextFrame.h"
28 #include "nsXULPopupManager.h"
29 #include "nsMenuPopupFrame.h"
30 #include "nsTextFragment.h"
31 #include "mozilla/Preferences.h"
32 #include "mozilla/LookAndFeel.h"
33 #include "mozilla/dom/Selection.h"
34 #include "nsIBidiKeyboard.h"
35 #include "nsContentUtils.h"
37 using namespace mozilla
;
38 using namespace mozilla::dom
;
40 // The bidi indicator hangs off the caret to one side, to show which
41 // direction the typing is in. It needs to be at least 2x2 to avoid looking like
42 // an insignificant dot
43 static const int32_t kMinBidiIndicatorPixels
= 2;
46 * Find the first frame in an in-order traversal of the frame subtree rooted
47 * at aFrame which is either a text frame logically at the end of a line,
48 * or which is aStopAtFrame. Return null if no such frame is found. We don't
49 * descend into the children of non-eLineParticipant frames.
52 CheckForTrailingTextFrameRecursive(nsIFrame
* aFrame
, nsIFrame
* aStopAtFrame
)
54 if (aFrame
== aStopAtFrame
||
55 ((aFrame
->GetType() == nsGkAtoms::textFrame
&&
56 (static_cast<nsTextFrame
*>(aFrame
))->IsAtEndOfLine())))
58 if (!aFrame
->IsFrameOfType(nsIFrame::eLineParticipant
))
61 for (nsIFrame
* f
= aFrame
->GetFirstPrincipalChild(); f
; f
= f
->GetNextSibling())
63 nsIFrame
* r
= CheckForTrailingTextFrameRecursive(f
, aStopAtFrame
);
71 FindContainingLine(nsIFrame
* aFrame
)
73 while (aFrame
&& aFrame
->IsFrameOfType(nsIFrame::eLineParticipant
))
75 nsIFrame
* parent
= aFrame
->GetParent();
76 nsBlockFrame
* blockParent
= nsLayoutUtils::GetAsBlock(parent
);
80 nsBlockInFlowLineIterator
iter(blockParent
, aFrame
, &isValid
);
81 return isValid
? iter
.GetLine().get() : nullptr;
89 AdjustCaretFrameForLineEnd(nsIFrame
** aFrame
, int32_t* aOffset
)
91 nsLineBox
* line
= FindContainingLine(*aFrame
);
94 int32_t count
= line
->GetChildCount();
95 for (nsIFrame
* f
= line
->mFirstChild
; count
> 0; --count
, f
= f
->GetNextSibling())
97 nsIFrame
* r
= CheckForTrailingTextFrameRecursive(f
, *aFrame
);
103 NS_ASSERTION(r
->GetType() == nsGkAtoms::textFrame
, "Expected text frame");
104 *aOffset
= (static_cast<nsTextFrame
*>(r
))->GetContentEnd();
113 return Preferences::GetBool("bidi.browser.ui");
121 , mShowDuringSelection(false)
122 , mIgnoreUserModify(true)
131 nsresult
nsCaret::Init(nsIPresShell
*inPresShell
)
133 NS_ENSURE_ARG(inPresShell
);
135 mPresShell
= do_GetWeakReference(inPresShell
); // the presshell owns us, so no addref
136 NS_ASSERTION(mPresShell
, "Hey, pres shell should support weak refs");
138 mShowDuringSelection
=
139 LookAndFeel::GetInt(LookAndFeel::eIntID_ShowCaretDuringSelection
,
140 mShowDuringSelection
? 1 : 0) != 0;
142 // get the selection from the pres shell, and set ourselves up as a selection
145 nsCOMPtr
<nsISelectionController
> selCon
= do_QueryReferent(mPresShell
);
147 return NS_ERROR_FAILURE
;
149 nsCOMPtr
<nsISelection
> domSelection
;
150 nsresult rv
= selCon
->GetSelection(nsISelectionController::SELECTION_NORMAL
,
151 getter_AddRefs(domSelection
));
155 return NS_ERROR_FAILURE
;
157 nsCOMPtr
<nsISelectionPrivate
> privateSelection
= do_QueryInterface(domSelection
);
158 if (privateSelection
)
159 privateSelection
->AddSelectionListener(this);
160 mDomSelectionWeak
= do_GetWeakReference(domSelection
);
166 DrawCJKCaret(nsIFrame
* aFrame
, int32_t aOffset
)
168 nsIContent
* content
= aFrame
->GetContent();
169 const nsTextFragment
* frag
= content
->GetText();
172 if (aOffset
< 0 || uint32_t(aOffset
) >= frag
->GetLength())
174 char16_t ch
= frag
->CharAt(aOffset
);
175 return 0x2e80 <= ch
&& ch
<= 0xd7ff;
179 nsCaret::ComputeMetrics(nsIFrame
* aFrame
, int32_t aOffset
, nscoord aCaretHeight
)
181 // Compute nominal sizes in appunits
183 (aCaretHeight
* LookAndFeel::GetFloat(LookAndFeel::eFloatID_CaretAspectRatio
, 0.0f
)) +
184 nsPresContext::CSSPixelsToAppUnits(
185 LookAndFeel::GetInt(LookAndFeel::eIntID_CaretWidth
, 1));
187 if (DrawCJKCaret(aFrame
, aOffset
)) {
188 caretWidth
+= nsPresContext::CSSPixelsToAppUnits(1);
190 nscoord bidiIndicatorSize
= nsPresContext::CSSPixelsToAppUnits(kMinBidiIndicatorPixels
);
191 bidiIndicatorSize
= std::max(caretWidth
, bidiIndicatorSize
);
193 // Round them to device pixels. Always round down, except that anything
194 // between 0 and 1 goes up to 1 so we don't let the caret disappear.
195 int32_t tpp
= aFrame
->PresContext()->AppUnitsPerDevPixel();
197 result
.mCaretWidth
= NS_ROUND_BORDER_TO_PIXELS(caretWidth
, tpp
);
198 result
.mBidiIndicatorSize
= NS_ROUND_BORDER_TO_PIXELS(bidiIndicatorSize
, tpp
);
202 void nsCaret::Terminate()
204 // this doesn't erase the caret if it's drawn. Should it? We might not have
205 // a good drawing environment during teardown.
208 mBlinkTimer
= nullptr;
210 // unregiser ourselves as a selection listener
211 nsCOMPtr
<nsISelection
> domSelection
= do_QueryReferent(mDomSelectionWeak
);
212 nsCOMPtr
<nsISelectionPrivate
> privateSelection(do_QueryInterface(domSelection
));
213 if (privateSelection
)
214 privateSelection
->RemoveSelectionListener(this);
215 mDomSelectionWeak
= nullptr;
216 mPresShell
= nullptr;
218 mOverrideContent
= nullptr;
221 NS_IMPL_ISUPPORTS(nsCaret
, nsISelectionListener
)
223 nsISelection
* nsCaret::GetSelection()
225 nsCOMPtr
<nsISelection
> sel(do_QueryReferent(mDomSelectionWeak
));
229 void nsCaret::SetSelection(nsISelection
*aDOMSel
)
232 mDomSelectionWeak
= do_GetWeakReference(aDOMSel
); // weak reference to pres shell
237 void nsCaret::SetVisible(bool inMakeVisible
)
239 mVisible
= inMakeVisible
;
240 mIgnoreUserModify
= mVisible
;
245 bool nsCaret::IsVisible()
251 if (!mShowDuringSelection
) {
252 Selection
* selection
= GetSelectionInternal();
257 if (NS_FAILED(selection
->GetIsCollapsed(&isCollapsed
)) || !isCollapsed
) {
262 if (IsMenuPopupHidingCaret()) {
269 void nsCaret::SetCaretReadOnly(bool inMakeReadonly
)
271 mReadOnly
= inMakeReadonly
;
277 nsCaret::GetGeometryForFrame(nsIFrame
* aFrame
,
278 int32_t aFrameOffset
,
279 nscoord
* aBidiIndicatorSize
)
281 nsPoint
framePos(0, 0);
283 nsresult rv
= aFrame
->GetPointFromOffset(aFrameOffset
, &framePos
);
285 if (aBidiIndicatorSize
) {
286 *aBidiIndicatorSize
= 0;
291 nsIFrame
* frame
= aFrame
->GetContentInsertionFrame();
295 NS_ASSERTION(!(frame
->GetStateBits() & NS_FRAME_IN_REFLOW
),
296 "We should not be in the middle of reflow");
297 nscoord baseline
= frame
->GetCaretBaseline();
298 nscoord ascent
= 0, descent
= 0;
299 nsRefPtr
<nsFontMetrics
> fm
;
300 nsLayoutUtils::GetFontMetricsForFrame(aFrame
, getter_AddRefs(fm
),
301 nsLayoutUtils::FontSizeInflationFor(aFrame
));
302 NS_ASSERTION(fm
, "We should be able to get the font metrics");
304 ascent
= fm
->MaxAscent();
305 descent
= fm
->MaxDescent();
307 nscoord height
= ascent
+ descent
;
308 framePos
.y
= baseline
- ascent
;
309 Metrics caretMetrics
= ComputeMetrics(aFrame
, aFrameOffset
, height
);
310 rect
= nsRect(framePos
, nsSize(caretMetrics
.mCaretWidth
, height
));
312 // Clamp the x-position to be within our scroll frame. If we don't, then it
313 // clips us, and we don't appear at all. See bug 335560.
314 nsIFrame
*scrollFrame
=
315 nsLayoutUtils::GetClosestFrameOfType(aFrame
, nsGkAtoms::scrollFrame
);
317 // First, use the scrollFrame to get at the scrollable view that we're in.
318 nsIScrollableFrame
*sf
= do_QueryFrame(scrollFrame
);
319 nsIFrame
*scrolled
= sf
->GetScrolledFrame();
320 nsRect caretInScroll
= rect
+ aFrame
->GetOffsetTo(scrolled
);
322 // Now see if thet caret extends beyond the view's bounds. If it does,
323 // then snap it back, put it as close to the edge as it can.
324 nscoord overflow
= caretInScroll
.XMost() -
325 scrolled
->GetVisualOverflowRectRelativeToSelf().width
;
331 if (aBidiIndicatorSize
) {
332 *aBidiIndicatorSize
= caretMetrics
.mBidiIndicatorSize
;
338 GetFrameAndOffset(Selection
* aSelection
,
339 nsINode
* aOverrideNode
, int32_t aOverrideOffset
,
340 int32_t* aFrameOffset
)
346 focusNode
= aOverrideNode
;
347 focusOffset
= aOverrideOffset
;
348 } else if (aSelection
) {
349 focusNode
= aSelection
->GetFocusNode();
350 aSelection
->GetFocusOffset(&focusOffset
);
355 if (!focusNode
|| !focusNode
->IsContent()) {
359 nsIContent
* contentNode
= focusNode
->AsContent();
360 nsFrameSelection
* frameSelection
= aSelection
->GetFrameSelection();
361 uint8_t bidiLevel
= frameSelection
->GetCaretBidiLevel();
363 nsresult rv
= nsCaret::GetCaretFrameForNodeOffset(
364 frameSelection
, contentNode
, focusOffset
,
365 frameSelection
->GetHint(), bidiLevel
, &frame
, aFrameOffset
);
366 if (NS_FAILED(rv
) || !frame
) {
373 /* static */ nsIFrame
*
374 nsCaret::GetGeometry(nsISelection
* aSelection
, nsRect
* aRect
)
377 nsIFrame
* frame
= GetFrameAndOffset(
378 static_cast<Selection
*>(aSelection
), nullptr, 0, &frameOffset
);
380 *aRect
= GetGeometryForFrame(frame
, frameOffset
, nullptr);
386 nsCaret::GetSelectionInternal()
388 return static_cast<Selection
*>(GetSelection());
391 void nsCaret::SchedulePaint()
393 Selection
* selection
= GetSelectionInternal();
395 if (mOverrideContent
) {
396 focusNode
= mOverrideContent
;
397 } else if (selection
) {
398 focusNode
= selection
->GetFocusNode();
402 if (!focusNode
|| !focusNode
->IsContent()) {
405 nsIFrame
* f
= focusNode
->AsContent()->GetPrimaryFrame();
409 // This may not be the correct continuation frame, but that's OK since we're
410 // just scheduling a paint of the window (or popup).
414 void nsCaret::SetVisibilityDuringSelection(bool aVisibility
)
416 mShowDuringSelection
= aVisibility
;
421 nsCaret::SetCaretPosition(nsIDOMNode
* aNode
, int32_t aOffset
)
423 mOverrideContent
= do_QueryInterface(aNode
);
424 mOverrideOffset
= aOffset
;
431 nsCaret::CheckSelectionLanguageChange()
437 bool isKeyboardRTL
= false;
438 nsIBidiKeyboard
* bidiKeyboard
= nsContentUtils::GetBidiKeyboard();
440 bidiKeyboard
->IsLangRTL(&isKeyboardRTL
);
442 // Call SelectionLanguageChange on every paint. Mostly it will be a noop
443 // but it should be fast anyway. This guarantees we never paint the caret
444 // at the wrong place.
445 Selection
* selection
= GetSelectionInternal();
447 selection
->SelectionLanguageChange(isKeyboardRTL
);
452 nsCaret::GetPaintGeometry(nsRect
* aRect
)
454 // Return null if we should not be visible.
455 if (!IsVisible() || !mIsBlinkOn
) {
459 // Update selection language direction now so the new direction will be
460 // taken into account when computing the caret position below.
461 CheckSelectionLanguageChange();
464 nsIFrame
*frame
= GetFrameAndOffset(GetSelectionInternal(),
465 mOverrideContent
, mOverrideOffset
, &frameOffset
);
470 // now we have a frame, check whether it's appropriate to show the caret here
471 const nsStyleUserInterface
* userinterface
= frame
->StyleUserInterface();
472 if ((!mIgnoreUserModify
&&
473 userinterface
->mUserModify
== NS_STYLE_USER_MODIFY_READ_ONLY
) ||
474 userinterface
->mUserInput
== NS_STYLE_USER_INPUT_NONE
||
475 userinterface
->mUserInput
== NS_STYLE_USER_INPUT_DISABLED
) {
479 // If the offset falls outside of the frame, then don't paint the caret.
480 int32_t startOffset
, endOffset
;
481 if (frame
->GetType() == nsGkAtoms::textFrame
&&
482 (NS_FAILED(frame
->GetOffsets(startOffset
, endOffset
)) ||
483 startOffset
> frameOffset
||
484 endOffset
< frameOffset
)) {
490 ComputeCaretRects(frame
, frameOffset
, &caretRect
, &hookRect
);
492 aRect
->UnionRect(caretRect
, hookRect
);
496 void nsCaret::PaintCaret(nsDisplayListBuilder
*aBuilder
,
497 nsRenderingContext
*aCtx
,
499 const nsPoint
&aOffset
)
501 int32_t contentOffset
;
502 nsIFrame
* frame
= GetFrameAndOffset(GetSelectionInternal(),
503 mOverrideContent
, mOverrideOffset
, &contentOffset
);
507 NS_ASSERTION(frame
== aForFrame
, "We're referring different frame");
509 nscolor foregroundColor
= aForFrame
->GetCaretColorAt(contentOffset
);
510 aCtx
->SetColor(foregroundColor
);
514 ComputeCaretRects(frame
, contentOffset
, &caretRect
, &hookRect
);
515 aCtx
->FillRect(caretRect
+ aOffset
);
516 if (!hookRect
.IsEmpty()) {
517 aCtx
->FillRect(hookRect
+ aOffset
);
522 nsCaret::NotifySelectionChanged(nsIDOMDocument
*, nsISelection
*aDomSel
,
525 if (aReason
& nsISelectionListener::MOUSEUP_REASON
)//this wont do
528 nsCOMPtr
<nsISelection
> domSel(do_QueryReferent(mDomSelectionWeak
));
530 // The same caret is shared amongst the document and any text widgets it
531 // may contain. This means that the caret could get notifications from
532 // multiple selections.
534 // If this notification is for a selection that is not the one the
535 // the caret is currently interested in (mDomSelectionWeak), then there
538 if (domSel
!= aDomSel
)
547 void nsCaret::ResetBlinking()
551 if (mReadOnly
|| !mVisible
) {
557 mBlinkTimer
->Cancel();
560 mBlinkTimer
= do_CreateInstance("@mozilla.org/timer;1", &err
);
565 uint32_t blinkRate
= static_cast<uint32_t>(
566 LookAndFeel::GetInt(LookAndFeel::eIntID_CaretBlinkTime
, 500));
568 mBlinkTimer
->InitWithFuncCallback(CaretBlinkCallback
, this, blinkRate
,
569 nsITimer::TYPE_REPEATING_SLACK
);
573 void nsCaret::StopBlinking()
577 mBlinkTimer
->Cancel();
582 nsCaret::GetCaretFrameForNodeOffset(nsFrameSelection
* aFrameSelection
,
583 nsIContent
* aContentNode
,
585 CaretAssociationHint aFrameHint
,
587 nsIFrame
** aReturnFrame
,
588 int32_t* aReturnOffset
)
590 if (!aFrameSelection
)
591 return NS_ERROR_FAILURE
;
592 nsIPresShell
* presShell
= aFrameSelection
->GetShell();
594 return NS_ERROR_FAILURE
;
596 if (!aContentNode
|| !aContentNode
->IsInComposedDoc() ||
597 presShell
->GetDocument() != aContentNode
->GetComposedDoc())
598 return NS_ERROR_FAILURE
;
600 nsIFrame
* theFrame
= nullptr;
601 int32_t theFrameOffset
= 0;
603 theFrame
= aFrameSelection
->GetFrameForNodeOffset(
604 aContentNode
, aOffset
, aFrameHint
, &theFrameOffset
);
606 return NS_ERROR_FAILURE
;
608 // if theFrame is after a text frame that's logically at the end of the line
609 // (e.g. if theFrame is a <br> frame), then put the caret at the end of
610 // that text frame instead. This way, the caret will be positioned as if
611 // trailing whitespace was not trimmed.
612 AdjustCaretFrameForLineEnd(&theFrame
, &theFrameOffset
);
614 // Mamdouh : modification of the caret to work at rtl and ltr with Bidi
616 // Direction Style from visibility->mDirection
617 // ------------------
618 // NS_STYLE_DIRECTION_LTR : LTR or Default
619 // NS_STYLE_DIRECTION_RTL
620 // NS_STYLE_DIRECTION_INHERIT
623 // If there has been a reflow, take the caret Bidi level to be the level of the current frame
624 if (aBidiLevel
& BIDI_LEVEL_UNDEFINED
)
625 aBidiLevel
= NS_GET_EMBEDDING_LEVEL(theFrame
);
629 nsIFrame
* frameBefore
;
630 nsIFrame
* frameAfter
;
631 uint8_t levelBefore
; // Bidi level of the character before the caret
632 uint8_t levelAfter
; // Bidi level of the character after the caret
634 theFrame
->GetOffsets(start
, end
);
635 if (start
== 0 || end
== 0 || start
== theFrameOffset
|| end
== theFrameOffset
)
637 nsPrevNextBidiLevels levels
= aFrameSelection
->
638 GetPrevNextBidiLevels(aContentNode
, aOffset
, false);
640 /* Boundary condition, we need to know the Bidi levels of the characters before and after the caret */
641 if (levels
.mFrameBefore
|| levels
.mFrameAfter
)
643 frameBefore
= levels
.mFrameBefore
;
644 frameAfter
= levels
.mFrameAfter
;
645 levelBefore
= levels
.mLevelBefore
;
646 levelAfter
= levels
.mLevelAfter
;
648 if ((levelBefore
!= levelAfter
) || (aBidiLevel
!= levelBefore
))
650 aBidiLevel
= std::max(aBidiLevel
, std::min(levelBefore
, levelAfter
)); // rule c3
651 aBidiLevel
= std::min(aBidiLevel
, std::max(levelBefore
, levelAfter
)); // rule c4
652 if (aBidiLevel
== levelBefore
// rule c1
653 || (aBidiLevel
> levelBefore
&& aBidiLevel
< levelAfter
&& !((aBidiLevel
^ levelBefore
) & 1)) // rule c5
654 || (aBidiLevel
< levelBefore
&& aBidiLevel
> levelAfter
&& !((aBidiLevel
^ levelBefore
) & 1))) // rule c9
656 if (theFrame
!= frameBefore
)
658 if (frameBefore
) // if there is a frameBefore, move into it
660 theFrame
= frameBefore
;
661 theFrame
->GetOffsets(start
, end
);
662 theFrameOffset
= end
;
666 // if there is no frameBefore, we must be at the beginning of the line
667 // so we stay with the current frame.
668 // Exception: when the first frame on the line has a different Bidi level from the paragraph level, there is no
669 // real frame for the caret to be in. We have to find the visually first frame on the line.
670 uint8_t baseLevel
= NS_GET_BASE_LEVEL(frameAfter
);
671 if (baseLevel
!= levelAfter
)
673 nsPeekOffsetStruct
pos(eSelectBeginLine
, eDirPrevious
, 0, 0, false, true, false, true);
674 if (NS_SUCCEEDED(frameAfter
->PeekOffset(&pos
))) {
675 theFrame
= pos
.mResultFrame
;
676 theFrameOffset
= pos
.mContentOffset
;
682 else if (aBidiLevel
== levelAfter
// rule c2
683 || (aBidiLevel
> levelBefore
&& aBidiLevel
< levelAfter
&& !((aBidiLevel
^ levelAfter
) & 1)) // rule c6
684 || (aBidiLevel
< levelBefore
&& aBidiLevel
> levelAfter
&& !((aBidiLevel
^ levelAfter
) & 1))) // rule c10
686 if (theFrame
!= frameAfter
)
690 // if there is a frameAfter, move into it
691 theFrame
= frameAfter
;
692 theFrame
->GetOffsets(start
, end
);
693 theFrameOffset
= start
;
697 // if there is no frameAfter, we must be at the end of the line
698 // so we stay with the current frame.
699 // Exception: when the last frame on the line has a different Bidi level from the paragraph level, there is no
700 // real frame for the caret to be in. We have to find the visually last frame on the line.
701 uint8_t baseLevel
= NS_GET_BASE_LEVEL(frameBefore
);
702 if (baseLevel
!= levelBefore
)
704 nsPeekOffsetStruct
pos(eSelectEndLine
, eDirNext
, 0, 0, false, true, false, true);
705 if (NS_SUCCEEDED(frameBefore
->PeekOffset(&pos
))) {
706 theFrame
= pos
.mResultFrame
;
707 theFrameOffset
= pos
.mContentOffset
;
713 else if (aBidiLevel
> levelBefore
&& aBidiLevel
< levelAfter
// rule c7/8
714 && !((levelBefore
^ levelAfter
) & 1) // before and after have the same parity
715 && ((aBidiLevel
^ levelAfter
) & 1)) // caret has different parity
717 if (NS_SUCCEEDED(aFrameSelection
->GetFrameFromLevel(frameAfter
, eDirNext
, aBidiLevel
, &theFrame
)))
719 theFrame
->GetOffsets(start
, end
);
720 levelAfter
= NS_GET_EMBEDDING_LEVEL(theFrame
);
721 if (aBidiLevel
& 1) // c8: caret to the right of the rightmost character
722 theFrameOffset
= (levelAfter
& 1) ? start
: end
;
723 else // c7: caret to the left of the leftmost character
724 theFrameOffset
= (levelAfter
& 1) ? end
: start
;
727 else if (aBidiLevel
< levelBefore
&& aBidiLevel
> levelAfter
// rule c11/12
728 && !((levelBefore
^ levelAfter
) & 1) // before and after have the same parity
729 && ((aBidiLevel
^ levelAfter
) & 1)) // caret has different parity
731 if (NS_SUCCEEDED(aFrameSelection
->GetFrameFromLevel(frameBefore
, eDirPrevious
, aBidiLevel
, &theFrame
)))
733 theFrame
->GetOffsets(start
, end
);
734 levelBefore
= NS_GET_EMBEDDING_LEVEL(theFrame
);
735 if (aBidiLevel
& 1) // c12: caret to the left of the leftmost character
736 theFrameOffset
= (levelBefore
& 1) ? end
: start
;
737 else // c11: caret to the right of the rightmost character
738 theFrameOffset
= (levelBefore
& 1) ? start
: end
;
746 *aReturnFrame
= theFrame
;
747 *aReturnOffset
= theFrameOffset
;
751 size_t nsCaret::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf
) const
753 size_t total
= aMallocSizeOf(this);
755 // We only want the size of the nsWeakReference object, not the PresShell
756 // (since we don't own the PresShell).
757 total
+= mPresShell
->SizeOfOnlyThis(aMallocSizeOf
);
759 if (mDomSelectionWeak
) {
760 // We only want size of the nsWeakReference object, not the selection
761 // (again, we don't own the selection).
762 total
+= mDomSelectionWeak
->SizeOfOnlyThis(aMallocSizeOf
);
765 total
+= mBlinkTimer
->SizeOfIncludingThis(aMallocSizeOf
);
770 bool nsCaret::IsMenuPopupHidingCaret()
773 // Check if there are open popups.
774 nsXULPopupManager
*popMgr
= nsXULPopupManager::GetInstance();
775 nsTArray
<nsIFrame
*> popups
;
776 popMgr
->GetVisiblePopups(popups
);
778 if (popups
.Length() == 0)
779 return false; // No popups, so caret can't be hidden by them.
781 // Get the selection focus content, that's where the caret would
782 // go if it was drawn.
783 nsCOMPtr
<nsIDOMNode
> node
;
784 nsCOMPtr
<nsISelection
> domSelection
= do_QueryReferent(mDomSelectionWeak
);
786 return true; // No selection/caret to draw.
787 domSelection
->GetFocusNode(getter_AddRefs(node
));
789 return true; // No selection/caret to draw.
790 nsCOMPtr
<nsIContent
> caretContent
= do_QueryInterface(node
);
792 return true; // No selection/caret to draw.
794 // If there's a menu popup open before the popup with
795 // the caret, don't show the caret.
796 for (uint32_t i
=0; i
<popups
.Length(); i
++) {
797 nsMenuPopupFrame
* popupFrame
= static_cast<nsMenuPopupFrame
*>(popups
[i
]);
798 nsIContent
* popupContent
= popupFrame
->GetContent();
800 if (nsContentUtils::ContentIsDescendantOf(caretContent
, popupContent
)) {
801 // The caret is in this popup. There were no menu popups before this
802 // popup, so don't hide the caret.
806 if (popupFrame
->PopupType() == ePopupTypeMenu
&& !popupFrame
->IsContextMenu()) {
807 // This is an open menu popup. It does not contain the caret (else we'd
808 // have returned above). Even if the caret is in a subsequent popup,
809 // or another document/frame, it should be hidden.
815 // There are no open menu popups, no need to hide the caret.
820 nsCaret::ComputeCaretRects(nsIFrame
* aFrame
, int32_t aFrameOffset
,
821 nsRect
* aCaretRect
, nsRect
* aHookRect
)
823 NS_ASSERTION(aFrame
, "Should have a frame here");
825 nscoord bidiIndicatorSize
;
826 *aCaretRect
= GetGeometryForFrame(aFrame
, aFrameOffset
, &bidiIndicatorSize
);
828 // on RTL frames the right edge of mCaretRect must be equal to framePos
829 const nsStyleVisibility
* vis
= aFrame
->StyleVisibility();
830 if (NS_STYLE_DIRECTION_RTL
== vis
->mDirection
) {
831 aCaretRect
->x
-= aCaretRect
->width
;
834 // Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction
835 aHookRect
->SetEmpty();
841 nsIBidiKeyboard
* bidiKeyboard
= nsContentUtils::GetBidiKeyboard();
842 // if bidiKeyboard->IsLangRTL() fails, there is no way to tell the
843 // keyboard direction, or the user has no right-to-left keyboard
844 // installed, so we never draw the hook.
845 if (bidiKeyboard
&& NS_SUCCEEDED(bidiKeyboard
->IsLangRTL(&isCaretRTL
))) {
846 // If keyboard language is RTL, draw the hook on the left; if LTR, to the right
847 // The height of the hook rectangle is the same as the width of the caret
849 aHookRect
->SetRect(aCaretRect
->x
+ (isCaretRTL
? bidiIndicatorSize
* -1 : aCaretRect
->width
),
850 aCaretRect
->y
+ bidiIndicatorSize
,
857 void nsCaret::CaretBlinkCallback(nsITimer
* aTimer
, void* aClosure
)
859 nsCaret
* theCaret
= reinterpret_cast<nsCaret
*>(aClosure
);
863 theCaret
->mIsBlinkOn
= !theCaret
->mIsBlinkOn
;
864 theCaret
->SchedulePaint();
868 nsCaret::SetIgnoreUserModify(bool aIgnoreUserModify
)
870 mIgnoreUserModify
= aIgnoreUserModify
;