1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsSelection.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,5886 @@ 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 +/* 1.11 + * Implementation of selection: nsISelection,nsISelectionPrivate and nsFrameSelection 1.12 + */ 1.13 + 1.14 +#include "mozilla/dom/Selection.h" 1.15 + 1.16 +#include "mozilla/Attributes.h" 1.17 +#include "mozilla/EventStates.h" 1.18 + 1.19 +#include "nsCOMPtr.h" 1.20 +#include "nsString.h" 1.21 +#include "nsFrameSelection.h" 1.22 +#include "nsISelectionListener.h" 1.23 +#include "nsContentCID.h" 1.24 +#include "nsIContent.h" 1.25 +#include "nsIDOMNode.h" 1.26 +#include "nsRange.h" 1.27 +#include "nsCOMArray.h" 1.28 +#include "nsIDOMKeyEvent.h" 1.29 +#include "nsITableCellLayout.h" 1.30 +#include "nsTArray.h" 1.31 +#include "nsTableOuterFrame.h" 1.32 +#include "nsTableCellFrame.h" 1.33 +#include "nsIScrollableFrame.h" 1.34 +#include "nsCCUncollectableMarker.h" 1.35 +#include "nsIContentIterator.h" 1.36 +#include "nsIDocumentEncoder.h" 1.37 +#include "nsTextFragment.h" 1.38 +#include <algorithm> 1.39 + 1.40 +#include "nsGkAtoms.h" 1.41 +#include "nsIFrameTraversal.h" 1.42 +#include "nsLayoutUtils.h" 1.43 +#include "nsLayoutCID.h" 1.44 +#include "nsBidiPresUtils.h" 1.45 +static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID); 1.46 +#include "nsTextFrame.h" 1.47 + 1.48 +#include "nsIDOMText.h" 1.49 + 1.50 +#include "nsContentUtils.h" 1.51 +#include "nsThreadUtils.h" 1.52 +#include "mozilla/Preferences.h" 1.53 +#include "nsDOMClassInfoID.h" 1.54 + 1.55 +//included for desired x position; 1.56 +#include "nsPresContext.h" 1.57 +#include "nsIPresShell.h" 1.58 +#include "nsCaret.h" 1.59 + 1.60 +#include "mozilla/MouseEvents.h" 1.61 +#include "mozilla/TextEvents.h" 1.62 + 1.63 +#include "nsITimer.h" 1.64 +#include "nsFrameManager.h" 1.65 +// notifications 1.66 +#include "nsIDOMDocument.h" 1.67 +#include "nsIDocument.h" 1.68 + 1.69 +#include "nsISelectionController.h"//for the enums 1.70 +#include "nsAutoCopyListener.h" 1.71 +#include "nsCopySupport.h" 1.72 +#include "nsIClipboard.h" 1.73 +#include "nsIFrameInlines.h" 1.74 + 1.75 +#include "nsIBidiKeyboard.h" 1.76 + 1.77 +#include "nsError.h" 1.78 +#include "mozilla/dom/Element.h" 1.79 +#include "mozilla/dom/ShadowRoot.h" 1.80 +#include "mozilla/ErrorResult.h" 1.81 +#include "mozilla/dom/SelectionBinding.h" 1.82 + 1.83 +using namespace mozilla; 1.84 +using namespace mozilla::dom; 1.85 + 1.86 +//#define DEBUG_TABLE 1 1.87 + 1.88 +static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode); 1.89 + 1.90 +static nsIAtom *GetTag(nsINode *aNode); 1.91 +// returns the parent 1.92 +static nsINode* ParentOffset(nsINode *aNode, int32_t *aChildOffset); 1.93 +static nsINode* GetCellParent(nsINode *aDomNode); 1.94 + 1.95 +#ifdef PRINT_RANGE 1.96 +static void printRange(nsRange *aDomRange); 1.97 +#define DEBUG_OUT_RANGE(x) printRange(x) 1.98 +#else 1.99 +#define DEBUG_OUT_RANGE(x) 1.100 +#endif // PRINT_RANGE 1.101 + 1.102 + 1.103 + 1.104 +//#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend. 1.105 +//#define DEBUG_NAVIGATION 1.106 + 1.107 + 1.108 +//#define DEBUG_TABLE_SELECTION 1 1.109 + 1.110 +struct CachedOffsetForFrame { 1.111 + CachedOffsetForFrame() 1.112 + : mCachedFrameOffset(0, 0) // nsPoint ctor 1.113 + , mLastCaretFrame(nullptr) 1.114 + , mLastContentOffset(0) 1.115 + , mCanCacheFrameOffset(false) 1.116 + {} 1.117 + 1.118 + nsPoint mCachedFrameOffset; // cached frame offset 1.119 + nsIFrame* mLastCaretFrame; // store the frame the caret was last drawn in. 1.120 + int32_t mLastContentOffset; // store last content offset 1.121 + bool mCanCacheFrameOffset; // cached frame offset is valid? 1.122 +}; 1.123 + 1.124 +// Stack-class to turn on/off selection batching for table selection 1.125 +class MOZ_STACK_CLASS nsSelectionBatcher MOZ_FINAL 1.126 +{ 1.127 +private: 1.128 + nsCOMPtr<nsISelectionPrivate> mSelection; 1.129 +public: 1.130 + nsSelectionBatcher(nsISelectionPrivate *aSelection) : mSelection(aSelection) 1.131 + { 1.132 + if (mSelection) mSelection->StartBatchChanges(); 1.133 + } 1.134 + ~nsSelectionBatcher() 1.135 + { 1.136 + if (mSelection) mSelection->EndBatchChanges(); 1.137 + } 1.138 +}; 1.139 + 1.140 +class nsAutoScrollTimer : public nsITimerCallback 1.141 +{ 1.142 +public: 1.143 + 1.144 + NS_DECL_ISUPPORTS 1.145 + 1.146 + nsAutoScrollTimer() 1.147 + : mFrameSelection(0), mSelection(0), mPresContext(0), mPoint(0,0), mDelay(30) 1.148 + { 1.149 + } 1.150 + 1.151 + virtual ~nsAutoScrollTimer() 1.152 + { 1.153 + if (mTimer) 1.154 + mTimer->Cancel(); 1.155 + } 1.156 + 1.157 + // aPoint is relative to aPresContext's root frame 1.158 + nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint) 1.159 + { 1.160 + mPoint = aPoint; 1.161 + 1.162 + // Store the presentation context. The timer will be 1.163 + // stopped by the selection if the prescontext is destroyed. 1.164 + mPresContext = aPresContext; 1.165 + 1.166 + mContent = nsIPresShell::GetCapturingContent(); 1.167 + 1.168 + if (!mTimer) 1.169 + { 1.170 + nsresult result; 1.171 + mTimer = do_CreateInstance("@mozilla.org/timer;1", &result); 1.172 + 1.173 + if (NS_FAILED(result)) 1.174 + return result; 1.175 + } 1.176 + 1.177 + return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT); 1.178 + } 1.179 + 1.180 + nsresult Stop() 1.181 + { 1.182 + if (mTimer) 1.183 + { 1.184 + mTimer->Cancel(); 1.185 + mTimer = 0; 1.186 + } 1.187 + 1.188 + mContent = nullptr; 1.189 + return NS_OK; 1.190 + } 1.191 + 1.192 + nsresult Init(nsFrameSelection* aFrameSelection, Selection* aSelection) 1.193 + { 1.194 + mFrameSelection = aFrameSelection; 1.195 + mSelection = aSelection; 1.196 + return NS_OK; 1.197 + } 1.198 + 1.199 + nsresult SetDelay(uint32_t aDelay) 1.200 + { 1.201 + mDelay = aDelay; 1.202 + return NS_OK; 1.203 + } 1.204 + 1.205 + NS_IMETHOD Notify(nsITimer *timer) MOZ_OVERRIDE 1.206 + { 1.207 + if (mSelection && mPresContext) 1.208 + { 1.209 + nsWeakFrame frame = 1.210 + mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nullptr; 1.211 + if (!frame) 1.212 + return NS_OK; 1.213 + mContent = nullptr; 1.214 + 1.215 + nsPoint pt = mPoint - 1.216 + frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame()); 1.217 + mFrameSelection->HandleDrag(frame, pt); 1.218 + if (!frame.IsAlive()) 1.219 + return NS_OK; 1.220 + 1.221 + NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?"); 1.222 + mSelection->DoAutoScroll(frame, pt); 1.223 + } 1.224 + return NS_OK; 1.225 + } 1.226 +private: 1.227 + nsFrameSelection *mFrameSelection; 1.228 + Selection* mSelection; 1.229 + nsPresContext *mPresContext; 1.230 + // relative to mPresContext's root frame 1.231 + nsPoint mPoint; 1.232 + nsCOMPtr<nsITimer> mTimer; 1.233 + nsCOMPtr<nsIContent> mContent; 1.234 + uint32_t mDelay; 1.235 +}; 1.236 + 1.237 +NS_IMPL_ISUPPORTS(nsAutoScrollTimer, nsITimerCallback) 1.238 + 1.239 +nsresult NS_NewDomSelection(nsISelection **aDomSelection) 1.240 +{ 1.241 + Selection* rlist = new Selection; 1.242 + *aDomSelection = (nsISelection *)rlist; 1.243 + NS_ADDREF(rlist); 1.244 + return NS_OK; 1.245 +} 1.246 + 1.247 +static int8_t 1.248 +GetIndexFromSelectionType(SelectionType aType) 1.249 +{ 1.250 + switch (aType) 1.251 + { 1.252 + case nsISelectionController::SELECTION_NORMAL: return 0; break; 1.253 + case nsISelectionController::SELECTION_SPELLCHECK: return 1; break; 1.254 + case nsISelectionController::SELECTION_IME_RAWINPUT: return 2; break; 1.255 + case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT: return 3; break; 1.256 + case nsISelectionController::SELECTION_IME_CONVERTEDTEXT: return 4; break; 1.257 + case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT: return 5; break; 1.258 + case nsISelectionController::SELECTION_ACCESSIBILITY: return 6; break; 1.259 + case nsISelectionController::SELECTION_FIND: return 7; break; 1.260 + case nsISelectionController::SELECTION_URLSECONDARY: return 8; break; 1.261 + default: 1.262 + return -1; break; 1.263 + } 1.264 + /* NOTREACHED */ 1.265 + return 0; 1.266 +} 1.267 + 1.268 +static SelectionType 1.269 +GetSelectionTypeFromIndex(int8_t aIndex) 1.270 +{ 1.271 + switch (aIndex) 1.272 + { 1.273 + case 0: return nsISelectionController::SELECTION_NORMAL; break; 1.274 + case 1: return nsISelectionController::SELECTION_SPELLCHECK; break; 1.275 + case 2: return nsISelectionController::SELECTION_IME_RAWINPUT; break; 1.276 + case 3: return nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT; break; 1.277 + case 4: return nsISelectionController::SELECTION_IME_CONVERTEDTEXT; break; 1.278 + case 5: return nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT; break; 1.279 + case 6: return nsISelectionController::SELECTION_ACCESSIBILITY; break; 1.280 + case 7: return nsISelectionController::SELECTION_FIND; break; 1.281 + case 8: return nsISelectionController::SELECTION_URLSECONDARY; break; 1.282 + default: 1.283 + return nsISelectionController::SELECTION_NORMAL; break; 1.284 + } 1.285 + /* NOTREACHED */ 1.286 + return 0; 1.287 +} 1.288 + 1.289 +/* 1.290 +The limiter is used specifically for the text areas and textfields 1.291 +In that case it is the DIV tag that is anonymously created for the text 1.292 +areas/fields. Text nodes and BR nodes fall beneath it. In the case of a 1.293 +BR node the limiter will be the parent and the offset will point before or 1.294 +after the BR node. In the case of the text node the parent content is 1.295 +the text node itself and the offset will be the exact character position. 1.296 +The offset is not important to check for validity. Simply look at the 1.297 +passed in content. If it equals the limiter then the selection point is valid. 1.298 +If its parent it the limiter then the point is also valid. In the case of 1.299 +NO limiter all points are valid since you are in a topmost iframe. (browser 1.300 +or composer) 1.301 +*/ 1.302 +bool 1.303 +IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode) 1.304 +{ 1.305 + if (!aFrameSel || !aNode) 1.306 + return false; 1.307 + 1.308 + nsIContent *limiter = aFrameSel->GetLimiter(); 1.309 + if (limiter && limiter != aNode && limiter != aNode->GetParent()) { 1.310 + //if newfocus == the limiter. that's ok. but if not there and not parent bad 1.311 + return false; //not in the right content. tLimiter said so 1.312 + } 1.313 + 1.314 + limiter = aFrameSel->GetAncestorLimiter(); 1.315 + return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter); 1.316 +} 1.317 + 1.318 + 1.319 +////////////BEGIN nsFrameSelection methods 1.320 + 1.321 +nsFrameSelection::nsFrameSelection() 1.322 +{ 1.323 + int32_t i; 1.324 + for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){ 1.325 + mDomSelections[i] = new Selection(this); 1.326 + mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i)); 1.327 + } 1.328 + mBatching = 0; 1.329 + mChangesDuringBatching = false; 1.330 + mNotifyFrames = true; 1.331 + 1.332 + mMouseDoubleDownState = false; 1.333 + 1.334 + mHint = HINTLEFT; 1.335 + mCaretBidiLevel = BIDI_LEVEL_UNDEFINED; 1.336 + mDragSelectingCells = false; 1.337 + mSelectingTableCellMode = 0; 1.338 + mSelectedCellIndex = 0; 1.339 + 1.340 + // Check to see if the autocopy pref is enabled 1.341 + // and add the autocopy listener if it is 1.342 + if (Preferences::GetBool("clipboard.autocopy")) { 1.343 + nsAutoCopyListener *autoCopy = nsAutoCopyListener::GetInstance(); 1.344 + 1.345 + if (autoCopy) { 1.346 + int8_t index = 1.347 + GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.348 + if (mDomSelections[index]) { 1.349 + autoCopy->Listen(mDomSelections[index]); 1.350 + } 1.351 + } 1.352 + } 1.353 + 1.354 + mDisplaySelection = nsISelectionController::SELECTION_OFF; 1.355 + mSelectionChangeReason = nsISelectionListener::NO_REASON; 1.356 + 1.357 + mDelayedMouseEventValid = false; 1.358 + // These values are not used since they are only valid when 1.359 + // mDelayedMouseEventValid is true, and setting mDelayedMouseEventValid 1.360 + //alwaysoverrides these values. 1.361 + mDelayedMouseEventIsShift = false; 1.362 + mDelayedMouseEventClickCount = 0; 1.363 +} 1.364 + 1.365 + 1.366 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameSelection) 1.367 + 1.368 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameSelection) 1.369 + int32_t i; 1.370 + for (i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; ++i) { 1.371 + tmp->mDomSelections[i] = nullptr; 1.372 + } 1.373 + 1.374 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCellParent) 1.375 + tmp->mSelectingTableCellMode = 0; 1.376 + tmp->mDragSelectingCells = false; 1.377 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartSelectedCell) 1.378 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSelectedCell) 1.379 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAppendStartSelectedCell) 1.380 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnselectCellOnMouseUp) 1.381 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mMaintainRange) 1.382 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLimiter) 1.383 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAncestorLimiter) 1.384 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.385 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameSelection) 1.386 + if (tmp->mShell && tmp->mShell->GetDocument() && 1.387 + nsCCUncollectableMarker::InGeneration(cb, 1.388 + tmp->mShell->GetDocument()-> 1.389 + GetMarkedCCGeneration())) { 1.390 + return NS_SUCCESS_INTERRUPTED_TRAVERSE; 1.391 + } 1.392 + int32_t i; 1.393 + for (i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; ++i) { 1.394 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDomSelections[i]) 1.395 + } 1.396 + 1.397 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCellParent) 1.398 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartSelectedCell) 1.399 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSelectedCell) 1.400 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAppendStartSelectedCell) 1.401 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnselectCellOnMouseUp) 1.402 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMaintainRange) 1.403 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLimiter) 1.404 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAncestorLimiter) 1.405 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.406 + 1.407 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsFrameSelection, AddRef) 1.408 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsFrameSelection, Release) 1.409 + 1.410 + 1.411 +nsresult 1.412 +nsFrameSelection::FetchDesiredX(nscoord &aDesiredX) //the x position requested by the Key Handling for up down 1.413 +{ 1.414 + if (!mShell) 1.415 + { 1.416 + NS_ERROR("fetch desired X failed"); 1.417 + return NS_ERROR_FAILURE; 1.418 + } 1.419 + if (mDesiredXSet) 1.420 + { 1.421 + aDesiredX = mDesiredX; 1.422 + return NS_OK; 1.423 + } 1.424 + 1.425 + nsRefPtr<nsCaret> caret = mShell->GetCaret(); 1.426 + if (!caret) 1.427 + return NS_ERROR_NULL_POINTER; 1.428 + 1.429 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.430 + nsresult result = caret->SetCaretDOMSelection(mDomSelections[index]); 1.431 + if (NS_FAILED(result)) 1.432 + return result; 1.433 + 1.434 + nsRect coord; 1.435 + nsIFrame* caretFrame = caret->GetGeometry(mDomSelections[index], &coord); 1.436 + if (!caretFrame) 1.437 + return NS_ERROR_FAILURE; 1.438 + nsPoint viewOffset(0, 0); 1.439 + nsView* view = nullptr; 1.440 + caretFrame->GetOffsetFromView(viewOffset, &view); 1.441 + if (view) 1.442 + coord.x += viewOffset.x; 1.443 + 1.444 + aDesiredX = coord.x; 1.445 + return NS_OK; 1.446 +} 1.447 + 1.448 + 1.449 + 1.450 +void 1.451 +nsFrameSelection::InvalidateDesiredX() //do not listen to mDesiredX you must get another. 1.452 +{ 1.453 + mDesiredXSet = false; 1.454 +} 1.455 + 1.456 + 1.457 + 1.458 +void 1.459 +nsFrameSelection::SetDesiredX(nscoord aX) //set the mDesiredX 1.460 +{ 1.461 + mDesiredX = aX; 1.462 + mDesiredXSet = true; 1.463 +} 1.464 + 1.465 +nsresult 1.466 +nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(nsIFrame *aFrame, 1.467 + nsPoint& aPoint, 1.468 + nsIFrame **aRetFrame, 1.469 + nsPoint& aRetPoint) 1.470 +{ 1.471 + // 1.472 + // The whole point of this method is to return a frame and point that 1.473 + // that lie within the same valid subtree as the anchor node's frame, 1.474 + // for use with the method GetContentAndOffsetsFromPoint(). 1.475 + // 1.476 + // A valid subtree is defined to be one where all the content nodes in 1.477 + // the tree have a valid parent-child relationship. 1.478 + // 1.479 + // If the anchor frame and aFrame are in the same subtree, aFrame will 1.480 + // be returned in aRetFrame. If they are in different subtrees, we 1.481 + // return the frame for the root of the subtree. 1.482 + // 1.483 + 1.484 + if (!aFrame || !aRetFrame) 1.485 + return NS_ERROR_NULL_POINTER; 1.486 + 1.487 + *aRetFrame = aFrame; 1.488 + aRetPoint = aPoint; 1.489 + 1.490 + // 1.491 + // Get the frame and content for the selection's anchor point! 1.492 + // 1.493 + 1.494 + nsresult result; 1.495 + nsCOMPtr<nsIDOMNode> anchorNode; 1.496 + int32_t anchorOffset = 0; 1.497 + 1.498 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.499 + if (!mDomSelections[index]) 1.500 + return NS_ERROR_NULL_POINTER; 1.501 + 1.502 + result = mDomSelections[index]->GetAnchorNode(getter_AddRefs(anchorNode)); 1.503 + 1.504 + if (NS_FAILED(result)) 1.505 + return result; 1.506 + 1.507 + if (!anchorNode) 1.508 + return NS_OK; 1.509 + 1.510 + result = mDomSelections[index]->GetAnchorOffset(&anchorOffset); 1.511 + 1.512 + if (NS_FAILED(result)) 1.513 + return result; 1.514 + 1.515 + nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode); 1.516 + 1.517 + if (!anchorContent) 1.518 + return NS_ERROR_FAILURE; 1.519 + 1.520 + // 1.521 + // Now find the root of the subtree containing the anchor's content. 1.522 + // 1.523 + 1.524 + NS_ENSURE_STATE(mShell); 1.525 + nsIContent* anchorRoot = anchorContent->GetSelectionRootContent(mShell); 1.526 + NS_ENSURE_TRUE(anchorRoot, NS_ERROR_UNEXPECTED); 1.527 + 1.528 + // 1.529 + // Now find the root of the subtree containing aFrame's content. 1.530 + // 1.531 + 1.532 + nsIContent* content = aFrame->GetContent(); 1.533 + 1.534 + if (content) 1.535 + { 1.536 + nsIContent* contentRoot = content->GetSelectionRootContent(mShell); 1.537 + NS_ENSURE_TRUE(contentRoot, NS_ERROR_UNEXPECTED); 1.538 + 1.539 + if (anchorRoot == contentRoot) 1.540 + { 1.541 + // If the aFrame's content isn't the capturing content, it should be 1.542 + // a descendant. At this time, we can return simply. 1.543 + nsIContent* capturedContent = nsIPresShell::GetCapturingContent(); 1.544 + if (capturedContent != content) 1.545 + { 1.546 + return NS_OK; 1.547 + } 1.548 + 1.549 + // Find the frame under the mouse cursor with the root frame. 1.550 + // At this time, don't use the anchor's frame because it may not have 1.551 + // fixed positioned frames. 1.552 + nsIFrame* rootFrame = mShell->FrameManager()->GetRootFrame(); 1.553 + nsPoint ptInRoot = aPoint + aFrame->GetOffsetTo(rootFrame); 1.554 + nsIFrame* cursorFrame = 1.555 + nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot); 1.556 + 1.557 + // If the mouse cursor in on a frame which is descendant of same 1.558 + // selection root, we can expand the selection to the frame. 1.559 + if (cursorFrame && cursorFrame->PresContext()->PresShell() == mShell) 1.560 + { 1.561 + nsIContent* cursorContent = cursorFrame->GetContent(); 1.562 + NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE); 1.563 + nsIContent* cursorContentRoot = 1.564 + cursorContent->GetSelectionRootContent(mShell); 1.565 + NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED); 1.566 + if (cursorContentRoot == anchorRoot) 1.567 + { 1.568 + *aRetFrame = cursorFrame; 1.569 + aRetPoint = aPoint + aFrame->GetOffsetTo(cursorFrame); 1.570 + return NS_OK; 1.571 + } 1.572 + } 1.573 + // Otherwise, e.g., the cursor isn't on any frames (e.g., the mouse 1.574 + // cursor is out of the window), we should use the frame of the anchor 1.575 + // root. 1.576 + } 1.577 + } 1.578 + 1.579 + // 1.580 + // When we can't find a frame which is under the mouse cursor and has a same 1.581 + // selection root as the anchor node's, we should return the selection root 1.582 + // frame. 1.583 + // 1.584 + 1.585 + *aRetFrame = anchorRoot->GetPrimaryFrame(); 1.586 + 1.587 + if (!*aRetFrame) 1.588 + return NS_ERROR_FAILURE; 1.589 + 1.590 + // 1.591 + // Now make sure that aRetPoint is converted to the same coordinate 1.592 + // system used by aRetFrame. 1.593 + // 1.594 + 1.595 + aRetPoint = aPoint + aFrame->GetOffsetTo(*aRetFrame); 1.596 + 1.597 + return NS_OK; 1.598 +} 1.599 + 1.600 +void 1.601 +nsFrameSelection::SetCaretBidiLevel(uint8_t aLevel) 1.602 +{ 1.603 + // If the current level is undefined, we have just inserted new text. 1.604 + // In this case, we don't want to reset the keyboard language 1.605 + mCaretBidiLevel = aLevel; 1.606 + return; 1.607 +} 1.608 + 1.609 +uint8_t 1.610 +nsFrameSelection::GetCaretBidiLevel() const 1.611 +{ 1.612 + return mCaretBidiLevel; 1.613 +} 1.614 + 1.615 +void 1.616 +nsFrameSelection::UndefineCaretBidiLevel() 1.617 +{ 1.618 + mCaretBidiLevel |= BIDI_LEVEL_UNDEFINED; 1.619 +} 1.620 + 1.621 +#ifdef PRINT_RANGE 1.622 +void printRange(nsRange *aDomRange) 1.623 +{ 1.624 + if (!aDomRange) 1.625 + { 1.626 + printf("NULL nsIDOMRange\n"); 1.627 + } 1.628 + nsINode* startNode = aDomRange->GetStartParent(); 1.629 + nsINode* endNode = aDomRange->GetEndParent(); 1.630 + int32_t startOffset = aDomRange->StartOffset(); 1.631 + int32_t endOffset = aDomRange->EndOffset(); 1.632 + 1.633 + printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n", 1.634 + (unsigned long)aDomRange, 1.635 + (unsigned long)startNode, (long)startOffset, 1.636 + (unsigned long)endNode, (long)endOffset); 1.637 + 1.638 +} 1.639 +#endif /* PRINT_RANGE */ 1.640 + 1.641 +static 1.642 +nsIAtom *GetTag(nsINode *aNode) 1.643 +{ 1.644 + nsCOMPtr<nsIContent> content = do_QueryInterface(aNode); 1.645 + if (!content) 1.646 + { 1.647 + NS_NOTREACHED("bad node passed to GetTag()"); 1.648 + return nullptr; 1.649 + } 1.650 + 1.651 + return content->Tag(); 1.652 +} 1.653 + 1.654 +// Returns the parent 1.655 +nsINode* 1.656 +ParentOffset(nsINode *aNode, int32_t *aChildOffset) 1.657 +{ 1.658 + if (!aNode || !aChildOffset) 1.659 + return nullptr; 1.660 + 1.661 + nsIContent* parent = aNode->GetParent(); 1.662 + if (parent) 1.663 + { 1.664 + *aChildOffset = parent->IndexOf(aNode); 1.665 + 1.666 + return parent; 1.667 + } 1.668 + 1.669 + return nullptr; 1.670 +} 1.671 + 1.672 +static nsINode* 1.673 +GetCellParent(nsINode *aDomNode) 1.674 +{ 1.675 + if (!aDomNode) 1.676 + return nullptr; 1.677 + nsINode* current = aDomNode; 1.678 + // Start with current node and look for a table cell 1.679 + while (current) 1.680 + { 1.681 + nsIAtom* tag = GetTag(current); 1.682 + if (tag == nsGkAtoms::td || tag == nsGkAtoms::th) 1.683 + return current; 1.684 + current = current->GetParent(); 1.685 + } 1.686 + return nullptr; 1.687 +} 1.688 + 1.689 + 1.690 +void 1.691 +nsFrameSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter) 1.692 +{ 1.693 + mShell = aShell; 1.694 + mMouseDownState = false; 1.695 + mDesiredXSet = false; 1.696 + mLimiter = aLimiter; 1.697 + mCaretMovementStyle = 1.698 + Preferences::GetInt("bidi.edit.caret_movement_style", 2); 1.699 +} 1.700 + 1.701 +nsresult 1.702 +nsFrameSelection::MoveCaret(uint32_t aKeycode, 1.703 + bool aContinueSelection, 1.704 + nsSelectionAmount aAmount) 1.705 +{ 1.706 + bool visualMovement = 1.707 + (aKeycode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE || 1.708 + aKeycode == nsIDOMKeyEvent::DOM_VK_DELETE || 1.709 + aKeycode == nsIDOMKeyEvent::DOM_VK_HOME || 1.710 + aKeycode == nsIDOMKeyEvent::DOM_VK_END) ? 1.711 + false : // Delete operations and home/end are always logical 1.712 + mCaretMovementStyle == 1 || 1.713 + (mCaretMovementStyle == 2 && !aContinueSelection); 1.714 + 1.715 + return MoveCaret(aKeycode, aContinueSelection, aAmount, visualMovement); 1.716 +} 1.717 + 1.718 +nsresult 1.719 +nsFrameSelection::MoveCaret(uint32_t aKeycode, 1.720 + bool aContinueSelection, 1.721 + nsSelectionAmount aAmount, 1.722 + bool aVisualMovement) 1.723 +{ 1.724 + NS_ENSURE_STATE(mShell); 1.725 + // Flush out layout, since we need it to be up to date to do caret 1.726 + // positioning. 1.727 + mShell->FlushPendingNotifications(Flush_Layout); 1.728 + 1.729 + if (!mShell) { 1.730 + return NS_OK; 1.731 + } 1.732 + 1.733 + nsPresContext *context = mShell->GetPresContext(); 1.734 + if (!context) 1.735 + return NS_ERROR_FAILURE; 1.736 + 1.737 + bool isCollapsed; 1.738 + nscoord desiredX = 0; //we must keep this around and revalidate it when its just UP/DOWN 1.739 + 1.740 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.741 + nsRefPtr<Selection> sel = mDomSelections[index]; 1.742 + if (!sel) 1.743 + return NS_ERROR_NULL_POINTER; 1.744 + 1.745 + int32_t scrollFlags = 0; 1.746 + nsINode* focusNode = sel->GetFocusNode(); 1.747 + if (focusNode && 1.748 + (focusNode->IsEditable() || 1.749 + (focusNode->IsElement() && 1.750 + focusNode->AsElement()->State(). 1.751 + HasState(NS_EVENT_STATE_MOZ_READWRITE)))) { 1.752 + // If caret moves in editor, it should cause scrolling even if it's in 1.753 + // overflow: hidden;. 1.754 + scrollFlags |= Selection::SCROLL_OVERFLOW_HIDDEN; 1.755 + } 1.756 + 1.757 + nsresult result = sel->GetIsCollapsed(&isCollapsed); 1.758 + if (NS_FAILED(result)) 1.759 + return result; 1.760 + if (aKeycode == nsIDOMKeyEvent::DOM_VK_UP || 1.761 + aKeycode == nsIDOMKeyEvent::DOM_VK_DOWN) 1.762 + { 1.763 + result = FetchDesiredX(desiredX); 1.764 + if (NS_FAILED(result)) 1.765 + return result; 1.766 + SetDesiredX(desiredX); 1.767 + } 1.768 + 1.769 + int32_t caretStyle = Preferences::GetInt("layout.selection.caret_style", 0); 1.770 + if (caretStyle == 0 1.771 +#ifdef XP_WIN 1.772 + && aKeycode != nsIDOMKeyEvent::DOM_VK_UP 1.773 + && aKeycode != nsIDOMKeyEvent::DOM_VK_DOWN 1.774 +#endif 1.775 + ) { 1.776 + // Put caret at the selection edge in the |aKeycode| direction. 1.777 + caretStyle = 2; 1.778 + } 1.779 + 1.780 + if (!isCollapsed && !aContinueSelection && caretStyle == 2) { 1.781 + switch (aKeycode){ 1.782 + case nsIDOMKeyEvent::DOM_VK_LEFT : 1.783 + case nsIDOMKeyEvent::DOM_VK_UP : 1.784 + { 1.785 + const nsRange* anchorFocusRange = sel->GetAnchorFocusRange(); 1.786 + if (anchorFocusRange) { 1.787 + PostReason(nsISelectionListener::COLLAPSETOSTART_REASON); 1.788 + sel->Collapse(anchorFocusRange->GetStartParent(), 1.789 + anchorFocusRange->StartOffset()); 1.790 + } 1.791 + mHint = HINTRIGHT; 1.792 + sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, 1.793 + nsIPresShell::ScrollAxis(), 1.794 + nsIPresShell::ScrollAxis(), scrollFlags); 1.795 + return NS_OK; 1.796 + } 1.797 + 1.798 + case nsIDOMKeyEvent::DOM_VK_RIGHT : 1.799 + case nsIDOMKeyEvent::DOM_VK_DOWN : 1.800 + { 1.801 + const nsRange* anchorFocusRange = sel->GetAnchorFocusRange(); 1.802 + if (anchorFocusRange) { 1.803 + PostReason(nsISelectionListener::COLLAPSETOEND_REASON); 1.804 + sel->Collapse(anchorFocusRange->GetEndParent(), 1.805 + anchorFocusRange->EndOffset()); 1.806 + } 1.807 + mHint = HINTLEFT; 1.808 + sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, 1.809 + nsIPresShell::ScrollAxis(), 1.810 + nsIPresShell::ScrollAxis(), scrollFlags); 1.811 + return NS_OK; 1.812 + } 1.813 + } 1.814 + } 1.815 + 1.816 + nsIFrame *frame; 1.817 + int32_t offsetused = 0; 1.818 + result = sel->GetPrimaryFrameForFocusNode(&frame, &offsetused, 1.819 + aVisualMovement); 1.820 + 1.821 + if (NS_FAILED(result) || !frame) 1.822 + return NS_FAILED(result) ? result : NS_ERROR_FAILURE; 1.823 + 1.824 + //set data using mLimiter to stop on scroll views. If we have a limiter then we stop peeking 1.825 + //when we hit scrollable views. If no limiter then just let it go ahead 1.826 + nsPeekOffsetStruct pos(aAmount, eDirPrevious, offsetused, desiredX, 1.827 + true, mLimiter != nullptr, true, aVisualMovement); 1.828 + 1.829 + nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(frame); 1.830 + 1.831 + HINT tHint(mHint); //temporary variable so we dont set mHint until it is necessary 1.832 + switch (aKeycode){ 1.833 + case nsIDOMKeyEvent::DOM_VK_RIGHT : 1.834 + InvalidateDesiredX(); 1.835 + pos.mDirection = (baseLevel & 1) ? eDirPrevious : eDirNext; 1.836 + break; 1.837 + case nsIDOMKeyEvent::DOM_VK_LEFT : 1.838 + InvalidateDesiredX(); 1.839 + pos.mDirection = (baseLevel & 1) ? eDirNext : eDirPrevious; 1.840 + break; 1.841 + case nsIDOMKeyEvent::DOM_VK_DELETE : 1.842 + InvalidateDesiredX(); 1.843 + pos.mDirection = eDirNext; 1.844 + break; 1.845 + case nsIDOMKeyEvent::DOM_VK_BACK_SPACE : 1.846 + InvalidateDesiredX(); 1.847 + pos.mDirection = eDirPrevious; 1.848 + break; 1.849 + case nsIDOMKeyEvent::DOM_VK_DOWN : 1.850 + pos.mAmount = eSelectLine; 1.851 + pos.mDirection = eDirNext; 1.852 + break; 1.853 + case nsIDOMKeyEvent::DOM_VK_UP : 1.854 + pos.mAmount = eSelectLine; 1.855 + pos.mDirection = eDirPrevious; 1.856 + break; 1.857 + case nsIDOMKeyEvent::DOM_VK_HOME : 1.858 + InvalidateDesiredX(); 1.859 + pos.mAmount = eSelectBeginLine; 1.860 + break; 1.861 + case nsIDOMKeyEvent::DOM_VK_END : 1.862 + InvalidateDesiredX(); 1.863 + pos.mAmount = eSelectEndLine; 1.864 + break; 1.865 + default :return NS_ERROR_FAILURE; 1.866 + } 1.867 + PostReason(nsISelectionListener::KEYPRESS_REASON); 1.868 + if (NS_SUCCEEDED(result = frame->PeekOffset(&pos)) && pos.mResultContent) 1.869 + { 1.870 + nsIFrame *theFrame; 1.871 + int32_t currentOffset, frameStart, frameEnd; 1.872 + 1.873 + if (aAmount >= eSelectCharacter && aAmount <= eSelectWord) 1.874 + { 1.875 + // For left/right, PeekOffset() sets pos.mResultFrame correctly, but does not set pos.mAttachForward, 1.876 + // so determine the hint here based on the result frame and offset: 1.877 + // If we're at the end of a text frame, set the hint to HINTLEFT to indicate that we 1.878 + // want the caret displayed at the end of this frame, not at the beginning of the next one. 1.879 + theFrame = pos.mResultFrame; 1.880 + theFrame->GetOffsets(frameStart, frameEnd); 1.881 + currentOffset = pos.mContentOffset; 1.882 + if (frameEnd == currentOffset && !(frameStart == 0 && frameEnd == 0)) 1.883 + tHint = HINTLEFT; 1.884 + else 1.885 + tHint = HINTRIGHT; 1.886 + } else { 1.887 + // For up/down and home/end, pos.mResultFrame might not be set correctly, or not at all. 1.888 + // In these cases, get the frame based on the content and hint returned by PeekOffset(). 1.889 + tHint = (HINT)pos.mAttachForward; 1.890 + theFrame = GetFrameForNodeOffset(pos.mResultContent, pos.mContentOffset, 1.891 + tHint, ¤tOffset); 1.892 + if (!theFrame) 1.893 + return NS_ERROR_FAILURE; 1.894 + 1.895 + theFrame->GetOffsets(frameStart, frameEnd); 1.896 + } 1.897 + 1.898 + if (context->BidiEnabled()) 1.899 + { 1.900 + switch (aKeycode) { 1.901 + case nsIDOMKeyEvent::DOM_VK_HOME: 1.902 + case nsIDOMKeyEvent::DOM_VK_END: 1.903 + // set the caret Bidi level to the paragraph embedding level 1.904 + SetCaretBidiLevel(NS_GET_BASE_LEVEL(theFrame)); 1.905 + break; 1.906 + 1.907 + default: 1.908 + // If the current position is not a frame boundary, it's enough just to take the Bidi level of the current frame 1.909 + if ((pos.mContentOffset != frameStart && pos.mContentOffset != frameEnd) 1.910 + || (eSelectLine == aAmount)) 1.911 + { 1.912 + SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(theFrame)); 1.913 + } 1.914 + else 1.915 + BidiLevelFromMove(mShell, pos.mResultContent, pos.mContentOffset, aKeycode, tHint); 1.916 + } 1.917 + } 1.918 + result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, 1.919 + tHint, aContinueSelection, false); 1.920 + } else if (aKeycode == nsIDOMKeyEvent::DOM_VK_RIGHT && !aContinueSelection) { 1.921 + // Collapse selection if PeekOffset failed, we either 1.922 + // 1. bumped into the BRFrame, bug 207623 1.923 + // 2. had select-all in a text input (DIV range), bug 352759. 1.924 + bool isBRFrame = frame->GetType() == nsGkAtoms::brFrame; 1.925 + sel->Collapse(sel->GetFocusNode(), sel->FocusOffset()); 1.926 + // Note: 'frame' might be dead here. 1.927 + if (!isBRFrame) { 1.928 + mHint = HINTLEFT; // We're now at the end of the frame to the left. 1.929 + } 1.930 + result = NS_OK; 1.931 + } 1.932 + if (NS_SUCCEEDED(result)) 1.933 + { 1.934 + result = mDomSelections[index]-> 1.935 + ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION, 1.936 + nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis(), 1.937 + scrollFlags); 1.938 + } 1.939 + 1.940 + return result; 1.941 +} 1.942 + 1.943 +//END nsFrameSelection methods 1.944 + 1.945 + 1.946 +//BEGIN nsFrameSelection methods 1.947 + 1.948 +NS_IMETHODIMP 1.949 +Selection::ToString(nsAString& aReturn) 1.950 +{ 1.951 + // We need Flush_Style here to make sure frames have been created for 1.952 + // the selected content. Use mFrameSelection->GetShell() which returns 1.953 + // null if the Selection has been disconnected (the shell is Destroyed). 1.954 + nsCOMPtr<nsIPresShell> shell = 1.955 + mFrameSelection ? mFrameSelection->GetShell() : nullptr; 1.956 + if (!shell) { 1.957 + aReturn.Truncate(); 1.958 + return NS_OK; 1.959 + } 1.960 + shell->FlushPendingNotifications(Flush_Style); 1.961 + 1.962 + return ToStringWithFormat("text/plain", 1.963 + nsIDocumentEncoder::SkipInvisibleContent, 1.964 + 0, aReturn); 1.965 +} 1.966 + 1.967 +void 1.968 +Selection::Stringify(nsAString& aResult) 1.969 +{ 1.970 + // Eat the error code 1.971 + ToString(aResult); 1.972 +} 1.973 + 1.974 +NS_IMETHODIMP 1.975 +Selection::ToStringWithFormat(const char* aFormatType, uint32_t aFlags, 1.976 + int32_t aWrapCol, nsAString& aReturn) 1.977 +{ 1.978 + ErrorResult result; 1.979 + NS_ConvertUTF8toUTF16 format(aFormatType); 1.980 + ToStringWithFormat(format, aFlags, aWrapCol, aReturn, result); 1.981 + if (result.Failed()) { 1.982 + return result.ErrorCode(); 1.983 + } 1.984 + return NS_OK; 1.985 +} 1.986 + 1.987 +void 1.988 +Selection::ToStringWithFormat(const nsAString& aFormatType, uint32_t aFlags, 1.989 + int32_t aWrapCol, nsAString& aReturn, 1.990 + ErrorResult& aRv) 1.991 +{ 1.992 + nsresult rv = NS_OK; 1.993 + NS_ConvertUTF8toUTF16 formatType( NS_DOC_ENCODER_CONTRACTID_BASE ); 1.994 + formatType.Append(aFormatType); 1.995 + nsCOMPtr<nsIDocumentEncoder> encoder = 1.996 + do_CreateInstance(NS_ConvertUTF16toUTF8(formatType).get(), &rv); 1.997 + if (NS_FAILED(rv)) { 1.998 + aRv.Throw(rv); 1.999 + return; 1.1000 + } 1.1001 + 1.1002 + nsIPresShell* shell = GetPresShell(); 1.1003 + if (!shell) { 1.1004 + aRv.Throw(NS_ERROR_FAILURE); 1.1005 + return; 1.1006 + } 1.1007 + 1.1008 + nsIDocument *doc = shell->GetDocument(); 1.1009 + 1.1010 + nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc); 1.1011 + NS_ASSERTION(domDoc, "Need a document"); 1.1012 + 1.1013 + // Flags should always include OutputSelectionOnly if we're coming from here: 1.1014 + aFlags |= nsIDocumentEncoder::OutputSelectionOnly; 1.1015 + nsAutoString readstring; 1.1016 + readstring.Assign(aFormatType); 1.1017 + rv = encoder->Init(domDoc, readstring, aFlags); 1.1018 + if (NS_FAILED(rv)) { 1.1019 + aRv.Throw(rv); 1.1020 + return; 1.1021 + } 1.1022 + 1.1023 + encoder->SetSelection(this); 1.1024 + if (aWrapCol != 0) 1.1025 + encoder->SetWrapColumn(aWrapCol); 1.1026 + 1.1027 + rv = encoder->EncodeToString(aReturn); 1.1028 + if (NS_FAILED(rv)) { 1.1029 + aRv.Throw(rv); 1.1030 + } 1.1031 +} 1.1032 + 1.1033 +NS_IMETHODIMP 1.1034 +Selection::SetInterlinePosition(bool aHintRight) 1.1035 +{ 1.1036 + ErrorResult result; 1.1037 + SetInterlinePosition(aHintRight, result); 1.1038 + if (result.Failed()) { 1.1039 + return result.ErrorCode(); 1.1040 + } 1.1041 + return NS_OK; 1.1042 +} 1.1043 + 1.1044 +void 1.1045 +Selection::SetInterlinePosition(bool aHintRight, ErrorResult& aRv) 1.1046 +{ 1.1047 + if (!mFrameSelection) { 1.1048 + aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection 1.1049 + return; 1.1050 + } 1.1051 + nsFrameSelection::HINT hint; 1.1052 + if (aHintRight) 1.1053 + hint = nsFrameSelection::HINTRIGHT; 1.1054 + else 1.1055 + hint = nsFrameSelection::HINTLEFT; 1.1056 + mFrameSelection->SetHint(hint); 1.1057 +} 1.1058 + 1.1059 +NS_IMETHODIMP 1.1060 +Selection::GetInterlinePosition(bool* aHintRight) 1.1061 +{ 1.1062 + ErrorResult result; 1.1063 + *aHintRight = GetInterlinePosition(result); 1.1064 + if (result.Failed()) { 1.1065 + return result.ErrorCode(); 1.1066 + } 1.1067 + return NS_OK; 1.1068 +} 1.1069 + 1.1070 +bool 1.1071 +Selection::GetInterlinePosition(ErrorResult& aRv) 1.1072 +{ 1.1073 + if (!mFrameSelection) { 1.1074 + aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection 1.1075 + return false; 1.1076 + } 1.1077 + return (mFrameSelection->GetHint() == nsFrameSelection::HINTRIGHT); 1.1078 +} 1.1079 + 1.1080 +nsPrevNextBidiLevels 1.1081 +nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode, 1.1082 + uint32_t aContentOffset, 1.1083 + bool aJumpLines) const 1.1084 +{ 1.1085 + return GetPrevNextBidiLevels(aNode, aContentOffset, mHint, aJumpLines); 1.1086 +} 1.1087 + 1.1088 +nsPrevNextBidiLevels 1.1089 +nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode, 1.1090 + uint32_t aContentOffset, 1.1091 + HINT aHint, 1.1092 + bool aJumpLines) const 1.1093 +{ 1.1094 + // Get the level of the frames on each side 1.1095 + nsIFrame *currentFrame; 1.1096 + int32_t currentOffset; 1.1097 + int32_t frameStart, frameEnd; 1.1098 + nsDirection direction; 1.1099 + 1.1100 + nsPrevNextBidiLevels levels; 1.1101 + levels.SetData(nullptr, nullptr, 0, 0); 1.1102 + 1.1103 + currentFrame = GetFrameForNodeOffset(aNode, aContentOffset, 1.1104 + aHint, ¤tOffset); 1.1105 + if (!currentFrame) 1.1106 + return levels; 1.1107 + 1.1108 + currentFrame->GetOffsets(frameStart, frameEnd); 1.1109 + 1.1110 + if (0 == frameStart && 0 == frameEnd) 1.1111 + direction = eDirPrevious; 1.1112 + else if (frameStart == currentOffset) 1.1113 + direction = eDirPrevious; 1.1114 + else if (frameEnd == currentOffset) 1.1115 + direction = eDirNext; 1.1116 + else { 1.1117 + // we are neither at the beginning nor at the end of the frame, so we have no worries 1.1118 + levels.SetData(currentFrame, currentFrame, 1.1119 + NS_GET_EMBEDDING_LEVEL(currentFrame), 1.1120 + NS_GET_EMBEDDING_LEVEL(currentFrame)); 1.1121 + return levels; 1.1122 + } 1.1123 + 1.1124 + nsIFrame *newFrame; 1.1125 + int32_t offset; 1.1126 + bool jumpedLine; 1.1127 + nsresult rv = currentFrame->GetFrameFromDirection(direction, false, 1.1128 + aJumpLines, true, 1.1129 + &newFrame, &offset, &jumpedLine); 1.1130 + if (NS_FAILED(rv)) 1.1131 + newFrame = nullptr; 1.1132 + 1.1133 + uint8_t baseLevel = NS_GET_BASE_LEVEL(currentFrame); 1.1134 + uint8_t currentLevel = NS_GET_EMBEDDING_LEVEL(currentFrame); 1.1135 + uint8_t newLevel = newFrame ? NS_GET_EMBEDDING_LEVEL(newFrame) : baseLevel; 1.1136 + 1.1137 + // If not jumping lines, disregard br frames, since they might be positioned incorrectly. 1.1138 + // XXX This could be removed once bug 339786 is fixed. 1.1139 + if (!aJumpLines) { 1.1140 + if (currentFrame->GetType() == nsGkAtoms::brFrame) { 1.1141 + currentFrame = nullptr; 1.1142 + currentLevel = baseLevel; 1.1143 + } 1.1144 + if (newFrame && newFrame->GetType() == nsGkAtoms::brFrame) { 1.1145 + newFrame = nullptr; 1.1146 + newLevel = baseLevel; 1.1147 + } 1.1148 + } 1.1149 + 1.1150 + if (direction == eDirNext) 1.1151 + levels.SetData(currentFrame, newFrame, currentLevel, newLevel); 1.1152 + else 1.1153 + levels.SetData(newFrame, currentFrame, newLevel, currentLevel); 1.1154 + 1.1155 + return levels; 1.1156 +} 1.1157 + 1.1158 +nsresult 1.1159 +nsFrameSelection::GetFrameFromLevel(nsIFrame *aFrameIn, 1.1160 + nsDirection aDirection, 1.1161 + uint8_t aBidiLevel, 1.1162 + nsIFrame **aFrameOut) const 1.1163 +{ 1.1164 + NS_ENSURE_STATE(mShell); 1.1165 + uint8_t foundLevel = 0; 1.1166 + nsIFrame *foundFrame = aFrameIn; 1.1167 + 1.1168 + nsCOMPtr<nsIFrameEnumerator> frameTraversal; 1.1169 + nsresult result; 1.1170 + nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result)); 1.1171 + if (NS_FAILED(result)) 1.1172 + return result; 1.1173 + 1.1174 + result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal), 1.1175 + mShell->GetPresContext(), aFrameIn, 1.1176 + eLeaf, 1.1177 + false, // aVisual 1.1178 + false, // aLockInScrollView 1.1179 + false // aFollowOOFs 1.1180 + ); 1.1181 + if (NS_FAILED(result)) 1.1182 + return result; 1.1183 + 1.1184 + do { 1.1185 + *aFrameOut = foundFrame; 1.1186 + if (aDirection == eDirNext) 1.1187 + frameTraversal->Next(); 1.1188 + else 1.1189 + frameTraversal->Prev(); 1.1190 + 1.1191 + foundFrame = frameTraversal->CurrentItem(); 1.1192 + if (!foundFrame) 1.1193 + return NS_ERROR_FAILURE; 1.1194 + foundLevel = NS_GET_EMBEDDING_LEVEL(foundFrame); 1.1195 + 1.1196 + } while (foundLevel > aBidiLevel); 1.1197 + 1.1198 + return NS_OK; 1.1199 +} 1.1200 + 1.1201 + 1.1202 +nsresult 1.1203 +nsFrameSelection::MaintainSelection(nsSelectionAmount aAmount) 1.1204 +{ 1.1205 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.1206 + if (!mDomSelections[index]) 1.1207 + return NS_ERROR_NULL_POINTER; 1.1208 + 1.1209 + mMaintainedAmount = aAmount; 1.1210 + 1.1211 + const nsRange* anchorFocusRange = 1.1212 + mDomSelections[index]->GetAnchorFocusRange(); 1.1213 + if (anchorFocusRange) { 1.1214 + mMaintainRange = anchorFocusRange->CloneRange(); 1.1215 + return NS_OK; 1.1216 + } 1.1217 + 1.1218 + mMaintainRange = nullptr; 1.1219 + return NS_OK; 1.1220 +} 1.1221 + 1.1222 + 1.1223 +/** After moving the caret, its Bidi level is set according to the following rules: 1.1224 + * 1.1225 + * After moving over a character with left/right arrow, set to the Bidi level of the last moved over character. 1.1226 + * After Home and End, set to the paragraph embedding level. 1.1227 + * After up/down arrow, PageUp/Down, set to the lower level of the 2 surrounding characters. 1.1228 + * After mouse click, set to the level of the current frame. 1.1229 + * 1.1230 + * The following two methods use GetPrevNextBidiLevels to determine the new Bidi level. 1.1231 + * BidiLevelFromMove is called when the caret is moved in response to a keyboard event 1.1232 + * 1.1233 + * @param aPresShell is the presentation shell 1.1234 + * @param aNode is the content node 1.1235 + * @param aContentOffset is the new caret position, as an offset into aNode 1.1236 + * @param aKeycode is the keyboard event that moved the caret to the new position 1.1237 + * @param aHint is the hint indicating in what logical direction the caret moved 1.1238 + */ 1.1239 +void nsFrameSelection::BidiLevelFromMove(nsIPresShell* aPresShell, 1.1240 + nsIContent *aNode, 1.1241 + uint32_t aContentOffset, 1.1242 + uint32_t aKeycode, 1.1243 + HINT aHint) 1.1244 +{ 1.1245 + switch (aKeycode) { 1.1246 + 1.1247 + // Right and Left: the new cursor Bidi level is the level of the character moved over 1.1248 + case nsIDOMKeyEvent::DOM_VK_RIGHT: 1.1249 + case nsIDOMKeyEvent::DOM_VK_LEFT: 1.1250 + { 1.1251 + nsPrevNextBidiLevels levels = GetPrevNextBidiLevels(aNode, aContentOffset, 1.1252 + aHint, false); 1.1253 + 1.1254 + if (HINTLEFT == aHint) 1.1255 + SetCaretBidiLevel(levels.mLevelBefore); 1.1256 + else 1.1257 + SetCaretBidiLevel(levels.mLevelAfter); 1.1258 + break; 1.1259 + } 1.1260 + /* 1.1261 + // Up and Down: the new cursor Bidi level is the smaller of the two surrounding characters 1.1262 + case nsIDOMKeyEvent::DOM_VK_UP: 1.1263 + case nsIDOMKeyEvent::DOM_VK_DOWN: 1.1264 + GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel); 1.1265 + aPresShell->SetCaretBidiLevel(std::min(firstLevel, secondLevel)); 1.1266 + break; 1.1267 + */ 1.1268 + 1.1269 + default: 1.1270 + UndefineCaretBidiLevel(); 1.1271 + } 1.1272 +} 1.1273 + 1.1274 +/** 1.1275 + * BidiLevelFromClick is called when the caret is repositioned by clicking the mouse 1.1276 + * 1.1277 + * @param aNode is the content node 1.1278 + * @param aContentOffset is the new caret position, as an offset into aNode 1.1279 + */ 1.1280 +void nsFrameSelection::BidiLevelFromClick(nsIContent *aNode, 1.1281 + uint32_t aContentOffset) 1.1282 +{ 1.1283 + nsIFrame* clickInFrame=nullptr; 1.1284 + int32_t OffsetNotUsed; 1.1285 + 1.1286 + clickInFrame = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &OffsetNotUsed); 1.1287 + if (!clickInFrame) 1.1288 + return; 1.1289 + 1.1290 + SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(clickInFrame)); 1.1291 +} 1.1292 + 1.1293 + 1.1294 +bool 1.1295 +nsFrameSelection::AdjustForMaintainedSelection(nsIContent *aContent, 1.1296 + int32_t aOffset) 1.1297 +{ 1.1298 + if (!mMaintainRange) 1.1299 + return false; 1.1300 + 1.1301 + if (!aContent) { 1.1302 + return false; 1.1303 + } 1.1304 + 1.1305 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.1306 + if (!mDomSelections[index]) 1.1307 + return false; 1.1308 + 1.1309 + nsINode* rangeStartNode = mMaintainRange->GetStartParent(); 1.1310 + nsINode* rangeEndNode = mMaintainRange->GetEndParent(); 1.1311 + int32_t rangeStartOffset = mMaintainRange->StartOffset(); 1.1312 + int32_t rangeEndOffset = mMaintainRange->EndOffset(); 1.1313 + 1.1314 + int32_t relToStart = 1.1315 + nsContentUtils::ComparePoints(rangeStartNode, rangeStartOffset, 1.1316 + aContent, aOffset); 1.1317 + int32_t relToEnd = 1.1318 + nsContentUtils::ComparePoints(rangeEndNode, rangeEndOffset, 1.1319 + aContent, aOffset); 1.1320 + 1.1321 + // If aContent/aOffset is inside the maintained selection, or if it is on the 1.1322 + // "anchor" side of the maintained selection, we need to do something. 1.1323 + if ((relToStart < 0 && relToEnd > 0) || 1.1324 + (relToStart > 0 && 1.1325 + mDomSelections[index]->GetDirection() == eDirNext) || 1.1326 + (relToEnd < 0 && 1.1327 + mDomSelections[index]->GetDirection() == eDirPrevious)) { 1.1328 + // Set the current range to the maintained range. 1.1329 + mDomSelections[index]->ReplaceAnchorFocusRange(mMaintainRange); 1.1330 + if (relToStart < 0 && relToEnd > 0) { 1.1331 + // We're inside the maintained selection, just keep it selected. 1.1332 + return true; 1.1333 + } 1.1334 + // Reverse the direction of the selection so that the anchor will be on the 1.1335 + // far side of the maintained selection, relative to aContent/aOffset. 1.1336 + mDomSelections[index]->SetDirection(relToStart > 0 ? eDirPrevious : eDirNext); 1.1337 + } 1.1338 + return false; 1.1339 +} 1.1340 + 1.1341 + 1.1342 +nsresult 1.1343 +nsFrameSelection::HandleClick(nsIContent *aNewFocus, 1.1344 + uint32_t aContentOffset, 1.1345 + uint32_t aContentEndOffset, 1.1346 + bool aContinueSelection, 1.1347 + bool aMultipleSelection, 1.1348 + bool aHint) 1.1349 +{ 1.1350 + if (!aNewFocus) 1.1351 + return NS_ERROR_INVALID_ARG; 1.1352 + 1.1353 + InvalidateDesiredX(); 1.1354 + 1.1355 + if (!aContinueSelection) { 1.1356 + mMaintainRange = nullptr; 1.1357 + if (!IsValidSelectionPoint(this, aNewFocus)) { 1.1358 + mAncestorLimiter = nullptr; 1.1359 + } 1.1360 + } 1.1361 + 1.1362 + // Don't take focus when dragging off of a table 1.1363 + if (!mDragSelectingCells) 1.1364 + { 1.1365 + BidiLevelFromClick(aNewFocus, aContentOffset); 1.1366 + PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON); 1.1367 + if (aContinueSelection && 1.1368 + AdjustForMaintainedSelection(aNewFocus, aContentOffset)) 1.1369 + return NS_OK; //shift clicked to maintained selection. rejected. 1.1370 + 1.1371 + return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, HINT(aHint), 1.1372 + aContinueSelection, aMultipleSelection); 1.1373 + } 1.1374 + 1.1375 + return NS_OK; 1.1376 +} 1.1377 + 1.1378 +void 1.1379 +nsFrameSelection::HandleDrag(nsIFrame *aFrame, nsPoint aPoint) 1.1380 +{ 1.1381 + if (!aFrame || !mShell) 1.1382 + return; 1.1383 + 1.1384 + nsresult result; 1.1385 + nsIFrame *newFrame = 0; 1.1386 + nsPoint newPoint; 1.1387 + 1.1388 + result = ConstrainFrameAndPointToAnchorSubtree(aFrame, aPoint, &newFrame, newPoint); 1.1389 + if (NS_FAILED(result)) 1.1390 + return; 1.1391 + if (!newFrame) 1.1392 + return; 1.1393 + 1.1394 + nsIFrame::ContentOffsets offsets = 1.1395 + newFrame->GetContentOffsetsFromPoint(newPoint); 1.1396 + if (!offsets.content) 1.1397 + return; 1.1398 + 1.1399 + if (newFrame->IsSelected() && 1.1400 + AdjustForMaintainedSelection(offsets.content, offsets.offset)) 1.1401 + return; 1.1402 + 1.1403 + // Adjust offsets according to maintained amount 1.1404 + if (mMaintainRange && 1.1405 + mMaintainedAmount != eSelectNoAmount) { 1.1406 + 1.1407 + nsINode* rangenode = mMaintainRange->GetStartParent(); 1.1408 + int32_t rangeOffset = mMaintainRange->StartOffset(); 1.1409 + int32_t relativePosition = 1.1410 + nsContentUtils::ComparePoints(rangenode, rangeOffset, 1.1411 + offsets.content, offsets.offset); 1.1412 + 1.1413 + nsDirection direction = relativePosition > 0 ? eDirPrevious : eDirNext; 1.1414 + nsSelectionAmount amount = mMaintainedAmount; 1.1415 + if (amount == eSelectBeginLine && direction == eDirNext) 1.1416 + amount = eSelectEndLine; 1.1417 + 1.1418 + int32_t offset; 1.1419 + nsIFrame* frame = GetFrameForNodeOffset(offsets.content, offsets.offset, HINTRIGHT, &offset); 1.1420 + 1.1421 + if (frame && amount == eSelectWord && direction == eDirPrevious) { 1.1422 + // To avoid selecting the previous word when at start of word, 1.1423 + // first move one character forward. 1.1424 + nsPeekOffsetStruct charPos(eSelectCharacter, eDirNext, offset, 0, 1.1425 + false, mLimiter != nullptr, false, false); 1.1426 + if (NS_SUCCEEDED(frame->PeekOffset(&charPos))) { 1.1427 + frame = charPos.mResultFrame; 1.1428 + offset = charPos.mContentOffset; 1.1429 + } 1.1430 + } 1.1431 + 1.1432 + nsPeekOffsetStruct pos(amount, direction, offset, 0, 1.1433 + false, mLimiter != nullptr, false, false); 1.1434 + 1.1435 + if (frame && NS_SUCCEEDED(frame->PeekOffset(&pos)) && pos.mResultContent) { 1.1436 + offsets.content = pos.mResultContent; 1.1437 + offsets.offset = pos.mContentOffset; 1.1438 + } 1.1439 + } 1.1440 + 1.1441 + HandleClick(offsets.content, offsets.offset, offsets.offset, 1.1442 + true, false, offsets.associateWithNext); 1.1443 +} 1.1444 + 1.1445 +nsresult 1.1446 +nsFrameSelection::StartAutoScrollTimer(nsIFrame *aFrame, 1.1447 + nsPoint aPoint, 1.1448 + uint32_t aDelay) 1.1449 +{ 1.1450 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.1451 + if (!mDomSelections[index]) 1.1452 + return NS_ERROR_NULL_POINTER; 1.1453 + 1.1454 + return mDomSelections[index]->StartAutoScrollTimer(aFrame, aPoint, aDelay); 1.1455 +} 1.1456 + 1.1457 +void 1.1458 +nsFrameSelection::StopAutoScrollTimer() 1.1459 +{ 1.1460 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.1461 + if (!mDomSelections[index]) 1.1462 + return; 1.1463 + 1.1464 + mDomSelections[index]->StopAutoScrollTimer(); 1.1465 +} 1.1466 + 1.1467 +/** 1.1468 +hard to go from nodes to frames, easy the other way! 1.1469 + */ 1.1470 +nsresult 1.1471 +nsFrameSelection::TakeFocus(nsIContent *aNewFocus, 1.1472 + uint32_t aContentOffset, 1.1473 + uint32_t aContentEndOffset, 1.1474 + HINT aHint, 1.1475 + bool aContinueSelection, 1.1476 + bool aMultipleSelection) 1.1477 +{ 1.1478 + if (!aNewFocus) 1.1479 + return NS_ERROR_NULL_POINTER; 1.1480 + 1.1481 + NS_ENSURE_STATE(mShell); 1.1482 + 1.1483 + if (!IsValidSelectionPoint(this,aNewFocus)) 1.1484 + return NS_ERROR_FAILURE; 1.1485 + 1.1486 + // Clear all table selection data 1.1487 + mSelectingTableCellMode = 0; 1.1488 + mDragSelectingCells = false; 1.1489 + mStartSelectedCell = nullptr; 1.1490 + mEndSelectedCell = nullptr; 1.1491 + mAppendStartSelectedCell = nullptr; 1.1492 + mHint = aHint; 1.1493 + 1.1494 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.1495 + if (!mDomSelections[index]) 1.1496 + return NS_ERROR_NULL_POINTER; 1.1497 + 1.1498 + //traverse through document and unselect crap here 1.1499 + if (!aContinueSelection) {//single click? setting cursor down 1.1500 + uint32_t batching = mBatching;//hack to use the collapse code. 1.1501 + bool changes = mChangesDuringBatching; 1.1502 + mBatching = 1; 1.1503 + 1.1504 + if (aMultipleSelection) { 1.1505 + // Remove existing collapsed ranges as there's no point in having 1.1506 + // non-anchor/focus collapsed ranges. 1.1507 + mDomSelections[index]->RemoveCollapsedRanges(); 1.1508 + 1.1509 + nsRefPtr<nsRange> newRange = new nsRange(aNewFocus); 1.1510 + 1.1511 + newRange->SetStart(aNewFocus, aContentOffset); 1.1512 + newRange->SetEnd(aNewFocus, aContentOffset); 1.1513 + mDomSelections[index]->AddRange(newRange); 1.1514 + mBatching = batching; 1.1515 + mChangesDuringBatching = changes; 1.1516 + } 1.1517 + else 1.1518 + { 1.1519 + bool oldDesiredXSet = mDesiredXSet; //need to keep old desired X if it was set. 1.1520 + mDomSelections[index]->Collapse(aNewFocus, aContentOffset); 1.1521 + mDesiredXSet = oldDesiredXSet; //now reset desired X back. 1.1522 + mBatching = batching; 1.1523 + mChangesDuringBatching = changes; 1.1524 + } 1.1525 + if (aContentEndOffset != aContentOffset) 1.1526 + mDomSelections[index]->Extend(aNewFocus, aContentEndOffset); 1.1527 + 1.1528 + //find out if we are inside a table. if so, find out which one and which cell 1.1529 + //once we do that, the next time we get a takefocus, check the parent tree. 1.1530 + //if we are no longer inside same table ,cell then switch to table selection mode. 1.1531 + // BUT only do this in an editor 1.1532 + 1.1533 + NS_ENSURE_STATE(mShell); 1.1534 + int16_t displaySelection = mShell->GetSelectionFlags(); 1.1535 + 1.1536 + // Editor has DISPLAY_ALL selection type 1.1537 + if (displaySelection == nsISelectionDisplay::DISPLAY_ALL) 1.1538 + { 1.1539 + mCellParent = GetCellParent(aNewFocus); 1.1540 +#ifdef DEBUG_TABLE_SELECTION 1.1541 + if (mCellParent) 1.1542 + printf(" * TakeFocus - Collapsing into new cell\n"); 1.1543 +#endif 1.1544 + } 1.1545 + } 1.1546 + else { 1.1547 + // Now update the range list: 1.1548 + if (aContinueSelection && aNewFocus) 1.1549 + { 1.1550 + int32_t offset; 1.1551 + nsINode *cellparent = GetCellParent(aNewFocus); 1.1552 + if (mCellParent && cellparent && cellparent != mCellParent) //switch to cell selection mode 1.1553 + { 1.1554 +#ifdef DEBUG_TABLE_SELECTION 1.1555 +printf(" * TakeFocus - moving into new cell\n"); 1.1556 +#endif 1.1557 + WidgetMouseEvent event(false, 0, nullptr, WidgetMouseEvent::eReal); 1.1558 + 1.1559 + // Start selecting in the cell we were in before 1.1560 + nsINode* parent = ParentOffset(mCellParent, &offset); 1.1561 + if (parent) 1.1562 + HandleTableSelection(parent, offset, 1.1563 + nsISelectionPrivate::TABLESELECTION_CELL, &event); 1.1564 + 1.1565 + // Find the parent of this new cell and extend selection to it 1.1566 + parent = ParentOffset(cellparent, &offset); 1.1567 + 1.1568 + // XXXX We need to REALLY get the current key shift state 1.1569 + // (we'd need to add event listener -- let's not bother for now) 1.1570 + event.modifiers &= ~MODIFIER_SHIFT; //aContinueSelection; 1.1571 + if (parent) 1.1572 + { 1.1573 + mCellParent = cellparent; 1.1574 + // Continue selection into next cell 1.1575 + HandleTableSelection(parent, offset, 1.1576 + nsISelectionPrivate::TABLESELECTION_CELL, &event); 1.1577 + } 1.1578 + } 1.1579 + else 1.1580 + { 1.1581 + // XXXX Problem: Shift+click in browser is appending text selection to selected table!!! 1.1582 + // is this the place to erase seleced cells ????? 1.1583 + if (mDomSelections[index]->GetDirection() == eDirNext && aContentEndOffset > aContentOffset) //didn't go far enough 1.1584 + { 1.1585 + mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);//this will only redraw the diff 1.1586 + } 1.1587 + else 1.1588 + mDomSelections[index]->Extend(aNewFocus, aContentOffset); 1.1589 + } 1.1590 + } 1.1591 + } 1.1592 + 1.1593 + // Don't notify selection listeners if batching is on: 1.1594 + if (GetBatching()) 1.1595 + return NS_OK; 1.1596 + return NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL); 1.1597 +} 1.1598 + 1.1599 + 1.1600 +SelectionDetails* 1.1601 +nsFrameSelection::LookUpSelection(nsIContent *aContent, 1.1602 + int32_t aContentOffset, 1.1603 + int32_t aContentLength, 1.1604 + bool aSlowCheck) const 1.1605 +{ 1.1606 + if (!aContent || !mShell) 1.1607 + return nullptr; 1.1608 + 1.1609 + SelectionDetails* details = nullptr; 1.1610 + 1.1611 + for (int32_t j = 0; j < nsISelectionController::NUM_SELECTIONTYPES; j++) { 1.1612 + if (mDomSelections[j]) { 1.1613 + mDomSelections[j]->LookUpSelection(aContent, aContentOffset, 1.1614 + aContentLength, &details, (SelectionType)(1<<j), aSlowCheck); 1.1615 + } 1.1616 + } 1.1617 + 1.1618 + return details; 1.1619 +} 1.1620 + 1.1621 +void 1.1622 +nsFrameSelection::SetMouseDownState(bool aState) 1.1623 +{ 1.1624 + if (mMouseDownState == aState) 1.1625 + return; 1.1626 + 1.1627 + mMouseDownState = aState; 1.1628 + 1.1629 + if (!mMouseDownState) 1.1630 + { 1.1631 + mDragSelectingCells = false; 1.1632 + PostReason(nsISelectionListener::MOUSEUP_REASON); 1.1633 + NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL); //notify that reason is mouse up please. 1.1634 + } 1.1635 +} 1.1636 + 1.1637 +Selection* 1.1638 +nsFrameSelection::GetSelection(SelectionType aType) const 1.1639 +{ 1.1640 + int8_t index = GetIndexFromSelectionType(aType); 1.1641 + if (index < 0) 1.1642 + return nullptr; 1.1643 + 1.1644 + return mDomSelections[index]; 1.1645 +} 1.1646 + 1.1647 +nsresult 1.1648 +nsFrameSelection::ScrollSelectionIntoView(SelectionType aType, 1.1649 + SelectionRegion aRegion, 1.1650 + int16_t aFlags) const 1.1651 +{ 1.1652 + int8_t index = GetIndexFromSelectionType(aType); 1.1653 + if (index < 0) 1.1654 + return NS_ERROR_INVALID_ARG; 1.1655 + 1.1656 + if (!mDomSelections[index]) 1.1657 + return NS_ERROR_NULL_POINTER; 1.1658 + 1.1659 + nsIPresShell::ScrollAxis verticalScroll = nsIPresShell::ScrollAxis(); 1.1660 + int32_t flags = Selection::SCROLL_DO_FLUSH; 1.1661 + if (aFlags & nsISelectionController::SCROLL_SYNCHRONOUS) { 1.1662 + flags |= Selection::SCROLL_SYNCHRONOUS; 1.1663 + } else if (aFlags & nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY) { 1.1664 + flags |= Selection::SCROLL_FIRST_ANCESTOR_ONLY; 1.1665 + } 1.1666 + if (aFlags & nsISelectionController::SCROLL_OVERFLOW_HIDDEN) { 1.1667 + flags |= Selection::SCROLL_OVERFLOW_HIDDEN; 1.1668 + } 1.1669 + if (aFlags & nsISelectionController::SCROLL_CENTER_VERTICALLY) { 1.1670 + verticalScroll = nsIPresShell::ScrollAxis( 1.1671 + nsIPresShell::SCROLL_CENTER, nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE); 1.1672 + } 1.1673 + 1.1674 + // After ScrollSelectionIntoView(), the pending notifications might be 1.1675 + // flushed and PresShell/PresContext/Frames may be dead. See bug 418470. 1.1676 + return mDomSelections[index]->ScrollIntoView(aRegion, 1.1677 + verticalScroll, 1.1678 + nsIPresShell::ScrollAxis(), 1.1679 + flags); 1.1680 +} 1.1681 + 1.1682 +nsresult 1.1683 +nsFrameSelection::RepaintSelection(SelectionType aType) const 1.1684 +{ 1.1685 + int8_t index = GetIndexFromSelectionType(aType); 1.1686 + if (index < 0) 1.1687 + return NS_ERROR_INVALID_ARG; 1.1688 + if (!mDomSelections[index]) 1.1689 + return NS_ERROR_NULL_POINTER; 1.1690 + NS_ENSURE_STATE(mShell); 1.1691 + return mDomSelections[index]->Repaint(mShell->GetPresContext()); 1.1692 +} 1.1693 + 1.1694 +nsIFrame* 1.1695 +nsFrameSelection::GetFrameForNodeOffset(nsIContent *aNode, 1.1696 + int32_t aOffset, 1.1697 + HINT aHint, 1.1698 + int32_t *aReturnOffset) const 1.1699 +{ 1.1700 + if (!aNode || !aReturnOffset || !mShell) 1.1701 + return nullptr; 1.1702 + 1.1703 + if (aOffset < 0) 1.1704 + return nullptr; 1.1705 + 1.1706 + *aReturnOffset = aOffset; 1.1707 + 1.1708 + nsCOMPtr<nsIContent> theNode = aNode; 1.1709 + 1.1710 + if (aNode->IsElement()) 1.1711 + { 1.1712 + int32_t childIndex = 0; 1.1713 + int32_t numChildren = theNode->GetChildCount(); 1.1714 + 1.1715 + if (aHint == HINTLEFT) 1.1716 + { 1.1717 + if (aOffset > 0) 1.1718 + childIndex = aOffset - 1; 1.1719 + else 1.1720 + childIndex = aOffset; 1.1721 + } 1.1722 + else // HINTRIGHT 1.1723 + { 1.1724 + if (aOffset >= numChildren) 1.1725 + { 1.1726 + if (numChildren > 0) 1.1727 + childIndex = numChildren - 1; 1.1728 + else 1.1729 + childIndex = 0; 1.1730 + } 1.1731 + else 1.1732 + childIndex = aOffset; 1.1733 + } 1.1734 + 1.1735 + if (childIndex > 0 || numChildren > 0) { 1.1736 + nsCOMPtr<nsIContent> childNode = theNode->GetChildAt(childIndex); 1.1737 + 1.1738 + if (!childNode) 1.1739 + return nullptr; 1.1740 + 1.1741 + theNode = childNode; 1.1742 + } 1.1743 + 1.1744 +#ifdef DONT_DO_THIS_YET 1.1745 + // XXX: We can't use this code yet because the hinting 1.1746 + // can cause us to attach to the wrong line frame. 1.1747 + 1.1748 + // Now that we have the child node, check if it too 1.1749 + // can contain children. If so, call this method again! 1.1750 + 1.1751 + if (theNode->IsElement()) 1.1752 + { 1.1753 + int32_t newOffset = 0; 1.1754 + 1.1755 + if (aOffset > childIndex) 1.1756 + { 1.1757 + numChildren = theNode->GetChildCount(); 1.1758 + 1.1759 + newOffset = numChildren; 1.1760 + } 1.1761 + 1.1762 + return GetFrameForNodeOffset(theNode, newOffset, aHint, aReturnOffset); 1.1763 + } 1.1764 + else 1.1765 +#endif // DONT_DO_THIS_YET 1.1766 + { 1.1767 + // Check to see if theNode is a text node. If it is, translate 1.1768 + // aOffset into an offset into the text node. 1.1769 + 1.1770 + nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(theNode); 1.1771 + 1.1772 + if (textNode) 1.1773 + { 1.1774 + if (theNode->GetPrimaryFrame()) 1.1775 + { 1.1776 + if (aOffset > childIndex) 1.1777 + { 1.1778 + uint32_t textLength = 0; 1.1779 + 1.1780 + nsresult rv = textNode->GetLength(&textLength); 1.1781 + if (NS_FAILED(rv)) 1.1782 + return nullptr; 1.1783 + 1.1784 + *aReturnOffset = (int32_t)textLength; 1.1785 + } 1.1786 + else 1.1787 + *aReturnOffset = 0; 1.1788 + } 1.1789 + else 1.1790 + { 1.1791 + // If we're at a collapsed whitespace content node (which 1.1792 + // does not have a primary frame), just use the original node 1.1793 + // to get the frame on which we should put the caret. 1.1794 + theNode = aNode; 1.1795 + } 1.1796 + } 1.1797 + } 1.1798 + } 1.1799 + 1.1800 + // If the node is a ShadowRoot, the frame needs to be adjusted, 1.1801 + // because a ShadowRoot does not get a frame. Its children are rendered 1.1802 + // as children of the host. 1.1803 + mozilla::dom::ShadowRoot* shadowRoot = 1.1804 + mozilla::dom::ShadowRoot::FromNode(theNode); 1.1805 + if (shadowRoot) { 1.1806 + theNode = shadowRoot->GetHost(); 1.1807 + } 1.1808 + 1.1809 + nsIFrame* returnFrame = theNode->GetPrimaryFrame(); 1.1810 + if (!returnFrame) 1.1811 + return nullptr; 1.1812 + 1.1813 + // find the child frame containing the offset we want 1.1814 + returnFrame->GetChildFrameContainingOffset(*aReturnOffset, aHint == HINTRIGHT, 1.1815 + &aOffset, &returnFrame); 1.1816 + return returnFrame; 1.1817 +} 1.1818 + 1.1819 +void 1.1820 +nsFrameSelection::CommonPageMove(bool aForward, 1.1821 + bool aExtend, 1.1822 + nsIScrollableFrame* aScrollableFrame) 1.1823 +{ 1.1824 + // expected behavior for PageMove is to scroll AND move the caret 1.1825 + // and remain relative position of the caret in view. see Bug 4302. 1.1826 + 1.1827 + //get the frame from the scrollable view 1.1828 + 1.1829 + nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame(); 1.1830 + if (!scrolledFrame) 1.1831 + return; 1.1832 + 1.1833 + // find out where the caret is. 1.1834 + // we should know mDesiredX value of nsFrameSelection, but I havent seen that behavior in other windows applications yet. 1.1835 + nsISelection* domSel = GetSelection(nsISelectionController::SELECTION_NORMAL); 1.1836 + if (!domSel) 1.1837 + return; 1.1838 + 1.1839 + nsRefPtr<nsCaret> caret = mShell->GetCaret(); 1.1840 + 1.1841 + nsRect caretPos; 1.1842 + nsIFrame* caretFrame = caret->GetGeometry(domSel, &caretPos); 1.1843 + if (!caretFrame) 1.1844 + return; 1.1845 + 1.1846 + //need to adjust caret jump by percentage scroll 1.1847 + nsSize scrollDelta = aScrollableFrame->GetPageScrollAmount(); 1.1848 + 1.1849 + if (aForward) 1.1850 + caretPos.y += scrollDelta.height; 1.1851 + else 1.1852 + caretPos.y -= scrollDelta.height; 1.1853 + 1.1854 + caretPos += caretFrame->GetOffsetTo(scrolledFrame); 1.1855 + 1.1856 + // get a content at desired location 1.1857 + nsPoint desiredPoint; 1.1858 + desiredPoint.x = caretPos.x; 1.1859 + desiredPoint.y = caretPos.y + caretPos.height/2; 1.1860 + nsIFrame::ContentOffsets offsets = 1.1861 + scrolledFrame->GetContentOffsetsFromPoint(desiredPoint); 1.1862 + 1.1863 + if (!offsets.content) 1.1864 + return; 1.1865 + 1.1866 + // scroll one page 1.1867 + aScrollableFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1), 1.1868 + nsIScrollableFrame::PAGES, 1.1869 + nsIScrollableFrame::SMOOTH); 1.1870 + 1.1871 + // place the caret 1.1872 + HandleClick(offsets.content, offsets.offset, 1.1873 + offsets.offset, aExtend, false, true); 1.1874 +} 1.1875 + 1.1876 +nsresult 1.1877 +nsFrameSelection::CharacterMove(bool aForward, bool aExtend) 1.1878 +{ 1.1879 + if (aForward) 1.1880 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT, aExtend, eSelectCluster); 1.1881 + else 1.1882 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT, aExtend, eSelectCluster); 1.1883 +} 1.1884 + 1.1885 +nsresult 1.1886 +nsFrameSelection::CharacterExtendForDelete() 1.1887 +{ 1.1888 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_DELETE, true, eSelectCluster); 1.1889 +} 1.1890 + 1.1891 +nsresult 1.1892 +nsFrameSelection::CharacterExtendForBackspace() 1.1893 +{ 1.1894 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_BACK_SPACE, true, eSelectCharacter); 1.1895 +} 1.1896 + 1.1897 +nsresult 1.1898 +nsFrameSelection::WordMove(bool aForward, bool aExtend) 1.1899 +{ 1.1900 + if (aForward) 1.1901 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT,aExtend,eSelectWord); 1.1902 + else 1.1903 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT,aExtend,eSelectWord); 1.1904 +} 1.1905 + 1.1906 +nsresult 1.1907 +nsFrameSelection::WordExtendForDelete(bool aForward) 1.1908 +{ 1.1909 + if (aForward) 1.1910 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_DELETE, true, eSelectWord); 1.1911 + else 1.1912 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_BACK_SPACE, true, eSelectWord); 1.1913 +} 1.1914 + 1.1915 +nsresult 1.1916 +nsFrameSelection::LineMove(bool aForward, bool aExtend) 1.1917 +{ 1.1918 + if (aForward) 1.1919 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_DOWN,aExtend,eSelectLine); 1.1920 + else 1.1921 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_UP,aExtend,eSelectLine); 1.1922 +} 1.1923 + 1.1924 +nsresult 1.1925 +nsFrameSelection::IntraLineMove(bool aForward, bool aExtend) 1.1926 +{ 1.1927 + if (aForward) 1.1928 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_END,aExtend,eSelectLine); 1.1929 + else 1.1930 + return MoveCaret(nsIDOMKeyEvent::DOM_VK_HOME,aExtend,eSelectLine); 1.1931 +} 1.1932 + 1.1933 +nsresult 1.1934 +nsFrameSelection::SelectAll() 1.1935 +{ 1.1936 + nsCOMPtr<nsIContent> rootContent; 1.1937 + if (mLimiter) 1.1938 + { 1.1939 + rootContent = mLimiter;//addrefit 1.1940 + } 1.1941 + else if (mAncestorLimiter) { 1.1942 + rootContent = mAncestorLimiter; 1.1943 + } 1.1944 + else 1.1945 + { 1.1946 + NS_ENSURE_STATE(mShell); 1.1947 + nsIDocument *doc = mShell->GetDocument(); 1.1948 + if (!doc) 1.1949 + return NS_ERROR_FAILURE; 1.1950 + rootContent = doc->GetRootElement(); 1.1951 + if (!rootContent) 1.1952 + return NS_ERROR_FAILURE; 1.1953 + } 1.1954 + int32_t numChildren = rootContent->GetChildCount(); 1.1955 + PostReason(nsISelectionListener::NO_REASON); 1.1956 + return TakeFocus(rootContent, 0, numChildren, HINTLEFT, false, false); 1.1957 +} 1.1958 + 1.1959 +//////////END FRAMESELECTION 1.1960 + 1.1961 +void 1.1962 +nsFrameSelection::StartBatchChanges() 1.1963 +{ 1.1964 + mBatching++; 1.1965 +} 1.1966 + 1.1967 +void 1.1968 +nsFrameSelection::EndBatchChanges() 1.1969 +{ 1.1970 + mBatching--; 1.1971 + NS_ASSERTION(mBatching >=0,"Bad mBatching"); 1.1972 + if (mBatching == 0 && mChangesDuringBatching){ 1.1973 + mChangesDuringBatching = false; 1.1974 + NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL); 1.1975 + } 1.1976 +} 1.1977 + 1.1978 + 1.1979 +nsresult 1.1980 +nsFrameSelection::NotifySelectionListeners(SelectionType aType) 1.1981 +{ 1.1982 + int8_t index = GetIndexFromSelectionType(aType); 1.1983 + if (index >=0 && mDomSelections[index]) 1.1984 + { 1.1985 + return mDomSelections[index]->NotifySelectionListeners(); 1.1986 + } 1.1987 + return NS_ERROR_FAILURE; 1.1988 +} 1.1989 + 1.1990 +// Start of Table Selection methods 1.1991 + 1.1992 +static bool IsCell(nsIContent *aContent) 1.1993 +{ 1.1994 + return ((aContent->Tag() == nsGkAtoms::td || 1.1995 + aContent->Tag() == nsGkAtoms::th) && 1.1996 + aContent->IsHTML()); 1.1997 +} 1.1998 + 1.1999 +nsITableCellLayout* 1.2000 +nsFrameSelection::GetCellLayout(nsIContent *aCellContent) const 1.2001 +{ 1.2002 + NS_ENSURE_TRUE(mShell, nullptr); 1.2003 + nsITableCellLayout *cellLayoutObject = 1.2004 + do_QueryFrame(aCellContent->GetPrimaryFrame()); 1.2005 + return cellLayoutObject; 1.2006 +} 1.2007 + 1.2008 +nsresult 1.2009 +nsFrameSelection::ClearNormalSelection() 1.2010 +{ 1.2011 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.2012 + if (!mDomSelections[index]) 1.2013 + return NS_ERROR_NULL_POINTER; 1.2014 + 1.2015 + return mDomSelections[index]->RemoveAllRanges(); 1.2016 +} 1.2017 + 1.2018 +static nsIContent* 1.2019 +GetFirstSelectedContent(nsRange* aRange) 1.2020 +{ 1.2021 + if (!aRange) { 1.2022 + return nullptr; 1.2023 + } 1.2024 + 1.2025 + NS_PRECONDITION(aRange->GetStartParent(), "Must have start parent!"); 1.2026 + NS_PRECONDITION(aRange->GetStartParent()->IsElement(), 1.2027 + "Unexpected parent"); 1.2028 + 1.2029 + return aRange->GetStartParent()->GetChildAt(aRange->StartOffset()); 1.2030 +} 1.2031 + 1.2032 +// Table selection support. 1.2033 +// TODO: Separate table methods into a separate nsITableSelection interface 1.2034 +nsresult 1.2035 +nsFrameSelection::HandleTableSelection(nsINode* aParentContent, 1.2036 + int32_t aContentOffset, 1.2037 + int32_t aTarget, 1.2038 + WidgetMouseEvent* aMouseEvent) 1.2039 +{ 1.2040 + NS_ENSURE_TRUE(aParentContent, NS_ERROR_NULL_POINTER); 1.2041 + NS_ENSURE_TRUE(aMouseEvent, NS_ERROR_NULL_POINTER); 1.2042 + 1.2043 + if (mMouseDownState && mDragSelectingCells && (aTarget & nsISelectionPrivate::TABLESELECTION_TABLE)) 1.2044 + { 1.2045 + // We were selecting cells and user drags mouse in table border or inbetween cells, 1.2046 + // just do nothing 1.2047 + return NS_OK; 1.2048 + } 1.2049 + 1.2050 + nsresult result = NS_OK; 1.2051 + 1.2052 + nsIContent *childContent = aParentContent->GetChildAt(aContentOffset); 1.2053 + 1.2054 + // When doing table selection, always set the direction to next so 1.2055 + // we can be sure that anchorNode's offset always points to the 1.2056 + // selected cell 1.2057 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.2058 + if (!mDomSelections[index]) 1.2059 + return NS_ERROR_NULL_POINTER; 1.2060 + 1.2061 + mDomSelections[index]->SetDirection(eDirNext); 1.2062 + 1.2063 + // Stack-class to wrap all table selection changes in 1.2064 + // BeginBatchChanges() / EndBatchChanges() 1.2065 + nsSelectionBatcher selectionBatcher(mDomSelections[index]); 1.2066 + 1.2067 + int32_t startRowIndex, startColIndex, curRowIndex, curColIndex; 1.2068 + if (mMouseDownState && mDragSelectingCells) 1.2069 + { 1.2070 + // We are drag-selecting 1.2071 + if (aTarget != nsISelectionPrivate::TABLESELECTION_TABLE) 1.2072 + { 1.2073 + // If dragging in the same cell as last event, do nothing 1.2074 + if (mEndSelectedCell == childContent) 1.2075 + return NS_OK; 1.2076 + 1.2077 +#ifdef DEBUG_TABLE_SELECTION 1.2078 +printf(" mStartSelectedCell = %x, mEndSelectedCell = %x, childContent = %x \n", mStartSelectedCell, mEndSelectedCell, childContent); 1.2079 +#endif 1.2080 + // aTarget can be any "cell mode", 1.2081 + // so we can easily drag-select rows and columns 1.2082 + // Once we are in row or column mode, 1.2083 + // we can drift into any cell to stay in that mode 1.2084 + // even if aTarget = TABLESELECTION_CELL 1.2085 + 1.2086 + if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW || 1.2087 + mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN) 1.2088 + { 1.2089 + if (mEndSelectedCell) 1.2090 + { 1.2091 + // Also check if cell is in same row/col 1.2092 + result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex); 1.2093 + if (NS_FAILED(result)) return result; 1.2094 + result = GetCellIndexes(childContent, curRowIndex, curColIndex); 1.2095 + if (NS_FAILED(result)) return result; 1.2096 + 1.2097 +#ifdef DEBUG_TABLE_SELECTION 1.2098 +printf(" curRowIndex = %d, startRowIndex = %d, curColIndex = %d, startColIndex = %d\n", curRowIndex, startRowIndex, curColIndex, startColIndex); 1.2099 +#endif 1.2100 + if ((mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW && startRowIndex == curRowIndex) || 1.2101 + (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN && startColIndex == curColIndex)) 1.2102 + return NS_OK; 1.2103 + } 1.2104 +#ifdef DEBUG_TABLE_SELECTION 1.2105 +printf(" Dragged into a new column or row\n"); 1.2106 +#endif 1.2107 + // Continue dragging row or column selection 1.2108 + return SelectRowOrColumn(childContent, mSelectingTableCellMode); 1.2109 + } 1.2110 + else if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_CELL) 1.2111 + { 1.2112 +#ifdef DEBUG_TABLE_SELECTION 1.2113 +printf("HandleTableSelection: Dragged into a new cell\n"); 1.2114 +#endif 1.2115 + // Trick for quick selection of rows and columns 1.2116 + // Hold down shift, then start selecting in one direction 1.2117 + // If next cell dragged into is in same row, select entire row, 1.2118 + // if next cell is in same column, select entire column 1.2119 + if (mStartSelectedCell && aMouseEvent->IsShift()) 1.2120 + { 1.2121 + result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex); 1.2122 + if (NS_FAILED(result)) return result; 1.2123 + result = GetCellIndexes(childContent, curRowIndex, curColIndex); 1.2124 + if (NS_FAILED(result)) return result; 1.2125 + 1.2126 + if (startRowIndex == curRowIndex || 1.2127 + startColIndex == curColIndex) 1.2128 + { 1.2129 + // Force new selection block 1.2130 + mStartSelectedCell = nullptr; 1.2131 + mDomSelections[index]->RemoveAllRanges(); 1.2132 + 1.2133 + if (startRowIndex == curRowIndex) 1.2134 + mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_ROW; 1.2135 + else 1.2136 + mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_COLUMN; 1.2137 + 1.2138 + return SelectRowOrColumn(childContent, mSelectingTableCellMode); 1.2139 + } 1.2140 + } 1.2141 + 1.2142 + // Reselect block of cells to new end location 1.2143 + return SelectBlockOfCells(mStartSelectedCell, childContent); 1.2144 + } 1.2145 + } 1.2146 + // Do nothing if dragging in table, but outside a cell 1.2147 + return NS_OK; 1.2148 + } 1.2149 + else 1.2150 + { 1.2151 + // Not dragging -- mouse event is down or up 1.2152 + if (mMouseDownState) 1.2153 + { 1.2154 +#ifdef DEBUG_TABLE_SELECTION 1.2155 +printf("HandleTableSelection: Mouse down event\n"); 1.2156 +#endif 1.2157 + // Clear cell we stored in mouse-down 1.2158 + mUnselectCellOnMouseUp = nullptr; 1.2159 + 1.2160 + if (aTarget == nsISelectionPrivate::TABLESELECTION_CELL) 1.2161 + { 1.2162 + bool isSelected = false; 1.2163 + 1.2164 + // Check if we have other selected cells 1.2165 + nsIContent* previousCellNode = 1.2166 + GetFirstSelectedContent(GetFirstCellRange()); 1.2167 + if (previousCellNode) 1.2168 + { 1.2169 + // We have at least 1 other selected cell 1.2170 + 1.2171 + // Check if new cell is already selected 1.2172 + nsIFrame *cellFrame = childContent->GetPrimaryFrame(); 1.2173 + if (!cellFrame) return NS_ERROR_NULL_POINTER; 1.2174 + isSelected = cellFrame->IsSelected(); 1.2175 + } 1.2176 + else 1.2177 + { 1.2178 + // No cells selected -- remove non-cell selection 1.2179 + mDomSelections[index]->RemoveAllRanges(); 1.2180 + } 1.2181 + mDragSelectingCells = true; // Signal to start drag-cell-selection 1.2182 + mSelectingTableCellMode = aTarget; 1.2183 + // Set start for new drag-selection block (not appended) 1.2184 + mStartSelectedCell = childContent; 1.2185 + // The initial block end is same as the start 1.2186 + mEndSelectedCell = childContent; 1.2187 + 1.2188 + if (isSelected) 1.2189 + { 1.2190 + // Remember this cell to (possibly) unselect it on mouseup 1.2191 + mUnselectCellOnMouseUp = childContent; 1.2192 +#ifdef DEBUG_TABLE_SELECTION 1.2193 +printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n"); 1.2194 +#endif 1.2195 + } 1.2196 + else 1.2197 + { 1.2198 + // Select an unselected cell 1.2199 + // but first remove existing selection if not in same table 1.2200 + if (previousCellNode && 1.2201 + !IsInSameTable(previousCellNode, childContent)) 1.2202 + { 1.2203 + mDomSelections[index]->RemoveAllRanges(); 1.2204 + // Reset selection mode that is cleared in RemoveAllRanges 1.2205 + mSelectingTableCellMode = aTarget; 1.2206 + } 1.2207 + 1.2208 + return SelectCellElement(childContent); 1.2209 + } 1.2210 + 1.2211 + return NS_OK; 1.2212 + } 1.2213 + else if (aTarget == nsISelectionPrivate::TABLESELECTION_TABLE) 1.2214 + { 1.2215 + //TODO: We currently select entire table when clicked between cells, 1.2216 + // should we restrict to only around border? 1.2217 + // *** How do we get location data for cell and click? 1.2218 + mDragSelectingCells = false; 1.2219 + mStartSelectedCell = nullptr; 1.2220 + mEndSelectedCell = nullptr; 1.2221 + 1.2222 + // Remove existing selection and select the table 1.2223 + mDomSelections[index]->RemoveAllRanges(); 1.2224 + return CreateAndAddRange(aParentContent, aContentOffset); 1.2225 + } 1.2226 + else if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW || aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN) 1.2227 + { 1.2228 +#ifdef DEBUG_TABLE_SELECTION 1.2229 +printf("aTarget == %d\n", aTarget); 1.2230 +#endif 1.2231 + 1.2232 + // Start drag-selecting mode so multiple rows/cols can be selected 1.2233 + // Note: Currently, nsFrame::GetDataForTableSelection 1.2234 + // will never call us for row or column selection on mouse down 1.2235 + mDragSelectingCells = true; 1.2236 + 1.2237 + // Force new selection block 1.2238 + mStartSelectedCell = nullptr; 1.2239 + mDomSelections[index]->RemoveAllRanges(); 1.2240 + // Always do this AFTER RemoveAllRanges 1.2241 + mSelectingTableCellMode = aTarget; 1.2242 + return SelectRowOrColumn(childContent, aTarget); 1.2243 + } 1.2244 + } 1.2245 + else 1.2246 + { 1.2247 +#ifdef DEBUG_TABLE_SELECTION 1.2248 +printf("HandleTableSelection: Mouse UP event. mDragSelectingCells=%d, mStartSelectedCell=%d\n", mDragSelectingCells, mStartSelectedCell); 1.2249 +#endif 1.2250 + // First check if we are extending a block selection 1.2251 + int32_t rangeCount; 1.2252 + result = mDomSelections[index]->GetRangeCount(&rangeCount); 1.2253 + if (NS_FAILED(result)) 1.2254 + return result; 1.2255 + 1.2256 + if (rangeCount > 0 && aMouseEvent->IsShift() && 1.2257 + mAppendStartSelectedCell && mAppendStartSelectedCell != childContent) 1.2258 + { 1.2259 + // Shift key is down: append a block selection 1.2260 + mDragSelectingCells = false; 1.2261 + return SelectBlockOfCells(mAppendStartSelectedCell, childContent); 1.2262 + } 1.2263 + 1.2264 + if (mDragSelectingCells) 1.2265 + mAppendStartSelectedCell = mStartSelectedCell; 1.2266 + 1.2267 + mDragSelectingCells = false; 1.2268 + mStartSelectedCell = nullptr; 1.2269 + mEndSelectedCell = nullptr; 1.2270 + 1.2271 + // Any other mouseup actions require that Ctrl or Cmd key is pressed 1.2272 + // else stop table selection mode 1.2273 + bool doMouseUpAction = false; 1.2274 +#ifdef XP_MACOSX 1.2275 + doMouseUpAction = aMouseEvent->IsMeta(); 1.2276 +#else 1.2277 + doMouseUpAction = aMouseEvent->IsControl(); 1.2278 +#endif 1.2279 + if (!doMouseUpAction) 1.2280 + { 1.2281 +#ifdef DEBUG_TABLE_SELECTION 1.2282 +printf("HandleTableSelection: Ending cell selection on mouseup: mAppendStartSelectedCell=%d\n", mAppendStartSelectedCell); 1.2283 +#endif 1.2284 + return NS_OK; 1.2285 + } 1.2286 + // Unselect a cell only if it wasn't 1.2287 + // just selected on mousedown 1.2288 + if( childContent == mUnselectCellOnMouseUp) 1.2289 + { 1.2290 + // Scan ranges to find the cell to unselect (the selection range to remove) 1.2291 + // XXXbz it's really weird that this lives outside the loop, so once we 1.2292 + // find one we keep looking at it even if we find no more cells... 1.2293 + nsINode* previousCellParent = nullptr; 1.2294 +#ifdef DEBUG_TABLE_SELECTION 1.2295 +printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount); 1.2296 +#endif 1.2297 + for( int32_t i = 0; i < rangeCount; i++) 1.2298 + { 1.2299 + // Strong reference, because sometimes we want to remove 1.2300 + // this range, and then we might be the only owner. 1.2301 + nsRefPtr<nsRange> range = mDomSelections[index]->GetRangeAt(i); 1.2302 + if (!range) return NS_ERROR_NULL_POINTER; 1.2303 + 1.2304 + nsINode* parent = range->GetStartParent(); 1.2305 + if (!parent) return NS_ERROR_NULL_POINTER; 1.2306 + 1.2307 + int32_t offset = range->StartOffset(); 1.2308 + // Be sure previous selection is a table cell 1.2309 + nsIContent* child = parent->GetChildAt(offset); 1.2310 + if (child && IsCell(child)) 1.2311 + previousCellParent = parent; 1.2312 + 1.2313 + // We're done if we didn't find parent of a previously-selected cell 1.2314 + if (!previousCellParent) break; 1.2315 + 1.2316 + if (previousCellParent == aParentContent && offset == aContentOffset) 1.2317 + { 1.2318 + // Cell is already selected 1.2319 + if (rangeCount == 1) 1.2320 + { 1.2321 +#ifdef DEBUG_TABLE_SELECTION 1.2322 +printf("HandleTableSelection: Unselecting single selected cell\n"); 1.2323 +#endif 1.2324 + // This was the only cell selected. 1.2325 + // Collapse to "normal" selection inside the cell 1.2326 + mStartSelectedCell = nullptr; 1.2327 + mEndSelectedCell = nullptr; 1.2328 + mAppendStartSelectedCell = nullptr; 1.2329 + //TODO: We need a "Collapse to just before deepest child" routine 1.2330 + // Even better, should we collapse to just after the LAST deepest child 1.2331 + // (i.e., at the end of the cell's contents)? 1.2332 + return mDomSelections[index]->Collapse(childContent, 0); 1.2333 + } 1.2334 +#ifdef DEBUG_TABLE_SELECTION 1.2335 +printf("HandleTableSelection: Removing cell from multi-cell selection\n"); 1.2336 +#endif 1.2337 + // Unselecting the start of previous block 1.2338 + // XXX What do we use now! 1.2339 + if (childContent == mAppendStartSelectedCell) 1.2340 + mAppendStartSelectedCell = nullptr; 1.2341 + 1.2342 + // Deselect cell by removing its range from selection 1.2343 + return mDomSelections[index]->RemoveRange(range); 1.2344 + } 1.2345 + } 1.2346 + mUnselectCellOnMouseUp = nullptr; 1.2347 + } 1.2348 + } 1.2349 + } 1.2350 + return result; 1.2351 +} 1.2352 + 1.2353 +nsresult 1.2354 +nsFrameSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCell) 1.2355 +{ 1.2356 + NS_ENSURE_TRUE(aStartCell, NS_ERROR_NULL_POINTER); 1.2357 + NS_ENSURE_TRUE(aEndCell, NS_ERROR_NULL_POINTER); 1.2358 + mEndSelectedCell = aEndCell; 1.2359 + 1.2360 + nsCOMPtr<nsIContent> startCell; 1.2361 + nsresult result = NS_OK; 1.2362 + 1.2363 + // If new end cell is in a different table, do nothing 1.2364 + nsIContent* table = IsInSameTable(aStartCell, aEndCell); 1.2365 + if (!table) { 1.2366 + return NS_OK; 1.2367 + } 1.2368 + 1.2369 + // Get starting and ending cells' location in the cellmap 1.2370 + int32_t startRowIndex, startColIndex, endRowIndex, endColIndex; 1.2371 + result = GetCellIndexes(aStartCell, startRowIndex, startColIndex); 1.2372 + if(NS_FAILED(result)) return result; 1.2373 + result = GetCellIndexes(aEndCell, endRowIndex, endColIndex); 1.2374 + if(NS_FAILED(result)) return result; 1.2375 + 1.2376 + if (mDragSelectingCells) 1.2377 + { 1.2378 + // Drag selecting: remove selected cells outside of new block limits 1.2379 + UnselectCells(table, startRowIndex, startColIndex, endRowIndex, endColIndex, 1.2380 + true); 1.2381 + } 1.2382 + 1.2383 + // Note that we select block in the direction of user's mouse dragging, 1.2384 + // which means start cell may be after the end cell in either row or column 1.2385 + return AddCellsToSelection(table, startRowIndex, startColIndex, 1.2386 + endRowIndex, endColIndex); 1.2387 +} 1.2388 + 1.2389 +nsresult 1.2390 +nsFrameSelection::UnselectCells(nsIContent *aTableContent, 1.2391 + int32_t aStartRowIndex, 1.2392 + int32_t aStartColumnIndex, 1.2393 + int32_t aEndRowIndex, 1.2394 + int32_t aEndColumnIndex, 1.2395 + bool aRemoveOutsideOfCellRange) 1.2396 +{ 1.2397 + int8_t index = 1.2398 + GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.2399 + if (!mDomSelections[index]) 1.2400 + return NS_ERROR_NULL_POINTER; 1.2401 + 1.2402 + nsTableOuterFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame()); 1.2403 + if (!tableFrame) 1.2404 + return NS_ERROR_FAILURE; 1.2405 + 1.2406 + int32_t minRowIndex = std::min(aStartRowIndex, aEndRowIndex); 1.2407 + int32_t maxRowIndex = std::max(aStartRowIndex, aEndRowIndex); 1.2408 + int32_t minColIndex = std::min(aStartColumnIndex, aEndColumnIndex); 1.2409 + int32_t maxColIndex = std::max(aStartColumnIndex, aEndColumnIndex); 1.2410 + 1.2411 + // Strong reference because we sometimes remove the range 1.2412 + nsRefPtr<nsRange> range = GetFirstCellRange(); 1.2413 + nsIContent* cellNode = GetFirstSelectedContent(range); 1.2414 + NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range"); 1.2415 + 1.2416 + int32_t curRowIndex, curColIndex; 1.2417 + while (cellNode) 1.2418 + { 1.2419 + nsresult result = GetCellIndexes(cellNode, curRowIndex, curColIndex); 1.2420 + if (NS_FAILED(result)) 1.2421 + return result; 1.2422 + 1.2423 +#ifdef DEBUG_TABLE_SELECTION 1.2424 + if (!range) 1.2425 + printf("RemoveCellsToSelection -- range is null\n"); 1.2426 +#endif 1.2427 + 1.2428 + if (range) { 1.2429 + if (aRemoveOutsideOfCellRange) { 1.2430 + if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex || 1.2431 + curColIndex < minColIndex || curColIndex > maxColIndex) { 1.2432 + 1.2433 + mDomSelections[index]->RemoveRange(range); 1.2434 + // Since we've removed the range, decrement pointer to next range 1.2435 + mSelectedCellIndex--; 1.2436 + } 1.2437 + 1.2438 + } else { 1.2439 + // Remove cell from selection if it belongs to the given cells range or 1.2440 + // it is spanned onto the cells range. 1.2441 + nsTableCellFrame* cellFrame = 1.2442 + tableFrame->GetCellFrameAt(curRowIndex, curColIndex); 1.2443 + 1.2444 + int32_t origRowIndex, origColIndex; 1.2445 + cellFrame->GetRowIndex(origRowIndex); 1.2446 + cellFrame->GetColIndex(origColIndex); 1.2447 + uint32_t actualRowSpan = 1.2448 + tableFrame->GetEffectiveRowSpanAt(origRowIndex, origColIndex); 1.2449 + uint32_t actualColSpan = 1.2450 + tableFrame->GetEffectiveColSpanAt(curRowIndex, curColIndex); 1.2451 + if (origRowIndex <= maxRowIndex && maxRowIndex >= 0 && 1.2452 + origRowIndex + actualRowSpan - 1 >= static_cast<uint32_t>(minRowIndex) && 1.2453 + origColIndex <= maxColIndex && maxColIndex >= 0 && 1.2454 + origColIndex + actualColSpan - 1 >= static_cast<uint32_t>(minColIndex)) { 1.2455 + 1.2456 + mDomSelections[index]->RemoveRange(range); 1.2457 + // Since we've removed the range, decrement pointer to next range 1.2458 + mSelectedCellIndex--; 1.2459 + } 1.2460 + } 1.2461 + } 1.2462 + 1.2463 + range = GetNextCellRange(); 1.2464 + cellNode = GetFirstSelectedContent(range); 1.2465 + NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range"); 1.2466 + } 1.2467 + 1.2468 + return NS_OK; 1.2469 +} 1.2470 + 1.2471 +nsresult 1.2472 +nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent, 1.2473 + int32_t aStartRowIndex, 1.2474 + int32_t aStartColumnIndex, 1.2475 + int32_t aEndRowIndex, 1.2476 + int32_t aEndColumnIndex) 1.2477 +{ 1.2478 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.2479 + if (!mDomSelections[index]) 1.2480 + return NS_ERROR_NULL_POINTER; 1.2481 + 1.2482 + nsTableOuterFrame* tableFrame = do_QueryFrame(aTableContent->GetPrimaryFrame()); 1.2483 + if (!tableFrame) // Check that |table| is a table. 1.2484 + return NS_ERROR_FAILURE; 1.2485 + 1.2486 + nsresult result = NS_OK; 1.2487 + int32_t row = aStartRowIndex; 1.2488 + while(true) 1.2489 + { 1.2490 + int32_t col = aStartColumnIndex; 1.2491 + while(true) 1.2492 + { 1.2493 + nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(row, col); 1.2494 + 1.2495 + // Skip cells that are spanned from previous locations or are already selected 1.2496 + if (cellFrame) { 1.2497 + int32_t origRow, origCol; 1.2498 + cellFrame->GetRowIndex(origRow); 1.2499 + cellFrame->GetColIndex(origCol); 1.2500 + if (origRow == row && origCol == col && !cellFrame->IsSelected()) { 1.2501 + result = SelectCellElement(cellFrame->GetContent()); 1.2502 + if (NS_FAILED(result)) return result; 1.2503 + } 1.2504 + } 1.2505 + // Done when we reach end column 1.2506 + if (col == aEndColumnIndex) break; 1.2507 + 1.2508 + if (aStartColumnIndex < aEndColumnIndex) 1.2509 + col ++; 1.2510 + else 1.2511 + col--; 1.2512 + }; 1.2513 + if (row == aEndRowIndex) break; 1.2514 + 1.2515 + if (aStartRowIndex < aEndRowIndex) 1.2516 + row++; 1.2517 + else 1.2518 + row--; 1.2519 + }; 1.2520 + return result; 1.2521 +} 1.2522 + 1.2523 +nsresult 1.2524 +nsFrameSelection::RemoveCellsFromSelection(nsIContent *aTable, 1.2525 + int32_t aStartRowIndex, 1.2526 + int32_t aStartColumnIndex, 1.2527 + int32_t aEndRowIndex, 1.2528 + int32_t aEndColumnIndex) 1.2529 +{ 1.2530 + return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex, 1.2531 + aEndRowIndex, aEndColumnIndex, false); 1.2532 +} 1.2533 + 1.2534 +nsresult 1.2535 +nsFrameSelection::RestrictCellsToSelection(nsIContent *aTable, 1.2536 + int32_t aStartRowIndex, 1.2537 + int32_t aStartColumnIndex, 1.2538 + int32_t aEndRowIndex, 1.2539 + int32_t aEndColumnIndex) 1.2540 +{ 1.2541 + return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex, 1.2542 + aEndRowIndex, aEndColumnIndex, true); 1.2543 +} 1.2544 + 1.2545 +nsresult 1.2546 +nsFrameSelection::SelectRowOrColumn(nsIContent *aCellContent, uint32_t aTarget) 1.2547 +{ 1.2548 + if (!aCellContent) return NS_ERROR_NULL_POINTER; 1.2549 + 1.2550 + nsIContent* table = GetParentTable(aCellContent); 1.2551 + if (!table) return NS_ERROR_NULL_POINTER; 1.2552 + 1.2553 + // Get table and cell layout interfaces to access 1.2554 + // cell data based on cellmap location 1.2555 + // Frames are not ref counted, so don't use an nsCOMPtr 1.2556 + nsTableOuterFrame* tableFrame = do_QueryFrame(table->GetPrimaryFrame()); 1.2557 + if (!tableFrame) return NS_ERROR_FAILURE; 1.2558 + nsITableCellLayout *cellLayout = GetCellLayout(aCellContent); 1.2559 + if (!cellLayout) return NS_ERROR_FAILURE; 1.2560 + 1.2561 + // Get location of target cell: 1.2562 + int32_t rowIndex, colIndex; 1.2563 + nsresult result = cellLayout->GetCellIndexes(rowIndex, colIndex); 1.2564 + if (NS_FAILED(result)) return result; 1.2565 + 1.2566 + // Be sure we start at proper beginning 1.2567 + // (This allows us to select row or col given ANY cell!) 1.2568 + if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW) 1.2569 + colIndex = 0; 1.2570 + if (aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN) 1.2571 + rowIndex = 0; 1.2572 + 1.2573 + nsCOMPtr<nsIContent> firstCell, lastCell; 1.2574 + while (true) { 1.2575 + // Loop through all cells in column or row to find first and last 1.2576 + nsCOMPtr<nsIContent> curCellContent = 1.2577 + tableFrame->GetCellAt(rowIndex, colIndex); 1.2578 + if (!curCellContent) 1.2579 + break; 1.2580 + 1.2581 + if (!firstCell) 1.2582 + firstCell = curCellContent; 1.2583 + 1.2584 + lastCell = curCellContent.forget(); 1.2585 + 1.2586 + // Move to next cell in cellmap, skipping spanned locations 1.2587 + if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW) 1.2588 + colIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex); 1.2589 + else 1.2590 + rowIndex += tableFrame->GetEffectiveRowSpanAt(rowIndex, colIndex); 1.2591 + } 1.2592 + 1.2593 + // Use SelectBlockOfCells: 1.2594 + // This will replace existing selection, 1.2595 + // but allow unselecting by dragging out of selected region 1.2596 + if (firstCell && lastCell) 1.2597 + { 1.2598 + if (!mStartSelectedCell) 1.2599 + { 1.2600 + // We are starting a new block, so select the first cell 1.2601 + result = SelectCellElement(firstCell); 1.2602 + if (NS_FAILED(result)) return result; 1.2603 + mStartSelectedCell = firstCell; 1.2604 + } 1.2605 + nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell); 1.2606 + result = SelectBlockOfCells(mStartSelectedCell, lastCellContent); 1.2607 + 1.2608 + // This gets set to the cell at end of row/col, 1.2609 + // but we need it to be the cell under cursor 1.2610 + mEndSelectedCell = aCellContent; 1.2611 + return result; 1.2612 + } 1.2613 + 1.2614 +#if 0 1.2615 +// This is a more efficient strategy that appends row to current selection, 1.2616 +// but doesn't allow dragging OFF of an existing selection to unselect! 1.2617 + do { 1.2618 + // Loop through all cells in column or row 1.2619 + result = tableLayout->GetCellDataAt(rowIndex, colIndex, 1.2620 + getter_AddRefs(cellElement), 1.2621 + curRowIndex, curColIndex, 1.2622 + rowSpan, colSpan, 1.2623 + actualRowSpan, actualColSpan, 1.2624 + isSelected); 1.2625 + if (NS_FAILED(result)) return result; 1.2626 + // We're done when cell is not found 1.2627 + if (!cellElement) break; 1.2628 + 1.2629 + 1.2630 + // Check spans else we infinitely loop 1.2631 + NS_ASSERTION(actualColSpan, "actualColSpan is 0!"); 1.2632 + NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!"); 1.2633 + 1.2634 + // Skip cells that are already selected or span from outside our region 1.2635 + if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex) 1.2636 + { 1.2637 + result = SelectCellElement(cellElement); 1.2638 + if (NS_FAILED(result)) return result; 1.2639 + } 1.2640 + // Move to next row or column in cellmap, skipping spanned locations 1.2641 + if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW) 1.2642 + colIndex += actualColSpan; 1.2643 + else 1.2644 + rowIndex += actualRowSpan; 1.2645 + } 1.2646 + while (cellElement); 1.2647 +#endif 1.2648 + 1.2649 + return NS_OK; 1.2650 +} 1.2651 + 1.2652 +nsIContent* 1.2653 +nsFrameSelection::GetFirstCellNodeInRange(nsRange *aRange) const 1.2654 +{ 1.2655 + if (!aRange) return nullptr; 1.2656 + 1.2657 + nsINode* startParent = aRange->GetStartParent(); 1.2658 + if (!startParent) 1.2659 + return nullptr; 1.2660 + 1.2661 + int32_t offset = aRange->StartOffset(); 1.2662 + 1.2663 + nsIContent* childContent = startParent->GetChildAt(offset); 1.2664 + if (!childContent) 1.2665 + return nullptr; 1.2666 + // Don't return node if not a cell 1.2667 + if (!IsCell(childContent)) 1.2668 + return nullptr; 1.2669 + 1.2670 + return childContent; 1.2671 +} 1.2672 + 1.2673 +nsRange* 1.2674 +nsFrameSelection::GetFirstCellRange() 1.2675 +{ 1.2676 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.2677 + if (!mDomSelections[index]) 1.2678 + return nullptr; 1.2679 + 1.2680 + nsRange* firstRange = mDomSelections[index]->GetRangeAt(0); 1.2681 + if (!GetFirstCellNodeInRange(firstRange)) { 1.2682 + return nullptr; 1.2683 + } 1.2684 + 1.2685 + // Setup for next cell 1.2686 + mSelectedCellIndex = 1; 1.2687 + 1.2688 + return firstRange; 1.2689 +} 1.2690 + 1.2691 +nsRange* 1.2692 +nsFrameSelection::GetNextCellRange() 1.2693 +{ 1.2694 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.2695 + if (!mDomSelections[index]) 1.2696 + return nullptr; 1.2697 + 1.2698 + nsRange* range = mDomSelections[index]->GetRangeAt(mSelectedCellIndex); 1.2699 + 1.2700 + // Get first node in next range of selection - test if it's a cell 1.2701 + if (!GetFirstCellNodeInRange(range)) { 1.2702 + return nullptr; 1.2703 + } 1.2704 + 1.2705 + // Setup for next cell 1.2706 + mSelectedCellIndex++; 1.2707 + 1.2708 + return range; 1.2709 +} 1.2710 + 1.2711 +nsresult 1.2712 +nsFrameSelection::GetCellIndexes(nsIContent *aCell, 1.2713 + int32_t &aRowIndex, 1.2714 + int32_t &aColIndex) 1.2715 +{ 1.2716 + if (!aCell) return NS_ERROR_NULL_POINTER; 1.2717 + 1.2718 + aColIndex=0; // initialize out params 1.2719 + aRowIndex=0; 1.2720 + 1.2721 + nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell); 1.2722 + if (!cellLayoutObject) return NS_ERROR_FAILURE; 1.2723 + return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex); 1.2724 +} 1.2725 + 1.2726 +nsIContent* 1.2727 +nsFrameSelection::IsInSameTable(nsIContent *aContent1, 1.2728 + nsIContent *aContent2) const 1.2729 +{ 1.2730 + if (!aContent1 || !aContent2) return nullptr; 1.2731 + 1.2732 + nsIContent* tableNode1 = GetParentTable(aContent1); 1.2733 + nsIContent* tableNode2 = GetParentTable(aContent2); 1.2734 + 1.2735 + // Must be in the same table. Note that we want to return false for 1.2736 + // the test if both tables are null. 1.2737 + return (tableNode1 == tableNode2) ? tableNode1 : nullptr; 1.2738 +} 1.2739 + 1.2740 +nsIContent* 1.2741 +nsFrameSelection::GetParentTable(nsIContent *aCell) const 1.2742 +{ 1.2743 + if (!aCell) { 1.2744 + return nullptr; 1.2745 + } 1.2746 + 1.2747 + for (nsIContent* parent = aCell->GetParent(); parent; 1.2748 + parent = parent->GetParent()) { 1.2749 + if (parent->Tag() == nsGkAtoms::table && 1.2750 + parent->IsHTML()) { 1.2751 + return parent; 1.2752 + } 1.2753 + } 1.2754 + 1.2755 + return nullptr; 1.2756 +} 1.2757 + 1.2758 +nsresult 1.2759 +nsFrameSelection::SelectCellElement(nsIContent *aCellElement) 1.2760 +{ 1.2761 + nsIContent *parent = aCellElement->GetParent(); 1.2762 + 1.2763 + // Get child offset 1.2764 + int32_t offset = parent->IndexOf(aCellElement); 1.2765 + 1.2766 + return CreateAndAddRange(parent, offset); 1.2767 +} 1.2768 + 1.2769 +nsresult 1.2770 +Selection::getTableCellLocationFromRange(nsRange* aRange, 1.2771 + int32_t* aSelectionType, 1.2772 + int32_t* aRow, int32_t* aCol) 1.2773 +{ 1.2774 + if (!aRange || !aSelectionType || !aRow || !aCol) 1.2775 + return NS_ERROR_NULL_POINTER; 1.2776 + 1.2777 + *aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE; 1.2778 + *aRow = 0; 1.2779 + *aCol = 0; 1.2780 + 1.2781 + // Must have access to frame selection to get cell info 1.2782 + if (!mFrameSelection) return NS_OK; 1.2783 + 1.2784 + nsresult result = GetTableSelectionType(aRange, aSelectionType); 1.2785 + if (NS_FAILED(result)) return result; 1.2786 + 1.2787 + // Don't fail if range does not point to a single table cell, 1.2788 + // let aSelectionType tell user if we don't have a cell 1.2789 + if (*aSelectionType != nsISelectionPrivate::TABLESELECTION_CELL) 1.2790 + return NS_OK; 1.2791 + 1.2792 + // Get the child content (the cell) pointed to by starting node of range 1.2793 + // We do minimal checking since GetTableSelectionType assures 1.2794 + // us that this really is a table cell 1.2795 + nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent()); 1.2796 + if (!content) 1.2797 + return NS_ERROR_FAILURE; 1.2798 + 1.2799 + nsIContent *child = content->GetChildAt(aRange->StartOffset()); 1.2800 + if (!child) 1.2801 + return NS_ERROR_FAILURE; 1.2802 + 1.2803 + //Note: This is a non-ref-counted pointer to the frame 1.2804 + nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child); 1.2805 + if (NS_FAILED(result)) 1.2806 + return result; 1.2807 + if (!cellLayout) 1.2808 + return NS_ERROR_FAILURE; 1.2809 + 1.2810 + return cellLayout->GetCellIndexes(*aRow, *aCol); 1.2811 +} 1.2812 + 1.2813 +nsresult 1.2814 +Selection::addTableCellRange(nsRange* aRange, bool* aDidAddRange, 1.2815 + int32_t* aOutIndex) 1.2816 +{ 1.2817 + if (!aDidAddRange || !aOutIndex) 1.2818 + return NS_ERROR_NULL_POINTER; 1.2819 + 1.2820 + *aDidAddRange = false; 1.2821 + *aOutIndex = -1; 1.2822 + 1.2823 + if (!mFrameSelection) 1.2824 + return NS_OK; 1.2825 + 1.2826 + if (!aRange) 1.2827 + return NS_ERROR_NULL_POINTER; 1.2828 + 1.2829 + nsresult result; 1.2830 + 1.2831 + // Get if we are adding a cell selection and the row, col of cell if we are 1.2832 + int32_t newRow, newCol, tableMode; 1.2833 + result = getTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol); 1.2834 + if (NS_FAILED(result)) return result; 1.2835 + 1.2836 + // If not adding a cell range, we are done here 1.2837 + if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL) 1.2838 + { 1.2839 + mFrameSelection->mSelectingTableCellMode = tableMode; 1.2840 + // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed 1.2841 + return NS_OK; 1.2842 + } 1.2843 + 1.2844 + // Set frame selection mode only if not already set to a table mode 1.2845 + // so we don't lose the select row and column flags (not detected by getTableCellLocation) 1.2846 + if (mFrameSelection->mSelectingTableCellMode == TABLESELECTION_NONE) 1.2847 + mFrameSelection->mSelectingTableCellMode = tableMode; 1.2848 + 1.2849 + *aDidAddRange = true; 1.2850 + return AddItem(aRange, aOutIndex); 1.2851 +} 1.2852 + 1.2853 +//TODO: Figure out TABLESELECTION_COLUMN and TABLESELECTION_ALLCELLS 1.2854 +nsresult 1.2855 +Selection::GetTableSelectionType(nsIDOMRange* aDOMRange, 1.2856 + int32_t* aTableSelectionType) 1.2857 +{ 1.2858 + if (!aDOMRange || !aTableSelectionType) 1.2859 + return NS_ERROR_NULL_POINTER; 1.2860 + nsRange* range = static_cast<nsRange*>(aDOMRange); 1.2861 + 1.2862 + *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_NONE; 1.2863 + 1.2864 + // Must have access to frame selection to get cell info 1.2865 + if(!mFrameSelection) return NS_OK; 1.2866 + 1.2867 + nsINode* startNode = range->GetStartParent(); 1.2868 + if (!startNode) return NS_ERROR_FAILURE; 1.2869 + 1.2870 + nsINode* endNode = range->GetEndParent(); 1.2871 + if (!endNode) return NS_ERROR_FAILURE; 1.2872 + 1.2873 + // Not a single selected node 1.2874 + if (startNode != endNode) return NS_OK; 1.2875 + 1.2876 + int32_t startOffset = range->StartOffset(); 1.2877 + int32_t endOffset = range->EndOffset(); 1.2878 + 1.2879 + // Not a single selected node 1.2880 + if ((endOffset - startOffset) != 1) 1.2881 + return NS_OK; 1.2882 + 1.2883 + nsIContent* startContent = static_cast<nsIContent*>(startNode); 1.2884 + if (!(startNode->IsElement() && startContent->IsHTML())) { 1.2885 + // Implies a check for being an element; if we ever make this work 1.2886 + // for non-HTML, need to keep checking for elements. 1.2887 + return NS_OK; 1.2888 + } 1.2889 + 1.2890 + nsIAtom *tag = startContent->Tag(); 1.2891 + 1.2892 + if (tag == nsGkAtoms::tr) 1.2893 + { 1.2894 + *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL; 1.2895 + } 1.2896 + else //check to see if we are selecting a table or row (column and all cells not done yet) 1.2897 + { 1.2898 + nsIContent *child = startNode->GetChildAt(startOffset); 1.2899 + if (!child) 1.2900 + return NS_ERROR_FAILURE; 1.2901 + 1.2902 + tag = child->Tag(); 1.2903 + 1.2904 + if (tag == nsGkAtoms::table) 1.2905 + *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE; 1.2906 + else if (tag == nsGkAtoms::tr) 1.2907 + *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW; 1.2908 + } 1.2909 + 1.2910 + return NS_OK; 1.2911 +} 1.2912 + 1.2913 +nsresult 1.2914 +nsFrameSelection::CreateAndAddRange(nsINode *aParentNode, int32_t aOffset) 1.2915 +{ 1.2916 + if (!aParentNode) return NS_ERROR_NULL_POINTER; 1.2917 + 1.2918 + nsRefPtr<nsRange> range = new nsRange(aParentNode); 1.2919 + 1.2920 + // Set range around child at given offset 1.2921 + nsresult result = range->SetStart(aParentNode, aOffset); 1.2922 + if (NS_FAILED(result)) return result; 1.2923 + result = range->SetEnd(aParentNode, aOffset+1); 1.2924 + if (NS_FAILED(result)) return result; 1.2925 + 1.2926 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.2927 + if (!mDomSelections[index]) 1.2928 + return NS_ERROR_NULL_POINTER; 1.2929 + 1.2930 + return mDomSelections[index]->AddRange(range); 1.2931 +} 1.2932 + 1.2933 +// End of Table Selection 1.2934 + 1.2935 +void 1.2936 +nsFrameSelection::SetAncestorLimiter(nsIContent *aLimiter) 1.2937 +{ 1.2938 + if (mAncestorLimiter != aLimiter) { 1.2939 + mAncestorLimiter = aLimiter; 1.2940 + int8_t index = 1.2941 + GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.2942 + if (!mDomSelections[index]) 1.2943 + return; 1.2944 + 1.2945 + if (!IsValidSelectionPoint(this, mDomSelections[index]->GetFocusNode())) { 1.2946 + ClearNormalSelection(); 1.2947 + if (mAncestorLimiter) { 1.2948 + PostReason(nsISelectionListener::NO_REASON); 1.2949 + TakeFocus(mAncestorLimiter, 0, 0, HINTLEFT, false, false); 1.2950 + } 1.2951 + } 1.2952 + } 1.2953 +} 1.2954 + 1.2955 +//END nsFrameSelection methods 1.2956 + 1.2957 + 1.2958 +//BEGIN nsISelection interface implementations 1.2959 + 1.2960 + 1.2961 + 1.2962 +nsresult 1.2963 +nsFrameSelection::DeleteFromDocument() 1.2964 +{ 1.2965 + nsresult res; 1.2966 + 1.2967 + // If we're already collapsed, then we do nothing (bug 719503). 1.2968 + bool isCollapsed; 1.2969 + int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); 1.2970 + if (!mDomSelections[index]) 1.2971 + return NS_ERROR_NULL_POINTER; 1.2972 + 1.2973 + mDomSelections[index]->GetIsCollapsed( &isCollapsed); 1.2974 + if (isCollapsed) 1.2975 + { 1.2976 + return NS_OK; 1.2977 + } 1.2978 + 1.2979 + nsRefPtr<Selection> selection = mDomSelections[index]; 1.2980 + for (int32_t rangeIdx = 0; rangeIdx < selection->GetRangeCount(); ++rangeIdx) { 1.2981 + nsRefPtr<nsRange> range = selection->GetRangeAt(rangeIdx); 1.2982 + res = range->DeleteContents(); 1.2983 + if (NS_FAILED(res)) 1.2984 + return res; 1.2985 + } 1.2986 + 1.2987 + // Collapse to the new location. 1.2988 + // If we deleted one character, then we move back one element. 1.2989 + // FIXME We don't know how to do this past frame boundaries yet. 1.2990 + if (isCollapsed) 1.2991 + mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->AnchorOffset()-1); 1.2992 + else if (mDomSelections[index]->AnchorOffset() > 0) 1.2993 + mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->AnchorOffset()); 1.2994 +#ifdef DEBUG 1.2995 + else 1.2996 + printf("Don't know how to set selection back past frame boundary\n"); 1.2997 +#endif 1.2998 + 1.2999 + return NS_OK; 1.3000 +} 1.3001 + 1.3002 +void 1.3003 +nsFrameSelection::SetDelayedCaretData(WidgetMouseEvent* aMouseEvent) 1.3004 +{ 1.3005 + if (aMouseEvent) { 1.3006 + mDelayedMouseEventValid = true; 1.3007 + mDelayedMouseEventIsShift = aMouseEvent->IsShift(); 1.3008 + mDelayedMouseEventClickCount = aMouseEvent->clickCount; 1.3009 + } else { 1.3010 + mDelayedMouseEventValid = false; 1.3011 + } 1.3012 +} 1.3013 + 1.3014 +void 1.3015 +nsFrameSelection::DisconnectFromPresShell() 1.3016 +{ 1.3017 + StopAutoScrollTimer(); 1.3018 + for (int32_t i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; i++) { 1.3019 + mDomSelections[i]->Clear(nullptr); 1.3020 + } 1.3021 + mShell = nullptr; 1.3022 +} 1.3023 + 1.3024 +//END nsISelection interface implementations 1.3025 + 1.3026 +#if 0 1.3027 +#pragma mark - 1.3028 +#endif 1.3029 + 1.3030 +// mozilla::dom::Selection implementation 1.3031 + 1.3032 +// note: this can return a nil anchor node 1.3033 + 1.3034 +Selection::Selection() 1.3035 + : mCachedOffsetForFrame(nullptr) 1.3036 + , mDirection(eDirNext) 1.3037 + , mType(nsISelectionController::SELECTION_NORMAL) 1.3038 +{ 1.3039 + SetIsDOMBinding(); 1.3040 +} 1.3041 + 1.3042 +Selection::Selection(nsFrameSelection* aList) 1.3043 + : mFrameSelection(aList) 1.3044 + , mCachedOffsetForFrame(nullptr) 1.3045 + , mDirection(eDirNext) 1.3046 + , mType(nsISelectionController::SELECTION_NORMAL) 1.3047 +{ 1.3048 + SetIsDOMBinding(); 1.3049 +} 1.3050 + 1.3051 +Selection::~Selection() 1.3052 +{ 1.3053 + setAnchorFocusRange(-1); 1.3054 + 1.3055 + uint32_t count = mRanges.Length(); 1.3056 + for (uint32_t i = 0; i < count; ++i) { 1.3057 + mRanges[i].mRange->SetInSelection(false); 1.3058 + } 1.3059 + 1.3060 + if (mAutoScrollTimer) { 1.3061 + mAutoScrollTimer->Stop(); 1.3062 + mAutoScrollTimer = nullptr; 1.3063 + } 1.3064 + 1.3065 + mScrollEvent.Revoke(); 1.3066 + 1.3067 + if (mCachedOffsetForFrame) { 1.3068 + delete mCachedOffsetForFrame; 1.3069 + mCachedOffsetForFrame = nullptr; 1.3070 + } 1.3071 +} 1.3072 + 1.3073 +nsIDocument* 1.3074 +Selection::GetParentObject() const 1.3075 +{ 1.3076 + nsIPresShell* shell = GetPresShell(); 1.3077 + if (shell) { 1.3078 + return shell->GetDocument(); 1.3079 + } 1.3080 + return nullptr; 1.3081 +} 1.3082 + 1.3083 +NS_IMPL_CYCLE_COLLECTION_CLASS(Selection) 1.3084 + 1.3085 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Selection) 1.3086 + // Unlink the selection listeners *before* we do RemoveAllRanges since 1.3087 + // we don't want to notify the listeners during JS GC (they could be 1.3088 + // in JS!). 1.3089 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionListeners) 1.3090 + tmp->RemoveAllRanges(); 1.3091 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameSelection) 1.3092 + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 1.3093 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.3094 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Selection) 1.3095 + { 1.3096 + uint32_t i, count = tmp->mRanges.Length(); 1.3097 + for (i = 0; i < count; ++i) { 1.3098 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRanges[i].mRange) 1.3099 + } 1.3100 + } 1.3101 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorFocusRange) 1.3102 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameSelection) 1.3103 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionListeners) 1.3104 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.3105 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.3106 +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Selection) 1.3107 + 1.3108 +DOMCI_DATA(Selection, Selection) 1.3109 + 1.3110 +// QueryInterface implementation for Selection 1.3111 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Selection) 1.3112 + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1.3113 + NS_INTERFACE_MAP_ENTRY(nsISelection) 1.3114 + NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate) 1.3115 + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 1.3116 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection) 1.3117 +NS_INTERFACE_MAP_END 1.3118 + 1.3119 +NS_IMPL_CYCLE_COLLECTING_ADDREF(Selection) 1.3120 +NS_IMPL_CYCLE_COLLECTING_RELEASE(Selection) 1.3121 + 1.3122 + 1.3123 +NS_IMETHODIMP 1.3124 +Selection::GetAnchorNode(nsIDOMNode** aAnchorNode) 1.3125 +{ 1.3126 + nsINode* anchorNode = GetAnchorNode(); 1.3127 + if (anchorNode) { 1.3128 + return CallQueryInterface(anchorNode, aAnchorNode); 1.3129 + } 1.3130 + 1.3131 + *aAnchorNode = nullptr; 1.3132 + return NS_OK; 1.3133 +} 1.3134 + 1.3135 +nsINode* 1.3136 +Selection::GetAnchorNode() 1.3137 +{ 1.3138 + if (!mAnchorFocusRange) 1.3139 + return nullptr; 1.3140 + 1.3141 + if (GetDirection() == eDirNext) { 1.3142 + return mAnchorFocusRange->GetStartParent(); 1.3143 + } 1.3144 + 1.3145 + return mAnchorFocusRange->GetEndParent(); 1.3146 +} 1.3147 + 1.3148 +NS_IMETHODIMP 1.3149 +Selection::GetAnchorOffset(int32_t* aAnchorOffset) 1.3150 +{ 1.3151 + *aAnchorOffset = static_cast<int32_t>(AnchorOffset()); 1.3152 + return NS_OK; 1.3153 +} 1.3154 + 1.3155 +// note: this can return a nil focus node 1.3156 +NS_IMETHODIMP 1.3157 +Selection::GetFocusNode(nsIDOMNode** aFocusNode) 1.3158 +{ 1.3159 + nsINode* focusNode = GetFocusNode(); 1.3160 + if (focusNode) { 1.3161 + return CallQueryInterface(focusNode, aFocusNode); 1.3162 + } 1.3163 + 1.3164 + *aFocusNode = nullptr; 1.3165 + return NS_OK; 1.3166 +} 1.3167 + 1.3168 +nsINode* 1.3169 +Selection::GetFocusNode() 1.3170 +{ 1.3171 + if (!mAnchorFocusRange) 1.3172 + return nullptr; 1.3173 + 1.3174 + if (GetDirection() == eDirNext){ 1.3175 + return mAnchorFocusRange->GetEndParent(); 1.3176 + } 1.3177 + 1.3178 + return mAnchorFocusRange->GetStartParent(); 1.3179 +} 1.3180 + 1.3181 +NS_IMETHODIMP 1.3182 +Selection::GetFocusOffset(int32_t* aFocusOffset) 1.3183 +{ 1.3184 + *aFocusOffset = static_cast<int32_t>(FocusOffset()); 1.3185 + return NS_OK; 1.3186 +} 1.3187 + 1.3188 +void 1.3189 +Selection::setAnchorFocusRange(int32_t indx) 1.3190 +{ 1.3191 + if (indx >= (int32_t)mRanges.Length()) 1.3192 + return; 1.3193 + if (indx < 0) //release all 1.3194 + { 1.3195 + mAnchorFocusRange = nullptr; 1.3196 + } 1.3197 + else{ 1.3198 + mAnchorFocusRange = mRanges[indx].mRange; 1.3199 + } 1.3200 +} 1.3201 + 1.3202 +uint32_t 1.3203 +Selection::AnchorOffset() 1.3204 +{ 1.3205 + if (!mAnchorFocusRange) 1.3206 + return 0; 1.3207 + 1.3208 + if (GetDirection() == eDirNext){ 1.3209 + return mAnchorFocusRange->StartOffset(); 1.3210 + } 1.3211 + 1.3212 + return mAnchorFocusRange->EndOffset(); 1.3213 +} 1.3214 + 1.3215 +uint32_t 1.3216 +Selection::FocusOffset() 1.3217 +{ 1.3218 + if (!mAnchorFocusRange) 1.3219 + return 0; 1.3220 + 1.3221 + if (GetDirection() == eDirNext){ 1.3222 + return mAnchorFocusRange->EndOffset(); 1.3223 + } 1.3224 + 1.3225 + return mAnchorFocusRange->StartOffset(); 1.3226 +} 1.3227 + 1.3228 +static nsresult 1.3229 +CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset, 1.3230 + nsRange* aRange, int32_t* aCmp) 1.3231 +{ 1.3232 + nsINode* start = aRange->GetStartParent(); 1.3233 + NS_ENSURE_STATE(aCompareNode && start); 1.3234 + // If the nodes that we're comparing are not in the same document, 1.3235 + // assume that aCompareNode will fall at the end of the ranges. 1.3236 + if (aCompareNode->GetCurrentDoc() != start->GetCurrentDoc() || 1.3237 + !start->GetCurrentDoc()) { 1.3238 + *aCmp = 1; 1.3239 + } else { 1.3240 + *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset, 1.3241 + start, aRange->StartOffset()); 1.3242 + } 1.3243 + return NS_OK; 1.3244 +} 1.3245 + 1.3246 +static nsresult 1.3247 +CompareToRangeEnd(nsINode* aCompareNode, int32_t aCompareOffset, 1.3248 + nsRange* aRange, int32_t* aCmp) 1.3249 +{ 1.3250 + nsINode* end = aRange->GetEndParent(); 1.3251 + NS_ENSURE_STATE(aCompareNode && end); 1.3252 + // If the nodes that we're comparing are not in the same document, 1.3253 + // assume that aCompareNode will fall at the end of the ranges. 1.3254 + if (aCompareNode->GetCurrentDoc() != end->GetCurrentDoc() || 1.3255 + !end->GetCurrentDoc()) { 1.3256 + *aCmp = 1; 1.3257 + } else { 1.3258 + *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset, 1.3259 + end, aRange->EndOffset()); 1.3260 + } 1.3261 + return NS_OK; 1.3262 +} 1.3263 + 1.3264 +// Selection::FindInsertionPoint 1.3265 +// 1.3266 +// Binary searches the given sorted array of ranges for the insertion point 1.3267 +// for the given node/offset. The given comparator is used, and the index 1.3268 +// where the point should appear in the array is placed in *aInsertionPoint. 1.3269 +// 1.3270 +// If there is an item in the array equal to the input point, we will return 1.3271 +// the index of this item. 1.3272 + 1.3273 +nsresult 1.3274 +Selection::FindInsertionPoint( 1.3275 + nsTArray<RangeData>* aElementArray, 1.3276 + nsINode* aPointNode, int32_t aPointOffset, 1.3277 + nsresult (*aComparator)(nsINode*,int32_t,nsRange*,int32_t*), 1.3278 + int32_t* aPoint) 1.3279 +{ 1.3280 + *aPoint = 0; 1.3281 + int32_t beginSearch = 0; 1.3282 + int32_t endSearch = aElementArray->Length(); // one beyond what to check 1.3283 + 1.3284 + if (endSearch) { 1.3285 + int32_t center = endSearch - 1; // Check last index, then binary search 1.3286 + do { 1.3287 + nsRange* range = (*aElementArray)[center].mRange; 1.3288 + 1.3289 + int32_t cmp; 1.3290 + nsresult rv = aComparator(aPointNode, aPointOffset, range, &cmp); 1.3291 + NS_ENSURE_SUCCESS(rv, rv); 1.3292 + 1.3293 + if (cmp < 0) { // point < cur 1.3294 + endSearch = center; 1.3295 + } else if (cmp > 0) { // point > cur 1.3296 + beginSearch = center + 1; 1.3297 + } else { // found match, done 1.3298 + beginSearch = center; 1.3299 + break; 1.3300 + } 1.3301 + center = (endSearch - beginSearch) / 2 + beginSearch; 1.3302 + } while (endSearch - beginSearch > 0); 1.3303 + } 1.3304 + 1.3305 + *aPoint = beginSearch; 1.3306 + return NS_OK; 1.3307 +} 1.3308 + 1.3309 +// Selection::SubtractRange 1.3310 +// 1.3311 +// A helper function that subtracts aSubtract from aRange, and adds 1.3312 +// 1 or 2 RangeData objects representing the remaining non-overlapping 1.3313 +// difference to aOutput. It is assumed that the caller has checked that 1.3314 +// aRange and aSubtract do indeed overlap 1.3315 + 1.3316 +nsresult 1.3317 +Selection::SubtractRange(RangeData* aRange, nsRange* aSubtract, 1.3318 + nsTArray<RangeData>* aOutput) 1.3319 +{ 1.3320 + nsRange* range = aRange->mRange; 1.3321 + 1.3322 + // First we want to compare to the range start 1.3323 + int32_t cmp; 1.3324 + nsresult rv = CompareToRangeStart(range->GetStartParent(), 1.3325 + range->StartOffset(), 1.3326 + aSubtract, &cmp); 1.3327 + NS_ENSURE_SUCCESS(rv, rv); 1.3328 + 1.3329 + // Also, make a comparison to the range end 1.3330 + int32_t cmp2; 1.3331 + rv = CompareToRangeEnd(range->GetEndParent(), 1.3332 + range->EndOffset(), 1.3333 + aSubtract, &cmp2); 1.3334 + NS_ENSURE_SUCCESS(rv, rv); 1.3335 + 1.3336 + // If the existing range left overlaps the new range (aSubtract) then 1.3337 + // cmp < 0, and cmp2 < 0 1.3338 + // If it right overlaps the new range then cmp > 0 and cmp2 > 0 1.3339 + // If it fully contains the new range, then cmp < 0 and cmp2 > 0 1.3340 + 1.3341 + if (cmp2 > 0) { 1.3342 + // We need to add a new RangeData to the output, running from 1.3343 + // the end of aSubtract to the end of range 1.3344 + nsRefPtr<nsRange> postOverlap = new nsRange(aSubtract->GetEndParent()); 1.3345 + 1.3346 + rv = 1.3347 + postOverlap->SetStart(aSubtract->GetEndParent(), aSubtract->EndOffset()); 1.3348 + NS_ENSURE_SUCCESS(rv, rv); 1.3349 + rv = 1.3350 + postOverlap->SetEnd(range->GetEndParent(), range->EndOffset()); 1.3351 + NS_ENSURE_SUCCESS(rv, rv); 1.3352 + if (!postOverlap->Collapsed()) { 1.3353 + if (!aOutput->InsertElementAt(0, RangeData(postOverlap))) 1.3354 + return NS_ERROR_OUT_OF_MEMORY; 1.3355 + (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle; 1.3356 + } 1.3357 + } 1.3358 + 1.3359 + if (cmp < 0) { 1.3360 + // We need to add a new RangeData to the output, running from 1.3361 + // the start of the range to the start of aSubtract 1.3362 + nsRefPtr<nsRange> preOverlap = new nsRange(range->GetStartParent()); 1.3363 + 1.3364 + nsresult rv = 1.3365 + preOverlap->SetStart(range->GetStartParent(), range->StartOffset()); 1.3366 + NS_ENSURE_SUCCESS(rv, rv); 1.3367 + rv = 1.3368 + preOverlap->SetEnd(aSubtract->GetStartParent(), aSubtract->StartOffset()); 1.3369 + NS_ENSURE_SUCCESS(rv, rv); 1.3370 + 1.3371 + if (!preOverlap->Collapsed()) { 1.3372 + if (!aOutput->InsertElementAt(0, RangeData(preOverlap))) 1.3373 + return NS_ERROR_OUT_OF_MEMORY; 1.3374 + (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle; 1.3375 + } 1.3376 + } 1.3377 + 1.3378 + return NS_OK; 1.3379 +} 1.3380 + 1.3381 +nsresult 1.3382 +Selection::AddItem(nsRange* aItem, int32_t* aOutIndex) 1.3383 +{ 1.3384 + if (!aItem) 1.3385 + return NS_ERROR_NULL_POINTER; 1.3386 + if (!aItem->IsPositioned()) 1.3387 + return NS_ERROR_UNEXPECTED; 1.3388 + 1.3389 + NS_ASSERTION(aOutIndex, "aOutIndex can't be null"); 1.3390 + 1.3391 + *aOutIndex = -1; 1.3392 + 1.3393 + // a common case is that we have no ranges yet 1.3394 + if (mRanges.Length() == 0) { 1.3395 + if (!mRanges.AppendElement(RangeData(aItem))) 1.3396 + return NS_ERROR_OUT_OF_MEMORY; 1.3397 + aItem->SetInSelection(true); 1.3398 + 1.3399 + *aOutIndex = 0; 1.3400 + return NS_OK; 1.3401 + } 1.3402 + 1.3403 + int32_t startIndex, endIndex; 1.3404 + nsresult rv = GetIndicesForInterval(aItem->GetStartParent(), 1.3405 + aItem->StartOffset(), 1.3406 + aItem->GetEndParent(), 1.3407 + aItem->EndOffset(), false, 1.3408 + &startIndex, &endIndex); 1.3409 + NS_ENSURE_SUCCESS(rv, rv); 1.3410 + 1.3411 + if (endIndex == -1) { 1.3412 + // All ranges start after the given range. We can insert our range at 1.3413 + // position 0, knowing there are no overlaps (handled below) 1.3414 + startIndex = 0; 1.3415 + endIndex = 0; 1.3416 + } else if (startIndex == -1) { 1.3417 + // All ranges end before the given range. We can insert our range at 1.3418 + // the end of the array, knowing there are no overlaps (handled below) 1.3419 + startIndex = mRanges.Length(); 1.3420 + endIndex = startIndex; 1.3421 + } 1.3422 + 1.3423 + // If the range is already contained in mRanges, silently succeed 1.3424 + bool sameRange = EqualsRangeAtPoint(aItem->GetStartParent(), 1.3425 + aItem->StartOffset(), 1.3426 + aItem->GetEndParent(), 1.3427 + aItem->EndOffset(), startIndex); 1.3428 + if (sameRange) { 1.3429 + *aOutIndex = startIndex; 1.3430 + return NS_OK; 1.3431 + } 1.3432 + 1.3433 + if (startIndex == endIndex) { 1.3434 + // The new range doesn't overlap any existing ranges 1.3435 + if (!mRanges.InsertElementAt(startIndex, RangeData(aItem))) 1.3436 + return NS_ERROR_OUT_OF_MEMORY; 1.3437 + aItem->SetInSelection(true); 1.3438 + *aOutIndex = startIndex; 1.3439 + return NS_OK; 1.3440 + } 1.3441 + 1.3442 + // We now know that at least 1 existing range overlaps with the range that 1.3443 + // we are trying to add. In fact, the only ranges of interest are those at 1.3444 + // the two end points, startIndex and endIndex - 1 (which may point to the 1.3445 + // same range) as these may partially overlap the new range. Any ranges 1.3446 + // between these indices are fully overlapped by the new range, and so can be 1.3447 + // removed 1.3448 + nsTArray<RangeData> overlaps; 1.3449 + if (!overlaps.InsertElementAt(0, mRanges[startIndex])) 1.3450 + return NS_ERROR_OUT_OF_MEMORY; 1.3451 + 1.3452 + if (endIndex - 1 != startIndex) { 1.3453 + if (!overlaps.InsertElementAt(1, mRanges[endIndex - 1])) 1.3454 + return NS_ERROR_OUT_OF_MEMORY; 1.3455 + } 1.3456 + 1.3457 + // Remove all the overlapping ranges 1.3458 + for (int32_t i = startIndex; i < endIndex; ++i) { 1.3459 + mRanges[i].mRange->SetInSelection(false); 1.3460 + } 1.3461 + mRanges.RemoveElementsAt(startIndex, endIndex - startIndex); 1.3462 + 1.3463 + nsTArray<RangeData> temp; 1.3464 + for (int32_t i = overlaps.Length() - 1; i >= 0; i--) { 1.3465 + nsresult rv = SubtractRange(&overlaps[i], aItem, &temp); 1.3466 + NS_ENSURE_SUCCESS(rv, rv); 1.3467 + } 1.3468 + 1.3469 + // Insert the new element into our "leftovers" array 1.3470 + int32_t insertionPoint; 1.3471 + rv = FindInsertionPoint(&temp, aItem->GetStartParent(), 1.3472 + aItem->StartOffset(), CompareToRangeStart, 1.3473 + &insertionPoint); 1.3474 + NS_ENSURE_SUCCESS(rv, rv); 1.3475 + 1.3476 + if (!temp.InsertElementAt(insertionPoint, RangeData(aItem))) 1.3477 + return NS_ERROR_OUT_OF_MEMORY; 1.3478 + 1.3479 + // Merge the leftovers back in to mRanges 1.3480 + if (!mRanges.InsertElementsAt(startIndex, temp)) 1.3481 + return NS_ERROR_OUT_OF_MEMORY; 1.3482 + 1.3483 + for (uint32_t i = 0; i < temp.Length(); ++i) { 1.3484 + temp[i].mRange->SetInSelection(true); 1.3485 + } 1.3486 + 1.3487 + *aOutIndex = startIndex + insertionPoint; 1.3488 + return NS_OK; 1.3489 +} 1.3490 + 1.3491 +nsresult 1.3492 +Selection::RemoveItem(nsRange* aItem) 1.3493 +{ 1.3494 + if (!aItem) 1.3495 + return NS_ERROR_NULL_POINTER; 1.3496 + 1.3497 + // Find the range's index & remove it. We could use FindInsertionPoint to 1.3498 + // get O(log n) time, but that requires many expensive DOM comparisons. 1.3499 + // For even several thousand items, this is probably faster because the 1.3500 + // comparisons are so fast. 1.3501 + int32_t idx = -1; 1.3502 + uint32_t i; 1.3503 + for (i = 0; i < mRanges.Length(); i ++) { 1.3504 + if (mRanges[i].mRange == aItem) { 1.3505 + idx = (int32_t)i; 1.3506 + break; 1.3507 + } 1.3508 + } 1.3509 + if (idx < 0) 1.3510 + return NS_ERROR_INVALID_ARG; 1.3511 + 1.3512 + mRanges.RemoveElementAt(idx); 1.3513 + aItem->SetInSelection(false); 1.3514 + return NS_OK; 1.3515 +} 1.3516 + 1.3517 +nsresult 1.3518 +Selection::RemoveCollapsedRanges() 1.3519 +{ 1.3520 + uint32_t i = 0; 1.3521 + while (i < mRanges.Length()) { 1.3522 + if (mRanges[i].mRange->Collapsed()) { 1.3523 + nsresult rv = RemoveItem(mRanges[i].mRange); 1.3524 + NS_ENSURE_SUCCESS(rv, rv); 1.3525 + } else { 1.3526 + ++i; 1.3527 + } 1.3528 + } 1.3529 + return NS_OK; 1.3530 +} 1.3531 + 1.3532 +nsresult 1.3533 +Selection::Clear(nsPresContext* aPresContext) 1.3534 +{ 1.3535 + setAnchorFocusRange(-1); 1.3536 + 1.3537 + for (uint32_t i = 0; i < mRanges.Length(); ++i) { 1.3538 + mRanges[i].mRange->SetInSelection(false); 1.3539 + selectFrames(aPresContext, mRanges[i].mRange, false); 1.3540 + } 1.3541 + mRanges.Clear(); 1.3542 + 1.3543 + // Reset direction so for more dependable table selection range handling 1.3544 + SetDirection(eDirNext); 1.3545 + 1.3546 + // If this was an ATTENTION selection, change it back to normal now 1.3547 + if (mFrameSelection && 1.3548 + mFrameSelection->GetDisplaySelection() == 1.3549 + nsISelectionController::SELECTION_ATTENTION) { 1.3550 + mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON); 1.3551 + } 1.3552 + 1.3553 + return NS_OK; 1.3554 +} 1.3555 + 1.3556 +NS_IMETHODIMP 1.3557 +Selection::GetType(int16_t* aType) 1.3558 +{ 1.3559 + NS_ENSURE_ARG_POINTER(aType); 1.3560 + *aType = Type(); 1.3561 + 1.3562 + return NS_OK; 1.3563 +} 1.3564 + 1.3565 +// RangeMatches*Point 1.3566 +// 1.3567 +// Compares the range beginning or ending point, and returns true if it 1.3568 +// exactly matches the given DOM point. 1.3569 + 1.3570 +static inline bool 1.3571 +RangeMatchesBeginPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset) 1.3572 +{ 1.3573 + return aRange->GetStartParent() == aNode && aRange->StartOffset() == aOffset; 1.3574 +} 1.3575 + 1.3576 +static inline bool 1.3577 +RangeMatchesEndPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset) 1.3578 +{ 1.3579 + return aRange->GetEndParent() == aNode && aRange->EndOffset() == aOffset; 1.3580 +} 1.3581 + 1.3582 +// Selection::EqualsRangeAtPoint 1.3583 +// 1.3584 +// Utility method for checking equivalence of two ranges. 1.3585 + 1.3586 +bool 1.3587 +Selection::EqualsRangeAtPoint( 1.3588 + nsINode* aBeginNode, int32_t aBeginOffset, 1.3589 + nsINode* aEndNode, int32_t aEndOffset, 1.3590 + int32_t aRangeIndex) 1.3591 +{ 1.3592 + if (aRangeIndex >=0 && aRangeIndex < (int32_t) mRanges.Length()) { 1.3593 + nsRange* range = mRanges[aRangeIndex].mRange; 1.3594 + if (RangeMatchesBeginPoint(range, aBeginNode, aBeginOffset) && 1.3595 + RangeMatchesEndPoint(range, aEndNode, aEndOffset)) 1.3596 + return true; 1.3597 + } 1.3598 + return false; 1.3599 +} 1.3600 + 1.3601 +// Selection::GetRangesForInterval 1.3602 +// 1.3603 +// XPCOM wrapper for the nsTArray version 1.3604 + 1.3605 +NS_IMETHODIMP 1.3606 +Selection::GetRangesForInterval(nsIDOMNode* aBeginNode, int32_t aBeginOffset, 1.3607 + nsIDOMNode* aEndNode, int32_t aEndOffset, 1.3608 + bool aAllowAdjacent, 1.3609 + uint32_t* aResultCount, 1.3610 + nsIDOMRange*** aResults) 1.3611 +{ 1.3612 + if (!aBeginNode || ! aEndNode || ! aResultCount || ! aResults) 1.3613 + return NS_ERROR_NULL_POINTER; 1.3614 + 1.3615 + *aResultCount = 0; 1.3616 + *aResults = nullptr; 1.3617 + 1.3618 + nsTArray<nsRefPtr<nsRange>> results; 1.3619 + ErrorResult result; 1.3620 + nsCOMPtr<nsINode> beginNode = do_QueryInterface(aBeginNode); 1.3621 + nsCOMPtr<nsINode> endNode = do_QueryInterface(aEndNode); 1.3622 + NS_ENSURE_TRUE(beginNode && endNode, NS_ERROR_NULL_POINTER); 1.3623 + GetRangesForInterval(*beginNode, aBeginOffset, *endNode, aEndOffset, 1.3624 + aAllowAdjacent, results, result); 1.3625 + if (result.Failed()) { 1.3626 + return result.ErrorCode(); 1.3627 + } 1.3628 + *aResultCount = results.Length(); 1.3629 + if (*aResultCount == 0) { 1.3630 + return NS_OK; 1.3631 + } 1.3632 + 1.3633 + *aResults = static_cast<nsIDOMRange**> 1.3634 + (nsMemory::Alloc(sizeof(nsIDOMRange*) * *aResultCount)); 1.3635 + NS_ENSURE_TRUE(*aResults, NS_ERROR_OUT_OF_MEMORY); 1.3636 + 1.3637 + for (uint32_t i = 0; i < *aResultCount; i++) { 1.3638 + (*aResults)[i] = results[i].forget().take(); 1.3639 + } 1.3640 + return NS_OK; 1.3641 +} 1.3642 + 1.3643 + 1.3644 +void 1.3645 +Selection::GetRangesForInterval(nsINode& aBeginNode, int32_t aBeginOffset, 1.3646 + nsINode& aEndNode, int32_t aEndOffset, 1.3647 + bool aAllowAdjacent, 1.3648 + nsTArray<nsRefPtr<nsRange>>& aReturn, 1.3649 + mozilla::ErrorResult& aRv) 1.3650 +{ 1.3651 + nsTArray<nsRange*> results; 1.3652 + nsresult rv = GetRangesForIntervalArray(&aBeginNode, aBeginOffset, 1.3653 + &aEndNode, aEndOffset, 1.3654 + aAllowAdjacent, &results); 1.3655 + if (NS_FAILED(rv)) { 1.3656 + aRv.Throw(rv); 1.3657 + return; 1.3658 + } 1.3659 + 1.3660 + aReturn.SetLength(results.Length()); 1.3661 + for (uint32_t i = 0; i < results.Length(); ++i) { 1.3662 + aReturn[i] = results[i]; // AddRefs 1.3663 + } 1.3664 +} 1.3665 + 1.3666 +// Selection::GetRangesForIntervalArray 1.3667 +// 1.3668 +// Fills a nsTArray with the ranges overlapping the range specified by 1.3669 +// the given endpoints. Ranges in the selection exactly adjacent to the 1.3670 +// input range are not returned unless aAllowAdjacent is set. 1.3671 +// 1.3672 +// For example, if the following ranges were in the selection 1.3673 +// (assume everything is within the same node) 1.3674 +// 1.3675 +// Start Offset: 0 2 7 9 1.3676 +// End Offset: 2 5 9 10 1.3677 +// 1.3678 +// and passed aBeginOffset of 2 and aEndOffset of 9, then with 1.3679 +// aAllowAdjacent set, all the ranges should be returned. If 1.3680 +// aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only 1.3681 +// should be returned 1.3682 +// 1.3683 +// Now that overlapping ranges are disallowed, there can be a maximum of 1.3684 +// 2 adjacent ranges 1.3685 + 1.3686 +nsresult 1.3687 +Selection::GetRangesForIntervalArray(nsINode* aBeginNode, int32_t aBeginOffset, 1.3688 + nsINode* aEndNode, int32_t aEndOffset, 1.3689 + bool aAllowAdjacent, 1.3690 + nsTArray<nsRange*>* aRanges) 1.3691 +{ 1.3692 + aRanges->Clear(); 1.3693 + int32_t startIndex, endIndex; 1.3694 + nsresult res = GetIndicesForInterval(aBeginNode, aBeginOffset, 1.3695 + aEndNode, aEndOffset, aAllowAdjacent, 1.3696 + &startIndex, &endIndex); 1.3697 + NS_ENSURE_SUCCESS(res, res); 1.3698 + 1.3699 + if (startIndex == -1 || endIndex == -1) 1.3700 + return NS_OK; 1.3701 + 1.3702 + for (int32_t i = startIndex; i < endIndex; i++) { 1.3703 + if (!aRanges->AppendElement(mRanges[i].mRange)) 1.3704 + return NS_ERROR_OUT_OF_MEMORY; 1.3705 + } 1.3706 + 1.3707 + return NS_OK; 1.3708 +} 1.3709 + 1.3710 +// Selection::GetIndicesForInterval 1.3711 +// 1.3712 +// Works on the same principle as GetRangesForIntervalArray above, however 1.3713 +// instead this returns the indices into mRanges between which the 1.3714 +// overlapping ranges lie. 1.3715 + 1.3716 +nsresult 1.3717 +Selection::GetIndicesForInterval(nsINode* aBeginNode, int32_t aBeginOffset, 1.3718 + nsINode* aEndNode, int32_t aEndOffset, 1.3719 + bool aAllowAdjacent, 1.3720 + int32_t* aStartIndex, int32_t* aEndIndex) 1.3721 +{ 1.3722 + int32_t startIndex; 1.3723 + int32_t endIndex; 1.3724 + 1.3725 + if (!aStartIndex) 1.3726 + aStartIndex = &startIndex; 1.3727 + if (!aEndIndex) 1.3728 + aEndIndex = &endIndex; 1.3729 + 1.3730 + *aStartIndex = -1; 1.3731 + *aEndIndex = -1; 1.3732 + 1.3733 + if (mRanges.Length() == 0) 1.3734 + return NS_OK; 1.3735 + 1.3736 + bool intervalIsCollapsed = aBeginNode == aEndNode && 1.3737 + aBeginOffset == aEndOffset; 1.3738 + 1.3739 + // Ranges that end before the given interval and begin after the given 1.3740 + // interval can be discarded 1.3741 + int32_t endsBeforeIndex; 1.3742 + if (NS_FAILED(FindInsertionPoint(&mRanges, aEndNode, aEndOffset, 1.3743 + &CompareToRangeStart, 1.3744 + &endsBeforeIndex))) { 1.3745 + return NS_OK; 1.3746 + } 1.3747 + 1.3748 + if (endsBeforeIndex == 0) { 1.3749 + nsRange* endRange = mRanges[endsBeforeIndex].mRange; 1.3750 + 1.3751 + // If the interval is strictly before the range at index 0, we can optimize 1.3752 + // by returning now - all ranges start after the given interval 1.3753 + if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset)) 1.3754 + return NS_OK; 1.3755 + 1.3756 + // We now know that the start point of mRanges[0].mRange equals the end of 1.3757 + // the interval. Thus, when aAllowadjacent is true, the caller is always 1.3758 + // interested in this range. However, when excluding adjacencies, we must 1.3759 + // remember to include the range when both it and the given interval are 1.3760 + // collapsed to the same point 1.3761 + if (!aAllowAdjacent && !(endRange->Collapsed() && intervalIsCollapsed)) 1.3762 + return NS_OK; 1.3763 + } 1.3764 + *aEndIndex = endsBeforeIndex; 1.3765 + 1.3766 + int32_t beginsAfterIndex; 1.3767 + if (NS_FAILED(FindInsertionPoint(&mRanges, aBeginNode, aBeginOffset, 1.3768 + &CompareToRangeEnd, 1.3769 + &beginsAfterIndex))) { 1.3770 + return NS_OK; 1.3771 + } 1.3772 + if (beginsAfterIndex == (int32_t) mRanges.Length()) 1.3773 + return NS_OK; // optimization: all ranges are strictly before us 1.3774 + 1.3775 + if (aAllowAdjacent) { 1.3776 + // At this point, one of the following holds: 1.3777 + // endsBeforeIndex == mRanges.Length(), 1.3778 + // endsBeforeIndex points to a range whose start point does not equal the 1.3779 + // given interval's start point 1.3780 + // endsBeforeIndex points to a range whose start point equals the given 1.3781 + // interval's start point 1.3782 + // In the final case, there can be two such ranges, a collapsed range, and 1.3783 + // an adjacent range (they will appear in mRanges in that order). For this 1.3784 + // final case, we need to increment endsBeforeIndex, until one of the 1.3785 + // first two possibilites hold 1.3786 + while (endsBeforeIndex < (int32_t) mRanges.Length()) { 1.3787 + nsRange* endRange = mRanges[endsBeforeIndex].mRange; 1.3788 + if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset)) 1.3789 + break; 1.3790 + endsBeforeIndex++; 1.3791 + } 1.3792 + 1.3793 + // Likewise, one of the following holds: 1.3794 + // beginsAfterIndex == 0, 1.3795 + // beginsAfterIndex points to a range whose end point does not equal 1.3796 + // the given interval's end point 1.3797 + // beginsOnOrAfter points to a range whose end point equals the given 1.3798 + // interval's end point 1.3799 + // In the final case, there can be two such ranges, an adjacent range, and 1.3800 + // a collapsed range (they will appear in mRanges in that order). For this 1.3801 + // final case, we only need to take action if both those ranges exist, and 1.3802 + // we are pointing to the collapsed range - we need to point to the 1.3803 + // adjacent range 1.3804 + nsRange* beginRange = mRanges[beginsAfterIndex].mRange; 1.3805 + if (beginsAfterIndex > 0 && beginRange->Collapsed() && 1.3806 + RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset)) { 1.3807 + beginRange = mRanges[beginsAfterIndex - 1].mRange; 1.3808 + if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset)) 1.3809 + beginsAfterIndex--; 1.3810 + } 1.3811 + } else { 1.3812 + // See above for the possibilities at this point. The only case where we 1.3813 + // need to take action is when the range at beginsAfterIndex ends on 1.3814 + // the given interval's start point, but that range isn't collapsed (a 1.3815 + // collapsed range should be included in the returned results). 1.3816 + nsRange* beginRange = mRanges[beginsAfterIndex].mRange; 1.3817 + if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset) && 1.3818 + !beginRange->Collapsed()) 1.3819 + beginsAfterIndex++; 1.3820 + 1.3821 + // Again, see above for the meaning of endsBeforeIndex at this point. 1.3822 + // In particular, endsBeforeIndex may point to a collaped range which 1.3823 + // represents the point at the end of the interval - this range should be 1.3824 + // included 1.3825 + if (endsBeforeIndex < (int32_t) mRanges.Length()) { 1.3826 + nsRange* endRange = mRanges[endsBeforeIndex].mRange; 1.3827 + if (RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset) && 1.3828 + endRange->Collapsed()) 1.3829 + endsBeforeIndex++; 1.3830 + } 1.3831 + } 1.3832 + 1.3833 + NS_ASSERTION(beginsAfterIndex <= endsBeforeIndex, 1.3834 + "Is mRanges not ordered?"); 1.3835 + NS_ENSURE_STATE(beginsAfterIndex <= endsBeforeIndex); 1.3836 + 1.3837 + *aStartIndex = beginsAfterIndex; 1.3838 + *aEndIndex = endsBeforeIndex; 1.3839 + return NS_OK; 1.3840 +} 1.3841 + 1.3842 +NS_IMETHODIMP 1.3843 +Selection::GetPrimaryFrameForAnchorNode(nsIFrame** aReturnFrame) 1.3844 +{ 1.3845 + if (!aReturnFrame) 1.3846 + return NS_ERROR_NULL_POINTER; 1.3847 + 1.3848 + int32_t frameOffset = 0; 1.3849 + *aReturnFrame = 0; 1.3850 + nsCOMPtr<nsIContent> content = do_QueryInterface(GetAnchorNode()); 1.3851 + if (content && mFrameSelection) 1.3852 + { 1.3853 + *aReturnFrame = mFrameSelection-> 1.3854 + GetFrameForNodeOffset(content, AnchorOffset(), 1.3855 + mFrameSelection->GetHint(), &frameOffset); 1.3856 + if (*aReturnFrame) 1.3857 + return NS_OK; 1.3858 + } 1.3859 + return NS_ERROR_FAILURE; 1.3860 +} 1.3861 + 1.3862 +NS_IMETHODIMP 1.3863 +Selection::GetPrimaryFrameForFocusNode(nsIFrame** aReturnFrame, 1.3864 + int32_t* aOffsetUsed, 1.3865 + bool aVisual) 1.3866 +{ 1.3867 + if (!aReturnFrame) 1.3868 + return NS_ERROR_NULL_POINTER; 1.3869 + 1.3870 + nsCOMPtr<nsIContent> content = do_QueryInterface(GetFocusNode()); 1.3871 + if (!content || !mFrameSelection) 1.3872 + return NS_ERROR_FAILURE; 1.3873 + 1.3874 + int32_t frameOffset = 0; 1.3875 + *aReturnFrame = 0; 1.3876 + if (!aOffsetUsed) 1.3877 + aOffsetUsed = &frameOffset; 1.3878 + 1.3879 + nsFrameSelection::HINT hint = mFrameSelection->GetHint(); 1.3880 + 1.3881 + if (aVisual) { 1.3882 + nsIPresShell *presShell = mFrameSelection->GetShell(); 1.3883 + if (!presShell) 1.3884 + return NS_ERROR_FAILURE; 1.3885 + 1.3886 + nsRefPtr<nsCaret> caret = presShell->GetCaret(); 1.3887 + if (!caret) 1.3888 + return NS_ERROR_FAILURE; 1.3889 + 1.3890 + uint8_t caretBidiLevel = mFrameSelection->GetCaretBidiLevel(); 1.3891 + 1.3892 + return caret->GetCaretFrameForNodeOffset(content, FocusOffset(), 1.3893 + hint, caretBidiLevel, aReturnFrame, aOffsetUsed); 1.3894 + } 1.3895 + 1.3896 + *aReturnFrame = mFrameSelection-> 1.3897 + GetFrameForNodeOffset(content, FocusOffset(), 1.3898 + hint, aOffsetUsed); 1.3899 + if (!*aReturnFrame) 1.3900 + return NS_ERROR_FAILURE; 1.3901 + 1.3902 + return NS_OK; 1.3903 +} 1.3904 + 1.3905 +//select all content children of aContent 1.3906 +nsresult 1.3907 +Selection::SelectAllFramesForContent(nsIContentIterator* aInnerIter, 1.3908 + nsIContent* aContent, 1.3909 + bool aSelected) 1.3910 +{ 1.3911 + nsresult result = aInnerIter->Init(aContent); 1.3912 + nsIFrame *frame; 1.3913 + if (NS_SUCCEEDED(result)) 1.3914 + { 1.3915 + // First select frame of content passed in 1.3916 + frame = aContent->GetPrimaryFrame(); 1.3917 + if (frame && frame->GetType() == nsGkAtoms::textFrame) { 1.3918 + nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame); 1.3919 + textFrame->SetSelectedRange(0, aContent->GetText()->GetLength(), aSelected, mType); 1.3920 + } 1.3921 + // Now iterated through the child frames and set them 1.3922 + while (!aInnerIter->IsDone()) { 1.3923 + nsCOMPtr<nsIContent> innercontent = 1.3924 + do_QueryInterface(aInnerIter->GetCurrentNode()); 1.3925 + 1.3926 + frame = innercontent->GetPrimaryFrame(); 1.3927 + if (frame) { 1.3928 + if (frame->GetType() == nsGkAtoms::textFrame) { 1.3929 + nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame); 1.3930 + textFrame->SetSelectedRange(0, innercontent->GetText()->GetLength(), aSelected, mType); 1.3931 + } else { 1.3932 + frame->InvalidateFrameSubtree(); // frame continuations? 1.3933 + } 1.3934 + } 1.3935 + 1.3936 + aInnerIter->Next(); 1.3937 + } 1.3938 + 1.3939 + return NS_OK; 1.3940 + } 1.3941 + 1.3942 + return NS_ERROR_FAILURE; 1.3943 +} 1.3944 + 1.3945 +/** 1.3946 + * The idea of this helper method is to select or deselect "top to bottom", 1.3947 + * traversing through the frames 1.3948 + */ 1.3949 +nsresult 1.3950 +Selection::selectFrames(nsPresContext* aPresContext, nsRange* aRange, 1.3951 + bool aSelect) 1.3952 +{ 1.3953 + if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) { 1.3954 + // nothing to do 1.3955 + return NS_OK; 1.3956 + } 1.3957 + MOZ_ASSERT(aRange); 1.3958 + 1.3959 + if (mFrameSelection->GetTableCellSelection()) { 1.3960 + nsINode* node = aRange->GetCommonAncestor(); 1.3961 + nsIFrame* frame = node->IsContent() ? node->AsContent()->GetPrimaryFrame() 1.3962 + : aPresContext->FrameManager()->GetRootFrame(); 1.3963 + if (frame) { 1.3964 + frame->InvalidateFrameSubtree(); 1.3965 + } 1.3966 + return NS_OK; 1.3967 + } 1.3968 + 1.3969 + nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator(); 1.3970 + iter->Init(aRange); 1.3971 + 1.3972 + // Loop through the content iterator for each content node; for each text 1.3973 + // node, call SetSelected on it: 1.3974 + nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent()); 1.3975 + NS_ENSURE_STATE(content); 1.3976 + 1.3977 + // We must call first one explicitly 1.3978 + if (content->IsNodeOfType(nsINode::eTEXT)) { 1.3979 + nsIFrame* frame = content->GetPrimaryFrame(); 1.3980 + // The frame could be an SVG text frame, in which case we'll ignore it. 1.3981 + if (frame && frame->GetType() == nsGkAtoms::textFrame) { 1.3982 + nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame); 1.3983 + uint32_t startOffset = aRange->StartOffset(); 1.3984 + uint32_t endOffset; 1.3985 + if (aRange->GetEndParent() == content) { 1.3986 + endOffset = aRange->EndOffset(); 1.3987 + } else { 1.3988 + endOffset = content->Length(); 1.3989 + } 1.3990 + textFrame->SetSelectedRange(startOffset, endOffset, aSelect, mType); 1.3991 + } 1.3992 + } 1.3993 + 1.3994 + iter->First(); 1.3995 + nsCOMPtr<nsIContentIterator> inneriter = NS_NewContentIterator(); 1.3996 + for (iter->First(); !iter->IsDone(); iter->Next()) { 1.3997 + content = do_QueryInterface(iter->GetCurrentNode()); 1.3998 + SelectAllFramesForContent(inneriter, content, aSelect); 1.3999 + } 1.4000 + 1.4001 + // We must now do the last one if it is not the same as the first 1.4002 + if (aRange->GetEndParent() != aRange->GetStartParent()) { 1.4003 + nsresult res; 1.4004 + content = do_QueryInterface(aRange->GetEndParent(), &res); 1.4005 + NS_ENSURE_SUCCESS(res, res); 1.4006 + NS_ENSURE_TRUE(content, res); 1.4007 + 1.4008 + if (content->IsNodeOfType(nsINode::eTEXT)) { 1.4009 + nsIFrame* frame = content->GetPrimaryFrame(); 1.4010 + // The frame could be an SVG text frame, in which case we'll ignore it. 1.4011 + if (frame && frame->GetType() == nsGkAtoms::textFrame) { 1.4012 + nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame); 1.4013 + textFrame->SetSelectedRange(0, aRange->EndOffset(), aSelect, mType); 1.4014 + } 1.4015 + } 1.4016 + } 1.4017 + return NS_OK; 1.4018 +} 1.4019 + 1.4020 + 1.4021 +// Selection::LookUpSelection 1.4022 +// 1.4023 +// This function is called when a node wants to know where the selection is 1.4024 +// over itself. 1.4025 +// 1.4026 +// Usually, this is called when we already know there is a selection over 1.4027 +// the node in question, and we only need to find the boundaries of it on 1.4028 +// that node. This is when slowCheck is false--a strict test is not needed. 1.4029 +// Other times, the caller has no idea, and wants us to test everything, 1.4030 +// so we are supposed to determine whether there is a selection over the 1.4031 +// node at all. 1.4032 +// 1.4033 +// A previous version of this code used this flag to do less work when 1.4034 +// inclusion was already known (slowCheck=false). However, our tree 1.4035 +// structure allows us to quickly determine ranges overlapping the node, 1.4036 +// so we just ignore the slowCheck flag and do the full test every time. 1.4037 +// 1.4038 +// PERFORMANCE: a common case is that we are doing a fast check with exactly 1.4039 +// one range in the selection. In this case, this function is slower than 1.4040 +// brute force because of the overhead of checking the tree. We can optimize 1.4041 +// this case to make it faster by doing the same thing the previous version 1.4042 +// of this function did in the case of 1 range. This would also mean that 1.4043 +// the aSlowCheck flag would have meaning again. 1.4044 + 1.4045 +NS_IMETHODIMP 1.4046 +Selection::LookUpSelection(nsIContent* aContent, int32_t aContentOffset, 1.4047 + int32_t aContentLength, 1.4048 + SelectionDetails** aReturnDetails, 1.4049 + SelectionType aType, bool aSlowCheck) 1.4050 +{ 1.4051 + nsresult rv; 1.4052 + if (!aContent || ! aReturnDetails) 1.4053 + return NS_ERROR_NULL_POINTER; 1.4054 + 1.4055 + // it is common to have no ranges, to optimize that 1.4056 + if (mRanges.Length() == 0) 1.4057 + return NS_OK; 1.4058 + 1.4059 + nsTArray<nsRange*> overlappingRanges; 1.4060 + rv = GetRangesForIntervalArray(aContent, aContentOffset, 1.4061 + aContent, aContentOffset + aContentLength, 1.4062 + false, 1.4063 + &overlappingRanges); 1.4064 + NS_ENSURE_SUCCESS(rv, rv); 1.4065 + if (overlappingRanges.Length() == 0) 1.4066 + return NS_OK; 1.4067 + 1.4068 + for (uint32_t i = 0; i < overlappingRanges.Length(); i++) { 1.4069 + nsRange* range = overlappingRanges[i]; 1.4070 + nsINode* startNode = range->GetStartParent(); 1.4071 + nsINode* endNode = range->GetEndParent(); 1.4072 + int32_t startOffset = range->StartOffset(); 1.4073 + int32_t endOffset = range->EndOffset(); 1.4074 + 1.4075 + int32_t start = -1, end = -1; 1.4076 + if (startNode == aContent && endNode == aContent) { 1.4077 + if (startOffset < (aContentOffset + aContentLength) && 1.4078 + endOffset > aContentOffset) { 1.4079 + // this range is totally inside the requested content range 1.4080 + start = std::max(0, startOffset - aContentOffset); 1.4081 + end = std::min(aContentLength, endOffset - aContentOffset); 1.4082 + } 1.4083 + // otherwise, range is inside the requested node, but does not intersect 1.4084 + // the requested content range, so ignore it 1.4085 + } else if (startNode == aContent) { 1.4086 + if (startOffset < (aContentOffset + aContentLength)) { 1.4087 + // the beginning of the range is inside the requested node, but the 1.4088 + // end is outside, select everything from there to the end 1.4089 + start = std::max(0, startOffset - aContentOffset); 1.4090 + end = aContentLength; 1.4091 + } 1.4092 + } else if (endNode == aContent) { 1.4093 + if (endOffset > aContentOffset) { 1.4094 + // the end of the range is inside the requested node, but the beginning 1.4095 + // is outside, select everything from the beginning to there 1.4096 + start = 0; 1.4097 + end = std::min(aContentLength, endOffset - aContentOffset); 1.4098 + } 1.4099 + } else { 1.4100 + // this range does not begin or end in the requested node, but since 1.4101 + // GetRangesForInterval returned this range, we know it overlaps. 1.4102 + // Therefore, this node is enclosed in the range, and we select all 1.4103 + // of it. 1.4104 + start = 0; 1.4105 + end = aContentLength; 1.4106 + } 1.4107 + if (start < 0) 1.4108 + continue; // the ranges do not overlap the input range 1.4109 + 1.4110 + SelectionDetails* details = new SelectionDetails; 1.4111 + 1.4112 + details->mNext = *aReturnDetails; 1.4113 + details->mStart = start; 1.4114 + details->mEnd = end; 1.4115 + details->mType = aType; 1.4116 + RangeData *rd = FindRangeData(range); 1.4117 + if (rd) { 1.4118 + details->mTextRangeStyle = rd->mTextRangeStyle; 1.4119 + } 1.4120 + *aReturnDetails = details; 1.4121 + } 1.4122 + return NS_OK; 1.4123 +} 1.4124 + 1.4125 +NS_IMETHODIMP 1.4126 +Selection::Repaint(nsPresContext* aPresContext) 1.4127 +{ 1.4128 + int32_t arrCount = (int32_t)mRanges.Length(); 1.4129 + 1.4130 + if (arrCount < 1) 1.4131 + return NS_OK; 1.4132 + 1.4133 + int32_t i; 1.4134 + 1.4135 + for (i = 0; i < arrCount; i++) 1.4136 + { 1.4137 + nsresult rv = selectFrames(aPresContext, mRanges[i].mRange, true); 1.4138 + 1.4139 + if (NS_FAILED(rv)) { 1.4140 + return rv; 1.4141 + } 1.4142 + } 1.4143 + 1.4144 + return NS_OK; 1.4145 +} 1.4146 + 1.4147 +NS_IMETHODIMP 1.4148 +Selection::GetCanCacheFrameOffset(bool* aCanCacheFrameOffset) 1.4149 +{ 1.4150 + NS_ENSURE_ARG_POINTER(aCanCacheFrameOffset); 1.4151 + 1.4152 + if (mCachedOffsetForFrame) 1.4153 + *aCanCacheFrameOffset = mCachedOffsetForFrame->mCanCacheFrameOffset; 1.4154 + else 1.4155 + *aCanCacheFrameOffset = false; 1.4156 + 1.4157 + return NS_OK; 1.4158 +} 1.4159 + 1.4160 +NS_IMETHODIMP 1.4161 +Selection::SetCanCacheFrameOffset(bool aCanCacheFrameOffset) 1.4162 +{ 1.4163 + if (!mCachedOffsetForFrame) { 1.4164 + mCachedOffsetForFrame = new CachedOffsetForFrame; 1.4165 + } 1.4166 + 1.4167 + mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset; 1.4168 + 1.4169 + // clean up cached frame when turn off cache 1.4170 + // fix bug 207936 1.4171 + if (!aCanCacheFrameOffset) { 1.4172 + mCachedOffsetForFrame->mLastCaretFrame = nullptr; 1.4173 + } 1.4174 + 1.4175 + return NS_OK; 1.4176 +} 1.4177 + 1.4178 +NS_IMETHODIMP 1.4179 +Selection::GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset, 1.4180 + nsPoint& aPoint) 1.4181 +{ 1.4182 + if (!mCachedOffsetForFrame) { 1.4183 + mCachedOffsetForFrame = new CachedOffsetForFrame; 1.4184 + } 1.4185 + 1.4186 + nsresult rv = NS_OK; 1.4187 + if (mCachedOffsetForFrame->mCanCacheFrameOffset && 1.4188 + mCachedOffsetForFrame->mLastCaretFrame && 1.4189 + (aFrame == mCachedOffsetForFrame->mLastCaretFrame) && 1.4190 + (inOffset == mCachedOffsetForFrame->mLastContentOffset)) 1.4191 + { 1.4192 + // get cached frame offset 1.4193 + aPoint = mCachedOffsetForFrame->mCachedFrameOffset; 1.4194 + } 1.4195 + else 1.4196 + { 1.4197 + // Recalculate frame offset and cache it. Don't cache a frame offset if 1.4198 + // GetPointFromOffset fails, though. 1.4199 + rv = aFrame->GetPointFromOffset(inOffset, &aPoint); 1.4200 + if (NS_SUCCEEDED(rv) && mCachedOffsetForFrame->mCanCacheFrameOffset) { 1.4201 + mCachedOffsetForFrame->mCachedFrameOffset = aPoint; 1.4202 + mCachedOffsetForFrame->mLastCaretFrame = aFrame; 1.4203 + mCachedOffsetForFrame->mLastContentOffset = inOffset; 1.4204 + } 1.4205 + } 1.4206 + 1.4207 + return rv; 1.4208 +} 1.4209 + 1.4210 +NS_IMETHODIMP 1.4211 +Selection::SetAncestorLimiter(nsIContent* aContent) 1.4212 +{ 1.4213 + if (mFrameSelection) 1.4214 + mFrameSelection->SetAncestorLimiter(aContent); 1.4215 + return NS_OK; 1.4216 +} 1.4217 + 1.4218 +RangeData* 1.4219 +Selection::FindRangeData(nsIDOMRange* aRange) 1.4220 +{ 1.4221 + NS_ENSURE_TRUE(aRange, nullptr); 1.4222 + for (uint32_t i = 0; i < mRanges.Length(); i++) { 1.4223 + if (mRanges[i].mRange == aRange) 1.4224 + return &mRanges[i]; 1.4225 + } 1.4226 + return nullptr; 1.4227 +} 1.4228 + 1.4229 +NS_IMETHODIMP 1.4230 +Selection::SetTextRangeStyle(nsIDOMRange* aRange, 1.4231 + const TextRangeStyle& aTextRangeStyle) 1.4232 +{ 1.4233 + NS_ENSURE_ARG_POINTER(aRange); 1.4234 + RangeData *rd = FindRangeData(aRange); 1.4235 + if (rd) { 1.4236 + rd->mTextRangeStyle = aTextRangeStyle; 1.4237 + } 1.4238 + return NS_OK; 1.4239 +} 1.4240 + 1.4241 +nsresult 1.4242 +Selection::StartAutoScrollTimer(nsIFrame* aFrame, nsPoint& aPoint, 1.4243 + uint32_t aDelay) 1.4244 +{ 1.4245 + NS_PRECONDITION(aFrame, "Need a frame"); 1.4246 + 1.4247 + nsresult result; 1.4248 + if (!mFrameSelection) 1.4249 + return NS_OK;//nothing to do 1.4250 + 1.4251 + if (!mAutoScrollTimer) 1.4252 + { 1.4253 + mAutoScrollTimer = new nsAutoScrollTimer(); 1.4254 + 1.4255 + result = mAutoScrollTimer->Init(mFrameSelection, this); 1.4256 + 1.4257 + if (NS_FAILED(result)) 1.4258 + return result; 1.4259 + } 1.4260 + 1.4261 + result = mAutoScrollTimer->SetDelay(aDelay); 1.4262 + 1.4263 + if (NS_FAILED(result)) 1.4264 + return result; 1.4265 + 1.4266 + return DoAutoScroll(aFrame, aPoint); 1.4267 +} 1.4268 + 1.4269 +nsresult 1.4270 +Selection::StopAutoScrollTimer() 1.4271 +{ 1.4272 + if (mAutoScrollTimer) 1.4273 + return mAutoScrollTimer->Stop(); 1.4274 + 1.4275 + return NS_OK; 1.4276 +} 1.4277 + 1.4278 +nsresult 1.4279 +Selection::DoAutoScroll(nsIFrame* aFrame, nsPoint& aPoint) 1.4280 +{ 1.4281 + NS_PRECONDITION(aFrame, "Need a frame"); 1.4282 + 1.4283 + if (mAutoScrollTimer) 1.4284 + (void)mAutoScrollTimer->Stop(); 1.4285 + 1.4286 + nsPresContext* presContext = aFrame->PresContext(); 1.4287 + nsRootPresContext* rootPC = presContext->GetRootPresContext(); 1.4288 + if (!rootPC) 1.4289 + return NS_OK; 1.4290 + nsIFrame* rootmostFrame = rootPC->PresShell()->FrameManager()->GetRootFrame(); 1.4291 + // Get the point relative to the root most frame because the scroll we are 1.4292 + // about to do will change the coordinates of aFrame. 1.4293 + nsPoint globalPoint = aPoint + aFrame->GetOffsetToCrossDoc(rootmostFrame); 1.4294 + 1.4295 + bool didScroll = presContext->PresShell()->ScrollFrameRectIntoView( 1.4296 + aFrame, 1.4297 + nsRect(aPoint, nsSize(0, 0)), 1.4298 + nsIPresShell::ScrollAxis(), 1.4299 + nsIPresShell::ScrollAxis(), 1.4300 + 0); 1.4301 + 1.4302 + // 1.4303 + // Start the AutoScroll timer if necessary. 1.4304 + // 1.4305 + 1.4306 + if (didScroll && mAutoScrollTimer) 1.4307 + { 1.4308 + nsPoint presContextPoint = globalPoint - 1.4309 + presContext->PresShell()->FrameManager()->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame); 1.4310 + mAutoScrollTimer->Start(presContext, presContextPoint); 1.4311 + } 1.4312 + 1.4313 + return NS_OK; 1.4314 +} 1.4315 + 1.4316 + 1.4317 +/** RemoveAllRanges zeroes the selection 1.4318 + */ 1.4319 +NS_IMETHODIMP 1.4320 +Selection::RemoveAllRanges() 1.4321 +{ 1.4322 + ErrorResult result; 1.4323 + RemoveAllRanges(result); 1.4324 + return result.ErrorCode(); 1.4325 +} 1.4326 + 1.4327 +void 1.4328 +Selection::RemoveAllRanges(ErrorResult& aRv) 1.4329 +{ 1.4330 + if (!mFrameSelection) 1.4331 + return; // nothing to do 1.4332 + nsRefPtr<nsPresContext> presContext = GetPresContext(); 1.4333 + nsresult result = Clear(presContext); 1.4334 + if (NS_FAILED(result)) { 1.4335 + aRv.Throw(result); 1.4336 + return; 1.4337 + } 1.4338 + 1.4339 + // Turn off signal for table selection 1.4340 + mFrameSelection->ClearTableCellSelection(); 1.4341 + 1.4342 + result = mFrameSelection->NotifySelectionListeners(GetType()); 1.4343 + // Also need to notify the frames! 1.4344 + // PresShell::CharacterDataChanged should do that on DocumentChanged 1.4345 + if (NS_FAILED(result)) { 1.4346 + aRv.Throw(result); 1.4347 + } 1.4348 +} 1.4349 + 1.4350 +/** AddRange adds the specified range to the selection 1.4351 + * @param aRange is the range to be added 1.4352 + */ 1.4353 +NS_IMETHODIMP 1.4354 +Selection::AddRange(nsIDOMRange* aDOMRange) 1.4355 +{ 1.4356 + if (!aDOMRange) { 1.4357 + return NS_ERROR_NULL_POINTER; 1.4358 + } 1.4359 + nsRange* range = static_cast<nsRange*>(aDOMRange); 1.4360 + ErrorResult result; 1.4361 + AddRange(*range, result); 1.4362 + return result.ErrorCode(); 1.4363 +} 1.4364 + 1.4365 +void 1.4366 +Selection::AddRange(nsRange& aRange, ErrorResult& aRv) 1.4367 +{ 1.4368 + // This inserts a table cell range in proper document order 1.4369 + // and returns NS_OK if range doesn't contain just one table cell 1.4370 + bool didAddRange; 1.4371 + int32_t rangeIndex; 1.4372 + nsresult result = addTableCellRange(&aRange, &didAddRange, &rangeIndex); 1.4373 + if (NS_FAILED(result)) { 1.4374 + aRv.Throw(result); 1.4375 + return; 1.4376 + } 1.4377 + 1.4378 + if (!didAddRange) 1.4379 + { 1.4380 + result = AddItem(&aRange, &rangeIndex); 1.4381 + if (NS_FAILED(result)) { 1.4382 + aRv.Throw(result); 1.4383 + return; 1.4384 + } 1.4385 + } 1.4386 + 1.4387 + NS_ASSERTION(rangeIndex >= 0, "Range index not returned"); 1.4388 + setAnchorFocusRange(rangeIndex); 1.4389 + 1.4390 + // Make sure the caret appears on the next line, if at a newline 1.4391 + if (mType == nsISelectionController::SELECTION_NORMAL) 1.4392 + SetInterlinePosition(true); 1.4393 + 1.4394 + nsRefPtr<nsPresContext> presContext = GetPresContext(); 1.4395 + selectFrames(presContext, &aRange, true); 1.4396 + 1.4397 + if (!mFrameSelection) 1.4398 + return;//nothing to do 1.4399 + 1.4400 + result = mFrameSelection->NotifySelectionListeners(GetType()); 1.4401 + if (NS_FAILED(result)) { 1.4402 + aRv.Throw(result); 1.4403 + } 1.4404 +} 1.4405 + 1.4406 +// Selection::RemoveRange 1.4407 +// 1.4408 +// Removes the given range from the selection. The tricky part is updating 1.4409 +// the flags on the frames that indicate whether they have a selection or 1.4410 +// not. There could be several selection ranges on the frame, and clearing 1.4411 +// the bit would cause the selection to not be drawn, even when there is 1.4412 +// another range on the frame (bug 346185). 1.4413 +// 1.4414 +// We therefore find any ranges that intersect the same nodes as the range 1.4415 +// being removed, and cause them to set the selected bits back on their 1.4416 +// selected frames after we've cleared the bit from ours. 1.4417 + 1.4418 +nsresult 1.4419 +Selection::RemoveRange(nsIDOMRange* aDOMRange) 1.4420 +{ 1.4421 + if (!aDOMRange) { 1.4422 + return NS_ERROR_INVALID_ARG; 1.4423 + } 1.4424 + nsRange* range = static_cast<nsRange*>(aDOMRange); 1.4425 + ErrorResult result; 1.4426 + RemoveRange(*range, result); 1.4427 + return result.ErrorCode(); 1.4428 +} 1.4429 + 1.4430 +void 1.4431 +Selection::RemoveRange(nsRange& aRange, ErrorResult& aRv) 1.4432 +{ 1.4433 + nsresult rv = RemoveItem(&aRange); 1.4434 + if (NS_FAILED(rv)) { 1.4435 + aRv.Throw(rv); 1.4436 + return; 1.4437 + } 1.4438 + 1.4439 + nsINode* beginNode = aRange.GetStartParent(); 1.4440 + nsINode* endNode = aRange.GetEndParent(); 1.4441 + 1.4442 + if (!beginNode || !endNode) { 1.4443 + // Detached range; nothing else to do here. 1.4444 + return; 1.4445 + } 1.4446 + 1.4447 + // find out the length of the end node, so we can select all of it 1.4448 + int32_t beginOffset, endOffset; 1.4449 + if (endNode->IsNodeOfType(nsINode::eTEXT)) { 1.4450 + // Get the length of the text. We can't just use the offset because 1.4451 + // another range could be touching this text node but not intersect our 1.4452 + // range. 1.4453 + beginOffset = 0; 1.4454 + endOffset = static_cast<nsIContent*>(endNode)->TextLength(); 1.4455 + } else { 1.4456 + // For non-text nodes, the given offsets should be sufficient. 1.4457 + beginOffset = aRange.StartOffset(); 1.4458 + endOffset = aRange.EndOffset(); 1.4459 + } 1.4460 + 1.4461 + // clear the selected bit from the removed range's frames 1.4462 + nsRefPtr<nsPresContext> presContext = GetPresContext(); 1.4463 + selectFrames(presContext, &aRange, false); 1.4464 + 1.4465 + // add back the selected bit for each range touching our nodes 1.4466 + nsTArray<nsRange*> affectedRanges; 1.4467 + rv = GetRangesForIntervalArray(beginNode, beginOffset, 1.4468 + endNode, endOffset, 1.4469 + true, &affectedRanges); 1.4470 + if (NS_FAILED(rv)) { 1.4471 + aRv.Throw(rv); 1.4472 + return; 1.4473 + } 1.4474 + for (uint32_t i = 0; i < affectedRanges.Length(); i++) { 1.4475 + selectFrames(presContext, affectedRanges[i], true); 1.4476 + } 1.4477 + 1.4478 + int32_t cnt = mRanges.Length(); 1.4479 + if (&aRange == mAnchorFocusRange) { 1.4480 + // Reset anchor to LAST range or clear it if there are no ranges. 1.4481 + setAnchorFocusRange(cnt - 1); 1.4482 + 1.4483 + // When the selection is user-created it makes sense to scroll the range 1.4484 + // into view. The spell-check selection, however, is created and destroyed 1.4485 + // in the background. We don't want to scroll in this case or the view 1.4486 + // might appear to be moving randomly (bug 337871). 1.4487 + if (mType != nsISelectionController::SELECTION_SPELLCHECK && cnt > 0) 1.4488 + ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION); 1.4489 + } 1.4490 + 1.4491 + if (!mFrameSelection) 1.4492 + return;//nothing to do 1.4493 + rv = mFrameSelection->NotifySelectionListeners(GetType()); 1.4494 + if (NS_FAILED(rv)) { 1.4495 + aRv.Throw(rv); 1.4496 + } 1.4497 +} 1.4498 + 1.4499 + 1.4500 + 1.4501 +/* 1.4502 + * Collapse sets the whole selection to be one point. 1.4503 + */ 1.4504 +NS_IMETHODIMP 1.4505 +Selection::Collapse(nsIDOMNode* aParentNode, int32_t aOffset) 1.4506 +{ 1.4507 + nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode); 1.4508 + return Collapse(parentNode, aOffset); 1.4509 +} 1.4510 + 1.4511 +NS_IMETHODIMP 1.4512 +Selection::CollapseNative(nsINode* aParentNode, int32_t aOffset) 1.4513 +{ 1.4514 + return Collapse(aParentNode, aOffset); 1.4515 +} 1.4516 + 1.4517 +nsresult 1.4518 +Selection::Collapse(nsINode* aParentNode, int32_t aOffset) 1.4519 +{ 1.4520 + if (!aParentNode) 1.4521 + return NS_ERROR_INVALID_ARG; 1.4522 + 1.4523 + ErrorResult result; 1.4524 + Collapse(*aParentNode, static_cast<uint32_t>(aOffset), result); 1.4525 + return result.ErrorCode(); 1.4526 +} 1.4527 + 1.4528 +void 1.4529 +Selection::Collapse(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv) 1.4530 +{ 1.4531 + if (!mFrameSelection) { 1.4532 + aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection 1.4533 + return; 1.4534 + } 1.4535 + 1.4536 + nsCOMPtr<nsINode> kungfuDeathGrip = &aParentNode; 1.4537 + 1.4538 + mFrameSelection->InvalidateDesiredX(); 1.4539 + if (!IsValidSelectionPoint(mFrameSelection, &aParentNode)) { 1.4540 + aRv.Throw(NS_ERROR_FAILURE); 1.4541 + return; 1.4542 + } 1.4543 + nsresult result; 1.4544 + 1.4545 + nsRefPtr<nsPresContext> presContext = GetPresContext(); 1.4546 + if (!presContext || presContext->Document() != aParentNode.OwnerDoc()) { 1.4547 + aRv.Throw(NS_ERROR_FAILURE); 1.4548 + return; 1.4549 + } 1.4550 + 1.4551 + // Delete all of the current ranges 1.4552 + Clear(presContext); 1.4553 + 1.4554 + // Turn off signal for table selection 1.4555 + mFrameSelection->ClearTableCellSelection(); 1.4556 + 1.4557 + nsRefPtr<nsRange> range = new nsRange(&aParentNode); 1.4558 + result = range->SetEnd(&aParentNode, aOffset); 1.4559 + if (NS_FAILED(result)) { 1.4560 + aRv.Throw(result); 1.4561 + return; 1.4562 + } 1.4563 + result = range->SetStart(&aParentNode, aOffset); 1.4564 + if (NS_FAILED(result)) { 1.4565 + aRv.Throw(result); 1.4566 + return; 1.4567 + } 1.4568 + 1.4569 +#ifdef DEBUG_SELECTION 1.4570 + nsCOMPtr<nsIContent> content = do_QueryInterface(&aParentNode); 1.4571 + nsCOMPtr<nsIDocument> doc = do_QueryInterface(&aParentNode); 1.4572 + printf ("Sel. Collapse to %p %s %d\n", &aParentNode, 1.4573 + content ? nsAtomCString(content->Tag()).get() 1.4574 + : (doc ? "DOCUMENT" : "???"), 1.4575 + aOffset); 1.4576 +#endif 1.4577 + 1.4578 + int32_t rangeIndex = -1; 1.4579 + result = AddItem(range, &rangeIndex); 1.4580 + if (NS_FAILED(result)) { 1.4581 + aRv.Throw(result); 1.4582 + return; 1.4583 + } 1.4584 + setAnchorFocusRange(0); 1.4585 + selectFrames(presContext, range, true); 1.4586 + result = mFrameSelection->NotifySelectionListeners(GetType()); 1.4587 + if (NS_FAILED(result)) { 1.4588 + aRv.Throw(result); 1.4589 + } 1.4590 +} 1.4591 + 1.4592 +/* 1.4593 + * Sets the whole selection to be one point 1.4594 + * at the start of the current selection 1.4595 + */ 1.4596 +NS_IMETHODIMP 1.4597 +Selection::CollapseToStart() 1.4598 +{ 1.4599 + ErrorResult result; 1.4600 + CollapseToStart(result); 1.4601 + return result.ErrorCode(); 1.4602 +} 1.4603 + 1.4604 +void 1.4605 +Selection::CollapseToStart(ErrorResult& aRv) 1.4606 +{ 1.4607 + int32_t cnt; 1.4608 + nsresult rv = GetRangeCount(&cnt); 1.4609 + if (NS_FAILED(rv) || cnt <= 0) { 1.4610 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.4611 + return; 1.4612 + } 1.4613 + 1.4614 + // Get the first range 1.4615 + nsRange* firstRange = mRanges[0].mRange; 1.4616 + if (!firstRange) { 1.4617 + aRv.Throw(NS_ERROR_FAILURE); 1.4618 + return; 1.4619 + } 1.4620 + 1.4621 + if (mFrameSelection) { 1.4622 + int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOSTART_REASON; 1.4623 + mFrameSelection->PostReason(reason); 1.4624 + } 1.4625 + nsINode* parent = firstRange->GetStartParent(); 1.4626 + if (!parent) { 1.4627 + aRv.Throw(NS_ERROR_FAILURE); 1.4628 + return; 1.4629 + } 1.4630 + Collapse(*parent, firstRange->StartOffset(), aRv); 1.4631 +} 1.4632 + 1.4633 +/* 1.4634 + * Sets the whole selection to be one point 1.4635 + * at the end of the current selection 1.4636 + */ 1.4637 +NS_IMETHODIMP 1.4638 +Selection::CollapseToEnd() 1.4639 +{ 1.4640 + ErrorResult result; 1.4641 + CollapseToEnd(result); 1.4642 + return result.ErrorCode(); 1.4643 +} 1.4644 + 1.4645 +void 1.4646 +Selection::CollapseToEnd(ErrorResult& aRv) 1.4647 +{ 1.4648 + int32_t cnt; 1.4649 + nsresult rv = GetRangeCount(&cnt); 1.4650 + if (NS_FAILED(rv) || cnt <= 0) { 1.4651 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.4652 + return; 1.4653 + } 1.4654 + 1.4655 + // Get the last range 1.4656 + nsRange* lastRange = mRanges[cnt - 1].mRange; 1.4657 + if (!lastRange) { 1.4658 + aRv.Throw(NS_ERROR_FAILURE); 1.4659 + return; 1.4660 + } 1.4661 + 1.4662 + if (mFrameSelection) { 1.4663 + int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOEND_REASON; 1.4664 + mFrameSelection->PostReason(reason); 1.4665 + } 1.4666 + nsINode* parent = lastRange->GetEndParent(); 1.4667 + if (!parent) { 1.4668 + aRv.Throw(NS_ERROR_FAILURE); 1.4669 + return; 1.4670 + } 1.4671 + Collapse(*parent, lastRange->EndOffset(), aRv); 1.4672 +} 1.4673 + 1.4674 +/* 1.4675 + * IsCollapsed -- is the whole selection just one point, or unset? 1.4676 + */ 1.4677 +bool 1.4678 +Selection::IsCollapsed() 1.4679 +{ 1.4680 + uint32_t cnt = mRanges.Length(); 1.4681 + if (cnt == 0) { 1.4682 + return true; 1.4683 + } 1.4684 + 1.4685 + if (cnt != 1) { 1.4686 + return false; 1.4687 + } 1.4688 + 1.4689 + return mRanges[0].mRange->Collapsed(); 1.4690 +} 1.4691 + 1.4692 +/* virtual */ 1.4693 +bool 1.4694 +Selection::Collapsed() 1.4695 +{ 1.4696 + return IsCollapsed(); 1.4697 +} 1.4698 + 1.4699 +NS_IMETHODIMP 1.4700 +Selection::GetIsCollapsed(bool* aIsCollapsed) 1.4701 +{ 1.4702 + NS_ENSURE_TRUE(aIsCollapsed, NS_ERROR_NULL_POINTER); 1.4703 + 1.4704 + *aIsCollapsed = IsCollapsed(); 1.4705 + return NS_OK; 1.4706 +} 1.4707 + 1.4708 +NS_IMETHODIMP 1.4709 +Selection::GetRangeCount(int32_t* aRangeCount) 1.4710 +{ 1.4711 + *aRangeCount = (int32_t)RangeCount(); 1.4712 + 1.4713 + return NS_OK; 1.4714 +} 1.4715 + 1.4716 +NS_IMETHODIMP 1.4717 +Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn) 1.4718 +{ 1.4719 + ErrorResult result; 1.4720 + *aReturn = GetRangeAt(aIndex, result); 1.4721 + NS_IF_ADDREF(*aReturn); 1.4722 + return result.ErrorCode(); 1.4723 +} 1.4724 + 1.4725 +nsRange* 1.4726 +Selection::GetRangeAt(uint32_t aIndex, ErrorResult& aRv) 1.4727 +{ 1.4728 + nsRange* range = GetRangeAt(aIndex); 1.4729 + if (!range) { 1.4730 + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); 1.4731 + return nullptr; 1.4732 + } 1.4733 + 1.4734 + return range; 1.4735 +} 1.4736 + 1.4737 +nsRange* 1.4738 +Selection::GetRangeAt(int32_t aIndex) 1.4739 +{ 1.4740 + RangeData empty(nullptr); 1.4741 + return mRanges.SafeElementAt(aIndex, empty).mRange; 1.4742 +} 1.4743 + 1.4744 +/* 1.4745 +utility function 1.4746 +*/ 1.4747 +nsresult 1.4748 +Selection::SetAnchorFocusToRange(nsRange* aRange) 1.4749 +{ 1.4750 + NS_ENSURE_STATE(mAnchorFocusRange); 1.4751 + 1.4752 + nsresult res = RemoveItem(mAnchorFocusRange); 1.4753 + if (NS_FAILED(res)) 1.4754 + return res; 1.4755 + 1.4756 + int32_t aOutIndex = -1; 1.4757 + res = AddItem(aRange, &aOutIndex); 1.4758 + if (NS_FAILED(res)) 1.4759 + return res; 1.4760 + setAnchorFocusRange(aOutIndex); 1.4761 + 1.4762 + return NS_OK; 1.4763 +} 1.4764 + 1.4765 +void 1.4766 +Selection::ReplaceAnchorFocusRange(nsRange* aRange) 1.4767 +{ 1.4768 + NS_ENSURE_TRUE_VOID(mAnchorFocusRange); 1.4769 + nsRefPtr<nsPresContext> presContext = GetPresContext(); 1.4770 + if (presContext) { 1.4771 + selectFrames(presContext, mAnchorFocusRange, false); 1.4772 + SetAnchorFocusToRange(aRange); 1.4773 + selectFrames(presContext, mAnchorFocusRange, true); 1.4774 + } 1.4775 +} 1.4776 + 1.4777 +/* 1.4778 +Notes which might come in handy for extend: 1.4779 + 1.4780 +We can tell the direction of the selection by asking for the anchors selection 1.4781 +if the begin is less than the end then we know the selection is to the "right". 1.4782 +else it is a backwards selection. 1.4783 +a = anchor 1.4784 +1 = old cursor 1.4785 +2 = new cursor 1.4786 + 1.4787 + if (a <= 1 && 1 <=2) a,1,2 or (a1,2) 1.4788 + if (a < 2 && 1 > 2) a,2,1 1.4789 + if (1 < a && a <2) 1,a,2 1.4790 + if (a > 2 && 2 >1) 1,2,a 1.4791 + if (2 < a && a <1) 2,a,1 1.4792 + if (a > 1 && 1 >2) 2,1,a 1.4793 +then execute 1.4794 +a 1 2 select from 1 to 2 1.4795 +a 2 1 deselect from 2 to 1 1.4796 +1 a 2 deselect from 1 to a select from a to 2 1.4797 +1 2 a deselect from 1 to 2 1.4798 +2 1 a = continue selection from 2 to 1 1.4799 +*/ 1.4800 + 1.4801 + 1.4802 +/* 1.4803 + * Extend extends the selection away from the anchor. 1.4804 + * We don't need to know the direction, because we always change the focus. 1.4805 + */ 1.4806 +NS_IMETHODIMP 1.4807 +Selection::Extend(nsIDOMNode* aParentNode, int32_t aOffset) 1.4808 +{ 1.4809 + nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode); 1.4810 + return Extend(parentNode, aOffset); 1.4811 +} 1.4812 + 1.4813 +NS_IMETHODIMP 1.4814 +Selection::ExtendNative(nsINode* aParentNode, int32_t aOffset) 1.4815 +{ 1.4816 + return Extend(aParentNode, aOffset); 1.4817 +} 1.4818 + 1.4819 +nsresult 1.4820 +Selection::Extend(nsINode* aParentNode, int32_t aOffset) 1.4821 +{ 1.4822 + if (!aParentNode) 1.4823 + return NS_ERROR_INVALID_ARG; 1.4824 + 1.4825 + ErrorResult result; 1.4826 + Extend(*aParentNode, static_cast<uint32_t>(aOffset), result); 1.4827 + return result.ErrorCode(); 1.4828 +} 1.4829 + 1.4830 +void 1.4831 +Selection::Extend(nsINode& aParentNode, uint32_t aOffset, ErrorResult& aRv) 1.4832 +{ 1.4833 + // First, find the range containing the old focus point: 1.4834 + if (!mAnchorFocusRange) { 1.4835 + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1.4836 + return; 1.4837 + } 1.4838 + 1.4839 + if (!mFrameSelection) { 1.4840 + aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection 1.4841 + return; 1.4842 + } 1.4843 + 1.4844 + nsresult res; 1.4845 + if (!IsValidSelectionPoint(mFrameSelection, &aParentNode)) { 1.4846 + aRv.Throw(NS_ERROR_FAILURE); 1.4847 + return; 1.4848 + } 1.4849 + 1.4850 + nsRefPtr<nsPresContext> presContext = GetPresContext(); 1.4851 + if (!presContext || presContext->Document() != aParentNode.OwnerDoc()) { 1.4852 + aRv.Throw(NS_ERROR_FAILURE); 1.4853 + return; 1.4854 + } 1.4855 + 1.4856 + //mFrameSelection->InvalidateDesiredX(); 1.4857 + 1.4858 + nsINode* anchorNode = GetAnchorNode(); 1.4859 + nsINode* focusNode = GetFocusNode(); 1.4860 + uint32_t anchorOffset = AnchorOffset(); 1.4861 + uint32_t focusOffset = FocusOffset(); 1.4862 + 1.4863 + nsRefPtr<nsRange> range = mAnchorFocusRange->CloneRange(); 1.4864 + 1.4865 + nsINode* startNode = range->GetStartParent(); 1.4866 + nsINode* endNode = range->GetEndParent(); 1.4867 + int32_t startOffset = range->StartOffset(); 1.4868 + int32_t endOffset = range->EndOffset(); 1.4869 + 1.4870 + nsDirection dir = GetDirection(); 1.4871 + 1.4872 + //compare anchor to old cursor. 1.4873 + 1.4874 + // We pass |disconnected| to the following ComparePoints calls in order 1.4875 + // to avoid assertions. ComparePoints returns 1 in the disconnected case 1.4876 + // and we can end up in various cases below, but it is assumed that in 1.4877 + // any of the cases we end up, the nsRange implementation will collapse 1.4878 + // the range to the new point because we can not make a valid range with 1.4879 + // a disconnected point. This means that whatever range is currently 1.4880 + // selected will be cleared. 1.4881 + bool disconnected = false; 1.4882 + bool shouldClearRange = false; 1.4883 + int32_t result1 = nsContentUtils::ComparePoints(anchorNode, anchorOffset, 1.4884 + focusNode, focusOffset, 1.4885 + &disconnected); 1.4886 + //compare old cursor to new cursor 1.4887 + shouldClearRange |= disconnected; 1.4888 + int32_t result2 = nsContentUtils::ComparePoints(focusNode, focusOffset, 1.4889 + &aParentNode, aOffset, 1.4890 + &disconnected); 1.4891 + //compare anchor to new cursor 1.4892 + shouldClearRange |= disconnected; 1.4893 + int32_t result3 = nsContentUtils::ComparePoints(anchorNode, anchorOffset, 1.4894 + &aParentNode, aOffset, 1.4895 + &disconnected); 1.4896 + 1.4897 + // If the points are disconnected, the range will be collapsed below, 1.4898 + // resulting in a range that selects nothing. 1.4899 + if (shouldClearRange) { 1.4900 + // Repaint the current range with the selection removed. 1.4901 + selectFrames(presContext, range, false); 1.4902 + } 1.4903 + 1.4904 + nsRefPtr<nsRange> difRange = new nsRange(&aParentNode); 1.4905 + if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2 a,1,2 1.4906 + //select from 1 to 2 unless they are collapsed 1.4907 + range->SetEnd(aParentNode, aOffset, aRv); 1.4908 + if (aRv.Failed()) { 1.4909 + return; 1.4910 + } 1.4911 + dir = eDirNext; 1.4912 + res = difRange->SetEnd(range->GetEndParent(), range->EndOffset()); 1.4913 + nsresult tmp = difRange->SetStart(focusNode, focusOffset); 1.4914 + if (NS_FAILED(tmp)) { 1.4915 + res = tmp; 1.4916 + } 1.4917 + if (NS_FAILED(res)) { 1.4918 + aRv.Throw(res); 1.4919 + return; 1.4920 + } 1.4921 + selectFrames(presContext, difRange , true); 1.4922 + res = SetAnchorFocusToRange(range); 1.4923 + if (NS_FAILED(res)) { 1.4924 + aRv.Throw(res); 1.4925 + return; 1.4926 + } 1.4927 + } 1.4928 + else if (result1 == 0 && result3 > 0){//2, a1 1.4929 + //select from 2 to 1a 1.4930 + dir = eDirPrevious; 1.4931 + range->SetStart(aParentNode, aOffset, aRv); 1.4932 + if (aRv.Failed()) { 1.4933 + return; 1.4934 + } 1.4935 + selectFrames(presContext, range, true); 1.4936 + res = SetAnchorFocusToRange(range); 1.4937 + if (NS_FAILED(res)) { 1.4938 + aRv.Throw(res); 1.4939 + return; 1.4940 + } 1.4941 + } 1.4942 + else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21 1.4943 + //deselect from 2 to 1 1.4944 + res = difRange->SetEnd(focusNode, focusOffset); 1.4945 + difRange->SetStart(aParentNode, aOffset, aRv); 1.4946 + if (aRv.Failed()) { 1.4947 + return; 1.4948 + } 1.4949 + if (NS_FAILED(res)) { 1.4950 + aRv.Throw(res); 1.4951 + return; 1.4952 + } 1.4953 + 1.4954 + range->SetEnd(aParentNode, aOffset, aRv); 1.4955 + if (aRv.Failed()) { 1.4956 + return; 1.4957 + } 1.4958 + res = SetAnchorFocusToRange(range); 1.4959 + if (NS_FAILED(res)) { 1.4960 + aRv.Throw(res); 1.4961 + return; 1.4962 + } 1.4963 + selectFrames(presContext, difRange, false); // deselect now 1.4964 + difRange->SetEnd(range->GetEndParent(), range->EndOffset()); 1.4965 + selectFrames(presContext, difRange, true); // must reselect last node maybe more 1.4966 + } 1.4967 + else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2 1.4968 + if (GetDirection() == eDirPrevious){ 1.4969 + res = range->SetStart(endNode, endOffset); 1.4970 + if (NS_FAILED(res)) { 1.4971 + aRv.Throw(res); 1.4972 + return; 1.4973 + } 1.4974 + } 1.4975 + dir = eDirNext; 1.4976 + range->SetEnd(aParentNode, aOffset, aRv); 1.4977 + if (aRv.Failed()) { 1.4978 + return; 1.4979 + } 1.4980 + if (focusNode != anchorNode || focusOffset != anchorOffset) {//if collapsed diff dont do anything 1.4981 + res = difRange->SetStart(focusNode, focusOffset); 1.4982 + nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset); 1.4983 + if (NS_FAILED(tmp)) { 1.4984 + res = tmp; 1.4985 + } 1.4986 + if (NS_FAILED(res)) { 1.4987 + aRv.Throw(res); 1.4988 + return; 1.4989 + } 1.4990 + res = SetAnchorFocusToRange(range); 1.4991 + if (NS_FAILED(res)) { 1.4992 + aRv.Throw(res); 1.4993 + return; 1.4994 + } 1.4995 + //deselect from 1 to a 1.4996 + selectFrames(presContext, difRange , false); 1.4997 + } 1.4998 + else 1.4999 + { 1.5000 + res = SetAnchorFocusToRange(range); 1.5001 + if (NS_FAILED(res)) { 1.5002 + aRv.Throw(res); 1.5003 + return; 1.5004 + } 1.5005 + } 1.5006 + //select from a to 2 1.5007 + selectFrames(presContext, range , true); 1.5008 + } 1.5009 + else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a 1.5010 + //deselect from 1 to 2 1.5011 + difRange->SetEnd(aParentNode, aOffset, aRv); 1.5012 + res = difRange->SetStart(focusNode, focusOffset); 1.5013 + if (aRv.Failed()) { 1.5014 + return; 1.5015 + } 1.5016 + if (NS_FAILED(res)) { 1.5017 + aRv.Throw(res); 1.5018 + return; 1.5019 + } 1.5020 + dir = eDirPrevious; 1.5021 + range->SetStart(aParentNode, aOffset, aRv); 1.5022 + if (aRv.Failed()) { 1.5023 + return; 1.5024 + } 1.5025 + 1.5026 + res = SetAnchorFocusToRange(range); 1.5027 + if (NS_FAILED(res)) { 1.5028 + aRv.Throw(res); 1.5029 + return; 1.5030 + } 1.5031 + selectFrames(presContext, difRange , false); 1.5032 + difRange->SetStart(range->GetStartParent(), range->StartOffset()); 1.5033 + selectFrames(presContext, difRange, true);//must reselect last node 1.5034 + } 1.5035 + else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1 1.5036 + if (GetDirection() == eDirNext){ 1.5037 + range->SetEnd(startNode, startOffset); 1.5038 + } 1.5039 + dir = eDirPrevious; 1.5040 + range->SetStart(aParentNode, aOffset, aRv); 1.5041 + if (aRv.Failed()) { 1.5042 + return; 1.5043 + } 1.5044 + //deselect from a to 1 1.5045 + if (focusNode != anchorNode || focusOffset!= anchorOffset) {//if collapsed diff dont do anything 1.5046 + res = difRange->SetStart(anchorNode, anchorOffset); 1.5047 + nsresult tmp = difRange->SetEnd(focusNode, focusOffset); 1.5048 + if (NS_FAILED(tmp)) { 1.5049 + res = tmp; 1.5050 + } 1.5051 + tmp = SetAnchorFocusToRange(range); 1.5052 + if (NS_FAILED(tmp)) { 1.5053 + res = tmp; 1.5054 + } 1.5055 + if (NS_FAILED(res)) { 1.5056 + aRv.Throw(res); 1.5057 + return; 1.5058 + } 1.5059 + selectFrames(presContext, difRange, false); 1.5060 + } 1.5061 + else 1.5062 + { 1.5063 + res = SetAnchorFocusToRange(range); 1.5064 + if (NS_FAILED(res)) { 1.5065 + aRv.Throw(res); 1.5066 + return; 1.5067 + } 1.5068 + } 1.5069 + //select from 2 to a 1.5070 + selectFrames(presContext, range , true); 1.5071 + } 1.5072 + else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a 1.5073 + //select from 2 to 1 1.5074 + range->SetStart(aParentNode, aOffset, aRv); 1.5075 + if (aRv.Failed()) { 1.5076 + return; 1.5077 + } 1.5078 + dir = eDirPrevious; 1.5079 + res = difRange->SetEnd(focusNode, focusOffset); 1.5080 + nsresult tmp = difRange->SetStart(range->GetStartParent(), range->StartOffset()); 1.5081 + if (NS_FAILED(tmp)) { 1.5082 + res = tmp; 1.5083 + } 1.5084 + if (NS_FAILED(res)) { 1.5085 + aRv.Throw(res); 1.5086 + return; 1.5087 + } 1.5088 + 1.5089 + selectFrames(presContext, difRange, true); 1.5090 + res = SetAnchorFocusToRange(range); 1.5091 + if (NS_FAILED(res)) { 1.5092 + aRv.Throw(res); 1.5093 + return; 1.5094 + } 1.5095 + } 1.5096 + 1.5097 + DEBUG_OUT_RANGE(range); 1.5098 +#ifdef DEBUG_SELECTION 1.5099 + if (eDirNext == mDirection) 1.5100 + printf(" direction = 1 LEFT TO RIGHT\n"); 1.5101 + else 1.5102 + printf(" direction = 0 RIGHT TO LEFT\n"); 1.5103 +#endif 1.5104 + SetDirection(dir); 1.5105 +#ifdef DEBUG_SELECTION 1.5106 + nsCOMPtr<nsIContent> content = do_QueryInterface(&aParentNode); 1.5107 + 1.5108 + printf ("Sel. Extend to %p %s %d\n", content.get(), 1.5109 + nsAtomCString(content->Tag()).get(), aOffset); 1.5110 +#endif 1.5111 + res = mFrameSelection->NotifySelectionListeners(GetType()); 1.5112 + if (NS_FAILED(res)) { 1.5113 + aRv.Throw(res); 1.5114 + } 1.5115 +} 1.5116 + 1.5117 +NS_IMETHODIMP 1.5118 +Selection::SelectAllChildren(nsIDOMNode* aParentNode) 1.5119 +{ 1.5120 + ErrorResult result; 1.5121 + nsCOMPtr<nsINode> node = do_QueryInterface(aParentNode); 1.5122 + NS_ENSURE_TRUE(node, NS_ERROR_INVALID_ARG); 1.5123 + SelectAllChildren(*node, result); 1.5124 + return result.ErrorCode(); 1.5125 +} 1.5126 + 1.5127 +void 1.5128 +Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv) 1.5129 +{ 1.5130 + if (mFrameSelection) 1.5131 + { 1.5132 + mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON); 1.5133 + } 1.5134 + Collapse(aNode, 0, aRv); 1.5135 + if (aRv.Failed()) { 1.5136 + return; 1.5137 + } 1.5138 + 1.5139 + if (mFrameSelection) 1.5140 + { 1.5141 + mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON); 1.5142 + } 1.5143 + Extend(aNode, aNode.GetChildCount(), aRv); 1.5144 +} 1.5145 + 1.5146 +NS_IMETHODIMP 1.5147 +Selection::ContainsNode(nsIDOMNode* aNode, bool aAllowPartial, bool* aYes) 1.5148 +{ 1.5149 + if (!aYes) 1.5150 + return NS_ERROR_NULL_POINTER; 1.5151 + *aYes = false; 1.5152 + 1.5153 + nsCOMPtr<nsINode> node = do_QueryInterface(aNode); 1.5154 + ErrorResult result; 1.5155 + *aYes = ContainsNode(node, aAllowPartial, result); 1.5156 + return result.ErrorCode(); 1.5157 +} 1.5158 + 1.5159 +bool 1.5160 +Selection::ContainsNode(nsINode* aNode, bool aAllowPartial, ErrorResult& aRv) 1.5161 +{ 1.5162 + nsresult rv; 1.5163 + if (mRanges.Length() == 0 || !aNode) 1.5164 + return false; 1.5165 + 1.5166 + // XXXbz this duplicates the GetNodeLength code in nsRange.cpp 1.5167 + uint32_t nodeLength; 1.5168 + bool isData = aNode->IsNodeOfType(nsINode::eDATA_NODE); 1.5169 + if (isData) { 1.5170 + nodeLength = static_cast<nsIContent*>(aNode)->TextLength(); 1.5171 + } else { 1.5172 + nodeLength = aNode->GetChildCount(); 1.5173 + } 1.5174 + 1.5175 + nsTArray<nsRange*> overlappingRanges; 1.5176 + rv = GetRangesForIntervalArray(aNode, 0, aNode, nodeLength, 1.5177 + false, &overlappingRanges); 1.5178 + if (NS_FAILED(rv)) { 1.5179 + aRv.Throw(rv); 1.5180 + return false; 1.5181 + } 1.5182 + if (overlappingRanges.Length() == 0) 1.5183 + return false; // no ranges overlap 1.5184 + 1.5185 + // if the caller said partial intersections are OK, we're done 1.5186 + if (aAllowPartial) { 1.5187 + return true; 1.5188 + } 1.5189 + 1.5190 + // text nodes always count as inside 1.5191 + if (isData) { 1.5192 + return true; 1.5193 + } 1.5194 + 1.5195 + // The caller wants to know if the node is entirely within the given range, 1.5196 + // so we have to check all intersecting ranges. 1.5197 + for (uint32_t i = 0; i < overlappingRanges.Length(); i++) { 1.5198 + bool nodeStartsBeforeRange, nodeEndsAfterRange; 1.5199 + if (NS_SUCCEEDED(nsRange::CompareNodeToRange(aNode, overlappingRanges[i], 1.5200 + &nodeStartsBeforeRange, 1.5201 + &nodeEndsAfterRange))) { 1.5202 + if (!nodeStartsBeforeRange && !nodeEndsAfterRange) { 1.5203 + return true; 1.5204 + } 1.5205 + } 1.5206 + } 1.5207 + return false; 1.5208 +} 1.5209 + 1.5210 + 1.5211 +nsPresContext* 1.5212 +Selection::GetPresContext() const 1.5213 +{ 1.5214 + nsIPresShell *shell = GetPresShell(); 1.5215 + if (!shell) { 1.5216 + return nullptr; 1.5217 + } 1.5218 + 1.5219 + return shell->GetPresContext(); 1.5220 +} 1.5221 + 1.5222 +nsIPresShell* 1.5223 +Selection::GetPresShell() const 1.5224 +{ 1.5225 + if (!mFrameSelection) 1.5226 + return nullptr;//nothing to do 1.5227 + 1.5228 + return mFrameSelection->GetShell(); 1.5229 +} 1.5230 + 1.5231 +nsIFrame * 1.5232 +Selection::GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect) 1.5233 +{ 1.5234 + if (!mFrameSelection) 1.5235 + return nullptr; // nothing to do 1.5236 + 1.5237 + NS_ENSURE_TRUE(aRect, nullptr); 1.5238 + 1.5239 + aRect->SetRect(0, 0, 0, 0); 1.5240 + 1.5241 + switch (aRegion) { 1.5242 + case nsISelectionController::SELECTION_ANCHOR_REGION: 1.5243 + case nsISelectionController::SELECTION_FOCUS_REGION: 1.5244 + return GetSelectionEndPointGeometry(aRegion, aRect); 1.5245 + break; 1.5246 + case nsISelectionController::SELECTION_WHOLE_SELECTION: 1.5247 + break; 1.5248 + default: 1.5249 + return nullptr; 1.5250 + } 1.5251 + 1.5252 + NS_ASSERTION(aRegion == nsISelectionController::SELECTION_WHOLE_SELECTION, 1.5253 + "should only be SELECTION_WHOLE_SELECTION here"); 1.5254 + 1.5255 + nsRect anchorRect; 1.5256 + nsIFrame* anchorFrame = GetSelectionEndPointGeometry( 1.5257 + nsISelectionController::SELECTION_ANCHOR_REGION, &anchorRect); 1.5258 + if (!anchorFrame) 1.5259 + return nullptr; 1.5260 + 1.5261 + nsRect focusRect; 1.5262 + nsIFrame* focusFrame = GetSelectionEndPointGeometry( 1.5263 + nsISelectionController::SELECTION_FOCUS_REGION, &focusRect); 1.5264 + if (!focusFrame) 1.5265 + return nullptr; 1.5266 + 1.5267 + NS_ASSERTION(anchorFrame->PresContext() == focusFrame->PresContext(), 1.5268 + "points of selection in different documents?"); 1.5269 + // make focusRect relative to anchorFrame 1.5270 + focusRect += focusFrame->GetOffsetTo(anchorFrame); 1.5271 + 1.5272 + aRect->UnionRectEdges(anchorRect, focusRect); 1.5273 + return anchorFrame; 1.5274 +} 1.5275 + 1.5276 +nsIFrame * 1.5277 +Selection::GetSelectionEndPointGeometry(SelectionRegion aRegion, nsRect* aRect) 1.5278 +{ 1.5279 + if (!mFrameSelection) 1.5280 + return nullptr; // nothing to do 1.5281 + 1.5282 + NS_ENSURE_TRUE(aRect, nullptr); 1.5283 + 1.5284 + aRect->SetRect(0, 0, 0, 0); 1.5285 + 1.5286 + nsINode *node = nullptr; 1.5287 + uint32_t nodeOffset = 0; 1.5288 + nsIFrame *frame = nullptr; 1.5289 + 1.5290 + switch (aRegion) { 1.5291 + case nsISelectionController::SELECTION_ANCHOR_REGION: 1.5292 + node = GetAnchorNode(); 1.5293 + nodeOffset = AnchorOffset(); 1.5294 + break; 1.5295 + case nsISelectionController::SELECTION_FOCUS_REGION: 1.5296 + node = GetFocusNode(); 1.5297 + nodeOffset = FocusOffset(); 1.5298 + break; 1.5299 + default: 1.5300 + return nullptr; 1.5301 + } 1.5302 + 1.5303 + if (!node) 1.5304 + return nullptr; 1.5305 + 1.5306 + nsCOMPtr<nsIContent> content = do_QueryInterface(node); 1.5307 + NS_ENSURE_TRUE(content.get(), nullptr); 1.5308 + int32_t frameOffset = 0; 1.5309 + frame = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset, 1.5310 + mFrameSelection->GetHint(), 1.5311 + &frameOffset); 1.5312 + if (!frame) 1.5313 + return nullptr; 1.5314 + 1.5315 + // Figure out what node type we have, then get the 1.5316 + // appropriate rect for it's nodeOffset. 1.5317 + bool isText = node->IsNodeOfType(nsINode::eTEXT); 1.5318 + 1.5319 + nsPoint pt(0, 0); 1.5320 + if (isText) { 1.5321 + nsIFrame* childFrame = nullptr; 1.5322 + frameOffset = 0; 1.5323 + nsresult rv = 1.5324 + frame->GetChildFrameContainingOffset(nodeOffset, 1.5325 + mFrameSelection->GetHint(), 1.5326 + &frameOffset, &childFrame); 1.5327 + if (NS_FAILED(rv)) 1.5328 + return nullptr; 1.5329 + if (!childFrame) 1.5330 + return nullptr; 1.5331 + 1.5332 + frame = childFrame; 1.5333 + 1.5334 + // Get the x coordinate of the offset into the text frame. 1.5335 + rv = GetCachedFrameOffset(frame, nodeOffset, pt); 1.5336 + if (NS_FAILED(rv)) 1.5337 + return nullptr; 1.5338 + } 1.5339 + 1.5340 + // Return the rect relative to the frame, with zero width. 1.5341 + if (isText) { 1.5342 + aRect->x = pt.x; 1.5343 + } else if (mFrameSelection->GetHint() == nsFrameSelection::HINTLEFT) { 1.5344 + // It's the frame's right edge we're interested in. 1.5345 + aRect->x = frame->GetRect().width; 1.5346 + } 1.5347 + aRect->height = frame->GetRect().height; 1.5348 + 1.5349 + return frame; 1.5350 +} 1.5351 + 1.5352 +NS_IMETHODIMP 1.5353 +Selection::ScrollSelectionIntoViewEvent::Run() 1.5354 +{ 1.5355 + if (!mSelection) 1.5356 + return NS_OK; // event revoked 1.5357 + 1.5358 + int32_t flags = Selection::SCROLL_DO_FLUSH | 1.5359 + Selection::SCROLL_SYNCHRONOUS; 1.5360 + 1.5361 + mSelection->mScrollEvent.Forget(); 1.5362 + mSelection->ScrollIntoView(mRegion, mVerticalScroll, 1.5363 + mHorizontalScroll, mFlags | flags); 1.5364 + return NS_OK; 1.5365 +} 1.5366 + 1.5367 +nsresult 1.5368 +Selection::PostScrollSelectionIntoViewEvent( 1.5369 + SelectionRegion aRegion, 1.5370 + int32_t aFlags, 1.5371 + nsIPresShell::ScrollAxis aVertical, 1.5372 + nsIPresShell::ScrollAxis aHorizontal) 1.5373 +{ 1.5374 + // If we've already posted an event, revoke it and place a new one at the 1.5375 + // end of the queue to make sure that any new pending reflow events are 1.5376 + // processed before we scroll. This will insure that we scroll to the 1.5377 + // correct place on screen. 1.5378 + mScrollEvent.Revoke(); 1.5379 + 1.5380 + nsRefPtr<ScrollSelectionIntoViewEvent> ev = 1.5381 + new ScrollSelectionIntoViewEvent(this, aRegion, aVertical, aHorizontal, 1.5382 + aFlags); 1.5383 + nsresult rv = NS_DispatchToCurrentThread(ev); 1.5384 + NS_ENSURE_SUCCESS(rv, rv); 1.5385 + 1.5386 + mScrollEvent = ev; 1.5387 + return NS_OK; 1.5388 +} 1.5389 + 1.5390 +NS_IMETHODIMP 1.5391 +Selection::ScrollIntoView(SelectionRegion aRegion, bool aIsSynchronous, 1.5392 + int16_t aVPercent, int16_t aHPercent) 1.5393 +{ 1.5394 + ErrorResult result; 1.5395 + ScrollIntoView(aRegion, aIsSynchronous, aVPercent, aHPercent, result); 1.5396 + if (result.Failed()) { 1.5397 + return result.ErrorCode(); 1.5398 + } 1.5399 + return NS_OK; 1.5400 +} 1.5401 + 1.5402 +void 1.5403 +Selection::ScrollIntoView(int16_t aRegion, bool aIsSynchronous, 1.5404 + int16_t aVPercent, int16_t aHPercent, 1.5405 + ErrorResult& aRv) 1.5406 +{ 1.5407 + nsresult rv = ScrollIntoViewInternal(aRegion, aIsSynchronous, 1.5408 + nsIPresShell::ScrollAxis(aVPercent), 1.5409 + nsIPresShell::ScrollAxis(aHPercent)); 1.5410 + if (NS_FAILED(rv)) { 1.5411 + aRv.Throw(rv); 1.5412 + } 1.5413 +} 1.5414 + 1.5415 +NS_IMETHODIMP 1.5416 +Selection::ScrollIntoViewInternal(SelectionRegion aRegion, bool aIsSynchronous, 1.5417 + nsIPresShell::ScrollAxis aVertical, 1.5418 + nsIPresShell::ScrollAxis aHorizontal) 1.5419 +{ 1.5420 + return ScrollIntoView(aRegion, aVertical, aHorizontal, 1.5421 + aIsSynchronous ? Selection::SCROLL_SYNCHRONOUS : 0); 1.5422 +} 1.5423 + 1.5424 +nsresult 1.5425 +Selection::ScrollIntoView(SelectionRegion aRegion, 1.5426 + nsIPresShell::ScrollAxis aVertical, 1.5427 + nsIPresShell::ScrollAxis aHorizontal, 1.5428 + int32_t aFlags) 1.5429 +{ 1.5430 + if (!mFrameSelection) 1.5431 + return NS_OK;//nothing to do 1.5432 + 1.5433 + nsCOMPtr<nsIPresShell> presShell = mFrameSelection->GetShell(); 1.5434 + if (!presShell) 1.5435 + return NS_OK; 1.5436 + 1.5437 + if (mFrameSelection->GetBatching()) 1.5438 + return NS_OK; 1.5439 + 1.5440 + if (!(aFlags & Selection::SCROLL_SYNCHRONOUS)) 1.5441 + return PostScrollSelectionIntoViewEvent(aRegion, aFlags, 1.5442 + aVertical, aHorizontal); 1.5443 + 1.5444 + // Now that text frame character offsets are always valid (though not 1.5445 + // necessarily correct), the worst that will happen if we don't flush here 1.5446 + // is that some callers might scroll to the wrong place. Those should 1.5447 + // either manually flush if they're in a safe position for it or use the 1.5448 + // async version of this method. 1.5449 + if (aFlags & Selection::SCROLL_DO_FLUSH) { 1.5450 + presShell->FlushPendingNotifications(Flush_Layout); 1.5451 + 1.5452 + // Reget the presshell, since it might have been Destroy'ed. 1.5453 + presShell = mFrameSelection ? mFrameSelection->GetShell() : nullptr; 1.5454 + if (!presShell) 1.5455 + return NS_OK; 1.5456 + } 1.5457 + 1.5458 + // 1.5459 + // Scroll the selection region into view. 1.5460 + // 1.5461 + 1.5462 + nsRect rect; 1.5463 + nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect); 1.5464 + if (!frame) 1.5465 + return NS_ERROR_FAILURE; 1.5466 + 1.5467 + // Scroll vertically to get the caret into view, but only if the container 1.5468 + // is perceived to be scrollable in that direction (i.e. there is a visible 1.5469 + // vertical scrollbar or the scroll range is at least one device pixel) 1.5470 + aVertical.mOnlyIfPerceivedScrollableDirection = true; 1.5471 + 1.5472 + uint32_t flags = 0; 1.5473 + if (aFlags & Selection::SCROLL_FIRST_ANCESTOR_ONLY) { 1.5474 + flags |= nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY; 1.5475 + } 1.5476 + if (aFlags & Selection::SCROLL_OVERFLOW_HIDDEN) { 1.5477 + flags |= nsIPresShell::SCROLL_OVERFLOW_HIDDEN; 1.5478 + } 1.5479 + 1.5480 + presShell->ScrollFrameRectIntoView(frame, rect, aVertical, aHorizontal, 1.5481 + flags); 1.5482 + return NS_OK; 1.5483 +} 1.5484 + 1.5485 +NS_IMETHODIMP 1.5486 +Selection::AddSelectionListener(nsISelectionListener* aNewListener) 1.5487 +{ 1.5488 + if (!aNewListener) 1.5489 + return NS_ERROR_NULL_POINTER; 1.5490 + ErrorResult result; 1.5491 + AddSelectionListener(aNewListener, result); 1.5492 + if (result.Failed()) { 1.5493 + return result.ErrorCode(); 1.5494 + } 1.5495 + return NS_OK; 1.5496 +} 1.5497 + 1.5498 +void 1.5499 +Selection::AddSelectionListener(nsISelectionListener* aNewListener, 1.5500 + ErrorResult& aRv) 1.5501 +{ 1.5502 + bool result = mSelectionListeners.AppendObject(aNewListener); // AddRefs 1.5503 + if (!result) { 1.5504 + aRv.Throw(NS_ERROR_FAILURE); 1.5505 + } 1.5506 +} 1.5507 + 1.5508 +NS_IMETHODIMP 1.5509 +Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove) 1.5510 +{ 1.5511 + if (!aListenerToRemove) 1.5512 + return NS_ERROR_NULL_POINTER; 1.5513 + ErrorResult result; 1.5514 + RemoveSelectionListener(aListenerToRemove, result); 1.5515 + if (result.Failed()) { 1.5516 + return result.ErrorCode(); 1.5517 + } 1.5518 + return NS_OK; 1.5519 +} 1.5520 + 1.5521 +void 1.5522 +Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove, 1.5523 + ErrorResult& aRv) 1.5524 +{ 1.5525 + bool result = mSelectionListeners.RemoveObject(aListenerToRemove); // Releases 1.5526 + if (!result) { 1.5527 + aRv.Throw(NS_ERROR_FAILURE); 1.5528 + } 1.5529 +} 1.5530 + 1.5531 +nsresult 1.5532 +Selection::NotifySelectionListeners() 1.5533 +{ 1.5534 + if (!mFrameSelection) 1.5535 + return NS_OK;//nothing to do 1.5536 + 1.5537 + if (mFrameSelection->GetBatching()) { 1.5538 + mFrameSelection->SetDirty(); 1.5539 + return NS_OK; 1.5540 + } 1.5541 + nsCOMArray<nsISelectionListener> selectionListeners(mSelectionListeners); 1.5542 + int32_t cnt = selectionListeners.Count(); 1.5543 + if (cnt != mSelectionListeners.Count()) { 1.5544 + return NS_ERROR_OUT_OF_MEMORY; // nsCOMArray is fallible 1.5545 + } 1.5546 + 1.5547 + nsCOMPtr<nsIDOMDocument> domdoc; 1.5548 + nsIPresShell* ps = GetPresShell(); 1.5549 + if (ps) { 1.5550 + domdoc = do_QueryInterface(ps->GetDocument()); 1.5551 + } 1.5552 + 1.5553 + short reason = mFrameSelection->PopReason(); 1.5554 + for (int32_t i = 0; i < cnt; i++) { 1.5555 + selectionListeners[i]->NotifySelectionChanged(domdoc, this, reason); 1.5556 + } 1.5557 + return NS_OK; 1.5558 +} 1.5559 + 1.5560 +NS_IMETHODIMP 1.5561 +Selection::StartBatchChanges() 1.5562 +{ 1.5563 + if (mFrameSelection) 1.5564 + mFrameSelection->StartBatchChanges(); 1.5565 + 1.5566 + return NS_OK; 1.5567 +} 1.5568 + 1.5569 + 1.5570 + 1.5571 +NS_IMETHODIMP 1.5572 +Selection::EndBatchChanges() 1.5573 +{ 1.5574 + if (mFrameSelection) 1.5575 + mFrameSelection->EndBatchChanges(); 1.5576 + 1.5577 + return NS_OK; 1.5578 +} 1.5579 + 1.5580 + 1.5581 + 1.5582 +NS_IMETHODIMP 1.5583 +Selection::DeleteFromDocument() 1.5584 +{ 1.5585 + ErrorResult result; 1.5586 + DeleteFromDocument(result); 1.5587 + return result.ErrorCode(); 1.5588 +} 1.5589 + 1.5590 +void 1.5591 +Selection::DeleteFromDocument(ErrorResult& aRv) 1.5592 +{ 1.5593 + if (!mFrameSelection) 1.5594 + return;//nothing to do 1.5595 + nsresult rv = mFrameSelection->DeleteFromDocument(); 1.5596 + if (NS_FAILED(rv)) { 1.5597 + aRv.Throw(rv); 1.5598 + } 1.5599 +} 1.5600 + 1.5601 +NS_IMETHODIMP 1.5602 +Selection::Modify(const nsAString& aAlter, const nsAString& aDirection, 1.5603 + const nsAString& aGranularity) 1.5604 +{ 1.5605 + ErrorResult result; 1.5606 + Modify(aAlter, aDirection, aGranularity, result); 1.5607 + return result.ErrorCode(); 1.5608 +} 1.5609 + 1.5610 +void 1.5611 +Selection::Modify(const nsAString& aAlter, const nsAString& aDirection, 1.5612 + const nsAString& aGranularity, ErrorResult& aRv) 1.5613 +{ 1.5614 + // Silently exit if there's no selection or no focus node. 1.5615 + if (!mFrameSelection || !GetAnchorFocusRange() || !GetFocusNode()) { 1.5616 + return; 1.5617 + } 1.5618 + 1.5619 + if (!aAlter.LowerCaseEqualsLiteral("move") && 1.5620 + !aAlter.LowerCaseEqualsLiteral("extend")) { 1.5621 + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); 1.5622 + return; 1.5623 + } 1.5624 + 1.5625 + if (!aDirection.LowerCaseEqualsLiteral("forward") && 1.5626 + !aDirection.LowerCaseEqualsLiteral("backward") && 1.5627 + !aDirection.LowerCaseEqualsLiteral("left") && 1.5628 + !aDirection.LowerCaseEqualsLiteral("right")) { 1.5629 + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); 1.5630 + return; 1.5631 + } 1.5632 + 1.5633 + // Line moves are always visual. 1.5634 + bool visual = aDirection.LowerCaseEqualsLiteral("left") || 1.5635 + aDirection.LowerCaseEqualsLiteral("right") || 1.5636 + aGranularity.LowerCaseEqualsLiteral("line"); 1.5637 + 1.5638 + bool forward = aDirection.LowerCaseEqualsLiteral("forward") || 1.5639 + aDirection.LowerCaseEqualsLiteral("right"); 1.5640 + 1.5641 + bool extend = aAlter.LowerCaseEqualsLiteral("extend"); 1.5642 + 1.5643 + // The uint32_t casts below prevent an enum mismatch warning. 1.5644 + nsSelectionAmount amount; 1.5645 + uint32_t keycode; 1.5646 + if (aGranularity.LowerCaseEqualsLiteral("character")) { 1.5647 + amount = eSelectCluster; 1.5648 + keycode = forward ? (uint32_t) nsIDOMKeyEvent::DOM_VK_RIGHT : 1.5649 + (uint32_t) nsIDOMKeyEvent::DOM_VK_LEFT; 1.5650 + } 1.5651 + else if (aGranularity.LowerCaseEqualsLiteral("word")) { 1.5652 + amount = eSelectWordNoSpace; 1.5653 + keycode = forward ? (uint32_t) nsIDOMKeyEvent::DOM_VK_RIGHT : 1.5654 + (uint32_t) nsIDOMKeyEvent::DOM_VK_LEFT; 1.5655 + } 1.5656 + else if (aGranularity.LowerCaseEqualsLiteral("line")) { 1.5657 + amount = eSelectLine; 1.5658 + keycode = forward ? (uint32_t) nsIDOMKeyEvent::DOM_VK_DOWN : 1.5659 + (uint32_t) nsIDOMKeyEvent::DOM_VK_UP; 1.5660 + } 1.5661 + else if (aGranularity.LowerCaseEqualsLiteral("lineboundary")) { 1.5662 + amount = eSelectLine; 1.5663 + keycode = forward ? (uint32_t) nsIDOMKeyEvent::DOM_VK_END : 1.5664 + (uint32_t) nsIDOMKeyEvent::DOM_VK_HOME; 1.5665 + } 1.5666 + else if (aGranularity.LowerCaseEqualsLiteral("sentence") || 1.5667 + aGranularity.LowerCaseEqualsLiteral("sentenceboundary") || 1.5668 + aGranularity.LowerCaseEqualsLiteral("paragraph") || 1.5669 + aGranularity.LowerCaseEqualsLiteral("paragraphboundary") || 1.5670 + aGranularity.LowerCaseEqualsLiteral("documentboundary")) { 1.5671 + aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); 1.5672 + } 1.5673 + else { 1.5674 + aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); 1.5675 + return; 1.5676 + } 1.5677 + 1.5678 + // If the anchor doesn't equal the focus and we try to move without first 1.5679 + // collapsing the selection, MoveCaret will collapse the selection and quit. 1.5680 + // To avoid this, we need to collapse the selection first. 1.5681 + nsresult rv = NS_OK; 1.5682 + if (!extend) { 1.5683 + nsINode* focusNode = GetFocusNode(); 1.5684 + // We should have checked earlier that there was a focus node. 1.5685 + if (!focusNode) { 1.5686 + aRv.Throw(NS_ERROR_UNEXPECTED); 1.5687 + return; 1.5688 + } 1.5689 + uint32_t focusOffset = FocusOffset(); 1.5690 + Collapse(focusNode, focusOffset); 1.5691 + } 1.5692 + 1.5693 + // If the base level of the focused frame is odd, we may have to swap the 1.5694 + // direction of the keycode. 1.5695 + nsIFrame *frame; 1.5696 + int32_t offset; 1.5697 + rv = GetPrimaryFrameForFocusNode(&frame, &offset, visual); 1.5698 + if (NS_SUCCEEDED(rv) && frame) { 1.5699 + nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(frame); 1.5700 + 1.5701 + if (baseLevel & 1) { 1.5702 + if (!visual && keycode == nsIDOMKeyEvent::DOM_VK_RIGHT) { 1.5703 + keycode = nsIDOMKeyEvent::DOM_VK_LEFT; 1.5704 + } 1.5705 + else if (!visual && keycode == nsIDOMKeyEvent::DOM_VK_LEFT) { 1.5706 + keycode = nsIDOMKeyEvent::DOM_VK_RIGHT; 1.5707 + } 1.5708 + else if (visual && keycode == nsIDOMKeyEvent::DOM_VK_HOME) { 1.5709 + keycode = nsIDOMKeyEvent::DOM_VK_END; 1.5710 + } 1.5711 + else if (visual && keycode == nsIDOMKeyEvent::DOM_VK_END) { 1.5712 + keycode = nsIDOMKeyEvent::DOM_VK_HOME; 1.5713 + } 1.5714 + } 1.5715 + } 1.5716 + 1.5717 + // MoveCaret will return an error if it can't move in the specified 1.5718 + // direction, but we just ignore this error unless it's a line move, in which 1.5719 + // case we call nsISelectionController::CompleteMove to move the cursor to 1.5720 + // the beginning/end of the line. 1.5721 + rv = mFrameSelection->MoveCaret(keycode, extend, amount, visual); 1.5722 + 1.5723 + if (aGranularity.LowerCaseEqualsLiteral("line") && NS_FAILED(rv)) { 1.5724 + nsCOMPtr<nsISelectionController> shell = 1.5725 + do_QueryInterface(mFrameSelection->GetShell()); 1.5726 + if (!shell) 1.5727 + return; 1.5728 + shell->CompleteMove(forward, extend); 1.5729 + } 1.5730 +} 1.5731 + 1.5732 +/** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction 1.5733 + * @param aLangRTL is true if the new language is right-to-left or false if the new language is left-to-right 1.5734 + */ 1.5735 +NS_IMETHODIMP 1.5736 +Selection::SelectionLanguageChange(bool aLangRTL) 1.5737 +{ 1.5738 + if (!mFrameSelection) 1.5739 + return NS_ERROR_NOT_INITIALIZED; // Can't do selection 1.5740 + nsresult result; 1.5741 + nsIFrame *focusFrame = 0; 1.5742 + 1.5743 + result = GetPrimaryFrameForFocusNode(&focusFrame, nullptr, false); 1.5744 + if (NS_FAILED(result)) { 1.5745 + return result; 1.5746 + } 1.5747 + if (!focusFrame) { 1.5748 + return NS_ERROR_FAILURE; 1.5749 + } 1.5750 + 1.5751 + int32_t frameStart, frameEnd; 1.5752 + focusFrame->GetOffsets(frameStart, frameEnd); 1.5753 + nsRefPtr<nsPresContext> context = GetPresContext(); 1.5754 + uint8_t levelBefore, levelAfter; 1.5755 + if (!context) { 1.5756 + return NS_ERROR_FAILURE; 1.5757 + } 1.5758 + 1.5759 + uint8_t level = NS_GET_EMBEDDING_LEVEL(focusFrame); 1.5760 + int32_t focusOffset = static_cast<int32_t>(FocusOffset()); 1.5761 + if ((focusOffset != frameStart) && (focusOffset != frameEnd)) 1.5762 + // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor 1.5763 + // is equal to the frame level 1.5764 + levelBefore = levelAfter = level; 1.5765 + else { 1.5766 + // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters 1.5767 + // before and after the cursor 1.5768 + nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode()); 1.5769 + /* 1.5770 + nsFrameSelection::HINT hint; 1.5771 + 1.5772 + if ((focusOffset == frameStart && level) // beginning of an RTL frame 1.5773 + || (focusOffset == frameEnd && !level)) { // end of an LTR frame 1.5774 + hint = nsFrameSelection::HINTRIGHT; 1.5775 + } 1.5776 + else { // end of an RTL frame or beginning of an LTR frame 1.5777 + hint = nsFrameSelection::HINTLEFT; 1.5778 + } 1.5779 + mFrameSelection->SetHint(hint); 1.5780 + */ 1.5781 + nsPrevNextBidiLevels levels = mFrameSelection-> 1.5782 + GetPrevNextBidiLevels(focusContent, focusOffset, false); 1.5783 + 1.5784 + levelBefore = levels.mLevelBefore; 1.5785 + levelAfter = levels.mLevelAfter; 1.5786 + } 1.5787 + 1.5788 + if ((levelBefore & 1) == (levelAfter & 1)) { 1.5789 + // if cursor is between two characters with the same orientation, changing the keyboard language 1.5790 + // must toggle the cursor level between the level of the character with the lowest level 1.5791 + // (if the new language corresponds to the orientation of that character) and this level plus 1 1.5792 + // (if the new language corresponds to the opposite orientation) 1.5793 + if ((level != levelBefore) && (level != levelAfter)) 1.5794 + level = std::min(levelBefore, levelAfter); 1.5795 + if ((level & 1) == aLangRTL) 1.5796 + mFrameSelection->SetCaretBidiLevel(level); 1.5797 + else 1.5798 + mFrameSelection->SetCaretBidiLevel(level + 1); 1.5799 + } 1.5800 + else { 1.5801 + // if cursor is between characters with opposite orientations, changing the keyboard language must change 1.5802 + // the cursor level to that of the adjacent character with the orientation corresponding to the new language. 1.5803 + if ((levelBefore & 1) == aLangRTL) 1.5804 + mFrameSelection->SetCaretBidiLevel(levelBefore); 1.5805 + else 1.5806 + mFrameSelection->SetCaretBidiLevel(levelAfter); 1.5807 + } 1.5808 + 1.5809 + // The caret might have moved, so invalidate the desired X position 1.5810 + // for future usages of up-arrow or down-arrow 1.5811 + mFrameSelection->InvalidateDesiredX(); 1.5812 + 1.5813 + return NS_OK; 1.5814 +} 1.5815 + 1.5816 +NS_IMETHODIMP_(nsDirection) 1.5817 +Selection::GetSelectionDirection() { 1.5818 + return mDirection; 1.5819 +} 1.5820 + 1.5821 +NS_IMETHODIMP_(void) 1.5822 +Selection::SetSelectionDirection(nsDirection aDirection) { 1.5823 + mDirection = aDirection; 1.5824 +} 1.5825 + 1.5826 +JSObject* 1.5827 +Selection::WrapObject(JSContext* aCx) 1.5828 +{ 1.5829 + return mozilla::dom::SelectionBinding::Wrap(aCx, this); 1.5830 +} 1.5831 + 1.5832 +// nsAutoCopyListener 1.5833 + 1.5834 +nsAutoCopyListener* nsAutoCopyListener::sInstance = nullptr; 1.5835 + 1.5836 +NS_IMPL_ISUPPORTS(nsAutoCopyListener, nsISelectionListener) 1.5837 + 1.5838 +/* 1.5839 + * What we do now: 1.5840 + * On every selection change, we copy to the clipboard anew, creating a 1.5841 + * HTML buffer, a transferable, an nsISupportsString and 1.5842 + * a huge mess every time. This is basically what nsPresShell::DoCopy does 1.5843 + * to move the selection into the clipboard for Edit->Copy. 1.5844 + * 1.5845 + * What we should do, to make our end of the deal faster: 1.5846 + * Create a singleton transferable with our own magic converter. When selection 1.5847 + * changes (use a quick cache to detect ``real'' changes), we put the new 1.5848 + * nsISelection in the transferable. Our magic converter will take care of 1.5849 + * transferable->whatever-other-format when the time comes to actually 1.5850 + * hand over the clipboard contents. 1.5851 + * 1.5852 + * Other issues: 1.5853 + * - which X clipboard should we populate? 1.5854 + * - should we use a different one than Edit->Copy, so that inadvertant 1.5855 + * selections (or simple clicks, which currently cause a selection 1.5856 + * notification, regardless of if they're in the document which currently has 1.5857 + * selection!) don't lose the contents of the ``application''? Or should we 1.5858 + * just put some intelligence in the ``is this a real selection?'' code to 1.5859 + * protect our selection against clicks in other documents that don't create 1.5860 + * selections? 1.5861 + * - maybe we should just never clear the X clipboard? That would make this 1.5862 + * problem just go away, which is very tempting. 1.5863 + */ 1.5864 + 1.5865 +NS_IMETHODIMP 1.5866 +nsAutoCopyListener::NotifySelectionChanged(nsIDOMDocument *aDoc, 1.5867 + nsISelection *aSel, int16_t aReason) 1.5868 +{ 1.5869 + if (!(aReason & nsISelectionListener::MOUSEUP_REASON || 1.5870 + aReason & nsISelectionListener::SELECTALL_REASON || 1.5871 + aReason & nsISelectionListener::KEYPRESS_REASON)) 1.5872 + return NS_OK; //dont care if we are still dragging 1.5873 + 1.5874 + bool collapsed; 1.5875 + if (!aDoc || !aSel || 1.5876 + NS_FAILED(aSel->GetIsCollapsed(&collapsed)) || collapsed) { 1.5877 +#ifdef DEBUG_CLIPBOARD 1.5878 + fprintf(stderr, "CLIPBOARD: no selection/collapsed selection\n"); 1.5879 +#endif 1.5880 + /* clear X clipboard? */ 1.5881 + return NS_OK; 1.5882 + } 1.5883 + 1.5884 + nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc); 1.5885 + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); 1.5886 + 1.5887 + // call the copy code 1.5888 + return nsCopySupport::HTMLCopy(aSel, doc, nsIClipboard::kSelectionClipboard); 1.5889 +}