1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/base/nsCaret.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1124 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=78: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* the caret is the text cursor used, e.g., when editing */ 1.11 + 1.12 +#include "nsCOMPtr.h" 1.13 + 1.14 +#include "nsITimer.h" 1.15 + 1.16 +#include "nsFrameSelection.h" 1.17 +#include "nsIFrame.h" 1.18 +#include "nsIScrollableFrame.h" 1.19 +#include "nsIDOMNode.h" 1.20 +#include "nsISelection.h" 1.21 +#include "nsISelectionPrivate.h" 1.22 +#include "nsIContent.h" 1.23 +#include "nsIPresShell.h" 1.24 +#include "nsRenderingContext.h" 1.25 +#include "nsPresContext.h" 1.26 +#include "nsBlockFrame.h" 1.27 +#include "nsISelectionController.h" 1.28 +#include "nsCaret.h" 1.29 +#include "nsTextFrame.h" 1.30 +#include "nsXULPopupManager.h" 1.31 +#include "nsMenuPopupFrame.h" 1.32 +#include "nsTextFragment.h" 1.33 +#include "mozilla/Preferences.h" 1.34 +#include "mozilla/LookAndFeel.h" 1.35 +#include "mozilla/dom/Selection.h" 1.36 +#include <algorithm> 1.37 + 1.38 +// The bidi indicator hangs off the caret to one side, to show which 1.39 +// direction the typing is in. It needs to be at least 2x2 to avoid looking like 1.40 +// an insignificant dot 1.41 +static const int32_t kMinBidiIndicatorPixels = 2; 1.42 + 1.43 +#include "nsIBidiKeyboard.h" 1.44 +#include "nsContentUtils.h" 1.45 + 1.46 +using namespace mozilla; 1.47 + 1.48 +/** 1.49 + * Find the first frame in an in-order traversal of the frame subtree rooted 1.50 + * at aFrame which is either a text frame logically at the end of a line, 1.51 + * or which is aStopAtFrame. Return null if no such frame is found. We don't 1.52 + * descend into the children of non-eLineParticipant frames. 1.53 + */ 1.54 +static nsIFrame* 1.55 +CheckForTrailingTextFrameRecursive(nsIFrame* aFrame, nsIFrame* aStopAtFrame) 1.56 +{ 1.57 + if (aFrame == aStopAtFrame || 1.58 + ((aFrame->GetType() == nsGkAtoms::textFrame && 1.59 + (static_cast<nsTextFrame*>(aFrame))->IsAtEndOfLine()))) 1.60 + return aFrame; 1.61 + if (!aFrame->IsFrameOfType(nsIFrame::eLineParticipant)) 1.62 + return nullptr; 1.63 + 1.64 + for (nsIFrame* f = aFrame->GetFirstPrincipalChild(); f; f = f->GetNextSibling()) 1.65 + { 1.66 + nsIFrame* r = CheckForTrailingTextFrameRecursive(f, aStopAtFrame); 1.67 + if (r) 1.68 + return r; 1.69 + } 1.70 + return nullptr; 1.71 +} 1.72 + 1.73 +static nsLineBox* 1.74 +FindContainingLine(nsIFrame* aFrame) 1.75 +{ 1.76 + while (aFrame && aFrame->IsFrameOfType(nsIFrame::eLineParticipant)) 1.77 + { 1.78 + nsIFrame* parent = aFrame->GetParent(); 1.79 + nsBlockFrame* blockParent = nsLayoutUtils::GetAsBlock(parent); 1.80 + if (blockParent) 1.81 + { 1.82 + bool isValid; 1.83 + nsBlockInFlowLineIterator iter(blockParent, aFrame, &isValid); 1.84 + return isValid ? iter.GetLine().get() : nullptr; 1.85 + } 1.86 + aFrame = parent; 1.87 + } 1.88 + return nullptr; 1.89 +} 1.90 + 1.91 +static void 1.92 +AdjustCaretFrameForLineEnd(nsIFrame** aFrame, int32_t* aOffset) 1.93 +{ 1.94 + nsLineBox* line = FindContainingLine(*aFrame); 1.95 + if (!line) 1.96 + return; 1.97 + int32_t count = line->GetChildCount(); 1.98 + for (nsIFrame* f = line->mFirstChild; count > 0; --count, f = f->GetNextSibling()) 1.99 + { 1.100 + nsIFrame* r = CheckForTrailingTextFrameRecursive(f, *aFrame); 1.101 + if (r == *aFrame) 1.102 + return; 1.103 + if (r) 1.104 + { 1.105 + *aFrame = r; 1.106 + NS_ASSERTION(r->GetType() == nsGkAtoms::textFrame, "Expected text frame"); 1.107 + *aOffset = (static_cast<nsTextFrame*>(r))->GetContentEnd(); 1.108 + return; 1.109 + } 1.110 + } 1.111 +} 1.112 + 1.113 +//----------------------------------------------------------------------------- 1.114 + 1.115 +nsCaret::nsCaret() 1.116 +: mPresShell(nullptr) 1.117 +, mBlinkRate(500) 1.118 +, mVisible(false) 1.119 +, mDrawn(false) 1.120 +, mPendingDraw(false) 1.121 +, mReadOnly(false) 1.122 +, mShowDuringSelection(false) 1.123 +, mIgnoreUserModify(true) 1.124 +, mKeyboardRTL(false) 1.125 +, mLastBidiLevel(0) 1.126 +, mLastContentOffset(0) 1.127 +, mLastHint(nsFrameSelection::HINTLEFT) 1.128 +{ 1.129 +} 1.130 + 1.131 +//----------------------------------------------------------------------------- 1.132 +nsCaret::~nsCaret() 1.133 +{ 1.134 + KillTimer(); 1.135 +} 1.136 + 1.137 +//----------------------------------------------------------------------------- 1.138 +nsresult nsCaret::Init(nsIPresShell *inPresShell) 1.139 +{ 1.140 + NS_ENSURE_ARG(inPresShell); 1.141 + 1.142 + mPresShell = do_GetWeakReference(inPresShell); // the presshell owns us, so no addref 1.143 + NS_ASSERTION(mPresShell, "Hey, pres shell should support weak refs"); 1.144 + 1.145 + // XXX we should just do this LookAndFeel consultation every time 1.146 + // we need these values. 1.147 + mCaretWidthCSSPx = LookAndFeel::GetInt(LookAndFeel::eIntID_CaretWidth, 1); 1.148 + mCaretAspectRatio = 1.149 + LookAndFeel::GetFloat(LookAndFeel::eFloatID_CaretAspectRatio, 0.0f); 1.150 + 1.151 + mBlinkRate = static_cast<uint32_t>( 1.152 + LookAndFeel::GetInt(LookAndFeel::eIntID_CaretBlinkTime, mBlinkRate)); 1.153 + mShowDuringSelection = 1.154 + LookAndFeel::GetInt(LookAndFeel::eIntID_ShowCaretDuringSelection, 1.155 + mShowDuringSelection ? 1 : 0) != 0; 1.156 + 1.157 + // get the selection from the pres shell, and set ourselves up as a selection 1.158 + // listener 1.159 + 1.160 + nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mPresShell); 1.161 + if (!selCon) 1.162 + return NS_ERROR_FAILURE; 1.163 + 1.164 + nsCOMPtr<nsISelection> domSelection; 1.165 + nsresult rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, 1.166 + getter_AddRefs(domSelection)); 1.167 + if (NS_FAILED(rv)) 1.168 + return rv; 1.169 + if (!domSelection) 1.170 + return NS_ERROR_FAILURE; 1.171 + 1.172 + nsCOMPtr<nsISelectionPrivate> privateSelection = do_QueryInterface(domSelection); 1.173 + if (privateSelection) 1.174 + privateSelection->AddSelectionListener(this); 1.175 + mDomSelectionWeak = do_GetWeakReference(domSelection); 1.176 + 1.177 + // set up the blink timer 1.178 + if (mVisible) 1.179 + { 1.180 + StartBlinking(); 1.181 + } 1.182 + mBidiUI = Preferences::GetBool("bidi.browser.ui"); 1.183 + 1.184 + return NS_OK; 1.185 +} 1.186 + 1.187 +static bool 1.188 +DrawCJKCaret(nsIFrame* aFrame, int32_t aOffset) 1.189 +{ 1.190 + nsIContent* content = aFrame->GetContent(); 1.191 + const nsTextFragment* frag = content->GetText(); 1.192 + if (!frag) 1.193 + return false; 1.194 + if (aOffset < 0 || uint32_t(aOffset) >= frag->GetLength()) 1.195 + return false; 1.196 + char16_t ch = frag->CharAt(aOffset); 1.197 + return 0x2e80 <= ch && ch <= 0xd7ff; 1.198 +} 1.199 + 1.200 +nsCaret::Metrics nsCaret::ComputeMetrics(nsIFrame* aFrame, int32_t aOffset, nscoord aCaretHeight) 1.201 +{ 1.202 + // Compute nominal sizes in appunits 1.203 + nscoord caretWidth = (aCaretHeight * mCaretAspectRatio) + 1.204 + nsPresContext::CSSPixelsToAppUnits(mCaretWidthCSSPx); 1.205 + 1.206 + if (DrawCJKCaret(aFrame, aOffset)) { 1.207 + caretWidth += nsPresContext::CSSPixelsToAppUnits(1); 1.208 + } 1.209 + nscoord bidiIndicatorSize = nsPresContext::CSSPixelsToAppUnits(kMinBidiIndicatorPixels); 1.210 + bidiIndicatorSize = std::max(caretWidth, bidiIndicatorSize); 1.211 + 1.212 + // Round them to device pixels. Always round down, except that anything 1.213 + // between 0 and 1 goes up to 1 so we don't let the caret disappear. 1.214 + int32_t tpp = aFrame->PresContext()->AppUnitsPerDevPixel(); 1.215 + Metrics result; 1.216 + result.mCaretWidth = NS_ROUND_BORDER_TO_PIXELS(caretWidth, tpp); 1.217 + result.mBidiIndicatorSize = NS_ROUND_BORDER_TO_PIXELS(bidiIndicatorSize, tpp); 1.218 + return result; 1.219 +} 1.220 + 1.221 +//----------------------------------------------------------------------------- 1.222 +void nsCaret::Terminate() 1.223 +{ 1.224 + // this doesn't erase the caret if it's drawn. Should it? We might not have 1.225 + // a good drawing environment during teardown. 1.226 + 1.227 + KillTimer(); 1.228 + mBlinkTimer = nullptr; 1.229 + 1.230 + // unregiser ourselves as a selection listener 1.231 + nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak); 1.232 + nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSelection)); 1.233 + if (privateSelection) 1.234 + privateSelection->RemoveSelectionListener(this); 1.235 + mDomSelectionWeak = nullptr; 1.236 + mPresShell = nullptr; 1.237 + 1.238 + mLastContent = nullptr; 1.239 +} 1.240 + 1.241 +//----------------------------------------------------------------------------- 1.242 +NS_IMPL_ISUPPORTS(nsCaret, nsISelectionListener) 1.243 + 1.244 +//----------------------------------------------------------------------------- 1.245 +nsISelection* nsCaret::GetCaretDOMSelection() 1.246 +{ 1.247 + nsCOMPtr<nsISelection> sel(do_QueryReferent(mDomSelectionWeak)); 1.248 + return sel; 1.249 +} 1.250 + 1.251 +//----------------------------------------------------------------------------- 1.252 +nsresult nsCaret::SetCaretDOMSelection(nsISelection *aDOMSel) 1.253 +{ 1.254 + NS_ENSURE_ARG_POINTER(aDOMSel); 1.255 + mDomSelectionWeak = do_GetWeakReference(aDOMSel); // weak reference to pres shell 1.256 + if (mVisible) 1.257 + { 1.258 + // Stop the caret from blinking in its previous location. 1.259 + StopBlinking(); 1.260 + // Start the caret blinking in the new location. 1.261 + StartBlinking(); 1.262 + } 1.263 + return NS_OK; 1.264 +} 1.265 + 1.266 + 1.267 +//----------------------------------------------------------------------------- 1.268 +void nsCaret::SetCaretVisible(bool inMakeVisible) 1.269 +{ 1.270 + mVisible = inMakeVisible; 1.271 + if (mVisible) { 1.272 + SetIgnoreUserModify(true); 1.273 + StartBlinking(); 1.274 + } else { 1.275 + StopBlinking(); 1.276 + SetIgnoreUserModify(false); 1.277 + } 1.278 +} 1.279 + 1.280 + 1.281 +//----------------------------------------------------------------------------- 1.282 +nsresult nsCaret::GetCaretVisible(bool *outMakeVisible) 1.283 +{ 1.284 + NS_ENSURE_ARG_POINTER(outMakeVisible); 1.285 + *outMakeVisible = (mVisible && MustDrawCaret(true)); 1.286 + return NS_OK; 1.287 +} 1.288 + 1.289 + 1.290 +//----------------------------------------------------------------------------- 1.291 +void nsCaret::SetCaretReadOnly(bool inMakeReadonly) 1.292 +{ 1.293 + mReadOnly = inMakeReadonly; 1.294 +} 1.295 + 1.296 +nsresult 1.297 +nsCaret::GetGeometryForFrame(nsIFrame* aFrame, 1.298 + int32_t aFrameOffset, 1.299 + nsRect* aRect, 1.300 + nscoord* aBidiIndicatorSize) 1.301 +{ 1.302 + nsPoint framePos(0, 0); 1.303 + nsresult rv = aFrame->GetPointFromOffset(aFrameOffset, &framePos); 1.304 + if (NS_FAILED(rv)) 1.305 + return rv; 1.306 + 1.307 + nsIFrame *frame = aFrame->GetContentInsertionFrame(); 1.308 + NS_ASSERTION(frame, "We should not be in the middle of reflow"); 1.309 + nscoord baseline = frame->GetCaretBaseline(); 1.310 + nscoord ascent = 0, descent = 0; 1.311 + nsRefPtr<nsFontMetrics> fm; 1.312 + nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm), 1.313 + nsLayoutUtils::FontSizeInflationFor(aFrame)); 1.314 + NS_ASSERTION(fm, "We should be able to get the font metrics"); 1.315 + if (fm) { 1.316 + ascent = fm->MaxAscent(); 1.317 + descent = fm->MaxDescent(); 1.318 + } 1.319 + nscoord height = ascent + descent; 1.320 + framePos.y = baseline - ascent; 1.321 + Metrics caretMetrics = ComputeMetrics(aFrame, aFrameOffset, height); 1.322 + *aRect = nsRect(framePos, nsSize(caretMetrics.mCaretWidth, height)); 1.323 + 1.324 + // Clamp the x-position to be within our scroll frame. If we don't, then it 1.325 + // clips us, and we don't appear at all. See bug 335560. 1.326 + nsIFrame *scrollFrame = 1.327 + nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::scrollFrame); 1.328 + if (scrollFrame) { 1.329 + // First, use the scrollFrame to get at the scrollable view that we're in. 1.330 + nsIScrollableFrame *sf = do_QueryFrame(scrollFrame); 1.331 + nsIFrame *scrolled = sf->GetScrolledFrame(); 1.332 + nsRect caretInScroll = *aRect + aFrame->GetOffsetTo(scrolled); 1.333 + 1.334 + // Now see if thet caret extends beyond the view's bounds. If it does, 1.335 + // then snap it back, put it as close to the edge as it can. 1.336 + nscoord overflow = caretInScroll.XMost() - 1.337 + scrolled->GetVisualOverflowRectRelativeToSelf().width; 1.338 + if (overflow > 0) 1.339 + aRect->x -= overflow; 1.340 + } 1.341 + 1.342 + if (aBidiIndicatorSize) 1.343 + *aBidiIndicatorSize = caretMetrics.mBidiIndicatorSize; 1.344 + 1.345 + return NS_OK; 1.346 +} 1.347 + 1.348 +nsIFrame* nsCaret::GetGeometry(nsISelection* aSelection, nsRect* aRect, 1.349 + nscoord* aBidiIndicatorSize) 1.350 +{ 1.351 + nsCOMPtr<nsIDOMNode> focusNode; 1.352 + nsresult rv = aSelection->GetFocusNode(getter_AddRefs(focusNode)); 1.353 + if (NS_FAILED(rv) || !focusNode) 1.354 + return nullptr; 1.355 + 1.356 + int32_t focusOffset; 1.357 + rv = aSelection->GetFocusOffset(&focusOffset); 1.358 + if (NS_FAILED(rv)) 1.359 + return nullptr; 1.360 + 1.361 + nsCOMPtr<nsIContent> contentNode = do_QueryInterface(focusNode); 1.362 + if (!contentNode) 1.363 + return nullptr; 1.364 + 1.365 + nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); 1.366 + if (!frameSelection) 1.367 + return nullptr; 1.368 + uint8_t bidiLevel = frameSelection->GetCaretBidiLevel(); 1.369 + nsIFrame* frame; 1.370 + int32_t frameOffset; 1.371 + rv = GetCaretFrameForNodeOffset(contentNode, focusOffset, 1.372 + frameSelection->GetHint(), bidiLevel, 1.373 + &frame, &frameOffset); 1.374 + if (NS_FAILED(rv) || !frame) 1.375 + return nullptr; 1.376 + 1.377 + GetGeometryForFrame(frame, frameOffset, aRect, aBidiIndicatorSize); 1.378 + return frame; 1.379 +} 1.380 + 1.381 +void nsCaret::DrawCaretAfterBriefDelay() 1.382 +{ 1.383 + // Make sure readonly caret gets drawn again if it needs to be 1.384 + if (!mBlinkTimer) { 1.385 + nsresult err; 1.386 + mBlinkTimer = do_CreateInstance("@mozilla.org/timer;1", &err); 1.387 + if (NS_FAILED(err)) 1.388 + return; 1.389 + } 1.390 + 1.391 + mBlinkTimer->InitWithFuncCallback(CaretBlinkCallback, this, 0, 1.392 + nsITimer::TYPE_ONE_SHOT); 1.393 +} 1.394 + 1.395 +void nsCaret::EraseCaret() 1.396 +{ 1.397 + if (mDrawn) { 1.398 + DrawCaret(true); 1.399 + if (mReadOnly && mBlinkRate) { 1.400 + // If readonly we don't have a blink timer set, so caret won't 1.401 + // be redrawn automatically. We need to force the caret to get 1.402 + // redrawn right after the paint 1.403 + DrawCaretAfterBriefDelay(); 1.404 + } 1.405 + } 1.406 +} 1.407 + 1.408 +void nsCaret::SetVisibilityDuringSelection(bool aVisibility) 1.409 +{ 1.410 + mShowDuringSelection = aVisibility; 1.411 +} 1.412 + 1.413 +static 1.414 +nsFrameSelection::HINT GetHintForPosition(nsIDOMNode* aNode, int32_t aOffset) 1.415 +{ 1.416 + nsFrameSelection::HINT hint = nsFrameSelection::HINTLEFT; 1.417 + nsCOMPtr<nsIContent> node = do_QueryInterface(aNode); 1.418 + if (!node || aOffset < 1) { 1.419 + return hint; 1.420 + } 1.421 + const nsTextFragment* text = node->GetText(); 1.422 + if (text && text->CharAt(aOffset - 1) == '\n') { 1.423 + // Attach the caret to the next line if needed 1.424 + hint = nsFrameSelection::HINTRIGHT; 1.425 + } 1.426 + return hint; 1.427 +} 1.428 + 1.429 +nsresult nsCaret::DrawAtPosition(nsIDOMNode* aNode, int32_t aOffset) 1.430 +{ 1.431 + NS_ENSURE_ARG(aNode); 1.432 + 1.433 + uint8_t bidiLevel; 1.434 + nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); 1.435 + if (!frameSelection) 1.436 + return NS_ERROR_FAILURE; 1.437 + bidiLevel = frameSelection->GetCaretBidiLevel(); 1.438 + 1.439 + // DrawAtPosition is used by consumers who want us to stay drawn where they 1.440 + // tell us. Setting mBlinkRate to 0 tells us to not set a timer to erase 1.441 + // ourselves, our consumer will take care of that. 1.442 + mBlinkRate = 0; 1.443 + 1.444 + nsresult rv = DrawAtPositionWithHint(aNode, aOffset, 1.445 + GetHintForPosition(aNode, aOffset), 1.446 + bidiLevel, true) 1.447 + ? NS_OK : NS_ERROR_FAILURE; 1.448 + ToggleDrawnStatus(); 1.449 + return rv; 1.450 +} 1.451 + 1.452 +nsIFrame * nsCaret::GetCaretFrame(int32_t *aOffset) 1.453 +{ 1.454 + // Return null if we're not drawn to prevent anybody from trying to draw us. 1.455 + if (!mDrawn) 1.456 + return nullptr; 1.457 + 1.458 + // Recompute the frame that we're supposed to draw in to guarantee that 1.459 + // we're not going to try to draw into a stale (dead) frame. 1.460 + int32_t offset; 1.461 + nsIFrame *frame = nullptr; 1.462 + nsresult rv = GetCaretFrameForNodeOffset(mLastContent, mLastContentOffset, 1.463 + mLastHint, mLastBidiLevel, &frame, 1.464 + &offset); 1.465 + if (NS_FAILED(rv)) 1.466 + return nullptr; 1.467 + 1.468 + if (aOffset) { 1.469 + *aOffset = offset; 1.470 + } 1.471 + return frame; 1.472 +} 1.473 + 1.474 +void nsCaret::InvalidateOutsideCaret() 1.475 +{ 1.476 + nsIFrame *frame = GetCaretFrame(); 1.477 + 1.478 + // Only invalidate if we are not fully contained by our frame's rect. 1.479 + if (frame && !frame->GetVisualOverflowRect().Contains(GetCaretRect())) { 1.480 + frame->SchedulePaint(); 1.481 + } 1.482 +} 1.483 + 1.484 +void nsCaret::UpdateCaretPosition() 1.485 +{ 1.486 + // We'll recalculate anyway if we're not drawn right now. 1.487 + if (!mDrawn) 1.488 + return; 1.489 + 1.490 + // A trick! Make the DrawCaret code recalculate the caret's current 1.491 + // position. 1.492 + mDrawn = false; 1.493 + DrawCaret(false); 1.494 +} 1.495 + 1.496 +void nsCaret::PaintCaret(nsDisplayListBuilder *aBuilder, 1.497 + nsRenderingContext *aCtx, 1.498 + nsIFrame* aForFrame, 1.499 + const nsPoint &aOffset) 1.500 +{ 1.501 + NS_ASSERTION(mDrawn, "The caret shouldn't be drawing"); 1.502 + 1.503 + const nsRect drawCaretRect = mCaretRect + aOffset; 1.504 + int32_t contentOffset; 1.505 + 1.506 +#ifdef DEBUG 1.507 + nsIFrame* frame = 1.508 +#endif 1.509 + GetCaretFrame(&contentOffset); 1.510 + NS_ASSERTION(frame == aForFrame, "We're referring different frame"); 1.511 + // If the offset falls outside of the frame, then don't paint the caret. 1.512 + int32_t startOffset, endOffset; 1.513 + if (aForFrame->GetType() == nsGkAtoms::textFrame && 1.514 + (NS_FAILED(aForFrame->GetOffsets(startOffset, endOffset)) || 1.515 + startOffset > contentOffset || 1.516 + endOffset < contentOffset)) { 1.517 + return; 1.518 + } 1.519 + nscolor foregroundColor = aForFrame->GetCaretColorAt(contentOffset); 1.520 + 1.521 + aCtx->SetColor(foregroundColor); 1.522 + aCtx->FillRect(drawCaretRect); 1.523 + if (!GetHookRect().IsEmpty()) 1.524 + aCtx->FillRect(GetHookRect() + aOffset); 1.525 +} 1.526 + 1.527 + 1.528 +//----------------------------------------------------------------------------- 1.529 +NS_IMETHODIMP nsCaret::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aDomSel, int16_t aReason) 1.530 +{ 1.531 + if (aReason & nsISelectionListener::MOUSEUP_REASON)//this wont do 1.532 + return NS_OK; 1.533 + 1.534 + nsCOMPtr<nsISelection> domSel(do_QueryReferent(mDomSelectionWeak)); 1.535 + 1.536 + // The same caret is shared amongst the document and any text widgets it 1.537 + // may contain. This means that the caret could get notifications from 1.538 + // multiple selections. 1.539 + // 1.540 + // If this notification is for a selection that is not the one the 1.541 + // the caret is currently interested in (mDomSelectionWeak), then there 1.542 + // is nothing to do! 1.543 + 1.544 + if (domSel != aDomSel) 1.545 + return NS_OK; 1.546 + 1.547 + if (mVisible) 1.548 + { 1.549 + // Stop the caret from blinking in its previous location. 1.550 + StopBlinking(); 1.551 + 1.552 + // Start the caret blinking in the new location. 1.553 + StartBlinking(); 1.554 + } 1.555 + 1.556 + return NS_OK; 1.557 +} 1.558 + 1.559 + 1.560 +//----------------------------------------------------------------------------- 1.561 +void nsCaret::KillTimer() 1.562 +{ 1.563 + if (mBlinkTimer) 1.564 + { 1.565 + mBlinkTimer->Cancel(); 1.566 + } 1.567 +} 1.568 + 1.569 + 1.570 +//----------------------------------------------------------------------------- 1.571 +nsresult nsCaret::PrimeTimer() 1.572 +{ 1.573 + // set up the blink timer 1.574 + if (!mReadOnly && mBlinkRate > 0) 1.575 + { 1.576 + if (!mBlinkTimer) { 1.577 + nsresult err; 1.578 + mBlinkTimer = do_CreateInstance("@mozilla.org/timer;1", &err); 1.579 + if (NS_FAILED(err)) 1.580 + return err; 1.581 + } 1.582 + 1.583 + mBlinkTimer->InitWithFuncCallback(CaretBlinkCallback, this, mBlinkRate, 1.584 + nsITimer::TYPE_REPEATING_SLACK); 1.585 + } 1.586 + 1.587 + return NS_OK; 1.588 +} 1.589 + 1.590 +//----------------------------------------------------------------------------- 1.591 +void nsCaret::StartBlinking() 1.592 +{ 1.593 + if (mReadOnly) { 1.594 + // Make sure the one draw command we use for a readonly caret isn't 1.595 + // done until the selection is set 1.596 + DrawCaretAfterBriefDelay(); 1.597 + return; 1.598 + } 1.599 + PrimeTimer(); 1.600 + 1.601 + // If we are currently drawn, then the second call to DrawCaret below will 1.602 + // actually erase the caret. That would cause the caret to spend an "off" 1.603 + // cycle before it appears, which is not really what we want. This first 1.604 + // call to DrawCaret makes sure that the first cycle after a call to 1.605 + // StartBlinking is an "on" cycle. 1.606 + if (mDrawn) 1.607 + DrawCaret(true); 1.608 + 1.609 + DrawCaret(true); // draw it right away 1.610 +} 1.611 + 1.612 + 1.613 +//----------------------------------------------------------------------------- 1.614 +void nsCaret::StopBlinking() 1.615 +{ 1.616 + if (mDrawn) // erase the caret if necessary 1.617 + DrawCaret(true); 1.618 + 1.619 + NS_ASSERTION(!mDrawn, "Caret still drawn after StopBlinking()."); 1.620 + KillTimer(); 1.621 +} 1.622 + 1.623 +bool 1.624 +nsCaret::DrawAtPositionWithHint(nsIDOMNode* aNode, 1.625 + int32_t aOffset, 1.626 + nsFrameSelection::HINT aFrameHint, 1.627 + uint8_t aBidiLevel, 1.628 + bool aInvalidate) 1.629 +{ 1.630 + nsCOMPtr<nsIContent> contentNode = do_QueryInterface(aNode); 1.631 + if (!contentNode) 1.632 + return false; 1.633 + 1.634 + nsIFrame* theFrame = nullptr; 1.635 + int32_t theFrameOffset = 0; 1.636 + 1.637 + nsresult rv = GetCaretFrameForNodeOffset(contentNode, aOffset, aFrameHint, aBidiLevel, 1.638 + &theFrame, &theFrameOffset); 1.639 + if (NS_FAILED(rv) || !theFrame) 1.640 + return false; 1.641 + 1.642 + // now we have a frame, check whether it's appropriate to show the caret here 1.643 + const nsStyleUserInterface* userinterface = theFrame->StyleUserInterface(); 1.644 + if ((!mIgnoreUserModify && 1.645 + userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) || 1.646 + (userinterface->mUserInput == NS_STYLE_USER_INPUT_NONE) || 1.647 + (userinterface->mUserInput == NS_STYLE_USER_INPUT_DISABLED)) 1.648 + { 1.649 + return false; 1.650 + } 1.651 + 1.652 + if (!mDrawn) 1.653 + { 1.654 + // save stuff so we can figure out what frame we're in later. 1.655 + mLastContent = contentNode; 1.656 + mLastContentOffset = aOffset; 1.657 + mLastHint = aFrameHint; 1.658 + mLastBidiLevel = aBidiLevel; 1.659 + 1.660 + // If there has been a reflow, set the caret Bidi level to the level of the current frame 1.661 + if (aBidiLevel & BIDI_LEVEL_UNDEFINED) { 1.662 + nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); 1.663 + if (!frameSelection) 1.664 + return false; 1.665 + frameSelection->SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(theFrame)); 1.666 + } 1.667 + 1.668 + // Only update the caret's rect when we're not currently drawn. 1.669 + if (!UpdateCaretRects(theFrame, theFrameOffset)) 1.670 + return false; 1.671 + } 1.672 + 1.673 + if (aInvalidate) 1.674 + theFrame->SchedulePaint(); 1.675 + 1.676 + return true; 1.677 +} 1.678 + 1.679 +nsresult 1.680 +nsCaret::GetCaretFrameForNodeOffset(nsIContent* aContentNode, 1.681 + int32_t aOffset, 1.682 + nsFrameSelection::HINT aFrameHint, 1.683 + uint8_t aBidiLevel, 1.684 + nsIFrame** aReturnFrame, 1.685 + int32_t* aReturnOffset) 1.686 +{ 1.687 + 1.688 + //get frame selection and find out what frame to use... 1.689 + nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell); 1.690 + if (!presShell) 1.691 + return NS_ERROR_FAILURE; 1.692 + 1.693 + if (!aContentNode || !aContentNode->IsInDoc() || 1.694 + presShell->GetDocument() != aContentNode->GetCurrentDoc()) 1.695 + return NS_ERROR_FAILURE; 1.696 + 1.697 + nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); 1.698 + if (!frameSelection) 1.699 + return NS_ERROR_FAILURE; 1.700 + 1.701 + nsIFrame* theFrame = nullptr; 1.702 + int32_t theFrameOffset = 0; 1.703 + 1.704 + theFrame = frameSelection->GetFrameForNodeOffset(aContentNode, aOffset, 1.705 + aFrameHint, &theFrameOffset); 1.706 + if (!theFrame) 1.707 + return NS_ERROR_FAILURE; 1.708 + 1.709 + // if theFrame is after a text frame that's logically at the end of the line 1.710 + // (e.g. if theFrame is a <br> frame), then put the caret at the end of 1.711 + // that text frame instead. This way, the caret will be positioned as if 1.712 + // trailing whitespace was not trimmed. 1.713 + AdjustCaretFrameForLineEnd(&theFrame, &theFrameOffset); 1.714 + 1.715 + // Mamdouh : modification of the caret to work at rtl and ltr with Bidi 1.716 + // 1.717 + // Direction Style from visibility->mDirection 1.718 + // ------------------ 1.719 + // NS_STYLE_DIRECTION_LTR : LTR or Default 1.720 + // NS_STYLE_DIRECTION_RTL 1.721 + // NS_STYLE_DIRECTION_INHERIT 1.722 + if (mBidiUI) 1.723 + { 1.724 + // If there has been a reflow, take the caret Bidi level to be the level of the current frame 1.725 + if (aBidiLevel & BIDI_LEVEL_UNDEFINED) 1.726 + aBidiLevel = NS_GET_EMBEDDING_LEVEL(theFrame); 1.727 + 1.728 + int32_t start; 1.729 + int32_t end; 1.730 + nsIFrame* frameBefore; 1.731 + nsIFrame* frameAfter; 1.732 + uint8_t levelBefore; // Bidi level of the character before the caret 1.733 + uint8_t levelAfter; // Bidi level of the character after the caret 1.734 + 1.735 + theFrame->GetOffsets(start, end); 1.736 + if (start == 0 || end == 0 || start == theFrameOffset || end == theFrameOffset) 1.737 + { 1.738 + nsPrevNextBidiLevels levels = frameSelection-> 1.739 + GetPrevNextBidiLevels(aContentNode, aOffset, false); 1.740 + 1.741 + /* Boundary condition, we need to know the Bidi levels of the characters before and after the caret */ 1.742 + if (levels.mFrameBefore || levels.mFrameAfter) 1.743 + { 1.744 + frameBefore = levels.mFrameBefore; 1.745 + frameAfter = levels.mFrameAfter; 1.746 + levelBefore = levels.mLevelBefore; 1.747 + levelAfter = levels.mLevelAfter; 1.748 + 1.749 + if ((levelBefore != levelAfter) || (aBidiLevel != levelBefore)) 1.750 + { 1.751 + aBidiLevel = std::max(aBidiLevel, std::min(levelBefore, levelAfter)); // rule c3 1.752 + aBidiLevel = std::min(aBidiLevel, std::max(levelBefore, levelAfter)); // rule c4 1.753 + if (aBidiLevel == levelBefore // rule c1 1.754 + || (aBidiLevel > levelBefore && aBidiLevel < levelAfter && !((aBidiLevel ^ levelBefore) & 1)) // rule c5 1.755 + || (aBidiLevel < levelBefore && aBidiLevel > levelAfter && !((aBidiLevel ^ levelBefore) & 1))) // rule c9 1.756 + { 1.757 + if (theFrame != frameBefore) 1.758 + { 1.759 + if (frameBefore) // if there is a frameBefore, move into it 1.760 + { 1.761 + theFrame = frameBefore; 1.762 + theFrame->GetOffsets(start, end); 1.763 + theFrameOffset = end; 1.764 + } 1.765 + else 1.766 + { 1.767 + // if there is no frameBefore, we must be at the beginning of the line 1.768 + // so we stay with the current frame. 1.769 + // Exception: when the first frame on the line has a different Bidi level from the paragraph level, there is no 1.770 + // real frame for the caret to be in. We have to find the visually first frame on the line. 1.771 + uint8_t baseLevel = NS_GET_BASE_LEVEL(frameAfter); 1.772 + if (baseLevel != levelAfter) 1.773 + { 1.774 + nsPeekOffsetStruct pos(eSelectBeginLine, eDirPrevious, 0, 0, false, true, false, true); 1.775 + if (NS_SUCCEEDED(frameAfter->PeekOffset(&pos))) { 1.776 + theFrame = pos.mResultFrame; 1.777 + theFrameOffset = pos.mContentOffset; 1.778 + } 1.779 + } 1.780 + } 1.781 + } 1.782 + } 1.783 + else if (aBidiLevel == levelAfter // rule c2 1.784 + || (aBidiLevel > levelBefore && aBidiLevel < levelAfter && !((aBidiLevel ^ levelAfter) & 1)) // rule c6 1.785 + || (aBidiLevel < levelBefore && aBidiLevel > levelAfter && !((aBidiLevel ^ levelAfter) & 1))) // rule c10 1.786 + { 1.787 + if (theFrame != frameAfter) 1.788 + { 1.789 + if (frameAfter) 1.790 + { 1.791 + // if there is a frameAfter, move into it 1.792 + theFrame = frameAfter; 1.793 + theFrame->GetOffsets(start, end); 1.794 + theFrameOffset = start; 1.795 + } 1.796 + else 1.797 + { 1.798 + // if there is no frameAfter, we must be at the end of the line 1.799 + // so we stay with the current frame. 1.800 + // Exception: when the last frame on the line has a different Bidi level from the paragraph level, there is no 1.801 + // real frame for the caret to be in. We have to find the visually last frame on the line. 1.802 + uint8_t baseLevel = NS_GET_BASE_LEVEL(frameBefore); 1.803 + if (baseLevel != levelBefore) 1.804 + { 1.805 + nsPeekOffsetStruct pos(eSelectEndLine, eDirNext, 0, 0, false, true, false, true); 1.806 + if (NS_SUCCEEDED(frameBefore->PeekOffset(&pos))) { 1.807 + theFrame = pos.mResultFrame; 1.808 + theFrameOffset = pos.mContentOffset; 1.809 + } 1.810 + } 1.811 + } 1.812 + } 1.813 + } 1.814 + else if (aBidiLevel > levelBefore && aBidiLevel < levelAfter // rule c7/8 1.815 + && !((levelBefore ^ levelAfter) & 1) // before and after have the same parity 1.816 + && ((aBidiLevel ^ levelAfter) & 1)) // caret has different parity 1.817 + { 1.818 + if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(frameAfter, eDirNext, aBidiLevel, &theFrame))) 1.819 + { 1.820 + theFrame->GetOffsets(start, end); 1.821 + levelAfter = NS_GET_EMBEDDING_LEVEL(theFrame); 1.822 + if (aBidiLevel & 1) // c8: caret to the right of the rightmost character 1.823 + theFrameOffset = (levelAfter & 1) ? start : end; 1.824 + else // c7: caret to the left of the leftmost character 1.825 + theFrameOffset = (levelAfter & 1) ? end : start; 1.826 + } 1.827 + } 1.828 + else if (aBidiLevel < levelBefore && aBidiLevel > levelAfter // rule c11/12 1.829 + && !((levelBefore ^ levelAfter) & 1) // before and after have the same parity 1.830 + && ((aBidiLevel ^ levelAfter) & 1)) // caret has different parity 1.831 + { 1.832 + if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(frameBefore, eDirPrevious, aBidiLevel, &theFrame))) 1.833 + { 1.834 + theFrame->GetOffsets(start, end); 1.835 + levelBefore = NS_GET_EMBEDDING_LEVEL(theFrame); 1.836 + if (aBidiLevel & 1) // c12: caret to the left of the leftmost character 1.837 + theFrameOffset = (levelBefore & 1) ? end : start; 1.838 + else // c11: caret to the right of the rightmost character 1.839 + theFrameOffset = (levelBefore & 1) ? start : end; 1.840 + } 1.841 + } 1.842 + } 1.843 + } 1.844 + } 1.845 + } 1.846 + 1.847 + NS_ASSERTION(!theFrame || theFrame->PresContext()->PresShell() == presShell, 1.848 + "caret frame is in wrong document"); 1.849 + *aReturnFrame = theFrame; 1.850 + *aReturnOffset = theFrameOffset; 1.851 + return NS_OK; 1.852 +} 1.853 + 1.854 +void 1.855 +nsCaret::CheckCaretDrawingState() 1.856 +{ 1.857 + if (mDrawn) { 1.858 + // The caret is drawn; if it shouldn't be, erase it. 1.859 + if (!mVisible || !MustDrawCaret(true)) 1.860 + EraseCaret(); 1.861 + } 1.862 + else 1.863 + { 1.864 + // The caret is not drawn; if it should be, draw it. 1.865 + if (mPendingDraw && (mVisible && MustDrawCaret(true))) 1.866 + DrawCaret(true); 1.867 + } 1.868 +} 1.869 + 1.870 +/*----------------------------------------------------------------------------- 1.871 + 1.872 + MustDrawCaret 1.873 + 1.874 + Find out if we need to do any caret drawing. This returns true if 1.875 + either: 1.876 + a) The caret has been drawn, and we need to erase it. 1.877 + b) The caret is not drawn, and the selection is collapsed. 1.878 + c) The caret is not hidden due to open XUL popups 1.879 + (see IsMenuPopupHidingCaret()). 1.880 + 1.881 +----------------------------------------------------------------------------- */ 1.882 +bool nsCaret::MustDrawCaret(bool aIgnoreDrawnState) 1.883 +{ 1.884 + if (!aIgnoreDrawnState && mDrawn) 1.885 + return true; 1.886 + 1.887 + nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak); 1.888 + if (!domSelection) 1.889 + return false; 1.890 + 1.891 + bool isCollapsed; 1.892 + if (NS_FAILED(domSelection->GetIsCollapsed(&isCollapsed))) 1.893 + return false; 1.894 + 1.895 + if (mShowDuringSelection) 1.896 + return true; // show the caret even in selections 1.897 + 1.898 + if (IsMenuPopupHidingCaret()) 1.899 + return false; 1.900 + 1.901 + return isCollapsed; 1.902 +} 1.903 + 1.904 +bool nsCaret::IsMenuPopupHidingCaret() 1.905 +{ 1.906 +#ifdef MOZ_XUL 1.907 + // Check if there are open popups. 1.908 + nsXULPopupManager *popMgr = nsXULPopupManager::GetInstance(); 1.909 + nsTArray<nsIFrame*> popups; 1.910 + popMgr->GetVisiblePopups(popups); 1.911 + 1.912 + if (popups.Length() == 0) 1.913 + return false; // No popups, so caret can't be hidden by them. 1.914 + 1.915 + // Get the selection focus content, that's where the caret would 1.916 + // go if it was drawn. 1.917 + nsCOMPtr<nsIDOMNode> node; 1.918 + nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak); 1.919 + if (!domSelection) 1.920 + return true; // No selection/caret to draw. 1.921 + domSelection->GetFocusNode(getter_AddRefs(node)); 1.922 + if (!node) 1.923 + return true; // No selection/caret to draw. 1.924 + nsCOMPtr<nsIContent> caretContent = do_QueryInterface(node); 1.925 + if (!caretContent) 1.926 + return true; // No selection/caret to draw. 1.927 + 1.928 + // If there's a menu popup open before the popup with 1.929 + // the caret, don't show the caret. 1.930 + for (uint32_t i=0; i<popups.Length(); i++) { 1.931 + nsMenuPopupFrame* popupFrame = static_cast<nsMenuPopupFrame*>(popups[i]); 1.932 + nsIContent* popupContent = popupFrame->GetContent(); 1.933 + 1.934 + if (nsContentUtils::ContentIsDescendantOf(caretContent, popupContent)) { 1.935 + // The caret is in this popup. There were no menu popups before this 1.936 + // popup, so don't hide the caret. 1.937 + return false; 1.938 + } 1.939 + 1.940 + if (popupFrame->PopupType() == ePopupTypeMenu && !popupFrame->IsContextMenu()) { 1.941 + // This is an open menu popup. It does not contain the caret (else we'd 1.942 + // have returned above). Even if the caret is in a subsequent popup, 1.943 + // or another document/frame, it should be hidden. 1.944 + return true; 1.945 + } 1.946 + } 1.947 +#endif 1.948 + 1.949 + // There are no open menu popups, no need to hide the caret. 1.950 + return false; 1.951 +} 1.952 + 1.953 +void nsCaret::DrawCaret(bool aInvalidate) 1.954 +{ 1.955 + // Do we need to draw the caret at all? 1.956 + if (!MustDrawCaret(false)) 1.957 + return; 1.958 + 1.959 + // Can we draw the caret now? 1.960 + nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell); 1.961 + NS_ENSURE_TRUE_VOID(presShell); 1.962 + { 1.963 + if (presShell->IsPaintingSuppressed()) 1.964 + { 1.965 + if (!mDrawn) 1.966 + mPendingDraw = true; 1.967 + 1.968 + // PresShell::UnsuppressAndInvalidate() will call CheckCaretDrawingState() 1.969 + // to get us drawn. 1.970 + return; 1.971 + } 1.972 + } 1.973 + 1.974 + nsCOMPtr<nsIDOMNode> node; 1.975 + int32_t offset; 1.976 + nsFrameSelection::HINT hint; 1.977 + uint8_t bidiLevel; 1.978 + 1.979 + if (!mDrawn) 1.980 + { 1.981 + nsCOMPtr<nsISelection> domSelection = do_QueryReferent(mDomSelectionWeak); 1.982 + nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(domSelection)); 1.983 + if (!privateSelection) return; 1.984 + 1.985 + bool isCollapsed = false; 1.986 + domSelection->GetIsCollapsed(&isCollapsed); 1.987 + if (!mShowDuringSelection && !isCollapsed) 1.988 + return; 1.989 + 1.990 + bool hintRight; 1.991 + privateSelection->GetInterlinePosition(&hintRight);//translate hint. 1.992 + hint = hintRight ? nsFrameSelection::HINTRIGHT : nsFrameSelection::HINTLEFT; 1.993 + 1.994 + // get the node and offset, which is where we want the caret to draw 1.995 + domSelection->GetFocusNode(getter_AddRefs(node)); 1.996 + if (!node) 1.997 + return; 1.998 + 1.999 + if (NS_FAILED(domSelection->GetFocusOffset(&offset))) 1.1000 + return; 1.1001 + 1.1002 + nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection(); 1.1003 + if (!frameSelection) 1.1004 + return; 1.1005 + 1.1006 + bidiLevel = frameSelection->GetCaretBidiLevel(); 1.1007 + mPendingDraw = false; 1.1008 + } 1.1009 + else 1.1010 + { 1.1011 + if (!mLastContent) 1.1012 + { 1.1013 + mDrawn = false; 1.1014 + return; 1.1015 + } 1.1016 + if (!mLastContent->IsInDoc() || 1.1017 + presShell->GetDocument() != mLastContent->GetCurrentDoc()) 1.1018 + { 1.1019 + mLastContent = nullptr; 1.1020 + mDrawn = false; 1.1021 + return; 1.1022 + } 1.1023 + node = do_QueryInterface(mLastContent); 1.1024 + offset = mLastContentOffset; 1.1025 + hint = mLastHint; 1.1026 + bidiLevel = mLastBidiLevel; 1.1027 + } 1.1028 + 1.1029 + DrawAtPositionWithHint(node, offset, hint, bidiLevel, aInvalidate); 1.1030 + ToggleDrawnStatus(); 1.1031 +} 1.1032 + 1.1033 +bool 1.1034 +nsCaret::UpdateCaretRects(nsIFrame* aFrame, int32_t aFrameOffset) 1.1035 +{ 1.1036 + NS_ASSERTION(aFrame, "Should have a frame here"); 1.1037 + 1.1038 + nscoord bidiIndicatorSize; 1.1039 + nsresult rv = 1.1040 + GetGeometryForFrame(aFrame, aFrameOffset, &mCaretRect, &bidiIndicatorSize); 1.1041 + if (NS_FAILED(rv)) { 1.1042 + return false; 1.1043 + } 1.1044 + 1.1045 + // on RTL frames the right edge of mCaretRect must be equal to framePos 1.1046 + const nsStyleVisibility* vis = aFrame->StyleVisibility(); 1.1047 + if (NS_STYLE_DIRECTION_RTL == vis->mDirection) 1.1048 + mCaretRect.x -= mCaretRect.width; 1.1049 + 1.1050 + mHookRect.SetEmpty(); 1.1051 + 1.1052 + // Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction 1.1053 + bool isCaretRTL = false; 1.1054 + nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard(); 1.1055 + // if bidiKeyboard->IsLangRTL() fails, there is no way to tell the 1.1056 + // keyboard direction, or the user has no right-to-left keyboard 1.1057 + // installed, so we never draw the hook. 1.1058 + if (bidiKeyboard && NS_SUCCEEDED(bidiKeyboard->IsLangRTL(&isCaretRTL)) && 1.1059 + mBidiUI) { 1.1060 + if (isCaretRTL != mKeyboardRTL) { 1.1061 + /* if the caret bidi level and the keyboard language direction are not in 1.1062 + * synch, the keyboard language must have been changed by the 1.1063 + * user, and if the caret is in a boundary condition (between left-to-right and 1.1064 + * right-to-left characters) it may have to change position to 1.1065 + * reflect the location in which the next character typed will 1.1066 + * appear. We will call |SelectionLanguageChange| and exit 1.1067 + * without drawing the caret in the old position. 1.1068 + */ 1.1069 + mKeyboardRTL = isCaretRTL; 1.1070 + nsCOMPtr<nsISelectionPrivate> domSelection = do_QueryReferent(mDomSelectionWeak); 1.1071 + if (!domSelection || 1.1072 + NS_SUCCEEDED(domSelection->SelectionLanguageChange(mKeyboardRTL))) 1.1073 + return false; 1.1074 + } 1.1075 + // If keyboard language is RTL, draw the hook on the left; if LTR, to the right 1.1076 + // The height of the hook rectangle is the same as the width of the caret 1.1077 + // rectangle. 1.1078 + mHookRect.SetRect(mCaretRect.x + ((isCaretRTL) ? 1.1079 + bidiIndicatorSize * -1 : 1.1080 + mCaretRect.width), 1.1081 + mCaretRect.y + bidiIndicatorSize, 1.1082 + bidiIndicatorSize, 1.1083 + mCaretRect.width); 1.1084 + } 1.1085 + return true; 1.1086 +} 1.1087 + 1.1088 +//----------------------------------------------------------------------------- 1.1089 +/* static */ 1.1090 +void nsCaret::CaretBlinkCallback(nsITimer *aTimer, void *aClosure) 1.1091 +{ 1.1092 + nsCaret *theCaret = reinterpret_cast<nsCaret*>(aClosure); 1.1093 + if (!theCaret) return; 1.1094 + 1.1095 + theCaret->DrawCaret(true); 1.1096 +} 1.1097 + 1.1098 + 1.1099 +//----------------------------------------------------------------------------- 1.1100 +nsFrameSelection* 1.1101 +nsCaret::GetFrameSelection() 1.1102 +{ 1.1103 + nsCOMPtr<nsISelection> sel = do_QueryReferent(mDomSelectionWeak); 1.1104 + if (!sel) 1.1105 + return nullptr; 1.1106 + 1.1107 + return static_cast<dom::Selection*>(sel.get())->GetFrameSelection(); 1.1108 +} 1.1109 + 1.1110 +void 1.1111 +nsCaret::SetIgnoreUserModify(bool aIgnoreUserModify) 1.1112 +{ 1.1113 + if (!aIgnoreUserModify && mIgnoreUserModify && mDrawn) { 1.1114 + // We're turning off mIgnoreUserModify. If the caret's drawn 1.1115 + // in a read-only node we must erase it, else the next call 1.1116 + // to DrawCaret() won't erase the old caret, due to the new 1.1117 + // mIgnoreUserModify value. 1.1118 + nsIFrame *frame = GetCaretFrame(); 1.1119 + if (frame) { 1.1120 + const nsStyleUserInterface* userinterface = frame->StyleUserInterface(); 1.1121 + if (userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) { 1.1122 + StopBlinking(); 1.1123 + } 1.1124 + } 1.1125 + } 1.1126 + mIgnoreUserModify = aIgnoreUserModify; 1.1127 +}