Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=78: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /*
8 : * Implementation of mozilla::dom::Selection
9 : */
10 :
11 : #include "mozilla/dom/Selection.h"
12 :
13 : #include "mozilla/Attributes.h"
14 : #include "mozilla/AutoRestore.h"
15 : #include "mozilla/EventStates.h"
16 :
17 : #include "nsCOMPtr.h"
18 : #include "nsString.h"
19 : #include "nsFrameSelection.h"
20 : #include "nsISelectionListener.h"
21 : #include "nsContentCID.h"
22 : #include "nsDeviceContext.h"
23 : #include "nsIContent.h"
24 : #include "nsIDOMNode.h"
25 : #include "nsRange.h"
26 : #include "nsITableCellLayout.h"
27 : #include "nsTArray.h"
28 : #include "nsTableWrapperFrame.h"
29 : #include "nsTableCellFrame.h"
30 : #include "nsIScrollableFrame.h"
31 : #include "nsCCUncollectableMarker.h"
32 : #include "nsIContentIterator.h"
33 : #include "nsIDocumentEncoder.h"
34 : #include "nsTextFragment.h"
35 : #include <algorithm>
36 : #include "nsContentUtils.h"
37 :
38 : #include "nsGkAtoms.h"
39 : #include "nsLayoutUtils.h"
40 : #include "nsBidiPresUtils.h"
41 : #include "nsTextFrame.h"
42 :
43 : #include "nsIDOMText.h"
44 :
45 : #include "nsContentUtils.h"
46 : #include "nsThreadUtils.h"
47 :
48 : #include "nsPresContext.h"
49 : #include "nsIPresShell.h"
50 : #include "nsCaret.h"
51 :
52 : #include "nsITimer.h"
53 : #include "nsIDOMDocument.h"
54 : #include "nsIDocument.h"
55 :
56 : #include "nsISelectionController.h"//for the enums
57 : #include "nsAutoCopyListener.h"
58 : #include "SelectionChangeListener.h"
59 : #include "nsCopySupport.h"
60 : #include "nsIClipboard.h"
61 : #include "nsIFrameInlines.h"
62 : #include "nsRefreshDriver.h"
63 : #include "nsIBidiKeyboard.h"
64 :
65 : #include "nsError.h"
66 : #include "mozilla/dom/Element.h"
67 : #include "mozilla/dom/ShadowRoot.h"
68 : #include "mozilla/ErrorResult.h"
69 : #include "mozilla/dom/SelectionBinding.h"
70 : #include "mozilla/AsyncEventDispatcher.h"
71 : #include "mozilla/Telemetry.h"
72 : #include "mozilla/layers/ScrollInputMethods.h"
73 : #include "nsViewManager.h"
74 :
75 : #include "nsIEditor.h"
76 : #include "nsIHTMLEditor.h"
77 : #include "nsFocusManager.h"
78 : #include "nsPIDOMWindow.h"
79 :
80 : using namespace mozilla;
81 : using namespace mozilla::dom;
82 : using mozilla::layers::ScrollInputMethod;
83 :
84 : //#define DEBUG_TABLE 1
85 :
86 : static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode);
87 :
88 : #ifdef PRINT_RANGE
89 : static void printRange(nsRange *aDomRange);
90 : #define DEBUG_OUT_RANGE(x) printRange(x)
91 : #else
92 : #define DEBUG_OUT_RANGE(x)
93 : #endif // PRINT_RANGE
94 :
95 : /******************************************************************************
96 : * Utility methods defined in nsISelectionController.idl
97 : ******************************************************************************/
98 :
99 : namespace mozilla {
100 :
101 : const char*
102 0 : ToChar(SelectionType aSelectionType)
103 : {
104 0 : switch (aSelectionType) {
105 : case SelectionType::eInvalid:
106 0 : return "SelectionType::eInvalid";
107 : case SelectionType::eNone:
108 0 : return "SelectionType::eNone";
109 : case SelectionType::eNormal:
110 0 : return "SelectionType::eNormal";
111 : case SelectionType::eSpellCheck:
112 0 : return "SelectionType::eSpellCheck";
113 : case SelectionType::eIMERawClause:
114 0 : return "SelectionType::eIMERawClause";
115 : case SelectionType::eIMESelectedRawClause:
116 0 : return "SelectionType::eIMESelectedRawClause";
117 : case SelectionType::eIMEConvertedClause:
118 0 : return "SelectionType::eIMEConvertedClause";
119 : case SelectionType::eIMESelectedClause:
120 0 : return "SelectionType::eIMESelectedClause";
121 : case SelectionType::eAccessibility:
122 0 : return "SelectionType::eAccessibility";
123 : case SelectionType::eFind:
124 0 : return "SelectionType::eFind";
125 : case SelectionType::eURLSecondary:
126 0 : return "SelectionType::eURLSecondary";
127 : case SelectionType::eURLStrikeout:
128 0 : return "SelectionType::eURLStrikeout";
129 : default:
130 0 : return "Invalid SelectionType";
131 : }
132 : }
133 :
134 : static bool
135 108 : IsValidSelectionType(RawSelectionType aRawSelectionType)
136 : {
137 108 : switch (static_cast<SelectionType>(aRawSelectionType)) {
138 : case SelectionType::eNone:
139 : case SelectionType::eNormal:
140 : case SelectionType::eSpellCheck:
141 : case SelectionType::eIMERawClause:
142 : case SelectionType::eIMESelectedRawClause:
143 : case SelectionType::eIMEConvertedClause:
144 : case SelectionType::eIMESelectedClause:
145 : case SelectionType::eAccessibility:
146 : case SelectionType::eFind:
147 : case SelectionType::eURLSecondary:
148 : case SelectionType::eURLStrikeout:
149 108 : return true;
150 : default:
151 0 : return false;
152 : }
153 : }
154 :
155 : SelectionType
156 108 : ToSelectionType(RawSelectionType aRawSelectionType)
157 : {
158 108 : if (!IsValidSelectionType(aRawSelectionType)) {
159 0 : return SelectionType::eInvalid;
160 : }
161 108 : return static_cast<SelectionType>(aRawSelectionType);
162 : }
163 :
164 : RawSelectionType
165 37 : ToRawSelectionType(SelectionType aSelectionType)
166 : {
167 37 : return static_cast<RawSelectionType>(aSelectionType);
168 : }
169 :
170 14 : bool operator &(SelectionType aSelectionType,
171 : RawSelectionType aRawSelectionTypes)
172 : {
173 14 : return (ToRawSelectionType(aSelectionType) & aRawSelectionTypes) != 0;
174 : }
175 :
176 : } // namespace mozilla
177 :
178 : //#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
179 : //#define DEBUG_NAVIGATION
180 :
181 :
182 : //#define DEBUG_TABLE_SELECTION 1
183 :
184 : struct CachedOffsetForFrame {
185 1 : CachedOffsetForFrame()
186 1 : : mCachedFrameOffset(0, 0) // nsPoint ctor
187 : , mLastCaretFrame(nullptr)
188 : , mLastContentOffset(0)
189 1 : , mCanCacheFrameOffset(false)
190 1 : {}
191 :
192 : nsPoint mCachedFrameOffset; // cached frame offset
193 : nsIFrame* mLastCaretFrame; // store the frame the caret was last drawn in.
194 : int32_t mLastContentOffset; // store last content offset
195 : bool mCanCacheFrameOffset; // cached frame offset is valid?
196 : };
197 :
198 : class nsAutoScrollTimer final : public nsITimerCallback
199 : {
200 : public:
201 :
202 : NS_DECL_ISUPPORTS
203 :
204 0 : nsAutoScrollTimer()
205 0 : : mFrameSelection(0), mSelection(0), mPresContext(0), mPoint(0,0), mDelay(30)
206 : {
207 0 : }
208 :
209 : // aPoint is relative to aPresContext's root frame
210 0 : nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint)
211 : {
212 0 : mPoint = aPoint;
213 :
214 : // Store the presentation context. The timer will be
215 : // stopped by the selection if the prescontext is destroyed.
216 0 : mPresContext = aPresContext;
217 :
218 0 : mContent = nsIPresShell::GetCapturingContent();
219 :
220 0 : if (!mTimer) {
221 : nsresult result;
222 0 : mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
223 0 : mTimer->SetTarget(
224 0 : mPresContext->Document()->EventTargetFor(TaskCategory::Other));
225 :
226 0 : if (NS_FAILED(result)) {
227 0 : return result;
228 : }
229 : }
230 :
231 0 : return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
232 : }
233 :
234 0 : nsresult Stop()
235 : {
236 0 : if (mTimer) {
237 0 : mTimer->Cancel();
238 0 : mTimer = nullptr;
239 : }
240 :
241 0 : mContent = nullptr;
242 0 : return NS_OK;
243 : }
244 :
245 0 : nsresult Init(nsFrameSelection* aFrameSelection, Selection* aSelection)
246 : {
247 0 : mFrameSelection = aFrameSelection;
248 0 : mSelection = aSelection;
249 0 : return NS_OK;
250 : }
251 :
252 0 : nsresult SetDelay(uint32_t aDelay)
253 : {
254 0 : mDelay = aDelay;
255 0 : return NS_OK;
256 : }
257 :
258 0 : NS_IMETHOD Notify(nsITimer *timer) override
259 : {
260 0 : if (mSelection && mPresContext)
261 : {
262 : AutoWeakFrame frame =
263 0 : mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nullptr;
264 0 : if (!frame) {
265 0 : return NS_OK;
266 : }
267 0 : mContent = nullptr;
268 :
269 : nsPoint pt = mPoint -
270 0 : frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
271 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
272 0 : frameSelection->HandleDrag(frame, pt);
273 0 : if (!frame.IsAlive()) {
274 0 : return NS_OK;
275 : }
276 :
277 0 : NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
278 0 : mSelection->DoAutoScroll(frame, pt);
279 : }
280 0 : return NS_OK;
281 : }
282 :
283 : protected:
284 0 : virtual ~nsAutoScrollTimer()
285 0 : {
286 0 : if (mTimer) {
287 0 : mTimer->Cancel();
288 : }
289 0 : }
290 :
291 : private:
292 : nsFrameSelection *mFrameSelection;
293 : Selection* mSelection;
294 : nsPresContext *mPresContext;
295 : // relative to mPresContext's root frame
296 : nsPoint mPoint;
297 : nsCOMPtr<nsITimer> mTimer;
298 : nsCOMPtr<nsIContent> mContent;
299 : uint32_t mDelay;
300 : };
301 :
302 0 : NS_IMPL_ISUPPORTS(nsAutoScrollTimer, nsITimerCallback)
303 :
304 0 : nsresult NS_NewDomSelection(nsISelection **aDomSelection)
305 : {
306 0 : Selection* rlist = new Selection;
307 0 : *aDomSelection = (nsISelection *)rlist;
308 0 : NS_ADDREF(rlist);
309 0 : return NS_OK;
310 : }
311 :
312 : /*
313 : The limiter is used specifically for the text areas and textfields
314 : In that case it is the DIV tag that is anonymously created for the text
315 : areas/fields. Text nodes and BR nodes fall beneath it. In the case of a
316 : BR node the limiter will be the parent and the offset will point before or
317 : after the BR node. In the case of the text node the parent content is
318 : the text node itself and the offset will be the exact character position.
319 : The offset is not important to check for validity. Simply look at the
320 : passed in content. If it equals the limiter then the selection point is valid.
321 : If its parent it the limiter then the point is also valid. In the case of
322 : NO limiter all points are valid since you are in a topmost iframe. (browser
323 : or composer)
324 : */
325 : bool
326 7 : IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode)
327 : {
328 7 : if (!aFrameSel || !aNode)
329 0 : return false;
330 :
331 7 : nsIContent *limiter = aFrameSel->GetLimiter();
332 7 : if (limiter && limiter != aNode && limiter != aNode->GetParent()) {
333 : //if newfocus == the limiter. that's ok. but if not there and not parent bad
334 0 : return false; //not in the right content. tLimiter said so
335 : }
336 :
337 7 : limiter = aFrameSel->GetAncestorLimiter();
338 7 : return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter);
339 : }
340 :
341 : namespace mozilla {
342 : struct MOZ_RAII AutoPrepareFocusRange
343 : {
344 : AutoPrepareFocusRange(Selection* aSelection,
345 : bool aContinueSelection,
346 : bool aMultipleSelection
347 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
348 : {
349 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
350 :
351 : if (aSelection->mRanges.Length() <= 1) {
352 : return;
353 : }
354 :
355 : if (aSelection->mFrameSelection->IsUserSelectionReason()) {
356 : mUserSelect.emplace(aSelection);
357 : }
358 : bool userSelection = aSelection->mUserInitiated;
359 :
360 : nsTArray<RangeData>& ranges = aSelection->mRanges;
361 : if (!userSelection ||
362 : (!aContinueSelection && aMultipleSelection)) {
363 : // Scripted command or the user is starting a new explicit multi-range
364 : // selection.
365 : for (RangeData& entry : ranges) {
366 : entry.mRange->SetIsGenerated(false);
367 : }
368 : return;
369 : }
370 :
371 : int16_t reason = aSelection->mFrameSelection->mSelectionChangeReason;
372 : bool isAnchorRelativeOp = (reason & (nsISelectionListener::DRAG_REASON |
373 : nsISelectionListener::MOUSEDOWN_REASON |
374 : nsISelectionListener::MOUSEUP_REASON |
375 : nsISelectionListener::COLLAPSETOSTART_REASON));
376 : if (!isAnchorRelativeOp) {
377 : return;
378 : }
379 :
380 : // This operation is against the anchor but our current mAnchorFocusRange
381 : // represents the focus in a multi-range selection. The anchor from a user
382 : // perspective is the most distant generated range on the opposite side.
383 : // Find that range and make it the mAnchorFocusRange.
384 : const size_t len = ranges.Length();
385 : size_t newAnchorFocusIndex = size_t(-1);
386 : if (aSelection->GetDirection() == eDirNext) {
387 : for (size_t i = 0; i < len; ++i) {
388 : if (ranges[i].mRange->IsGenerated()) {
389 : newAnchorFocusIndex = i;
390 : break;
391 : }
392 : }
393 : } else {
394 : size_t i = len;
395 : while (i--) {
396 : if (ranges[i].mRange->IsGenerated()) {
397 : newAnchorFocusIndex = i;
398 : break;
399 : }
400 : }
401 : }
402 :
403 : if (newAnchorFocusIndex == size_t(-1)) {
404 : // There are no generated ranges - that's fine.
405 : return;
406 : }
407 :
408 : // Setup the new mAnchorFocusRange and mark the old one as generated.
409 : if (aSelection->mAnchorFocusRange) {
410 : aSelection->mAnchorFocusRange->SetIsGenerated(true);
411 : }
412 : nsRange* range = ranges[newAnchorFocusIndex].mRange;
413 : range->SetIsGenerated(false);
414 : aSelection->mAnchorFocusRange = range;
415 :
416 : // Remove all generated ranges (including the old mAnchorFocusRange).
417 : RefPtr<nsPresContext> presContext = aSelection->GetPresContext();
418 : size_t i = len;
419 : while (i--) {
420 : range = aSelection->mRanges[i].mRange;
421 : if (range->IsGenerated()) {
422 : range->SetSelection(nullptr);
423 : aSelection->SelectFrames(presContext, range, false);
424 : aSelection->mRanges.RemoveElementAt(i);
425 : }
426 : }
427 : if (aSelection->mFrameSelection) {
428 : aSelection->mFrameSelection->InvalidateDesiredPos();
429 : }
430 : }
431 :
432 : Maybe<Selection::AutoUserInitiated> mUserSelect;
433 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
434 : };
435 :
436 : } // namespace mozilla
437 :
438 :
439 : #ifdef PRINT_RANGE
440 : void printRange(nsRange *aDomRange)
441 : {
442 : if (!aDomRange)
443 : {
444 : printf("NULL nsIDOMRange\n");
445 : }
446 : nsINode* startNode = aDomRange->GetStartContainer();
447 : nsINode* endNode = aDomRange->GetEndContainer();
448 : int32_t startOffset = aDomRange->StartOffset();
449 : int32_t endOffset = aDomRange->EndOffset();
450 :
451 : printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
452 : (unsigned long)aDomRange,
453 : (unsigned long)startNode, (long)startOffset,
454 : (unsigned long)endNode, (long)endOffset);
455 :
456 : }
457 : #endif /* PRINT_RANGE */
458 :
459 : NS_IMETHODIMP
460 0 : Selection::ToString(nsAString& aReturn)
461 : {
462 : // We need FlushType::Frames here to make sure frames have been created for
463 : // the selected content. Use mFrameSelection->GetShell() which returns
464 : // null if the Selection has been disconnected (the shell is Destroyed).
465 : nsCOMPtr<nsIPresShell> shell =
466 0 : mFrameSelection ? mFrameSelection->GetShell() : nullptr;
467 0 : if (!shell) {
468 0 : aReturn.Truncate();
469 0 : return NS_OK;
470 : }
471 0 : shell->FlushPendingNotifications(FlushType::Frames);
472 :
473 : return ToStringWithFormat("text/plain",
474 : nsIDocumentEncoder::SkipInvisibleContent,
475 0 : 0, aReturn);
476 : }
477 :
478 : void
479 0 : Selection::Stringify(nsAString& aResult)
480 : {
481 : // Eat the error code
482 0 : ToString(aResult);
483 0 : }
484 :
485 : NS_IMETHODIMP
486 0 : Selection::ToStringWithFormat(const char* aFormatType, uint32_t aFlags,
487 : int32_t aWrapCol, nsAString& aReturn)
488 : {
489 0 : ErrorResult result;
490 0 : NS_ConvertUTF8toUTF16 format(aFormatType);
491 0 : ToStringWithFormat(format, aFlags, aWrapCol, aReturn, result);
492 0 : if (result.Failed()) {
493 0 : return result.StealNSResult();
494 : }
495 0 : return NS_OK;
496 : }
497 :
498 : void
499 0 : Selection::ToStringWithFormat(const nsAString& aFormatType, uint32_t aFlags,
500 : int32_t aWrapCol, nsAString& aReturn,
501 : ErrorResult& aRv)
502 : {
503 0 : nsresult rv = NS_OK;
504 0 : NS_ConvertUTF8toUTF16 formatType( NS_DOC_ENCODER_CONTRACTID_BASE );
505 0 : formatType.Append(aFormatType);
506 : nsCOMPtr<nsIDocumentEncoder> encoder =
507 0 : do_CreateInstance(NS_ConvertUTF16toUTF8(formatType).get(), &rv);
508 0 : if (NS_FAILED(rv)) {
509 0 : aRv.Throw(rv);
510 0 : return;
511 : }
512 :
513 0 : nsIPresShell* shell = GetPresShell();
514 0 : if (!shell) {
515 0 : aRv.Throw(NS_ERROR_FAILURE);
516 0 : return;
517 : }
518 :
519 0 : nsIDocument *doc = shell->GetDocument();
520 :
521 0 : nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
522 0 : NS_ASSERTION(domDoc, "Need a document");
523 :
524 : // Flags should always include OutputSelectionOnly if we're coming from here:
525 0 : aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
526 0 : nsAutoString readstring;
527 0 : readstring.Assign(aFormatType);
528 0 : rv = encoder->Init(domDoc, readstring, aFlags);
529 0 : if (NS_FAILED(rv)) {
530 0 : aRv.Throw(rv);
531 0 : return;
532 : }
533 :
534 0 : encoder->SetSelection(this);
535 0 : if (aWrapCol != 0)
536 0 : encoder->SetWrapColumn(aWrapCol);
537 :
538 0 : rv = encoder->EncodeToString(aReturn);
539 0 : if (NS_FAILED(rv)) {
540 0 : aRv.Throw(rv);
541 : }
542 : }
543 :
544 : NS_IMETHODIMP
545 8 : Selection::SetInterlinePosition(bool aHintRight)
546 : {
547 16 : ErrorResult result;
548 8 : SetInterlinePosition(aHintRight, result);
549 8 : if (result.Failed()) {
550 0 : return result.StealNSResult();
551 : }
552 8 : return NS_OK;
553 : }
554 :
555 : void
556 8 : Selection::SetInterlinePosition(bool aHintRight, ErrorResult& aRv)
557 : {
558 8 : if (!mFrameSelection) {
559 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
560 0 : return;
561 : }
562 8 : mFrameSelection->SetHint(aHintRight ? CARET_ASSOCIATE_AFTER : CARET_ASSOCIATE_BEFORE);
563 : }
564 :
565 : NS_IMETHODIMP
566 0 : Selection::GetInterlinePosition(bool* aHintRight)
567 : {
568 0 : ErrorResult result;
569 0 : *aHintRight = GetInterlinePosition(result);
570 0 : if (result.Failed()) {
571 0 : return result.StealNSResult();
572 : }
573 0 : return NS_OK;
574 : }
575 :
576 : bool
577 0 : Selection::GetInterlinePosition(ErrorResult& aRv)
578 : {
579 0 : if (!mFrameSelection) {
580 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
581 0 : return false;
582 : }
583 0 : return mFrameSelection->GetHint() == CARET_ASSOCIATE_AFTER;
584 : }
585 :
586 : Nullable<int16_t>
587 0 : Selection::GetCaretBidiLevel(mozilla::ErrorResult& aRv) const
588 : {
589 0 : if (!mFrameSelection) {
590 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED);
591 0 : return Nullable<int16_t>();
592 : }
593 0 : nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
594 : return (caretBidiLevel & BIDI_LEVEL_UNDEFINED) ?
595 0 : Nullable<int16_t>() : Nullable<int16_t>(caretBidiLevel);
596 : }
597 :
598 : void
599 0 : Selection::SetCaretBidiLevel(const Nullable<int16_t>& aCaretBidiLevel, mozilla::ErrorResult& aRv)
600 : {
601 0 : if (!mFrameSelection) {
602 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED);
603 0 : return;
604 : }
605 0 : if (aCaretBidiLevel.IsNull()) {
606 0 : mFrameSelection->UndefineCaretBidiLevel();
607 : } else {
608 0 : mFrameSelection->SetCaretBidiLevel(aCaretBidiLevel.Value());
609 : }
610 : }
611 :
612 : nsresult
613 10 : Selection::GetTableCellLocationFromRange(nsRange* aRange,
614 : int32_t* aSelectionType,
615 : int32_t* aRow, int32_t* aCol)
616 : {
617 10 : if (!aRange || !aSelectionType || !aRow || !aCol)
618 0 : return NS_ERROR_NULL_POINTER;
619 :
620 10 : *aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
621 10 : *aRow = 0;
622 10 : *aCol = 0;
623 :
624 : // Must have access to frame selection to get cell info
625 10 : if (!mFrameSelection) return NS_OK;
626 :
627 10 : nsresult result = GetTableSelectionType(aRange, aSelectionType);
628 10 : if (NS_FAILED(result)) return result;
629 :
630 : // Don't fail if range does not point to a single table cell,
631 : // let aSelectionType tell user if we don't have a cell
632 10 : if (*aSelectionType != nsISelectionPrivate::TABLESELECTION_CELL)
633 10 : return NS_OK;
634 :
635 : // Get the child content (the cell) pointed to by starting node of range
636 : // We do minimal checking since GetTableSelectionType assures
637 : // us that this really is a table cell
638 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartContainer());
639 0 : if (!content)
640 0 : return NS_ERROR_FAILURE;
641 :
642 0 : nsIContent *child = content->GetChildAt(aRange->StartOffset());
643 0 : if (!child)
644 0 : return NS_ERROR_FAILURE;
645 :
646 : //Note: This is a non-ref-counted pointer to the frame
647 0 : nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
648 0 : if (NS_FAILED(result))
649 0 : return result;
650 0 : if (!cellLayout)
651 0 : return NS_ERROR_FAILURE;
652 :
653 0 : return cellLayout->GetCellIndexes(*aRow, *aCol);
654 : }
655 :
656 : nsresult
657 10 : Selection::AddTableCellRange(nsRange* aRange, bool* aDidAddRange,
658 : int32_t* aOutIndex)
659 : {
660 10 : if (!aDidAddRange || !aOutIndex)
661 0 : return NS_ERROR_NULL_POINTER;
662 :
663 10 : *aDidAddRange = false;
664 10 : *aOutIndex = -1;
665 :
666 10 : if (!mFrameSelection)
667 0 : return NS_OK;
668 :
669 10 : if (!aRange)
670 0 : return NS_ERROR_NULL_POINTER;
671 :
672 : nsresult result;
673 :
674 : // Get if we are adding a cell selection and the row, col of cell if we are
675 : int32_t newRow, newCol, tableMode;
676 10 : result = GetTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
677 10 : if (NS_FAILED(result)) return result;
678 :
679 : // If not adding a cell range, we are done here
680 10 : if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL)
681 : {
682 10 : mFrameSelection->mSelectingTableCellMode = tableMode;
683 : // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
684 10 : return NS_OK;
685 : }
686 :
687 : // Set frame selection mode only if not already set to a table mode
688 : // so we don't lose the select row and column flags (not detected by getTableCellLocation)
689 0 : if (mFrameSelection->mSelectingTableCellMode == TABLESELECTION_NONE)
690 0 : mFrameSelection->mSelectingTableCellMode = tableMode;
691 :
692 0 : *aDidAddRange = true;
693 0 : return AddItem(aRange, aOutIndex);
694 : }
695 :
696 : //TODO: Figure out TABLESELECTION_COLUMN and TABLESELECTION_ALLCELLS
697 : nsresult
698 10 : Selection::GetTableSelectionType(nsIDOMRange* aDOMRange,
699 : int32_t* aTableSelectionType)
700 : {
701 10 : if (!aDOMRange || !aTableSelectionType)
702 0 : return NS_ERROR_NULL_POINTER;
703 10 : nsRange* range = static_cast<nsRange*>(aDOMRange);
704 :
705 10 : *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
706 :
707 : // Must have access to frame selection to get cell info
708 10 : if(!mFrameSelection) return NS_OK;
709 :
710 10 : nsINode* startNode = range->GetStartContainer();
711 10 : if (!startNode) return NS_ERROR_FAILURE;
712 :
713 10 : nsINode* endNode = range->GetEndContainer();
714 10 : if (!endNode) return NS_ERROR_FAILURE;
715 :
716 : // Not a single selected node
717 10 : if (startNode != endNode) return NS_OK;
718 :
719 10 : int32_t startOffset = range->StartOffset();
720 10 : int32_t endOffset = range->EndOffset();
721 :
722 : // Not a single selected node
723 10 : if ((endOffset - startOffset) != 1)
724 10 : return NS_OK;
725 :
726 0 : nsIContent* startContent = static_cast<nsIContent*>(startNode);
727 0 : if (!(startNode->IsElement() && startContent->IsHTMLElement())) {
728 : // Implies a check for being an element; if we ever make this work
729 : // for non-HTML, need to keep checking for elements.
730 0 : return NS_OK;
731 : }
732 :
733 0 : if (startContent->IsHTMLElement(nsGkAtoms::tr))
734 : {
735 0 : *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
736 : }
737 : else //check to see if we are selecting a table or row (column and all cells not done yet)
738 : {
739 0 : nsIContent *child = startNode->GetChildAt(startOffset);
740 0 : if (!child)
741 0 : return NS_ERROR_FAILURE;
742 :
743 0 : if (child->IsHTMLElement(nsGkAtoms::table))
744 0 : *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE;
745 0 : else if (child->IsHTMLElement(nsGkAtoms::tr))
746 0 : *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
747 : }
748 :
749 0 : return NS_OK;
750 : }
751 :
752 0 : Selection::Selection()
753 : : mCachedOffsetForFrame(nullptr)
754 : , mDirection(eDirNext)
755 : , mSelectionType(SelectionType::eNormal)
756 : , mCustomColors(nullptr)
757 : , mUserInitiated(false)
758 : , mCalledByJS(false)
759 0 : , mSelectionChangeBlockerCount(0)
760 : {
761 0 : }
762 :
763 320 : Selection::Selection(nsFrameSelection* aList)
764 : : mFrameSelection(aList)
765 : , mCachedOffsetForFrame(nullptr)
766 : , mDirection(eDirNext)
767 : , mSelectionType(SelectionType::eNormal)
768 : , mCustomColors(nullptr)
769 : , mUserInitiated(false)
770 : , mCalledByJS(false)
771 320 : , mSelectionChangeBlockerCount(0)
772 : {
773 320 : }
774 :
775 0 : Selection::~Selection()
776 : {
777 0 : SetAnchorFocusRange(-1);
778 :
779 0 : uint32_t count = mRanges.Length();
780 0 : for (uint32_t i = 0; i < count; ++i) {
781 0 : mRanges[i].mRange->SetSelection(nullptr);
782 : }
783 :
784 0 : if (mAutoScrollTimer) {
785 0 : mAutoScrollTimer->Stop();
786 0 : mAutoScrollTimer = nullptr;
787 : }
788 :
789 0 : mScrollEvent.Revoke();
790 :
791 0 : if (mCachedOffsetForFrame) {
792 0 : delete mCachedOffsetForFrame;
793 0 : mCachedOffsetForFrame = nullptr;
794 : }
795 0 : }
796 :
797 : nsIDocument*
798 37 : Selection::GetParentObject() const
799 : {
800 37 : nsIPresShell* shell = GetPresShell();
801 37 : if (shell) {
802 37 : return shell->GetDocument();
803 : }
804 0 : return nullptr;
805 : }
806 :
807 : NS_IMPL_CYCLE_COLLECTION_CLASS(Selection)
808 :
809 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Selection)
810 : // Unlink the selection listeners *before* we do RemoveAllRanges since
811 : // we don't want to notify the listeners during JS GC (they could be
812 : // in JS!).
813 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionListeners)
814 0 : tmp->RemoveAllRanges();
815 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameSelection)
816 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
817 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
818 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Selection)
819 : {
820 0 : uint32_t i, count = tmp->mRanges.Length();
821 0 : for (i = 0; i < count; ++i) {
822 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRanges[i].mRange)
823 : }
824 : }
825 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchorFocusRange)
826 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameSelection)
827 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionListeners)
828 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
829 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Selection)
830 :
831 : // QueryInterface implementation for Selection
832 501 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Selection)
833 181 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
834 175 : NS_INTERFACE_MAP_ENTRY(nsISelection)
835 72 : NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
836 28 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
837 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
838 0 : NS_INTERFACE_MAP_END
839 :
840 725 : NS_IMPL_CYCLE_COLLECTING_ADDREF(Selection)
841 397 : NS_IMPL_CYCLE_COLLECTING_RELEASE(Selection)
842 :
843 :
844 : NS_IMETHODIMP
845 0 : Selection::GetAnchorNode(nsIDOMNode** aAnchorNode)
846 : {
847 0 : nsINode* anchorNode = GetAnchorNode();
848 0 : if (anchorNode) {
849 0 : return CallQueryInterface(anchorNode, aAnchorNode);
850 : }
851 :
852 0 : *aAnchorNode = nullptr;
853 0 : return NS_OK;
854 : }
855 :
856 : nsINode*
857 2 : Selection::GetAnchorNode()
858 : {
859 2 : if (!mAnchorFocusRange)
860 2 : return nullptr;
861 :
862 0 : if (GetDirection() == eDirNext) {
863 0 : return mAnchorFocusRange->GetStartContainer();
864 : }
865 :
866 0 : return mAnchorFocusRange->GetEndContainer();
867 : }
868 :
869 : NS_IMETHODIMP
870 2 : Selection::GetAnchorOffset(int32_t* aAnchorOffset)
871 : {
872 2 : *aAnchorOffset = static_cast<int32_t>(AnchorOffset());
873 2 : return NS_OK;
874 : }
875 :
876 : // note: this can return a nil focus node
877 : NS_IMETHODIMP
878 0 : Selection::GetFocusNode(nsIDOMNode** aFocusNode)
879 : {
880 0 : nsINode* focusNode = GetFocusNode();
881 0 : if (focusNode) {
882 0 : return CallQueryInterface(focusNode, aFocusNode);
883 : }
884 :
885 0 : *aFocusNode = nullptr;
886 0 : return NS_OK;
887 : }
888 :
889 : nsINode*
890 3 : Selection::GetFocusNode()
891 : {
892 3 : if (!mAnchorFocusRange)
893 2 : return nullptr;
894 :
895 1 : if (GetDirection() == eDirNext){
896 1 : return mAnchorFocusRange->GetEndContainer();
897 : }
898 :
899 0 : return mAnchorFocusRange->GetStartContainer();
900 : }
901 :
902 : NS_IMETHODIMP
903 0 : Selection::GetFocusOffset(int32_t* aFocusOffset)
904 : {
905 0 : *aFocusOffset = static_cast<int32_t>(FocusOffset());
906 0 : return NS_OK;
907 : }
908 :
909 : void
910 99 : Selection::SetAnchorFocusRange(int32_t indx)
911 : {
912 99 : if (indx >= (int32_t)mRanges.Length())
913 0 : return;
914 99 : if (indx < 0) //release all
915 : {
916 82 : mAnchorFocusRange = nullptr;
917 : }
918 : else{
919 17 : mAnchorFocusRange = mRanges[indx].mRange;
920 : }
921 : }
922 :
923 : uint32_t
924 2 : Selection::AnchorOffset()
925 : {
926 2 : if (!mAnchorFocusRange)
927 2 : return 0;
928 :
929 0 : if (GetDirection() == eDirNext){
930 0 : return mAnchorFocusRange->StartOffset();
931 : }
932 :
933 0 : return mAnchorFocusRange->EndOffset();
934 : }
935 :
936 : uint32_t
937 1 : Selection::FocusOffset()
938 : {
939 1 : if (!mAnchorFocusRange)
940 0 : return 0;
941 :
942 1 : if (GetDirection() == eDirNext){
943 1 : return mAnchorFocusRange->EndOffset();
944 : }
945 :
946 0 : return mAnchorFocusRange->StartOffset();
947 : }
948 :
949 : static nsresult
950 4 : CompareToRangeStart(nsINode* aCompareNode, int32_t aCompareOffset,
951 : nsRange* aRange, int32_t* aCmp)
952 : {
953 4 : nsINode* start = aRange->GetStartContainer();
954 4 : NS_ENSURE_STATE(aCompareNode && start);
955 : // If the nodes that we're comparing are not in the same document,
956 : // assume that aCompareNode will fall at the end of the ranges.
957 8 : if (aCompareNode->GetComposedDoc() != start->GetComposedDoc() ||
958 4 : !start->GetComposedDoc()) {
959 0 : *aCmp = 1;
960 : } else {
961 4 : *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
962 : start, aRange->StartOffset());
963 : }
964 4 : return NS_OK;
965 : }
966 :
967 : static nsresult
968 4 : CompareToRangeEnd(nsINode* aCompareNode, int32_t aCompareOffset,
969 : nsRange* aRange, int32_t* aCmp)
970 : {
971 4 : nsINode* end = aRange->GetEndContainer();
972 4 : NS_ENSURE_STATE(aCompareNode && end);
973 : // If the nodes that we're comparing are not in the same document,
974 : // assume that aCompareNode will fall at the end of the ranges.
975 8 : if (aCompareNode->GetComposedDoc() != end->GetComposedDoc() ||
976 4 : !end->GetComposedDoc()) {
977 0 : *aCmp = 1;
978 : } else {
979 4 : *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
980 : end, aRange->EndOffset());
981 : }
982 4 : return NS_OK;
983 : }
984 :
985 : // Selection::FindInsertionPoint
986 : //
987 : // Binary searches the given sorted array of ranges for the insertion point
988 : // for the given node/offset. The given comparator is used, and the index
989 : // where the point should appear in the array is placed in *aInsertionPoint.
990 : //
991 : // If there is an item in the array equal to the input point, we will return
992 : // the index of this item.
993 :
994 : nsresult
995 8 : Selection::FindInsertionPoint(
996 : nsTArray<RangeData>* aElementArray,
997 : nsINode* aPointNode, int32_t aPointOffset,
998 : nsresult (*aComparator)(nsINode*,int32_t,nsRange*,int32_t*),
999 : int32_t* aPoint)
1000 : {
1001 8 : *aPoint = 0;
1002 8 : int32_t beginSearch = 0;
1003 8 : int32_t endSearch = aElementArray->Length(); // one beyond what to check
1004 :
1005 8 : if (endSearch) {
1006 8 : int32_t center = endSearch - 1; // Check last index, then binary search
1007 0 : do {
1008 8 : nsRange* range = (*aElementArray)[center].mRange;
1009 :
1010 : int32_t cmp;
1011 8 : nsresult rv = aComparator(aPointNode, aPointOffset, range, &cmp);
1012 8 : NS_ENSURE_SUCCESS(rv, rv);
1013 :
1014 8 : if (cmp < 0) { // point < cur
1015 2 : endSearch = center;
1016 6 : } else if (cmp > 0) { // point > cur
1017 4 : beginSearch = center + 1;
1018 : } else { // found match, done
1019 2 : beginSearch = center;
1020 2 : break;
1021 : }
1022 6 : center = (endSearch - beginSearch) / 2 + beginSearch;
1023 6 : } while (endSearch - beginSearch > 0);
1024 : }
1025 :
1026 8 : *aPoint = beginSearch;
1027 8 : return NS_OK;
1028 : }
1029 :
1030 : // Selection::SubtractRange
1031 : //
1032 : // A helper function that subtracts aSubtract from aRange, and adds
1033 : // 1 or 2 RangeData objects representing the remaining non-overlapping
1034 : // difference to aOutput. It is assumed that the caller has checked that
1035 : // aRange and aSubtract do indeed overlap
1036 :
1037 : nsresult
1038 0 : Selection::SubtractRange(RangeData* aRange, nsRange* aSubtract,
1039 : nsTArray<RangeData>* aOutput)
1040 : {
1041 0 : nsRange* range = aRange->mRange;
1042 :
1043 : // First we want to compare to the range start
1044 : int32_t cmp;
1045 0 : nsresult rv = CompareToRangeStart(range->GetStartContainer(),
1046 : range->StartOffset(),
1047 0 : aSubtract, &cmp);
1048 0 : NS_ENSURE_SUCCESS(rv, rv);
1049 :
1050 : // Also, make a comparison to the range end
1051 : int32_t cmp2;
1052 0 : rv = CompareToRangeEnd(range->GetEndContainer(),
1053 : range->EndOffset(),
1054 0 : aSubtract, &cmp2);
1055 0 : NS_ENSURE_SUCCESS(rv, rv);
1056 :
1057 : // If the existing range left overlaps the new range (aSubtract) then
1058 : // cmp < 0, and cmp2 < 0
1059 : // If it right overlaps the new range then cmp > 0 and cmp2 > 0
1060 : // If it fully contains the new range, then cmp < 0 and cmp2 > 0
1061 :
1062 0 : if (cmp2 > 0) {
1063 : // We need to add a new RangeData to the output, running from
1064 : // the end of aSubtract to the end of range
1065 0 : RefPtr<nsRange> postOverlap = new nsRange(aSubtract->GetEndContainer());
1066 0 : rv = postOverlap->SetStartAndEnd(
1067 : aSubtract->GetEndContainer(), aSubtract->EndOffset(),
1068 0 : range->GetEndContainer(), range->EndOffset());
1069 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1070 0 : return rv;
1071 : }
1072 0 : if (!postOverlap->Collapsed()) {
1073 0 : if (!aOutput->InsertElementAt(0, RangeData(postOverlap)))
1074 0 : return NS_ERROR_OUT_OF_MEMORY;
1075 0 : (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
1076 : }
1077 : }
1078 :
1079 0 : if (cmp < 0) {
1080 : // We need to add a new RangeData to the output, running from
1081 : // the start of the range to the start of aSubtract
1082 0 : RefPtr<nsRange> preOverlap = new nsRange(range->GetStartContainer());
1083 0 : rv = preOverlap->SetStartAndEnd(range->GetStartContainer(),
1084 : range->StartOffset(),
1085 : aSubtract->GetStartContainer(),
1086 0 : aSubtract->StartOffset());
1087 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1088 0 : return rv;
1089 : }
1090 0 : if (!preOverlap->Collapsed()) {
1091 0 : if (!aOutput->InsertElementAt(0, RangeData(preOverlap)))
1092 0 : return NS_ERROR_OUT_OF_MEMORY;
1093 0 : (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
1094 : }
1095 : }
1096 :
1097 0 : return NS_OK;
1098 : }
1099 :
1100 : void
1101 0 : Selection::UserSelectRangesToAdd(nsRange* aItem, nsTArray<RefPtr<nsRange>>& aRangesToAdd)
1102 : {
1103 0 : aItem->ExcludeNonSelectableNodes(&aRangesToAdd);
1104 0 : if (aRangesToAdd.IsEmpty()) {
1105 0 : ErrorResult err;
1106 0 : nsINode* node = aItem->GetStartContainer(err);
1107 0 : if (node && node->IsContent() && node->AsContent()->GetEditingHost()) {
1108 : // A contenteditable node with user-select:none, for example.
1109 : // Allow it to have a collapsed selection (for the caret).
1110 0 : aItem->Collapse(GetDirection() == eDirPrevious);
1111 0 : aRangesToAdd.AppendElement(aItem);
1112 : }
1113 : }
1114 0 : }
1115 :
1116 : nsresult
1117 17 : Selection::AddItem(nsRange* aItem, int32_t* aOutIndex, bool aNoStartSelect)
1118 : {
1119 17 : if (!aItem)
1120 0 : return NS_ERROR_NULL_POINTER;
1121 17 : if (!aItem->IsPositioned())
1122 0 : return NS_ERROR_UNEXPECTED;
1123 :
1124 17 : NS_ASSERTION(aOutIndex, "aOutIndex can't be null");
1125 :
1126 17 : if (mUserInitiated) {
1127 0 : AutoTArray<RefPtr<nsRange>, 4> rangesToAdd;
1128 0 : *aOutIndex = int32_t(mRanges.Length()) - 1;
1129 :
1130 0 : nsIDocument* doc = GetParentObject();
1131 : bool selectEventsEnabled =
1132 0 : nsFrameSelection::sSelectionEventsEnabled ||
1133 0 : (doc && nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
1134 :
1135 0 : if (!aNoStartSelect &&
1136 0 : mSelectionType == SelectionType::eNormal &&
1137 0 : selectEventsEnabled && Collapsed() &&
1138 0 : !IsBlockingSelectionChangeEvents()) {
1139 : // First, we generate the ranges to add with a scratch range, which is a
1140 : // clone of the original range passed in. We do this seperately, because the
1141 : // selectstart event could have caused the world to change, and required
1142 : // ranges to be re-generated
1143 0 : RefPtr<nsRange> scratchRange = aItem->CloneRange();
1144 0 : UserSelectRangesToAdd(scratchRange, rangesToAdd);
1145 0 : bool newRangesNonEmpty = rangesToAdd.Length() > 1 ||
1146 0 : (rangesToAdd.Length() == 1 && !rangesToAdd[0]->Collapsed());
1147 :
1148 0 : MOZ_ASSERT(!newRangesNonEmpty || nsContentUtils::IsSafeToRunScript());
1149 0 : if (newRangesNonEmpty && nsContentUtils::IsSafeToRunScript()) {
1150 : // We consider a selection to be starting if we are currently collapsed,
1151 : // and the selection is becoming uncollapsed, and this is caused by a user
1152 : // initiated event.
1153 0 : bool defaultAction = true;
1154 :
1155 : // The spec currently doesn't say that we should dispatch this event
1156 : // on text controls, so for now we only support doing that under a
1157 : // pref, disabled by default.
1158 : // See https://github.com/w3c/selection-api/issues/53.
1159 0 : bool dispatchEvent = true;
1160 0 : nsCOMPtr<nsINode> target = aItem->GetStartContainer();
1161 0 : if (nsFrameSelection::sSelectionEventsOnTextControlsEnabled) {
1162 : // Get the first element which isn't in a native anonymous subtree
1163 0 : while (target && target->IsInNativeAnonymousSubtree()) {
1164 0 : target = target->GetParent();
1165 : }
1166 : } else {
1167 0 : if (target->IsInNativeAnonymousSubtree()) {
1168 : // This is a selection under a text control, so don't dispatch the
1169 : // event.
1170 0 : dispatchEvent = false;
1171 : }
1172 : }
1173 :
1174 0 : if (dispatchEvent) {
1175 0 : nsContentUtils::DispatchTrustedEvent(GetParentObject(), target,
1176 0 : NS_LITERAL_STRING("selectstart"),
1177 0 : true, true, &defaultAction);
1178 :
1179 0 : if (!defaultAction) {
1180 0 : return NS_OK;
1181 : }
1182 :
1183 : // As we just dispatched an event to the DOM, something could have
1184 : // changed under our feet. Re-generate the rangesToAdd array, and ensure
1185 : // that the range we are about to add is still valid.
1186 0 : if (!aItem->IsPositioned()) {
1187 0 : return NS_ERROR_UNEXPECTED;
1188 : }
1189 : }
1190 : }
1191 :
1192 : // The scratch ranges we generated may be invalid now, throw them out
1193 0 : rangesToAdd.ClearAndRetainStorage();
1194 : }
1195 :
1196 : // Generate the ranges to add
1197 0 : UserSelectRangesToAdd(aItem, rangesToAdd);
1198 : size_t newAnchorFocusIndex =
1199 0 : GetDirection() == eDirPrevious ? 0 : rangesToAdd.Length() - 1;
1200 0 : for (size_t i = 0; i < rangesToAdd.Length(); ++i) {
1201 : int32_t index;
1202 0 : nsresult rv = AddItemInternal(rangesToAdd[i], &index);
1203 0 : NS_ENSURE_SUCCESS(rv, rv);
1204 0 : if (i == newAnchorFocusIndex) {
1205 0 : *aOutIndex = index;
1206 0 : rangesToAdd[i]->SetIsGenerated(false);
1207 : } else {
1208 0 : rangesToAdd[i]->SetIsGenerated(true);
1209 : }
1210 : }
1211 0 : return NS_OK;
1212 : }
1213 17 : return AddItemInternal(aItem, aOutIndex);
1214 : }
1215 :
1216 : nsresult
1217 17 : Selection::AddItemInternal(nsRange* aItem, int32_t* aOutIndex)
1218 : {
1219 17 : MOZ_ASSERT(aItem);
1220 17 : MOZ_ASSERT(aItem->IsPositioned());
1221 17 : MOZ_ASSERT(aOutIndex);
1222 :
1223 17 : *aOutIndex = -1;
1224 :
1225 : // a common case is that we have no ranges yet
1226 17 : if (mRanges.Length() == 0) {
1227 17 : if (!mRanges.AppendElement(RangeData(aItem)))
1228 0 : return NS_ERROR_OUT_OF_MEMORY;
1229 17 : aItem->SetSelection(this);
1230 :
1231 17 : *aOutIndex = 0;
1232 17 : return NS_OK;
1233 : }
1234 :
1235 : int32_t startIndex, endIndex;
1236 0 : nsresult rv = GetIndicesForInterval(aItem->GetStartContainer(),
1237 : aItem->StartOffset(),
1238 : aItem->GetEndContainer(),
1239 : aItem->EndOffset(), false,
1240 0 : &startIndex, &endIndex);
1241 0 : NS_ENSURE_SUCCESS(rv, rv);
1242 :
1243 0 : if (endIndex == -1) {
1244 : // All ranges start after the given range. We can insert our range at
1245 : // position 0, knowing there are no overlaps (handled below)
1246 0 : startIndex = 0;
1247 0 : endIndex = 0;
1248 0 : } else if (startIndex == -1) {
1249 : // All ranges end before the given range. We can insert our range at
1250 : // the end of the array, knowing there are no overlaps (handled below)
1251 0 : startIndex = mRanges.Length();
1252 0 : endIndex = startIndex;
1253 : }
1254 :
1255 : // If the range is already contained in mRanges, silently succeed
1256 0 : bool sameRange = EqualsRangeAtPoint(aItem->GetStartContainer(),
1257 : aItem->StartOffset(),
1258 : aItem->GetEndContainer(),
1259 0 : aItem->EndOffset(), startIndex);
1260 0 : if (sameRange) {
1261 0 : *aOutIndex = startIndex;
1262 0 : return NS_OK;
1263 : }
1264 :
1265 0 : if (startIndex == endIndex) {
1266 : // The new range doesn't overlap any existing ranges
1267 0 : if (!mRanges.InsertElementAt(startIndex, RangeData(aItem)))
1268 0 : return NS_ERROR_OUT_OF_MEMORY;
1269 0 : aItem->SetSelection(this);
1270 0 : *aOutIndex = startIndex;
1271 0 : return NS_OK;
1272 : }
1273 :
1274 : // We now know that at least 1 existing range overlaps with the range that
1275 : // we are trying to add. In fact, the only ranges of interest are those at
1276 : // the two end points, startIndex and endIndex - 1 (which may point to the
1277 : // same range) as these may partially overlap the new range. Any ranges
1278 : // between these indices are fully overlapped by the new range, and so can be
1279 : // removed
1280 0 : nsTArray<RangeData> overlaps;
1281 0 : if (!overlaps.InsertElementAt(0, mRanges[startIndex]))
1282 0 : return NS_ERROR_OUT_OF_MEMORY;
1283 :
1284 0 : if (endIndex - 1 != startIndex) {
1285 0 : if (!overlaps.InsertElementAt(1, mRanges[endIndex - 1]))
1286 0 : return NS_ERROR_OUT_OF_MEMORY;
1287 : }
1288 :
1289 : // Remove all the overlapping ranges
1290 0 : for (int32_t i = startIndex; i < endIndex; ++i) {
1291 0 : mRanges[i].mRange->SetSelection(nullptr);
1292 : }
1293 0 : mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
1294 :
1295 0 : nsTArray<RangeData> temp;
1296 0 : for (int32_t i = overlaps.Length() - 1; i >= 0; i--) {
1297 0 : nsresult rv = SubtractRange(&overlaps[i], aItem, &temp);
1298 0 : NS_ENSURE_SUCCESS(rv, rv);
1299 : }
1300 :
1301 : // Insert the new element into our "leftovers" array
1302 : int32_t insertionPoint;
1303 0 : rv = FindInsertionPoint(&temp, aItem->GetStartContainer(),
1304 : aItem->StartOffset(), CompareToRangeStart,
1305 0 : &insertionPoint);
1306 0 : NS_ENSURE_SUCCESS(rv, rv);
1307 :
1308 0 : if (!temp.InsertElementAt(insertionPoint, RangeData(aItem)))
1309 0 : return NS_ERROR_OUT_OF_MEMORY;
1310 :
1311 : // Merge the leftovers back in to mRanges
1312 0 : if (!mRanges.InsertElementsAt(startIndex, temp))
1313 0 : return NS_ERROR_OUT_OF_MEMORY;
1314 :
1315 0 : for (uint32_t i = 0; i < temp.Length(); ++i) {
1316 0 : temp[i].mRange->SetSelection(this);
1317 : }
1318 :
1319 0 : *aOutIndex = startIndex + insertionPoint;
1320 0 : return NS_OK;
1321 : }
1322 :
1323 : nsresult
1324 0 : Selection::RemoveItem(nsRange* aItem)
1325 : {
1326 0 : if (!aItem)
1327 0 : return NS_ERROR_NULL_POINTER;
1328 :
1329 : // Find the range's index & remove it. We could use FindInsertionPoint to
1330 : // get O(log n) time, but that requires many expensive DOM comparisons.
1331 : // For even several thousand items, this is probably faster because the
1332 : // comparisons are so fast.
1333 0 : int32_t idx = -1;
1334 : uint32_t i;
1335 0 : for (i = 0; i < mRanges.Length(); i ++) {
1336 0 : if (mRanges[i].mRange == aItem) {
1337 0 : idx = (int32_t)i;
1338 0 : break;
1339 : }
1340 : }
1341 0 : if (idx < 0)
1342 0 : return NS_ERROR_DOM_NOT_FOUND_ERR;
1343 :
1344 0 : mRanges.RemoveElementAt(idx);
1345 0 : aItem->SetSelection(nullptr);
1346 0 : return NS_OK;
1347 : }
1348 :
1349 : nsresult
1350 0 : Selection::RemoveCollapsedRanges()
1351 : {
1352 0 : uint32_t i = 0;
1353 0 : while (i < mRanges.Length()) {
1354 0 : if (mRanges[i].mRange->Collapsed()) {
1355 0 : nsresult rv = RemoveItem(mRanges[i].mRange);
1356 0 : NS_ENSURE_SUCCESS(rv, rv);
1357 : } else {
1358 0 : ++i;
1359 : }
1360 : }
1361 0 : return NS_OK;
1362 : }
1363 :
1364 : nsresult
1365 82 : Selection::Clear(nsPresContext* aPresContext)
1366 : {
1367 82 : SetAnchorFocusRange(-1);
1368 :
1369 95 : for (uint32_t i = 0; i < mRanges.Length(); ++i) {
1370 13 : mRanges[i].mRange->SetSelection(nullptr);
1371 13 : SelectFrames(aPresContext, mRanges[i].mRange, false);
1372 : }
1373 82 : mRanges.Clear();
1374 :
1375 : // Reset direction so for more dependable table selection range handling
1376 82 : SetDirection(eDirNext);
1377 :
1378 : // If this was an ATTENTION selection, change it back to normal now
1379 164 : if (mFrameSelection &&
1380 82 : mFrameSelection->GetDisplaySelection() ==
1381 82 : nsISelectionController::SELECTION_ATTENTION) {
1382 0 : mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
1383 : }
1384 :
1385 82 : return NS_OK;
1386 : }
1387 :
1388 : NS_IMETHODIMP
1389 0 : Selection::GetType(int16_t* aType)
1390 : {
1391 0 : NS_ENSURE_ARG_POINTER(aType);
1392 0 : *aType = ToRawSelectionType(Type());
1393 0 : return NS_OK;
1394 : }
1395 :
1396 : // RangeMatches*Point
1397 : //
1398 : // Compares the range beginning or ending point, and returns true if it
1399 : // exactly matches the given DOM point.
1400 :
1401 : static inline bool
1402 0 : RangeMatchesBeginPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
1403 : {
1404 0 : return aRange->GetStartContainer() == aNode &&
1405 0 : aRange->StartOffset() == aOffset;
1406 : }
1407 :
1408 : static inline bool
1409 4 : RangeMatchesEndPoint(nsRange* aRange, nsINode* aNode, int32_t aOffset)
1410 : {
1411 4 : return aRange->GetEndContainer() == aNode && aRange->EndOffset() == aOffset;
1412 : }
1413 :
1414 : // Selection::EqualsRangeAtPoint
1415 : //
1416 : // Utility method for checking equivalence of two ranges.
1417 :
1418 : bool
1419 0 : Selection::EqualsRangeAtPoint(
1420 : nsINode* aBeginNode, int32_t aBeginOffset,
1421 : nsINode* aEndNode, int32_t aEndOffset,
1422 : int32_t aRangeIndex)
1423 : {
1424 0 : if (aRangeIndex >=0 && aRangeIndex < (int32_t) mRanges.Length()) {
1425 0 : nsRange* range = mRanges[aRangeIndex].mRange;
1426 0 : if (RangeMatchesBeginPoint(range, aBeginNode, aBeginOffset) &&
1427 0 : RangeMatchesEndPoint(range, aEndNode, aEndOffset))
1428 0 : return true;
1429 : }
1430 0 : return false;
1431 : }
1432 :
1433 : // Selection::GetRangesForInterval
1434 : //
1435 : // XPCOM wrapper for the nsTArray version
1436 :
1437 : NS_IMETHODIMP
1438 0 : Selection::GetRangesForInterval(nsIDOMNode* aBeginNode, int32_t aBeginOffset,
1439 : nsIDOMNode* aEndNode, int32_t aEndOffset,
1440 : bool aAllowAdjacent,
1441 : uint32_t* aResultCount,
1442 : nsIDOMRange*** aResults)
1443 : {
1444 0 : if (!aBeginNode || ! aEndNode || ! aResultCount || ! aResults)
1445 0 : return NS_ERROR_NULL_POINTER;
1446 :
1447 0 : *aResultCount = 0;
1448 0 : *aResults = nullptr;
1449 :
1450 0 : nsTArray<RefPtr<nsRange>> results;
1451 0 : ErrorResult result;
1452 0 : nsCOMPtr<nsINode> beginNode = do_QueryInterface(aBeginNode);
1453 0 : nsCOMPtr<nsINode> endNode = do_QueryInterface(aEndNode);
1454 0 : NS_ENSURE_TRUE(beginNode && endNode, NS_ERROR_NULL_POINTER);
1455 0 : GetRangesForInterval(*beginNode, aBeginOffset, *endNode, aEndOffset,
1456 0 : aAllowAdjacent, results, result);
1457 0 : if (result.Failed()) {
1458 0 : return result.StealNSResult();
1459 : }
1460 0 : *aResultCount = results.Length();
1461 0 : if (*aResultCount == 0) {
1462 0 : return NS_OK;
1463 : }
1464 :
1465 0 : *aResults = static_cast<nsIDOMRange**>
1466 0 : (moz_xmalloc(sizeof(nsIDOMRange*) * *aResultCount));
1467 0 : NS_ENSURE_TRUE(*aResults, NS_ERROR_OUT_OF_MEMORY);
1468 :
1469 0 : for (uint32_t i = 0; i < *aResultCount; i++) {
1470 0 : (*aResults)[i] = results[i].forget().take();
1471 : }
1472 0 : return NS_OK;
1473 : }
1474 :
1475 :
1476 : void
1477 0 : Selection::GetRangesForInterval(nsINode& aBeginNode, int32_t aBeginOffset,
1478 : nsINode& aEndNode, int32_t aEndOffset,
1479 : bool aAllowAdjacent,
1480 : nsTArray<RefPtr<nsRange>>& aReturn,
1481 : mozilla::ErrorResult& aRv)
1482 : {
1483 0 : nsTArray<nsRange*> results;
1484 0 : nsresult rv = GetRangesForIntervalArray(&aBeginNode, aBeginOffset,
1485 : &aEndNode, aEndOffset,
1486 0 : aAllowAdjacent, &results);
1487 0 : if (NS_FAILED(rv)) {
1488 0 : aRv.Throw(rv);
1489 0 : return;
1490 : }
1491 :
1492 0 : aReturn.SetLength(results.Length());
1493 0 : for (uint32_t i = 0; i < results.Length(); ++i) {
1494 0 : aReturn[i] = results[i]; // AddRefs
1495 : }
1496 : }
1497 :
1498 : // Selection::GetRangesForIntervalArray
1499 : //
1500 : // Fills a nsTArray with the ranges overlapping the range specified by
1501 : // the given endpoints. Ranges in the selection exactly adjacent to the
1502 : // input range are not returned unless aAllowAdjacent is set.
1503 : //
1504 : // For example, if the following ranges were in the selection
1505 : // (assume everything is within the same node)
1506 : //
1507 : // Start Offset: 0 2 7 9
1508 : // End Offset: 2 5 9 10
1509 : //
1510 : // and passed aBeginOffset of 2 and aEndOffset of 9, then with
1511 : // aAllowAdjacent set, all the ranges should be returned. If
1512 : // aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
1513 : // should be returned
1514 : //
1515 : // Now that overlapping ranges are disallowed, there can be a maximum of
1516 : // 2 adjacent ranges
1517 :
1518 : nsresult
1519 4 : Selection::GetRangesForIntervalArray(nsINode* aBeginNode, int32_t aBeginOffset,
1520 : nsINode* aEndNode, int32_t aEndOffset,
1521 : bool aAllowAdjacent,
1522 : nsTArray<nsRange*>* aRanges)
1523 : {
1524 4 : aRanges->Clear();
1525 : int32_t startIndex, endIndex;
1526 4 : nsresult res = GetIndicesForInterval(aBeginNode, aBeginOffset,
1527 : aEndNode, aEndOffset, aAllowAdjacent,
1528 4 : &startIndex, &endIndex);
1529 4 : NS_ENSURE_SUCCESS(res, res);
1530 :
1531 4 : if (startIndex == -1 || endIndex == -1)
1532 0 : return NS_OK;
1533 :
1534 8 : for (int32_t i = startIndex; i < endIndex; i++) {
1535 4 : if (!aRanges->AppendElement(mRanges[i].mRange))
1536 0 : return NS_ERROR_OUT_OF_MEMORY;
1537 : }
1538 :
1539 4 : return NS_OK;
1540 : }
1541 :
1542 : // Selection::GetIndicesForInterval
1543 : //
1544 : // Works on the same principle as GetRangesForIntervalArray above, however
1545 : // instead this returns the indices into mRanges between which the
1546 : // overlapping ranges lie.
1547 :
1548 : nsresult
1549 4 : Selection::GetIndicesForInterval(nsINode* aBeginNode, int32_t aBeginOffset,
1550 : nsINode* aEndNode, int32_t aEndOffset,
1551 : bool aAllowAdjacent,
1552 : int32_t* aStartIndex, int32_t* aEndIndex)
1553 : {
1554 : int32_t startIndex;
1555 : int32_t endIndex;
1556 :
1557 4 : if (!aStartIndex)
1558 0 : aStartIndex = &startIndex;
1559 4 : if (!aEndIndex)
1560 0 : aEndIndex = &endIndex;
1561 :
1562 4 : *aStartIndex = -1;
1563 4 : *aEndIndex = -1;
1564 :
1565 4 : if (mRanges.Length() == 0)
1566 0 : return NS_OK;
1567 :
1568 4 : bool intervalIsCollapsed = aBeginNode == aEndNode &&
1569 4 : aBeginOffset == aEndOffset;
1570 :
1571 : // Ranges that end before the given interval and begin after the given
1572 : // interval can be discarded
1573 : int32_t endsBeforeIndex;
1574 4 : if (NS_FAILED(FindInsertionPoint(&mRanges, aEndNode, aEndOffset,
1575 : &CompareToRangeStart,
1576 : &endsBeforeIndex))) {
1577 0 : return NS_OK;
1578 : }
1579 :
1580 4 : if (endsBeforeIndex == 0) {
1581 0 : nsRange* endRange = mRanges[endsBeforeIndex].mRange;
1582 :
1583 : // If the interval is strictly before the range at index 0, we can optimize
1584 : // by returning now - all ranges start after the given interval
1585 0 : if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
1586 0 : return NS_OK;
1587 :
1588 : // We now know that the start point of mRanges[0].mRange equals the end of
1589 : // the interval. Thus, when aAllowadjacent is true, the caller is always
1590 : // interested in this range. However, when excluding adjacencies, we must
1591 : // remember to include the range when both it and the given interval are
1592 : // collapsed to the same point
1593 0 : if (!aAllowAdjacent && !(endRange->Collapsed() && intervalIsCollapsed))
1594 0 : return NS_OK;
1595 : }
1596 4 : *aEndIndex = endsBeforeIndex;
1597 :
1598 : int32_t beginsAfterIndex;
1599 4 : if (NS_FAILED(FindInsertionPoint(&mRanges, aBeginNode, aBeginOffset,
1600 : &CompareToRangeEnd,
1601 : &beginsAfterIndex))) {
1602 0 : return NS_OK;
1603 : }
1604 4 : if (beginsAfterIndex == (int32_t) mRanges.Length())
1605 0 : return NS_OK; // optimization: all ranges are strictly before us
1606 :
1607 4 : if (aAllowAdjacent) {
1608 : // At this point, one of the following holds:
1609 : // endsBeforeIndex == mRanges.Length(),
1610 : // endsBeforeIndex points to a range whose start point does not equal the
1611 : // given interval's start point
1612 : // endsBeforeIndex points to a range whose start point equals the given
1613 : // interval's start point
1614 : // In the final case, there can be two such ranges, a collapsed range, and
1615 : // an adjacent range (they will appear in mRanges in that order). For this
1616 : // final case, we need to increment endsBeforeIndex, until one of the
1617 : // first two possibilites hold
1618 0 : while (endsBeforeIndex < (int32_t) mRanges.Length()) {
1619 0 : nsRange* endRange = mRanges[endsBeforeIndex].mRange;
1620 0 : if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
1621 0 : break;
1622 0 : endsBeforeIndex++;
1623 : }
1624 :
1625 : // Likewise, one of the following holds:
1626 : // beginsAfterIndex == 0,
1627 : // beginsAfterIndex points to a range whose end point does not equal
1628 : // the given interval's end point
1629 : // beginsOnOrAfter points to a range whose end point equals the given
1630 : // interval's end point
1631 : // In the final case, there can be two such ranges, an adjacent range, and
1632 : // a collapsed range (they will appear in mRanges in that order). For this
1633 : // final case, we only need to take action if both those ranges exist, and
1634 : // we are pointing to the collapsed range - we need to point to the
1635 : // adjacent range
1636 0 : nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
1637 0 : if (beginsAfterIndex > 0 && beginRange->Collapsed() &&
1638 0 : RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset)) {
1639 0 : beginRange = mRanges[beginsAfterIndex - 1].mRange;
1640 0 : if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset))
1641 0 : beginsAfterIndex--;
1642 : }
1643 : } else {
1644 : // See above for the possibilities at this point. The only case where we
1645 : // need to take action is when the range at beginsAfterIndex ends on
1646 : // the given interval's start point, but that range isn't collapsed (a
1647 : // collapsed range should be included in the returned results).
1648 4 : nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
1649 6 : if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset) &&
1650 2 : !beginRange->Collapsed())
1651 0 : beginsAfterIndex++;
1652 :
1653 : // Again, see above for the meaning of endsBeforeIndex at this point.
1654 : // In particular, endsBeforeIndex may point to a collaped range which
1655 : // represents the point at the end of the interval - this range should be
1656 : // included
1657 4 : if (endsBeforeIndex < (int32_t) mRanges.Length()) {
1658 0 : nsRange* endRange = mRanges[endsBeforeIndex].mRange;
1659 0 : if (RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset) &&
1660 0 : endRange->Collapsed())
1661 0 : endsBeforeIndex++;
1662 : }
1663 : }
1664 :
1665 4 : NS_ASSERTION(beginsAfterIndex <= endsBeforeIndex,
1666 : "Is mRanges not ordered?");
1667 4 : NS_ENSURE_STATE(beginsAfterIndex <= endsBeforeIndex);
1668 :
1669 4 : *aStartIndex = beginsAfterIndex;
1670 4 : *aEndIndex = endsBeforeIndex;
1671 4 : return NS_OK;
1672 : }
1673 :
1674 : NS_IMETHODIMP
1675 0 : Selection::GetPrimaryFrameForAnchorNode(nsIFrame** aReturnFrame)
1676 : {
1677 0 : if (!aReturnFrame)
1678 0 : return NS_ERROR_NULL_POINTER;
1679 :
1680 0 : int32_t frameOffset = 0;
1681 0 : *aReturnFrame = 0;
1682 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(GetAnchorNode());
1683 0 : if (content && mFrameSelection)
1684 : {
1685 0 : *aReturnFrame = mFrameSelection->
1686 0 : GetFrameForNodeOffset(content, AnchorOffset(),
1687 : mFrameSelection->GetHint(), &frameOffset);
1688 0 : if (*aReturnFrame)
1689 0 : return NS_OK;
1690 : }
1691 0 : return NS_ERROR_FAILURE;
1692 : }
1693 :
1694 : NS_IMETHODIMP
1695 0 : Selection::GetPrimaryFrameForFocusNode(nsIFrame** aReturnFrame,
1696 : int32_t* aOffsetUsed,
1697 : bool aVisual)
1698 : {
1699 0 : if (!aReturnFrame)
1700 0 : return NS_ERROR_NULL_POINTER;
1701 :
1702 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(GetFocusNode());
1703 0 : if (!content || !mFrameSelection)
1704 0 : return NS_ERROR_FAILURE;
1705 :
1706 0 : int32_t frameOffset = 0;
1707 0 : *aReturnFrame = 0;
1708 0 : if (!aOffsetUsed)
1709 0 : aOffsetUsed = &frameOffset;
1710 :
1711 0 : CaretAssociationHint hint = mFrameSelection->GetHint();
1712 :
1713 0 : if (aVisual) {
1714 0 : nsBidiLevel caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
1715 :
1716 0 : return nsCaret::GetCaretFrameForNodeOffset(mFrameSelection,
1717 0 : content, FocusOffset(), hint, caretBidiLevel, aReturnFrame, aOffsetUsed);
1718 : }
1719 :
1720 0 : *aReturnFrame = mFrameSelection->
1721 0 : GetFrameForNodeOffset(content, FocusOffset(),
1722 : hint, aOffsetUsed);
1723 0 : if (!*aReturnFrame)
1724 0 : return NS_ERROR_FAILURE;
1725 :
1726 0 : return NS_OK;
1727 : }
1728 :
1729 : void
1730 17 : Selection::SelectFramesForContent(nsIContent* aContent,
1731 : bool aSelected)
1732 : {
1733 17 : nsIFrame* frame = aContent->GetPrimaryFrame();
1734 17 : if (!frame) {
1735 0 : return;
1736 : }
1737 : // The frame could be an SVG text frame, in which case we don't treat it
1738 : // as a text frame.
1739 17 : if (frame->IsTextFrame()) {
1740 0 : nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
1741 0 : textFrame->SetSelectedRange(0, aContent->GetText()->GetLength(),
1742 0 : aSelected, mSelectionType);
1743 : } else {
1744 17 : frame->InvalidateFrameSubtree(); // frame continuations?
1745 : }
1746 : }
1747 :
1748 : //select all content children of aContent
1749 : nsresult
1750 0 : Selection::SelectAllFramesForContent(nsIContentIterator* aInnerIter,
1751 : nsIContent* aContent,
1752 : bool aSelected)
1753 : {
1754 : // If aContent doesn't have children, we should avoid to use the content
1755 : // iterator for performance reason.
1756 0 : if (!aContent->HasChildren()) {
1757 0 : SelectFramesForContent(aContent, aSelected);
1758 0 : return NS_OK;
1759 : }
1760 :
1761 0 : if (NS_WARN_IF(NS_FAILED(aInnerIter->Init(aContent)))) {
1762 0 : return NS_ERROR_FAILURE;
1763 : }
1764 :
1765 0 : for (; !aInnerIter->IsDone(); aInnerIter->Next()) {
1766 0 : nsINode* node = aInnerIter->GetCurrentNode();
1767 0 : MOZ_ASSERT(node);
1768 0 : nsIContent* innercontent = node->IsContent() ? node->AsContent() : nullptr;
1769 0 : SelectFramesForContent(innercontent, aSelected);
1770 : }
1771 :
1772 0 : return NS_OK;
1773 : }
1774 :
1775 : /**
1776 : * The idea of this helper method is to select or deselect "top to bottom",
1777 : * traversing through the frames
1778 : */
1779 : nsresult
1780 30 : Selection::SelectFrames(nsPresContext* aPresContext, nsRange* aRange,
1781 : bool aSelect)
1782 : {
1783 30 : if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
1784 : // nothing to do
1785 1 : return NS_OK;
1786 : }
1787 29 : MOZ_ASSERT(aRange && aRange->IsPositioned());
1788 :
1789 29 : if (mFrameSelection->GetTableCellSelection()) {
1790 0 : nsINode* node = aRange->GetCommonAncestor();
1791 0 : nsIFrame* frame = node->IsContent() ? node->AsContent()->GetPrimaryFrame()
1792 0 : : aPresContext->FrameManager()->GetRootFrame();
1793 0 : if (frame) {
1794 0 : frame->InvalidateFrameSubtree();
1795 : }
1796 0 : return NS_OK;
1797 : }
1798 :
1799 :
1800 : // Loop through the content iterator for each content node; for each text
1801 : // node, call SetSelected on it:
1802 29 : nsINode* startNode = aRange->GetStartContainer();
1803 : nsIContent* startContent =
1804 29 : startNode->IsContent() ? startNode->AsContent() : nullptr;
1805 29 : if (!startContent) {
1806 : // Don't warn, bug 1055722
1807 : // XXX The range can start from a document node and such range can be
1808 : // added to Selection with JS. Therefore, even in such cases,
1809 : // shouldn't we handle selection in the range?
1810 0 : return NS_ERROR_UNEXPECTED;
1811 : }
1812 :
1813 : // We must call first one explicitly
1814 29 : bool isFirstContentTextNode = startContent->IsNodeOfType(nsINode::eTEXT);
1815 29 : nsINode* endNode = aRange->GetEndContainer();
1816 29 : if (isFirstContentTextNode) {
1817 12 : nsIFrame* frame = startContent->GetPrimaryFrame();
1818 : // The frame could be an SVG text frame, in which case we don't treat it
1819 : // as a text frame.
1820 12 : if (frame) {
1821 6 : if (frame->IsTextFrame()) {
1822 6 : nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
1823 6 : uint32_t startOffset = aRange->StartOffset();
1824 : uint32_t endOffset;
1825 6 : if (endNode == startContent) {
1826 6 : endOffset = aRange->EndOffset();
1827 : } else {
1828 0 : endOffset = startContent->Length();
1829 : }
1830 6 : textFrame->SetSelectedRange(startOffset, endOffset, aSelect,
1831 6 : mSelectionType);
1832 : } else {
1833 0 : frame->InvalidateFrameSubtree();
1834 : }
1835 : }
1836 : }
1837 :
1838 : // If the range is in a node and the node is a leaf node, we don't need to
1839 : // walk the subtree.
1840 58 : if (aRange->Collapsed() ||
1841 7 : (startNode == endNode && !startNode->HasChildren())) {
1842 29 : if (!isFirstContentTextNode) {
1843 17 : SelectFramesForContent(startContent, aSelect);
1844 : }
1845 29 : return NS_OK;
1846 : }
1847 :
1848 0 : nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
1849 0 : iter->Init(aRange);
1850 0 : if (isFirstContentTextNode && !iter->IsDone() &&
1851 0 : iter->GetCurrentNode() == startNode) {
1852 0 : iter->Next(); // first content has already been handled.
1853 : }
1854 0 : nsCOMPtr<nsIContentIterator> inneriter = NS_NewContentIterator();
1855 0 : for (; !iter->IsDone(); iter->Next()) {
1856 0 : nsINode* node = iter->GetCurrentNode();
1857 0 : MOZ_ASSERT(node);
1858 0 : nsIContent* content = node->IsContent() ? node->AsContent() : nullptr;
1859 0 : SelectAllFramesForContent(inneriter, content, aSelect);
1860 : }
1861 :
1862 : // We must now do the last one if it is not the same as the first
1863 0 : if (endNode != startNode) {
1864 : nsIContent* endContent =
1865 0 : endNode->IsContent() ? endNode->AsContent() : nullptr;
1866 : // XXX The range can end at a document node and such range can be
1867 : // added to Selection with JS. Therefore, even in such cases,
1868 : // shouldn't we handle selection in the range?
1869 0 : if (NS_WARN_IF(!endContent)) {
1870 0 : return NS_ERROR_UNEXPECTED;
1871 : }
1872 0 : if (endContent->IsNodeOfType(nsINode::eTEXT)) {
1873 0 : nsIFrame* frame = endContent->GetPrimaryFrame();
1874 : // The frame could be an SVG text frame, in which case we'll ignore it.
1875 0 : if (frame && frame->IsTextFrame()) {
1876 0 : nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
1877 0 : textFrame->SetSelectedRange(0, aRange->EndOffset(), aSelect,
1878 0 : mSelectionType);
1879 : }
1880 : }
1881 : }
1882 0 : return NS_OK;
1883 : }
1884 :
1885 :
1886 : // Selection::LookUpSelection
1887 : //
1888 : // This function is called when a node wants to know where the selection is
1889 : // over itself.
1890 : //
1891 : // Usually, this is called when we already know there is a selection over
1892 : // the node in question, and we only need to find the boundaries of it on
1893 : // that node. This is when slowCheck is false--a strict test is not needed.
1894 : // Other times, the caller has no idea, and wants us to test everything,
1895 : // so we are supposed to determine whether there is a selection over the
1896 : // node at all.
1897 : //
1898 : // A previous version of this code used this flag to do less work when
1899 : // inclusion was already known (slowCheck=false). However, our tree
1900 : // structure allows us to quickly determine ranges overlapping the node,
1901 : // so we just ignore the slowCheck flag and do the full test every time.
1902 : //
1903 : // PERFORMANCE: a common case is that we are doing a fast check with exactly
1904 : // one range in the selection. In this case, this function is slower than
1905 : // brute force because of the overhead of checking the tree. We can optimize
1906 : // this case to make it faster by doing the same thing the previous version
1907 : // of this function did in the case of 1 range. This would also mean that
1908 : // the aSlowCheck flag would have meaning again.
1909 :
1910 : UniquePtr<SelectionDetails>
1911 20 : Selection::LookUpSelection(nsIContent* aContent, int32_t aContentOffset,
1912 : int32_t aContentLength,
1913 : UniquePtr<SelectionDetails> aDetailsHead,
1914 : SelectionType aSelectionType,
1915 : bool aSlowCheck)
1916 : {
1917 20 : if (!aContent) {
1918 0 : return aDetailsHead;
1919 : }
1920 :
1921 : // it is common to have no ranges, to optimize that
1922 20 : if (mRanges.Length() == 0) {
1923 16 : return aDetailsHead;
1924 : }
1925 :
1926 8 : nsTArray<nsRange*> overlappingRanges;
1927 4 : nsresult rv = GetRangesForIntervalArray(aContent, aContentOffset,
1928 : aContent, aContentOffset + aContentLength,
1929 : false,
1930 4 : &overlappingRanges);
1931 4 : if (NS_FAILED(rv)) {
1932 0 : return aDetailsHead;
1933 : }
1934 :
1935 4 : if (overlappingRanges.Length() == 0) {
1936 0 : return aDetailsHead;
1937 : }
1938 :
1939 8 : UniquePtr<SelectionDetails> detailsHead = Move(aDetailsHead);
1940 :
1941 8 : for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
1942 4 : nsRange* range = overlappingRanges[i];
1943 4 : nsINode* startNode = range->GetStartContainer();
1944 4 : nsINode* endNode = range->GetEndContainer();
1945 4 : int32_t startOffset = range->StartOffset();
1946 4 : int32_t endOffset = range->EndOffset();
1947 :
1948 4 : int32_t start = -1, end = -1;
1949 4 : if (startNode == aContent && endNode == aContent) {
1950 8 : if (startOffset < (aContentOffset + aContentLength) &&
1951 : endOffset > aContentOffset) {
1952 : // this range is totally inside the requested content range
1953 2 : start = std::max(0, startOffset - aContentOffset);
1954 2 : end = std::min(aContentLength, endOffset - aContentOffset);
1955 : }
1956 : // otherwise, range is inside the requested node, but does not intersect
1957 : // the requested content range, so ignore it
1958 0 : } else if (startNode == aContent) {
1959 0 : if (startOffset < (aContentOffset + aContentLength)) {
1960 : // the beginning of the range is inside the requested node, but the
1961 : // end is outside, select everything from there to the end
1962 0 : start = std::max(0, startOffset - aContentOffset);
1963 0 : end = aContentLength;
1964 : }
1965 0 : } else if (endNode == aContent) {
1966 0 : if (endOffset > aContentOffset) {
1967 : // the end of the range is inside the requested node, but the beginning
1968 : // is outside, select everything from the beginning to there
1969 0 : start = 0;
1970 0 : end = std::min(aContentLength, endOffset - aContentOffset);
1971 : }
1972 : } else {
1973 : // this range does not begin or end in the requested node, but since
1974 : // GetRangesForInterval returned this range, we know it overlaps.
1975 : // Therefore, this node is enclosed in the range, and we select all
1976 : // of it.
1977 0 : start = 0;
1978 0 : end = aContentLength;
1979 : }
1980 4 : if (start < 0)
1981 2 : continue; // the ranges do not overlap the input range
1982 :
1983 4 : auto newHead = MakeUnique<SelectionDetails>();
1984 :
1985 2 : newHead->mNext = Move(detailsHead);
1986 2 : newHead->mStart = start;
1987 2 : newHead->mEnd = end;
1988 2 : newHead->mSelectionType = aSelectionType;
1989 2 : RangeData *rd = FindRangeData(range);
1990 2 : if (rd) {
1991 2 : newHead->mTextRangeStyle = rd->mTextRangeStyle;
1992 : }
1993 2 : detailsHead = Move(newHead);
1994 : }
1995 4 : return detailsHead;
1996 : }
1997 :
1998 : NS_IMETHODIMP
1999 3 : Selection::Repaint(nsPresContext* aPresContext)
2000 : {
2001 3 : int32_t arrCount = (int32_t)mRanges.Length();
2002 :
2003 3 : if (arrCount < 1)
2004 3 : return NS_OK;
2005 :
2006 : int32_t i;
2007 :
2008 0 : for (i = 0; i < arrCount; i++)
2009 : {
2010 0 : nsresult rv = SelectFrames(aPresContext, mRanges[i].mRange, true);
2011 :
2012 0 : if (NS_FAILED(rv)) {
2013 0 : return rv;
2014 : }
2015 : }
2016 :
2017 0 : return NS_OK;
2018 : }
2019 :
2020 : NS_IMETHODIMP
2021 0 : Selection::GetCanCacheFrameOffset(bool* aCanCacheFrameOffset)
2022 : {
2023 0 : NS_ENSURE_ARG_POINTER(aCanCacheFrameOffset);
2024 :
2025 0 : if (mCachedOffsetForFrame)
2026 0 : *aCanCacheFrameOffset = mCachedOffsetForFrame->mCanCacheFrameOffset;
2027 : else
2028 0 : *aCanCacheFrameOffset = false;
2029 :
2030 0 : return NS_OK;
2031 : }
2032 :
2033 : NS_IMETHODIMP
2034 2 : Selection::SetCanCacheFrameOffset(bool aCanCacheFrameOffset)
2035 : {
2036 2 : if (!mCachedOffsetForFrame) {
2037 1 : mCachedOffsetForFrame = new CachedOffsetForFrame;
2038 : }
2039 :
2040 2 : mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
2041 :
2042 : // clean up cached frame when turn off cache
2043 : // fix bug 207936
2044 2 : if (!aCanCacheFrameOffset) {
2045 1 : mCachedOffsetForFrame->mLastCaretFrame = nullptr;
2046 : }
2047 :
2048 2 : return NS_OK;
2049 : }
2050 :
2051 : NS_IMETHODIMP
2052 1 : Selection::GetCachedFrameOffset(nsIFrame* aFrame, int32_t inOffset,
2053 : nsPoint& aPoint)
2054 : {
2055 1 : if (!mCachedOffsetForFrame) {
2056 0 : mCachedOffsetForFrame = new CachedOffsetForFrame;
2057 : }
2058 :
2059 1 : nsresult rv = NS_OK;
2060 1 : if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
2061 0 : mCachedOffsetForFrame->mLastCaretFrame &&
2062 0 : (aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
2063 0 : (inOffset == mCachedOffsetForFrame->mLastContentOffset))
2064 : {
2065 : // get cached frame offset
2066 0 : aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
2067 : }
2068 : else
2069 : {
2070 : // Recalculate frame offset and cache it. Don't cache a frame offset if
2071 : // GetPointFromOffset fails, though.
2072 1 : rv = aFrame->GetPointFromOffset(inOffset, &aPoint);
2073 1 : if (NS_SUCCEEDED(rv) && mCachedOffsetForFrame->mCanCacheFrameOffset) {
2074 0 : mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
2075 0 : mCachedOffsetForFrame->mLastCaretFrame = aFrame;
2076 0 : mCachedOffsetForFrame->mLastContentOffset = inOffset;
2077 : }
2078 : }
2079 :
2080 1 : return rv;
2081 : }
2082 :
2083 : NS_IMETHODIMP
2084 0 : Selection::GetAncestorLimiter(nsIContent** aContent)
2085 : {
2086 0 : if (mFrameSelection) {
2087 0 : nsCOMPtr<nsIContent> c = mFrameSelection->GetAncestorLimiter();
2088 0 : c.forget(aContent);
2089 : }
2090 0 : return NS_OK;
2091 : }
2092 :
2093 : NS_IMETHODIMP
2094 0 : Selection::SetAncestorLimiter(nsIContent* aContent)
2095 : {
2096 0 : if (mFrameSelection) {
2097 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
2098 0 : frameSelection->SetAncestorLimiter(aContent);
2099 : }
2100 0 : return NS_OK;
2101 : }
2102 :
2103 : RangeData*
2104 2 : Selection::FindRangeData(nsIDOMRange* aRange)
2105 : {
2106 2 : NS_ENSURE_TRUE(aRange, nullptr);
2107 2 : for (uint32_t i = 0; i < mRanges.Length(); i++) {
2108 2 : if (mRanges[i].mRange == aRange)
2109 2 : return &mRanges[i];
2110 : }
2111 0 : return nullptr;
2112 : }
2113 :
2114 : NS_IMETHODIMP
2115 0 : Selection::SetTextRangeStyle(nsIDOMRange* aRange,
2116 : const TextRangeStyle& aTextRangeStyle)
2117 : {
2118 0 : NS_ENSURE_ARG_POINTER(aRange);
2119 0 : RangeData *rd = FindRangeData(aRange);
2120 0 : if (rd) {
2121 0 : rd->mTextRangeStyle = aTextRangeStyle;
2122 : }
2123 0 : return NS_OK;
2124 : }
2125 :
2126 : nsresult
2127 0 : Selection::StartAutoScrollTimer(nsIFrame* aFrame, nsPoint& aPoint,
2128 : uint32_t aDelay)
2129 : {
2130 0 : NS_PRECONDITION(aFrame, "Need a frame");
2131 :
2132 : nsresult result;
2133 0 : if (!mFrameSelection) {
2134 0 : return NS_OK;//nothing to do
2135 : }
2136 :
2137 0 : if (!mAutoScrollTimer) {
2138 0 : mAutoScrollTimer = new nsAutoScrollTimer();
2139 :
2140 0 : result = mAutoScrollTimer->Init(mFrameSelection, this);
2141 :
2142 0 : if (NS_FAILED(result)) {
2143 0 : return result;
2144 : }
2145 : }
2146 :
2147 0 : result = mAutoScrollTimer->SetDelay(aDelay);
2148 :
2149 0 : if (NS_FAILED(result)) {
2150 0 : return result;
2151 : }
2152 :
2153 0 : return DoAutoScroll(aFrame, aPoint);
2154 : }
2155 :
2156 : nsresult
2157 6 : Selection::StopAutoScrollTimer()
2158 : {
2159 6 : if (mAutoScrollTimer) {
2160 0 : return mAutoScrollTimer->Stop();
2161 : }
2162 6 : return NS_OK;
2163 : }
2164 :
2165 : nsresult
2166 0 : Selection::DoAutoScroll(nsIFrame* aFrame, nsPoint& aPoint)
2167 : {
2168 0 : NS_PRECONDITION(aFrame, "Need a frame");
2169 :
2170 0 : if (mAutoScrollTimer) {
2171 0 : (void)mAutoScrollTimer->Stop();
2172 : }
2173 :
2174 0 : nsPresContext* presContext = aFrame->PresContext();
2175 0 : nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
2176 0 : nsRootPresContext* rootPC = presContext->GetRootPresContext();
2177 0 : if (!rootPC)
2178 0 : return NS_OK;
2179 0 : nsIFrame* rootmostFrame = rootPC->PresShell()->FrameManager()->GetRootFrame();
2180 0 : AutoWeakFrame weakRootFrame(rootmostFrame);
2181 0 : AutoWeakFrame weakFrame(aFrame);
2182 : // Get the point relative to the root most frame because the scroll we are
2183 : // about to do will change the coordinates of aFrame.
2184 0 : nsPoint globalPoint = aPoint + aFrame->GetOffsetToCrossDoc(rootmostFrame);
2185 :
2186 0 : bool done = false;
2187 : bool didScroll;
2188 : while (true) {
2189 0 : didScroll = shell->ScrollFrameRectIntoView(
2190 0 : aFrame, nsRect(aPoint, nsSize(0, 0)),
2191 : nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis(),
2192 0 : 0);
2193 0 : if (!weakFrame || !weakRootFrame) {
2194 0 : return NS_OK;
2195 : }
2196 0 : if (!didScroll && !done) {
2197 : // If aPoint is at the screen edge then try to scroll anyway, once.
2198 0 : RefPtr<nsDeviceContext> dx = shell->GetViewManager()->GetDeviceContext();
2199 0 : nsRect screen;
2200 0 : dx->GetRect(screen);
2201 : nsPoint screenPoint = globalPoint +
2202 0 : rootmostFrame->GetScreenRectInAppUnits().TopLeft();
2203 0 : nscoord onePx = nsPresContext::AppUnitsPerCSSPixel();
2204 0 : nscoord scrollAmount = 10 * onePx;
2205 0 : if (std::abs(screen.x - screenPoint.x) <= onePx) {
2206 0 : aPoint.x -= scrollAmount;
2207 0 : } else if (std::abs(screen.XMost() - screenPoint.x) <= onePx) {
2208 0 : aPoint.x += scrollAmount;
2209 0 : } else if (std::abs(screen.y - screenPoint.y) <= onePx) {
2210 0 : aPoint.y -= scrollAmount;
2211 0 : } else if (std::abs(screen.YMost() - screenPoint.y) <= onePx) {
2212 0 : aPoint.y += scrollAmount;
2213 : } else {
2214 0 : break;
2215 : }
2216 0 : done = true;
2217 0 : continue;
2218 : }
2219 0 : break;
2220 0 : }
2221 :
2222 : // Start the AutoScroll timer if necessary.
2223 0 : if (didScroll && mAutoScrollTimer) {
2224 : nsPoint presContextPoint = globalPoint -
2225 0 : shell->FrameManager()->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame);
2226 0 : mAutoScrollTimer->Start(presContext, presContextPoint);
2227 : }
2228 :
2229 0 : return NS_OK;
2230 : }
2231 :
2232 :
2233 : /** RemoveAllRanges zeroes the selection
2234 : */
2235 : NS_IMETHODIMP
2236 9 : Selection::RemoveAllRanges()
2237 : {
2238 18 : ErrorResult result;
2239 9 : RemoveAllRanges(result);
2240 18 : return result.StealNSResult();
2241 : }
2242 :
2243 : void
2244 15 : Selection::RemoveAllRanges(ErrorResult& aRv)
2245 : {
2246 15 : if (!mFrameSelection)
2247 0 : return; // nothing to do
2248 30 : RefPtr<nsPresContext> presContext = GetPresContext();
2249 15 : nsresult result = Clear(presContext);
2250 15 : if (NS_FAILED(result)) {
2251 0 : aRv.Throw(result);
2252 0 : return;
2253 : }
2254 :
2255 : // Turn off signal for table selection
2256 30 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
2257 15 : frameSelection->ClearTableCellSelection();
2258 :
2259 : // Be aware, this instance may be destroyed after this call.
2260 : // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
2261 15 : result = frameSelection->NotifySelectionListeners(GetType());
2262 :
2263 : // Also need to notify the frames!
2264 : // PresShell::CharacterDataChanged should do that on DocumentChanged
2265 15 : if (NS_FAILED(result)) {
2266 0 : aRv.Throw(result);
2267 : }
2268 : }
2269 :
2270 : /** AddRange adds the specified range to the selection
2271 : * @param aRange is the range to be added
2272 : */
2273 : NS_IMETHODIMP
2274 8 : Selection::AddRange(nsIDOMRange* aDOMRange)
2275 : {
2276 8 : if (!aDOMRange) {
2277 0 : return NS_ERROR_NULL_POINTER;
2278 : }
2279 8 : nsRange* range = static_cast<nsRange*>(aDOMRange);
2280 16 : ErrorResult result;
2281 8 : AddRange(*range, result);
2282 8 : return result.StealNSResult();
2283 : }
2284 :
2285 : void
2286 2 : Selection::AddRangeJS(nsRange& aRange, ErrorResult& aRv)
2287 : {
2288 4 : AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
2289 2 : mCalledByJS = true;
2290 2 : AddRange(aRange, aRv);
2291 2 : }
2292 :
2293 : void
2294 10 : Selection::AddRange(nsRange& aRange, ErrorResult& aRv)
2295 : {
2296 10 : return AddRangeInternal(aRange, GetParentObject(), aRv);
2297 : }
2298 :
2299 : void
2300 10 : Selection::AddRangeInternal(nsRange& aRange, nsIDocument* aDocument,
2301 : ErrorResult& aRv)
2302 : {
2303 10 : nsINode* rangeRoot = aRange.GetRoot();
2304 18 : if (aDocument != rangeRoot && (!rangeRoot ||
2305 8 : aDocument != rangeRoot->GetComposedDoc())) {
2306 : // http://w3c.github.io/selection-api/#dom-selection-addrange
2307 : // "... if the root of the range's boundary points are the document
2308 : // associated with context object. Otherwise, this method must do nothing."
2309 0 : return;
2310 : }
2311 :
2312 : // This inserts a table cell range in proper document order
2313 : // and returns NS_OK if range doesn't contain just one table cell
2314 : bool didAddRange;
2315 : int32_t rangeIndex;
2316 10 : nsresult result = AddTableCellRange(&aRange, &didAddRange, &rangeIndex);
2317 10 : if (NS_FAILED(result)) {
2318 0 : aRv.Throw(result);
2319 0 : return;
2320 : }
2321 :
2322 10 : if (!didAddRange) {
2323 10 : result = AddItem(&aRange, &rangeIndex);
2324 10 : if (NS_FAILED(result)) {
2325 0 : aRv.Throw(result);
2326 0 : return;
2327 : }
2328 : }
2329 :
2330 10 : if (rangeIndex < 0) {
2331 0 : return;
2332 : }
2333 :
2334 10 : SetAnchorFocusRange(rangeIndex);
2335 :
2336 : // Make sure the caret appears on the next line, if at a newline
2337 10 : if (mSelectionType == SelectionType::eNormal) {
2338 8 : SetInterlinePosition(true);
2339 : }
2340 :
2341 20 : RefPtr<nsPresContext> presContext = GetPresContext();
2342 10 : SelectFrames(presContext, &aRange, true);
2343 :
2344 10 : if (!mFrameSelection)
2345 0 : return;//nothing to do
2346 :
2347 : // Be aware, this instance may be destroyed after this call.
2348 : // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
2349 20 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
2350 10 : result = frameSelection->NotifySelectionListeners(GetType());
2351 10 : if (NS_FAILED(result)) {
2352 0 : aRv.Throw(result);
2353 : }
2354 : }
2355 :
2356 : // Selection::RemoveRange
2357 : //
2358 : // Removes the given range from the selection. The tricky part is updating
2359 : // the flags on the frames that indicate whether they have a selection or
2360 : // not. There could be several selection ranges on the frame, and clearing
2361 : // the bit would cause the selection to not be drawn, even when there is
2362 : // another range on the frame (bug 346185).
2363 : //
2364 : // We therefore find any ranges that intersect the same nodes as the range
2365 : // being removed, and cause them to set the selected bits back on their
2366 : // selected frames after we've cleared the bit from ours.
2367 :
2368 : nsresult
2369 0 : Selection::RemoveRange(nsIDOMRange* aDOMRange)
2370 : {
2371 0 : if (!aDOMRange) {
2372 0 : return NS_ERROR_INVALID_ARG;
2373 : }
2374 0 : nsRange* range = static_cast<nsRange*>(aDOMRange);
2375 0 : ErrorResult result;
2376 0 : RemoveRange(*range, result);
2377 0 : return result.StealNSResult();
2378 : }
2379 :
2380 : void
2381 0 : Selection::RemoveRange(nsRange& aRange, ErrorResult& aRv)
2382 : {
2383 0 : nsresult rv = RemoveItem(&aRange);
2384 0 : if (NS_FAILED(rv)) {
2385 0 : aRv.Throw(rv);
2386 0 : return;
2387 : }
2388 :
2389 0 : nsINode* beginNode = aRange.GetStartContainer();
2390 0 : nsINode* endNode = aRange.GetEndContainer();
2391 :
2392 0 : if (!beginNode || !endNode) {
2393 : // Detached range; nothing else to do here.
2394 0 : return;
2395 : }
2396 :
2397 : // find out the length of the end node, so we can select all of it
2398 : int32_t beginOffset, endOffset;
2399 0 : if (endNode->IsNodeOfType(nsINode::eTEXT)) {
2400 : // Get the length of the text. We can't just use the offset because
2401 : // another range could be touching this text node but not intersect our
2402 : // range.
2403 0 : beginOffset = 0;
2404 0 : endOffset = static_cast<nsIContent*>(endNode)->TextLength();
2405 : } else {
2406 : // For non-text nodes, the given offsets should be sufficient.
2407 0 : beginOffset = aRange.StartOffset();
2408 0 : endOffset = aRange.EndOffset();
2409 : }
2410 :
2411 : // clear the selected bit from the removed range's frames
2412 0 : RefPtr<nsPresContext> presContext = GetPresContext();
2413 0 : SelectFrames(presContext, &aRange, false);
2414 :
2415 : // add back the selected bit for each range touching our nodes
2416 0 : nsTArray<nsRange*> affectedRanges;
2417 : rv = GetRangesForIntervalArray(beginNode, beginOffset,
2418 : endNode, endOffset,
2419 0 : true, &affectedRanges);
2420 0 : if (NS_FAILED(rv)) {
2421 0 : aRv.Throw(rv);
2422 0 : return;
2423 : }
2424 0 : for (uint32_t i = 0; i < affectedRanges.Length(); i++) {
2425 0 : SelectFrames(presContext, affectedRanges[i], true);
2426 : }
2427 :
2428 0 : int32_t cnt = mRanges.Length();
2429 0 : if (&aRange == mAnchorFocusRange) {
2430 : // Reset anchor to LAST range or clear it if there are no ranges.
2431 0 : SetAnchorFocusRange(cnt - 1);
2432 :
2433 : // When the selection is user-created it makes sense to scroll the range
2434 : // into view. The spell-check selection, however, is created and destroyed
2435 : // in the background. We don't want to scroll in this case or the view
2436 : // might appear to be moving randomly (bug 337871).
2437 0 : if (mSelectionType != SelectionType::eSpellCheck && cnt > 0) {
2438 0 : ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
2439 : }
2440 : }
2441 :
2442 0 : if (!mFrameSelection)
2443 0 : return;//nothing to do
2444 :
2445 : // Be aware, this instance may be destroyed after this call.
2446 : // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
2447 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
2448 0 : rv = frameSelection->NotifySelectionListeners(GetType());
2449 0 : if (NS_FAILED(rv)) {
2450 0 : aRv.Throw(rv);
2451 : }
2452 : }
2453 :
2454 :
2455 :
2456 : /*
2457 : * Collapse sets the whole selection to be one point.
2458 : */
2459 : NS_IMETHODIMP
2460 0 : Selection::Collapse(nsIDOMNode* aContainer, int32_t aOffset)
2461 : {
2462 0 : nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
2463 0 : return Collapse(container, aOffset);
2464 : }
2465 :
2466 : NS_IMETHODIMP
2467 0 : Selection::CollapseNative(nsINode* aContainer, int32_t aOffset)
2468 : {
2469 0 : return Collapse(aContainer, aOffset);
2470 : }
2471 :
2472 : void
2473 0 : Selection::CollapseJS(nsINode* aContainer, uint32_t aOffset, ErrorResult& aRv)
2474 : {
2475 0 : AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
2476 0 : mCalledByJS = true;
2477 0 : if (!aContainer) {
2478 0 : RemoveAllRanges(aRv);
2479 0 : return;
2480 : }
2481 0 : Collapse(*aContainer, aOffset, aRv);
2482 : }
2483 :
2484 : nsresult
2485 5 : Selection::Collapse(nsINode* aContainer, int32_t aOffset)
2486 : {
2487 5 : if (!aContainer) {
2488 0 : return NS_ERROR_INVALID_ARG;
2489 : }
2490 :
2491 10 : ErrorResult result;
2492 5 : Collapse(*aContainer, static_cast<uint32_t>(aOffset), result);
2493 5 : return result.StealNSResult();
2494 : }
2495 :
2496 : void
2497 7 : Selection::Collapse(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
2498 : {
2499 7 : if (!mFrameSelection) {
2500 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
2501 0 : return;
2502 : }
2503 :
2504 14 : nsCOMPtr<nsINode> container = &aContainer;
2505 :
2506 14 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
2507 7 : frameSelection->InvalidateDesiredPos();
2508 7 : if (!IsValidSelectionPoint(frameSelection, container)) {
2509 0 : aRv.Throw(NS_ERROR_FAILURE);
2510 0 : return;
2511 : }
2512 : nsresult result;
2513 :
2514 14 : RefPtr<nsPresContext> presContext = GetPresContext();
2515 7 : if (!presContext || presContext->Document() != container->OwnerDoc()) {
2516 0 : aRv.Throw(NS_ERROR_FAILURE);
2517 0 : return;
2518 : }
2519 :
2520 : // Cache current range is if there is because it may be reusable.
2521 14 : RefPtr<nsRange> oldRange = !mRanges.IsEmpty() ? mRanges[0].mRange : nullptr;
2522 :
2523 : // Delete all of the current ranges
2524 7 : Clear(presContext);
2525 :
2526 : // Turn off signal for table selection
2527 7 : frameSelection->ClearTableCellSelection();
2528 :
2529 : // Hack to display the caret on the right line (bug 1237236).
2530 11 : if (frameSelection->GetHint() != CARET_ASSOCIATE_AFTER &&
2531 4 : container->IsContent()) {
2532 : int32_t frameOffset;
2533 : nsTextFrame* f =
2534 8 : do_QueryFrame(nsCaret::GetFrameAndOffset(this, container,
2535 4 : aOffset, &frameOffset));
2536 4 : if (f && f->IsAtEndOfLine() && f->HasSignificantTerminalNewline()) {
2537 0 : if ((container->AsContent() == f->GetContent() &&
2538 0 : f->GetContentEnd() == int32_t(aOffset)) ||
2539 0 : (container == f->GetContent()->GetParentNode() &&
2540 0 : container->IndexOf(f->GetContent()) + 1 == int32_t(aOffset))) {
2541 0 : frameSelection->SetHint(CARET_ASSOCIATE_AFTER);
2542 : }
2543 : }
2544 : }
2545 :
2546 14 : RefPtr<nsRange> range;
2547 : // If the old range isn't referred by anybody other than this method,
2548 : // we should reuse it for reducing the recreation cost.
2549 7 : if (oldRange && oldRange->GetRefCount() == 1) {
2550 2 : range = oldRange;
2551 : } else {
2552 10 : range = new nsRange(container);
2553 : }
2554 7 : result = range->CollapseTo(container, aOffset);
2555 7 : if (NS_FAILED(result)) {
2556 0 : aRv.Throw(result);
2557 0 : return;
2558 : }
2559 :
2560 : #ifdef DEBUG_SELECTION
2561 : nsCOMPtr<nsIContent> content = do_QueryInterface(container);
2562 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(container);
2563 : printf ("Sel. Collapse to %p %s %d\n", container.get(),
2564 : content ? nsAtomCString(content->NodeInfo()->NameAtom()).get()
2565 : : (doc ? "DOCUMENT" : "???"),
2566 : aOffset);
2567 : #endif
2568 :
2569 7 : int32_t rangeIndex = -1;
2570 7 : result = AddItem(range, &rangeIndex);
2571 7 : if (NS_FAILED(result)) {
2572 0 : aRv.Throw(result);
2573 0 : return;
2574 : }
2575 7 : SetAnchorFocusRange(0);
2576 7 : SelectFrames(presContext, range, true);
2577 :
2578 : // Be aware, this instance may be destroyed after this call.
2579 : // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
2580 7 : result = frameSelection->NotifySelectionListeners(GetType());
2581 7 : if (NS_FAILED(result)) {
2582 0 : aRv.Throw(result);
2583 : }
2584 : }
2585 :
2586 : /*
2587 : * Sets the whole selection to be one point
2588 : * at the start of the current selection
2589 : */
2590 : NS_IMETHODIMP
2591 2 : Selection::CollapseToStart()
2592 : {
2593 4 : ErrorResult result;
2594 2 : CollapseToStart(result);
2595 4 : return result.StealNSResult();
2596 : }
2597 :
2598 : void
2599 0 : Selection::CollapseToStartJS(ErrorResult& aRv)
2600 : {
2601 0 : AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
2602 0 : mCalledByJS = true;
2603 0 : CollapseToStart(aRv);
2604 0 : }
2605 :
2606 : void
2607 2 : Selection::CollapseToStart(ErrorResult& aRv)
2608 : {
2609 : int32_t cnt;
2610 2 : nsresult rv = GetRangeCount(&cnt);
2611 2 : if (NS_FAILED(rv) || cnt <= 0) {
2612 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2613 0 : return;
2614 : }
2615 :
2616 : // Get the first range
2617 2 : nsRange* firstRange = mRanges[0].mRange;
2618 2 : if (!firstRange) {
2619 0 : aRv.Throw(NS_ERROR_FAILURE);
2620 0 : return;
2621 : }
2622 :
2623 2 : if (mFrameSelection) {
2624 2 : int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOSTART_REASON;
2625 2 : mFrameSelection->PostReason(reason);
2626 : }
2627 2 : nsINode* container = firstRange->GetStartContainer();
2628 2 : if (!container) {
2629 0 : aRv.Throw(NS_ERROR_FAILURE);
2630 0 : return;
2631 : }
2632 2 : Collapse(*container, firstRange->StartOffset(), aRv);
2633 : }
2634 :
2635 : /*
2636 : * Sets the whole selection to be one point
2637 : * at the end of the current selection
2638 : */
2639 : NS_IMETHODIMP
2640 0 : Selection::CollapseToEnd()
2641 : {
2642 0 : ErrorResult result;
2643 0 : CollapseToEnd(result);
2644 0 : return result.StealNSResult();
2645 : }
2646 :
2647 : void
2648 0 : Selection::CollapseToEndJS(ErrorResult& aRv)
2649 : {
2650 0 : AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
2651 0 : mCalledByJS = true;
2652 0 : CollapseToEnd(aRv);
2653 0 : }
2654 :
2655 : void
2656 0 : Selection::CollapseToEnd(ErrorResult& aRv)
2657 : {
2658 : int32_t cnt;
2659 0 : nsresult rv = GetRangeCount(&cnt);
2660 0 : if (NS_FAILED(rv) || cnt <= 0) {
2661 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2662 0 : return;
2663 : }
2664 :
2665 : // Get the last range
2666 0 : nsRange* lastRange = mRanges[cnt - 1].mRange;
2667 0 : if (!lastRange) {
2668 0 : aRv.Throw(NS_ERROR_FAILURE);
2669 0 : return;
2670 : }
2671 :
2672 0 : if (mFrameSelection) {
2673 0 : int16_t reason = mFrameSelection->PopReason() | nsISelectionListener::COLLAPSETOEND_REASON;
2674 0 : mFrameSelection->PostReason(reason);
2675 : }
2676 0 : nsINode* container = lastRange->GetEndContainer();
2677 0 : if (!container) {
2678 0 : aRv.Throw(NS_ERROR_FAILURE);
2679 0 : return;
2680 : }
2681 0 : Collapse(*container, lastRange->EndOffset(), aRv);
2682 : }
2683 :
2684 : /*
2685 : * IsCollapsed -- is the whole selection just one point, or unset?
2686 : */
2687 : bool
2688 20 : Selection::IsCollapsed() const
2689 : {
2690 20 : uint32_t cnt = mRanges.Length();
2691 20 : if (cnt == 0) {
2692 9 : return true;
2693 : }
2694 :
2695 11 : if (cnt != 1) {
2696 0 : return false;
2697 : }
2698 :
2699 11 : return mRanges[0].mRange->Collapsed();
2700 : }
2701 :
2702 : /* virtual */
2703 : bool
2704 0 : Selection::Collapsed()
2705 : {
2706 0 : return IsCollapsed();
2707 : }
2708 :
2709 : NS_IMETHODIMP
2710 20 : Selection::GetIsCollapsed(bool* aIsCollapsed)
2711 : {
2712 20 : NS_ENSURE_TRUE(aIsCollapsed, NS_ERROR_NULL_POINTER);
2713 :
2714 20 : *aIsCollapsed = IsCollapsed();
2715 20 : return NS_OK;
2716 : }
2717 :
2718 : NS_IMETHODIMP
2719 4 : Selection::GetRangeCount(int32_t* aRangeCount)
2720 : {
2721 4 : *aRangeCount = (int32_t)RangeCount();
2722 :
2723 4 : return NS_OK;
2724 : }
2725 :
2726 : NS_IMETHODIMP
2727 0 : Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn)
2728 : {
2729 0 : ErrorResult result;
2730 0 : *aReturn = GetRangeAt(aIndex, result);
2731 0 : NS_IF_ADDREF(*aReturn);
2732 0 : return result.StealNSResult();
2733 : }
2734 :
2735 : nsRange*
2736 0 : Selection::GetRangeAt(uint32_t aIndex, ErrorResult& aRv)
2737 : {
2738 0 : nsRange* range = GetRangeAt(aIndex);
2739 0 : if (!range) {
2740 0 : aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
2741 0 : return nullptr;
2742 : }
2743 :
2744 0 : return range;
2745 : }
2746 :
2747 : nsRange*
2748 24 : Selection::GetRangeAt(int32_t aIndex) const
2749 : {
2750 48 : RangeData empty(nullptr);
2751 48 : return mRanges.SafeElementAt(aIndex, empty).mRange;
2752 : }
2753 :
2754 : /*
2755 : utility function
2756 : */
2757 : nsresult
2758 0 : Selection::SetAnchorFocusToRange(nsRange* aRange)
2759 : {
2760 0 : NS_ENSURE_STATE(mAnchorFocusRange);
2761 :
2762 0 : bool collapsed = Collapsed();
2763 :
2764 0 : nsresult res = RemoveItem(mAnchorFocusRange);
2765 0 : if (NS_FAILED(res))
2766 0 : return res;
2767 :
2768 0 : int32_t aOutIndex = -1;
2769 0 : res = AddItem(aRange, &aOutIndex, !collapsed);
2770 0 : if (NS_FAILED(res))
2771 0 : return res;
2772 0 : SetAnchorFocusRange(aOutIndex);
2773 :
2774 0 : return NS_OK;
2775 : }
2776 :
2777 : void
2778 0 : Selection::ReplaceAnchorFocusRange(nsRange* aRange)
2779 : {
2780 0 : NS_ENSURE_TRUE_VOID(mAnchorFocusRange);
2781 0 : RefPtr<nsPresContext> presContext = GetPresContext();
2782 0 : if (presContext) {
2783 0 : SelectFrames(presContext, mAnchorFocusRange, false);
2784 0 : SetAnchorFocusToRange(aRange);
2785 0 : SelectFrames(presContext, mAnchorFocusRange, true);
2786 : }
2787 : }
2788 :
2789 : void
2790 0 : Selection::AdjustAnchorFocusForMultiRange(nsDirection aDirection)
2791 : {
2792 0 : if (aDirection == mDirection) {
2793 0 : return;
2794 : }
2795 0 : SetDirection(aDirection);
2796 :
2797 0 : if (RangeCount() <= 1) {
2798 0 : return;
2799 : }
2800 :
2801 0 : nsRange* firstRange = GetRangeAt(0);
2802 0 : nsRange* lastRange = GetRangeAt(RangeCount() - 1);
2803 :
2804 0 : if (mDirection == eDirPrevious) {
2805 0 : firstRange->SetIsGenerated(false);
2806 0 : lastRange->SetIsGenerated(true);
2807 0 : SetAnchorFocusRange(0);
2808 : } else { // aDir == eDirNext
2809 0 : firstRange->SetIsGenerated(true);
2810 0 : lastRange->SetIsGenerated(false);
2811 0 : SetAnchorFocusRange(RangeCount() - 1);
2812 : }
2813 : }
2814 :
2815 : /*
2816 : Notes which might come in handy for extend:
2817 :
2818 : We can tell the direction of the selection by asking for the anchors selection
2819 : if the begin is less than the end then we know the selection is to the "right".
2820 : else it is a backwards selection.
2821 : a = anchor
2822 : 1 = old cursor
2823 : 2 = new cursor
2824 :
2825 : if (a <= 1 && 1 <=2) a,1,2 or (a1,2)
2826 : if (a < 2 && 1 > 2) a,2,1
2827 : if (1 < a && a <2) 1,a,2
2828 : if (a > 2 && 2 >1) 1,2,a
2829 : if (2 < a && a <1) 2,a,1
2830 : if (a > 1 && 1 >2) 2,1,a
2831 : then execute
2832 : a 1 2 select from 1 to 2
2833 : a 2 1 deselect from 2 to 1
2834 : 1 a 2 deselect from 1 to a select from a to 2
2835 : 1 2 a deselect from 1 to 2
2836 : 2 1 a = continue selection from 2 to 1
2837 : */
2838 :
2839 :
2840 : /*
2841 : * Extend extends the selection away from the anchor.
2842 : * We don't need to know the direction, because we always change the focus.
2843 : */
2844 : NS_IMETHODIMP
2845 0 : Selection::Extend(nsIDOMNode* aContainer, int32_t aOffset)
2846 : {
2847 0 : nsCOMPtr<nsINode> container = do_QueryInterface(aContainer);
2848 0 : return Extend(container, aOffset);
2849 : }
2850 :
2851 : NS_IMETHODIMP
2852 0 : Selection::ExtendNative(nsINode* aContainer, int32_t aOffset)
2853 : {
2854 0 : return Extend(aContainer, aOffset);
2855 : }
2856 :
2857 : void
2858 0 : Selection::ExtendJS(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
2859 : {
2860 0 : AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
2861 0 : mCalledByJS = true;
2862 0 : Extend(aContainer, aOffset, aRv);
2863 0 : }
2864 :
2865 : nsresult
2866 0 : Selection::Extend(nsINode* aContainer, int32_t aOffset)
2867 : {
2868 0 : if (!aContainer) {
2869 0 : return NS_ERROR_INVALID_ARG;
2870 : }
2871 :
2872 0 : ErrorResult result;
2873 0 : Extend(*aContainer, static_cast<uint32_t>(aOffset), result);
2874 0 : return result.StealNSResult();
2875 : }
2876 :
2877 : void
2878 0 : Selection::Extend(nsINode& aContainer, uint32_t aOffset, ErrorResult& aRv)
2879 : {
2880 : // First, find the range containing the old focus point:
2881 0 : if (!mAnchorFocusRange) {
2882 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2883 0 : return;
2884 : }
2885 :
2886 0 : if (!mFrameSelection) {
2887 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED); // Can't do selection
2888 0 : return;
2889 : }
2890 :
2891 : nsresult res;
2892 0 : if (!IsValidSelectionPoint(mFrameSelection, &aContainer)) {
2893 0 : aRv.Throw(NS_ERROR_FAILURE);
2894 0 : return;
2895 : }
2896 :
2897 0 : RefPtr<nsPresContext> presContext = GetPresContext();
2898 0 : if (!presContext || presContext->Document() != aContainer.OwnerDoc()) {
2899 0 : aRv.Throw(NS_ERROR_FAILURE);
2900 0 : return;
2901 : }
2902 :
2903 : #ifdef DEBUG_SELECTION
2904 : nsDirection oldDirection = GetDirection();
2905 : #endif
2906 0 : nsINode* anchorNode = GetAnchorNode();
2907 0 : nsINode* focusNode = GetFocusNode();
2908 0 : uint32_t anchorOffset = AnchorOffset();
2909 0 : uint32_t focusOffset = FocusOffset();
2910 :
2911 0 : RefPtr<nsRange> range = mAnchorFocusRange->CloneRange();
2912 :
2913 0 : nsINode* startNode = range->GetStartContainer();
2914 0 : nsINode* endNode = range->GetEndContainer();
2915 0 : int32_t startOffset = range->StartOffset();
2916 0 : int32_t endOffset = range->EndOffset();
2917 :
2918 : //compare anchor to old cursor.
2919 :
2920 : // We pass |disconnected| to the following ComparePoints calls in order
2921 : // to avoid assertions. ComparePoints returns 1 in the disconnected case
2922 : // and we can end up in various cases below, but it is assumed that in
2923 : // any of the cases we end up, the nsRange implementation will collapse
2924 : // the range to the new point because we can not make a valid range with
2925 : // a disconnected point. This means that whatever range is currently
2926 : // selected will be cleared.
2927 0 : bool disconnected = false;
2928 0 : bool shouldClearRange = false;
2929 0 : int32_t result1 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
2930 : focusNode, focusOffset,
2931 0 : &disconnected);
2932 : //compare old cursor to new cursor
2933 0 : shouldClearRange |= disconnected;
2934 0 : int32_t result2 = nsContentUtils::ComparePoints(focusNode, focusOffset,
2935 : &aContainer, aOffset,
2936 0 : &disconnected);
2937 : //compare anchor to new cursor
2938 0 : shouldClearRange |= disconnected;
2939 0 : int32_t result3 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
2940 : &aContainer, aOffset,
2941 0 : &disconnected);
2942 :
2943 : // If the points are disconnected, the range will be collapsed below,
2944 : // resulting in a range that selects nothing.
2945 0 : if (shouldClearRange) {
2946 : // Repaint the current range with the selection removed.
2947 0 : SelectFrames(presContext, range, false);
2948 : }
2949 :
2950 0 : RefPtr<nsRange> difRange = new nsRange(&aContainer);
2951 0 : if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2 a,1,2
2952 : //select from 1 to 2 unless they are collapsed
2953 0 : range->SetEnd(aContainer, aOffset, aRv);
2954 0 : if (aRv.Failed()) {
2955 0 : return;
2956 : }
2957 0 : SetDirection(eDirNext);
2958 0 : res = difRange->SetStartAndEnd(focusNode, focusOffset,
2959 : range->GetEndContainer(),
2960 0 : range->EndOffset());
2961 0 : if (NS_FAILED(res)) {
2962 0 : aRv.Throw(res);
2963 0 : return;
2964 : }
2965 0 : SelectFrames(presContext, difRange , true);
2966 0 : res = SetAnchorFocusToRange(range);
2967 0 : if (NS_FAILED(res)) {
2968 0 : aRv.Throw(res);
2969 0 : return;
2970 : }
2971 : }
2972 0 : else if (result1 == 0 && result3 > 0){//2, a1
2973 : //select from 2 to 1a
2974 0 : SetDirection(eDirPrevious);
2975 0 : range->SetStart(aContainer, aOffset, aRv);
2976 0 : if (aRv.Failed()) {
2977 0 : return;
2978 : }
2979 0 : SelectFrames(presContext, range, true);
2980 0 : res = SetAnchorFocusToRange(range);
2981 0 : if (NS_FAILED(res)) {
2982 0 : aRv.Throw(res);
2983 0 : return;
2984 : }
2985 : }
2986 0 : else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21
2987 : //deselect from 2 to 1
2988 0 : res = difRange->SetStartAndEnd(&aContainer, aOffset,
2989 0 : focusNode, focusOffset);
2990 0 : if (NS_FAILED(res)) {
2991 0 : aRv.Throw(res);
2992 0 : return;
2993 : }
2994 :
2995 0 : range->SetEnd(aContainer, aOffset, aRv);
2996 0 : if (aRv.Failed()) {
2997 0 : return;
2998 : }
2999 0 : res = SetAnchorFocusToRange(range);
3000 0 : if (NS_FAILED(res)) {
3001 0 : aRv.Throw(res);
3002 0 : return;
3003 : }
3004 0 : SelectFrames(presContext, difRange, false); // deselect now
3005 0 : difRange->SetEnd(range->GetEndContainer(), range->EndOffset());
3006 0 : SelectFrames(presContext, difRange, true); // must reselect last node
3007 : // maybe more
3008 : }
3009 0 : else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2
3010 0 : if (GetDirection() == eDirPrevious){
3011 0 : res = range->SetStart(endNode, endOffset);
3012 0 : if (NS_FAILED(res)) {
3013 0 : aRv.Throw(res);
3014 0 : return;
3015 : }
3016 : }
3017 0 : SetDirection(eDirNext);
3018 0 : range->SetEnd(aContainer, aOffset, aRv);
3019 0 : if (aRv.Failed()) {
3020 0 : return;
3021 : }
3022 0 : if (focusNode != anchorNode || focusOffset != anchorOffset) {//if collapsed diff dont do anything
3023 0 : res = difRange->SetStart(focusNode, focusOffset);
3024 0 : nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset);
3025 0 : if (NS_FAILED(tmp)) {
3026 0 : res = tmp;
3027 : }
3028 0 : if (NS_FAILED(res)) {
3029 0 : aRv.Throw(res);
3030 0 : return;
3031 : }
3032 0 : res = SetAnchorFocusToRange(range);
3033 0 : if (NS_FAILED(res)) {
3034 0 : aRv.Throw(res);
3035 0 : return;
3036 : }
3037 : //deselect from 1 to a
3038 0 : SelectFrames(presContext, difRange , false);
3039 : }
3040 : else
3041 : {
3042 0 : res = SetAnchorFocusToRange(range);
3043 0 : if (NS_FAILED(res)) {
3044 0 : aRv.Throw(res);
3045 0 : return;
3046 : }
3047 : }
3048 : //select from a to 2
3049 0 : SelectFrames(presContext, range , true);
3050 : }
3051 0 : else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a
3052 : //deselect from 1 to 2
3053 0 : res = difRange->SetStartAndEnd(focusNode, focusOffset,
3054 0 : &aContainer, aOffset);
3055 0 : if (NS_FAILED(res)) {
3056 0 : aRv.Throw(res);
3057 0 : return;
3058 : }
3059 0 : SetDirection(eDirPrevious);
3060 0 : range->SetStart(aContainer, aOffset, aRv);
3061 0 : if (aRv.Failed()) {
3062 0 : return;
3063 : }
3064 :
3065 0 : res = SetAnchorFocusToRange(range);
3066 0 : if (NS_FAILED(res)) {
3067 0 : aRv.Throw(res);
3068 0 : return;
3069 : }
3070 0 : SelectFrames(presContext, difRange , false);
3071 0 : difRange->SetStart(range->GetStartContainer(), range->StartOffset());
3072 0 : SelectFrames(presContext, difRange, true); // must reselect last node
3073 : }
3074 0 : else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1
3075 0 : if (GetDirection() == eDirNext){
3076 0 : range->SetEnd(startNode, startOffset);
3077 : }
3078 0 : SetDirection(eDirPrevious);
3079 0 : range->SetStart(aContainer, aOffset, aRv);
3080 0 : if (aRv.Failed()) {
3081 0 : return;
3082 : }
3083 : //deselect from a to 1
3084 0 : if (focusNode != anchorNode || focusOffset!= anchorOffset) {//if collapsed diff dont do anything
3085 0 : res = difRange->SetStartAndEnd(anchorNode, anchorOffset,
3086 0 : focusNode, focusOffset);
3087 0 : nsresult tmp = SetAnchorFocusToRange(range);
3088 0 : if (NS_FAILED(tmp)) {
3089 0 : res = tmp;
3090 : }
3091 0 : if (NS_FAILED(res)) {
3092 0 : aRv.Throw(res);
3093 0 : return;
3094 : }
3095 0 : SelectFrames(presContext, difRange, false);
3096 : }
3097 : else
3098 : {
3099 0 : res = SetAnchorFocusToRange(range);
3100 0 : if (NS_FAILED(res)) {
3101 0 : aRv.Throw(res);
3102 0 : return;
3103 : }
3104 : }
3105 : //select from 2 to a
3106 0 : SelectFrames(presContext, range , true);
3107 : }
3108 0 : else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a
3109 : //select from 2 to 1
3110 0 : range->SetStart(aContainer, aOffset, aRv);
3111 0 : if (aRv.Failed()) {
3112 0 : return;
3113 : }
3114 0 : SetDirection(eDirPrevious);
3115 0 : res = difRange->SetStartAndEnd(
3116 : range->GetStartContainer(), range->StartOffset(),
3117 0 : focusNode, focusOffset);
3118 0 : if (NS_FAILED(res)) {
3119 0 : aRv.Throw(res);
3120 0 : return;
3121 : }
3122 :
3123 0 : SelectFrames(presContext, difRange, true);
3124 0 : res = SetAnchorFocusToRange(range);
3125 0 : if (NS_FAILED(res)) {
3126 0 : aRv.Throw(res);
3127 0 : return;
3128 : }
3129 : }
3130 :
3131 0 : if (mRanges.Length() > 1) {
3132 0 : for (size_t i = 0; i < mRanges.Length(); ++i) {
3133 0 : nsRange* range = mRanges[i].mRange;
3134 0 : MOZ_ASSERT(range->IsInSelection());
3135 0 : SelectFrames(presContext, range, range->IsInSelection());
3136 : }
3137 : }
3138 :
3139 : DEBUG_OUT_RANGE(range);
3140 : #ifdef DEBUG_SELECTION
3141 : if (GetDirection() != oldDirection) {
3142 : printf(" direction changed to %s\n",
3143 : GetDirection() == eDirNext? "eDirNext":"eDirPrevious");
3144 : }
3145 : nsCOMPtr<nsIContent> content = do_QueryInterface(&aContainer);
3146 : printf ("Sel. Extend to %p %s %d\n", content.get(),
3147 : nsAtomCString(content->NodeInfo()->NameAtom()).get(), aOffset);
3148 : #endif
3149 :
3150 : // Be aware, this instance may be destroyed after this call.
3151 : // XXX Why doesn't this call Selection::NotifySelectionListener() directly?
3152 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3153 0 : res = frameSelection->NotifySelectionListeners(GetType());
3154 0 : if (NS_FAILED(res)) {
3155 0 : aRv.Throw(res);
3156 : }
3157 : }
3158 :
3159 : NS_IMETHODIMP
3160 0 : Selection::SelectAllChildren(nsIDOMNode* aNode)
3161 : {
3162 0 : ErrorResult result;
3163 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
3164 0 : NS_ENSURE_TRUE(node, NS_ERROR_INVALID_ARG);
3165 0 : SelectAllChildren(*node, result);
3166 0 : return result.StealNSResult();
3167 : }
3168 :
3169 : void
3170 0 : Selection::SelectAllChildrenJS(nsINode& aNode, ErrorResult& aRv)
3171 : {
3172 0 : AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
3173 0 : mCalledByJS = true;
3174 0 : SelectAllChildren(aNode, aRv);
3175 0 : }
3176 :
3177 : void
3178 0 : Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv)
3179 : {
3180 0 : if (mFrameSelection) {
3181 0 : mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
3182 : }
3183 0 : SelectionBatcher batch(this);
3184 :
3185 0 : Collapse(aNode, 0, aRv);
3186 0 : if (aRv.Failed()) {
3187 0 : return;
3188 : }
3189 :
3190 0 : Extend(aNode, aNode.GetChildCount(), aRv);
3191 : }
3192 :
3193 : NS_IMETHODIMP
3194 0 : Selection::ContainsNode(nsIDOMNode* aNode, bool aAllowPartial, bool* aYes)
3195 : {
3196 0 : if (!aYes) {
3197 0 : return NS_ERROR_NULL_POINTER;
3198 : }
3199 0 : *aYes = false;
3200 :
3201 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
3202 0 : if (!node) {
3203 0 : return NS_ERROR_NULL_POINTER;
3204 : }
3205 0 : ErrorResult result;
3206 0 : *aYes = ContainsNode(*node, aAllowPartial, result);
3207 0 : return result.StealNSResult();
3208 : }
3209 :
3210 : bool
3211 0 : Selection::ContainsNode(nsINode& aNode, bool aAllowPartial, ErrorResult& aRv)
3212 : {
3213 : nsresult rv;
3214 0 : if (mRanges.Length() == 0) {
3215 0 : return false;
3216 : }
3217 :
3218 : // XXXbz this duplicates the GetNodeLength code in nsRange.cpp
3219 : uint32_t nodeLength;
3220 0 : bool isData = aNode.IsNodeOfType(nsINode::eDATA_NODE);
3221 0 : if (isData) {
3222 0 : nodeLength = static_cast<nsIContent&>(aNode).TextLength();
3223 : } else {
3224 0 : nodeLength = aNode.GetChildCount();
3225 : }
3226 :
3227 0 : nsTArray<nsRange*> overlappingRanges;
3228 0 : rv = GetRangesForIntervalArray(&aNode, 0, &aNode, nodeLength,
3229 0 : false, &overlappingRanges);
3230 0 : if (NS_FAILED(rv)) {
3231 0 : aRv.Throw(rv);
3232 0 : return false;
3233 : }
3234 0 : if (overlappingRanges.Length() == 0)
3235 0 : return false; // no ranges overlap
3236 :
3237 : // if the caller said partial intersections are OK, we're done
3238 0 : if (aAllowPartial) {
3239 0 : return true;
3240 : }
3241 :
3242 : // text nodes always count as inside
3243 0 : if (isData) {
3244 0 : return true;
3245 : }
3246 :
3247 : // The caller wants to know if the node is entirely within the given range,
3248 : // so we have to check all intersecting ranges.
3249 0 : for (uint32_t i = 0; i < overlappingRanges.Length(); i++) {
3250 : bool nodeStartsBeforeRange, nodeEndsAfterRange;
3251 0 : if (NS_SUCCEEDED(nsRange::CompareNodeToRange(&aNode, overlappingRanges[i],
3252 : &nodeStartsBeforeRange,
3253 : &nodeEndsAfterRange))) {
3254 0 : if (!nodeStartsBeforeRange && !nodeEndsAfterRange) {
3255 0 : return true;
3256 : }
3257 : }
3258 : }
3259 0 : return false;
3260 : }
3261 :
3262 : class PointInRectChecker : public nsLayoutUtils::RectCallback {
3263 : public:
3264 0 : explicit PointInRectChecker(const nsPoint& aPoint)
3265 0 : : mPoint(aPoint)
3266 0 : , mMatchFound(false)
3267 : {
3268 0 : }
3269 :
3270 0 : void AddRect(const nsRect& aRect) override
3271 : {
3272 0 : mMatchFound = mMatchFound || aRect.Contains(mPoint);
3273 0 : }
3274 :
3275 0 : bool MatchFound()
3276 : {
3277 0 : return mMatchFound;
3278 : }
3279 :
3280 : private:
3281 : nsPoint mPoint;
3282 : bool mMatchFound;
3283 : };
3284 :
3285 : bool
3286 0 : Selection::ContainsPoint(const nsPoint& aPoint)
3287 : {
3288 0 : if (IsCollapsed()) {
3289 0 : return false;
3290 : }
3291 0 : PointInRectChecker checker(aPoint);
3292 0 : for (uint32_t i = 0; i < RangeCount(); i++) {
3293 0 : nsRange* range = GetRangeAt(i);
3294 0 : nsRange::CollectClientRectsAndText(&checker, nullptr, range,
3295 : range->GetStartContainer(),
3296 : range->StartOffset(),
3297 : range->GetEndContainer(),
3298 : range->EndOffset(),
3299 0 : true, false);
3300 0 : if (checker.MatchFound()) {
3301 0 : return true;
3302 : }
3303 : }
3304 0 : return false;
3305 : }
3306 :
3307 : nsPresContext*
3308 34 : Selection::GetPresContext() const
3309 : {
3310 34 : nsIPresShell *shell = GetPresShell();
3311 34 : if (!shell) {
3312 0 : return nullptr;
3313 : }
3314 :
3315 34 : return shell->GetPresContext();
3316 : }
3317 :
3318 : nsIPresShell*
3319 102 : Selection::GetPresShell() const
3320 : {
3321 102 : if (!mFrameSelection)
3322 0 : return nullptr;//nothing to do
3323 :
3324 102 : return mFrameSelection->GetShell();
3325 : }
3326 :
3327 : nsIDocument*
3328 0 : Selection::GetDocument() const
3329 : {
3330 0 : nsIPresShell* presShell = GetPresShell();
3331 0 : return presShell ? presShell->GetDocument() : nullptr;
3332 : }
3333 :
3334 : nsPIDOMWindowOuter*
3335 0 : Selection::GetWindow() const
3336 : {
3337 0 : nsIDocument* document = GetDocument();
3338 0 : return document ? document->GetWindow() : nullptr;
3339 : }
3340 :
3341 : nsIEditor*
3342 0 : Selection::GetEditor() const
3343 : {
3344 0 : nsPresContext* presContext = GetPresContext();
3345 0 : if (!presContext) {
3346 0 : return nullptr;
3347 : }
3348 0 : return nsContentUtils::GetHTMLEditor(presContext);
3349 : }
3350 :
3351 : nsIFrame *
3352 1 : Selection::GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect* aRect)
3353 : {
3354 1 : if (!mFrameSelection)
3355 0 : return nullptr; // nothing to do
3356 :
3357 1 : NS_ENSURE_TRUE(aRect, nullptr);
3358 :
3359 1 : aRect->SetRect(0, 0, 0, 0);
3360 :
3361 1 : switch (aRegion) {
3362 : case nsISelectionController::SELECTION_ANCHOR_REGION:
3363 : case nsISelectionController::SELECTION_FOCUS_REGION:
3364 1 : return GetSelectionEndPointGeometry(aRegion, aRect);
3365 : case nsISelectionController::SELECTION_WHOLE_SELECTION:
3366 0 : break;
3367 : default:
3368 0 : return nullptr;
3369 : }
3370 :
3371 0 : NS_ASSERTION(aRegion == nsISelectionController::SELECTION_WHOLE_SELECTION,
3372 : "should only be SELECTION_WHOLE_SELECTION here");
3373 :
3374 0 : nsRect anchorRect;
3375 : nsIFrame* anchorFrame = GetSelectionEndPointGeometry(
3376 0 : nsISelectionController::SELECTION_ANCHOR_REGION, &anchorRect);
3377 0 : if (!anchorFrame)
3378 0 : return nullptr;
3379 :
3380 0 : nsRect focusRect;
3381 : nsIFrame* focusFrame = GetSelectionEndPointGeometry(
3382 0 : nsISelectionController::SELECTION_FOCUS_REGION, &focusRect);
3383 0 : if (!focusFrame)
3384 0 : return nullptr;
3385 :
3386 0 : NS_ASSERTION(anchorFrame->PresContext() == focusFrame->PresContext(),
3387 : "points of selection in different documents?");
3388 : // make focusRect relative to anchorFrame
3389 0 : focusRect += focusFrame->GetOffsetTo(anchorFrame);
3390 :
3391 0 : aRect->UnionRectEdges(anchorRect, focusRect);
3392 0 : return anchorFrame;
3393 : }
3394 :
3395 : nsIFrame *
3396 1 : Selection::GetSelectionEndPointGeometry(SelectionRegion aRegion, nsRect* aRect)
3397 : {
3398 1 : if (!mFrameSelection)
3399 0 : return nullptr; // nothing to do
3400 :
3401 1 : NS_ENSURE_TRUE(aRect, nullptr);
3402 :
3403 1 : aRect->SetRect(0, 0, 0, 0);
3404 :
3405 1 : nsINode *node = nullptr;
3406 1 : uint32_t nodeOffset = 0;
3407 1 : nsIFrame *frame = nullptr;
3408 :
3409 1 : switch (aRegion) {
3410 : case nsISelectionController::SELECTION_ANCHOR_REGION:
3411 0 : node = GetAnchorNode();
3412 0 : nodeOffset = AnchorOffset();
3413 0 : break;
3414 : case nsISelectionController::SELECTION_FOCUS_REGION:
3415 1 : node = GetFocusNode();
3416 1 : nodeOffset = FocusOffset();
3417 1 : break;
3418 : default:
3419 0 : return nullptr;
3420 : }
3421 :
3422 1 : if (!node)
3423 0 : return nullptr;
3424 :
3425 2 : nsCOMPtr<nsIContent> content = do_QueryInterface(node);
3426 1 : NS_ENSURE_TRUE(content.get(), nullptr);
3427 1 : int32_t frameOffset = 0;
3428 1 : frame = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset,
3429 : mFrameSelection->GetHint(),
3430 1 : &frameOffset);
3431 1 : if (!frame)
3432 0 : return nullptr;
3433 :
3434 : // Figure out what node type we have, then get the
3435 : // appropriate rect for it's nodeOffset.
3436 1 : bool isText = node->IsNodeOfType(nsINode::eTEXT);
3437 :
3438 1 : nsPoint pt(0, 0);
3439 1 : if (isText) {
3440 1 : nsIFrame* childFrame = nullptr;
3441 1 : frameOffset = 0;
3442 : nsresult rv =
3443 1 : frame->GetChildFrameContainingOffset(nodeOffset,
3444 1 : mFrameSelection->GetHint(),
3445 2 : &frameOffset, &childFrame);
3446 1 : if (NS_FAILED(rv))
3447 0 : return nullptr;
3448 1 : if (!childFrame)
3449 0 : return nullptr;
3450 :
3451 1 : frame = childFrame;
3452 :
3453 : // Get the x coordinate of the offset into the text frame.
3454 1 : rv = GetCachedFrameOffset(frame, nodeOffset, pt);
3455 1 : if (NS_FAILED(rv))
3456 0 : return nullptr;
3457 : }
3458 :
3459 : // Return the rect relative to the frame, with zero width.
3460 1 : if (isText) {
3461 1 : aRect->x = pt.x;
3462 0 : } else if (mFrameSelection->GetHint() == CARET_ASSOCIATE_BEFORE) {
3463 : // It's the frame's right edge we're interested in.
3464 0 : aRect->x = frame->GetRect().width;
3465 : }
3466 1 : aRect->height = frame->GetRect().height;
3467 :
3468 1 : return frame;
3469 : }
3470 :
3471 : NS_IMETHODIMP
3472 2 : Selection::ScrollSelectionIntoViewEvent::Run()
3473 : {
3474 2 : if (!mSelection)
3475 1 : return NS_OK; // event revoked
3476 :
3477 : int32_t flags = Selection::SCROLL_DO_FLUSH |
3478 1 : Selection::SCROLL_SYNCHRONOUS;
3479 :
3480 1 : Selection* sel = mSelection; // workaround to satisfy static analysis
3481 2 : RefPtr<Selection> kungFuDeathGrip(sel);
3482 1 : mSelection->mScrollEvent.Forget();
3483 1 : mSelection->ScrollIntoView(mRegion, mVerticalScroll,
3484 2 : mHorizontalScroll, mFlags | flags);
3485 1 : return NS_OK;
3486 : }
3487 :
3488 : nsresult
3489 2 : Selection::PostScrollSelectionIntoViewEvent(
3490 : SelectionRegion aRegion,
3491 : int32_t aFlags,
3492 : nsIPresShell::ScrollAxis aVertical,
3493 : nsIPresShell::ScrollAxis aHorizontal)
3494 : {
3495 : // If we've already posted an event, revoke it and place a new one at the
3496 : // end of the queue to make sure that any new pending reflow events are
3497 : // processed before we scroll. This will insure that we scroll to the
3498 : // correct place on screen.
3499 2 : mScrollEvent.Revoke();
3500 2 : nsPresContext* presContext = GetPresContext();
3501 2 : NS_ENSURE_STATE(presContext);
3502 2 : nsRefreshDriver* refreshDriver = presContext->RefreshDriver();
3503 2 : NS_ENSURE_STATE(refreshDriver);
3504 :
3505 : RefPtr<ScrollSelectionIntoViewEvent> ev =
3506 : new ScrollSelectionIntoViewEvent(this, aRegion, aVertical, aHorizontal,
3507 6 : aFlags);
3508 2 : mScrollEvent = ev;
3509 2 : refreshDriver->AddEarlyRunner(ev);
3510 2 : return NS_OK;
3511 : }
3512 :
3513 : NS_IMETHODIMP
3514 0 : Selection::ScrollIntoView(SelectionRegion aRegion, bool aIsSynchronous,
3515 : int16_t aVPercent, int16_t aHPercent)
3516 : {
3517 0 : ErrorResult result;
3518 0 : ScrollIntoView(aRegion, aIsSynchronous, aVPercent, aHPercent, result);
3519 0 : if (result.Failed()) {
3520 0 : return result.StealNSResult();
3521 : }
3522 0 : return NS_OK;
3523 : }
3524 :
3525 : void
3526 0 : Selection::ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
3527 : int16_t aVPercent, int16_t aHPercent,
3528 : ErrorResult& aRv)
3529 : {
3530 0 : nsresult rv = ScrollIntoViewInternal(aRegion, aIsSynchronous,
3531 : nsIPresShell::ScrollAxis(aVPercent),
3532 0 : nsIPresShell::ScrollAxis(aHPercent));
3533 0 : if (NS_FAILED(rv)) {
3534 0 : aRv.Throw(rv);
3535 : }
3536 0 : }
3537 :
3538 : NS_IMETHODIMP
3539 0 : Selection::ScrollIntoViewInternal(SelectionRegion aRegion, bool aIsSynchronous,
3540 : nsIPresShell::ScrollAxis aVertical,
3541 : nsIPresShell::ScrollAxis aHorizontal)
3542 : {
3543 0 : return ScrollIntoView(aRegion, aVertical, aHorizontal,
3544 0 : aIsSynchronous ? Selection::SCROLL_SYNCHRONOUS : 0);
3545 : }
3546 :
3547 : nsresult
3548 4 : Selection::ScrollIntoView(SelectionRegion aRegion,
3549 : nsIPresShell::ScrollAxis aVertical,
3550 : nsIPresShell::ScrollAxis aHorizontal,
3551 : int32_t aFlags)
3552 : {
3553 4 : if (!mFrameSelection)
3554 0 : return NS_OK;//nothing to do
3555 :
3556 8 : nsCOMPtr<nsIPresShell> presShell = mFrameSelection->GetShell();
3557 4 : if (!presShell)
3558 0 : return NS_OK;
3559 :
3560 4 : if (mFrameSelection->GetBatching())
3561 1 : return NS_OK;
3562 :
3563 3 : if (!(aFlags & Selection::SCROLL_SYNCHRONOUS))
3564 2 : return PostScrollSelectionIntoViewEvent(aRegion, aFlags,
3565 2 : aVertical, aHorizontal);
3566 :
3567 : // Now that text frame character offsets are always valid (though not
3568 : // necessarily correct), the worst that will happen if we don't flush here
3569 : // is that some callers might scroll to the wrong place. Those should
3570 : // either manually flush if they're in a safe position for it or use the
3571 : // async version of this method.
3572 1 : if (aFlags & Selection::SCROLL_DO_FLUSH) {
3573 1 : presShell->FlushPendingNotifications(FlushType::Layout);
3574 :
3575 : // Reget the presshell, since it might have been Destroy'ed.
3576 1 : presShell = mFrameSelection ? mFrameSelection->GetShell() : nullptr;
3577 1 : if (!presShell)
3578 0 : return NS_OK;
3579 : }
3580 :
3581 : //
3582 : // Scroll the selection region into view.
3583 : //
3584 :
3585 2 : nsRect rect;
3586 1 : nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect);
3587 1 : if (!frame)
3588 0 : return NS_ERROR_FAILURE;
3589 :
3590 : // Scroll vertically to get the caret into view, but only if the container
3591 : // is perceived to be scrollable in that direction (i.e. there is a visible
3592 : // vertical scrollbar or the scroll range is at least one device pixel)
3593 1 : aVertical.mOnlyIfPerceivedScrollableDirection = true;
3594 :
3595 1 : uint32_t flags = 0;
3596 1 : if (aFlags & Selection::SCROLL_FIRST_ANCESTOR_ONLY) {
3597 1 : flags |= nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY;
3598 : }
3599 1 : if (aFlags & Selection::SCROLL_OVERFLOW_HIDDEN) {
3600 0 : flags |= nsIPresShell::SCROLL_OVERFLOW_HIDDEN;
3601 : }
3602 :
3603 1 : if (aFlags & Selection::SCROLL_FOR_CARET_MOVE) {
3604 : mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
3605 0 : (uint32_t) ScrollInputMethod::MainThreadScrollCaretIntoView);
3606 : }
3607 :
3608 1 : presShell->ScrollFrameRectIntoView(frame, rect, aVertical, aHorizontal,
3609 1 : flags);
3610 1 : return NS_OK;
3611 : }
3612 :
3613 : NS_IMETHODIMP
3614 128 : Selection::AddSelectionListener(nsISelectionListener* aNewListener)
3615 : {
3616 128 : if (!aNewListener)
3617 0 : return NS_ERROR_NULL_POINTER;
3618 256 : ErrorResult result;
3619 128 : AddSelectionListener(aNewListener, result);
3620 128 : if (result.Failed()) {
3621 0 : return result.StealNSResult();
3622 : }
3623 128 : return NS_OK;
3624 : }
3625 :
3626 : void
3627 128 : Selection::AddSelectionListener(nsISelectionListener* aNewListener,
3628 : ErrorResult& aRv)
3629 : {
3630 128 : bool result = mSelectionListeners.AppendElement(aNewListener, fallible); // AddRefs
3631 128 : if (!result) {
3632 0 : aRv.Throw(NS_ERROR_FAILURE);
3633 : }
3634 128 : }
3635 :
3636 : NS_IMETHODIMP
3637 10 : Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove)
3638 : {
3639 10 : if (!aListenerToRemove)
3640 0 : return NS_ERROR_NULL_POINTER;
3641 20 : ErrorResult result;
3642 10 : RemoveSelectionListener(aListenerToRemove, result);
3643 10 : if (result.Failed()) {
3644 0 : return result.StealNSResult();
3645 : }
3646 10 : return NS_OK;
3647 : }
3648 :
3649 : void
3650 10 : Selection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove,
3651 : ErrorResult& aRv)
3652 : {
3653 10 : bool result = mSelectionListeners.RemoveElement(aListenerToRemove); // Releases
3654 10 : if (!result) {
3655 0 : aRv.Throw(NS_ERROR_FAILURE);
3656 : }
3657 10 : }
3658 :
3659 : Element*
3660 0 : Selection::GetCommonEditingHostForAllRanges()
3661 : {
3662 0 : Element* editingHost = nullptr;
3663 0 : for (RangeData& rangeData : mRanges) {
3664 0 : nsRange* range = rangeData.mRange;
3665 0 : MOZ_ASSERT(range);
3666 0 : nsINode* commonAncestorNode = range->GetCommonAncestor();
3667 0 : if (!commonAncestorNode || !commonAncestorNode->IsContent()) {
3668 0 : return nullptr;
3669 : }
3670 0 : nsIContent* commonAncestor = commonAncestorNode->AsContent();
3671 0 : Element* foundEditingHost = commonAncestor->GetEditingHost();
3672 : // Even when common ancestor is a non-editable element in a contenteditable
3673 : // element, we don't need to move focus to the contenteditable element
3674 : // because Chromium doesn't set focus to it.
3675 0 : if (!foundEditingHost) {
3676 0 : return nullptr;
3677 : }
3678 0 : if (!editingHost) {
3679 0 : editingHost = foundEditingHost;
3680 0 : continue;
3681 : }
3682 0 : if (editingHost == foundEditingHost) {
3683 0 : continue;
3684 : }
3685 0 : if (nsContentUtils::ContentIsDescendantOf(foundEditingHost, editingHost)) {
3686 0 : continue;
3687 : }
3688 0 : if (nsContentUtils::ContentIsDescendantOf(editingHost, foundEditingHost)) {
3689 0 : editingHost = foundEditingHost;
3690 0 : continue;
3691 : }
3692 : // editingHost and foundEditingHost are not a descendant of the other.
3693 : // So, there is no common editing host.
3694 0 : return nullptr;
3695 : }
3696 0 : return editingHost;
3697 : }
3698 :
3699 : nsresult
3700 0 : Selection::NotifySelectionListeners(bool aCalledByJS)
3701 : {
3702 0 : AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
3703 0 : mCalledByJS = aCalledByJS;
3704 0 : return NotifySelectionListeners();
3705 : }
3706 :
3707 : nsresult
3708 35 : Selection::NotifySelectionListeners()
3709 : {
3710 35 : if (!mFrameSelection)
3711 0 : return NS_OK;//nothing to do
3712 :
3713 : // Our internal code should not move focus with using this class while
3714 : // this moves focus nor from selection listeners.
3715 70 : AutoRestore<bool> calledByJSRestorer(mCalledByJS);
3716 35 : mCalledByJS = false;
3717 :
3718 : // When normal selection is changed by Selection API, we need to move focus
3719 : // if common ancestor of all ranges are in an editing host. Note that we
3720 : // don't need to move focus *to* the other focusable node because other
3721 : // browsers don't do it either.
3722 62 : if (mSelectionType == SelectionType::eNormal &&
3723 27 : calledByJSRestorer.SavedValue()) {
3724 0 : nsPIDOMWindowOuter* window = GetWindow();
3725 0 : nsIDocument* document = GetDocument();
3726 : // If the document is in design mode or doesn't have contenteditable
3727 : // element, we don't need to move focus.
3728 0 : if (window && document && !document->HasFlag(NODE_IS_EDITABLE) &&
3729 0 : GetEditor()) {
3730 0 : RefPtr<Element> newEditingHost = GetCommonEditingHostForAllRanges();
3731 0 : nsFocusManager* fm = nsFocusManager::GetFocusManager();
3732 0 : nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
3733 : nsIContent* focusedContent =
3734 0 : fm->GetFocusedDescendant(window, false, getter_AddRefs(focusedWindow));
3735 0 : nsCOMPtr<Element> focusedElement = do_QueryInterface(focusedContent);
3736 : // When all selected ranges are in an editing host, it should take focus.
3737 : // But otherwise, we shouldn't move focus since Chromium doesn't move
3738 : // focus but only selection range is updated.
3739 0 : if (newEditingHost && newEditingHost != focusedElement) {
3740 0 : MOZ_ASSERT(!newEditingHost->IsInNativeAnonymousSubtree());
3741 : nsCOMPtr<nsIDOMElement> domElementToFocus =
3742 0 : do_QueryInterface(newEditingHost->AsDOMNode());
3743 : // Note that don't steal focus from focused window if the window doesn't
3744 : // have focus and if the window isn't focused window, shouldn't be
3745 : // scrolled to the new focused element.
3746 0 : uint32_t flags = nsIFocusManager::FLAG_NOSWITCHFRAME;
3747 0 : if (focusedWindow != fm->GetFocusedWindow()) {
3748 0 : flags |= nsIFocusManager::FLAG_NOSCROLL;
3749 : }
3750 0 : fm->SetFocus(domElementToFocus, flags);
3751 : }
3752 : }
3753 : }
3754 :
3755 70 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3756 35 : if (frameSelection->GetBatching()) {
3757 4 : frameSelection->SetDirty();
3758 4 : return NS_OK;
3759 : }
3760 : AutoTArray<nsCOMPtr<nsISelectionListener>, 8>
3761 62 : selectionListeners(mSelectionListeners);
3762 :
3763 62 : nsCOMPtr<nsIDOMDocument> domdoc;
3764 31 : nsIPresShell* ps = GetPresShell();
3765 31 : if (ps) {
3766 31 : domdoc = do_QueryInterface(ps->GetDocument());
3767 : }
3768 :
3769 31 : short reason = frameSelection->PopReason();
3770 123 : for (auto& listener : selectionListeners) {
3771 92 : listener->NotifySelectionChanged(domdoc, this, reason);
3772 : }
3773 31 : return NS_OK;
3774 : }
3775 :
3776 : NS_IMETHODIMP
3777 7 : Selection::StartBatchChanges()
3778 : {
3779 7 : if (mFrameSelection) {
3780 14 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3781 7 : frameSelection->StartBatchChanges();
3782 : }
3783 7 : return NS_OK;
3784 : }
3785 :
3786 :
3787 :
3788 : NS_IMETHODIMP
3789 6 : Selection::EndBatchChanges()
3790 : {
3791 6 : return EndBatchChangesInternal();
3792 : }
3793 :
3794 : nsresult
3795 7 : Selection::EndBatchChangesInternal(int16_t aReason)
3796 : {
3797 7 : if (mFrameSelection) {
3798 14 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3799 7 : frameSelection->EndBatchChanges(aReason);
3800 : }
3801 7 : return NS_OK;
3802 : }
3803 :
3804 : void
3805 6 : Selection::AddSelectionChangeBlocker()
3806 : {
3807 6 : mSelectionChangeBlockerCount++;
3808 6 : }
3809 :
3810 : void
3811 6 : Selection::RemoveSelectionChangeBlocker()
3812 : {
3813 6 : MOZ_ASSERT(mSelectionChangeBlockerCount > 0,
3814 : "mSelectionChangeBlockerCount has an invalid value - "
3815 : "maybe you have a mismatched RemoveSelectionChangeBlocker?");
3816 6 : mSelectionChangeBlockerCount--;
3817 6 : }
3818 :
3819 : bool
3820 11 : Selection::IsBlockingSelectionChangeEvents() const
3821 : {
3822 11 : return mSelectionChangeBlockerCount > 0;
3823 : }
3824 :
3825 : NS_IMETHODIMP
3826 0 : Selection::DeleteFromDocument()
3827 : {
3828 0 : ErrorResult result;
3829 0 : DeleteFromDocument(result);
3830 0 : return result.StealNSResult();
3831 : }
3832 :
3833 : void
3834 0 : Selection::DeleteFromDocument(ErrorResult& aRv)
3835 : {
3836 0 : if (!mFrameSelection)
3837 0 : return;//nothing to do
3838 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3839 0 : nsresult rv = frameSelection->DeleteFromDocument();
3840 0 : if (NS_FAILED(rv)) {
3841 0 : aRv.Throw(rv);
3842 : }
3843 : }
3844 :
3845 : NS_IMETHODIMP
3846 0 : Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
3847 : const nsAString& aGranularity)
3848 : {
3849 0 : ErrorResult result;
3850 0 : Modify(aAlter, aDirection, aGranularity, result);
3851 0 : return result.StealNSResult();
3852 : }
3853 :
3854 : void
3855 0 : Selection::Modify(const nsAString& aAlter, const nsAString& aDirection,
3856 : const nsAString& aGranularity, ErrorResult& aRv)
3857 : {
3858 : // Silently exit if there's no selection or no focus node.
3859 0 : if (!mFrameSelection || !GetAnchorFocusRange() || !GetFocusNode()) {
3860 0 : return;
3861 : }
3862 :
3863 0 : if (!aAlter.LowerCaseEqualsLiteral("move") &&
3864 0 : !aAlter.LowerCaseEqualsLiteral("extend")) {
3865 0 : aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
3866 0 : return;
3867 : }
3868 :
3869 0 : if (!aDirection.LowerCaseEqualsLiteral("forward") &&
3870 0 : !aDirection.LowerCaseEqualsLiteral("backward") &&
3871 0 : !aDirection.LowerCaseEqualsLiteral("left") &&
3872 0 : !aDirection.LowerCaseEqualsLiteral("right")) {
3873 0 : aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
3874 0 : return;
3875 : }
3876 :
3877 : // Line moves are always visual.
3878 0 : bool visual = aDirection.LowerCaseEqualsLiteral("left") ||
3879 0 : aDirection.LowerCaseEqualsLiteral("right") ||
3880 0 : aGranularity.LowerCaseEqualsLiteral("line");
3881 :
3882 0 : bool forward = aDirection.LowerCaseEqualsLiteral("forward") ||
3883 0 : aDirection.LowerCaseEqualsLiteral("right");
3884 :
3885 0 : bool extend = aAlter.LowerCaseEqualsLiteral("extend");
3886 :
3887 : nsSelectionAmount amount;
3888 0 : if (aGranularity.LowerCaseEqualsLiteral("character")) {
3889 0 : amount = eSelectCluster;
3890 0 : } else if (aGranularity.LowerCaseEqualsLiteral("word")) {
3891 0 : amount = eSelectWordNoSpace;
3892 0 : } else if (aGranularity.LowerCaseEqualsLiteral("line")) {
3893 0 : amount = eSelectLine;
3894 0 : } else if (aGranularity.LowerCaseEqualsLiteral("lineboundary")) {
3895 0 : amount = forward ? eSelectEndLine : eSelectBeginLine;
3896 0 : } else if (aGranularity.LowerCaseEqualsLiteral("sentence") ||
3897 0 : aGranularity.LowerCaseEqualsLiteral("sentenceboundary") ||
3898 0 : aGranularity.LowerCaseEqualsLiteral("paragraph") ||
3899 0 : aGranularity.LowerCaseEqualsLiteral("paragraphboundary") ||
3900 0 : aGranularity.LowerCaseEqualsLiteral("documentboundary")) {
3901 0 : aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
3902 0 : return;
3903 : } else {
3904 0 : aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
3905 0 : return;
3906 : }
3907 :
3908 : // If the anchor doesn't equal the focus and we try to move without first
3909 : // collapsing the selection, MoveCaret will collapse the selection and quit.
3910 : // To avoid this, we need to collapse the selection first.
3911 0 : nsresult rv = NS_OK;
3912 0 : if (!extend) {
3913 0 : nsINode* focusNode = GetFocusNode();
3914 : // We should have checked earlier that there was a focus node.
3915 0 : if (!focusNode) {
3916 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
3917 0 : return;
3918 : }
3919 0 : uint32_t focusOffset = FocusOffset();
3920 0 : Collapse(focusNode, focusOffset);
3921 : }
3922 :
3923 : // If the paragraph direction of the focused frame is right-to-left,
3924 : // we may have to swap the direction of movement.
3925 : nsIFrame *frame;
3926 : int32_t offset;
3927 0 : rv = GetPrimaryFrameForFocusNode(&frame, &offset, visual);
3928 0 : if (NS_SUCCEEDED(rv) && frame) {
3929 0 : nsBidiDirection paraDir = nsBidiPresUtils::ParagraphDirection(frame);
3930 :
3931 0 : if (paraDir == NSBIDI_RTL && visual) {
3932 0 : if (amount == eSelectBeginLine) {
3933 0 : amount = eSelectEndLine;
3934 0 : forward = !forward;
3935 0 : } else if (amount == eSelectEndLine) {
3936 0 : amount = eSelectBeginLine;
3937 0 : forward = !forward;
3938 : }
3939 : }
3940 : }
3941 :
3942 : // MoveCaret will return an error if it can't move in the specified
3943 : // direction, but we just ignore this error unless it's a line move, in which
3944 : // case we call nsISelectionController::CompleteMove to move the cursor to
3945 : // the beginning/end of the line.
3946 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
3947 0 : rv = frameSelection->MoveCaret(forward ? eDirNext : eDirPrevious,
3948 : extend, amount,
3949 : visual ? nsFrameSelection::eVisual
3950 0 : : nsFrameSelection::eLogical);
3951 :
3952 0 : if (aGranularity.LowerCaseEqualsLiteral("line") && NS_FAILED(rv)) {
3953 : nsCOMPtr<nsISelectionController> shell =
3954 0 : do_QueryInterface(frameSelection->GetShell());
3955 0 : if (!shell)
3956 0 : return;
3957 0 : shell->CompleteMove(forward, extend);
3958 : }
3959 : }
3960 :
3961 : void
3962 0 : Selection::SetBaseAndExtentJS(nsINode& aAnchorNode,
3963 : uint32_t aAnchorOffset,
3964 : nsINode& aFocusNode,
3965 : uint32_t aFocusOffset,
3966 : ErrorResult& aRv)
3967 : {
3968 0 : AutoRestore<bool> calledFromJSRestorer(mCalledByJS);
3969 0 : mCalledByJS = true;
3970 : SetBaseAndExtent(aAnchorNode, aAnchorOffset,
3971 0 : aFocusNode, aFocusOffset, aRv);
3972 0 : }
3973 :
3974 : void
3975 0 : Selection::SetBaseAndExtent(nsINode& aAnchorNode, uint32_t aAnchorOffset,
3976 : nsINode& aFocusNode, uint32_t aFocusOffset,
3977 : ErrorResult& aRv)
3978 : {
3979 0 : if (!mFrameSelection) {
3980 0 : return;
3981 : }
3982 :
3983 0 : SelectionBatcher batch(this);
3984 :
3985 : int32_t relativePosition =
3986 0 : nsContentUtils::ComparePoints(&aAnchorNode, aAnchorOffset,
3987 0 : &aFocusNode, aFocusOffset);
3988 0 : nsINode* start = &aAnchorNode;
3989 0 : nsINode* end = &aFocusNode;
3990 0 : uint32_t startOffset = aAnchorOffset;
3991 0 : uint32_t endOffset = aFocusOffset;
3992 0 : if (relativePosition > 0) {
3993 0 : start = &aFocusNode;
3994 0 : end = &aAnchorNode;
3995 0 : startOffset = aFocusOffset;
3996 0 : endOffset = aAnchorOffset;
3997 : }
3998 :
3999 0 : RefPtr<nsRange> newRange;
4000 0 : nsresult rv = nsRange::CreateRange(start, startOffset, end, endOffset,
4001 0 : getter_AddRefs(newRange));
4002 : // CreateRange returns IndexSizeError if any offset is out of bounds.
4003 0 : if (NS_FAILED(rv)) {
4004 0 : aRv.Throw(rv);
4005 0 : return;
4006 : }
4007 :
4008 0 : rv = RemoveAllRanges();
4009 0 : if (NS_FAILED(rv)) {
4010 0 : aRv.Throw(rv);
4011 0 : return;
4012 : }
4013 :
4014 0 : rv = AddRange(newRange);
4015 0 : if (NS_FAILED(rv)) {
4016 0 : aRv.Throw(rv);
4017 0 : return;
4018 : }
4019 :
4020 0 : SetDirection(relativePosition > 0 ? eDirPrevious : eDirNext);
4021 : }
4022 :
4023 : /** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction
4024 : * @param aLangRTL is true if the new language is right-to-left or false if the new language is left-to-right
4025 : */
4026 : NS_IMETHODIMP
4027 0 : Selection::SelectionLanguageChange(bool aLangRTL)
4028 : {
4029 0 : if (!mFrameSelection)
4030 0 : return NS_ERROR_NOT_INITIALIZED; // Can't do selection
4031 :
4032 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
4033 :
4034 : // if the direction of the language hasn't changed, nothing to do
4035 0 : nsBidiLevel kbdBidiLevel = aLangRTL ? NSBIDI_RTL : NSBIDI_LTR;
4036 0 : if (kbdBidiLevel == frameSelection->mKbdBidiLevel) {
4037 0 : return NS_OK;
4038 : }
4039 :
4040 0 : frameSelection->mKbdBidiLevel = kbdBidiLevel;
4041 :
4042 : nsresult result;
4043 0 : nsIFrame *focusFrame = 0;
4044 :
4045 0 : result = GetPrimaryFrameForFocusNode(&focusFrame, nullptr, false);
4046 0 : if (NS_FAILED(result)) {
4047 0 : return result;
4048 : }
4049 0 : if (!focusFrame) {
4050 0 : return NS_ERROR_FAILURE;
4051 : }
4052 :
4053 : int32_t frameStart, frameEnd;
4054 0 : focusFrame->GetOffsets(frameStart, frameEnd);
4055 0 : RefPtr<nsPresContext> context = GetPresContext();
4056 : nsBidiLevel levelBefore, levelAfter;
4057 0 : if (!context) {
4058 0 : return NS_ERROR_FAILURE;
4059 : }
4060 :
4061 0 : nsBidiLevel level = focusFrame->GetEmbeddingLevel();
4062 0 : int32_t focusOffset = static_cast<int32_t>(FocusOffset());
4063 0 : if ((focusOffset != frameStart) && (focusOffset != frameEnd))
4064 : // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor
4065 : // is equal to the frame level
4066 0 : levelBefore = levelAfter = level;
4067 : else {
4068 : // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters
4069 : // before and after the cursor
4070 0 : nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode());
4071 : nsPrevNextBidiLevels levels = frameSelection->
4072 0 : GetPrevNextBidiLevels(focusContent, focusOffset, false);
4073 :
4074 0 : levelBefore = levels.mLevelBefore;
4075 0 : levelAfter = levels.mLevelAfter;
4076 : }
4077 :
4078 0 : if (IS_SAME_DIRECTION(levelBefore, levelAfter)) {
4079 : // if cursor is between two characters with the same orientation, changing the keyboard language
4080 : // must toggle the cursor level between the level of the character with the lowest level
4081 : // (if the new language corresponds to the orientation of that character) and this level plus 1
4082 : // (if the new language corresponds to the opposite orientation)
4083 0 : if ((level != levelBefore) && (level != levelAfter))
4084 0 : level = std::min(levelBefore, levelAfter);
4085 0 : if (IS_SAME_DIRECTION(level, kbdBidiLevel))
4086 0 : frameSelection->SetCaretBidiLevel(level);
4087 : else
4088 0 : frameSelection->SetCaretBidiLevel(level + 1);
4089 : }
4090 : else {
4091 : // if cursor is between characters with opposite orientations, changing the keyboard language must change
4092 : // the cursor level to that of the adjacent character with the orientation corresponding to the new language.
4093 0 : if (IS_SAME_DIRECTION(levelBefore, kbdBidiLevel))
4094 0 : frameSelection->SetCaretBidiLevel(levelBefore);
4095 : else
4096 0 : frameSelection->SetCaretBidiLevel(levelAfter);
4097 : }
4098 :
4099 : // The caret might have moved, so invalidate the desired position
4100 : // for future usages of up-arrow or down-arrow
4101 0 : frameSelection->InvalidateDesiredPos();
4102 :
4103 0 : return NS_OK;
4104 : }
4105 :
4106 : NS_IMETHODIMP
4107 0 : Selection::SetColors(const nsAString& aForegroundColor,
4108 : const nsAString& aBackgroundColor,
4109 : const nsAString& aAltForegroundColor,
4110 : const nsAString& aAltBackgroundColor)
4111 : {
4112 0 : ErrorResult result;
4113 : SetColors(aForegroundColor, aBackgroundColor,
4114 0 : aAltForegroundColor, aAltBackgroundColor, result);
4115 0 : return result.StealNSResult();
4116 : }
4117 :
4118 : void
4119 0 : Selection::SetColors(const nsAString& aForegroundColor,
4120 : const nsAString& aBackgroundColor,
4121 : const nsAString& aAltForegroundColor,
4122 : const nsAString& aAltBackgroundColor,
4123 : ErrorResult& aRv)
4124 : {
4125 0 : if (mSelectionType != SelectionType::eFind) {
4126 0 : aRv.Throw(NS_ERROR_FAILURE);
4127 0 : return;
4128 : }
4129 :
4130 0 : mCustomColors.reset(new SelectionCustomColors);
4131 :
4132 0 : NS_NAMED_LITERAL_STRING(currentColorStr, "currentColor");
4133 0 : NS_NAMED_LITERAL_STRING(transparentStr, "transparent");
4134 :
4135 0 : if (!aForegroundColor.Equals(currentColorStr)) {
4136 : nscolor foregroundColor;
4137 0 : nsAttrValue aForegroundColorValue;
4138 0 : aForegroundColorValue.ParseColor(aForegroundColor);
4139 0 : if (!aForegroundColorValue.GetColorValue(foregroundColor)) {
4140 0 : aRv.Throw(NS_ERROR_INVALID_ARG);
4141 0 : return;
4142 : }
4143 0 : mCustomColors->mForegroundColor = Some(foregroundColor);
4144 : } else {
4145 0 : mCustomColors->mForegroundColor = Nothing();
4146 : }
4147 :
4148 0 : if (!aBackgroundColor.Equals(transparentStr)) {
4149 : nscolor backgroundColor;
4150 0 : nsAttrValue aBackgroundColorValue;
4151 0 : aBackgroundColorValue.ParseColor(aBackgroundColor);
4152 0 : if (!aBackgroundColorValue.GetColorValue(backgroundColor)) {
4153 0 : aRv.Throw(NS_ERROR_INVALID_ARG);
4154 0 : return;
4155 : }
4156 0 : mCustomColors->mBackgroundColor = Some(backgroundColor);
4157 : } else {
4158 0 : mCustomColors->mBackgroundColor = Nothing();
4159 : }
4160 :
4161 0 : if (!aAltForegroundColor.Equals(currentColorStr)) {
4162 : nscolor altForegroundColor;
4163 0 : nsAttrValue aAltForegroundColorValue;
4164 0 : aAltForegroundColorValue.ParseColor(aAltForegroundColor);
4165 0 : if (!aAltForegroundColorValue.GetColorValue(altForegroundColor)) {
4166 0 : aRv.Throw(NS_ERROR_INVALID_ARG);
4167 0 : return;
4168 : }
4169 0 : mCustomColors->mAltForegroundColor = Some(altForegroundColor);
4170 : } else {
4171 0 : mCustomColors->mAltForegroundColor = Nothing();
4172 : }
4173 :
4174 0 : if (!aAltBackgroundColor.Equals(transparentStr)) {
4175 : nscolor altBackgroundColor;
4176 0 : nsAttrValue aAltBackgroundColorValue;
4177 0 : aAltBackgroundColorValue.ParseColor(aAltBackgroundColor);
4178 0 : if (!aAltBackgroundColorValue.GetColorValue(altBackgroundColor)) {
4179 0 : aRv.Throw(NS_ERROR_INVALID_ARG);
4180 0 : return;
4181 : }
4182 0 : mCustomColors->mAltBackgroundColor = Some(altBackgroundColor);
4183 : } else {
4184 0 : mCustomColors->mAltBackgroundColor = Nothing();
4185 : }
4186 : }
4187 :
4188 : NS_IMETHODIMP
4189 0 : Selection::ResetColors()
4190 : {
4191 0 : ErrorResult result;
4192 0 : ResetColors(result);
4193 0 : return result.StealNSResult();
4194 : }
4195 :
4196 : void
4197 0 : Selection::ResetColors(ErrorResult& aRv)
4198 : {
4199 0 : mCustomColors = nullptr;
4200 0 : }
4201 :
4202 : NS_IMETHODIMP_(nsDirection)
4203 5 : Selection::GetSelectionDirection() {
4204 5 : return mDirection;
4205 : }
4206 :
4207 : NS_IMETHODIMP_(void)
4208 6 : Selection::SetSelectionDirection(nsDirection aDirection) {
4209 6 : mDirection = aDirection;
4210 6 : }
4211 :
4212 : JSObject*
4213 4 : Selection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
4214 : {
4215 4 : return mozilla::dom::SelectionBinding::Wrap(aCx, this, aGivenProto);
4216 : }
4217 :
4218 : // AutoHideSelectionChanges
4219 6 : AutoHideSelectionChanges::AutoHideSelectionChanges(const nsFrameSelection* aFrame)
4220 : : AutoHideSelectionChanges(
4221 6 : aFrame ? aFrame->GetSelection(SelectionType::eNormal) : nullptr)
4222 6 : {}
|