LCOV - code coverage report
Current view: directory - layout/base - nsCaret.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 526 0 0.0 %
Date: 2012-06-02 Functions: 45 0 0.0 %

       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
       5                 :  *
       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/
      10                 :  *
      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.
      15                 :  *
      16                 :  * The Original Code is mozilla.org code.
      17                 :  *
      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.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Pierre Phaneuf <pp@ludusdesign.com>
      25                 :  *   Mats Palmgren <matspal@gmail.com>
      26                 :  *
      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.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : /* the caret is the text cursor used, e.g., when editing */
      42                 : 
      43                 : #include "nsCOMPtr.h"
      44                 : 
      45                 : #include "nsITimer.h"
      46                 : 
      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 "nsISelection.h"
      55                 : #include "nsISelectionPrivate.h"
      56                 : #include "nsIDOMCharacterData.h"
      57                 : #include "nsIContent.h"
      58                 : #include "nsIPresShell.h"
      59                 : #include "nsRenderingContext.h"
      60                 : #include "nsPresContext.h"
      61                 : #include "nsBlockFrame.h"
      62                 : #include "nsISelectionController.h"
      63                 : #include "nsDisplayList.h"
      64                 : #include "nsCaret.h"
      65                 : #include "nsTextFrame.h"
      66                 : #include "nsXULPopupManager.h"
      67                 : #include "nsMenuPopupFrame.h"
      68                 : #include "nsTextFragment.h"
      69                 : #include "nsThemeConstants.h"
      70                 : #include "mozilla/Preferences.h"
      71                 : #include "mozilla/LookAndFeel.h"
      72                 : 
      73                 : // The bidi indicator hangs off the caret to one side, to show which
      74                 : // direction the typing is in. It needs to be at least 2x2 to avoid looking like 
      75                 : // an insignificant dot
      76                 : static const PRInt32 kMinBidiIndicatorPixels = 2;
      77                 : 
      78                 : #ifdef IBMBIDI
      79                 : #include "nsIBidiKeyboard.h"
      80                 : #include "nsContentUtils.h"
      81                 : #endif //IBMBIDI
      82                 : 
      83                 : using namespace mozilla;
      84                 : 
      85                 : /**
      86                 :  * Find the first frame in an in-order traversal of the frame subtree rooted
      87                 :  * at aFrame which is either a text frame logically at the end of a line,
      88                 :  * or which is aStopAtFrame. Return null if no such frame is found. We don't
      89                 :  * descend into the children of non-eLineParticipant frames.
      90                 :  */
      91                 : static nsIFrame*
      92               0 : CheckForTrailingTextFrameRecursive(nsIFrame* aFrame, nsIFrame* aStopAtFrame)
      93                 : {
      94               0 :   if (aFrame == aStopAtFrame ||
      95               0 :       ((aFrame->GetType() == nsGkAtoms::textFrame &&
      96               0 :        (static_cast<nsTextFrame*>(aFrame))->IsAtEndOfLine())))
      97               0 :     return aFrame;
      98               0 :   if (!aFrame->IsFrameOfType(nsIFrame::eLineParticipant))
      99               0 :     return nsnull;
     100                 : 
     101               0 :   for (nsIFrame* f = aFrame->GetFirstPrincipalChild(); f; f = f->GetNextSibling())
     102                 :   {
     103               0 :     nsIFrame* r = CheckForTrailingTextFrameRecursive(f, aStopAtFrame);
     104               0 :     if (r)
     105               0 :       return r;
     106                 :   }
     107               0 :   return nsnull;
     108                 : }
     109                 : 
     110                 : static nsLineBox*
     111               0 : FindContainingLine(nsIFrame* aFrame)
     112                 : {
     113               0 :   while (aFrame && aFrame->IsFrameOfType(nsIFrame::eLineParticipant))
     114                 :   {
     115               0 :     nsIFrame* parent = aFrame->GetParent();
     116               0 :     nsBlockFrame* blockParent = nsLayoutUtils::GetAsBlock(parent);
     117               0 :     if (blockParent)
     118                 :     {
     119                 :       bool isValid;
     120               0 :       nsBlockInFlowLineIterator iter(blockParent, aFrame, &isValid);
     121               0 :       return isValid ? iter.GetLine().get() : nsnull;
     122                 :     }
     123               0 :     aFrame = parent;
     124                 :   }
     125               0 :   return nsnull;
     126                 : }
     127                 : 
     128                 : static void
     129               0 : AdjustCaretFrameForLineEnd(nsIFrame** aFrame, PRInt32* aOffset)
     130                 : {
     131               0 :   nsLineBox* line = FindContainingLine(*aFrame);
     132               0 :   if (!line)
     133               0 :     return;
     134               0 :   PRInt32 count = line->GetChildCount();
     135               0 :   for (nsIFrame* f = line->mFirstChild; count > 0; --count, f = f->GetNextSibling())
     136                 :   {
     137               0 :     nsIFrame* r = CheckForTrailingTextFrameRecursive(f, *aFrame);
     138               0 :     if (r == *aFrame)
     139               0 :       return;
     140               0 :     if (r)
     141                 :     {
     142               0 :       *aFrame = r;
     143               0 :       NS_ASSERTION(r->GetType() == nsGkAtoms::textFrame, "Expected text frame");
     144               0 :       *aOffset = (static_cast<nsTextFrame*>(r))->GetContentEnd();
     145               0 :       return;
     146                 :     }
     147                 :   }
     148                 : }
     149                 : 
     150                 : //-----------------------------------------------------------------------------
     151                 : 
     152               0 : nsCaret::nsCaret()
     153                 : : mPresShell(nsnull)
     154                 : , mBlinkRate(500)
     155                 : , mVisible(false)
     156                 : , mDrawn(false)
     157                 : , mPendingDraw(false)
     158                 : , mReadOnly(false)
     159                 : , mShowDuringSelection(false)
     160                 : , mIgnoreUserModify(true)
     161                 : #ifdef IBMBIDI
     162                 : , mKeyboardRTL(false)
     163                 : , mLastBidiLevel(0)
     164                 : #endif
     165                 : , mLastContentOffset(0)
     166               0 : , mLastHint(nsFrameSelection::HINTLEFT)
     167                 : {
     168               0 : }
     169                 : 
     170                 : //-----------------------------------------------------------------------------
     171               0 : nsCaret::~nsCaret()
     172                 : {
     173               0 :   KillTimer();
     174               0 : }
     175                 : 
     176                 : //-----------------------------------------------------------------------------
     177               0 : nsresult nsCaret::Init(nsIPresShell *inPresShell)
     178                 : {
     179               0 :   NS_ENSURE_ARG(inPresShell);
     180                 : 
     181               0 :   mPresShell = do_GetWeakReference(inPresShell);    // the presshell owns us, so no addref
     182               0 :   NS_ASSERTION(mPresShell, "Hey, pres shell should support weak refs");
     183                 : 
     184                 :   // XXX we should just do this LookAndFeel consultation every time
     185                 :   // we need these values.
     186               0 :   mCaretWidthCSSPx = LookAndFeel::GetInt(LookAndFeel::eIntID_CaretWidth, 1);
     187                 :   mCaretAspectRatio =
     188               0 :     LookAndFeel::GetFloat(LookAndFeel::eFloatID_CaretAspectRatio, 0.0f);
     189                 : 
     190                 :   mBlinkRate = static_cast<PRUint32>(
     191               0 :     LookAndFeel::GetInt(LookAndFeel::eIntID_CaretBlinkTime, mBlinkRate));
     192                 :   mShowDuringSelection =
     193                 :     LookAndFeel::GetInt(LookAndFeel::eIntID_ShowCaretDuringSelection,
     194               0 :                         mShowDuringSelection ? 1 : 0) != 0;
     195                 : 
     196                 :   // get the selection from the pres shell, and set ourselves up as a selection
     197                 :   // listener
     198                 : 
     199               0 :   nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mPresShell);
     200               0 :   if (!selCon)
     201               0 :     return NS_ERROR_FAILURE;
     202                 : 
     203               0 :   nsCOMPtr<nsISelection> domSelection;
     204               0 :   nsresult rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
     205               0 :                                      getter_AddRefs(domSelection));
     206               0 :   if (NS_FAILED(rv))
     207               0 :     return rv;
     208               0 :   if (!domSelection)
     209               0 :     return NS_ERROR_FAILURE;
     210                 : 
     211               0 :   nsCOMPtr<nsISelectionPrivate> privateSelection = do_QueryInterface(domSelection);
     212               0 :   if (privateSelection)
     213               0 :     privateSelection->AddSelectionListener(this);
     214               0 :   mDomSelectionWeak = do_GetWeakReference(domSelection);
     215                 :   
     216                 :   // set up the blink timer
     217               0 :   if (mVisible)
     218                 :   {
     219               0 :     StartBlinking();
     220                 :   }
     221                 : #ifdef IBMBIDI
     222               0 :   mBidiUI = Preferences::GetBool("bidi.browser.ui");
     223                 : #endif
     224                 : 
     225               0 :   return NS_OK;
     226                 : }
     227                 : 
     228                 : static bool
     229               0 : DrawCJKCaret(nsIFrame* aFrame, PRInt32 aOffset)
     230                 : {
     231               0 :   nsIContent* content = aFrame->GetContent();
     232               0 :   const nsTextFragment* frag = content->GetText();
     233               0 :   if (!frag)
     234               0 :     return false;
     235               0 :   if (aOffset < 0 || PRUint32(aOffset) >= frag->GetLength())
     236               0 :     return false;
     237               0 :   PRUnichar ch = frag->CharAt(aOffset);
     238               0 :   return 0x2e80 <= ch && ch <= 0xd7ff;
     239                 : }
     240                 : 
     241               0 : nsCaret::Metrics nsCaret::ComputeMetrics(nsIFrame* aFrame, PRInt32 aOffset, nscoord aCaretHeight)
     242                 : {
     243                 :   // Compute nominal sizes in appunits
     244                 :   nscoord caretWidth = (aCaretHeight * mCaretAspectRatio) +
     245               0 :                        nsPresContext::CSSPixelsToAppUnits(mCaretWidthCSSPx);
     246                 : 
     247               0 :   if (DrawCJKCaret(aFrame, aOffset)) {
     248               0 :     caretWidth += nsPresContext::CSSPixelsToAppUnits(1);
     249                 :   }
     250               0 :   nscoord bidiIndicatorSize = nsPresContext::CSSPixelsToAppUnits(kMinBidiIndicatorPixels);
     251               0 :   bidiIndicatorSize = NS_MAX(caretWidth, bidiIndicatorSize);
     252                 : 
     253                 :   // Round them to device pixels. Always round down, except that anything
     254                 :   // between 0 and 1 goes up to 1 so we don't let the caret disappear.
     255               0 :   PRUint32 tpp = aFrame->PresContext()->AppUnitsPerDevPixel();
     256                 :   Metrics result;
     257               0 :   result.mCaretWidth = NS_ROUND_BORDER_TO_PIXELS(caretWidth, tpp);
     258               0 :   result.mBidiIndicatorSize = NS_ROUND_BORDER_TO_PIXELS(bidiIndicatorSize, tpp);
     259                 :   return result;
     260                 : }
     261                 : 
     262                 : //-----------------------------------------------------------------------------
     263               0 : void nsCaret::Terminate()
     264                 : {
     265                 :   // this doesn't erase the caret if it's drawn. Should it? We might not have
     266                 :   // a good drawing environment during teardown.
     267                 :   
     268               0 :   KillTimer();
     269               0 :   mBlinkTimer = nsnull;
     270                 : 
     271                 :   // unregiser ourselves as a selection listener
     272               0 :   nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
     273               0 :   nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSelection));
     274               0 :   if (privateSelection)
     275               0 :     privateSelection->RemoveSelectionListener(this);
     276               0 :   mDomSelectionWeak = nsnull;
     277               0 :   mPresShell = nsnull;
     278                 : 
     279               0 :   mLastContent = nsnull;
     280               0 : }
     281                 : 
     282                 : //-----------------------------------------------------------------------------
     283               0 : NS_IMPL_ISUPPORTS1(nsCaret, nsISelectionListener)
     284                 : 
     285                 : //-----------------------------------------------------------------------------
     286               0 : nsISelection* nsCaret::GetCaretDOMSelection()
     287                 : {
     288               0 :   nsCOMPtr<nsISelection> sel(do_QueryReferent(mDomSelectionWeak));
     289               0 :   return sel;  
     290                 : }
     291                 : 
     292                 : //-----------------------------------------------------------------------------
     293               0 : nsresult nsCaret::SetCaretDOMSelection(nsISelection *aDOMSel)
     294                 : {
     295               0 :   NS_ENSURE_ARG_POINTER(aDOMSel);
     296               0 :   mDomSelectionWeak = do_GetWeakReference(aDOMSel);   // weak reference to pres shell
     297               0 :   if (mVisible)
     298                 :   {
     299                 :     // Stop the caret from blinking in its previous location.
     300               0 :     StopBlinking();
     301                 :     // Start the caret blinking in the new location.
     302               0 :     StartBlinking();
     303                 :   }
     304               0 :   return NS_OK;
     305                 : }
     306                 : 
     307                 : 
     308                 : //-----------------------------------------------------------------------------
     309               0 : void nsCaret::SetCaretVisible(bool inMakeVisible)
     310                 : {
     311               0 :   mVisible = inMakeVisible;
     312               0 :   if (mVisible) {
     313               0 :     SetIgnoreUserModify(true);
     314               0 :     StartBlinking();
     315                 :   } else {
     316               0 :     StopBlinking();
     317               0 :     SetIgnoreUserModify(false);
     318                 :   }
     319               0 : }
     320                 : 
     321                 : 
     322                 : //-----------------------------------------------------------------------------
     323               0 : nsresult nsCaret::GetCaretVisible(bool *outMakeVisible)
     324                 : {
     325               0 :   NS_ENSURE_ARG_POINTER(outMakeVisible);
     326               0 :   *outMakeVisible = (mVisible && MustDrawCaret(true));
     327               0 :   return NS_OK;
     328                 : }
     329                 : 
     330                 : 
     331                 : //-----------------------------------------------------------------------------
     332               0 : void nsCaret::SetCaretReadOnly(bool inMakeReadonly)
     333                 : {
     334               0 :   mReadOnly = inMakeReadonly;
     335               0 : }
     336                 : 
     337                 : nsresult
     338               0 : nsCaret::GetGeometryForFrame(nsIFrame* aFrame,
     339                 :                              PRInt32   aFrameOffset,
     340                 :                              nsRect*   aRect,
     341                 :                              nscoord*  aBidiIndicatorSize)
     342                 : {
     343               0 :   nsPoint framePos(0, 0);
     344               0 :   nsresult rv = aFrame->GetPointFromOffset(aFrameOffset, &framePos);
     345               0 :   if (NS_FAILED(rv))
     346               0 :     return rv;
     347                 : 
     348               0 :   nsIFrame *frame = aFrame->GetContentInsertionFrame();
     349               0 :   NS_ASSERTION(frame, "We should not be in the middle of reflow");
     350               0 :   nscoord baseline = frame->GetCaretBaseline();
     351               0 :   nscoord ascent = 0, descent = 0;
     352               0 :   nsRefPtr<nsFontMetrics> fm;
     353                 :   nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm),
     354               0 :     nsLayoutUtils::FontSizeInflationFor(aFrame, nsLayoutUtils::eNotInReflow));
     355               0 :   NS_ASSERTION(fm, "We should be able to get the font metrics");
     356               0 :   if (fm) {
     357               0 :     ascent = fm->MaxAscent();
     358               0 :     descent = fm->MaxDescent();
     359                 :   }
     360               0 :   nscoord height = ascent + descent;
     361               0 :   framePos.y = baseline - ascent;
     362               0 :   Metrics caretMetrics = ComputeMetrics(aFrame, aFrameOffset, height);
     363               0 :   *aRect = nsRect(framePos, nsSize(caretMetrics.mCaretWidth, height));
     364                 : 
     365                 :   // Clamp the x-position to be within our scroll frame. If we don't, then it
     366                 :   // clips us, and we don't appear at all. See bug 335560.
     367                 :   nsIFrame *scrollFrame =
     368               0 :     nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::scrollFrame);
     369               0 :   if (scrollFrame) {
     370                 :     // First, use the scrollFrame to get at the scrollable view that we're in.
     371               0 :     nsIScrollableFrame *sf = do_QueryFrame(scrollFrame);
     372               0 :     nsIFrame *scrolled = sf->GetScrolledFrame();
     373               0 :     nsRect caretInScroll = *aRect + aFrame->GetOffsetTo(scrolled);
     374                 : 
     375                 :     // Now see if thet caret extends beyond the view's bounds. If it does,
     376                 :     // then snap it back, put it as close to the edge as it can.
     377               0 :     nscoord overflow = caretInScroll.XMost() -
     378               0 :       scrolled->GetVisualOverflowRectRelativeToSelf().width;
     379               0 :     if (overflow > 0)
     380               0 :       aRect->x -= overflow;
     381                 :   }
     382                 : 
     383               0 :   if (aBidiIndicatorSize)
     384               0 :     *aBidiIndicatorSize = caretMetrics.mBidiIndicatorSize;
     385                 : 
     386               0 :   return NS_OK;
     387                 : }
     388                 : 
     389               0 : nsIFrame* nsCaret::GetGeometry(nsISelection* aSelection, nsRect* aRect,
     390                 :                                nscoord* aBidiIndicatorSize)
     391                 : {
     392               0 :   nsCOMPtr<nsIDOMNode> focusNode;
     393               0 :   nsresult rv = aSelection->GetFocusNode(getter_AddRefs(focusNode));
     394               0 :   if (NS_FAILED(rv) || !focusNode)
     395               0 :     return nsnull;
     396                 : 
     397                 :   PRInt32 focusOffset;
     398               0 :   rv = aSelection->GetFocusOffset(&focusOffset);
     399               0 :   if (NS_FAILED(rv))
     400               0 :     return nsnull;
     401                 :     
     402               0 :   nsCOMPtr<nsIContent> contentNode = do_QueryInterface(focusNode);
     403               0 :   if (!contentNode)
     404               0 :     return nsnull;
     405                 : 
     406               0 :   nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
     407               0 :   if (!frameSelection)
     408               0 :     return nsnull;
     409               0 :   PRUint8 bidiLevel = frameSelection->GetCaretBidiLevel();
     410                 :   nsIFrame* frame;
     411                 :   PRInt32 frameOffset;
     412                 :   rv = GetCaretFrameForNodeOffset(contentNode, focusOffset,
     413                 :                                   frameSelection->GetHint(), bidiLevel,
     414               0 :                                   &frame, &frameOffset);
     415               0 :   if (NS_FAILED(rv) || !frame)
     416               0 :     return nsnull;
     417                 : 
     418               0 :   GetGeometryForFrame(frame, frameOffset, aRect, aBidiIndicatorSize);
     419               0 :   return frame;
     420                 : }
     421                 : 
     422               0 : void nsCaret::DrawCaretAfterBriefDelay()
     423                 : {
     424                 :   // Make sure readonly caret gets drawn again if it needs to be
     425               0 :   if (!mBlinkTimer) {
     426                 :     nsresult  err;
     427               0 :     mBlinkTimer = do_CreateInstance("@mozilla.org/timer;1", &err);    
     428               0 :     if (NS_FAILED(err))
     429               0 :       return;
     430                 :   }    
     431                 : 
     432               0 :   mBlinkTimer->InitWithFuncCallback(CaretBlinkCallback, this, 0,
     433               0 :                                     nsITimer::TYPE_ONE_SHOT);
     434                 : }
     435                 : 
     436               0 : void nsCaret::EraseCaret()
     437                 : {
     438               0 :   if (mDrawn) {
     439               0 :     DrawCaret(true);
     440               0 :     if (mReadOnly && mBlinkRate) {
     441                 :       // If readonly we don't have a blink timer set, so caret won't
     442                 :       // be redrawn automatically. We need to force the caret to get
     443                 :       // redrawn right after the paint
     444               0 :       DrawCaretAfterBriefDelay();
     445                 :     }
     446                 :   }
     447               0 : }
     448                 : 
     449               0 : void nsCaret::SetVisibilityDuringSelection(bool aVisibility) 
     450                 : {
     451               0 :   mShowDuringSelection = aVisibility;
     452               0 : }
     453                 : 
     454               0 : nsresult nsCaret::DrawAtPosition(nsIDOMNode* aNode, PRInt32 aOffset)
     455                 : {
     456               0 :   NS_ENSURE_ARG(aNode);
     457                 : 
     458                 :   PRUint8 bidiLevel;
     459               0 :   nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
     460               0 :   if (!frameSelection)
     461               0 :     return NS_ERROR_FAILURE;
     462               0 :   bidiLevel = frameSelection->GetCaretBidiLevel();
     463                 : 
     464                 :   // DrawAtPosition is used by consumers who want us to stay drawn where they
     465                 :   // tell us. Setting mBlinkRate to 0 tells us to not set a timer to erase
     466                 :   // ourselves, our consumer will take care of that.
     467               0 :   mBlinkRate = 0;
     468                 : 
     469                 :   // XXX we need to do more work here to get the correct hint.
     470                 :   nsresult rv = DrawAtPositionWithHint(aNode, aOffset,
     471                 :                                        nsFrameSelection::HINTLEFT,
     472               0 :                                        bidiLevel, true)
     473               0 :     ?  NS_OK : NS_ERROR_FAILURE;
     474               0 :   ToggleDrawnStatus();
     475               0 :   return rv;
     476                 : }
     477                 : 
     478               0 : nsIFrame * nsCaret::GetCaretFrame(PRInt32 *aOffset)
     479                 : {
     480                 :   // Return null if we're not drawn to prevent anybody from trying to draw us.
     481               0 :   if (!mDrawn)
     482               0 :     return nsnull;
     483                 : 
     484                 :   // Recompute the frame that we're supposed to draw in to guarantee that
     485                 :   // we're not going to try to draw into a stale (dead) frame.
     486                 :   PRInt32 offset;
     487               0 :   nsIFrame *frame = nsnull;
     488                 :   nsresult rv = GetCaretFrameForNodeOffset(mLastContent, mLastContentOffset,
     489                 :                                            mLastHint, mLastBidiLevel, &frame,
     490               0 :                                            &offset);
     491               0 :   if (NS_FAILED(rv))
     492               0 :     return nsnull;
     493                 : 
     494               0 :   if (aOffset) {
     495               0 :     *aOffset = offset;
     496                 :   }
     497               0 :   return frame;
     498                 : }
     499                 : 
     500               0 : void nsCaret::InvalidateOutsideCaret()
     501                 : {
     502               0 :   nsIFrame *frame = GetCaretFrame();
     503                 : 
     504                 :   // Only invalidate if we are not fully contained by our frame's rect.
     505               0 :   if (frame && !frame->GetVisualOverflowRect().Contains(GetCaretRect()))
     506               0 :     InvalidateRects(mCaretRect, GetHookRect(), frame);
     507               0 : }
     508                 : 
     509               0 : void nsCaret::UpdateCaretPosition()
     510                 : {
     511                 :   // We'll recalculate anyway if we're not drawn right now.
     512               0 :   if (!mDrawn)
     513               0 :     return;
     514                 : 
     515                 :   // A trick! Make the DrawCaret code recalculate the caret's current
     516                 :   // position.
     517               0 :   mDrawn = false;
     518               0 :   DrawCaret(false);
     519                 : }
     520                 : 
     521               0 : void nsCaret::PaintCaret(nsDisplayListBuilder *aBuilder,
     522                 :                          nsRenderingContext *aCtx,
     523                 :                          nsIFrame* aForFrame,
     524                 :                          const nsPoint &aOffset)
     525                 : {
     526               0 :   NS_ASSERTION(mDrawn, "The caret shouldn't be drawing");
     527                 : 
     528               0 :   const nsRect drawCaretRect = mCaretRect + aOffset;
     529                 :   PRInt32 contentOffset;
     530                 : 
     531                 : #ifdef DEBUG
     532                 :   nsIFrame* frame =
     533                 : #endif
     534               0 :     GetCaretFrame(&contentOffset);
     535               0 :   NS_ASSERTION(frame == aForFrame, "We're referring different frame");
     536                 :   // If the offset falls outside of the frame, then don't paint the caret.
     537                 :   PRInt32 startOffset, endOffset;
     538               0 :   if (aForFrame->GetType() == nsGkAtoms::textFrame &&
     539               0 :       (NS_FAILED(aForFrame->GetOffsets(startOffset, endOffset)) ||
     540                 :       startOffset > contentOffset ||
     541                 :       endOffset < contentOffset)) {
     542                 :     return;
     543                 :   }
     544               0 :   nscolor foregroundColor = aForFrame->GetCaretColorAt(contentOffset);
     545                 : 
     546                 :   // Only draw the native caret if the foreground color matches that of
     547                 :   // -moz-fieldtext (the color of the text in a textbox). If it doesn't match
     548                 :   // we are likely in contenteditable or a custom widget and we risk being hard to see
     549                 :   // against the background. In that case, fall back to the CSS color.
     550               0 :   nsPresContext* presContext = aForFrame->PresContext();
     551                 : 
     552               0 :   if (GetHookRect().IsEmpty() && presContext) {
     553               0 :     nsITheme *theme = presContext->GetTheme();
     554               0 :     if (theme && theme->ThemeSupportsWidget(presContext, aForFrame, NS_THEME_TEXTFIELD_CARET)) {
     555                 :       nscolor fieldText;
     556                 :       nsresult rv = LookAndFeel::GetColor(LookAndFeel::eColorID__moz_fieldtext,
     557               0 :                                           &fieldText);
     558               0 :       if (NS_SUCCEEDED(rv) && fieldText == foregroundColor) {
     559                 :         theme->DrawWidgetBackground(aCtx, aForFrame, NS_THEME_TEXTFIELD_CARET,
     560               0 :                                     drawCaretRect, drawCaretRect);
     561                 :         return;
     562                 :       }
     563                 :     }
     564                 :   }
     565                 : 
     566               0 :   aCtx->SetColor(foregroundColor);
     567               0 :   aCtx->FillRect(drawCaretRect);
     568               0 :   if (!GetHookRect().IsEmpty())
     569               0 :     aCtx->FillRect(GetHookRect() + aOffset);
     570                 : }
     571                 : 
     572                 : 
     573                 : //-----------------------------------------------------------------------------
     574               0 : NS_IMETHODIMP nsCaret::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aDomSel, PRInt16 aReason)
     575                 : {
     576               0 :   if (aReason & nsISelectionListener::MOUSEUP_REASON)//this wont do
     577               0 :     return NS_OK;
     578                 : 
     579               0 :   nsCOMPtr<nsISelection> domSel(do_QueryReferent(mDomSelectionWeak));
     580                 : 
     581                 :   // The same caret is shared amongst the document and any text widgets it
     582                 :   // may contain. This means that the caret could get notifications from
     583                 :   // multiple selections.
     584                 :   //
     585                 :   // If this notification is for a selection that is not the one the
     586                 :   // the caret is currently interested in (mDomSelectionWeak), then there
     587                 :   // is nothing to do!
     588                 : 
     589               0 :   if (domSel != aDomSel)
     590               0 :     return NS_OK;
     591                 : 
     592               0 :   if (mVisible)
     593                 :   {
     594                 :     // Stop the caret from blinking in its previous location.
     595               0 :     StopBlinking();
     596                 : 
     597                 :     // Start the caret blinking in the new location.
     598               0 :     StartBlinking();
     599                 :   }
     600                 : 
     601               0 :   return NS_OK;
     602                 : }
     603                 : 
     604                 : 
     605                 : //-----------------------------------------------------------------------------
     606               0 : void nsCaret::KillTimer()
     607                 : {
     608               0 :   if (mBlinkTimer)
     609                 :   {
     610               0 :     mBlinkTimer->Cancel();
     611                 :   }
     612               0 : }
     613                 : 
     614                 : 
     615                 : //-----------------------------------------------------------------------------
     616               0 : nsresult nsCaret::PrimeTimer()
     617                 : {
     618                 :   // set up the blink timer
     619               0 :   if (!mReadOnly && mBlinkRate > 0)
     620                 :   {
     621               0 :     if (!mBlinkTimer) {
     622                 :       nsresult  err;
     623               0 :       mBlinkTimer = do_CreateInstance("@mozilla.org/timer;1", &err);    
     624               0 :       if (NS_FAILED(err))
     625               0 :         return err;
     626                 :     }    
     627                 : 
     628               0 :     mBlinkTimer->InitWithFuncCallback(CaretBlinkCallback, this, mBlinkRate,
     629               0 :                                       nsITimer::TYPE_REPEATING_SLACK);
     630                 :   }
     631                 : 
     632               0 :   return NS_OK;
     633                 : }
     634                 : 
     635               0 : void nsCaret::InvalidateTextOverflowBlock()
     636                 : {
     637                 :   // If the nearest block has a potential 'text-overflow' marker then
     638                 :   // invalidate it.
     639               0 :   if (mLastContent) {
     640               0 :     nsIFrame* caretFrame = mLastContent->GetPrimaryFrame();
     641               0 :     if (caretFrame) {
     642               0 :       nsIFrame* block = nsLayoutUtils::GetAsBlock(caretFrame) ? caretFrame :
     643               0 :         nsLayoutUtils::FindNearestBlockAncestor(caretFrame);
     644               0 :       if (block) {
     645               0 :         const nsStyleTextReset* style = block->GetStyleTextReset();
     646               0 :         if (style->mTextOverflow.mLeft.mType != NS_STYLE_TEXT_OVERFLOW_CLIP ||
     647                 :             style->mTextOverflow.mRight.mType != NS_STYLE_TEXT_OVERFLOW_CLIP) {
     648               0 :           block->InvalidateOverflowRect();
     649                 :         }
     650                 :       }
     651                 :     }
     652                 :   }
     653               0 : }
     654                 : 
     655                 : //-----------------------------------------------------------------------------
     656               0 : void nsCaret::StartBlinking()
     657                 : {
     658               0 :   InvalidateTextOverflowBlock();
     659                 : 
     660               0 :   if (mReadOnly) {
     661                 :     // Make sure the one draw command we use for a readonly caret isn't
     662                 :     // done until the selection is set
     663               0 :     DrawCaretAfterBriefDelay();
     664               0 :     return;
     665                 :   }
     666               0 :   PrimeTimer();
     667                 : 
     668                 :   // If we are currently drawn, then the second call to DrawCaret below will
     669                 :   // actually erase the caret. That would cause the caret to spend an "off"
     670                 :   // cycle before it appears, which is not really what we want. This first
     671                 :   // call to DrawCaret makes sure that the first cycle after a call to
     672                 :   // StartBlinking is an "on" cycle.
     673               0 :   if (mDrawn)
     674               0 :     DrawCaret(true);
     675                 : 
     676               0 :   DrawCaret(true);    // draw it right away
     677                 : }
     678                 : 
     679                 : 
     680                 : //-----------------------------------------------------------------------------
     681               0 : void nsCaret::StopBlinking()
     682                 : {
     683               0 :   InvalidateTextOverflowBlock();
     684                 : 
     685               0 :   if (mDrawn)     // erase the caret if necessary
     686               0 :     DrawCaret(true);
     687                 : 
     688               0 :   NS_ASSERTION(!mDrawn, "Caret still drawn after StopBlinking().");
     689               0 :   KillTimer();
     690               0 : }
     691                 : 
     692                 : bool
     693               0 : nsCaret::DrawAtPositionWithHint(nsIDOMNode*             aNode,
     694                 :                                 PRInt32                 aOffset,
     695                 :                                 nsFrameSelection::HINT  aFrameHint,
     696                 :                                 PRUint8                 aBidiLevel,
     697                 :                                 bool                    aInvalidate)
     698                 : {
     699               0 :   nsCOMPtr<nsIContent> contentNode = do_QueryInterface(aNode);
     700               0 :   if (!contentNode)
     701               0 :     return false;
     702                 : 
     703               0 :   nsIFrame* theFrame = nsnull;
     704               0 :   PRInt32   theFrameOffset = 0;
     705                 : 
     706                 :   nsresult rv = GetCaretFrameForNodeOffset(contentNode, aOffset, aFrameHint, aBidiLevel,
     707               0 :                                            &theFrame, &theFrameOffset);
     708               0 :   if (NS_FAILED(rv) || !theFrame)
     709               0 :     return false;
     710                 : 
     711                 :   // now we have a frame, check whether it's appropriate to show the caret here
     712               0 :   const nsStyleUserInterface* userinterface = theFrame->GetStyleUserInterface();
     713               0 :   if ((!mIgnoreUserModify &&
     714                 :        userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) ||
     715                 :       (userinterface->mUserInput == NS_STYLE_USER_INPUT_NONE) ||
     716                 :       (userinterface->mUserInput == NS_STYLE_USER_INPUT_DISABLED))
     717                 :   {
     718               0 :     return false;
     719                 :   }  
     720                 : 
     721               0 :   if (!mDrawn)
     722                 :   {
     723                 :     // save stuff so we can figure out what frame we're in later.
     724               0 :     mLastContent = contentNode;
     725               0 :     mLastContentOffset = aOffset;
     726               0 :     mLastHint = aFrameHint;
     727               0 :     mLastBidiLevel = aBidiLevel;
     728                 : 
     729                 :     // If there has been a reflow, set the caret Bidi level to the level of the current frame
     730               0 :     if (aBidiLevel & BIDI_LEVEL_UNDEFINED) {
     731               0 :       nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
     732               0 :       if (!frameSelection)
     733               0 :         return false;
     734               0 :       frameSelection->SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(theFrame));
     735                 :     }
     736                 : 
     737                 :     // Only update the caret's rect when we're not currently drawn.
     738               0 :     if (!UpdateCaretRects(theFrame, theFrameOffset))
     739               0 :       return false;
     740                 :   }
     741                 : 
     742               0 :   if (aInvalidate)
     743               0 :     InvalidateRects(mCaretRect, mHookRect, theFrame);
     744                 : 
     745               0 :   return true;
     746                 : }
     747                 : 
     748                 : nsresult 
     749               0 : nsCaret::GetCaretFrameForNodeOffset(nsIContent*             aContentNode,
     750                 :                                     PRInt32                 aOffset,
     751                 :                                     nsFrameSelection::HINT aFrameHint,
     752                 :                                     PRUint8                 aBidiLevel,
     753                 :                                     nsIFrame**              aReturnFrame,
     754                 :                                     PRInt32*                aReturnOffset)
     755                 : {
     756                 : 
     757                 :   //get frame selection and find out what frame to use...
     758               0 :   nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
     759               0 :   if (!presShell)
     760               0 :     return NS_ERROR_FAILURE;
     761                 : 
     762               0 :   if (!aContentNode || !aContentNode->IsInDoc() ||
     763               0 :       presShell->GetDocument() != aContentNode->GetCurrentDoc())
     764               0 :     return NS_ERROR_FAILURE;
     765                 : 
     766               0 :   nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
     767               0 :   if (!frameSelection)
     768               0 :     return NS_ERROR_FAILURE;
     769                 : 
     770               0 :   nsIFrame* theFrame = nsnull;
     771               0 :   PRInt32   theFrameOffset = 0;
     772                 : 
     773               0 :   theFrame = frameSelection->GetFrameForNodeOffset(aContentNode, aOffset,
     774               0 :                                                    aFrameHint, &theFrameOffset);
     775               0 :   if (!theFrame)
     776               0 :     return NS_ERROR_FAILURE;
     777                 : 
     778                 :   // if theFrame is after a text frame that's logically at the end of the line
     779                 :   // (e.g. if theFrame is a <br> frame), then put the caret at the end of
     780                 :   // that text frame instead. This way, the caret will be positioned as if
     781                 :   // trailing whitespace was not trimmed.
     782               0 :   AdjustCaretFrameForLineEnd(&theFrame, &theFrameOffset);
     783                 :   
     784                 :   // Mamdouh : modification of the caret to work at rtl and ltr with Bidi
     785                 :   //
     786                 :   // Direction Style from this->GetStyleData()
     787                 :   // now in (visibility->mDirection)
     788                 :   // ------------------
     789                 :   // NS_STYLE_DIRECTION_LTR : LTR or Default
     790                 :   // NS_STYLE_DIRECTION_RTL
     791                 :   // NS_STYLE_DIRECTION_INHERIT
     792               0 :   if (mBidiUI)
     793                 :   {
     794                 :     // If there has been a reflow, take the caret Bidi level to be the level of the current frame
     795               0 :     if (aBidiLevel & BIDI_LEVEL_UNDEFINED)
     796               0 :       aBidiLevel = NS_GET_EMBEDDING_LEVEL(theFrame);
     797                 : 
     798                 :     PRInt32 start;
     799                 :     PRInt32 end;
     800                 :     nsIFrame* frameBefore;
     801                 :     nsIFrame* frameAfter;
     802                 :     PRUint8 levelBefore;     // Bidi level of the character before the caret
     803                 :     PRUint8 levelAfter;      // Bidi level of the character after the caret
     804                 : 
     805               0 :     theFrame->GetOffsets(start, end);
     806               0 :     if (start == 0 || end == 0 || start == theFrameOffset || end == theFrameOffset)
     807                 :     {
     808               0 :       nsPrevNextBidiLevels levels = frameSelection->
     809               0 :         GetPrevNextBidiLevels(aContentNode, aOffset, false);
     810                 :     
     811                 :       /* Boundary condition, we need to know the Bidi levels of the characters before and after the caret */
     812               0 :       if (levels.mFrameBefore || levels.mFrameAfter)
     813                 :       {
     814               0 :         frameBefore = levels.mFrameBefore;
     815               0 :         frameAfter = levels.mFrameAfter;
     816               0 :         levelBefore = levels.mLevelBefore;
     817               0 :         levelAfter = levels.mLevelAfter;
     818                 : 
     819               0 :         if ((levelBefore != levelAfter) || (aBidiLevel != levelBefore))
     820                 :         {
     821               0 :           aBidiLevel = NS_MAX(aBidiLevel, NS_MIN(levelBefore, levelAfter));                                  // rule c3
     822               0 :           aBidiLevel = NS_MIN(aBidiLevel, NS_MAX(levelBefore, levelAfter));                                  // rule c4
     823               0 :           if (aBidiLevel == levelBefore                                                                      // rule c1
     824               0 :               || (aBidiLevel > levelBefore && aBidiLevel < levelAfter && !((aBidiLevel ^ levelBefore) & 1))    // rule c5
     825               0 :               || (aBidiLevel < levelBefore && aBidiLevel > levelAfter && !((aBidiLevel ^ levelBefore) & 1)))  // rule c9
     826                 :           {
     827               0 :             if (theFrame != frameBefore)
     828                 :             {
     829               0 :               if (frameBefore) // if there is a frameBefore, move into it
     830                 :               {
     831               0 :                 theFrame = frameBefore;
     832               0 :                 theFrame->GetOffsets(start, end);
     833               0 :                 theFrameOffset = end;
     834                 :               }
     835                 :               else 
     836                 :               {
     837                 :                 // if there is no frameBefore, we must be at the beginning of the line
     838                 :                 // so we stay with the current frame.
     839                 :                 // Exception: when the first frame on the line has a different Bidi level from the paragraph level, there is no
     840                 :                 // real frame for the caret to be in. We have to find the visually first frame on the line.
     841               0 :                 PRUint8 baseLevel = NS_GET_BASE_LEVEL(frameAfter);
     842               0 :                 if (baseLevel != levelAfter)
     843                 :                 {
     844               0 :                   nsPeekOffsetStruct pos;
     845               0 :                   pos.SetData(eSelectBeginLine, eDirPrevious, 0, 0, false, true, false, true);
     846               0 :                   if (NS_SUCCEEDED(frameAfter->PeekOffset(&pos))) {
     847               0 :                     theFrame = pos.mResultFrame;
     848               0 :                     theFrameOffset = pos.mContentOffset;
     849                 :                   }
     850                 :                 }
     851                 :               }
     852               0 :             }
     853                 :           }
     854               0 :           else if (aBidiLevel == levelAfter                                                                     // rule c2
     855               0 :                    || (aBidiLevel > levelBefore && aBidiLevel < levelAfter && !((aBidiLevel ^ levelAfter) & 1))   // rule c6
     856               0 :                    || (aBidiLevel < levelBefore && aBidiLevel > levelAfter && !((aBidiLevel ^ levelAfter) & 1)))  // rule c10
     857                 :           {
     858               0 :             if (theFrame != frameAfter)
     859                 :             {
     860               0 :               if (frameAfter)
     861                 :               {
     862                 :                 // if there is a frameAfter, move into it
     863               0 :                 theFrame = frameAfter;
     864               0 :                 theFrame->GetOffsets(start, end);
     865               0 :                 theFrameOffset = start;
     866                 :               }
     867                 :               else 
     868                 :               {
     869                 :                 // if there is no frameAfter, we must be at the end of the line
     870                 :                 // so we stay with the current frame.
     871                 :                 // Exception: when the last frame on the line has a different Bidi level from the paragraph level, there is no
     872                 :                 // real frame for the caret to be in. We have to find the visually last frame on the line.
     873               0 :                 PRUint8 baseLevel = NS_GET_BASE_LEVEL(frameBefore);
     874               0 :                 if (baseLevel != levelBefore)
     875                 :                 {
     876               0 :                   nsPeekOffsetStruct pos;
     877               0 :                   pos.SetData(eSelectEndLine, eDirNext, 0, 0, false, true, false, true);
     878               0 :                   if (NS_SUCCEEDED(frameBefore->PeekOffset(&pos))) {
     879               0 :                     theFrame = pos.mResultFrame;
     880               0 :                     theFrameOffset = pos.mContentOffset;
     881                 :                   }
     882                 :                 }
     883                 :               }
     884               0 :             }
     885                 :           }
     886               0 :           else if (aBidiLevel > levelBefore && aBidiLevel < levelAfter  // rule c7/8
     887               0 :                    && !((levelBefore ^ levelAfter) & 1)                 // before and after have the same parity
     888                 :                    && ((aBidiLevel ^ levelAfter) & 1))                  // caret has different parity
     889                 :           {
     890               0 :             if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(frameAfter, eDirNext, aBidiLevel, &theFrame)))
     891                 :             {
     892               0 :               theFrame->GetOffsets(start, end);
     893               0 :               levelAfter = NS_GET_EMBEDDING_LEVEL(theFrame);
     894               0 :               if (aBidiLevel & 1) // c8: caret to the right of the rightmost character
     895               0 :                 theFrameOffset = (levelAfter & 1) ? start : end;
     896                 :               else               // c7: caret to the left of the leftmost character
     897               0 :                 theFrameOffset = (levelAfter & 1) ? end : start;
     898                 :             }
     899                 :           }
     900               0 :           else if (aBidiLevel < levelBefore && aBidiLevel > levelAfter  // rule c11/12
     901               0 :                    && !((levelBefore ^ levelAfter) & 1)                 // before and after have the same parity
     902                 :                    && ((aBidiLevel ^ levelAfter) & 1))                  // caret has different parity
     903                 :           {
     904               0 :             if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(frameBefore, eDirPrevious, aBidiLevel, &theFrame)))
     905                 :             {
     906               0 :               theFrame->GetOffsets(start, end);
     907               0 :               levelBefore = NS_GET_EMBEDDING_LEVEL(theFrame);
     908               0 :               if (aBidiLevel & 1) // c12: caret to the left of the leftmost character
     909               0 :                 theFrameOffset = (levelBefore & 1) ? end : start;
     910                 :               else               // c11: caret to the right of the rightmost character
     911               0 :                 theFrameOffset = (levelBefore & 1) ? start : end;
     912                 :             }
     913                 :           }   
     914                 :         }
     915                 :       }
     916                 :     }
     917                 :   }
     918                 : 
     919               0 :   NS_ASSERTION(!theFrame || theFrame->PresContext()->PresShell() == presShell,
     920                 :                "caret frame is in wrong document");
     921               0 :   *aReturnFrame = theFrame;
     922               0 :   *aReturnOffset = theFrameOffset;
     923               0 :   return NS_OK;
     924                 : }
     925                 : 
     926               0 : nsresult nsCaret::CheckCaretDrawingState()
     927                 : {
     928               0 :   if (mDrawn) {
     929                 :     // The caret is drawn; if it shouldn't be, erase it.
     930               0 :     if (!mVisible || !MustDrawCaret(true))
     931               0 :       EraseCaret();
     932                 :   }
     933                 :   else
     934                 :   {
     935                 :     // The caret is not drawn; if it should be, draw it.
     936               0 :     if (mPendingDraw && (mVisible && MustDrawCaret(true)))
     937               0 :       DrawCaret(true);
     938                 :   }
     939               0 :   return NS_OK;
     940                 : }
     941                 : 
     942                 : /*-----------------------------------------------------------------------------
     943                 : 
     944                 :   MustDrawCaret
     945                 :   
     946                 :   Find out if we need to do any caret drawing. This returns true if
     947                 :   either:
     948                 :   a) The caret has been drawn, and we need to erase it.
     949                 :   b) The caret is not drawn, and the selection is collapsed.
     950                 :   c) The caret is not hidden due to open XUL popups
     951                 :      (see IsMenuPopupHidingCaret()).
     952                 :   
     953                 : ----------------------------------------------------------------------------- */
     954               0 : bool nsCaret::MustDrawCaret(bool aIgnoreDrawnState)
     955                 : {
     956               0 :   if (!aIgnoreDrawnState && mDrawn)
     957               0 :     return true;
     958                 : 
     959               0 :   nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
     960               0 :   if (!domSelection)
     961               0 :     return false;
     962                 : 
     963                 :   bool isCollapsed;
     964               0 :   if (NS_FAILED(domSelection->GetIsCollapsed(&isCollapsed)))
     965               0 :     return false;
     966                 : 
     967               0 :   if (mShowDuringSelection)
     968               0 :     return true;      // show the caret even in selections
     969                 : 
     970               0 :   if (IsMenuPopupHidingCaret())
     971               0 :     return false;
     972                 : 
     973               0 :   return isCollapsed;
     974                 : }
     975                 : 
     976               0 : bool nsCaret::IsMenuPopupHidingCaret()
     977                 : {
     978                 : #ifdef MOZ_XUL
     979                 :   // Check if there are open popups.
     980               0 :   nsXULPopupManager *popMgr = nsXULPopupManager::GetInstance();
     981               0 :   nsTArray<nsIFrame*> popups = popMgr->GetVisiblePopups();
     982                 : 
     983               0 :   if (popups.Length() == 0)
     984               0 :     return false; // No popups, so caret can't be hidden by them.
     985                 : 
     986                 :   // Get the selection focus content, that's where the caret would 
     987                 :   // go if it was drawn.
     988               0 :   nsCOMPtr<nsIDOMNode> node;
     989               0 :   nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
     990               0 :   if (!domSelection)
     991               0 :     return true; // No selection/caret to draw.
     992               0 :   domSelection->GetFocusNode(getter_AddRefs(node));
     993               0 :   if (!node)
     994               0 :     return true; // No selection/caret to draw.
     995               0 :   nsCOMPtr<nsIContent> caretContent = do_QueryInterface(node);
     996               0 :   if (!caretContent)
     997               0 :     return true; // No selection/caret to draw.
     998                 : 
     999                 :   // If there's a menu popup open before the popup with
    1000                 :   // the caret, don't show the caret.
    1001               0 :   for (PRUint32 i=0; i<popups.Length(); i++) {
    1002               0 :     nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame*>(popups[i]);
    1003               0 :     nsIContent* popupContent = popupFrame->GetContent();
    1004                 : 
    1005               0 :     if (nsContentUtils::ContentIsDescendantOf(caretContent, popupContent)) {
    1006                 :       // The caret is in this popup. There were no menu popups before this
    1007                 :       // popup, so don't hide the caret.
    1008               0 :       return false;
    1009                 :     }
    1010                 : 
    1011               0 :     if (popupFrame->PopupType() == ePopupTypeMenu && !popupFrame->IsContextMenu()) {
    1012                 :       // This is an open menu popup. It does not contain the caret (else we'd
    1013                 :       // have returned above). Even if the caret is in a subsequent popup,
    1014                 :       // or another document/frame, it should be hidden.
    1015               0 :       return true;
    1016                 :     }
    1017                 :   }
    1018                 : #endif
    1019                 : 
    1020                 :   // There are no open menu popups, no need to hide the caret.
    1021               0 :   return false;
    1022                 : }
    1023                 : 
    1024               0 : void nsCaret::DrawCaret(bool aInvalidate)
    1025                 : {
    1026                 :   // Do we need to draw the caret at all?
    1027               0 :   if (!MustDrawCaret(false))
    1028               0 :     return;
    1029                 :   
    1030                 :   // Can we draw the caret now?
    1031               0 :   nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
    1032               0 :   NS_ENSURE_TRUE(presShell, /**/);
    1033                 :   {
    1034               0 :     if (presShell->IsPaintingSuppressed())
    1035                 :     {
    1036               0 :       if (!mDrawn)
    1037               0 :         mPendingDraw = true;
    1038                 : 
    1039                 :       // PresShell::UnsuppressAndInvalidate() will call CheckCaretDrawingState()
    1040                 :       // to get us drawn.
    1041                 :       return;
    1042                 :     }
    1043                 :   }
    1044                 : 
    1045               0 :   nsCOMPtr<nsIDOMNode> node;
    1046                 :   PRInt32 offset;
    1047                 :   nsFrameSelection::HINT hint;
    1048                 :   PRUint8 bidiLevel;
    1049                 : 
    1050               0 :   if (!mDrawn)
    1051                 :   {
    1052               0 :     nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
    1053               0 :     nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSelection));
    1054               0 :     if (!privateSelection) return;
    1055                 :     
    1056               0 :     bool isCollapsed = false;
    1057               0 :     domSelection->GetIsCollapsed(&isCollapsed);
    1058               0 :     if (!mShowDuringSelection && !isCollapsed)
    1059                 :       return;
    1060                 : 
    1061                 :     bool hintRight;
    1062               0 :     privateSelection->GetInterlinePosition(&hintRight);//translate hint.
    1063               0 :     hint = hintRight ? nsFrameSelection::HINTRIGHT : nsFrameSelection::HINTLEFT;
    1064                 : 
    1065                 :     // get the node and offset, which is where we want the caret to draw
    1066               0 :     domSelection->GetFocusNode(getter_AddRefs(node));
    1067               0 :     if (!node)
    1068                 :       return;
    1069                 :     
    1070               0 :     if (NS_FAILED(domSelection->GetFocusOffset(&offset)))
    1071                 :       return;
    1072                 : 
    1073               0 :     nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
    1074               0 :     if (!frameSelection)
    1075                 :       return;
    1076                 : 
    1077               0 :     bidiLevel = frameSelection->GetCaretBidiLevel();
    1078               0 :     mPendingDraw = false;
    1079                 :   }
    1080                 :   else
    1081                 :   {
    1082               0 :     if (!mLastContent)
    1083                 :     {
    1084               0 :       mDrawn = false;
    1085                 :       return;
    1086                 :     }
    1087               0 :     if (!mLastContent->IsInDoc() ||
    1088               0 :         presShell->GetDocument() != mLastContent->GetCurrentDoc())
    1089                 :     {
    1090               0 :       mLastContent = nsnull;
    1091               0 :       mDrawn = false;
    1092                 :       return;
    1093                 :     }
    1094               0 :     node = do_QueryInterface(mLastContent);
    1095               0 :     offset = mLastContentOffset;
    1096               0 :     hint = mLastHint;
    1097               0 :     bidiLevel = mLastBidiLevel;
    1098                 :   }
    1099                 : 
    1100               0 :   DrawAtPositionWithHint(node, offset, hint, bidiLevel, aInvalidate);
    1101               0 :   ToggleDrawnStatus();
    1102                 : }
    1103                 : 
    1104                 : bool
    1105               0 : nsCaret::UpdateCaretRects(nsIFrame* aFrame, PRInt32 aFrameOffset)
    1106                 : {
    1107               0 :   NS_ASSERTION(aFrame, "Should have a frame here");
    1108                 : 
    1109                 :   nscoord bidiIndicatorSize;
    1110                 :   nsresult rv =
    1111               0 :     GetGeometryForFrame(aFrame, aFrameOffset, &mCaretRect, &bidiIndicatorSize);
    1112               0 :   if (NS_FAILED(rv)) {
    1113               0 :     return false;
    1114                 :   }
    1115                 : 
    1116                 :   // on RTL frames the right edge of mCaretRect must be equal to framePos
    1117               0 :   const nsStyleVisibility* vis = aFrame->GetStyleVisibility();
    1118               0 :   if (NS_STYLE_DIRECTION_RTL == vis->mDirection)
    1119               0 :     mCaretRect.x -= mCaretRect.width;
    1120                 : 
    1121                 : #ifdef IBMBIDI
    1122               0 :   mHookRect.SetEmpty();
    1123                 : 
    1124                 :   // Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction
    1125               0 :   bool isCaretRTL = false;
    1126               0 :   nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
    1127                 :   // if bidiKeyboard->IsLangRTL() fails, there is no way to tell the
    1128                 :   // keyboard direction, or the user has no right-to-left keyboard
    1129                 :   // installed, so we never draw the hook.
    1130               0 :   if (bidiKeyboard && NS_SUCCEEDED(bidiKeyboard->IsLangRTL(&isCaretRTL)) &&
    1131                 :       mBidiUI) {
    1132               0 :     if (isCaretRTL != mKeyboardRTL) {
    1133                 :       /* if the caret bidi level and the keyboard language direction are not in
    1134                 :        * synch, the keyboard language must have been changed by the
    1135                 :        * user, and if the caret is in a boundary condition (between left-to-right and
    1136                 :        * right-to-left characters) it may have to change position to
    1137                 :        * reflect the location in which the next character typed will
    1138                 :        * appear. We will call |SelectionLanguageChange| and exit
    1139                 :        * without drawing the caret in the old position.
    1140                 :        */ 
    1141               0 :       mKeyboardRTL = isCaretRTL;
    1142               0 :       nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak);
    1143               0 :       if (!domSelection ||
    1144               0 :           NS_SUCCEEDED(domSelection->SelectionLanguageChange(mKeyboardRTL)))
    1145               0 :         return false;
    1146                 :     }
    1147                 :     // If keyboard language is RTL, draw the hook on the left; if LTR, to the right
    1148                 :     // The height of the hook rectangle is the same as the width of the caret
    1149                 :     // rectangle.
    1150                 :     mHookRect.SetRect(mCaretRect.x + ((isCaretRTL) ?
    1151                 :                       bidiIndicatorSize * -1 :
    1152                 :                       mCaretRect.width),
    1153                 :                       mCaretRect.y + bidiIndicatorSize,
    1154                 :                       bidiIndicatorSize,
    1155               0 :                       mCaretRect.width);
    1156                 :   }
    1157                 : #endif //IBMBIDI
    1158               0 :   return true;
    1159                 : }
    1160                 : 
    1161                 : // static
    1162               0 : void nsCaret::InvalidateRects(const nsRect &aRect, const nsRect &aHook,
    1163                 :                               nsIFrame *aFrame)
    1164                 : {
    1165               0 :   NS_ASSERTION(aFrame, "Must have a frame to invalidate");
    1166               0 :   nsRect rect;
    1167               0 :   rect.UnionRect(aRect, aHook);
    1168               0 :   aFrame->Invalidate(rect);
    1169               0 : }
    1170                 : 
    1171                 : //-----------------------------------------------------------------------------
    1172                 : /* static */
    1173               0 : void nsCaret::CaretBlinkCallback(nsITimer *aTimer, void *aClosure)
    1174                 : {
    1175               0 :   nsCaret   *theCaret = reinterpret_cast<nsCaret*>(aClosure);
    1176               0 :   if (!theCaret) return;
    1177                 :   
    1178               0 :   theCaret->DrawCaret(true);
    1179                 : }
    1180                 : 
    1181                 : 
    1182                 : //-----------------------------------------------------------------------------
    1183                 : already_AddRefed<nsFrameSelection>
    1184               0 : nsCaret::GetFrameSelection()
    1185                 : {
    1186               0 :   nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryReferent(mDomSelectionWeak));
    1187               0 :   if (!privateSelection)
    1188               0 :     return nsnull;
    1189               0 :   nsFrameSelection* frameSelection = nsnull;
    1190               0 :   privateSelection->GetFrameSelection(&frameSelection);
    1191               0 :   return frameSelection;
    1192                 : }
    1193                 : 
    1194                 : void
    1195               0 : nsCaret::SetIgnoreUserModify(bool aIgnoreUserModify)
    1196                 : {
    1197               0 :   if (!aIgnoreUserModify && mIgnoreUserModify && mDrawn) {
    1198                 :     // We're turning off mIgnoreUserModify. If the caret's drawn
    1199                 :     // in a read-only node we must erase it, else the next call
    1200                 :     // to DrawCaret() won't erase the old caret, due to the new
    1201                 :     // mIgnoreUserModify value.
    1202               0 :     nsIFrame *frame = GetCaretFrame();
    1203               0 :     if (frame) {
    1204               0 :       const nsStyleUserInterface* userinterface = frame->GetStyleUserInterface();
    1205               0 :       if (userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) {
    1206               0 :         StopBlinking();
    1207                 :       }
    1208                 :     }
    1209                 :   }
    1210               0 :   mIgnoreUserModify = aIgnoreUserModify;
    1211               0 : }

Generated by: LCOV version 1.7