LCOV - code coverage report
Current view: top level - layout/base - nsCaret.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 125 492 25.4 %
Date: 2017-07-14 16:53:18 Functions: 23 41 56.1 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : /* the caret is the text cursor used, e.g., when editing */
       8             : 
       9             : #include "nsCaret.h"
      10             : 
      11             : #include <algorithm>
      12             : 
      13             : #include "gfxUtils.h"
      14             : #include "mozilla/gfx/2D.h"
      15             : #include "nsCOMPtr.h"
      16             : #include "nsFontMetrics.h"
      17             : #include "nsITimer.h"
      18             : #include "nsFrameSelection.h"
      19             : #include "nsIFrame.h"
      20             : #include "nsIScrollableFrame.h"
      21             : #include "nsIDOMNode.h"
      22             : #include "nsISelection.h"
      23             : #include "nsISelectionPrivate.h"
      24             : #include "nsIContent.h"
      25             : #include "nsIPresShell.h"
      26             : #include "nsLayoutUtils.h"
      27             : #include "nsPresContext.h"
      28             : #include "nsBlockFrame.h"
      29             : #include "nsISelectionController.h"
      30             : #include "nsTextFrame.h"
      31             : #include "nsXULPopupManager.h"
      32             : #include "nsMenuPopupFrame.h"
      33             : #include "nsTextFragment.h"
      34             : #include "mozilla/Preferences.h"
      35             : #include "mozilla/LookAndFeel.h"
      36             : #include "mozilla/dom/Selection.h"
      37             : #include "nsIBidiKeyboard.h"
      38             : #include "nsContentUtils.h"
      39             : 
      40             : using namespace mozilla;
      41             : using namespace mozilla::dom;
      42             : using namespace mozilla::gfx;
      43             : 
      44             : // The bidi indicator hangs off the caret to one side, to show which
      45             : // direction the typing is in. It needs to be at least 2x2 to avoid looking like
      46             : // an insignificant dot
      47             : static const int32_t kMinBidiIndicatorPixels = 2;
      48             : 
      49             : // The default caret blinking rate (in ms of blinking interval)
      50             : static const uint32_t kDefaultCaretBlinkRate = 500;
      51             : 
      52             : /**
      53             :  * Find the first frame in an in-order traversal of the frame subtree rooted
      54             :  * at aFrame which is either a text frame logically at the end of a line,
      55             :  * or which is aStopAtFrame. Return null if no such frame is found. We don't
      56             :  * descend into the children of non-eLineParticipant frames.
      57             :  */
      58             : static nsIFrame*
      59           4 : CheckForTrailingTextFrameRecursive(nsIFrame* aFrame, nsIFrame* aStopAtFrame)
      60             : {
      61           8 :   if (aFrame == aStopAtFrame ||
      62           0 :       ((aFrame->IsTextFrame() &&
      63           0 :        (static_cast<nsTextFrame*>(aFrame))->IsAtEndOfLine())))
      64           4 :     return aFrame;
      65           0 :   if (!aFrame->IsFrameOfType(nsIFrame::eLineParticipant))
      66           0 :     return nullptr;
      67             : 
      68           0 :   for (nsIFrame* f : aFrame->PrincipalChildList())
      69             :   {
      70           0 :     nsIFrame* r = CheckForTrailingTextFrameRecursive(f, aStopAtFrame);
      71           0 :     if (r)
      72           0 :       return r;
      73             :   }
      74           0 :   return nullptr;
      75             : }
      76             : 
      77             : static nsLineBox*
      78           4 : FindContainingLine(nsIFrame* aFrame)
      79             : {
      80           4 :   while (aFrame && aFrame->IsFrameOfType(nsIFrame::eLineParticipant))
      81             :   {
      82           4 :     nsIFrame* parent = aFrame->GetParent();
      83           4 :     nsBlockFrame* blockParent = nsLayoutUtils::GetAsBlock(parent);
      84           4 :     if (blockParent)
      85             :     {
      86             :       bool isValid;
      87           4 :       nsBlockInFlowLineIterator iter(blockParent, aFrame, &isValid);
      88           4 :       return isValid ? iter.GetLine().get() : nullptr;
      89             :     }
      90           0 :     aFrame = parent;
      91             :   }
      92           0 :   return nullptr;
      93             : }
      94             : 
      95             : static void
      96           4 : AdjustCaretFrameForLineEnd(nsIFrame** aFrame, int32_t* aOffset)
      97             : {
      98           4 :   nsLineBox* line = FindContainingLine(*aFrame);
      99           4 :   if (!line)
     100           0 :     return;
     101           4 :   int32_t count = line->GetChildCount();
     102           4 :   for (nsIFrame* f = line->mFirstChild; count > 0; --count, f = f->GetNextSibling())
     103             :   {
     104           4 :     nsIFrame* r = CheckForTrailingTextFrameRecursive(f, *aFrame);
     105           4 :     if (r == *aFrame)
     106           4 :       return;
     107           0 :     if (r)
     108             :     {
     109           0 :       *aFrame = r;
     110           0 :       NS_ASSERTION(r->IsTextFrame(), "Expected text frame");
     111           0 :       *aOffset = (static_cast<nsTextFrame*>(r))->GetContentEnd();
     112           0 :       return;
     113             :     }
     114             :   }
     115             : }
     116             : 
     117             : static bool
     118           0 : IsBidiUI()
     119             : {
     120           0 :   return Preferences::GetBool("bidi.browser.ui");
     121             : }
     122             : 
     123          28 : nsCaret::nsCaret()
     124             : : mOverrideOffset(0)
     125             : , mBlinkCount(-1)
     126             : , mBlinkRate(0)
     127             : , mHideCount(0)
     128             : , mIsBlinkOn(false)
     129             : , mVisible(false)
     130             : , mReadOnly(false)
     131             : , mShowDuringSelection(false)
     132          28 : , mIgnoreUserModify(true)
     133             : {
     134          28 : }
     135             : 
     136          12 : nsCaret::~nsCaret()
     137             : {
     138           4 :   StopBlinking();
     139          12 : }
     140             : 
     141          28 : nsresult nsCaret::Init(nsIPresShell *inPresShell)
     142             : {
     143          28 :   NS_ENSURE_ARG(inPresShell);
     144             : 
     145          28 :   mPresShell = do_GetWeakReference(inPresShell);    // the presshell owns us, so no addref
     146          28 :   NS_ASSERTION(mPresShell, "Hey, pres shell should support weak refs");
     147             : 
     148          28 :   mShowDuringSelection =
     149          28 :     LookAndFeel::GetInt(LookAndFeel::eIntID_ShowCaretDuringSelection,
     150          56 :                         mShowDuringSelection ? 1 : 0) != 0;
     151             : 
     152             :   // get the selection from the pres shell, and set ourselves up as a selection
     153             :   // listener
     154             : 
     155          56 :   nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mPresShell);
     156          28 :   if (!selCon)
     157           0 :     return NS_ERROR_FAILURE;
     158             : 
     159          56 :   nsCOMPtr<nsISelection> domSelection;
     160          56 :   nsresult rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
     161          56 :                                      getter_AddRefs(domSelection));
     162          28 :   if (NS_FAILED(rv))
     163           0 :     return rv;
     164          28 :   if (!domSelection)
     165           0 :     return NS_ERROR_FAILURE;
     166             : 
     167          56 :   nsCOMPtr<nsISelectionPrivate> privateSelection = do_QueryInterface(domSelection);
     168          28 :   if (privateSelection)
     169          28 :     privateSelection->AddSelectionListener(this);
     170          28 :   mDomSelectionWeak = do_GetWeakReference(domSelection);
     171             : 
     172          28 :   return NS_OK;
     173             : }
     174             : 
     175             : static bool
     176           0 : DrawCJKCaret(nsIFrame* aFrame, int32_t aOffset)
     177             : {
     178           0 :   nsIContent* content = aFrame->GetContent();
     179           0 :   const nsTextFragment* frag = content->GetText();
     180           0 :   if (!frag)
     181           0 :     return false;
     182           0 :   if (aOffset < 0 || uint32_t(aOffset) >= frag->GetLength())
     183           0 :     return false;
     184           0 :   char16_t ch = frag->CharAt(aOffset);
     185           0 :   return 0x2e80 <= ch && ch <= 0xd7ff;
     186             : }
     187             : 
     188             : nsCaret::Metrics
     189           0 : nsCaret::ComputeMetrics(nsIFrame* aFrame, int32_t aOffset, nscoord aCaretHeight)
     190             : {
     191             :   // Compute nominal sizes in appunits
     192             :   nscoord caretWidth =
     193           0 :     (aCaretHeight * LookAndFeel::GetFloat(LookAndFeel::eFloatID_CaretAspectRatio, 0.0f)) +
     194           0 :     nsPresContext::CSSPixelsToAppUnits(
     195           0 :         LookAndFeel::GetInt(LookAndFeel::eIntID_CaretWidth, 1));
     196             : 
     197           0 :   if (DrawCJKCaret(aFrame, aOffset)) {
     198           0 :     caretWidth += nsPresContext::CSSPixelsToAppUnits(1);
     199             :   }
     200           0 :   nscoord bidiIndicatorSize = nsPresContext::CSSPixelsToAppUnits(kMinBidiIndicatorPixels);
     201           0 :   bidiIndicatorSize = std::max(caretWidth, bidiIndicatorSize);
     202             : 
     203             :   // Round them to device pixels. Always round down, except that anything
     204             :   // between 0 and 1 goes up to 1 so we don't let the caret disappear.
     205           0 :   int32_t tpp = aFrame->PresContext()->AppUnitsPerDevPixel();
     206             :   Metrics result;
     207           0 :   result.mCaretWidth = NS_ROUND_BORDER_TO_PIXELS(caretWidth, tpp);
     208           0 :   result.mBidiIndicatorSize = NS_ROUND_BORDER_TO_PIXELS(bidiIndicatorSize, tpp);
     209           0 :   return result;
     210             : }
     211             : 
     212           4 : void nsCaret::Terminate()
     213             : {
     214             :   // this doesn't erase the caret if it's drawn. Should it? We might not have
     215             :   // a good drawing environment during teardown.
     216             : 
     217           4 :   StopBlinking();
     218           4 :   mBlinkTimer = nullptr;
     219             : 
     220             :   // unregiser ourselves as a selection listener
     221           8 :   nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
     222           8 :   nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSelection));
     223           4 :   if (privateSelection)
     224           4 :     privateSelection->RemoveSelectionListener(this);
     225           4 :   mDomSelectionWeak = nullptr;
     226           4 :   mPresShell = nullptr;
     227             : 
     228           4 :   mOverrideContent = nullptr;
     229           4 : }
     230             : 
     231         326 : NS_IMPL_ISUPPORTS(nsCaret, nsISelectionListener)
     232             : 
     233           2 : nsISelection* nsCaret::GetSelection()
     234             : {
     235           4 :   nsCOMPtr<nsISelection> sel(do_QueryReferent(mDomSelectionWeak));
     236           4 :   return sel;
     237             : }
     238             : 
     239           0 : void nsCaret::SetSelection(nsISelection *aDOMSel)
     240             : {
     241           0 :   MOZ_ASSERT(aDOMSel);
     242           0 :   mDomSelectionWeak = do_GetWeakReference(aDOMSel);   // weak reference to pres shell
     243           0 :   ResetBlinking();
     244           0 :   SchedulePaint();
     245           0 : }
     246             : 
     247           0 : void nsCaret::SetVisible(bool inMakeVisible)
     248             : {
     249           0 :   mVisible = inMakeVisible;
     250           0 :   mIgnoreUserModify = mVisible;
     251           0 :   ResetBlinking();
     252           0 :   SchedulePaint();
     253           0 : }
     254             : 
     255          52 : bool nsCaret::IsVisible()
     256             : {
     257          52 :   if (!mVisible || mHideCount) {
     258          52 :     return false;
     259             :   }
     260             : 
     261           0 :   if (!mShowDuringSelection) {
     262           0 :     Selection* selection = GetSelectionInternal();
     263           0 :     if (!selection) {
     264           0 :       return false;
     265             :     }
     266             :     bool isCollapsed;
     267           0 :     if (NS_FAILED(selection->GetIsCollapsed(&isCollapsed)) || !isCollapsed) {
     268           0 :       return false;
     269             :     }
     270             :   }
     271             : 
     272           0 :   if (IsMenuPopupHidingCaret()) {
     273           0 :     return false;
     274             :   }
     275             : 
     276           0 :   return true;
     277             : }
     278             : 
     279           0 : void nsCaret::AddForceHide()
     280             : {
     281           0 :   MOZ_ASSERT(mHideCount < UINT32_MAX);
     282           0 :   if (++mHideCount > 1) {
     283           0 :     return;
     284             :   }
     285           0 :   ResetBlinking();
     286           0 :   SchedulePaint();
     287             : }
     288             : 
     289           0 : void nsCaret::RemoveForceHide()
     290             : {
     291           0 :   if (!mHideCount || --mHideCount) {
     292           0 :     return;
     293             :   }
     294           0 :   ResetBlinking();
     295           0 :   SchedulePaint();
     296             : }
     297             : 
     298           2 : void nsCaret::SetCaretReadOnly(bool inMakeReadonly)
     299             : {
     300           2 :   mReadOnly = inMakeReadonly;
     301           2 :   ResetBlinking();
     302           2 :   SchedulePaint();
     303           2 : }
     304             : 
     305             : /* static */ nsRect
     306           0 : nsCaret::GetGeometryForFrame(nsIFrame* aFrame,
     307             :                              int32_t   aFrameOffset,
     308             :                              nscoord*  aBidiIndicatorSize)
     309             : {
     310           0 :   nsPoint framePos(0, 0);
     311           0 :   nsRect rect;
     312           0 :   nsresult rv = aFrame->GetPointFromOffset(aFrameOffset, &framePos);
     313           0 :   if (NS_FAILED(rv)) {
     314           0 :     if (aBidiIndicatorSize) {
     315           0 :       *aBidiIndicatorSize = 0;
     316             :     }
     317           0 :     return rect;
     318             :   }
     319             : 
     320           0 :   nsIFrame* frame = aFrame->GetContentInsertionFrame();
     321           0 :   if (!frame) {
     322           0 :     frame = aFrame;
     323             :   }
     324           0 :   NS_ASSERTION(!(frame->GetStateBits() & NS_FRAME_IN_REFLOW),
     325             :                "We should not be in the middle of reflow");
     326           0 :   nscoord baseline = frame->GetCaretBaseline();
     327           0 :   nscoord ascent = 0, descent = 0;
     328             :   RefPtr<nsFontMetrics> fm =
     329           0 :     nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);
     330           0 :   NS_ASSERTION(fm, "We should be able to get the font metrics");
     331           0 :   if (fm) {
     332           0 :     ascent = fm->MaxAscent();
     333           0 :     descent = fm->MaxDescent();
     334             :   }
     335           0 :   nscoord height = ascent + descent;
     336           0 :   WritingMode wm = aFrame->GetWritingMode();
     337           0 :   bool vertical = wm.IsVertical();
     338           0 :   if (vertical) {
     339           0 :     if (wm.IsLineInverted()) {
     340           0 :       framePos.x = baseline - descent;
     341             :     } else {
     342           0 :       framePos.x = baseline - ascent;
     343             :     }
     344             :   } else {
     345           0 :     framePos.y = baseline - ascent;
     346             :   }
     347           0 :   Metrics caretMetrics = ComputeMetrics(aFrame, aFrameOffset, height);
     348             : 
     349           0 :   nsTextFrame* textFrame = do_QueryFrame(aFrame);
     350           0 :   if (textFrame) {
     351             :     gfxTextRun* textRun =
     352           0 :       textFrame->GetTextRun(nsTextFrame::TextRunType::eInflated);
     353           0 :     if (textRun) {
     354             :       // For "upstream" text where the textrun direction is reversed from the
     355             :       // frame's inline-dir we want the caret to be painted before rather than
     356             :       // after its nominal inline position, so we offset by its width.
     357             :       bool textRunDirIsReverseOfFrame =
     358           0 :         wm.IsInlineReversed() != textRun->IsInlineReversed();
     359             :       // However, in sideways-lr mode we invert this behavior because this is
     360             :       // the one writing mode where bidi-LTR corresponds to inline-reversed
     361             :       // already, which reverses the desired caret placement behavior.
     362             :       // Note that the following condition is equivalent to:
     363             :       //   if ( (!textRun->IsSidewaysLeft() && textRunDirIsReverseOfFrame) ||
     364             :       //        (textRun->IsSidewaysLeft()  && !textRunDirIsReverseOfFrame) )
     365           0 :       if (textRunDirIsReverseOfFrame != textRun->IsSidewaysLeft()) {
     366           0 :         int dir = wm.IsBidiLTR() ? -1 : 1;
     367           0 :         if (vertical) {
     368           0 :           framePos.y += dir * caretMetrics.mCaretWidth;
     369             :         } else {
     370           0 :           framePos.x += dir * caretMetrics.mCaretWidth;
     371             :         }
     372             :       }
     373             :     }
     374             :   }
     375             : 
     376           0 :   rect = nsRect(framePos, vertical ? nsSize(height, caretMetrics.mCaretWidth) :
     377             :                                      nsSize(caretMetrics.mCaretWidth, height));
     378             : 
     379             :   // Clamp the inline-position to be within our scroll frame. If we don't, then
     380             :   // it clips us, and we don't appear at all. See bug 335560.
     381             :   nsIFrame* scrollFrame =
     382           0 :     nsLayoutUtils::GetClosestFrameOfType(aFrame, LayoutFrameType::Scroll);
     383           0 :   if (scrollFrame) {
     384             :     // First, use the scrollFrame to get at the scrollable view that we're in.
     385           0 :     nsIScrollableFrame *sf = do_QueryFrame(scrollFrame);
     386           0 :     nsIFrame *scrolled = sf->GetScrolledFrame();
     387           0 :     nsRect caretInScroll = rect + aFrame->GetOffsetTo(scrolled);
     388             : 
     389             :     // Now see if the caret extends beyond the view's bounds. If it does,
     390             :     // then snap it back, put it as close to the edge as it can.
     391           0 :     if (vertical) {
     392           0 :       nscoord overflow = caretInScroll.YMost() -
     393           0 :         scrolled->GetVisualOverflowRectRelativeToSelf().height;
     394           0 :       if (overflow > 0) {
     395           0 :         rect.y -= overflow;
     396             :       }
     397             :     } else {
     398           0 :       nscoord overflow = caretInScroll.XMost() -
     399           0 :         scrolled->GetVisualOverflowRectRelativeToSelf().width;
     400           0 :       if (overflow > 0) {
     401           0 :         rect.x -= overflow;
     402             :       }
     403             :     }
     404             :   }
     405             : 
     406           0 :   if (aBidiIndicatorSize) {
     407           0 :     *aBidiIndicatorSize = caretMetrics.mBidiIndicatorSize;
     408             :   }
     409           0 :   return rect;
     410             : }
     411             : 
     412             : nsIFrame*
     413           4 : nsCaret::GetFrameAndOffset(Selection* aSelection,
     414             :                            nsINode* aOverrideNode, int32_t aOverrideOffset,
     415             :                            int32_t* aFrameOffset)
     416             : {
     417             :   nsINode* focusNode;
     418             :   int32_t focusOffset;
     419             : 
     420           4 :   if (aOverrideNode) {
     421           4 :     focusNode = aOverrideNode;
     422           4 :     focusOffset = aOverrideOffset;
     423           0 :   } else if (aSelection) {
     424           0 :     focusNode = aSelection->GetFocusNode();
     425           0 :     aSelection->GetFocusOffset(&focusOffset);
     426             :   } else {
     427           0 :     return nullptr;
     428             :   }
     429             : 
     430           4 :   if (!focusNode || !focusNode->IsContent()) {
     431           0 :     return nullptr;
     432             :   }
     433             : 
     434           4 :   nsIContent* contentNode = focusNode->AsContent();
     435           4 :   nsFrameSelection* frameSelection = aSelection->GetFrameSelection();
     436           4 :   nsBidiLevel bidiLevel = frameSelection->GetCaretBidiLevel();
     437             :   nsIFrame* frame;
     438           4 :   nsresult rv = nsCaret::GetCaretFrameForNodeOffset(
     439             :       frameSelection, contentNode, focusOffset,
     440           4 :       frameSelection->GetHint(), bidiLevel, &frame, aFrameOffset);
     441           4 :   if (NS_FAILED(rv) || !frame) {
     442           0 :     return nullptr;
     443             :   }
     444             : 
     445           4 :   return frame;
     446             : }
     447             : 
     448             : /* static */ nsIFrame*
     449           0 : nsCaret::GetGeometry(nsISelection* aSelection, nsRect* aRect)
     450             : {
     451             :   int32_t frameOffset;
     452           0 :   Selection* selection = aSelection ? aSelection->AsSelection() : nullptr;
     453           0 :   nsIFrame* frame = GetFrameAndOffset(selection, nullptr, 0, &frameOffset);
     454           0 :   if (frame) {
     455           0 :     *aRect = GetGeometryForFrame(frame, frameOffset, nullptr);
     456             :   }
     457           0 :   return frame;
     458             : }
     459             : 
     460             : Selection*
     461           2 : nsCaret::GetSelectionInternal()
     462             : {
     463           2 :   nsISelection* domSelection = GetSelection();
     464           2 :   return domSelection ? domSelection->AsSelection() : nullptr;
     465             : }
     466             : 
     467           2 : void nsCaret::SchedulePaint()
     468             : {
     469           2 :   Selection* selection = GetSelectionInternal();
     470             :   nsINode* focusNode;
     471           2 :   if (mOverrideContent) {
     472           0 :     focusNode = mOverrideContent;
     473           2 :   } else if (selection) {
     474           2 :     focusNode = selection->GetFocusNode();
     475             :   } else {
     476           0 :     return;
     477             :   }
     478           2 :   if (!focusNode || !focusNode->IsContent()) {
     479           2 :     return;
     480             :   }
     481           0 :   nsIFrame* f = focusNode->AsContent()->GetPrimaryFrame();
     482           0 :   if (!f) {
     483           0 :     return;
     484             :   }
     485             :   // This may not be the correct continuation frame, but that's OK since we're
     486             :   // just scheduling a paint of the window (or popup).
     487           0 :   f->SchedulePaint();
     488             : }
     489             : 
     490           0 : void nsCaret::SetVisibilityDuringSelection(bool aVisibility)
     491             : {
     492           0 :   mShowDuringSelection = aVisibility;
     493           0 :   SchedulePaint();
     494           0 : }
     495             : 
     496             : void
     497           0 : nsCaret::SetCaretPosition(nsIDOMNode* aNode, int32_t aOffset)
     498             : {
     499           0 :   mOverrideContent = do_QueryInterface(aNode);
     500           0 :   mOverrideOffset = aOffset;
     501             : 
     502           0 :   ResetBlinking();
     503           0 :   SchedulePaint();
     504           0 : }
     505             : 
     506             : void
     507           0 : nsCaret::CheckSelectionLanguageChange()
     508             : {
     509           0 :   if (!IsBidiUI()) {
     510           0 :     return;
     511             :   }
     512             : 
     513           0 :   bool isKeyboardRTL = false;
     514           0 :   nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
     515           0 :   if (bidiKeyboard) {
     516           0 :     bidiKeyboard->IsLangRTL(&isKeyboardRTL);
     517             :   }
     518             :   // Call SelectionLanguageChange on every paint. Mostly it will be a noop
     519             :   // but it should be fast anyway. This guarantees we never paint the caret
     520             :   // at the wrong place.
     521           0 :   Selection* selection = GetSelectionInternal();
     522           0 :   if (selection) {
     523           0 :     selection->SelectionLanguageChange(isKeyboardRTL);
     524             :   }
     525             : }
     526             : 
     527             : nsIFrame*
     528          26 : nsCaret::GetPaintGeometry(nsRect* aRect)
     529             : {
     530             :   // Return null if we should not be visible.
     531          26 :   if (!IsVisible() || !mIsBlinkOn) {
     532          26 :     return nullptr;
     533             :   }
     534             : 
     535             :   // Update selection language direction now so the new direction will be
     536             :   // taken into account when computing the caret position below.
     537           0 :   CheckSelectionLanguageChange();
     538             : 
     539             :   int32_t frameOffset;
     540           0 :   nsIFrame *frame = GetFrameAndOffset(GetSelectionInternal(),
     541           0 :       mOverrideContent, mOverrideOffset, &frameOffset);
     542           0 :   if (!frame) {
     543           0 :     return nullptr;
     544             :   }
     545             : 
     546             :   // now we have a frame, check whether it's appropriate to show the caret here
     547           0 :   const nsStyleUserInterface* userinterface = frame->StyleUserInterface();
     548           0 :   if ((!mIgnoreUserModify &&
     549           0 :        userinterface->mUserModify == StyleUserModify::ReadOnly) ||
     550           0 :       userinterface->mUserInput == StyleUserInput::None ||
     551           0 :       userinterface->mUserInput == StyleUserInput::Disabled) {
     552           0 :     return nullptr;
     553             :   }
     554             : 
     555             :   // If the offset falls outside of the frame, then don't paint the caret.
     556             :   int32_t startOffset, endOffset;
     557           0 :   if (frame->IsTextFrame() &&
     558           0 :       (NS_FAILED(frame->GetOffsets(startOffset, endOffset)) ||
     559           0 :        startOffset > frameOffset || endOffset < frameOffset)) {
     560           0 :     return nullptr;
     561             :   }
     562             : 
     563           0 :   nsRect caretRect;
     564           0 :   nsRect hookRect;
     565           0 :   ComputeCaretRects(frame, frameOffset, &caretRect, &hookRect);
     566             : 
     567           0 :   aRect->UnionRect(caretRect, hookRect);
     568           0 :   return frame;
     569             : }
     570             : 
     571             : nsIFrame*
     572           0 : nsCaret::GetFrame(int32_t* aContentOffset) {
     573           0 :   return GetFrameAndOffset(GetSelectionInternal(),
     574             :                            mOverrideContent,
     575             :                            mOverrideOffset,
     576           0 :                            aContentOffset);
     577             : }
     578             : 
     579           0 : void nsCaret::PaintCaret(DrawTarget& aDrawTarget,
     580             :                          nsIFrame* aForFrame,
     581             :                          const nsPoint &aOffset)
     582             : {
     583             :   int32_t contentOffset;
     584           0 :   nsIFrame* frame = GetFrame(&contentOffset);
     585           0 :   if (!frame) {
     586           0 :     return;
     587             :   }
     588           0 :   NS_ASSERTION(frame == aForFrame, "We're referring different frame");
     589             : 
     590           0 :   int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
     591             : 
     592           0 :   nsRect caretRect;
     593           0 :   nsRect hookRect;
     594           0 :   ComputeCaretRects(frame, contentOffset, &caretRect, &hookRect);
     595             : 
     596             :   Rect devPxCaretRect =
     597           0 :     NSRectToSnappedRect(caretRect + aOffset, appUnitsPerDevPixel, aDrawTarget);
     598             :   Rect devPxHookRect =
     599           0 :     NSRectToSnappedRect(hookRect + aOffset, appUnitsPerDevPixel, aDrawTarget);
     600           0 :   ColorPattern color(ToDeviceColor(frame->GetCaretColorAt(contentOffset)));
     601             : 
     602           0 :   aDrawTarget.FillRect(devPxCaretRect, color);
     603           0 :   if (!hookRect.IsEmpty()) {
     604           0 :     aDrawTarget.FillRect(devPxHookRect, color);
     605             :   }
     606             : }
     607             : 
     608             : NS_IMETHODIMP
     609          23 : nsCaret::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aDomSel,
     610             :                                 int16_t aReason)
     611             : {
     612          23 :   if ((aReason & nsISelectionListener::MOUSEUP_REASON) || !IsVisible())//this wont do
     613          23 :     return NS_OK;
     614             : 
     615           0 :   nsCOMPtr<nsISelection> domSel(do_QueryReferent(mDomSelectionWeak));
     616             : 
     617             :   // The same caret is shared amongst the document and any text widgets it
     618             :   // may contain. This means that the caret could get notifications from
     619             :   // multiple selections.
     620             :   //
     621             :   // If this notification is for a selection that is not the one the
     622             :   // the caret is currently interested in (mDomSelectionWeak), then there
     623             :   // is nothing to do!
     624             : 
     625           0 :   if (domSel != aDomSel)
     626           0 :     return NS_OK;
     627             : 
     628           0 :   ResetBlinking();
     629           0 :   SchedulePaint();
     630             : 
     631           0 :   return NS_OK;
     632             : }
     633             : 
     634           2 : void nsCaret::ResetBlinking()
     635             : {
     636           2 :   mIsBlinkOn = true;
     637             : 
     638           2 :   if (mReadOnly || !mVisible || mHideCount) {
     639           2 :     StopBlinking();
     640           2 :     return;
     641             :   }
     642             : 
     643             :   uint32_t blinkRate = static_cast<uint32_t>(
     644           0 :     LookAndFeel::GetInt(LookAndFeel::eIntID_CaretBlinkTime,
     645           0 :                         kDefaultCaretBlinkRate));
     646           0 :   if (mBlinkRate == blinkRate) {
     647             :     // If the rate hasn't changed, then there is nothing to do.
     648           0 :     return;
     649             :   }
     650           0 :   mBlinkRate = blinkRate;
     651             : 
     652           0 :   if (mBlinkTimer) {
     653           0 :     mBlinkTimer->Cancel();
     654             :   } else {
     655             :     nsresult  err;
     656           0 :     mBlinkTimer = do_CreateInstance("@mozilla.org/timer;1", &err);
     657           0 :     if (NS_FAILED(err))
     658           0 :       return;
     659             :   }
     660             : 
     661           0 :   if (blinkRate > 0) {
     662           0 :     mBlinkCount = Preferences::GetInt("ui.caretBlinkCount", -1);
     663           0 :     mBlinkTimer->InitWithNamedFuncCallback(CaretBlinkCallback, this, blinkRate,
     664             :                                            nsITimer::TYPE_REPEATING_SLACK,
     665           0 :                                            "nsCaret::CaretBlinkCallback_timer");
     666             :   }
     667             : }
     668             : 
     669          10 : void nsCaret::StopBlinking()
     670             : {
     671          10 :   if (mBlinkTimer)
     672             :   {
     673           0 :     mBlinkTimer->Cancel();
     674           0 :     mBlinkRate = 0;
     675             :   }
     676          10 : }
     677             : 
     678             : nsresult
     679           4 : nsCaret::GetCaretFrameForNodeOffset(nsFrameSelection*    aFrameSelection,
     680             :                                     nsIContent*          aContentNode,
     681             :                                     int32_t              aOffset,
     682             :                                     CaretAssociationHint aFrameHint,
     683             :                                     nsBidiLevel          aBidiLevel,
     684             :                                     nsIFrame**           aReturnFrame,
     685             :                                     int32_t*             aReturnOffset)
     686             : {
     687           4 :   if (!aFrameSelection)
     688           0 :     return NS_ERROR_FAILURE;
     689           4 :   nsIPresShell* presShell = aFrameSelection->GetShell();
     690           4 :   if (!presShell)
     691           0 :     return NS_ERROR_FAILURE;
     692             : 
     693           8 :   if (!aContentNode || !aContentNode->IsInComposedDoc() ||
     694           4 :       presShell->GetDocument() != aContentNode->GetComposedDoc())
     695           0 :     return NS_ERROR_FAILURE;
     696             : 
     697           4 :   nsIFrame* theFrame = nullptr;
     698           4 :   int32_t   theFrameOffset = 0;
     699             : 
     700           4 :   theFrame = aFrameSelection->GetFrameForNodeOffset(
     701             :       aContentNode, aOffset, aFrameHint, &theFrameOffset);
     702           4 :   if (!theFrame)
     703           0 :     return NS_ERROR_FAILURE;
     704             : 
     705             :   // if theFrame is after a text frame that's logically at the end of the line
     706             :   // (e.g. if theFrame is a <br> frame), then put the caret at the end of
     707             :   // that text frame instead. This way, the caret will be positioned as if
     708             :   // trailing whitespace was not trimmed.
     709           4 :   AdjustCaretFrameForLineEnd(&theFrame, &theFrameOffset);
     710             : 
     711             :   // Mamdouh : modification of the caret to work at rtl and ltr with Bidi
     712             :   //
     713             :   // Direction Style from visibility->mDirection
     714             :   // ------------------
     715             :   // NS_STYLE_DIRECTION_LTR : LTR or Default
     716             :   // NS_STYLE_DIRECTION_RTL
     717           4 :   if (theFrame->PresContext()->BidiEnabled())
     718             :   {
     719             :     // If there has been a reflow, take the caret Bidi level to be the level of the current frame
     720           0 :     if (aBidiLevel & BIDI_LEVEL_UNDEFINED) {
     721           0 :       aBidiLevel = theFrame->GetEmbeddingLevel();
     722             :     }
     723             : 
     724             :     int32_t start;
     725             :     int32_t end;
     726             :     nsIFrame* frameBefore;
     727             :     nsIFrame* frameAfter;
     728             :     nsBidiLevel levelBefore; // Bidi level of the character before the caret
     729             :     nsBidiLevel levelAfter;  // Bidi level of the character after the caret
     730             : 
     731           0 :     theFrame->GetOffsets(start, end);
     732           0 :     if (start == 0 || end == 0 || start == theFrameOffset || end == theFrameOffset)
     733             :     {
     734             :       nsPrevNextBidiLevels levels = aFrameSelection->
     735           0 :         GetPrevNextBidiLevels(aContentNode, aOffset, false);
     736             : 
     737             :       /* Boundary condition, we need to know the Bidi levels of the characters before and after the caret */
     738           0 :       if (levels.mFrameBefore || levels.mFrameAfter)
     739             :       {
     740           0 :         frameBefore = levels.mFrameBefore;
     741           0 :         frameAfter = levels.mFrameAfter;
     742           0 :         levelBefore = levels.mLevelBefore;
     743           0 :         levelAfter = levels.mLevelAfter;
     744             : 
     745           0 :         if ((levelBefore != levelAfter) || (aBidiLevel != levelBefore))
     746             :         {
     747           0 :           aBidiLevel = std::max(aBidiLevel, std::min(levelBefore, levelAfter));                                  // rule c3
     748           0 :           aBidiLevel = std::min(aBidiLevel, std::max(levelBefore, levelAfter));                                  // rule c4
     749           0 :           if (aBidiLevel == levelBefore                                                                      // rule c1
     750           0 :               || (aBidiLevel > levelBefore && aBidiLevel < levelAfter &&
     751           0 :                   IS_SAME_DIRECTION(aBidiLevel, levelBefore))   // rule c5
     752           0 :               || (aBidiLevel < levelBefore && aBidiLevel > levelAfter &&
     753           0 :                   IS_SAME_DIRECTION(aBidiLevel, levelBefore)))  // rule c9
     754             :           {
     755           0 :             if (theFrame != frameBefore)
     756             :             {
     757           0 :               if (frameBefore) // if there is a frameBefore, move into it
     758             :               {
     759           0 :                 theFrame = frameBefore;
     760           0 :                 theFrame->GetOffsets(start, end);
     761           0 :                 theFrameOffset = end;
     762             :               }
     763             :               else
     764             :               {
     765             :                 // if there is no frameBefore, we must be at the beginning of the line
     766             :                 // so we stay with the current frame.
     767             :                 // Exception: when the first frame on the line has a different Bidi level from the paragraph level, there is no
     768             :                 // real frame for the caret to be in. We have to find the visually first frame on the line.
     769           0 :                 nsBidiLevel baseLevel = frameAfter->GetBaseLevel();
     770           0 :                 if (baseLevel != levelAfter)
     771             :                 {
     772             :                   nsPeekOffsetStruct pos(eSelectBeginLine, eDirPrevious, 0,
     773           0 :                                          nsPoint(0, 0), false, true, false,
     774           0 :                                          true, false);
     775           0 :                   if (NS_SUCCEEDED(frameAfter->PeekOffset(&pos))) {
     776           0 :                     theFrame = pos.mResultFrame;
     777           0 :                     theFrameOffset = pos.mContentOffset;
     778             :                   }
     779             :                 }
     780             :               }
     781           0 :             }
     782             :           }
     783           0 :           else if (aBidiLevel == levelAfter                                                                     // rule c2
     784           0 :                    || (aBidiLevel > levelBefore && aBidiLevel < levelAfter &&
     785           0 :                        IS_SAME_DIRECTION(aBidiLevel, levelAfter))   // rule c6
     786           0 :                    || (aBidiLevel < levelBefore && aBidiLevel > levelAfter &&
     787           0 :                        IS_SAME_DIRECTION(aBidiLevel, levelAfter)))  // rule c10
     788             :           {
     789           0 :             if (theFrame != frameAfter)
     790             :             {
     791           0 :               if (frameAfter)
     792             :               {
     793             :                 // if there is a frameAfter, move into it
     794           0 :                 theFrame = frameAfter;
     795           0 :                 theFrame->GetOffsets(start, end);
     796           0 :                 theFrameOffset = start;
     797             :               }
     798             :               else
     799             :               {
     800             :                 // if there is no frameAfter, we must be at the end of the line
     801             :                 // so we stay with the current frame.
     802             :                 // Exception: when the last frame on the line has a different Bidi level from the paragraph level, there is no
     803             :                 // real frame for the caret to be in. We have to find the visually last frame on the line.
     804           0 :                 nsBidiLevel baseLevel = frameBefore->GetBaseLevel();
     805           0 :                 if (baseLevel != levelBefore)
     806             :                 {
     807             :                   nsPeekOffsetStruct pos(eSelectEndLine, eDirNext, 0,
     808           0 :                                          nsPoint(0, 0), false, true, false,
     809           0 :                                          true, false);
     810           0 :                   if (NS_SUCCEEDED(frameBefore->PeekOffset(&pos))) {
     811           0 :                     theFrame = pos.mResultFrame;
     812           0 :                     theFrameOffset = pos.mContentOffset;
     813             :                   }
     814             :                 }
     815             :               }
     816           0 :             }
     817             :           }
     818           0 :           else if (aBidiLevel > levelBefore && aBidiLevel < levelAfter  // rule c7/8
     819           0 :                    && IS_SAME_DIRECTION(levelBefore, levelAfter)        // before and after have the same parity
     820           0 :                    && !IS_SAME_DIRECTION(aBidiLevel, levelAfter))       // caret has different parity
     821             :           {
     822           0 :             if (NS_SUCCEEDED(aFrameSelection->GetFrameFromLevel(frameAfter, eDirNext, aBidiLevel, &theFrame)))
     823             :             {
     824           0 :               theFrame->GetOffsets(start, end);
     825           0 :               levelAfter = theFrame->GetEmbeddingLevel();
     826           0 :               if (IS_LEVEL_RTL(aBidiLevel)) // c8: caret to the right of the rightmost character
     827           0 :                 theFrameOffset = IS_LEVEL_RTL(levelAfter) ? start : end;
     828             :               else               // c7: caret to the left of the leftmost character
     829           0 :                 theFrameOffset = IS_LEVEL_RTL(levelAfter) ? end : start;
     830             :             }
     831             :           }
     832           0 :           else if (aBidiLevel < levelBefore && aBidiLevel > levelAfter  // rule c11/12
     833           0 :                    && IS_SAME_DIRECTION(levelBefore, levelAfter)        // before and after have the same parity
     834           0 :                    && !IS_SAME_DIRECTION(aBidiLevel, levelAfter))       // caret has different parity
     835             :           {
     836           0 :             if (NS_SUCCEEDED(aFrameSelection->GetFrameFromLevel(frameBefore, eDirPrevious, aBidiLevel, &theFrame)))
     837             :             {
     838           0 :               theFrame->GetOffsets(start, end);
     839           0 :               levelBefore = theFrame->GetEmbeddingLevel();
     840           0 :               if (IS_LEVEL_RTL(aBidiLevel)) // c12: caret to the left of the leftmost character
     841           0 :                 theFrameOffset = IS_LEVEL_RTL(levelBefore) ? end : start;
     842             :               else               // c11: caret to the right of the rightmost character
     843           0 :                 theFrameOffset = IS_LEVEL_RTL(levelBefore) ? start : end;
     844             :             }
     845             :           }
     846             :         }
     847             :       }
     848             :     }
     849             :   }
     850             : 
     851           4 :   *aReturnFrame = theFrame;
     852           4 :   *aReturnOffset = theFrameOffset;
     853           4 :   return NS_OK;
     854             : }
     855             : 
     856          21 : size_t nsCaret::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     857             : {
     858          21 :   size_t total = aMallocSizeOf(this);
     859          21 :   if (mPresShell) {
     860             :     // We only want the size of the nsWeakReference object, not the PresShell
     861             :     // (since we don't own the PresShell).
     862          21 :     total += mPresShell->SizeOfOnlyThis(aMallocSizeOf);
     863             :   }
     864          21 :   if (mDomSelectionWeak) {
     865             :     // We only want size of the nsWeakReference object, not the selection
     866             :     // (again, we don't own the selection).
     867          21 :     total += mDomSelectionWeak->SizeOfOnlyThis(aMallocSizeOf);
     868             :   }
     869          21 :   if (mBlinkTimer) {
     870           0 :     total += mBlinkTimer->SizeOfIncludingThis(aMallocSizeOf);
     871             :   }
     872          21 :   return total;
     873             : }
     874             : 
     875           0 : bool nsCaret::IsMenuPopupHidingCaret()
     876             : {
     877             : #ifdef MOZ_XUL
     878             :   // Check if there are open popups.
     879           0 :   nsXULPopupManager *popMgr = nsXULPopupManager::GetInstance();
     880           0 :   nsTArray<nsIFrame*> popups;
     881           0 :   popMgr->GetVisiblePopups(popups);
     882             : 
     883           0 :   if (popups.Length() == 0)
     884           0 :     return false; // No popups, so caret can't be hidden by them.
     885             : 
     886             :   // Get the selection focus content, that's where the caret would
     887             :   // go if it was drawn.
     888           0 :   nsCOMPtr<nsIDOMNode> node;
     889           0 :   nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
     890           0 :   if (!domSelection)
     891           0 :     return true; // No selection/caret to draw.
     892           0 :   domSelection->GetFocusNode(getter_AddRefs(node));
     893           0 :   if (!node)
     894           0 :     return true; // No selection/caret to draw.
     895           0 :   nsCOMPtr<nsIContent> caretContent = do_QueryInterface(node);
     896           0 :   if (!caretContent)
     897           0 :     return true; // No selection/caret to draw.
     898             : 
     899             :   // If there's a menu popup open before the popup with
     900             :   // the caret, don't show the caret.
     901           0 :   for (uint32_t i=0; i<popups.Length(); i++) {
     902           0 :     nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame*>(popups[i]);
     903           0 :     nsIContent* popupContent = popupFrame->GetContent();
     904             : 
     905           0 :     if (nsContentUtils::ContentIsDescendantOf(caretContent, popupContent)) {
     906             :       // The caret is in this popup. There were no menu popups before this
     907             :       // popup, so don't hide the caret.
     908           0 :       return false;
     909             :     }
     910             : 
     911           0 :     if (popupFrame->PopupType() == ePopupTypeMenu && !popupFrame->IsContextMenu()) {
     912             :       // This is an open menu popup. It does not contain the caret (else we'd
     913             :       // have returned above). Even if the caret is in a subsequent popup,
     914             :       // or another document/frame, it should be hidden.
     915           0 :       return true;
     916             :     }
     917             :   }
     918             : #endif
     919             : 
     920             :   // There are no open menu popups, no need to hide the caret.
     921           0 :   return false;
     922             : }
     923             : 
     924             : void
     925           0 : nsCaret::ComputeCaretRects(nsIFrame* aFrame, int32_t aFrameOffset,
     926             :                            nsRect* aCaretRect, nsRect* aHookRect)
     927             : {
     928           0 :   NS_ASSERTION(aFrame, "Should have a frame here");
     929             : 
     930           0 :   WritingMode wm = aFrame->GetWritingMode();
     931           0 :   bool isVertical = wm.IsVertical();
     932             : 
     933             :   nscoord bidiIndicatorSize;
     934           0 :   *aCaretRect = GetGeometryForFrame(aFrame, aFrameOffset, &bidiIndicatorSize);
     935             : 
     936             :   // on RTL frames the right edge of mCaretRect must be equal to framePos
     937           0 :   const nsStyleVisibility* vis = aFrame->StyleVisibility();
     938           0 :   if (NS_STYLE_DIRECTION_RTL == vis->mDirection) {
     939           0 :     if (isVertical) {
     940           0 :       aCaretRect->y -= aCaretRect->height;
     941             :     } else {
     942           0 :       aCaretRect->x -= aCaretRect->width;
     943             :     }
     944             :   }
     945             : 
     946             :   // Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction
     947           0 :   aHookRect->SetEmpty();
     948           0 :   if (!IsBidiUI()) {
     949           0 :     return;
     950             :   }
     951             : 
     952             :   bool isCaretRTL;
     953           0 :   nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
     954             :   // if bidiKeyboard->IsLangRTL() fails, there is no way to tell the
     955             :   // keyboard direction, or the user has no right-to-left keyboard
     956             :   // installed, so we never draw the hook.
     957           0 :   if (bidiKeyboard && NS_SUCCEEDED(bidiKeyboard->IsLangRTL(&isCaretRTL))) {
     958             :     // If keyboard language is RTL, draw the hook on the left; if LTR, to the right
     959             :     // The height of the hook rectangle is the same as the width of the caret
     960             :     // rectangle.
     961           0 :     if (isVertical) {
     962           0 :       bool isSidewaysLR = wm.IsVerticalLR() && !wm.IsLineInverted();
     963           0 :       if (isSidewaysLR) {
     964           0 :         aHookRect->SetRect(aCaretRect->x + bidiIndicatorSize,
     965           0 :                            aCaretRect->y + (!isCaretRTL ? bidiIndicatorSize * -1 :
     966             :                                                           aCaretRect->height),
     967             :                            aCaretRect->height,
     968           0 :                            bidiIndicatorSize);
     969             :       } else {
     970           0 :         aHookRect->SetRect(aCaretRect->XMost() - bidiIndicatorSize,
     971           0 :                            aCaretRect->y + (isCaretRTL ? bidiIndicatorSize * -1 :
     972             :                                                          aCaretRect->height),
     973             :                            aCaretRect->height,
     974           0 :                            bidiIndicatorSize);
     975             :       }
     976             :     } else {
     977           0 :       aHookRect->SetRect(aCaretRect->x + (isCaretRTL ? bidiIndicatorSize * -1 :
     978             :                                                        aCaretRect->width),
     979           0 :                          aCaretRect->y + bidiIndicatorSize,
     980             :                          bidiIndicatorSize,
     981           0 :                          aCaretRect->width);
     982             :     }
     983             :   }
     984             : }
     985             : 
     986             : /* static */
     987           0 : void nsCaret::CaretBlinkCallback(nsITimer* aTimer, void* aClosure)
     988             : {
     989           0 :   nsCaret* theCaret = reinterpret_cast<nsCaret*>(aClosure);
     990           0 :   if (!theCaret) {
     991           0 :     return;
     992             :   }
     993           0 :   theCaret->mIsBlinkOn = !theCaret->mIsBlinkOn;
     994           0 :   theCaret->SchedulePaint();
     995             : 
     996             :   // mBlinkCount of -1 means blink count is not enabled.
     997           0 :   if (theCaret->mBlinkCount == -1) {
     998           0 :     return;
     999             :   }
    1000             : 
    1001             :   // Track the blink count, but only at end of a blink cycle.
    1002           0 :   if (!theCaret->mIsBlinkOn) {
    1003             :     // If we exceeded the blink count, stop the timer.
    1004           0 :     if (--theCaret->mBlinkCount <= 0) {
    1005           0 :       theCaret->StopBlinking();
    1006             :     }
    1007             :   }
    1008             : }
    1009             : 
    1010             : void
    1011           0 : nsCaret::SetIgnoreUserModify(bool aIgnoreUserModify)
    1012             : {
    1013           0 :   mIgnoreUserModify = aIgnoreUserModify;
    1014           0 :   SchedulePaint();
    1015           0 : }

Generated by: LCOV version 1.13