1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=78: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Mats Palmgren <matspal@gmail.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /*
41 : * Implementation of selection: nsISelection,nsISelectionPrivate and nsFrameSelection
42 : */
43 :
44 : #include "mozilla/Attributes.h"
45 :
46 : #include "nsCOMPtr.h"
47 : #include "nsWeakReference.h"
48 : #include "nsIFactory.h"
49 : #include "nsIEnumerator.h"
50 : #include "nsString.h"
51 : #include "nsReadableUtils.h"
52 : #include "nsFrameSelection.h"
53 : #include "nsISelectionPrivate.h"
54 : #include "nsISelectionListener.h"
55 : #include "nsIComponentManager.h"
56 : #include "nsContentCID.h"
57 : #include "nsIContent.h"
58 : #include "nsIDOMElement.h"
59 : #include "nsIDOMNode.h"
60 : #include "nsRange.h"
61 : #include "nsCOMArray.h"
62 : #include "nsGUIEvent.h"
63 : #include "nsIDOMKeyEvent.h"
64 : #include "nsITableLayout.h"
65 : #include "nsITableCellLayout.h"
66 : #include "nsIDOMNodeList.h"
67 : #include "nsTArray.h"
68 : #include "nsIScrollableFrame.h"
69 : #include "nsCCUncollectableMarker.h"
70 : #include "nsIContentIterator.h"
71 : #include "nsIDocumentEncoder.h"
72 :
73 : // for IBMBIDI
74 : #include "nsFrameTraversal.h"
75 : #include "nsILineIterator.h"
76 : #include "nsGkAtoms.h"
77 : #include "nsIFrameTraversal.h"
78 : #include "nsLayoutUtils.h"
79 : #include "nsLayoutCID.h"
80 : #include "nsBidiPresUtils.h"
81 : static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
82 : #include "nsTextFrame.h"
83 :
84 : #include "nsIDOMText.h"
85 :
86 : #include "nsContentUtils.h"
87 : #include "nsThreadUtils.h"
88 : #include "mozilla/Preferences.h"
89 :
90 : //included for desired x position;
91 : #include "nsPresContext.h"
92 : #include "nsIPresShell.h"
93 : #include "nsCaret.h"
94 :
95 :
96 : #include "nsITimer.h"
97 : #include "nsIServiceManager.h"
98 : #include "nsFrameManager.h"
99 : // notifications
100 : #include "nsIDOMDocument.h"
101 : #include "nsIDocument.h"
102 :
103 : #include "nsISelectionController.h"//for the enums
104 : #include "nsAutoCopyListener.h"
105 : #include "nsCopySupport.h"
106 : #include "nsIClipboard.h"
107 :
108 : #ifdef IBMBIDI
109 : #include "nsIBidiKeyboard.h"
110 : #endif // IBMBIDI
111 :
112 : #include "nsDOMError.h"
113 : #include "mozilla/dom/Element.h"
114 :
115 : using namespace mozilla;
116 :
117 : //#define DEBUG_TABLE 1
118 :
119 : static NS_DEFINE_IID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
120 : static NS_DEFINE_IID(kCSubtreeIteratorCID, NS_SUBTREEITERATOR_CID);
121 :
122 : //PROTOTYPES
123 : class nsSelectionIterator;
124 : class nsFrameSelection;
125 : class nsAutoScrollTimer;
126 :
127 : static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode);
128 :
129 : static nsIAtom *GetTag(nsINode *aNode);
130 : // returns the parent
131 : static nsINode* ParentOffset(nsINode *aNode, PRInt32 *aChildOffset);
132 : static nsINode* GetCellParent(nsINode *aDomNode);
133 :
134 : #ifdef PRINT_RANGE
135 : static void printRange(nsRange *aDomRange);
136 : #define DEBUG_OUT_RANGE(x) printRange(x)
137 : #else
138 : #define DEBUG_OUT_RANGE(x)
139 : #endif //MOZ_DEBUG
140 :
141 :
142 :
143 : //#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
144 : //#define DEBUG_NAVIGATION
145 :
146 :
147 : //#define DEBUG_TABLE_SELECTION 1
148 :
149 : struct CachedOffsetForFrame {
150 0 : CachedOffsetForFrame()
151 : : mCachedFrameOffset(0, 0) // nsPoint ctor
152 : , mLastCaretFrame(nsnull)
153 : , mLastContentOffset(0)
154 0 : , mCanCacheFrameOffset(false)
155 0 : {}
156 :
157 : nsPoint mCachedFrameOffset; // cached frame offset
158 : nsIFrame* mLastCaretFrame; // store the frame the caret was last drawn in.
159 : PRInt32 mLastContentOffset; // store last content offset
160 : bool mCanCacheFrameOffset; // cached frame offset is valid?
161 : };
162 :
163 : struct RangeData
164 1487 : {
165 1464 : RangeData(nsRange* aRange)
166 1464 : : mRange(aRange)
167 1464 : {}
168 :
169 : nsRefPtr<nsRange> mRange;
170 : nsTextRangeStyle mTextRangeStyle;
171 : };
172 :
173 1464 : static RangeData sEmptyData(nsnull);
174 :
175 : // Note, the ownership of nsTypedSelection depends on which way the object is
176 : // created. When nsFrameSelection has created nsTypedSelection,
177 : // addreffing/releasing nsTypedSelection object is aggregated to
178 : // nsFrameSelection. Otherwise normal addref/release is used.
179 : // This ensures that nsFrameSelection is never deleted before its
180 : // nsTypedSelections.
181 :
182 : class nsTypedSelection : public nsISelectionPrivate,
183 : public nsSupportsWeakReference
184 : {
185 : public:
186 : nsTypedSelection();
187 : nsTypedSelection(nsFrameSelection *aList);
188 : virtual ~nsTypedSelection();
189 :
190 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
191 1464 : NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTypedSelection, nsISelectionPrivate)
192 : NS_DECL_NSISELECTION
193 : NS_DECL_NSISELECTIONPRIVATE
194 :
195 : // utility methods for scrolling the selection into view
196 : nsresult GetPresContext(nsPresContext **aPresContext);
197 : nsresult GetPresShell(nsIPresShell **aPresShell);
198 : // Returns a rect containing the selection region, and frame that that
199 : // position is relative to. For SELECTION_ANCHOR_REGION or
200 : // SELECTION_FOCUS_REGION the rect is a zero-width rectangle. For
201 : // SELECTION_WHOLE_SELECTION the rect contains both the anchor and focus
202 : // region rects.
203 : nsIFrame* GetSelectionAnchorGeometry(SelectionRegion aRegion, nsRect *aRect);
204 : // Returns the position of the region (SELECTION_ANCHOR_REGION or
205 : // SELECTION_FOCUS_REGION only), and frame that that position is relative to.
206 : // The 'position' is a zero-width rectangle.
207 : nsIFrame* GetSelectionEndPointGeometry(SelectionRegion aRegion, nsRect *aRect);
208 :
209 : nsresult PostScrollSelectionIntoViewEvent(SelectionRegion aRegion,
210 : bool aFirstAncestorOnly,
211 : PRInt16 aVPercent,
212 : PRInt16 aHPercent);
213 : enum {
214 : SCROLL_SYNCHRONOUS = 1<<1,
215 : SCROLL_FIRST_ANCESTOR_ONLY = 1<<2,
216 : SCROLL_DO_FLUSH = 1<<3
217 : };
218 : // aDoFlush only matters if aIsSynchronous is true. If not, we'll just flush
219 : // when the scroll event fires so we make sure to scroll to the right place.
220 : nsresult ScrollIntoView(SelectionRegion aRegion,
221 : PRInt16 aVPercent = NS_PRESSHELL_SCROLL_ANYWHERE,
222 : PRInt16 aHPercent = NS_PRESSHELL_SCROLL_ANYWHERE,
223 : PRInt32 aFlags = 0);
224 : nsresult SubtractRange(RangeData* aRange, nsRange* aSubtract,
225 : nsTArray<RangeData>* aOutput);
226 : nsresult AddItem(nsRange *aRange, PRInt32* aOutIndex = nsnull);
227 : nsresult RemoveItem(nsRange *aRange);
228 : nsresult RemoveCollapsedRanges();
229 : nsresult Clear(nsPresContext* aPresContext);
230 : nsresult Collapse(nsINode* aParentNode, PRInt32 aOffset);
231 : nsresult Extend(nsINode* aParentNode, PRInt32 aOffset);
232 : nsRange* GetRangeAt(PRInt32 aIndex);
233 :
234 : // methods for convenience. Note, these don't addref
235 : nsINode* GetAnchorNode();
236 : PRInt32 GetAnchorOffset();
237 :
238 : nsINode* GetFocusNode();
239 : PRInt32 GetFocusOffset();
240 :
241 : // Get the anchor-to-focus range if we don't care which end is
242 : // anchor and which end is focus.
243 0 : const nsRange* GetAnchorFocusRange() const {
244 0 : return mAnchorFocusRange;
245 : }
246 :
247 0 : nsDirection GetDirection(){return mDirection;}
248 0 : void SetDirection(nsDirection aDir){mDirection = aDir;}
249 : nsresult CopyRangeToAnchorFocus(nsRange *aRange);
250 : void ReplaceAnchorFocusRange(nsRange *aRange);
251 :
252 : // NS_IMETHOD GetPrimaryFrameForRangeEndpoint(nsIDOMNode *aNode, PRInt32 aOffset, bool aIsEndNode, nsIFrame **aResultFrame);
253 : NS_IMETHOD GetPrimaryFrameForAnchorNode(nsIFrame **aResultFrame);
254 : NS_IMETHOD GetPrimaryFrameForFocusNode(nsIFrame **aResultFrame, PRInt32 *aOffset, bool aVisual);
255 : NS_IMETHOD LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset, PRInt32 aContentLength,
256 : SelectionDetails **aReturnDetails, SelectionType aType, bool aSlowCheck);
257 : NS_IMETHOD Repaint(nsPresContext* aPresContext);
258 :
259 : // Note: StartAutoScrollTimer might destroy arbitrary frames etc.
260 : nsresult StartAutoScrollTimer(nsIFrame *aFrame,
261 : nsPoint& aPoint,
262 : PRUint32 aDelay);
263 :
264 : nsresult StopAutoScrollTimer();
265 :
266 : private:
267 : friend class nsAutoScrollTimer;
268 :
269 : // Note: DoAutoScroll might destroy arbitrary frames etc.
270 : nsresult DoAutoScroll(nsIFrame *aFrame, nsPoint& aPoint);
271 :
272 : public:
273 0 : SelectionType GetType(){return mType;}
274 0 : void SetType(SelectionType aType){mType = aType;}
275 :
276 : nsresult NotifySelectionListeners();
277 :
278 : private:
279 : friend class nsSelectionIterator;
280 :
281 : class ScrollSelectionIntoViewEvent;
282 : friend class ScrollSelectionIntoViewEvent;
283 :
284 0 : class ScrollSelectionIntoViewEvent : public nsRunnable {
285 : public:
286 : NS_DECL_NSIRUNNABLE
287 0 : ScrollSelectionIntoViewEvent(nsTypedSelection *aTypedSelection,
288 : SelectionRegion aRegion,
289 : PRInt16 aVScroll,
290 : PRInt16 aHScroll,
291 : bool aFirstAncestorOnly)
292 : : mTypedSelection(aTypedSelection),
293 : mRegion(aRegion),
294 : mVerticalScroll(aVScroll),
295 : mHorizontalScroll(aHScroll),
296 0 : mFirstAncestorOnly(aFirstAncestorOnly) {
297 0 : NS_ASSERTION(aTypedSelection, "null parameter");
298 0 : }
299 0 : void Revoke() { mTypedSelection = nsnull; }
300 : private:
301 : nsTypedSelection *mTypedSelection;
302 : SelectionRegion mRegion;
303 : PRInt16 mVerticalScroll;
304 : PRInt16 mHorizontalScroll;
305 : bool mFirstAncestorOnly;
306 : };
307 :
308 : void setAnchorFocusRange(PRInt32 aIndex); // pass in index into mRanges;
309 : // negative value clears
310 : // mAnchorFocusRange
311 : nsresult SelectAllFramesForContent(nsIContentIterator *aInnerIter,
312 : nsIContent *aContent,
313 : bool aSelected);
314 : nsresult selectFrames(nsPresContext* aPresContext, nsRange *aRange, bool aSelect);
315 : nsresult getTableCellLocationFromRange(nsRange *aRange, PRInt32 *aSelectionType, PRInt32 *aRow, PRInt32 *aCol);
316 : nsresult addTableCellRange(nsRange *aRange, bool *aDidAddRange, PRInt32 *aOutIndex);
317 :
318 : nsresult FindInsertionPoint(
319 : nsTArray<RangeData>* aElementArray,
320 : nsINode* aPointNode, PRInt32 aPointOffset,
321 : nsresult (*aComparator)(nsINode*,PRInt32,nsRange*,PRInt32*),
322 : PRInt32* aPoint);
323 : bool EqualsRangeAtPoint(nsINode* aBeginNode, PRInt32 aBeginOffset,
324 : nsINode* aEndNode, PRInt32 aEndOffset,
325 : PRInt32 aRangeIndex);
326 : void GetIndicesForInterval(nsINode* aBeginNode, PRInt32 aBeginOffset,
327 : nsINode* aEndNode, PRInt32 aEndOffset,
328 : bool aAllowAdjacent,
329 : PRInt32 *aStartIndex, PRInt32 *aEndIndex);
330 : RangeData* FindRangeData(nsIDOMRange* aRange);
331 :
332 : // These are the ranges inside this selection. They are kept sorted in order
333 : // of DOM start position.
334 : //
335 : // This data structure is sorted by the range beginnings. As the ranges are
336 : // disjoint, it is also implicitly sorted by the range endings. This allows
337 : // us to perform binary searches when searching for existence of a range,
338 : // giving us O(log n) search time.
339 : //
340 : // Inserting a new range requires finding the overlapping interval, requiring
341 : // two binary searches plus up to an additional 6 DOM comparisons. If this
342 : // proves to be a performance concern, then an interval tree may be a
343 : // possible solution, allowing the calculation of the overlap interval in
344 : // O(log n) time, though this would require rebalancing and other overhead.
345 : nsTArray<RangeData> mRanges;
346 :
347 : nsRefPtr<nsRange> mAnchorFocusRange;
348 : nsRefPtr<nsFrameSelection> mFrameSelection;
349 : nsWeakPtr mPresShellWeak;
350 : nsRefPtr<nsAutoScrollTimer> mAutoScrollTimer;
351 : nsCOMArray<nsISelectionListener> mSelectionListeners;
352 : nsRevocableEventPtr<ScrollSelectionIntoViewEvent> mScrollEvent;
353 : CachedOffsetForFrame *mCachedOffsetForFrame;
354 : nsDirection mDirection;
355 : SelectionType mType;
356 : };
357 :
358 : // Stack-class to turn on/off selection batching for table selection
359 : class NS_STACK_CLASS nsSelectionBatcher MOZ_FINAL
360 : {
361 : private:
362 : nsCOMPtr<nsISelectionPrivate> mSelection;
363 : public:
364 0 : nsSelectionBatcher(nsISelectionPrivate *aSelection) : mSelection(aSelection)
365 : {
366 0 : if (mSelection) mSelection->StartBatchChanges();
367 0 : }
368 0 : ~nsSelectionBatcher()
369 0 : {
370 0 : if (mSelection) mSelection->EndBatchChanges();
371 0 : }
372 : };
373 :
374 : class nsSelectionIterator : public nsIBidirectionalEnumerator
375 : {
376 : public:
377 : /*BEGIN nsIEnumerator interfaces
378 : see the nsIEnumerator for more details*/
379 :
380 : NS_DECL_ISUPPORTS
381 :
382 : NS_DECL_NSIENUMERATOR
383 :
384 : NS_DECL_NSIBIDIRECTIONALENUMERATOR
385 :
386 : /*END nsIEnumerator interfaces*/
387 : /*BEGIN Helper Methods*/
388 : nsRange* CurrentItem();
389 : /*END Helper Methods*/
390 : private:
391 : friend class nsTypedSelection;
392 :
393 : //lame lame lame if delete from document goes away then get rid of this unless its debug
394 : friend class nsFrameSelection;
395 :
396 : nsSelectionIterator(nsTypedSelection *);
397 : virtual ~nsSelectionIterator();
398 : PRInt32 mIndex;
399 : nsTypedSelection *mDomSelection;
400 : SelectionType mType;
401 : };
402 :
403 : class nsAutoScrollTimer : public nsITimerCallback
404 : {
405 : public:
406 :
407 : NS_DECL_ISUPPORTS
408 :
409 0 : nsAutoScrollTimer()
410 0 : : mFrameSelection(0), mSelection(0), mPresContext(0), mPoint(0,0), mDelay(30)
411 : {
412 0 : }
413 :
414 0 : virtual ~nsAutoScrollTimer()
415 0 : {
416 0 : if (mTimer)
417 0 : mTimer->Cancel();
418 0 : }
419 :
420 : // aPoint is relative to aPresContext's root frame
421 0 : nsresult Start(nsPresContext *aPresContext, nsPoint &aPoint)
422 : {
423 0 : mPoint = aPoint;
424 :
425 : // Store the presentation context. The timer will be
426 : // stopped by the selection if the prescontext is destroyed.
427 0 : mPresContext = aPresContext;
428 :
429 0 : mContent = nsIPresShell::GetCapturingContent();
430 :
431 0 : if (!mTimer)
432 : {
433 : nsresult result;
434 0 : mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
435 :
436 0 : if (NS_FAILED(result))
437 0 : return result;
438 : }
439 :
440 0 : return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
441 : }
442 :
443 0 : nsresult Stop()
444 : {
445 0 : if (mTimer)
446 : {
447 0 : mTimer->Cancel();
448 0 : mTimer = 0;
449 : }
450 :
451 0 : mContent = nsnull;
452 0 : return NS_OK;
453 : }
454 :
455 0 : nsresult Init(nsFrameSelection *aFrameSelection, nsTypedSelection *aSelection)
456 : {
457 0 : mFrameSelection = aFrameSelection;
458 0 : mSelection = aSelection;
459 0 : return NS_OK;
460 : }
461 :
462 0 : nsresult SetDelay(PRUint32 aDelay)
463 : {
464 0 : mDelay = aDelay;
465 0 : return NS_OK;
466 : }
467 :
468 0 : NS_IMETHOD Notify(nsITimer *timer)
469 : {
470 0 : if (mSelection && mPresContext)
471 : {
472 : nsWeakFrame frame =
473 0 : mContent ? mPresContext->GetPrimaryFrameFor(mContent) : nsnull;
474 0 : if (!frame)
475 0 : return NS_OK;
476 0 : mContent = nsnull;
477 :
478 : nsPoint pt = mPoint -
479 0 : frame->GetOffsetTo(mPresContext->PresShell()->FrameManager()->GetRootFrame());
480 0 : mFrameSelection->HandleDrag(frame, pt);
481 0 : if (!frame.IsAlive())
482 0 : return NS_OK;
483 :
484 0 : NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
485 0 : mSelection->DoAutoScroll(frame, pt);
486 : }
487 0 : return NS_OK;
488 : }
489 : private:
490 : nsFrameSelection *mFrameSelection;
491 : nsTypedSelection *mSelection;
492 : nsPresContext *mPresContext;
493 : // relative to mPresContext's root frame
494 : nsPoint mPoint;
495 : nsCOMPtr<nsITimer> mTimer;
496 : nsCOMPtr<nsIContent> mContent;
497 : PRUint32 mDelay;
498 : };
499 :
500 0 : NS_IMPL_ISUPPORTS1(nsAutoScrollTimer, nsITimerCallback)
501 :
502 0 : nsresult NS_NewDomSelection(nsISelection **aDomSelection)
503 : {
504 0 : nsTypedSelection *rlist = new nsTypedSelection;
505 0 : *aDomSelection = (nsISelection *)rlist;
506 0 : NS_ADDREF(rlist);
507 0 : return NS_OK;
508 : }
509 :
510 : static PRInt8
511 0 : GetIndexFromSelectionType(SelectionType aType)
512 : {
513 0 : switch (aType)
514 : {
515 0 : case nsISelectionController::SELECTION_NORMAL: return 0; break;
516 0 : case nsISelectionController::SELECTION_SPELLCHECK: return 1; break;
517 0 : case nsISelectionController::SELECTION_IME_RAWINPUT: return 2; break;
518 0 : case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT: return 3; break;
519 0 : case nsISelectionController::SELECTION_IME_CONVERTEDTEXT: return 4; break;
520 0 : case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT: return 5; break;
521 0 : case nsISelectionController::SELECTION_ACCESSIBILITY: return 6; break;
522 0 : case nsISelectionController::SELECTION_FIND: return 7; break;
523 0 : case nsISelectionController::SELECTION_URLSECONDARY: return 8; break;
524 : default:
525 0 : return -1; break;
526 : }
527 : /* NOTREACHED */
528 : return 0;
529 : }
530 :
531 : static SelectionType
532 0 : GetSelectionTypeFromIndex(PRInt8 aIndex)
533 : {
534 0 : switch (aIndex)
535 : {
536 0 : case 0: return nsISelectionController::SELECTION_NORMAL; break;
537 0 : case 1: return nsISelectionController::SELECTION_SPELLCHECK; break;
538 0 : case 2: return nsISelectionController::SELECTION_IME_RAWINPUT; break;
539 0 : case 3: return nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT; break;
540 0 : case 4: return nsISelectionController::SELECTION_IME_CONVERTEDTEXT; break;
541 0 : case 5: return nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT; break;
542 0 : case 6: return nsISelectionController::SELECTION_ACCESSIBILITY; break;
543 0 : case 7: return nsISelectionController::SELECTION_FIND; break;
544 0 : case 8: return nsISelectionController::SELECTION_URLSECONDARY; break;
545 : default:
546 0 : return nsISelectionController::SELECTION_NORMAL; break;
547 : }
548 : /* NOTREACHED */
549 : return 0;
550 : }
551 :
552 : /*
553 : The limiter is used specifically for the text areas and textfields
554 : In that case it is the DIV tag that is anonymously created for the text
555 : areas/fields. Text nodes and BR nodes fall beneath it. In the case of a
556 : BR node the limiter will be the parent and the offset will point before or
557 : after the BR node. In the case of the text node the parent content is
558 : the text node itself and the offset will be the exact character position.
559 : The offset is not important to check for validity. Simply look at the
560 : passed in content. If it equals the limiter then the selection point is valid.
561 : If its parent it the limiter then the point is also valid. In the case of
562 : NO limiter all points are valid since you are in a topmost iframe. (browser
563 : or composer)
564 : */
565 : bool
566 0 : IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode)
567 : {
568 0 : if (!aFrameSel || !aNode)
569 0 : return false;
570 :
571 0 : nsIContent *limiter = aFrameSel->GetLimiter();
572 0 : if (limiter && limiter != aNode && limiter != aNode->GetParent()) {
573 : //if newfocus == the limiter. that's ok. but if not there and not parent bad
574 0 : return false; //not in the right content. tLimiter said so
575 : }
576 :
577 0 : limiter = aFrameSel->GetAncestorLimiter();
578 0 : return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter);
579 : }
580 :
581 :
582 0 : NS_IMPL_ADDREF(nsSelectionIterator)
583 0 : NS_IMPL_RELEASE(nsSelectionIterator)
584 :
585 0 : NS_INTERFACE_MAP_BEGIN(nsSelectionIterator)
586 0 : NS_INTERFACE_MAP_ENTRY(nsIEnumerator)
587 0 : NS_INTERFACE_MAP_ENTRY(nsIBidirectionalEnumerator)
588 0 : NS_INTERFACE_MAP_END_AGGREGATED(mDomSelection)
589 :
590 :
591 : ///////////BEGIN nsSelectionIterator methods
592 :
593 0 : nsSelectionIterator::nsSelectionIterator(nsTypedSelection *aList)
594 0 : :mIndex(0)
595 : {
596 0 : if (!aList)
597 : {
598 0 : NS_NOTREACHED("nsFrameSelection");
599 0 : return;
600 : }
601 0 : mDomSelection = aList;
602 : }
603 :
604 :
605 :
606 0 : nsSelectionIterator::~nsSelectionIterator()
607 : {
608 0 : }
609 :
610 :
611 :
612 : ////////////END nsSelectionIterator methods
613 :
614 : ////////////BEGIN nsSelectionIterator methods
615 :
616 :
617 :
618 : NS_IMETHODIMP
619 0 : nsSelectionIterator::Next()
620 : {
621 0 : mIndex++;
622 0 : PRInt32 cnt = mDomSelection->mRanges.Length();
623 0 : if (mIndex < cnt)
624 0 : return NS_OK;
625 0 : return NS_ERROR_FAILURE;
626 : }
627 :
628 :
629 :
630 : NS_IMETHODIMP
631 0 : nsSelectionIterator::Prev()
632 : {
633 0 : mIndex--;
634 0 : if (mIndex >= 0 )
635 0 : return NS_OK;
636 0 : return NS_ERROR_FAILURE;
637 : }
638 :
639 :
640 :
641 : NS_IMETHODIMP
642 0 : nsSelectionIterator::First()
643 : {
644 0 : if (!mDomSelection)
645 0 : return NS_ERROR_NULL_POINTER;
646 0 : mIndex = 0;
647 0 : return NS_OK;
648 : }
649 :
650 :
651 :
652 : NS_IMETHODIMP
653 0 : nsSelectionIterator::Last()
654 : {
655 0 : if (!mDomSelection)
656 0 : return NS_ERROR_NULL_POINTER;
657 0 : mIndex = mDomSelection->mRanges.Length() - 1;
658 0 : return NS_OK;
659 : }
660 :
661 :
662 :
663 : NS_IMETHODIMP
664 0 : nsSelectionIterator::CurrentItem(nsISupports **aItem)
665 : {
666 0 : *aItem = static_cast<nsIDOMRange*>(CurrentItem());
667 0 : if (!*aItem) {
668 0 : return NS_ERROR_FAILURE;
669 : }
670 :
671 0 : NS_ADDREF(*aItem);
672 0 : return NS_OK;
673 : }
674 :
675 : nsRange*
676 0 : nsSelectionIterator::CurrentItem()
677 : {
678 0 : return mDomSelection->mRanges.SafeElementAt(mIndex, sEmptyData).mRange;
679 : }
680 :
681 :
682 :
683 : NS_IMETHODIMP
684 0 : nsSelectionIterator::IsDone()
685 : {
686 0 : PRInt32 cnt = mDomSelection->mRanges.Length();
687 0 : if (mIndex >= 0 && mIndex < cnt) {
688 0 : return NS_ENUMERATOR_FALSE;
689 : }
690 0 : return NS_OK;
691 : }
692 :
693 :
694 : ////////////END nsSelectionIterator methods
695 :
696 :
697 : ////////////BEGIN nsFrameSelection methods
698 :
699 0 : nsFrameSelection::nsFrameSelection()
700 0 : : mDelayedMouseEvent(false, 0, nsnull, nsMouseEvent::eReal)
701 : {
702 : PRInt32 i;
703 0 : for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
704 0 : mDomSelections[i] = new nsTypedSelection(this);
705 0 : mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
706 : }
707 0 : mBatching = 0;
708 0 : mChangesDuringBatching = false;
709 0 : mNotifyFrames = true;
710 0 : mLimiter = nsnull; //no default limiter.
711 0 : mAncestorLimiter = nsnull;
712 :
713 0 : mMouseDoubleDownState = false;
714 :
715 0 : mHint = HINTLEFT;
716 : #ifdef IBMBIDI
717 0 : mCaretBidiLevel = BIDI_LEVEL_UNDEFINED;
718 : #endif
719 0 : mDragSelectingCells = false;
720 0 : mSelectingTableCellMode = 0;
721 0 : mSelectedCellIndex = 0;
722 :
723 : // Check to see if the autocopy pref is enabled
724 : // and add the autocopy listener if it is
725 0 : if (Preferences::GetBool("clipboard.autocopy")) {
726 0 : nsAutoCopyListener *autoCopy = nsAutoCopyListener::GetInstance();
727 :
728 0 : if (autoCopy) {
729 : PRInt8 index =
730 0 : GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
731 0 : if (mDomSelections[index]) {
732 0 : autoCopy->Listen(mDomSelections[index]);
733 : }
734 : }
735 : }
736 :
737 0 : mDisplaySelection = nsISelectionController::SELECTION_OFF;
738 :
739 0 : mDelayedMouseEventValid = false;
740 0 : mSelectionChangeReason = nsISelectionListener::NO_REASON;
741 0 : }
742 :
743 :
744 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameSelection)
745 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameSelection)
746 : PRInt32 i;
747 0 : for (i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; ++i) {
748 0 : tmp->mDomSelections[i] = nsnull;
749 : }
750 :
751 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCellParent)
752 0 : tmp->mSelectingTableCellMode = 0;
753 0 : tmp->mDragSelectingCells = false;
754 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStartSelectedCell)
755 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEndSelectedCell)
756 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mAppendStartSelectedCell)
757 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mUnselectCellOnMouseUp)
758 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mMaintainRange)
759 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
760 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameSelection)
761 0 : if (tmp->mShell && tmp->mShell->GetDocument() &&
762 : nsCCUncollectableMarker::InGeneration(cb,
763 : tmp->mShell->GetDocument()->
764 0 : GetMarkedCCGeneration())) {
765 0 : return NS_SUCCESS_INTERRUPTED_TRAVERSE;
766 : }
767 : PRInt32 i;
768 0 : for (i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; ++i) {
769 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mDomSelections[i],
770 : nsISelection)
771 : }
772 :
773 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCellParent)
774 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStartSelectedCell)
775 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEndSelectedCell)
776 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mAppendStartSelectedCell)
777 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mUnselectCellOnMouseUp)
778 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mMaintainRange, nsIDOMRange)
779 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
780 :
781 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameSelection)
782 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameSelection)
783 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameSelection)
784 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
785 0 : NS_INTERFACE_MAP_END
786 :
787 :
788 : nsresult
789 0 : nsFrameSelection::FetchDesiredX(nscoord &aDesiredX) //the x position requested by the Key Handling for up down
790 : {
791 0 : if (!mShell)
792 : {
793 0 : NS_ERROR("fetch desired X failed");
794 0 : return NS_ERROR_FAILURE;
795 : }
796 0 : if (mDesiredXSet)
797 : {
798 0 : aDesiredX = mDesiredX;
799 0 : return NS_OK;
800 : }
801 :
802 0 : nsRefPtr<nsCaret> caret = mShell->GetCaret();
803 0 : if (!caret)
804 0 : return NS_ERROR_NULL_POINTER;
805 :
806 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
807 0 : nsresult result = caret->SetCaretDOMSelection(mDomSelections[index]);
808 0 : if (NS_FAILED(result))
809 0 : return result;
810 :
811 0 : nsRect coord;
812 0 : nsIFrame* caretFrame = caret->GetGeometry(mDomSelections[index], &coord);
813 0 : if (!caretFrame)
814 0 : return NS_ERROR_FAILURE;
815 0 : nsPoint viewOffset(0, 0);
816 0 : nsIView* view = nsnull;
817 0 : caretFrame->GetOffsetFromView(viewOffset, &view);
818 0 : if (view)
819 0 : coord.x += viewOffset.x;
820 :
821 0 : aDesiredX = coord.x;
822 0 : return NS_OK;
823 : }
824 :
825 :
826 :
827 : void
828 0 : nsFrameSelection::InvalidateDesiredX() //do not listen to mDesiredX you must get another.
829 : {
830 0 : mDesiredXSet = false;
831 0 : }
832 :
833 :
834 :
835 : void
836 0 : nsFrameSelection::SetDesiredX(nscoord aX) //set the mDesiredX
837 : {
838 0 : mDesiredX = aX;
839 0 : mDesiredXSet = true;
840 0 : }
841 :
842 : nsresult
843 0 : nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(nsIFrame *aFrame,
844 : nsPoint& aPoint,
845 : nsIFrame **aRetFrame,
846 : nsPoint& aRetPoint)
847 : {
848 : //
849 : // The whole point of this method is to return a frame and point that
850 : // that lie within the same valid subtree as the anchor node's frame,
851 : // for use with the method GetContentAndOffsetsFromPoint().
852 : //
853 : // A valid subtree is defined to be one where all the content nodes in
854 : // the tree have a valid parent-child relationship.
855 : //
856 : // If the anchor frame and aFrame are in the same subtree, aFrame will
857 : // be returned in aRetFrame. If they are in different subtrees, we
858 : // return the frame for the root of the subtree.
859 : //
860 :
861 0 : if (!aFrame || !aRetFrame)
862 0 : return NS_ERROR_NULL_POINTER;
863 :
864 0 : *aRetFrame = aFrame;
865 0 : aRetPoint = aPoint;
866 :
867 : //
868 : // Get the frame and content for the selection's anchor point!
869 : //
870 :
871 : nsresult result;
872 0 : nsCOMPtr<nsIDOMNode> anchorNode;
873 0 : PRInt32 anchorOffset = 0;
874 :
875 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
876 0 : if (!mDomSelections[index])
877 0 : return NS_ERROR_NULL_POINTER;
878 :
879 0 : result = mDomSelections[index]->GetAnchorNode(getter_AddRefs(anchorNode));
880 :
881 0 : if (NS_FAILED(result))
882 0 : return result;
883 :
884 0 : if (!anchorNode)
885 0 : return NS_OK;
886 :
887 0 : result = mDomSelections[index]->GetAnchorOffset(&anchorOffset);
888 :
889 0 : if (NS_FAILED(result))
890 0 : return result;
891 :
892 0 : nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode);
893 :
894 0 : if (!anchorContent)
895 0 : return NS_ERROR_FAILURE;
896 :
897 : //
898 : // Now find the root of the subtree containing the anchor's content.
899 : //
900 :
901 0 : NS_ENSURE_STATE(mShell);
902 0 : nsIContent* anchorRoot = anchorContent->GetSelectionRootContent(mShell);
903 0 : NS_ENSURE_TRUE(anchorRoot, NS_ERROR_UNEXPECTED);
904 :
905 : //
906 : // Now find the root of the subtree containing aFrame's content.
907 : //
908 :
909 0 : nsIContent* content = aFrame->GetContent();
910 :
911 0 : if (content)
912 : {
913 0 : nsIContent* contentRoot = content->GetSelectionRootContent(mShell);
914 0 : NS_ENSURE_TRUE(contentRoot, NS_ERROR_UNEXPECTED);
915 :
916 0 : if (anchorRoot == contentRoot)
917 : {
918 : // If the aFrame's content isn't the capturing content, it should be
919 : // a descendant. At this time, we can return simply.
920 0 : nsIContent* capturedContent = nsIPresShell::GetCapturingContent();
921 0 : if (capturedContent != content)
922 : {
923 0 : return NS_OK;
924 : }
925 :
926 : // Find the frame under the mouse cursor with the root frame.
927 : // At this time, don't use the anchor's frame because it may not have
928 : // fixed positioned frames.
929 0 : nsIFrame* rootFrame = mShell->FrameManager()->GetRootFrame();
930 0 : nsPoint ptInRoot = aPoint + aFrame->GetOffsetTo(rootFrame);
931 : nsIFrame* cursorFrame =
932 0 : nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
933 :
934 : // If the mouse cursor in on a frame which is descendant of same
935 : // selection root, we can expand the selection to the frame.
936 0 : if (cursorFrame && cursorFrame->PresContext()->PresShell() == mShell)
937 : {
938 0 : nsIContent* cursorContent = cursorFrame->GetContent();
939 0 : NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE);
940 : nsIContent* cursorContentRoot =
941 0 : cursorContent->GetSelectionRootContent(mShell);
942 0 : NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED);
943 0 : if (cursorContentRoot == anchorRoot)
944 : {
945 0 : *aRetFrame = cursorFrame;
946 0 : aRetPoint = aPoint + aFrame->GetOffsetTo(cursorFrame);
947 0 : return NS_OK;
948 : }
949 : }
950 : // Otherwise, e.g., the cursor isn't on any frames (e.g., the mouse
951 : // cursor is out of the window), we should use the frame of the anchor
952 : // root.
953 : }
954 : }
955 :
956 : //
957 : // When we can't find a frame which is under the mouse cursor and has a same
958 : // selection root as the anchor node's, we should return the selection root
959 : // frame.
960 : //
961 :
962 0 : *aRetFrame = anchorRoot->GetPrimaryFrame();
963 :
964 0 : if (!*aRetFrame)
965 0 : return NS_ERROR_FAILURE;
966 :
967 : //
968 : // Now make sure that aRetPoint is converted to the same coordinate
969 : // system used by aRetFrame.
970 : //
971 :
972 0 : aRetPoint = aPoint + aFrame->GetOffsetTo(*aRetFrame);
973 :
974 0 : return NS_OK;
975 : }
976 :
977 : #ifdef IBMBIDI
978 : void
979 0 : nsFrameSelection::SetCaretBidiLevel(PRUint8 aLevel)
980 : {
981 : // If the current level is undefined, we have just inserted new text.
982 : // In this case, we don't want to reset the keyboard language
983 0 : bool afterInsert = !!(mCaretBidiLevel & BIDI_LEVEL_UNDEFINED);
984 0 : mCaretBidiLevel = aLevel;
985 :
986 0 : nsIBidiKeyboard* bidiKeyboard = nsContentUtils::GetBidiKeyboard();
987 0 : if (bidiKeyboard && !afterInsert)
988 0 : bidiKeyboard->SetLangFromBidiLevel(aLevel);
989 : return;
990 : }
991 :
992 : PRUint8
993 0 : nsFrameSelection::GetCaretBidiLevel() const
994 : {
995 0 : return mCaretBidiLevel;
996 : }
997 :
998 : void
999 0 : nsFrameSelection::UndefineCaretBidiLevel()
1000 : {
1001 0 : mCaretBidiLevel |= BIDI_LEVEL_UNDEFINED;
1002 0 : }
1003 : #endif
1004 :
1005 :
1006 : #ifdef PRINT_RANGE
1007 : void printRange(nsRange *aDomRange)
1008 : {
1009 : if (!aDomRange)
1010 : {
1011 : printf("NULL nsIDOMRange\n");
1012 : }
1013 : nsINode* startNode = aDomRange->GetStartParent();
1014 : nsINode* endNode = aDomRange->GetEndParent();
1015 : PRInt32 startOffset = aDomRange->StartOffset();
1016 : PRInt32 endOffset = aDomRange->EndOffset();
1017 :
1018 : printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
1019 : (unsigned long)aDomRange,
1020 : (unsigned long)startNode, (long)startOffset,
1021 : (unsigned long)endNode, (long)endOffset);
1022 :
1023 : }
1024 : #endif /* PRINT_RANGE */
1025 :
1026 : static
1027 0 : nsIAtom *GetTag(nsINode *aNode)
1028 : {
1029 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
1030 0 : if (!content)
1031 : {
1032 0 : NS_NOTREACHED("bad node passed to GetTag()");
1033 0 : return nsnull;
1034 : }
1035 :
1036 0 : return content->Tag();
1037 : }
1038 :
1039 : // Returns the parent
1040 : nsINode*
1041 0 : ParentOffset(nsINode *aNode, PRInt32 *aChildOffset)
1042 : {
1043 0 : if (!aNode || !aChildOffset)
1044 0 : return nsnull;
1045 :
1046 0 : nsIContent* parent = aNode->GetParent();
1047 0 : if (parent)
1048 : {
1049 0 : *aChildOffset = parent->IndexOf(aNode);
1050 :
1051 0 : return parent;
1052 : }
1053 :
1054 0 : return nsnull;
1055 : }
1056 :
1057 : static nsINode*
1058 0 : GetCellParent(nsINode *aDomNode)
1059 : {
1060 0 : if (!aDomNode)
1061 0 : return nsnull;
1062 0 : nsINode* current = aDomNode;
1063 : // Start with current node and look for a table cell
1064 0 : while (current)
1065 : {
1066 0 : nsIAtom* tag = GetTag(current);
1067 0 : if (tag == nsGkAtoms::td || tag == nsGkAtoms::th)
1068 0 : return current;
1069 0 : current = current->GetParent();
1070 : }
1071 0 : return nsnull;
1072 : }
1073 :
1074 :
1075 : void
1076 0 : nsFrameSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter)
1077 : {
1078 0 : mShell = aShell;
1079 0 : mMouseDownState = false;
1080 0 : mDesiredXSet = false;
1081 0 : mLimiter = aLimiter;
1082 : mCaretMovementStyle =
1083 0 : Preferences::GetInt("bidi.edit.caret_movement_style", 2);
1084 0 : }
1085 :
1086 : nsresult
1087 0 : nsFrameSelection::MoveCaret(PRUint32 aKeycode,
1088 : bool aContinueSelection,
1089 : nsSelectionAmount aAmount)
1090 : {
1091 : bool visualMovement =
1092 : (aKeycode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE ||
1093 : aKeycode == nsIDOMKeyEvent::DOM_VK_DELETE ||
1094 : aKeycode == nsIDOMKeyEvent::DOM_VK_HOME ||
1095 : aKeycode == nsIDOMKeyEvent::DOM_VK_END) ?
1096 : false : // Delete operations and home/end are always logical
1097 : mCaretMovementStyle == 1 ||
1098 0 : (mCaretMovementStyle == 2 && !aContinueSelection);
1099 :
1100 0 : return MoveCaret(aKeycode, aContinueSelection, aAmount, visualMovement);
1101 : }
1102 :
1103 : nsresult
1104 0 : nsFrameSelection::MoveCaret(PRUint32 aKeycode,
1105 : bool aContinueSelection,
1106 : nsSelectionAmount aAmount,
1107 : bool aVisualMovement)
1108 : {
1109 0 : NS_ENSURE_STATE(mShell);
1110 : // Flush out layout, since we need it to be up to date to do caret
1111 : // positioning.
1112 0 : mShell->FlushPendingNotifications(Flush_Layout);
1113 :
1114 0 : if (!mShell) {
1115 0 : return NS_OK;
1116 : }
1117 :
1118 0 : nsPresContext *context = mShell->GetPresContext();
1119 0 : if (!context)
1120 0 : return NS_ERROR_FAILURE;
1121 :
1122 : bool isCollapsed;
1123 0 : nscoord desiredX = 0; //we must keep this around and revalidate it when its just UP/DOWN
1124 :
1125 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1126 0 : nsRefPtr<nsTypedSelection> sel = mDomSelections[index];
1127 0 : if (!sel)
1128 0 : return NS_ERROR_NULL_POINTER;
1129 :
1130 0 : nsresult result = sel->GetIsCollapsed(&isCollapsed);
1131 0 : if (NS_FAILED(result))
1132 0 : return result;
1133 0 : if (aKeycode == nsIDOMKeyEvent::DOM_VK_UP ||
1134 : aKeycode == nsIDOMKeyEvent::DOM_VK_DOWN)
1135 : {
1136 0 : result = FetchDesiredX(desiredX);
1137 0 : if (NS_FAILED(result))
1138 0 : return result;
1139 0 : SetDesiredX(desiredX);
1140 : }
1141 :
1142 : PRInt32 caretStyle =
1143 0 : Preferences::GetInt("layout.selection.caret_style", 0);
1144 : #ifdef XP_MACOSX
1145 : if (caretStyle == 0) {
1146 : caretStyle = 2; // put caret at the selection edge in the |aKeycode| direction
1147 : }
1148 : #endif
1149 :
1150 0 : if (!isCollapsed && !aContinueSelection && caretStyle == 2) {
1151 0 : switch (aKeycode){
1152 : case nsIDOMKeyEvent::DOM_VK_LEFT :
1153 : case nsIDOMKeyEvent::DOM_VK_UP :
1154 : {
1155 0 : const nsRange* anchorFocusRange = sel->GetAnchorFocusRange();
1156 0 : if (anchorFocusRange) {
1157 : sel->Collapse(anchorFocusRange->GetStartParent(),
1158 0 : anchorFocusRange->StartOffset());
1159 : }
1160 0 : mHint = HINTRIGHT;
1161 0 : sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
1162 0 : return NS_OK;
1163 : }
1164 :
1165 : case nsIDOMKeyEvent::DOM_VK_RIGHT :
1166 : case nsIDOMKeyEvent::DOM_VK_DOWN :
1167 : {
1168 0 : const nsRange* anchorFocusRange = sel->GetAnchorFocusRange();
1169 0 : if (anchorFocusRange) {
1170 : sel->Collapse(anchorFocusRange->GetEndParent(),
1171 0 : anchorFocusRange->EndOffset());
1172 : }
1173 0 : mHint = HINTLEFT;
1174 0 : sel->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
1175 0 : return NS_OK;
1176 : }
1177 : }
1178 : }
1179 :
1180 : nsIFrame *frame;
1181 0 : PRInt32 offsetused = 0;
1182 0 : result = sel->GetPrimaryFrameForFocusNode(&frame, &offsetused,
1183 0 : aVisualMovement);
1184 :
1185 0 : if (NS_FAILED(result) || !frame)
1186 0 : return result?result:NS_ERROR_FAILURE;
1187 :
1188 0 : nsPeekOffsetStruct pos;
1189 : //set data using mLimiter to stop on scroll views. If we have a limiter then we stop peeking
1190 : //when we hit scrollable views. If no limiter then just let it go ahead
1191 : pos.SetData(aAmount, eDirPrevious, offsetused, desiredX,
1192 0 : true, mLimiter != nsnull, true, aVisualMovement);
1193 :
1194 0 : nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(frame);
1195 :
1196 0 : HINT tHint(mHint); //temporary variable so we dont set mHint until it is necessary
1197 0 : switch (aKeycode){
1198 : case nsIDOMKeyEvent::DOM_VK_RIGHT :
1199 0 : InvalidateDesiredX();
1200 0 : pos.mDirection = (baseLevel & 1) ? eDirPrevious : eDirNext;
1201 0 : break;
1202 : case nsIDOMKeyEvent::DOM_VK_LEFT :
1203 0 : InvalidateDesiredX();
1204 0 : pos.mDirection = (baseLevel & 1) ? eDirNext : eDirPrevious;
1205 0 : break;
1206 : case nsIDOMKeyEvent::DOM_VK_DELETE :
1207 0 : InvalidateDesiredX();
1208 0 : pos.mDirection = eDirNext;
1209 0 : break;
1210 : case nsIDOMKeyEvent::DOM_VK_BACK_SPACE :
1211 0 : InvalidateDesiredX();
1212 0 : pos.mDirection = eDirPrevious;
1213 0 : break;
1214 : case nsIDOMKeyEvent::DOM_VK_DOWN :
1215 0 : pos.mAmount = eSelectLine;
1216 0 : pos.mDirection = eDirNext;
1217 0 : break;
1218 : case nsIDOMKeyEvent::DOM_VK_UP :
1219 0 : pos.mAmount = eSelectLine;
1220 0 : pos.mDirection = eDirPrevious;
1221 0 : break;
1222 : case nsIDOMKeyEvent::DOM_VK_HOME :
1223 0 : InvalidateDesiredX();
1224 0 : pos.mAmount = eSelectBeginLine;
1225 0 : break;
1226 : case nsIDOMKeyEvent::DOM_VK_END :
1227 0 : InvalidateDesiredX();
1228 0 : pos.mAmount = eSelectEndLine;
1229 0 : break;
1230 0 : default :return NS_ERROR_FAILURE;
1231 : }
1232 0 : PostReason(nsISelectionListener::KEYPRESS_REASON);
1233 0 : if (NS_SUCCEEDED(result = frame->PeekOffset(&pos)) && pos.mResultContent)
1234 : {
1235 : nsIFrame *theFrame;
1236 : PRInt32 currentOffset, frameStart, frameEnd;
1237 :
1238 0 : if (aAmount >= eSelectCharacter && aAmount <= eSelectWord)
1239 : {
1240 : // For left/right, PeekOffset() sets pos.mResultFrame correctly, but does not set pos.mAttachForward,
1241 : // so determine the hint here based on the result frame and offset:
1242 : // If we're at the end of a text frame, set the hint to HINTLEFT to indicate that we
1243 : // want the caret displayed at the end of this frame, not at the beginning of the next one.
1244 0 : theFrame = pos.mResultFrame;
1245 0 : theFrame->GetOffsets(frameStart, frameEnd);
1246 0 : currentOffset = pos.mContentOffset;
1247 0 : if (frameEnd == currentOffset && !(frameStart == 0 && frameEnd == 0))
1248 0 : tHint = HINTLEFT;
1249 : else
1250 0 : tHint = HINTRIGHT;
1251 : } else {
1252 : // For up/down and home/end, pos.mResultFrame might not be set correctly, or not at all.
1253 : // In these cases, get the frame based on the content and hint returned by PeekOffset().
1254 0 : tHint = (HINT)pos.mAttachForward;
1255 : theFrame = GetFrameForNodeOffset(pos.mResultContent, pos.mContentOffset,
1256 0 : tHint, ¤tOffset);
1257 0 : if (!theFrame)
1258 0 : return NS_ERROR_FAILURE;
1259 :
1260 0 : theFrame->GetOffsets(frameStart, frameEnd);
1261 : }
1262 :
1263 0 : if (context->BidiEnabled())
1264 : {
1265 0 : switch (aKeycode) {
1266 : case nsIDOMKeyEvent::DOM_VK_HOME:
1267 : case nsIDOMKeyEvent::DOM_VK_END:
1268 : // set the caret Bidi level to the paragraph embedding level
1269 0 : SetCaretBidiLevel(NS_GET_BASE_LEVEL(theFrame));
1270 0 : break;
1271 :
1272 : default:
1273 : // If the current position is not a frame boundary, it's enough just to take the Bidi level of the current frame
1274 0 : if ((pos.mContentOffset != frameStart && pos.mContentOffset != frameEnd)
1275 : || (eSelectLine == aAmount))
1276 : {
1277 0 : SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(theFrame));
1278 : }
1279 : else
1280 0 : BidiLevelFromMove(mShell, pos.mResultContent, pos.mContentOffset, aKeycode, tHint);
1281 : }
1282 : }
1283 : result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset,
1284 0 : tHint, aContinueSelection, false);
1285 0 : } else if (aKeycode == nsIDOMKeyEvent::DOM_VK_RIGHT && !aContinueSelection) {
1286 : // Collapse selection if PeekOffset failed, we either
1287 : // 1. bumped into the BRFrame, bug 207623
1288 : // 2. had select-all in a text input (DIV range), bug 352759.
1289 0 : bool isBRFrame = frame->GetType() == nsGkAtoms::brFrame;
1290 0 : sel->Collapse(sel->GetFocusNode(), sel->GetFocusOffset());
1291 : // Note: 'frame' might be dead here.
1292 0 : if (!isBRFrame) {
1293 0 : mHint = HINTLEFT; // We're now at the end of the frame to the left.
1294 : }
1295 0 : result = NS_OK;
1296 : }
1297 0 : if (NS_SUCCEEDED(result))
1298 : {
1299 0 : result = mDomSelections[index]->
1300 0 : ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
1301 : }
1302 :
1303 0 : return result;
1304 : }
1305 :
1306 : //END nsFrameSelection methods
1307 :
1308 :
1309 : //BEGIN nsFrameSelection methods
1310 :
1311 : NS_IMETHODIMP
1312 0 : nsTypedSelection::ToString(PRUnichar **aReturn)
1313 : {
1314 : return ToStringWithFormat("text/plain",
1315 : nsIDocumentEncoder::SkipInvisibleContent,
1316 0 : 0, aReturn);
1317 : }
1318 :
1319 :
1320 : NS_IMETHODIMP
1321 0 : nsTypedSelection::ToStringWithFormat(const char * aFormatType, PRUint32 aFlags,
1322 : PRInt32 aWrapCol, PRUnichar **aReturn)
1323 : {
1324 0 : nsresult rv = NS_OK;
1325 0 : if (!aReturn)
1326 0 : return NS_ERROR_NULL_POINTER;
1327 :
1328 0 : nsCAutoString formatType( NS_DOC_ENCODER_CONTRACTID_BASE );
1329 0 : formatType.Append(aFormatType);
1330 : nsCOMPtr<nsIDocumentEncoder> encoder =
1331 0 : do_CreateInstance(formatType.get(), &rv);
1332 0 : NS_ENSURE_SUCCESS(rv, rv);
1333 :
1334 0 : nsCOMPtr<nsIPresShell> shell;
1335 0 : rv = GetPresShell(getter_AddRefs(shell));
1336 0 : if (NS_FAILED(rv) || !shell) {
1337 0 : return NS_ERROR_FAILURE;
1338 : }
1339 :
1340 0 : nsIDocument *doc = shell->GetDocument();
1341 0 : NS_ENSURE_SUCCESS(rv, rv);
1342 :
1343 0 : nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(doc);
1344 0 : NS_ASSERTION(domDoc, "Need a document");
1345 :
1346 : // Flags should always include OutputSelectionOnly if we're coming from here:
1347 0 : aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
1348 0 : nsAutoString readstring;
1349 0 : readstring.AssignASCII(aFormatType);
1350 0 : rv = encoder->Init(domDoc, readstring, aFlags);
1351 0 : NS_ENSURE_SUCCESS(rv, rv);
1352 :
1353 0 : encoder->SetSelection(this);
1354 0 : if (aWrapCol != 0)
1355 0 : encoder->SetWrapColumn(aWrapCol);
1356 :
1357 0 : nsAutoString tmp;
1358 0 : rv = encoder->EncodeToString(tmp);
1359 0 : *aReturn = ToNewUnicode(tmp);//get the unicode pointer from it. this is temporary
1360 0 : return rv;
1361 : }
1362 :
1363 : NS_IMETHODIMP
1364 0 : nsTypedSelection::SetInterlinePosition(bool aHintRight)
1365 : {
1366 0 : if (!mFrameSelection)
1367 0 : return NS_ERROR_NOT_INITIALIZED; // Can't do selection
1368 : nsFrameSelection::HINT hint;
1369 0 : if (aHintRight)
1370 0 : hint = nsFrameSelection::HINTRIGHT;
1371 : else
1372 0 : hint = nsFrameSelection::HINTLEFT;
1373 0 : mFrameSelection->SetHint(hint);
1374 :
1375 0 : return NS_OK;
1376 : }
1377 :
1378 : NS_IMETHODIMP
1379 0 : nsTypedSelection::GetInterlinePosition(bool *aHintRight)
1380 : {
1381 0 : if (!mFrameSelection)
1382 0 : return NS_ERROR_NOT_INITIALIZED; // Can't do selection
1383 0 : *aHintRight = (mFrameSelection->GetHint() == nsFrameSelection::HINTRIGHT);
1384 0 : return NS_OK;
1385 : }
1386 :
1387 : nsPrevNextBidiLevels
1388 0 : nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode,
1389 : PRUint32 aContentOffset,
1390 : bool aJumpLines) const
1391 : {
1392 0 : return GetPrevNextBidiLevels(aNode, aContentOffset, mHint, aJumpLines);
1393 : }
1394 :
1395 : nsPrevNextBidiLevels
1396 0 : nsFrameSelection::GetPrevNextBidiLevels(nsIContent *aNode,
1397 : PRUint32 aContentOffset,
1398 : HINT aHint,
1399 : bool aJumpLines) const
1400 : {
1401 : // Get the level of the frames on each side
1402 : nsIFrame *currentFrame;
1403 : PRInt32 currentOffset;
1404 : PRInt32 frameStart, frameEnd;
1405 : nsDirection direction;
1406 :
1407 : nsPrevNextBidiLevels levels;
1408 0 : levels.SetData(nsnull, nsnull, 0, 0);
1409 :
1410 : currentFrame = GetFrameForNodeOffset(aNode, aContentOffset,
1411 0 : aHint, ¤tOffset);
1412 0 : if (!currentFrame)
1413 0 : return levels;
1414 :
1415 0 : currentFrame->GetOffsets(frameStart, frameEnd);
1416 :
1417 0 : if (0 == frameStart && 0 == frameEnd)
1418 0 : direction = eDirPrevious;
1419 0 : else if (frameStart == currentOffset)
1420 0 : direction = eDirPrevious;
1421 0 : else if (frameEnd == currentOffset)
1422 0 : direction = eDirNext;
1423 : else {
1424 : // we are neither at the beginning nor at the end of the frame, so we have no worries
1425 : levels.SetData(currentFrame, currentFrame,
1426 0 : NS_GET_EMBEDDING_LEVEL(currentFrame),
1427 0 : NS_GET_EMBEDDING_LEVEL(currentFrame));
1428 0 : return levels;
1429 : }
1430 :
1431 : nsIFrame *newFrame;
1432 : PRInt32 offset;
1433 : bool jumpedLine;
1434 : nsresult rv = currentFrame->GetFrameFromDirection(direction, false,
1435 : aJumpLines, true,
1436 0 : &newFrame, &offset, &jumpedLine);
1437 0 : if (NS_FAILED(rv))
1438 0 : newFrame = nsnull;
1439 :
1440 0 : PRUint8 baseLevel = NS_GET_BASE_LEVEL(currentFrame);
1441 0 : PRUint8 currentLevel = NS_GET_EMBEDDING_LEVEL(currentFrame);
1442 0 : PRUint8 newLevel = newFrame ? NS_GET_EMBEDDING_LEVEL(newFrame) : baseLevel;
1443 :
1444 : // If not jumping lines, disregard br frames, since they might be positioned incorrectly.
1445 : // XXX This could be removed once bug 339786 is fixed.
1446 0 : if (!aJumpLines) {
1447 0 : if (currentFrame->GetType() == nsGkAtoms::brFrame) {
1448 0 : currentFrame = nsnull;
1449 0 : currentLevel = baseLevel;
1450 : }
1451 0 : if (newFrame && newFrame->GetType() == nsGkAtoms::brFrame) {
1452 0 : newFrame = nsnull;
1453 0 : newLevel = baseLevel;
1454 : }
1455 : }
1456 :
1457 0 : if (direction == eDirNext)
1458 0 : levels.SetData(currentFrame, newFrame, currentLevel, newLevel);
1459 : else
1460 0 : levels.SetData(newFrame, currentFrame, newLevel, currentLevel);
1461 :
1462 0 : return levels;
1463 : }
1464 :
1465 : nsresult
1466 0 : nsFrameSelection::GetFrameFromLevel(nsIFrame *aFrameIn,
1467 : nsDirection aDirection,
1468 : PRUint8 aBidiLevel,
1469 : nsIFrame **aFrameOut) const
1470 : {
1471 0 : NS_ENSURE_STATE(mShell);
1472 0 : PRUint8 foundLevel = 0;
1473 0 : nsIFrame *foundFrame = aFrameIn;
1474 :
1475 0 : nsCOMPtr<nsIFrameEnumerator> frameTraversal;
1476 : nsresult result;
1477 0 : nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
1478 0 : if (NS_FAILED(result))
1479 0 : return result;
1480 :
1481 0 : result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),
1482 : mShell->GetPresContext(), aFrameIn,
1483 : eLeaf,
1484 : false, // aVisual
1485 : false, // aLockInScrollView
1486 : false // aFollowOOFs
1487 0 : );
1488 0 : if (NS_FAILED(result))
1489 0 : return result;
1490 :
1491 0 : do {
1492 0 : *aFrameOut = foundFrame;
1493 0 : if (aDirection == eDirNext)
1494 0 : frameTraversal->Next();
1495 : else
1496 0 : frameTraversal->Prev();
1497 :
1498 0 : foundFrame = frameTraversal->CurrentItem();
1499 0 : if (!foundFrame)
1500 0 : return NS_ERROR_FAILURE;
1501 0 : foundLevel = NS_GET_EMBEDDING_LEVEL(foundFrame);
1502 :
1503 : } while (foundLevel > aBidiLevel);
1504 :
1505 0 : return NS_OK;
1506 : }
1507 :
1508 :
1509 : nsresult
1510 0 : nsFrameSelection::MaintainSelection(nsSelectionAmount aAmount)
1511 : {
1512 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1513 0 : if (!mDomSelections[index])
1514 0 : return NS_ERROR_NULL_POINTER;
1515 :
1516 0 : mMaintainedAmount = aAmount;
1517 :
1518 : const nsRange* anchorFocusRange =
1519 0 : mDomSelections[index]->GetAnchorFocusRange();
1520 0 : if (anchorFocusRange) {
1521 0 : return anchorFocusRange->CloneRange(getter_AddRefs(mMaintainRange));
1522 : }
1523 :
1524 0 : mMaintainRange = nsnull;
1525 0 : return NS_OK;
1526 : }
1527 :
1528 :
1529 : /** After moving the caret, its Bidi level is set according to the following rules:
1530 : *
1531 : * After moving over a character with left/right arrow, set to the Bidi level of the last moved over character.
1532 : * After Home and End, set to the paragraph embedding level.
1533 : * After up/down arrow, PageUp/Down, set to the lower level of the 2 surrounding characters.
1534 : * After mouse click, set to the level of the current frame.
1535 : *
1536 : * The following two methods use GetPrevNextBidiLevels to determine the new Bidi level.
1537 : * BidiLevelFromMove is called when the caret is moved in response to a keyboard event
1538 : *
1539 : * @param aPresShell is the presentation shell
1540 : * @param aNode is the content node
1541 : * @param aContentOffset is the new caret position, as an offset into aNode
1542 : * @param aKeycode is the keyboard event that moved the caret to the new position
1543 : * @param aHint is the hint indicating in what logical direction the caret moved
1544 : */
1545 0 : void nsFrameSelection::BidiLevelFromMove(nsIPresShell* aPresShell,
1546 : nsIContent *aNode,
1547 : PRUint32 aContentOffset,
1548 : PRUint32 aKeycode,
1549 : HINT aHint)
1550 : {
1551 0 : switch (aKeycode) {
1552 :
1553 : // Right and Left: the new cursor Bidi level is the level of the character moved over
1554 : case nsIDOMKeyEvent::DOM_VK_RIGHT:
1555 : case nsIDOMKeyEvent::DOM_VK_LEFT:
1556 : {
1557 : nsPrevNextBidiLevels levels = GetPrevNextBidiLevels(aNode, aContentOffset,
1558 0 : aHint, false);
1559 :
1560 0 : if (HINTLEFT == aHint)
1561 0 : SetCaretBidiLevel(levels.mLevelBefore);
1562 : else
1563 0 : SetCaretBidiLevel(levels.mLevelAfter);
1564 0 : break;
1565 : }
1566 : /*
1567 : // Up and Down: the new cursor Bidi level is the smaller of the two surrounding characters
1568 : case nsIDOMKeyEvent::DOM_VK_UP:
1569 : case nsIDOMKeyEvent::DOM_VK_DOWN:
1570 : GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel);
1571 : aPresShell->SetCaretBidiLevel(NS_MIN(firstLevel, secondLevel));
1572 : break;
1573 : */
1574 :
1575 : default:
1576 0 : UndefineCaretBidiLevel();
1577 : }
1578 0 : }
1579 :
1580 : /**
1581 : * BidiLevelFromClick is called when the caret is repositioned by clicking the mouse
1582 : *
1583 : * @param aNode is the content node
1584 : * @param aContentOffset is the new caret position, as an offset into aNode
1585 : */
1586 0 : void nsFrameSelection::BidiLevelFromClick(nsIContent *aNode,
1587 : PRUint32 aContentOffset)
1588 : {
1589 0 : nsIFrame* clickInFrame=nsnull;
1590 : PRInt32 OffsetNotUsed;
1591 :
1592 0 : clickInFrame = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &OffsetNotUsed);
1593 0 : if (!clickInFrame)
1594 0 : return;
1595 :
1596 0 : SetCaretBidiLevel(NS_GET_EMBEDDING_LEVEL(clickInFrame));
1597 : }
1598 :
1599 :
1600 : bool
1601 0 : nsFrameSelection::AdjustForMaintainedSelection(nsIContent *aContent,
1602 : PRInt32 aOffset)
1603 : {
1604 0 : if (!mMaintainRange)
1605 0 : return false;
1606 :
1607 0 : if (!aContent) {
1608 0 : return false;
1609 : }
1610 :
1611 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1612 0 : if (!mDomSelections[index])
1613 0 : return false;
1614 :
1615 0 : nsINode* rangeStartNode = mMaintainRange->GetStartParent();
1616 0 : nsINode* rangeEndNode = mMaintainRange->GetEndParent();
1617 0 : PRInt32 rangeStartOffset = mMaintainRange->StartOffset();
1618 0 : PRInt32 rangeEndOffset = mMaintainRange->EndOffset();
1619 :
1620 : PRInt32 relToStart =
1621 : nsContentUtils::ComparePoints(rangeStartNode, rangeStartOffset,
1622 0 : aContent, aOffset);
1623 : PRInt32 relToEnd =
1624 : nsContentUtils::ComparePoints(rangeEndNode, rangeEndOffset,
1625 0 : aContent, aOffset);
1626 :
1627 : // If aContent/aOffset is inside the maintained selection, or if it is on the
1628 : // "anchor" side of the maintained selection, we need to do something.
1629 0 : if ((relToStart < 0 && relToEnd > 0) ||
1630 : (relToStart > 0 &&
1631 0 : mDomSelections[index]->GetDirection() == eDirNext) ||
1632 : (relToEnd < 0 &&
1633 0 : mDomSelections[index]->GetDirection() == eDirPrevious)) {
1634 : // Set the current range to the maintained range.
1635 0 : mDomSelections[index]->ReplaceAnchorFocusRange(mMaintainRange);
1636 0 : if (relToStart < 0 && relToEnd > 0) {
1637 : // We're inside the maintained selection, just keep it selected.
1638 0 : return true;
1639 : }
1640 : // Reverse the direction of the selection so that the anchor will be on the
1641 : // far side of the maintained selection, relative to aContent/aOffset.
1642 0 : mDomSelections[index]->SetDirection(relToStart > 0 ? eDirPrevious : eDirNext);
1643 : }
1644 0 : return false;
1645 : }
1646 :
1647 :
1648 : nsresult
1649 0 : nsFrameSelection::HandleClick(nsIContent *aNewFocus,
1650 : PRUint32 aContentOffset,
1651 : PRUint32 aContentEndOffset,
1652 : bool aContinueSelection,
1653 : bool aMultipleSelection,
1654 : bool aHint)
1655 : {
1656 0 : if (!aNewFocus)
1657 0 : return NS_ERROR_INVALID_ARG;
1658 :
1659 0 : InvalidateDesiredX();
1660 :
1661 0 : if (!aContinueSelection) {
1662 0 : mMaintainRange = nsnull;
1663 0 : if (!IsValidSelectionPoint(this, aNewFocus)) {
1664 0 : mAncestorLimiter = nsnull;
1665 : }
1666 : }
1667 :
1668 : // Don't take focus when dragging off of a table
1669 0 : if (!mDragSelectingCells)
1670 : {
1671 0 : BidiLevelFromClick(aNewFocus, aContentOffset);
1672 0 : PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
1673 0 : if (aContinueSelection &&
1674 0 : AdjustForMaintainedSelection(aNewFocus, aContentOffset))
1675 0 : return NS_OK; //shift clicked to maintained selection. rejected.
1676 :
1677 : return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, HINT(aHint),
1678 0 : aContinueSelection, aMultipleSelection);
1679 : }
1680 :
1681 0 : return NS_OK;
1682 : }
1683 :
1684 : void
1685 0 : nsFrameSelection::HandleDrag(nsIFrame *aFrame, nsPoint aPoint)
1686 : {
1687 0 : if (!aFrame || !mShell)
1688 0 : return;
1689 :
1690 : nsresult result;
1691 0 : nsIFrame *newFrame = 0;
1692 0 : nsPoint newPoint;
1693 :
1694 0 : result = ConstrainFrameAndPointToAnchorSubtree(aFrame, aPoint, &newFrame, newPoint);
1695 0 : if (NS_FAILED(result))
1696 0 : return;
1697 0 : if (!newFrame)
1698 0 : return;
1699 :
1700 : nsIFrame::ContentOffsets offsets =
1701 0 : newFrame->GetContentOffsetsFromPoint(newPoint);
1702 0 : if (!offsets.content)
1703 : return;
1704 :
1705 0 : if (newFrame->IsSelected() &&
1706 0 : AdjustForMaintainedSelection(offsets.content, offsets.offset))
1707 : return;
1708 :
1709 : // Adjust offsets according to maintained amount
1710 0 : if (mMaintainRange &&
1711 0 : mMaintainedAmount != eSelectNoAmount) {
1712 :
1713 0 : nsINode* rangenode = mMaintainRange->GetStartParent();
1714 0 : PRInt32 rangeOffset = mMaintainRange->StartOffset();
1715 : PRInt32 relativePosition =
1716 : nsContentUtils::ComparePoints(rangenode, rangeOffset,
1717 0 : offsets.content, offsets.offset);
1718 :
1719 0 : nsDirection direction = relativePosition > 0 ? eDirPrevious : eDirNext;
1720 0 : nsSelectionAmount amount = mMaintainedAmount;
1721 0 : if (amount == eSelectBeginLine && direction == eDirNext)
1722 0 : amount = eSelectEndLine;
1723 :
1724 : PRInt32 offset;
1725 0 : nsIFrame* frame = GetFrameForNodeOffset(offsets.content, offsets.offset, HINTRIGHT, &offset);
1726 :
1727 0 : if (frame && amount == eSelectWord && direction == eDirPrevious) {
1728 : // To avoid selecting the previous word when at start of word,
1729 : // first move one character forward.
1730 0 : nsPeekOffsetStruct charPos;
1731 : charPos.SetData(eSelectCharacter, eDirNext, offset, 0,
1732 0 : false, mLimiter != nsnull, false, false);
1733 0 : if (NS_SUCCEEDED(frame->PeekOffset(&charPos))) {
1734 0 : frame = charPos.mResultFrame;
1735 0 : offset = charPos.mContentOffset;
1736 : }
1737 : }
1738 :
1739 0 : nsPeekOffsetStruct pos;
1740 : pos.SetData(amount, direction, offset, 0,
1741 0 : false, mLimiter != nsnull, false, false);
1742 :
1743 0 : if (frame && NS_SUCCEEDED(frame->PeekOffset(&pos)) && pos.mResultContent) {
1744 0 : offsets.content = pos.mResultContent;
1745 0 : offsets.offset = pos.mContentOffset;
1746 : }
1747 : }
1748 :
1749 : HandleClick(offsets.content, offsets.offset, offsets.offset,
1750 0 : true, false, offsets.associateWithNext);
1751 : }
1752 :
1753 : nsresult
1754 0 : nsFrameSelection::StartAutoScrollTimer(nsIFrame *aFrame,
1755 : nsPoint aPoint,
1756 : PRUint32 aDelay)
1757 : {
1758 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1759 0 : if (!mDomSelections[index])
1760 0 : return NS_ERROR_NULL_POINTER;
1761 :
1762 0 : return mDomSelections[index]->StartAutoScrollTimer(aFrame, aPoint, aDelay);
1763 : }
1764 :
1765 : void
1766 0 : nsFrameSelection::StopAutoScrollTimer()
1767 : {
1768 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1769 0 : if (!mDomSelections[index])
1770 0 : return;
1771 :
1772 0 : mDomSelections[index]->StopAutoScrollTimer();
1773 : }
1774 :
1775 : /**
1776 : hard to go from nodes to frames, easy the other way!
1777 : */
1778 : nsresult
1779 0 : nsFrameSelection::TakeFocus(nsIContent *aNewFocus,
1780 : PRUint32 aContentOffset,
1781 : PRUint32 aContentEndOffset,
1782 : HINT aHint,
1783 : bool aContinueSelection,
1784 : bool aMultipleSelection)
1785 : {
1786 0 : if (!aNewFocus)
1787 0 : return NS_ERROR_NULL_POINTER;
1788 :
1789 0 : NS_ENSURE_STATE(mShell);
1790 :
1791 0 : if (!IsValidSelectionPoint(this,aNewFocus))
1792 0 : return NS_ERROR_FAILURE;
1793 :
1794 : // Clear all table selection data
1795 0 : mSelectingTableCellMode = 0;
1796 0 : mDragSelectingCells = false;
1797 0 : mStartSelectedCell = nsnull;
1798 0 : mEndSelectedCell = nsnull;
1799 0 : mAppendStartSelectedCell = nsnull;
1800 :
1801 : //HACKHACKHACK
1802 0 : if (!aNewFocus->GetParent())
1803 0 : return NS_ERROR_FAILURE;
1804 : //END HACKHACKHACK /checking for root frames/content
1805 :
1806 0 : mHint = aHint;
1807 :
1808 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1809 0 : if (!mDomSelections[index])
1810 0 : return NS_ERROR_NULL_POINTER;
1811 :
1812 : //traverse through document and unselect crap here
1813 0 : if (!aContinueSelection) {//single click? setting cursor down
1814 0 : PRUint32 batching = mBatching;//hack to use the collapse code.
1815 0 : bool changes = mChangesDuringBatching;
1816 0 : mBatching = 1;
1817 :
1818 0 : if (aMultipleSelection) {
1819 : // Remove existing collapsed ranges as there's no point in having
1820 : // non-anchor/focus collapsed ranges.
1821 0 : mDomSelections[index]->RemoveCollapsedRanges();
1822 :
1823 0 : nsRefPtr<nsRange> newRange = new nsRange();
1824 :
1825 0 : newRange->SetStart(aNewFocus, aContentOffset);
1826 0 : newRange->SetEnd(aNewFocus, aContentOffset);
1827 0 : mDomSelections[index]->AddRange(newRange);
1828 0 : mBatching = batching;
1829 0 : mChangesDuringBatching = changes;
1830 : }
1831 : else
1832 : {
1833 0 : bool oldDesiredXSet = mDesiredXSet; //need to keep old desired X if it was set.
1834 0 : mDomSelections[index]->Collapse(aNewFocus, aContentOffset);
1835 0 : mDesiredXSet = oldDesiredXSet; //now reset desired X back.
1836 0 : mBatching = batching;
1837 0 : mChangesDuringBatching = changes;
1838 : }
1839 0 : if (aContentEndOffset != aContentOffset)
1840 0 : mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);
1841 :
1842 : //find out if we are inside a table. if so, find out which one and which cell
1843 : //once we do that, the next time we get a takefocus, check the parent tree.
1844 : //if we are no longer inside same table ,cell then switch to table selection mode.
1845 : // BUT only do this in an editor
1846 :
1847 0 : NS_ENSURE_STATE(mShell);
1848 0 : PRInt16 displaySelection = mShell->GetSelectionFlags();
1849 :
1850 : // Editor has DISPLAY_ALL selection type
1851 0 : if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
1852 : {
1853 0 : mCellParent = GetCellParent(aNewFocus);
1854 : #ifdef DEBUG_TABLE_SELECTION
1855 : if (mCellParent)
1856 : printf(" * TakeFocus - Collapsing into new cell\n");
1857 : #endif
1858 : }
1859 : }
1860 : else {
1861 : // Now update the range list:
1862 0 : if (aContinueSelection && aNewFocus)
1863 : {
1864 : PRInt32 offset;
1865 0 : nsINode *cellparent = GetCellParent(aNewFocus);
1866 0 : if (mCellParent && cellparent && cellparent != mCellParent) //switch to cell selection mode
1867 : {
1868 : #ifdef DEBUG_TABLE_SELECTION
1869 : printf(" * TakeFocus - moving into new cell\n");
1870 : #endif
1871 0 : nsMouseEvent event(false, 0, nsnull, nsMouseEvent::eReal);
1872 :
1873 : // Start selecting in the cell we were in before
1874 0 : nsINode* parent = ParentOffset(mCellParent, &offset);
1875 0 : if (parent)
1876 : HandleTableSelection(parent, offset,
1877 0 : nsISelectionPrivate::TABLESELECTION_CELL, &event);
1878 :
1879 : // Find the parent of this new cell and extend selection to it
1880 0 : parent = ParentOffset(cellparent, &offset);
1881 :
1882 : // XXXX We need to REALLY get the current key shift state
1883 : // (we'd need to add event listener -- let's not bother for now)
1884 0 : event.isShift = false; //aContinueSelection;
1885 0 : if (parent)
1886 : {
1887 0 : mCellParent = cellparent;
1888 : // Continue selection into next cell
1889 : HandleTableSelection(parent, offset,
1890 0 : nsISelectionPrivate::TABLESELECTION_CELL, &event);
1891 : }
1892 : }
1893 : else
1894 : {
1895 : // XXXX Problem: Shift+click in browser is appending text selection to selected table!!!
1896 : // is this the place to erase seleced cells ?????
1897 0 : if (mDomSelections[index]->GetDirection() == eDirNext && aContentEndOffset > aContentOffset) //didn't go far enough
1898 : {
1899 0 : mDomSelections[index]->Extend(aNewFocus, aContentEndOffset);//this will only redraw the diff
1900 : }
1901 : else
1902 0 : mDomSelections[index]->Extend(aNewFocus, aContentOffset);
1903 : }
1904 : }
1905 : }
1906 :
1907 : // Don't notify selection listeners if batching is on:
1908 0 : if (GetBatching())
1909 0 : return NS_OK;
1910 0 : return NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
1911 : }
1912 :
1913 :
1914 : SelectionDetails*
1915 0 : nsFrameSelection::LookUpSelection(nsIContent *aContent,
1916 : PRInt32 aContentOffset,
1917 : PRInt32 aContentLength,
1918 : bool aSlowCheck) const
1919 : {
1920 0 : if (!aContent || !mShell)
1921 0 : return nsnull;
1922 :
1923 0 : SelectionDetails* details = nsnull;
1924 :
1925 0 : for (PRInt32 j = 0; j < nsISelectionController::NUM_SELECTIONTYPES; j++) {
1926 0 : if (mDomSelections[j]) {
1927 0 : mDomSelections[j]->LookUpSelection(aContent, aContentOffset,
1928 0 : aContentLength, &details, (SelectionType)(1<<j), aSlowCheck);
1929 : }
1930 : }
1931 :
1932 0 : return details;
1933 : }
1934 :
1935 : void
1936 0 : nsFrameSelection::SetMouseDownState(bool aState)
1937 : {
1938 0 : if (mMouseDownState == aState)
1939 0 : return;
1940 :
1941 0 : mMouseDownState = aState;
1942 :
1943 0 : if (!mMouseDownState)
1944 : {
1945 0 : mDragSelectingCells = false;
1946 0 : PostReason(nsISelectionListener::MOUSEUP_REASON);
1947 0 : NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL); //notify that reason is mouse up please.
1948 : }
1949 : }
1950 :
1951 : nsISelection*
1952 0 : nsFrameSelection::GetSelection(SelectionType aType) const
1953 : {
1954 0 : PRInt8 index = GetIndexFromSelectionType(aType);
1955 0 : if (index < 0)
1956 0 : return nsnull;
1957 :
1958 0 : return static_cast<nsISelection*>(mDomSelections[index]);
1959 : }
1960 :
1961 : nsresult
1962 0 : nsFrameSelection::ScrollSelectionIntoView(SelectionType aType,
1963 : SelectionRegion aRegion,
1964 : PRInt16 aFlags) const
1965 : {
1966 0 : PRInt8 index = GetIndexFromSelectionType(aType);
1967 0 : if (index < 0)
1968 0 : return NS_ERROR_INVALID_ARG;
1969 :
1970 0 : if (!mDomSelections[index])
1971 0 : return NS_ERROR_NULL_POINTER;
1972 :
1973 0 : PRInt16 verticalScroll = PRInt16(NS_PRESSHELL_SCROLL_ANYWHERE);
1974 0 : PRInt32 flags = nsTypedSelection::SCROLL_DO_FLUSH;
1975 0 : if (aFlags & nsISelectionController::SCROLL_SYNCHRONOUS) {
1976 0 : flags |= nsTypedSelection::SCROLL_SYNCHRONOUS;
1977 0 : } else if (aFlags & nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY) {
1978 0 : flags |= nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY;
1979 : }
1980 0 : if (aFlags & nsISelectionController::SCROLL_CENTER_VERTICALLY) {
1981 0 : verticalScroll = PRInt16(NS_PRESSHELL_SCROLL_CENTER);
1982 : }
1983 :
1984 : // After ScrollSelectionIntoView(), the pending notifications might be
1985 : // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
1986 0 : return mDomSelections[index]->ScrollIntoView(aRegion,
1987 : verticalScroll,
1988 : PRInt16(NS_PRESSHELL_SCROLL_ANYWHERE),
1989 0 : flags);
1990 : }
1991 :
1992 : nsresult
1993 0 : nsFrameSelection::RepaintSelection(SelectionType aType) const
1994 : {
1995 0 : PRInt8 index = GetIndexFromSelectionType(aType);
1996 0 : if (index < 0)
1997 0 : return NS_ERROR_INVALID_ARG;
1998 0 : if (!mDomSelections[index])
1999 0 : return NS_ERROR_NULL_POINTER;
2000 0 : NS_ENSURE_STATE(mShell);
2001 0 : return mDomSelections[index]->Repaint(mShell->GetPresContext());
2002 : }
2003 :
2004 : nsIFrame*
2005 0 : nsFrameSelection::GetFrameForNodeOffset(nsIContent *aNode,
2006 : PRInt32 aOffset,
2007 : HINT aHint,
2008 : PRInt32 *aReturnOffset) const
2009 : {
2010 0 : if (!aNode || !aReturnOffset || !mShell)
2011 0 : return nsnull;
2012 :
2013 0 : if (aOffset < 0)
2014 0 : return nsnull;
2015 :
2016 0 : *aReturnOffset = aOffset;
2017 :
2018 0 : nsCOMPtr<nsIContent> theNode = aNode;
2019 :
2020 0 : if (aNode->IsElement())
2021 : {
2022 0 : PRInt32 childIndex = 0;
2023 0 : PRInt32 numChildren = theNode->GetChildCount();
2024 :
2025 0 : if (aHint == HINTLEFT)
2026 : {
2027 0 : if (aOffset > 0)
2028 0 : childIndex = aOffset - 1;
2029 : else
2030 0 : childIndex = aOffset;
2031 : }
2032 : else // HINTRIGHT
2033 : {
2034 0 : if (aOffset >= numChildren)
2035 : {
2036 0 : if (numChildren > 0)
2037 0 : childIndex = numChildren - 1;
2038 : else
2039 0 : childIndex = 0;
2040 : }
2041 : else
2042 0 : childIndex = aOffset;
2043 : }
2044 :
2045 0 : if (childIndex > 0 || numChildren > 0) {
2046 0 : nsCOMPtr<nsIContent> childNode = theNode->GetChildAt(childIndex);
2047 :
2048 0 : if (!childNode)
2049 0 : return nsnull;
2050 :
2051 0 : theNode = childNode;
2052 : }
2053 :
2054 : #ifdef DONT_DO_THIS_YET
2055 : // XXX: We can't use this code yet because the hinting
2056 : // can cause us to attach to the wrong line frame.
2057 :
2058 : // Now that we have the child node, check if it too
2059 : // can contain children. If so, call this method again!
2060 :
2061 : if (theNode->IsElement())
2062 : {
2063 : PRInt32 newOffset = 0;
2064 :
2065 : if (aOffset > childIndex)
2066 : {
2067 : numChildren = theNode->GetChildCount();
2068 :
2069 : newOffset = numChildren;
2070 : }
2071 :
2072 : return GetFrameForNodeOffset(theNode, newOffset, aHint, aReturnOffset);
2073 : }
2074 : else
2075 : #endif // DONT_DO_THIS_YET
2076 : {
2077 : // Check to see if theNode is a text node. If it is, translate
2078 : // aOffset into an offset into the text node.
2079 :
2080 0 : nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(theNode);
2081 :
2082 0 : if (textNode)
2083 : {
2084 0 : if (theNode->GetPrimaryFrame())
2085 : {
2086 0 : if (aOffset > childIndex)
2087 : {
2088 0 : PRUint32 textLength = 0;
2089 :
2090 0 : nsresult rv = textNode->GetLength(&textLength);
2091 0 : if (NS_FAILED(rv))
2092 0 : return nsnull;
2093 :
2094 0 : *aReturnOffset = (PRInt32)textLength;
2095 : }
2096 : else
2097 0 : *aReturnOffset = 0;
2098 : }
2099 : else
2100 : {
2101 : // If we're at a collapsed whitespace content node (which
2102 : // does not have a primary frame), just use the original node
2103 : // to get the frame on which we should put the caret.
2104 0 : theNode = aNode;
2105 : }
2106 : }
2107 : }
2108 : }
2109 :
2110 0 : nsIFrame* returnFrame = theNode->GetPrimaryFrame();
2111 0 : if (!returnFrame)
2112 0 : return nsnull;
2113 :
2114 : // find the child frame containing the offset we want
2115 : returnFrame->GetChildFrameContainingOffset(*aReturnOffset, aHint,
2116 0 : &aOffset, &returnFrame);
2117 0 : return returnFrame;
2118 : }
2119 :
2120 : void
2121 0 : nsFrameSelection::CommonPageMove(bool aForward,
2122 : bool aExtend,
2123 : nsIScrollableFrame* aScrollableFrame)
2124 : {
2125 : // expected behavior for PageMove is to scroll AND move the caret
2126 : // and remain relative position of the caret in view. see Bug 4302.
2127 :
2128 : //get the frame from the scrollable view
2129 :
2130 0 : nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
2131 0 : if (!scrolledFrame)
2132 0 : return;
2133 :
2134 : // find out where the caret is.
2135 : // we should know mDesiredX value of nsFrameSelection, but I havent seen that behavior in other windows applications yet.
2136 0 : nsISelection* domSel = GetSelection(nsISelectionController::SELECTION_NORMAL);
2137 0 : if (!domSel)
2138 0 : return;
2139 :
2140 0 : nsRefPtr<nsCaret> caret = mShell->GetCaret();
2141 :
2142 0 : nsRect caretPos;
2143 0 : nsIFrame* caretFrame = caret->GetGeometry(domSel, &caretPos);
2144 0 : if (!caretFrame)
2145 : return;
2146 :
2147 : //need to adjust caret jump by percentage scroll
2148 0 : nsSize scrollDelta = aScrollableFrame->GetPageScrollAmount();
2149 :
2150 0 : if (aForward)
2151 0 : caretPos.y += scrollDelta.height;
2152 : else
2153 0 : caretPos.y -= scrollDelta.height;
2154 :
2155 0 : caretPos += caretFrame->GetOffsetTo(scrolledFrame);
2156 :
2157 : // get a content at desired location
2158 0 : nsPoint desiredPoint;
2159 0 : desiredPoint.x = caretPos.x;
2160 0 : desiredPoint.y = caretPos.y + caretPos.height/2;
2161 : nsIFrame::ContentOffsets offsets =
2162 0 : scrolledFrame->GetContentOffsetsFromPoint(desiredPoint);
2163 :
2164 0 : if (!offsets.content)
2165 : return;
2166 :
2167 : // scroll one page
2168 : aScrollableFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
2169 : nsIScrollableFrame::PAGES,
2170 0 : nsIScrollableFrame::SMOOTH);
2171 :
2172 : // place the caret
2173 : HandleClick(offsets.content, offsets.offset,
2174 0 : offsets.offset, aExtend, false, true);
2175 : }
2176 :
2177 : nsresult
2178 0 : nsFrameSelection::CharacterMove(bool aForward, bool aExtend)
2179 : {
2180 0 : if (aForward)
2181 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT, aExtend, eSelectCluster);
2182 : else
2183 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT, aExtend, eSelectCluster);
2184 : }
2185 :
2186 : nsresult
2187 0 : nsFrameSelection::CharacterExtendForDelete()
2188 : {
2189 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_DELETE, true, eSelectCluster);
2190 : }
2191 :
2192 : nsresult
2193 0 : nsFrameSelection::CharacterExtendForBackspace()
2194 : {
2195 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_BACK_SPACE, true, eSelectCharacter);
2196 : }
2197 :
2198 : nsresult
2199 0 : nsFrameSelection::WordMove(bool aForward, bool aExtend)
2200 : {
2201 0 : if (aForward)
2202 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT,aExtend,eSelectWord);
2203 : else
2204 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT,aExtend,eSelectWord);
2205 : }
2206 :
2207 : nsresult
2208 0 : nsFrameSelection::WordExtendForDelete(bool aForward)
2209 : {
2210 0 : if (aForward)
2211 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_DELETE, true, eSelectWord);
2212 : else
2213 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_BACK_SPACE, true, eSelectWord);
2214 : }
2215 :
2216 : nsresult
2217 0 : nsFrameSelection::LineMove(bool aForward, bool aExtend)
2218 : {
2219 0 : if (aForward)
2220 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_DOWN,aExtend,eSelectLine);
2221 : else
2222 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_UP,aExtend,eSelectLine);
2223 : }
2224 :
2225 : nsresult
2226 0 : nsFrameSelection::IntraLineMove(bool aForward, bool aExtend)
2227 : {
2228 0 : if (aForward)
2229 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_END,aExtend,eSelectLine);
2230 : else
2231 0 : return MoveCaret(nsIDOMKeyEvent::DOM_VK_HOME,aExtend,eSelectLine);
2232 : }
2233 :
2234 : nsresult
2235 0 : nsFrameSelection::SelectAll()
2236 : {
2237 0 : nsCOMPtr<nsIContent> rootContent;
2238 0 : if (mLimiter)
2239 : {
2240 0 : rootContent = mLimiter;//addrefit
2241 : }
2242 0 : else if (mAncestorLimiter) {
2243 0 : rootContent = mAncestorLimiter;
2244 : }
2245 : else
2246 : {
2247 0 : NS_ENSURE_STATE(mShell);
2248 0 : nsIDocument *doc = mShell->GetDocument();
2249 0 : if (!doc)
2250 0 : return NS_ERROR_FAILURE;
2251 0 : rootContent = doc->GetRootElement();
2252 0 : if (!rootContent)
2253 0 : return NS_ERROR_FAILURE;
2254 : }
2255 0 : PRInt32 numChildren = rootContent->GetChildCount();
2256 0 : PostReason(nsISelectionListener::NO_REASON);
2257 0 : return TakeFocus(rootContent, 0, numChildren, HINTLEFT, false, false);
2258 : }
2259 :
2260 : //////////END FRAMESELECTION
2261 :
2262 : void
2263 0 : nsFrameSelection::StartBatchChanges()
2264 : {
2265 0 : mBatching++;
2266 0 : }
2267 :
2268 : void
2269 0 : nsFrameSelection::EndBatchChanges()
2270 : {
2271 0 : mBatching--;
2272 0 : NS_ASSERTION(mBatching >=0,"Bad mBatching");
2273 0 : if (mBatching == 0 && mChangesDuringBatching){
2274 0 : mChangesDuringBatching = false;
2275 0 : NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
2276 : }
2277 0 : }
2278 :
2279 :
2280 : nsresult
2281 0 : nsFrameSelection::NotifySelectionListeners(SelectionType aType)
2282 : {
2283 0 : PRInt8 index = GetIndexFromSelectionType(aType);
2284 0 : if (index >=0 && mDomSelections[index])
2285 : {
2286 0 : return mDomSelections[index]->NotifySelectionListeners();
2287 : }
2288 0 : return NS_ERROR_FAILURE;
2289 : }
2290 :
2291 : // Start of Table Selection methods
2292 :
2293 0 : static bool IsCell(nsIContent *aContent)
2294 : {
2295 0 : return ((aContent->Tag() == nsGkAtoms::td ||
2296 0 : aContent->Tag() == nsGkAtoms::th) &&
2297 0 : aContent->IsHTML());
2298 : }
2299 :
2300 : nsITableCellLayout*
2301 0 : nsFrameSelection::GetCellLayout(nsIContent *aCellContent) const
2302 : {
2303 0 : NS_ENSURE_TRUE(mShell, nsnull);
2304 : nsITableCellLayout *cellLayoutObject =
2305 0 : do_QueryFrame(aCellContent->GetPrimaryFrame());
2306 0 : return cellLayoutObject;
2307 : }
2308 :
2309 : nsITableLayout*
2310 0 : nsFrameSelection::GetTableLayout(nsIContent *aTableContent) const
2311 : {
2312 0 : NS_ENSURE_TRUE(mShell, nsnull);
2313 : nsITableLayout *tableLayoutObject =
2314 0 : do_QueryFrame(aTableContent->GetPrimaryFrame());
2315 0 : return tableLayoutObject;
2316 : }
2317 :
2318 : nsresult
2319 0 : nsFrameSelection::ClearNormalSelection()
2320 : {
2321 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
2322 0 : if (!mDomSelections[index])
2323 0 : return NS_ERROR_NULL_POINTER;
2324 :
2325 0 : return mDomSelections[index]->RemoveAllRanges();
2326 : }
2327 :
2328 : static nsIContent*
2329 0 : GetFirstSelectedContent(nsRange* aRange)
2330 : {
2331 0 : if (!aRange) {
2332 0 : return nsnull;
2333 : }
2334 :
2335 0 : NS_PRECONDITION(aRange->GetStartParent(), "Must have start parent!");
2336 0 : NS_PRECONDITION(aRange->GetStartParent()->IsElement(),
2337 : "Unexpected parent");
2338 :
2339 0 : return aRange->GetStartParent()->GetChildAt(aRange->StartOffset());
2340 : }
2341 :
2342 : // Table selection support.
2343 : // TODO: Separate table methods into a separate nsITableSelection interface
2344 : nsresult
2345 0 : nsFrameSelection::HandleTableSelection(nsINode *aParentContent,
2346 : PRInt32 aContentOffset,
2347 : PRInt32 aTarget,
2348 : nsMouseEvent *aMouseEvent)
2349 : {
2350 0 : NS_ENSURE_TRUE(aParentContent, NS_ERROR_NULL_POINTER);
2351 0 : NS_ENSURE_TRUE(aMouseEvent, NS_ERROR_NULL_POINTER);
2352 :
2353 0 : if (mMouseDownState && mDragSelectingCells && (aTarget & nsISelectionPrivate::TABLESELECTION_TABLE))
2354 : {
2355 : // We were selecting cells and user drags mouse in table border or inbetween cells,
2356 : // just do nothing
2357 0 : return NS_OK;
2358 : }
2359 :
2360 0 : nsresult result = NS_OK;
2361 :
2362 0 : nsIContent *childContent = aParentContent->GetChildAt(aContentOffset);
2363 :
2364 : // When doing table selection, always set the direction to next so
2365 : // we can be sure that anchorNode's offset always points to the
2366 : // selected cell
2367 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
2368 0 : if (!mDomSelections[index])
2369 0 : return NS_ERROR_NULL_POINTER;
2370 :
2371 0 : mDomSelections[index]->SetDirection(eDirNext);
2372 :
2373 : // Stack-class to wrap all table selection changes in
2374 : // BeginBatchChanges() / EndBatchChanges()
2375 0 : nsSelectionBatcher selectionBatcher(mDomSelections[index]);
2376 :
2377 : PRInt32 startRowIndex, startColIndex, curRowIndex, curColIndex;
2378 0 : if (mMouseDownState && mDragSelectingCells)
2379 : {
2380 : // We are drag-selecting
2381 0 : if (aTarget != nsISelectionPrivate::TABLESELECTION_TABLE)
2382 : {
2383 : // If dragging in the same cell as last event, do nothing
2384 0 : if (mEndSelectedCell == childContent)
2385 0 : return NS_OK;
2386 :
2387 : #ifdef DEBUG_TABLE_SELECTION
2388 : printf(" mStartSelectedCell = %x, mEndSelectedCell = %x, childContent = %x \n", mStartSelectedCell, mEndSelectedCell, childContent);
2389 : #endif
2390 : // aTarget can be any "cell mode",
2391 : // so we can easily drag-select rows and columns
2392 : // Once we are in row or column mode,
2393 : // we can drift into any cell to stay in that mode
2394 : // even if aTarget = TABLESELECTION_CELL
2395 :
2396 0 : if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW ||
2397 : mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN)
2398 : {
2399 0 : if (mEndSelectedCell)
2400 : {
2401 : // Also check if cell is in same row/col
2402 0 : result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex);
2403 0 : if (NS_FAILED(result)) return result;
2404 0 : result = GetCellIndexes(childContent, curRowIndex, curColIndex);
2405 0 : if (NS_FAILED(result)) return result;
2406 :
2407 : #ifdef DEBUG_TABLE_SELECTION
2408 : printf(" curRowIndex = %d, startRowIndex = %d, curColIndex = %d, startColIndex = %d\n", curRowIndex, startRowIndex, curColIndex, startColIndex);
2409 : #endif
2410 0 : if ((mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
2411 : (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN && startColIndex == curColIndex))
2412 0 : return NS_OK;
2413 : }
2414 : #ifdef DEBUG_TABLE_SELECTION
2415 : printf(" Dragged into a new column or row\n");
2416 : #endif
2417 : // Continue dragging row or column selection
2418 0 : return SelectRowOrColumn(childContent, mSelectingTableCellMode);
2419 : }
2420 0 : else if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_CELL)
2421 : {
2422 : #ifdef DEBUG_TABLE_SELECTION
2423 : printf("HandleTableSelection: Dragged into a new cell\n");
2424 : #endif
2425 : // Trick for quick selection of rows and columns
2426 : // Hold down shift, then start selecting in one direction
2427 : // If next cell dragged into is in same row, select entire row,
2428 : // if next cell is in same column, select entire column
2429 0 : if (mStartSelectedCell && aMouseEvent->isShift)
2430 : {
2431 0 : result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
2432 0 : if (NS_FAILED(result)) return result;
2433 0 : result = GetCellIndexes(childContent, curRowIndex, curColIndex);
2434 0 : if (NS_FAILED(result)) return result;
2435 :
2436 0 : if (startRowIndex == curRowIndex ||
2437 : startColIndex == curColIndex)
2438 : {
2439 : // Force new selection block
2440 0 : mStartSelectedCell = nsnull;
2441 0 : mDomSelections[index]->RemoveAllRanges();
2442 :
2443 0 : if (startRowIndex == curRowIndex)
2444 0 : mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_ROW;
2445 : else
2446 0 : mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_COLUMN;
2447 :
2448 0 : return SelectRowOrColumn(childContent, mSelectingTableCellMode);
2449 : }
2450 : }
2451 :
2452 : // Reselect block of cells to new end location
2453 0 : return SelectBlockOfCells(mStartSelectedCell, childContent);
2454 : }
2455 : }
2456 : // Do nothing if dragging in table, but outside a cell
2457 0 : return NS_OK;
2458 : }
2459 : else
2460 : {
2461 : // Not dragging -- mouse event is down or up
2462 0 : if (mMouseDownState)
2463 : {
2464 : #ifdef DEBUG_TABLE_SELECTION
2465 : printf("HandleTableSelection: Mouse down event\n");
2466 : #endif
2467 : // Clear cell we stored in mouse-down
2468 0 : mUnselectCellOnMouseUp = nsnull;
2469 :
2470 0 : if (aTarget == nsISelectionPrivate::TABLESELECTION_CELL)
2471 : {
2472 0 : bool isSelected = false;
2473 :
2474 : // Check if we have other selected cells
2475 : nsIContent* previousCellNode =
2476 0 : GetFirstSelectedContent(GetFirstCellRange());
2477 0 : if (previousCellNode)
2478 : {
2479 : // We have at least 1 other selected cell
2480 :
2481 : // Check if new cell is already selected
2482 0 : nsIFrame *cellFrame = childContent->GetPrimaryFrame();
2483 0 : if (!cellFrame) return NS_ERROR_NULL_POINTER;
2484 0 : isSelected = cellFrame->IsSelected();
2485 : }
2486 : else
2487 : {
2488 : // No cells selected -- remove non-cell selection
2489 0 : mDomSelections[index]->RemoveAllRanges();
2490 : }
2491 0 : mDragSelectingCells = true; // Signal to start drag-cell-selection
2492 0 : mSelectingTableCellMode = aTarget;
2493 : // Set start for new drag-selection block (not appended)
2494 0 : mStartSelectedCell = childContent;
2495 : // The initial block end is same as the start
2496 0 : mEndSelectedCell = childContent;
2497 :
2498 0 : if (isSelected)
2499 : {
2500 : // Remember this cell to (possibly) unselect it on mouseup
2501 0 : mUnselectCellOnMouseUp = childContent;
2502 : #ifdef DEBUG_TABLE_SELECTION
2503 : printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
2504 : #endif
2505 : }
2506 : else
2507 : {
2508 : // Select an unselected cell
2509 : // but first remove existing selection if not in same table
2510 0 : if (previousCellNode &&
2511 0 : !IsInSameTable(previousCellNode, childContent))
2512 : {
2513 0 : mDomSelections[index]->RemoveAllRanges();
2514 : // Reset selection mode that is cleared in RemoveAllRanges
2515 0 : mSelectingTableCellMode = aTarget;
2516 : }
2517 :
2518 0 : return SelectCellElement(childContent);
2519 : }
2520 :
2521 0 : return NS_OK;
2522 : }
2523 0 : else if (aTarget == nsISelectionPrivate::TABLESELECTION_TABLE)
2524 : {
2525 : //TODO: We currently select entire table when clicked between cells,
2526 : // should we restrict to only around border?
2527 : // *** How do we get location data for cell and click?
2528 0 : mDragSelectingCells = false;
2529 0 : mStartSelectedCell = nsnull;
2530 0 : mEndSelectedCell = nsnull;
2531 :
2532 : // Remove existing selection and select the table
2533 0 : mDomSelections[index]->RemoveAllRanges();
2534 0 : return CreateAndAddRange(aParentContent, aContentOffset);
2535 : }
2536 0 : else if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW || aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
2537 : {
2538 : #ifdef DEBUG_TABLE_SELECTION
2539 : printf("aTarget == %d\n", aTarget);
2540 : #endif
2541 :
2542 : // Start drag-selecting mode so multiple rows/cols can be selected
2543 : // Note: Currently, nsFrame::GetDataForTableSelection
2544 : // will never call us for row or column selection on mouse down
2545 0 : mDragSelectingCells = true;
2546 :
2547 : // Force new selection block
2548 0 : mStartSelectedCell = nsnull;
2549 0 : mDomSelections[index]->RemoveAllRanges();
2550 : // Always do this AFTER RemoveAllRanges
2551 0 : mSelectingTableCellMode = aTarget;
2552 0 : return SelectRowOrColumn(childContent, aTarget);
2553 : }
2554 : }
2555 : else
2556 : {
2557 : #ifdef DEBUG_TABLE_SELECTION
2558 : printf("HandleTableSelection: Mouse UP event. mDragSelectingCells=%d, mStartSelectedCell=%d\n", mDragSelectingCells, mStartSelectedCell);
2559 : #endif
2560 : // First check if we are extending a block selection
2561 : PRInt32 rangeCount;
2562 0 : result = mDomSelections[index]->GetRangeCount(&rangeCount);
2563 0 : if (NS_FAILED(result))
2564 0 : return result;
2565 :
2566 0 : if (rangeCount > 0 && aMouseEvent->isShift &&
2567 0 : mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
2568 : {
2569 : // Shift key is down: append a block selection
2570 0 : mDragSelectingCells = false;
2571 0 : return SelectBlockOfCells(mAppendStartSelectedCell, childContent);
2572 : }
2573 :
2574 0 : if (mDragSelectingCells)
2575 0 : mAppendStartSelectedCell = mStartSelectedCell;
2576 :
2577 0 : mDragSelectingCells = false;
2578 0 : mStartSelectedCell = nsnull;
2579 0 : mEndSelectedCell = nsnull;
2580 :
2581 : // Any other mouseup actions require that Ctrl or Cmd key is pressed
2582 : // else stop table selection mode
2583 0 : bool doMouseUpAction = false;
2584 : #ifdef XP_MACOSX
2585 : doMouseUpAction = aMouseEvent->isMeta;
2586 : #else
2587 0 : doMouseUpAction = aMouseEvent->isControl;
2588 : #endif
2589 0 : if (!doMouseUpAction)
2590 : {
2591 : #ifdef DEBUG_TABLE_SELECTION
2592 : printf("HandleTableSelection: Ending cell selection on mouseup: mAppendStartSelectedCell=%d\n", mAppendStartSelectedCell);
2593 : #endif
2594 0 : return NS_OK;
2595 : }
2596 : // Unselect a cell only if it wasn't
2597 : // just selected on mousedown
2598 0 : if( childContent == mUnselectCellOnMouseUp)
2599 : {
2600 : // Scan ranges to find the cell to unselect (the selection range to remove)
2601 : // XXXbz it's really weird that this lives outside the loop, so once we
2602 : // find one we keep looking at it even if we find no more cells...
2603 0 : nsINode* previousCellParent = nsnull;
2604 : #ifdef DEBUG_TABLE_SELECTION
2605 : printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
2606 : #endif
2607 0 : for( PRInt32 i = 0; i < rangeCount; i++)
2608 : {
2609 : // Strong reference, because sometimes we want to remove
2610 : // this range, and then we might be the only owner.
2611 0 : nsRefPtr<nsRange> range = mDomSelections[index]->GetRangeAt(i);
2612 0 : if (!range) return NS_ERROR_NULL_POINTER;
2613 :
2614 0 : nsINode* parent = range->GetStartParent();
2615 0 : if (!parent) return NS_ERROR_NULL_POINTER;
2616 :
2617 0 : PRInt32 offset = range->StartOffset();
2618 : // Be sure previous selection is a table cell
2619 0 : nsIContent* child = parent->GetChildAt(offset);
2620 0 : if (child && IsCell(child))
2621 0 : previousCellParent = parent;
2622 :
2623 : // We're done if we didn't find parent of a previously-selected cell
2624 0 : if (!previousCellParent) break;
2625 :
2626 0 : if (previousCellParent == aParentContent && offset == aContentOffset)
2627 : {
2628 : // Cell is already selected
2629 0 : if (rangeCount == 1)
2630 : {
2631 : #ifdef DEBUG_TABLE_SELECTION
2632 : printf("HandleTableSelection: Unselecting single selected cell\n");
2633 : #endif
2634 : // This was the only cell selected.
2635 : // Collapse to "normal" selection inside the cell
2636 0 : mStartSelectedCell = nsnull;
2637 0 : mEndSelectedCell = nsnull;
2638 0 : mAppendStartSelectedCell = nsnull;
2639 : //TODO: We need a "Collapse to just before deepest child" routine
2640 : // Even better, should we collapse to just after the LAST deepest child
2641 : // (i.e., at the end of the cell's contents)?
2642 0 : return mDomSelections[index]->Collapse(childContent, 0);
2643 : }
2644 : #ifdef DEBUG_TABLE_SELECTION
2645 : printf("HandleTableSelection: Removing cell from multi-cell selection\n");
2646 : #endif
2647 : // Unselecting the start of previous block
2648 : // XXX What do we use now!
2649 0 : if (childContent == mAppendStartSelectedCell)
2650 0 : mAppendStartSelectedCell = nsnull;
2651 :
2652 : // Deselect cell by removing its range from selection
2653 0 : return mDomSelections[index]->RemoveRange(range);
2654 : }
2655 : }
2656 0 : mUnselectCellOnMouseUp = nsnull;
2657 : }
2658 : }
2659 : }
2660 0 : return result;
2661 : }
2662 :
2663 : nsresult
2664 0 : nsFrameSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCell)
2665 : {
2666 0 : NS_ENSURE_TRUE(aStartCell, NS_ERROR_NULL_POINTER);
2667 0 : NS_ENSURE_TRUE(aEndCell, NS_ERROR_NULL_POINTER);
2668 0 : mEndSelectedCell = aEndCell;
2669 :
2670 0 : nsCOMPtr<nsIContent> startCell;
2671 0 : nsresult result = NS_OK;
2672 :
2673 : // If new end cell is in a different table, do nothing
2674 0 : nsIContent* table = IsInSameTable(aStartCell, aEndCell);
2675 0 : if (!table) {
2676 0 : return NS_OK;
2677 : }
2678 :
2679 : // Get starting and ending cells' location in the cellmap
2680 : PRInt32 startRowIndex, startColIndex, endRowIndex, endColIndex;
2681 0 : result = GetCellIndexes(aStartCell, startRowIndex, startColIndex);
2682 0 : if(NS_FAILED(result)) return result;
2683 0 : result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
2684 0 : if(NS_FAILED(result)) return result;
2685 :
2686 0 : if (mDragSelectingCells)
2687 : {
2688 : // Drag selecting: remove selected cells outside of new block limits
2689 : UnselectCells(table, startRowIndex, startColIndex, endRowIndex, endColIndex,
2690 0 : true);
2691 : }
2692 :
2693 : // Note that we select block in the direction of user's mouse dragging,
2694 : // which means start cell may be after the end cell in either row or column
2695 : return AddCellsToSelection(table, startRowIndex, startColIndex,
2696 0 : endRowIndex, endColIndex);
2697 : }
2698 :
2699 : nsresult
2700 0 : nsFrameSelection::UnselectCells(nsIContent *aTableContent,
2701 : PRInt32 aStartRowIndex,
2702 : PRInt32 aStartColumnIndex,
2703 : PRInt32 aEndRowIndex,
2704 : PRInt32 aEndColumnIndex,
2705 : bool aRemoveOutsideOfCellRange)
2706 : {
2707 : PRInt8 index =
2708 0 : GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
2709 0 : if (!mDomSelections[index])
2710 0 : return NS_ERROR_NULL_POINTER;
2711 :
2712 0 : nsITableLayout *tableLayout = GetTableLayout(aTableContent);
2713 0 : if (!tableLayout)
2714 0 : return NS_ERROR_FAILURE;
2715 :
2716 0 : PRInt32 minRowIndex = NS_MIN(aStartRowIndex, aEndRowIndex);
2717 0 : PRInt32 maxRowIndex = NS_MAX(aStartRowIndex, aEndRowIndex);
2718 0 : PRInt32 minColIndex = NS_MIN(aStartColumnIndex, aEndColumnIndex);
2719 0 : PRInt32 maxColIndex = NS_MAX(aStartColumnIndex, aEndColumnIndex);
2720 :
2721 : // Strong reference because we sometimes remove the range
2722 0 : nsRefPtr<nsRange> range = GetFirstCellRange();
2723 0 : nsIContent* cellNode = GetFirstSelectedContent(range);
2724 0 : NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
2725 :
2726 : PRInt32 curRowIndex, curColIndex;
2727 0 : while (cellNode)
2728 : {
2729 0 : nsresult result = GetCellIndexes(cellNode, curRowIndex, curColIndex);
2730 0 : if (NS_FAILED(result))
2731 0 : return result;
2732 :
2733 : #ifdef DEBUG_TABLE_SELECTION
2734 : if (!range)
2735 : printf("RemoveCellsToSelection -- range is null\n");
2736 : #endif
2737 :
2738 0 : if (range) {
2739 0 : if (aRemoveOutsideOfCellRange) {
2740 0 : if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex ||
2741 : curColIndex < minColIndex || curColIndex > maxColIndex) {
2742 :
2743 0 : mDomSelections[index]->RemoveRange(range);
2744 : // Since we've removed the range, decrement pointer to next range
2745 0 : mSelectedCellIndex--;
2746 : }
2747 :
2748 : } else {
2749 : // Remove cell from selection if it belongs to the given cells range or
2750 : // it is spanned onto the cells range.
2751 0 : nsCOMPtr<nsIDOMElement> cellElement;
2752 : PRInt32 origRowIndex, origColIndex, rowSpan, colSpan,
2753 : actualRowSpan, actualColSpan;
2754 : bool isSelected;
2755 :
2756 : result = tableLayout->GetCellDataAt(curRowIndex, curColIndex,
2757 0 : *getter_AddRefs(cellElement),
2758 : origRowIndex, origColIndex,
2759 : rowSpan, colSpan,
2760 : actualRowSpan, actualColSpan,
2761 0 : isSelected);
2762 0 : if (NS_FAILED(result))
2763 0 : return result;
2764 :
2765 0 : if (origRowIndex <= maxRowIndex &&
2766 : origRowIndex + actualRowSpan - 1 >= minRowIndex &&
2767 : origColIndex <= maxColIndex &&
2768 : origColIndex + actualColSpan - 1 >= minColIndex) {
2769 :
2770 0 : mDomSelections[index]->RemoveRange(range);
2771 : // Since we've removed the range, decrement pointer to next range
2772 0 : mSelectedCellIndex--;
2773 : }
2774 : }
2775 : }
2776 :
2777 0 : range = GetNextCellRange();
2778 0 : cellNode = GetFirstSelectedContent(range);
2779 0 : NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
2780 : }
2781 :
2782 0 : return NS_OK;
2783 : }
2784 :
2785 : nsresult
2786 0 : nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent,
2787 : PRInt32 aStartRowIndex,
2788 : PRInt32 aStartColumnIndex,
2789 : PRInt32 aEndRowIndex,
2790 : PRInt32 aEndColumnIndex)
2791 : {
2792 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
2793 0 : if (!mDomSelections[index])
2794 0 : return NS_ERROR_NULL_POINTER;
2795 :
2796 : // Get TableLayout interface to access cell data based on cellmap location
2797 : // frames are not ref counted, so don't use an nsCOMPtr
2798 0 : nsITableLayout *tableLayoutObject = GetTableLayout(aTableContent);
2799 0 : if (!tableLayoutObject) // Check that |table| is a table.
2800 0 : return NS_ERROR_FAILURE;
2801 :
2802 0 : nsCOMPtr<nsIDOMElement> cellElement;
2803 : PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan,
2804 : curRowIndex, curColIndex;
2805 : bool isSelected;
2806 0 : nsresult result = NS_OK;
2807 :
2808 0 : PRInt32 row = aStartRowIndex;
2809 0 : while(true)
2810 : {
2811 0 : PRInt32 col = aStartColumnIndex;
2812 0 : while(true)
2813 : {
2814 0 : result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cellElement),
2815 : curRowIndex, curColIndex, rowSpan, colSpan,
2816 0 : actualRowSpan, actualColSpan, isSelected);
2817 0 : if (NS_FAILED(result)) return result;
2818 :
2819 0 : NS_ASSERTION(actualColSpan, "!actualColSpan is 0!");
2820 :
2821 : // Skip cells that are spanned from previous locations or are already selected
2822 0 : if (!isSelected && cellElement && row == curRowIndex && col == curColIndex)
2823 : {
2824 0 : nsCOMPtr<nsIContent> cellContent = do_QueryInterface(cellElement);
2825 0 : result = SelectCellElement(cellContent);
2826 0 : if (NS_FAILED(result)) return result;
2827 : }
2828 : // Done when we reach end column
2829 0 : if (col == aEndColumnIndex) break;
2830 :
2831 0 : if (aStartColumnIndex < aEndColumnIndex)
2832 0 : col ++;
2833 : else
2834 0 : col--;
2835 : };
2836 0 : if (row == aEndRowIndex) break;
2837 :
2838 0 : if (aStartRowIndex < aEndRowIndex)
2839 0 : row++;
2840 : else
2841 0 : row--;
2842 : };
2843 0 : return result;
2844 : }
2845 :
2846 : nsresult
2847 0 : nsFrameSelection::RemoveCellsFromSelection(nsIContent *aTable,
2848 : PRInt32 aStartRowIndex,
2849 : PRInt32 aStartColumnIndex,
2850 : PRInt32 aEndRowIndex,
2851 : PRInt32 aEndColumnIndex)
2852 : {
2853 : return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
2854 0 : aEndRowIndex, aEndColumnIndex, false);
2855 : }
2856 :
2857 : nsresult
2858 0 : nsFrameSelection::RestrictCellsToSelection(nsIContent *aTable,
2859 : PRInt32 aStartRowIndex,
2860 : PRInt32 aStartColumnIndex,
2861 : PRInt32 aEndRowIndex,
2862 : PRInt32 aEndColumnIndex)
2863 : {
2864 : return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
2865 0 : aEndRowIndex, aEndColumnIndex, true);
2866 : }
2867 :
2868 : nsresult
2869 0 : nsFrameSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
2870 : {
2871 0 : if (!aCellContent) return NS_ERROR_NULL_POINTER;
2872 :
2873 0 : nsIContent* table = GetParentTable(aCellContent);
2874 0 : if (!table) return NS_ERROR_NULL_POINTER;
2875 :
2876 : // Get table and cell layout interfaces to access
2877 : // cell data based on cellmap location
2878 : // Frames are not ref counted, so don't use an nsCOMPtr
2879 0 : nsITableLayout *tableLayout = GetTableLayout(table);
2880 0 : if (!tableLayout) return NS_ERROR_FAILURE;
2881 0 : nsITableCellLayout *cellLayout = GetCellLayout(aCellContent);
2882 0 : if (!cellLayout) return NS_ERROR_FAILURE;
2883 :
2884 : // Get location of target cell:
2885 : PRInt32 rowIndex, colIndex, curRowIndex, curColIndex;
2886 0 : nsresult result = cellLayout->GetCellIndexes(rowIndex, colIndex);
2887 0 : if (NS_FAILED(result)) return result;
2888 :
2889 : // Be sure we start at proper beginning
2890 : // (This allows us to select row or col given ANY cell!)
2891 0 : if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
2892 0 : colIndex = 0;
2893 0 : if (aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
2894 0 : rowIndex = 0;
2895 :
2896 0 : nsCOMPtr<nsIDOMElement> cellElement;
2897 0 : nsCOMPtr<nsIContent> firstCell;
2898 0 : nsCOMPtr<nsIDOMElement> lastCell;
2899 : PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
2900 : bool isSelected;
2901 :
2902 0 : do {
2903 : // Loop through all cells in column or row to find first and last
2904 0 : result = tableLayout->GetCellDataAt(rowIndex, colIndex, *getter_AddRefs(cellElement),
2905 : curRowIndex, curColIndex, rowSpan, colSpan,
2906 0 : actualRowSpan, actualColSpan, isSelected);
2907 0 : if (NS_FAILED(result)) return result;
2908 0 : if (cellElement)
2909 : {
2910 0 : NS_ASSERTION(actualRowSpan > 0 && actualColSpan> 0, "SelectRowOrColumn: Bad rowspan or colspan\n");
2911 0 : if (!firstCell)
2912 0 : firstCell = do_QueryInterface(cellElement);
2913 :
2914 0 : lastCell = cellElement;
2915 :
2916 : // Move to next cell in cellmap, skipping spanned locations
2917 0 : if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
2918 0 : colIndex += actualColSpan;
2919 : else
2920 0 : rowIndex += actualRowSpan;
2921 : }
2922 : }
2923 0 : while (cellElement);
2924 :
2925 : // Use SelectBlockOfCells:
2926 : // This will replace existing selection,
2927 : // but allow unselecting by dragging out of selected region
2928 0 : if (firstCell && lastCell)
2929 : {
2930 0 : if (!mStartSelectedCell)
2931 : {
2932 : // We are starting a new block, so select the first cell
2933 0 : result = SelectCellElement(firstCell);
2934 0 : if (NS_FAILED(result)) return result;
2935 0 : mStartSelectedCell = firstCell;
2936 : }
2937 0 : nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
2938 0 : result = SelectBlockOfCells(mStartSelectedCell, lastCellContent);
2939 :
2940 : // This gets set to the cell at end of row/col,
2941 : // but we need it to be the cell under cursor
2942 0 : mEndSelectedCell = aCellContent;
2943 0 : return result;
2944 : }
2945 :
2946 : #if 0
2947 : // This is a more efficient strategy that appends row to current selection,
2948 : // but doesn't allow dragging OFF of an existing selection to unselect!
2949 : do {
2950 : // Loop through all cells in column or row
2951 : result = tableLayout->GetCellDataAt(rowIndex, colIndex,
2952 : getter_AddRefs(cellElement),
2953 : curRowIndex, curColIndex,
2954 : rowSpan, colSpan,
2955 : actualRowSpan, actualColSpan,
2956 : isSelected);
2957 : if (NS_FAILED(result)) return result;
2958 : // We're done when cell is not found
2959 : if (!cellElement) break;
2960 :
2961 :
2962 : // Check spans else we infinitely loop
2963 : NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
2964 : NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
2965 :
2966 : // Skip cells that are already selected or span from outside our region
2967 : if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
2968 : {
2969 : result = SelectCellElement(cellElement);
2970 : if (NS_FAILED(result)) return result;
2971 : }
2972 : // Move to next row or column in cellmap, skipping spanned locations
2973 : if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
2974 : colIndex += actualColSpan;
2975 : else
2976 : rowIndex += actualRowSpan;
2977 : }
2978 : while (cellElement);
2979 : #endif
2980 :
2981 0 : return NS_OK;
2982 : }
2983 :
2984 : nsIContent*
2985 0 : nsFrameSelection::GetFirstCellNodeInRange(nsRange *aRange) const
2986 : {
2987 0 : if (!aRange) return nsnull;
2988 :
2989 0 : nsINode* startParent = aRange->GetStartParent();
2990 0 : if (!startParent)
2991 0 : return nsnull;
2992 :
2993 0 : PRInt32 offset = aRange->StartOffset();
2994 :
2995 0 : nsIContent* childContent = startParent->GetChildAt(offset);
2996 0 : if (!childContent)
2997 0 : return nsnull;
2998 : // Don't return node if not a cell
2999 0 : if (!IsCell(childContent))
3000 0 : return nsnull;
3001 :
3002 0 : return childContent;
3003 : }
3004 :
3005 : nsRange*
3006 0 : nsFrameSelection::GetFirstCellRange()
3007 : {
3008 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
3009 0 : if (!mDomSelections[index])
3010 0 : return nsnull;
3011 :
3012 0 : nsRange* firstRange = mDomSelections[index]->GetRangeAt(0);
3013 0 : if (!GetFirstCellNodeInRange(firstRange)) {
3014 0 : return nsnull;
3015 : }
3016 :
3017 : // Setup for next cell
3018 0 : mSelectedCellIndex = 1;
3019 :
3020 0 : return firstRange;
3021 : }
3022 :
3023 : nsRange*
3024 0 : nsFrameSelection::GetNextCellRange()
3025 : {
3026 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
3027 0 : if (!mDomSelections[index])
3028 0 : return nsnull;
3029 :
3030 0 : nsRange* range = mDomSelections[index]->GetRangeAt(mSelectedCellIndex);
3031 :
3032 : // Get first node in next range of selection - test if it's a cell
3033 0 : if (!GetFirstCellNodeInRange(range)) {
3034 0 : return nsnull;
3035 : }
3036 :
3037 : // Setup for next cell
3038 0 : mSelectedCellIndex++;
3039 :
3040 0 : return range;
3041 : }
3042 :
3043 : nsresult
3044 0 : nsFrameSelection::GetCellIndexes(nsIContent *aCell,
3045 : PRInt32 &aRowIndex,
3046 : PRInt32 &aColIndex)
3047 : {
3048 0 : if (!aCell) return NS_ERROR_NULL_POINTER;
3049 :
3050 0 : aColIndex=0; // initialize out params
3051 0 : aRowIndex=0;
3052 :
3053 0 : nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
3054 0 : if (!cellLayoutObject) return NS_ERROR_FAILURE;
3055 0 : return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
3056 : }
3057 :
3058 : nsIContent*
3059 0 : nsFrameSelection::IsInSameTable(nsIContent *aContent1,
3060 : nsIContent *aContent2) const
3061 : {
3062 0 : if (!aContent1 || !aContent2) return nsnull;
3063 :
3064 0 : nsIContent* tableNode1 = GetParentTable(aContent1);
3065 0 : nsIContent* tableNode2 = GetParentTable(aContent2);
3066 :
3067 : // Must be in the same table. Note that we want to return false for
3068 : // the test if both tables are null.
3069 0 : return (tableNode1 == tableNode2) ? tableNode1 : nsnull;
3070 : }
3071 :
3072 : nsIContent*
3073 0 : nsFrameSelection::GetParentTable(nsIContent *aCell) const
3074 : {
3075 0 : if (!aCell) {
3076 0 : return nsnull;
3077 : }
3078 :
3079 0 : for (nsIContent* parent = aCell->GetParent(); parent;
3080 0 : parent = parent->GetParent()) {
3081 0 : if (parent->Tag() == nsGkAtoms::table &&
3082 0 : parent->IsHTML()) {
3083 0 : return parent;
3084 : }
3085 : }
3086 :
3087 0 : return nsnull;
3088 : }
3089 :
3090 : nsresult
3091 0 : nsFrameSelection::SelectCellElement(nsIContent *aCellElement)
3092 : {
3093 0 : nsIContent *parent = aCellElement->GetParent();
3094 :
3095 : // Get child offset
3096 0 : PRInt32 offset = parent->IndexOf(aCellElement);
3097 :
3098 0 : return CreateAndAddRange(parent, offset);
3099 : }
3100 :
3101 : nsresult
3102 0 : nsTypedSelection::getTableCellLocationFromRange(nsRange *aRange, PRInt32 *aSelectionType, PRInt32 *aRow, PRInt32 *aCol)
3103 : {
3104 0 : if (!aRange || !aSelectionType || !aRow || !aCol)
3105 0 : return NS_ERROR_NULL_POINTER;
3106 :
3107 0 : *aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
3108 0 : *aRow = 0;
3109 0 : *aCol = 0;
3110 :
3111 : // Must have access to frame selection to get cell info
3112 0 : if (!mFrameSelection) return NS_OK;
3113 :
3114 0 : nsresult result = GetTableSelectionType(aRange, aSelectionType);
3115 0 : if (NS_FAILED(result)) return result;
3116 :
3117 : // Don't fail if range does not point to a single table cell,
3118 : // let aSelectionType tell user if we don't have a cell
3119 0 : if (*aSelectionType != nsISelectionPrivate::TABLESELECTION_CELL)
3120 0 : return NS_OK;
3121 :
3122 : // Get the child content (the cell) pointed to by starting node of range
3123 : // We do minimal checking since GetTableSelectionType assures
3124 : // us that this really is a table cell
3125 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
3126 0 : if (!content)
3127 0 : return NS_ERROR_FAILURE;
3128 :
3129 0 : nsIContent *child = content->GetChildAt(aRange->StartOffset());
3130 0 : if (!child)
3131 0 : return NS_ERROR_FAILURE;
3132 :
3133 : //Note: This is a non-ref-counted pointer to the frame
3134 0 : nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
3135 0 : if (NS_FAILED(result))
3136 0 : return result;
3137 0 : if (!cellLayout)
3138 0 : return NS_ERROR_FAILURE;
3139 :
3140 0 : return cellLayout->GetCellIndexes(*aRow, *aCol);
3141 : }
3142 :
3143 : nsresult
3144 0 : nsTypedSelection::addTableCellRange(nsRange *aRange, bool *aDidAddRange,
3145 : PRInt32 *aOutIndex)
3146 : {
3147 0 : if (!aDidAddRange || !aOutIndex)
3148 0 : return NS_ERROR_NULL_POINTER;
3149 :
3150 0 : *aDidAddRange = false;
3151 0 : *aOutIndex = -1;
3152 :
3153 0 : if (!mFrameSelection)
3154 0 : return NS_OK;
3155 :
3156 0 : if (!aRange)
3157 0 : return NS_ERROR_NULL_POINTER;
3158 :
3159 : nsresult result;
3160 :
3161 : // Get if we are adding a cell selection and the row, col of cell if we are
3162 : PRInt32 newRow, newCol, tableMode;
3163 0 : result = getTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
3164 0 : if (NS_FAILED(result)) return result;
3165 :
3166 : // If not adding a cell range, we are done here
3167 0 : if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL)
3168 : {
3169 0 : mFrameSelection->mSelectingTableCellMode = tableMode;
3170 : // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
3171 0 : return NS_OK;
3172 : }
3173 :
3174 : // Set frame selection mode only if not already set to a table mode
3175 : // so we don't lose the select row and column flags (not detected by getTableCellLocation)
3176 0 : if (mFrameSelection->mSelectingTableCellMode == TABLESELECTION_NONE)
3177 0 : mFrameSelection->mSelectingTableCellMode = tableMode;
3178 :
3179 0 : *aDidAddRange = true;
3180 0 : return AddItem(aRange, aOutIndex);
3181 : }
3182 :
3183 : //TODO: Figure out TABLESELECTION_COLUMN and TABLESELECTION_ALLCELLS
3184 : nsresult
3185 0 : nsTypedSelection::GetTableSelectionType(nsIDOMRange* aDOMRange,
3186 : PRInt32* aTableSelectionType)
3187 : {
3188 0 : if (!aDOMRange || !aTableSelectionType)
3189 0 : return NS_ERROR_NULL_POINTER;
3190 0 : nsRange* range = static_cast<nsRange*>(aDOMRange);
3191 :
3192 0 : *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
3193 :
3194 : // Must have access to frame selection to get cell info
3195 0 : if(!mFrameSelection) return NS_OK;
3196 :
3197 0 : nsINode* startNode = range->GetStartParent();
3198 0 : if (!startNode) return NS_ERROR_FAILURE;
3199 :
3200 0 : nsINode* endNode = range->GetEndParent();
3201 0 : if (!endNode) return NS_ERROR_FAILURE;
3202 :
3203 : // Not a single selected node
3204 0 : if (startNode != endNode) return NS_OK;
3205 :
3206 0 : PRInt32 startOffset = range->StartOffset();
3207 0 : PRInt32 endOffset = range->EndOffset();
3208 :
3209 : // Not a single selected node
3210 0 : if ((endOffset - startOffset) != 1)
3211 0 : return NS_OK;
3212 :
3213 0 : nsIContent* startContent = static_cast<nsIContent*>(startNode);
3214 0 : if (!(startNode->IsElement() && startContent->IsHTML())) {
3215 : // Implies a check for being an element; if we ever make this work
3216 : // for non-HTML, need to keep checking for elements.
3217 0 : return NS_OK;
3218 : }
3219 :
3220 0 : nsIAtom *tag = startContent->Tag();
3221 :
3222 0 : if (tag == nsGkAtoms::tr)
3223 : {
3224 0 : *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
3225 : }
3226 : else //check to see if we are selecting a table or row (column and all cells not done yet)
3227 : {
3228 0 : nsIContent *child = startNode->GetChildAt(startOffset);
3229 0 : if (!child)
3230 0 : return NS_ERROR_FAILURE;
3231 :
3232 0 : tag = child->Tag();
3233 :
3234 0 : if (tag == nsGkAtoms::table)
3235 0 : *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE;
3236 0 : else if (tag == nsGkAtoms::tr)
3237 0 : *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
3238 : }
3239 :
3240 0 : return NS_OK;
3241 : }
3242 :
3243 : nsresult
3244 0 : nsFrameSelection::CreateAndAddRange(nsINode *aParentNode, PRInt32 aOffset)
3245 : {
3246 0 : if (!aParentNode) return NS_ERROR_NULL_POINTER;
3247 :
3248 0 : nsRefPtr<nsRange> range = new nsRange();
3249 :
3250 : // Set range around child at given offset
3251 0 : nsresult result = range->SetStart(aParentNode, aOffset);
3252 0 : if (NS_FAILED(result)) return result;
3253 0 : result = range->SetEnd(aParentNode, aOffset+1);
3254 0 : if (NS_FAILED(result)) return result;
3255 :
3256 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
3257 0 : if (!mDomSelections[index])
3258 0 : return NS_ERROR_NULL_POINTER;
3259 :
3260 0 : return mDomSelections[index]->AddRange(range);
3261 : }
3262 :
3263 : // End of Table Selection
3264 :
3265 : void
3266 0 : nsFrameSelection::SetAncestorLimiter(nsIContent *aLimiter)
3267 : {
3268 0 : if (mAncestorLimiter != aLimiter) {
3269 0 : mAncestorLimiter = aLimiter;
3270 : PRInt8 index =
3271 0 : GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
3272 0 : if (!mDomSelections[index])
3273 0 : return;
3274 :
3275 0 : if (!IsValidSelectionPoint(this, mDomSelections[index]->GetFocusNode())) {
3276 0 : ClearNormalSelection();
3277 0 : if (mAncestorLimiter) {
3278 0 : PostReason(nsISelectionListener::NO_REASON);
3279 0 : TakeFocus(mAncestorLimiter, 0, 0, HINTLEFT, false, false);
3280 : }
3281 : }
3282 : }
3283 : }
3284 :
3285 : //END nsFrameSelection methods
3286 :
3287 :
3288 : //BEGIN nsISelection interface implementations
3289 :
3290 :
3291 :
3292 : nsresult
3293 0 : nsFrameSelection::DeleteFromDocument()
3294 : {
3295 : nsresult res;
3296 :
3297 : // If we're already collapsed, then we do nothing (bug 719503).
3298 : bool isCollapsed;
3299 0 : PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
3300 0 : if (!mDomSelections[index])
3301 0 : return NS_ERROR_NULL_POINTER;
3302 :
3303 0 : mDomSelections[index]->GetIsCollapsed( &isCollapsed);
3304 0 : if (isCollapsed)
3305 : {
3306 0 : return NS_OK;
3307 : }
3308 :
3309 : // Get an iterator
3310 0 : nsSelectionIterator iter(mDomSelections[index]);
3311 0 : res = iter.First();
3312 0 : if (NS_FAILED(res))
3313 0 : return res;
3314 :
3315 0 : while (iter.IsDone())
3316 : {
3317 0 : nsRefPtr<nsRange> range = iter.CurrentItem();
3318 0 : res = range->DeleteContents();
3319 0 : if (NS_FAILED(res))
3320 0 : return res;
3321 0 : iter.Next();
3322 : }
3323 :
3324 : // Collapse to the new location.
3325 : // If we deleted one character, then we move back one element.
3326 : // FIXME We don't know how to do this past frame boundaries yet.
3327 0 : if (isCollapsed)
3328 0 : mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->GetAnchorOffset()-1);
3329 0 : else if (mDomSelections[index]->GetAnchorOffset() > 0)
3330 0 : mDomSelections[index]->Collapse(mDomSelections[index]->GetAnchorNode(), mDomSelections[index]->GetAnchorOffset());
3331 : #ifdef DEBUG
3332 : else
3333 0 : printf("Don't know how to set selection back past frame boundary\n");
3334 : #endif
3335 :
3336 0 : return NS_OK;
3337 : }
3338 :
3339 : void
3340 0 : nsFrameSelection::SetDelayedCaretData(nsMouseEvent *aMouseEvent)
3341 : {
3342 0 : if (aMouseEvent)
3343 : {
3344 0 : mDelayedMouseEventValid = true;
3345 0 : mDelayedMouseEvent = *aMouseEvent;
3346 :
3347 : // Don't cache the widget. We don't need it and it could go away.
3348 0 : mDelayedMouseEvent.widget = nsnull;
3349 : }
3350 : else
3351 0 : mDelayedMouseEventValid = false;
3352 0 : }
3353 :
3354 : nsMouseEvent*
3355 0 : nsFrameSelection::GetDelayedCaretData()
3356 : {
3357 0 : if (mDelayedMouseEventValid)
3358 0 : return &mDelayedMouseEvent;
3359 :
3360 0 : return nsnull;
3361 : }
3362 :
3363 : void
3364 0 : nsFrameSelection::DisconnectFromPresShell()
3365 : {
3366 0 : StopAutoScrollTimer();
3367 0 : for (PRInt32 i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; i++) {
3368 0 : mDomSelections[i]->Clear(nsnull);
3369 : }
3370 0 : mShell = nsnull;
3371 0 : }
3372 :
3373 : //END nsISelection interface implementations
3374 :
3375 : #if 0
3376 : #pragma mark -
3377 : #endif
3378 :
3379 : // nsTypedSelection implementation
3380 :
3381 : // note: this can return a nil anchor node
3382 :
3383 0 : nsTypedSelection::nsTypedSelection()
3384 : : mCachedOffsetForFrame(nsnull)
3385 : , mDirection(eDirNext)
3386 0 : , mType(nsISelectionController::SELECTION_NORMAL)
3387 : {
3388 0 : }
3389 :
3390 0 : nsTypedSelection::nsTypedSelection(nsFrameSelection *aList)
3391 : : mFrameSelection(aList)
3392 : , mCachedOffsetForFrame(nsnull)
3393 : , mDirection(eDirNext)
3394 0 : , mType(nsISelectionController::SELECTION_NORMAL)
3395 : {
3396 0 : }
3397 :
3398 0 : nsTypedSelection::~nsTypedSelection()
3399 : {
3400 0 : setAnchorFocusRange(-1);
3401 :
3402 0 : PRUint32 count = mRanges.Length();
3403 0 : for (PRUint32 i = 0; i < count; ++i) {
3404 0 : mRanges[i].mRange->SetInSelection(false);
3405 : }
3406 :
3407 0 : if (mAutoScrollTimer) {
3408 0 : mAutoScrollTimer->Stop();
3409 0 : mAutoScrollTimer = nsnull;
3410 : }
3411 :
3412 0 : mScrollEvent.Revoke();
3413 :
3414 0 : if (mCachedOffsetForFrame) {
3415 0 : delete mCachedOffsetForFrame;
3416 0 : mCachedOffsetForFrame = nsnull;
3417 : }
3418 0 : }
3419 :
3420 :
3421 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsTypedSelection)
3422 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTypedSelection)
3423 : // Unlink the selection listeners *before* we do RemoveAllRanges since
3424 : // we don't want to notify the listeners during JS GC (they could be
3425 : // in JS!).
3426 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mSelectionListeners)
3427 0 : tmp->RemoveAllRanges();
3428 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFrameSelection)
3429 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3430 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTypedSelection)
3431 : {
3432 0 : PRUint32 i, count = tmp->mRanges.Length();
3433 0 : for (i = 0; i < count; ++i) {
3434 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mRanges[i].mRange, nsIDOMRange)
3435 : }
3436 : }
3437 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mAnchorFocusRange, nsIDOMRange)
3438 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFrameSelection)
3439 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mSelectionListeners)
3440 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3441 :
3442 : DOMCI_DATA(Selection, nsTypedSelection)
3443 :
3444 : // QueryInterface implementation for nsTypedSelection
3445 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTypedSelection)
3446 0 : NS_INTERFACE_MAP_ENTRY(nsISelection)
3447 0 : NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
3448 0 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
3449 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
3450 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Selection)
3451 0 : NS_INTERFACE_MAP_END
3452 :
3453 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTypedSelection)
3454 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTypedSelection)
3455 :
3456 : NS_IMETHODIMP
3457 0 : nsTypedSelection::SetPresShell(nsIPresShell *aPresShell)
3458 : {
3459 0 : mPresShellWeak = do_GetWeakReference(aPresShell);
3460 0 : return NS_OK;
3461 : }
3462 :
3463 :
3464 :
3465 : NS_IMETHODIMP
3466 0 : nsTypedSelection::GetAnchorNode(nsIDOMNode** aAnchorNode)
3467 : {
3468 0 : nsINode* anchorNode = GetAnchorNode();
3469 0 : if (anchorNode) {
3470 0 : return CallQueryInterface(anchorNode, aAnchorNode);
3471 : }
3472 :
3473 0 : *aAnchorNode = nsnull;
3474 0 : return NS_OK;
3475 : }
3476 :
3477 : nsINode*
3478 0 : nsTypedSelection::GetAnchorNode()
3479 : {
3480 0 : if (!mAnchorFocusRange)
3481 0 : return nsnull;
3482 :
3483 0 : if (GetDirection() == eDirNext) {
3484 0 : return mAnchorFocusRange->GetStartParent();
3485 : }
3486 :
3487 0 : return mAnchorFocusRange->GetEndParent();
3488 : }
3489 :
3490 : NS_IMETHODIMP
3491 0 : nsTypedSelection::GetAnchorOffset(PRInt32* aAnchorOffset)
3492 : {
3493 0 : *aAnchorOffset = GetAnchorOffset();
3494 0 : return NS_OK;
3495 : }
3496 :
3497 : // note: this can return a nil focus node
3498 : NS_IMETHODIMP
3499 0 : nsTypedSelection::GetFocusNode(nsIDOMNode** aFocusNode)
3500 : {
3501 0 : nsINode* focusNode = GetFocusNode();
3502 0 : if (focusNode) {
3503 0 : return CallQueryInterface(focusNode, aFocusNode);
3504 : }
3505 :
3506 0 : *aFocusNode = nsnull;
3507 0 : return NS_OK;
3508 : }
3509 :
3510 : nsINode*
3511 0 : nsTypedSelection::GetFocusNode()
3512 : {
3513 0 : if (!mAnchorFocusRange)
3514 0 : return nsnull;
3515 :
3516 0 : if (GetDirection() == eDirNext){
3517 0 : return mAnchorFocusRange->GetEndParent();
3518 : }
3519 :
3520 0 : return mAnchorFocusRange->GetStartParent();
3521 : }
3522 :
3523 0 : NS_IMETHODIMP nsTypedSelection::GetFocusOffset(PRInt32* aFocusOffset)
3524 : {
3525 0 : *aFocusOffset = GetFocusOffset();
3526 0 : return NS_OK;
3527 : }
3528 :
3529 0 : void nsTypedSelection::setAnchorFocusRange(PRInt32 indx)
3530 : {
3531 0 : if (indx >= (PRInt32)mRanges.Length())
3532 0 : return;
3533 0 : if (indx < 0) //release all
3534 : {
3535 0 : mAnchorFocusRange = nsnull;
3536 : }
3537 : else{
3538 0 : mAnchorFocusRange = mRanges[indx].mRange;
3539 : }
3540 : }
3541 :
3542 : PRInt32
3543 0 : nsTypedSelection::GetAnchorOffset()
3544 : {
3545 0 : if (!mAnchorFocusRange)
3546 0 : return 0;
3547 :
3548 0 : if (GetDirection() == eDirNext){
3549 0 : return mAnchorFocusRange->StartOffset();
3550 : }
3551 :
3552 0 : return mAnchorFocusRange->EndOffset();
3553 : }
3554 :
3555 : PRInt32
3556 0 : nsTypedSelection::GetFocusOffset()
3557 : {
3558 0 : if (!mAnchorFocusRange)
3559 0 : return 0;
3560 :
3561 0 : if (GetDirection() == eDirNext){
3562 0 : return mAnchorFocusRange->EndOffset();
3563 : }
3564 :
3565 0 : return mAnchorFocusRange->StartOffset();
3566 : }
3567 :
3568 : static nsresult
3569 0 : CompareToRangeStart(nsINode* aCompareNode, PRInt32 aCompareOffset,
3570 : nsRange* aRange, PRInt32* aCmp)
3571 : {
3572 0 : nsINode* start = aRange->GetStartParent();
3573 0 : NS_ENSURE_STATE(aCompareNode && start);
3574 : // If the nodes that we're comparing are not in the same document,
3575 : // assume that aCompareNode will fall at the end of the ranges.
3576 0 : if (aCompareNode->GetCurrentDoc() != start->GetCurrentDoc() ||
3577 0 : !start->GetCurrentDoc()) {
3578 0 : *aCmp = 1;
3579 : } else {
3580 : *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
3581 0 : start, aRange->StartOffset());
3582 : }
3583 0 : return NS_OK;
3584 : }
3585 :
3586 : static nsresult
3587 0 : CompareToRangeEnd(nsINode* aCompareNode, PRInt32 aCompareOffset,
3588 : nsRange* aRange, PRInt32* aCmp)
3589 : {
3590 0 : nsINode* end = aRange->GetEndParent();
3591 0 : NS_ENSURE_STATE(aCompareNode && end);
3592 : // If the nodes that we're comparing are not in the same document,
3593 : // assume that aCompareNode will fall at the end of the ranges.
3594 0 : if (aCompareNode->GetCurrentDoc() != end->GetCurrentDoc() ||
3595 0 : !end->GetCurrentDoc()) {
3596 0 : *aCmp = 1;
3597 : } else {
3598 : *aCmp = nsContentUtils::ComparePoints(aCompareNode, aCompareOffset,
3599 0 : end, aRange->EndOffset());
3600 : }
3601 0 : return NS_OK;
3602 : }
3603 :
3604 : // nsTypedSelection::FindInsertionPoint
3605 : //
3606 : // Binary searches the given sorted array of ranges for the insertion point
3607 : // for the given node/offset. The given comparator is used, and the index
3608 : // where the point should appear in the array is placed in *aInsertionPoint.
3609 : //
3610 : // If there is an item in the array equal to the input point, we will return
3611 : // the index of this item.
3612 :
3613 : nsresult
3614 0 : nsTypedSelection::FindInsertionPoint(
3615 : nsTArray<RangeData>* aElementArray,
3616 : nsINode* aPointNode, PRInt32 aPointOffset,
3617 : nsresult (*aComparator)(nsINode*,PRInt32,nsRange*,PRInt32*),
3618 : PRInt32* aPoint)
3619 : {
3620 0 : *aPoint = 0;
3621 0 : PRInt32 beginSearch = 0;
3622 0 : PRInt32 endSearch = aElementArray->Length(); // one beyond what to check
3623 0 : while (endSearch - beginSearch > 0) {
3624 0 : PRInt32 center = (endSearch - beginSearch) / 2 + beginSearch;
3625 :
3626 0 : nsRange* range = (*aElementArray)[center].mRange;
3627 :
3628 : PRInt32 cmp;
3629 0 : nsresult rv = aComparator(aPointNode, aPointOffset, range, &cmp);
3630 0 : NS_ENSURE_SUCCESS(rv, rv);
3631 :
3632 0 : if (cmp < 0) { // point < cur
3633 0 : endSearch = center;
3634 0 : } else if (cmp > 0) { // point > cur
3635 0 : beginSearch = center + 1;
3636 : } else { // found match, done
3637 0 : beginSearch = center;
3638 0 : break;
3639 : }
3640 : }
3641 0 : *aPoint = beginSearch;
3642 0 : return NS_OK;
3643 : }
3644 :
3645 : // nsTypedSelection::SubtractRange
3646 : //
3647 : // A helper function that subtracts aSubtract from aRange, and adds
3648 : // 1 or 2 RangeData objects representing the remaining non-overlapping
3649 : // difference to aOutput. It is assumed that the caller has checked that
3650 : // aRange and aSubtract do indeed overlap
3651 :
3652 : nsresult
3653 0 : nsTypedSelection::SubtractRange(RangeData* aRange, nsRange* aSubtract,
3654 : nsTArray<RangeData>* aOutput)
3655 : {
3656 0 : nsRange* range = aRange->mRange;
3657 :
3658 : // First we want to compare to the range start
3659 : PRInt32 cmp;
3660 : nsresult rv = CompareToRangeStart(range->GetStartParent(),
3661 : range->StartOffset(),
3662 0 : aSubtract, &cmp);
3663 0 : NS_ENSURE_SUCCESS(rv, rv);
3664 :
3665 : // Also, make a comparison to the range end
3666 : PRInt32 cmp2;
3667 : rv = CompareToRangeEnd(range->GetEndParent(),
3668 : range->EndOffset(),
3669 0 : aSubtract, &cmp2);
3670 0 : NS_ENSURE_SUCCESS(rv, rv);
3671 :
3672 : // If the existing range left overlaps the new range (aSubtract) then
3673 : // cmp < 0, and cmp2 < 0
3674 : // If it right overlaps the new range then cmp > 0 and cmp2 > 0
3675 : // If it fully contains the new range, then cmp < 0 and cmp2 > 0
3676 :
3677 0 : if (cmp2 > 0) {
3678 : // We need to add a new RangeData to the output, running from
3679 : // the end of aSubtract to the end of range
3680 0 : nsRange* postOverlap = new nsRange();
3681 :
3682 : rv =
3683 0 : postOverlap->SetStart(aSubtract->GetEndParent(), aSubtract->EndOffset());
3684 0 : NS_ENSURE_SUCCESS(rv, rv);
3685 : rv =
3686 0 : postOverlap->SetEnd(range->GetEndParent(), range->EndOffset());
3687 0 : NS_ENSURE_SUCCESS(rv, rv);
3688 0 : if (!postOverlap->Collapsed()) {
3689 0 : if (!aOutput->InsertElementAt(0, RangeData(postOverlap)))
3690 0 : return NS_ERROR_OUT_OF_MEMORY;
3691 0 : (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
3692 : }
3693 : }
3694 :
3695 0 : if (cmp < 0) {
3696 : // We need to add a new RangeData to the output, running from
3697 : // the start of the range to the start of aSubtract
3698 0 : nsRange* preOverlap = new nsRange();
3699 :
3700 : nsresult rv =
3701 0 : preOverlap->SetStart(range->GetStartParent(), range->StartOffset());
3702 0 : NS_ENSURE_SUCCESS(rv, rv);
3703 : rv =
3704 0 : preOverlap->SetEnd(aSubtract->GetStartParent(), aSubtract->StartOffset());
3705 0 : NS_ENSURE_SUCCESS(rv, rv);
3706 :
3707 0 : if (!preOverlap->Collapsed()) {
3708 0 : if (!aOutput->InsertElementAt(0, RangeData(preOverlap)))
3709 0 : return NS_ERROR_OUT_OF_MEMORY;
3710 0 : (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
3711 : }
3712 : }
3713 :
3714 0 : return NS_OK;
3715 : }
3716 :
3717 : nsresult
3718 0 : nsTypedSelection::AddItem(nsRange *aItem, PRInt32 *aOutIndex)
3719 : {
3720 0 : if (!aItem)
3721 0 : return NS_ERROR_NULL_POINTER;
3722 0 : if (!aItem->IsPositioned())
3723 0 : return NS_ERROR_UNEXPECTED;
3724 0 : if (aOutIndex)
3725 0 : *aOutIndex = -1;
3726 :
3727 : // a common case is that we have no ranges yet
3728 0 : if (mRanges.Length() == 0) {
3729 0 : if (!mRanges.AppendElement(RangeData(aItem)))
3730 0 : return NS_ERROR_OUT_OF_MEMORY;
3731 0 : aItem->SetInSelection(true);
3732 :
3733 0 : if (aOutIndex)
3734 0 : *aOutIndex = 0;
3735 0 : return NS_OK;
3736 : }
3737 :
3738 : PRInt32 startIndex, endIndex;
3739 : GetIndicesForInterval(aItem->GetStartParent(), aItem->StartOffset(),
3740 : aItem->GetEndParent(), aItem->EndOffset(),
3741 0 : false, &startIndex, &endIndex);
3742 :
3743 0 : if (endIndex == -1) {
3744 : // All ranges start after the given range. We can insert our range at
3745 : // position 0, knowing there are no overlaps (handled below)
3746 0 : startIndex = 0;
3747 0 : endIndex = 0;
3748 0 : } else if (startIndex == -1) {
3749 : // All ranges end before the given range. We can insert our range at
3750 : // the end of the array, knowing there are no overlaps (handled below)
3751 0 : startIndex = mRanges.Length();
3752 0 : endIndex = startIndex;
3753 : }
3754 :
3755 : // If the range is already contained in mRanges, silently succeed
3756 : bool sameRange = EqualsRangeAtPoint(aItem->GetStartParent(),
3757 : aItem->StartOffset(),
3758 : aItem->GetEndParent(),
3759 0 : aItem->EndOffset(), startIndex);
3760 0 : if (sameRange) {
3761 0 : if (aOutIndex)
3762 0 : *aOutIndex = startIndex;
3763 0 : return NS_OK;
3764 : }
3765 :
3766 0 : if (startIndex == endIndex) {
3767 : // The new range doesn't overlap any existing ranges
3768 0 : if (!mRanges.InsertElementAt(startIndex, RangeData(aItem)))
3769 0 : return NS_ERROR_OUT_OF_MEMORY;
3770 0 : aItem->SetInSelection(true);
3771 0 : if (aOutIndex)
3772 0 : *aOutIndex = startIndex;
3773 0 : return NS_OK;
3774 : }
3775 :
3776 : // We now know that at least 1 existing range overlaps with the range that
3777 : // we are trying to add. In fact, the only ranges of interest are those at
3778 : // the two end points, startIndex and endIndex - 1 (which may point to the
3779 : // same range) as these may partially overlap the new range. Any ranges
3780 : // between these indices are fully overlapped by the new range, and so can be
3781 : // removed
3782 0 : nsTArray<RangeData> overlaps;
3783 0 : if (!overlaps.InsertElementAt(0, mRanges[startIndex]))
3784 0 : return NS_ERROR_OUT_OF_MEMORY;
3785 :
3786 0 : if (endIndex - 1 != startIndex) {
3787 0 : if (!overlaps.InsertElementAt(1, mRanges[endIndex - 1]))
3788 0 : return NS_ERROR_OUT_OF_MEMORY;
3789 : }
3790 :
3791 : // Remove all the overlapping ranges
3792 0 : for (PRInt32 i = startIndex; i < endIndex; ++i) {
3793 0 : mRanges[i].mRange->SetInSelection(false);
3794 : }
3795 0 : mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
3796 :
3797 0 : nsTArray<RangeData> temp;
3798 0 : for (PRInt32 i = overlaps.Length() - 1; i >= 0; i--) {
3799 0 : nsresult rv = SubtractRange(&overlaps[i], aItem, &temp);
3800 0 : NS_ENSURE_SUCCESS(rv, rv);
3801 : }
3802 :
3803 : // Insert the new element into our "leftovers" array
3804 : PRInt32 insertionPoint;
3805 : nsresult rv = FindInsertionPoint(&temp, aItem->GetStartParent(),
3806 : aItem->StartOffset(),
3807 : CompareToRangeStart,
3808 0 : &insertionPoint);
3809 0 : NS_ENSURE_SUCCESS(rv, rv);
3810 :
3811 0 : if (!temp.InsertElementAt(insertionPoint, RangeData(aItem)))
3812 0 : return NS_ERROR_OUT_OF_MEMORY;
3813 :
3814 : // Merge the leftovers back in to mRanges
3815 0 : if (!mRanges.InsertElementsAt(startIndex, temp))
3816 0 : return NS_ERROR_OUT_OF_MEMORY;
3817 :
3818 0 : for (PRUint32 i = 0; i < temp.Length(); ++i) {
3819 0 : temp[i].mRange->SetInSelection(true);
3820 : }
3821 :
3822 0 : *aOutIndex = startIndex + insertionPoint;
3823 0 : return NS_OK;
3824 : }
3825 :
3826 : nsresult
3827 0 : nsTypedSelection::RemoveItem(nsRange *aItem)
3828 : {
3829 0 : if (!aItem)
3830 0 : return NS_ERROR_NULL_POINTER;
3831 :
3832 : // Find the range's index & remove it. We could use FindInsertionPoint to
3833 : // get O(log n) time, but that requires many expensive DOM comparisons.
3834 : // For even several thousand items, this is probably faster because the
3835 : // comparisons are so fast.
3836 0 : PRInt32 idx = -1;
3837 : PRUint32 i;
3838 0 : for (i = 0; i < mRanges.Length(); i ++) {
3839 0 : if (mRanges[i].mRange == aItem) {
3840 0 : idx = (PRInt32)i;
3841 0 : break;
3842 : }
3843 : }
3844 0 : if (idx < 0)
3845 0 : return NS_ERROR_INVALID_ARG;
3846 :
3847 0 : mRanges.RemoveElementAt(idx);
3848 0 : aItem->SetInSelection(false);
3849 0 : return NS_OK;
3850 : }
3851 :
3852 : nsresult
3853 0 : nsTypedSelection::RemoveCollapsedRanges()
3854 : {
3855 0 : PRUint32 i = 0;
3856 0 : while (i < mRanges.Length()) {
3857 0 : if (mRanges[i].mRange->Collapsed()) {
3858 0 : nsresult rv = RemoveItem(mRanges[i].mRange);
3859 0 : NS_ENSURE_SUCCESS(rv, rv);
3860 : } else {
3861 0 : ++i;
3862 : }
3863 : }
3864 0 : return NS_OK;
3865 : }
3866 :
3867 : nsresult
3868 0 : nsTypedSelection::Clear(nsPresContext* aPresContext)
3869 : {
3870 0 : setAnchorFocusRange(-1);
3871 :
3872 0 : for (PRUint32 i = 0; i < mRanges.Length(); ++i) {
3873 0 : mRanges[i].mRange->SetInSelection(false);
3874 0 : selectFrames(aPresContext, mRanges[i].mRange, false);
3875 : }
3876 0 : mRanges.Clear();
3877 :
3878 : // Reset direction so for more dependable table selection range handling
3879 0 : SetDirection(eDirNext);
3880 :
3881 : // If this was an ATTENTION selection, change it back to normal now
3882 0 : if (mFrameSelection &&
3883 0 : mFrameSelection->GetDisplaySelection() ==
3884 0 : nsISelectionController::SELECTION_ATTENTION) {
3885 0 : mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
3886 : }
3887 :
3888 0 : return NS_OK;
3889 : }
3890 :
3891 : NS_IMETHODIMP
3892 0 : nsTypedSelection::GetType(PRInt16 *aType)
3893 : {
3894 0 : NS_ENSURE_ARG_POINTER(aType);
3895 0 : *aType = mType;
3896 :
3897 0 : return NS_OK;
3898 : }
3899 :
3900 : // RangeMatches*Point
3901 : //
3902 : // Compares the range beginning or ending point, and returns true if it
3903 : // exactly matches the given DOM point.
3904 :
3905 : static inline bool
3906 0 : RangeMatchesBeginPoint(nsRange* aRange, nsINode* aNode, PRInt32 aOffset)
3907 : {
3908 0 : return aRange->GetStartParent() == aNode && aRange->StartOffset() == aOffset;
3909 : }
3910 :
3911 : static inline bool
3912 0 : RangeMatchesEndPoint(nsRange* aRange, nsINode* aNode, PRInt32 aOffset)
3913 : {
3914 0 : return aRange->GetEndParent() == aNode && aRange->EndOffset() == aOffset;
3915 : }
3916 :
3917 : // nsTypedSelection::EqualsRangeAtPoint
3918 : //
3919 : // Utility method for checking equivalence of two ranges.
3920 :
3921 : bool
3922 0 : nsTypedSelection::EqualsRangeAtPoint(
3923 : nsINode* aBeginNode, PRInt32 aBeginOffset,
3924 : nsINode* aEndNode, PRInt32 aEndOffset,
3925 : PRInt32 aRangeIndex)
3926 : {
3927 0 : if (aRangeIndex >=0 && aRangeIndex < (PRInt32) mRanges.Length()) {
3928 0 : nsRange* range = mRanges[aRangeIndex].mRange;
3929 0 : if (RangeMatchesBeginPoint(range, aBeginNode, aBeginOffset) &&
3930 0 : RangeMatchesEndPoint(range, aEndNode, aEndOffset))
3931 0 : return true;
3932 : }
3933 0 : return false;
3934 : }
3935 :
3936 : // nsTypedSelection::GetRangesForInterval
3937 : //
3938 : // XPCOM wrapper for the nsTArray version
3939 :
3940 : NS_IMETHODIMP
3941 0 : nsTypedSelection::GetRangesForInterval(nsIDOMNode* aBeginNode, PRInt32 aBeginOffset,
3942 : nsIDOMNode* aEndNode, PRInt32 aEndOffset,
3943 : bool aAllowAdjacent,
3944 : PRUint32 *aResultCount,
3945 : nsIDOMRange ***aResults)
3946 : {
3947 0 : if (!aBeginNode || ! aEndNode || ! aResultCount || ! aResults)
3948 0 : return NS_ERROR_NULL_POINTER;
3949 :
3950 0 : *aResultCount = 0;
3951 0 : *aResults = nsnull;
3952 :
3953 0 : nsCOMPtr<nsINode> beginNode = do_QueryInterface(aBeginNode);
3954 0 : nsCOMPtr<nsINode> endNode = do_QueryInterface(aEndNode);
3955 :
3956 0 : nsTArray<nsRange*> results;
3957 : nsresult rv = GetRangesForIntervalArray(beginNode, aBeginOffset,
3958 : endNode, aEndOffset,
3959 0 : aAllowAdjacent, &results);
3960 0 : NS_ENSURE_SUCCESS(rv, rv);
3961 0 : *aResultCount = results.Length();
3962 0 : if (*aResultCount == 0) {
3963 0 : return NS_OK;
3964 : }
3965 :
3966 : *aResults = static_cast<nsIDOMRange**>
3967 0 : (nsMemory::Alloc(sizeof(nsIDOMRange*) * *aResultCount));
3968 0 : NS_ENSURE_TRUE(*aResults, NS_ERROR_OUT_OF_MEMORY);
3969 :
3970 0 : for (PRUint32 i = 0; i < *aResultCount; i++)
3971 0 : NS_ADDREF((*aResults)[i] = results[i]);
3972 0 : return NS_OK;
3973 : }
3974 :
3975 : // nsTypedSelection::GetRangesForIntervalArray
3976 : //
3977 : // Fills a nsTArray with the ranges overlapping the range specified by
3978 : // the given endpoints. Ranges in the selection exactly adjacent to the
3979 : // input range are not returned unless aAllowAdjacent is set.
3980 : //
3981 : // For example, if the following ranges were in the selection
3982 : // (assume everything is within the same node)
3983 : //
3984 : // Start Offset: 0 2 7 9
3985 : // End Offset: 2 5 9 10
3986 : //
3987 : // and passed aBeginOffset of 2 and aEndOffset of 9, then with
3988 : // aAllowAdjacent set, all the ranges should be returned. If
3989 : // aAllowAdjacent was false, the ranges [2, 5] and [7, 9] only
3990 : // should be returned
3991 : //
3992 : // Now that overlapping ranges are disallowed, there can be a maximum of
3993 : // 2 adjacent ranges
3994 :
3995 : nsresult
3996 0 : nsTypedSelection::GetRangesForIntervalArray(nsINode* aBeginNode, PRInt32 aBeginOffset,
3997 : nsINode* aEndNode, PRInt32 aEndOffset,
3998 : bool aAllowAdjacent,
3999 : nsTArray<nsRange*>* aRanges)
4000 : {
4001 0 : aRanges->Clear();
4002 : PRInt32 startIndex, endIndex;
4003 : GetIndicesForInterval(aBeginNode, aBeginOffset, aEndNode, aEndOffset,
4004 0 : aAllowAdjacent, &startIndex, &endIndex);
4005 0 : if (startIndex == -1 || endIndex == -1)
4006 0 : return NS_OK;
4007 :
4008 0 : for (PRInt32 i = startIndex; i < endIndex; i++) {
4009 0 : if (!aRanges->AppendElement(mRanges[i].mRange))
4010 0 : return NS_ERROR_OUT_OF_MEMORY;
4011 : }
4012 :
4013 0 : return NS_OK;
4014 : }
4015 :
4016 : // nsTypedSelection::GetIndicesForInterval
4017 : //
4018 : // Works on the same principle as GetRangesForIntervalArray above, however
4019 : // instead this returns the indices into mRanges between which the
4020 : // overlapping ranges lie.
4021 :
4022 : void
4023 0 : nsTypedSelection::GetIndicesForInterval(nsINode* aBeginNode,
4024 : PRInt32 aBeginOffset,
4025 : nsINode* aEndNode, PRInt32 aEndOffset,
4026 : bool aAllowAdjacent,
4027 : PRInt32 *aStartIndex,
4028 : PRInt32 *aEndIndex)
4029 : {
4030 : PRInt32 startIndex;
4031 : PRInt32 endIndex;
4032 :
4033 0 : if (!aStartIndex)
4034 0 : aStartIndex = &startIndex;
4035 0 : if (!aEndIndex)
4036 0 : aEndIndex = &endIndex;
4037 :
4038 0 : *aStartIndex = -1;
4039 0 : *aEndIndex = -1;
4040 :
4041 0 : if (mRanges.Length() == 0)
4042 0 : return;
4043 :
4044 : bool intervalIsCollapsed = aBeginNode == aEndNode &&
4045 0 : aBeginOffset == aEndOffset;
4046 :
4047 : // Ranges that end before the given interval and begin after the given
4048 : // interval can be discarded
4049 : PRInt32 endsBeforeIndex;
4050 0 : if (NS_FAILED(FindInsertionPoint(&mRanges, aEndNode, aEndOffset,
4051 : &CompareToRangeStart,
4052 : &endsBeforeIndex))) {
4053 0 : return;
4054 : }
4055 :
4056 0 : if (endsBeforeIndex == 0) {
4057 0 : nsRange* endRange = mRanges[endsBeforeIndex].mRange;
4058 :
4059 : // If the interval is strictly before the range at index 0, we can optimize
4060 : // by returning now - all ranges start after the given interval
4061 0 : if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
4062 0 : return;
4063 :
4064 : // We now know that the start point of mRanges[0].mRange equals the end of
4065 : // the interval. Thus, when aAllowadjacent is true, the caller is always
4066 : // interested in this range. However, when excluding adjacencies, we must
4067 : // remember to include the range when both it and the given interval are
4068 : // collapsed to the same point
4069 0 : if (!aAllowAdjacent && !(endRange->Collapsed() && intervalIsCollapsed))
4070 0 : return;
4071 : }
4072 0 : *aEndIndex = endsBeforeIndex;
4073 :
4074 : PRInt32 beginsAfterIndex;
4075 0 : if (NS_FAILED(FindInsertionPoint(&mRanges, aBeginNode, aBeginOffset,
4076 : &CompareToRangeEnd,
4077 : &beginsAfterIndex))) {
4078 0 : return;
4079 : }
4080 0 : if (beginsAfterIndex == (PRInt32) mRanges.Length())
4081 0 : return; // optimization: all ranges are strictly before us
4082 :
4083 0 : if (aAllowAdjacent) {
4084 : // At this point, one of the following holds:
4085 : // endsBeforeIndex == mRanges.Length(),
4086 : // endsBeforeIndex points to a range whose start point does not equal the
4087 : // given interval's start point
4088 : // endsBeforeIndex points to a range whose start point equals the given
4089 : // interval's start point
4090 : // In the final case, there can be two such ranges, a collapsed range, and
4091 : // an adjacent range (they will appear in mRanges in that order). For this
4092 : // final case, we need to increment endsBeforeIndex, until one of the
4093 : // first two possibilites hold
4094 0 : while (endsBeforeIndex < (PRInt32) mRanges.Length()) {
4095 0 : nsRange* endRange = mRanges[endsBeforeIndex].mRange;
4096 0 : if (!RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset))
4097 0 : break;
4098 0 : endsBeforeIndex++;
4099 : }
4100 :
4101 : // Likewise, one of the following holds:
4102 : // beginsAfterIndex == 0,
4103 : // beginsAfterIndex points to a range whose end point does not equal
4104 : // the given interval's end point
4105 : // beginsOnOrAfter points to a range whose end point equals the given
4106 : // interval's end point
4107 : // In the final case, there can be two such ranges, an adjacent range, and
4108 : // a collapsed range (they will appear in mRanges in that order). For this
4109 : // final case, we only need to take action if both those ranges exist, and
4110 : // we are pointing to the collapsed range - we need to point to the
4111 : // adjacent range
4112 0 : nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
4113 0 : if (beginsAfterIndex > 0 && beginRange->Collapsed() &&
4114 0 : RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset)) {
4115 0 : beginRange = mRanges[beginsAfterIndex - 1].mRange;
4116 0 : if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset))
4117 0 : beginsAfterIndex--;
4118 : }
4119 : } else {
4120 : // See above for the possibilities at this point. The only case where we
4121 : // need to take action is when the range at beginsAfterIndex ends on
4122 : // the given interval's start point, but that range isn't collapsed (a
4123 : // collapsed range should be included in the returned results).
4124 0 : nsRange* beginRange = mRanges[beginsAfterIndex].mRange;
4125 0 : if (RangeMatchesEndPoint(beginRange, aBeginNode, aBeginOffset) &&
4126 0 : !beginRange->Collapsed())
4127 0 : beginsAfterIndex++;
4128 :
4129 : // Again, see above for the meaning of endsBeforeIndex at this point.
4130 : // In particular, endsBeforeIndex may point to a collaped range which
4131 : // represents the point at the end of the interval - this range should be
4132 : // included
4133 0 : if (endsBeforeIndex < (PRInt32) mRanges.Length()) {
4134 0 : nsRange* endRange = mRanges[endsBeforeIndex].mRange;
4135 0 : if (RangeMatchesBeginPoint(endRange, aEndNode, aEndOffset) &&
4136 0 : endRange->Collapsed())
4137 0 : endsBeforeIndex++;
4138 : }
4139 : }
4140 :
4141 0 : *aStartIndex = beginsAfterIndex;
4142 0 : *aEndIndex = endsBeforeIndex;
4143 0 : return;
4144 : }
4145 :
4146 : //utility method to get the primary frame of node or use the offset to get frame of child node
4147 :
4148 : #if 0
4149 : NS_IMETHODIMP
4150 : nsTypedSelection::GetPrimaryFrameForRangeEndpoint(nsIDOMNode *aNode, PRInt32 aOffset, bool aIsEndNode, nsIFrame **aReturnFrame)
4151 : {
4152 : if (!aNode || !aReturnFrame || !mFrameSelection)
4153 : return NS_ERROR_NULL_POINTER;
4154 :
4155 : if (aOffset < 0)
4156 : return NS_ERROR_FAILURE;
4157 :
4158 : *aReturnFrame = 0;
4159 :
4160 : nsresult result = NS_OK;
4161 :
4162 : nsCOMPtr<nsIDOMNode> node = aNode;
4163 :
4164 : if (!node)
4165 : return NS_ERROR_NULL_POINTER;
4166 :
4167 : nsCOMPtr<nsIContent> content = do_QueryInterface(node, &result);
4168 :
4169 : if (NS_FAILED(result))
4170 : return result;
4171 :
4172 : if (!content)
4173 : return NS_ERROR_NULL_POINTER;
4174 :
4175 : if (content->IsElement())
4176 : {
4177 : if (aIsEndNode)
4178 : aOffset--;
4179 :
4180 : if (aOffset >= 0)
4181 : {
4182 : nsIContent *child = content->GetChildAt(aOffset);
4183 : if (!child) //out of bounds?
4184 : return NS_ERROR_FAILURE;
4185 :
4186 : content = child; // releases the focusnode
4187 : }
4188 : }
4189 : *aReturnFrame = content->GetPrimaryFrame();
4190 : return NS_OK;
4191 : }
4192 : #endif
4193 :
4194 :
4195 : NS_IMETHODIMP
4196 0 : nsTypedSelection::GetPrimaryFrameForAnchorNode(nsIFrame **aReturnFrame)
4197 : {
4198 0 : if (!aReturnFrame)
4199 0 : return NS_ERROR_NULL_POINTER;
4200 :
4201 0 : PRInt32 frameOffset = 0;
4202 0 : *aReturnFrame = 0;
4203 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(GetAnchorNode());
4204 0 : if (content && mFrameSelection)
4205 : {
4206 0 : *aReturnFrame = mFrameSelection->
4207 : GetFrameForNodeOffset(content, GetAnchorOffset(),
4208 0 : mFrameSelection->GetHint(), &frameOffset);
4209 0 : if (*aReturnFrame)
4210 0 : return NS_OK;
4211 : }
4212 0 : return NS_ERROR_FAILURE;
4213 : }
4214 :
4215 : NS_IMETHODIMP
4216 0 : nsTypedSelection::GetPrimaryFrameForFocusNode(nsIFrame **aReturnFrame, PRInt32 *aOffsetUsed,
4217 : bool aVisual)
4218 : {
4219 0 : if (!aReturnFrame)
4220 0 : return NS_ERROR_NULL_POINTER;
4221 :
4222 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(GetFocusNode());
4223 0 : if (!content || !mFrameSelection)
4224 0 : return NS_ERROR_FAILURE;
4225 :
4226 0 : nsIPresShell *presShell = mFrameSelection->GetShell();
4227 :
4228 0 : PRInt32 frameOffset = 0;
4229 0 : *aReturnFrame = 0;
4230 0 : if (!aOffsetUsed)
4231 0 : aOffsetUsed = &frameOffset;
4232 :
4233 0 : nsFrameSelection::HINT hint = mFrameSelection->GetHint();
4234 :
4235 0 : if (aVisual) {
4236 0 : nsRefPtr<nsCaret> caret = presShell->GetCaret();
4237 0 : if (!caret)
4238 0 : return NS_ERROR_FAILURE;
4239 :
4240 0 : PRUint8 caretBidiLevel = mFrameSelection->GetCaretBidiLevel();
4241 :
4242 : return caret->GetCaretFrameForNodeOffset(content, GetFocusOffset(),
4243 0 : hint, caretBidiLevel, aReturnFrame, aOffsetUsed);
4244 : }
4245 :
4246 0 : *aReturnFrame = mFrameSelection->
4247 : GetFrameForNodeOffset(content, GetFocusOffset(),
4248 0 : hint, aOffsetUsed);
4249 0 : if (!*aReturnFrame)
4250 0 : return NS_ERROR_FAILURE;
4251 :
4252 0 : return NS_OK;
4253 : }
4254 :
4255 : //select all content children of aContent
4256 : nsresult
4257 0 : nsTypedSelection::SelectAllFramesForContent(nsIContentIterator *aInnerIter,
4258 : nsIContent *aContent,
4259 : bool aSelected)
4260 : {
4261 0 : nsresult result = aInnerIter->Init(aContent);
4262 : nsIFrame *frame;
4263 0 : if (NS_SUCCEEDED(result))
4264 : {
4265 : // First select frame of content passed in
4266 0 : frame = aContent->GetPrimaryFrame();
4267 0 : if (frame && frame->GetType() == nsGkAtoms::textFrame) {
4268 0 : nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
4269 0 : textFrame->SetSelectedRange(0, aContent->GetText()->GetLength(), aSelected, mType);
4270 : }
4271 : // Now iterated through the child frames and set them
4272 0 : while (!aInnerIter->IsDone()) {
4273 : nsCOMPtr<nsIContent> innercontent =
4274 0 : do_QueryInterface(aInnerIter->GetCurrentNode());
4275 :
4276 0 : frame = innercontent->GetPrimaryFrame();
4277 0 : if (frame) {
4278 0 : if (frame->GetType() == nsGkAtoms::textFrame) {
4279 0 : nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
4280 0 : textFrame->SetSelectedRange(0, innercontent->GetText()->GetLength(), aSelected, mType);
4281 : } else {
4282 0 : frame->InvalidateFrameSubtree(); // frame continuations?
4283 : }
4284 : }
4285 :
4286 0 : aInnerIter->Next();
4287 : }
4288 :
4289 0 : return NS_OK;
4290 : }
4291 :
4292 0 : return NS_ERROR_FAILURE;
4293 : }
4294 :
4295 : //the idea of this helper method is to select, deselect "top to bottom" traversing through the frames
4296 : nsresult
4297 0 : nsTypedSelection::selectFrames(nsPresContext* aPresContext, nsRange* aRange, bool aFlags)
4298 : {
4299 0 : if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell()) {
4300 0 : return NS_OK; // nothing to do
4301 : }
4302 0 : if (!aRange) {
4303 0 : return NS_ERROR_NULL_POINTER;
4304 : }
4305 :
4306 0 : if (mFrameSelection->GetTableCellSelection()) {
4307 0 : nsINode* node = aRange->GetCommonAncestor();
4308 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(node);
4309 0 : nsIFrame* frame = content ? content->GetPrimaryFrame()
4310 0 : : aPresContext->FrameManager()->GetRootFrame();
4311 0 : if (frame) {
4312 0 : frame->InvalidateFrameSubtree();
4313 : }
4314 0 : return NS_OK;
4315 : }
4316 :
4317 : nsresult result;
4318 : nsCOMPtr<nsIContentIterator> iter = do_CreateInstance(
4319 : kCSubtreeIteratorCID,
4320 0 : &result);
4321 0 : if (NS_FAILED(result))
4322 0 : return result;
4323 :
4324 : nsCOMPtr<nsIContentIterator> inneriter = do_CreateInstance(
4325 : kCContentIteratorCID,
4326 0 : &result);
4327 :
4328 0 : if ((NS_SUCCEEDED(result)) && iter) {
4329 0 : result = iter->Init(aRange);
4330 :
4331 : // loop through the content iterator for each content node
4332 : // for each text node, call SetSelected on it:
4333 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
4334 :
4335 : // we must call first one explicitly
4336 0 : if (!content)
4337 0 : return NS_ERROR_UNEXPECTED;
4338 :
4339 0 : if (content->IsNodeOfType(nsINode::eTEXT)) {
4340 0 : nsIFrame* frame = content->GetPrimaryFrame();
4341 : // The frame could be an SVG text frame, in which case we'll ignore
4342 : // it.
4343 0 : if (frame && frame->GetType() == nsGkAtoms::textFrame)
4344 : {
4345 0 : nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
4346 0 : PRUint32 startOffset = aRange->StartOffset();
4347 : PRUint32 endOffset;
4348 0 : if (aRange->GetEndParent() == content) {
4349 0 : endOffset = aRange->EndOffset();
4350 : } else {
4351 0 : endOffset = content->GetText()->GetLength();
4352 : }
4353 0 : textFrame->SetSelectedRange(startOffset, endOffset, aFlags, mType);
4354 : }
4355 : }
4356 :
4357 0 : iter->First();
4358 0 : while (!iter->IsDone()) {
4359 0 : content = do_QueryInterface(iter->GetCurrentNode());
4360 0 : SelectAllFramesForContent(inneriter, content, aFlags);
4361 0 : iter->Next();
4362 : }
4363 :
4364 : //we must now do the last one if it is not the same as the first
4365 0 : if (aRange->GetEndParent() != aRange->GetStartParent())
4366 : {
4367 0 : content = do_QueryInterface(aRange->GetEndParent(), &result);
4368 0 : if (NS_FAILED(result) || !content)
4369 0 : return result;
4370 :
4371 0 : if (content->IsNodeOfType(nsINode::eTEXT))
4372 : {
4373 0 : nsIFrame* frame = content->GetPrimaryFrame();
4374 : // The frame could be an SVG text frame, in which case we'll
4375 : // ignore it.
4376 0 : if (frame && frame->GetType() == nsGkAtoms::textFrame)
4377 : {
4378 0 : nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
4379 0 : textFrame->SetSelectedRange(0, aRange->EndOffset(), aFlags, mType);
4380 : }
4381 : }
4382 : }
4383 : }
4384 0 : return result;
4385 : }
4386 :
4387 :
4388 : // nsTypedSelection::LookUpSelection
4389 : //
4390 : // This function is called when a node wants to know where the selection is
4391 : // over itself.
4392 : //
4393 : // Usually, this is called when we already know there is a selection over
4394 : // the node in question, and we only need to find the boundaries of it on
4395 : // that node. This is when slowCheck is false--a strict test is not needed.
4396 : // Other times, the caller has no idea, and wants us to test everything,
4397 : // so we are supposed to determine whether there is a selection over the
4398 : // node at all.
4399 : //
4400 : // A previous version of this code used this flag to do less work when
4401 : // inclusion was already known (slowCheck=false). However, our tree
4402 : // structure allows us to quickly determine ranges overlapping the node,
4403 : // so we just ignore the slowCheck flag and do the full test every time.
4404 : //
4405 : // PERFORMANCE: a common case is that we are doing a fast check with exactly
4406 : // one range in the selection. In this case, this function is slower than
4407 : // brute force because of the overhead of checking the tree. We can optimize
4408 : // this case to make it faster by doing the same thing the previous version
4409 : // of this function did in the case of 1 range. This would also mean that
4410 : // the aSlowCheck flag would have meaning again.
4411 :
4412 : NS_IMETHODIMP
4413 0 : nsTypedSelection::LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset,
4414 : PRInt32 aContentLength,
4415 : SelectionDetails **aReturnDetails,
4416 : SelectionType aType, bool aSlowCheck)
4417 : {
4418 : nsresult rv;
4419 0 : if (!aContent || ! aReturnDetails)
4420 0 : return NS_ERROR_NULL_POINTER;
4421 :
4422 : // it is common to have no ranges, to optimize that
4423 0 : if (mRanges.Length() == 0)
4424 0 : return NS_OK;
4425 :
4426 0 : nsTArray<nsRange*> overlappingRanges;
4427 : rv = GetRangesForIntervalArray(aContent, aContentOffset,
4428 : aContent, aContentOffset + aContentLength,
4429 : false,
4430 0 : &overlappingRanges);
4431 0 : NS_ENSURE_SUCCESS(rv, rv);
4432 0 : if (overlappingRanges.Length() == 0)
4433 0 : return NS_OK;
4434 :
4435 0 : for (PRUint32 i = 0; i < overlappingRanges.Length(); i++) {
4436 0 : nsRange* range = overlappingRanges[i];
4437 0 : nsINode* startNode = range->GetStartParent();
4438 0 : nsINode* endNode = range->GetEndParent();
4439 0 : PRInt32 startOffset = range->StartOffset();
4440 0 : PRInt32 endOffset = range->EndOffset();
4441 :
4442 0 : PRInt32 start = -1, end = -1;
4443 0 : if (startNode == aContent && endNode == aContent) {
4444 0 : if (startOffset < (aContentOffset + aContentLength) &&
4445 : endOffset > aContentOffset) {
4446 : // this range is totally inside the requested content range
4447 0 : start = NS_MAX(0, startOffset - aContentOffset);
4448 0 : end = NS_MIN(aContentLength, endOffset - aContentOffset);
4449 : }
4450 : // otherwise, range is inside the requested node, but does not intersect
4451 : // the requested content range, so ignore it
4452 0 : } else if (startNode == aContent) {
4453 0 : if (startOffset < (aContentOffset + aContentLength)) {
4454 : // the beginning of the range is inside the requested node, but the
4455 : // end is outside, select everything from there to the end
4456 0 : start = NS_MAX(0, startOffset - aContentOffset);
4457 0 : end = aContentLength;
4458 : }
4459 0 : } else if (endNode == aContent) {
4460 0 : if (endOffset > aContentOffset) {
4461 : // the end of the range is inside the requested node, but the beginning
4462 : // is outside, select everything from the beginning to there
4463 0 : start = 0;
4464 0 : end = NS_MIN(aContentLength, endOffset - aContentOffset);
4465 : }
4466 : } else {
4467 : // this range does not begin or end in the requested node, but since
4468 : // GetRangesForInterval returned this range, we know it overlaps.
4469 : // Therefore, this node is enclosed in the range, and we select all
4470 : // of it.
4471 0 : start = 0;
4472 0 : end = aContentLength;
4473 : }
4474 0 : if (start < 0)
4475 0 : continue; // the ranges do not overlap the input range
4476 :
4477 0 : SelectionDetails* details = new SelectionDetails;
4478 :
4479 0 : details->mNext = *aReturnDetails;
4480 0 : details->mStart = start;
4481 0 : details->mEnd = end;
4482 0 : details->mType = aType;
4483 0 : RangeData *rd = FindRangeData(range);
4484 0 : if (rd) {
4485 0 : details->mTextRangeStyle = rd->mTextRangeStyle;
4486 : }
4487 0 : *aReturnDetails = details;
4488 : }
4489 0 : return NS_OK;
4490 : }
4491 :
4492 : NS_IMETHODIMP
4493 0 : nsTypedSelection::Repaint(nsPresContext* aPresContext)
4494 : {
4495 0 : PRInt32 arrCount = (PRInt32)mRanges.Length();
4496 :
4497 0 : if (arrCount < 1)
4498 0 : return NS_OK;
4499 :
4500 : PRInt32 i;
4501 :
4502 0 : for (i = 0; i < arrCount; i++)
4503 : {
4504 0 : nsresult rv = selectFrames(aPresContext, mRanges[i].mRange, true);
4505 :
4506 0 : if (NS_FAILED(rv)) {
4507 0 : return rv;
4508 : }
4509 : }
4510 :
4511 0 : return NS_OK;
4512 : }
4513 :
4514 : NS_IMETHODIMP
4515 0 : nsTypedSelection::GetCanCacheFrameOffset(bool *aCanCacheFrameOffset)
4516 : {
4517 0 : NS_ENSURE_ARG_POINTER(aCanCacheFrameOffset);
4518 :
4519 0 : if (mCachedOffsetForFrame)
4520 0 : *aCanCacheFrameOffset = mCachedOffsetForFrame->mCanCacheFrameOffset;
4521 : else
4522 0 : *aCanCacheFrameOffset = false;
4523 :
4524 0 : return NS_OK;
4525 : }
4526 :
4527 : NS_IMETHODIMP
4528 0 : nsTypedSelection::SetCanCacheFrameOffset(bool aCanCacheFrameOffset)
4529 : {
4530 0 : if (!mCachedOffsetForFrame) {
4531 0 : mCachedOffsetForFrame = new CachedOffsetForFrame;
4532 : }
4533 :
4534 0 : mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
4535 :
4536 : // clean up cached frame when turn off cache
4537 : // fix bug 207936
4538 0 : if (!aCanCacheFrameOffset) {
4539 0 : mCachedOffsetForFrame->mLastCaretFrame = nsnull;
4540 : }
4541 :
4542 0 : return NS_OK;
4543 : }
4544 :
4545 : NS_IMETHODIMP
4546 0 : nsTypedSelection::GetCachedFrameOffset(nsIFrame *aFrame, PRInt32 inOffset, nsPoint& aPoint)
4547 : {
4548 0 : if (!mCachedOffsetForFrame) {
4549 0 : mCachedOffsetForFrame = new CachedOffsetForFrame;
4550 : }
4551 :
4552 0 : nsresult rv = NS_OK;
4553 0 : if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
4554 : mCachedOffsetForFrame->mLastCaretFrame &&
4555 : (aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
4556 : (inOffset == mCachedOffsetForFrame->mLastContentOffset))
4557 : {
4558 : // get cached frame offset
4559 0 : aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
4560 : }
4561 : else
4562 : {
4563 : // Recalculate frame offset and cache it. Don't cache a frame offset if
4564 : // GetPointFromOffset fails, though.
4565 0 : rv = aFrame->GetPointFromOffset(inOffset, &aPoint);
4566 0 : if (NS_SUCCEEDED(rv) && mCachedOffsetForFrame->mCanCacheFrameOffset) {
4567 0 : mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
4568 0 : mCachedOffsetForFrame->mLastCaretFrame = aFrame;
4569 0 : mCachedOffsetForFrame->mLastContentOffset = inOffset;
4570 : }
4571 : }
4572 :
4573 0 : return rv;
4574 : }
4575 :
4576 : NS_IMETHODIMP
4577 0 : nsTypedSelection::GetFrameSelection(nsFrameSelection **aFrameSelection) {
4578 0 : NS_ENSURE_ARG_POINTER(aFrameSelection);
4579 0 : *aFrameSelection = mFrameSelection;
4580 0 : NS_IF_ADDREF(*aFrameSelection);
4581 0 : return NS_OK;
4582 : }
4583 :
4584 : NS_IMETHODIMP
4585 0 : nsTypedSelection::SetAncestorLimiter(nsIContent *aContent)
4586 : {
4587 0 : if (mFrameSelection)
4588 0 : mFrameSelection->SetAncestorLimiter(aContent);
4589 0 : return NS_OK;
4590 : }
4591 :
4592 : RangeData*
4593 0 : nsTypedSelection::FindRangeData(nsIDOMRange* aRange)
4594 : {
4595 0 : NS_ENSURE_TRUE(aRange, nsnull);
4596 0 : for (PRUint32 i = 0; i < mRanges.Length(); i++) {
4597 0 : if (mRanges[i].mRange == aRange)
4598 0 : return &mRanges[i];
4599 : }
4600 0 : return nsnull;
4601 : }
4602 :
4603 : NS_IMETHODIMP
4604 0 : nsTypedSelection::SetTextRangeStyle(nsIDOMRange *aRange,
4605 : const nsTextRangeStyle &aTextRangeStyle)
4606 : {
4607 0 : NS_ENSURE_ARG_POINTER(aRange);
4608 0 : RangeData *rd = FindRangeData(aRange);
4609 0 : if (rd) {
4610 0 : rd->mTextRangeStyle = aTextRangeStyle;
4611 : }
4612 0 : return NS_OK;
4613 : }
4614 :
4615 : nsresult
4616 0 : nsTypedSelection::StartAutoScrollTimer(nsIFrame *aFrame,
4617 : nsPoint& aPoint,
4618 : PRUint32 aDelay)
4619 : {
4620 0 : NS_PRECONDITION(aFrame, "Need a frame");
4621 :
4622 : nsresult result;
4623 0 : if (!mFrameSelection)
4624 0 : return NS_OK;//nothing to do
4625 :
4626 0 : if (!mAutoScrollTimer)
4627 : {
4628 0 : mAutoScrollTimer = new nsAutoScrollTimer();
4629 :
4630 0 : result = mAutoScrollTimer->Init(mFrameSelection, this);
4631 :
4632 0 : if (NS_FAILED(result))
4633 0 : return result;
4634 : }
4635 :
4636 0 : result = mAutoScrollTimer->SetDelay(aDelay);
4637 :
4638 0 : if (NS_FAILED(result))
4639 0 : return result;
4640 :
4641 0 : return DoAutoScroll(aFrame, aPoint);
4642 : }
4643 :
4644 : nsresult
4645 0 : nsTypedSelection::StopAutoScrollTimer()
4646 : {
4647 0 : if (mAutoScrollTimer)
4648 0 : return mAutoScrollTimer->Stop();
4649 :
4650 0 : return NS_OK;
4651 : }
4652 :
4653 : nsresult
4654 0 : nsTypedSelection::DoAutoScroll(nsIFrame *aFrame, nsPoint& aPoint)
4655 : {
4656 0 : NS_PRECONDITION(aFrame, "Need a frame");
4657 :
4658 0 : if (mAutoScrollTimer)
4659 0 : (void)mAutoScrollTimer->Stop();
4660 :
4661 0 : nsPresContext* presContext = aFrame->PresContext();
4662 0 : nsRootPresContext* rootPC = presContext->GetRootPresContext();
4663 0 : if (!rootPC)
4664 0 : return NS_OK;
4665 0 : nsIFrame* rootmostFrame = rootPC->PresShell()->FrameManager()->GetRootFrame();
4666 : // Get the point relative to the root most frame because the scroll we are
4667 : // about to do will change the coordinates of aFrame.
4668 0 : nsPoint globalPoint = aPoint + aFrame->GetOffsetToCrossDoc(rootmostFrame);
4669 :
4670 0 : bool didScroll = presContext->PresShell()->
4671 0 : ScrollFrameRectIntoView(aFrame, nsRect(aPoint, nsSize(1,1)),
4672 : NS_PRESSHELL_SCROLL_ANYWHERE,
4673 0 : NS_PRESSHELL_SCROLL_ANYWHERE, 0);
4674 :
4675 : //
4676 : // Start the AutoScroll timer if necessary.
4677 : //
4678 :
4679 0 : if (didScroll && mAutoScrollTimer)
4680 : {
4681 : nsPoint presContextPoint = globalPoint -
4682 0 : presContext->PresShell()->FrameManager()->GetRootFrame()->GetOffsetToCrossDoc(rootmostFrame);
4683 0 : mAutoScrollTimer->Start(presContext, presContextPoint);
4684 : }
4685 :
4686 0 : return NS_OK;
4687 : }
4688 :
4689 : NS_IMETHODIMP
4690 0 : nsTypedSelection::GetEnumerator(nsIEnumerator **aIterator)
4691 : {
4692 0 : NS_ADDREF(*aIterator = new nsSelectionIterator(this));
4693 0 : return NS_OK;
4694 : }
4695 :
4696 :
4697 :
4698 : /** RemoveAllRanges zeroes the selection
4699 : */
4700 : NS_IMETHODIMP
4701 0 : nsTypedSelection::RemoveAllRanges()
4702 : {
4703 0 : if (!mFrameSelection)
4704 0 : return NS_OK;//nothing to do
4705 0 : nsRefPtr<nsPresContext> presContext;
4706 0 : GetPresContext(getter_AddRefs(presContext));
4707 :
4708 :
4709 0 : nsresult result = Clear(presContext);
4710 0 : if (NS_FAILED(result))
4711 0 : return result;
4712 :
4713 : // Turn off signal for table selection
4714 0 : mFrameSelection->ClearTableCellSelection();
4715 :
4716 0 : return mFrameSelection->NotifySelectionListeners(GetType());
4717 : // Also need to notify the frames!
4718 : // PresShell::CharacterDataChanged should do that on DocumentChanged
4719 : }
4720 :
4721 : /** AddRange adds the specified range to the selection
4722 : * @param aRange is the range to be added
4723 : */
4724 : NS_IMETHODIMP
4725 0 : nsTypedSelection::AddRange(nsIDOMRange* aDOMRange)
4726 : {
4727 0 : if (!aDOMRange) {
4728 0 : return NS_ERROR_NULL_POINTER;
4729 : }
4730 0 : nsRange* range = static_cast<nsRange*>(aDOMRange);
4731 :
4732 : // This inserts a table cell range in proper document order
4733 : // and returns NS_OK if range doesn't contain just one table cell
4734 : bool didAddRange;
4735 : PRInt32 rangeIndex;
4736 0 : nsresult result = addTableCellRange(range, &didAddRange, &rangeIndex);
4737 0 : if (NS_FAILED(result)) return result;
4738 :
4739 0 : if (!didAddRange)
4740 : {
4741 0 : result = AddItem(range, &rangeIndex);
4742 0 : if (NS_FAILED(result)) return result;
4743 : }
4744 :
4745 0 : NS_ASSERTION(rangeIndex >= 0, "Range index not returned");
4746 0 : setAnchorFocusRange(rangeIndex);
4747 :
4748 : // Make sure the caret appears on the next line, if at a newline
4749 0 : if (mType == nsISelectionController::SELECTION_NORMAL)
4750 0 : SetInterlinePosition(true);
4751 :
4752 0 : nsRefPtr<nsPresContext> presContext;
4753 0 : GetPresContext(getter_AddRefs(presContext));
4754 0 : selectFrames(presContext, range, true);
4755 :
4756 0 : if (!mFrameSelection)
4757 0 : return NS_OK;//nothing to do
4758 :
4759 0 : return mFrameSelection->NotifySelectionListeners(GetType());
4760 : }
4761 :
4762 : // nsTypedSelection::RemoveRange
4763 : //
4764 : // Removes the given range from the selection. The tricky part is updating
4765 : // the flags on the frames that indicate whether they have a selection or
4766 : // not. There could be several selection ranges on the frame, and clearing
4767 : // the bit would cause the selection to not be drawn, even when there is
4768 : // another range on the frame (bug 346185).
4769 : //
4770 : // We therefore find any ranges that intersect the same nodes as the range
4771 : // being removed, and cause them to set the selected bits back on their
4772 : // selected frames after we've cleared the bit from ours.
4773 :
4774 : nsresult
4775 0 : nsTypedSelection::RemoveRange(nsIDOMRange* aDOMRange)
4776 : {
4777 0 : if (!aDOMRange) {
4778 0 : return NS_ERROR_INVALID_ARG;
4779 : }
4780 0 : nsRefPtr<nsRange> range = static_cast<nsRange*>(aDOMRange);
4781 :
4782 0 : nsresult rv = RemoveItem(range);
4783 0 : if (NS_FAILED(rv))
4784 0 : return rv;
4785 :
4786 0 : nsINode* beginNode = range->GetStartParent();
4787 0 : nsINode* endNode = range->GetEndParent();
4788 :
4789 0 : if (!beginNode || !endNode) {
4790 : // Detached range; nothing else to do here.
4791 0 : return NS_OK;
4792 : }
4793 :
4794 : // find out the length of the end node, so we can select all of it
4795 : PRInt32 beginOffset, endOffset;
4796 0 : if (endNode->IsNodeOfType(nsINode::eTEXT)) {
4797 : // Get the length of the text. We can't just use the offset because
4798 : // another range could be touching this text node but not intersect our
4799 : // range.
4800 0 : beginOffset = 0;
4801 0 : endOffset = static_cast<nsIContent*>(endNode)->TextLength();
4802 : } else {
4803 : // For non-text nodes, the given offsets should be sufficient.
4804 0 : beginOffset = range->StartOffset();
4805 0 : endOffset = range->EndOffset();
4806 : }
4807 :
4808 : // clear the selected bit from the removed range's frames
4809 0 : nsRefPtr<nsPresContext> presContext;
4810 0 : GetPresContext(getter_AddRefs(presContext));
4811 0 : selectFrames(presContext, range, false);
4812 :
4813 : // add back the selected bit for each range touching our nodes
4814 0 : nsTArray<nsRange*> affectedRanges;
4815 : rv = GetRangesForIntervalArray(beginNode, beginOffset,
4816 : endNode, endOffset,
4817 0 : true, &affectedRanges);
4818 0 : NS_ENSURE_SUCCESS(rv, rv);
4819 0 : for (PRUint32 i = 0; i < affectedRanges.Length(); i++) {
4820 0 : selectFrames(presContext, affectedRanges[i], true);
4821 : }
4822 :
4823 0 : PRInt32 cnt = mRanges.Length();
4824 0 : if (range == mAnchorFocusRange) {
4825 : // Reset anchor to LAST range or clear it if there are no ranges.
4826 0 : setAnchorFocusRange(cnt - 1);
4827 :
4828 : // When the selection is user-created it makes sense to scroll the range
4829 : // into view. The spell-check selection, however, is created and destroyed
4830 : // in the background. We don't want to scroll in this case or the view
4831 : // might appear to be moving randomly (bug 337871).
4832 0 : if (mType != nsISelectionController::SELECTION_SPELLCHECK && cnt > 0)
4833 0 : ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION);
4834 : }
4835 :
4836 0 : if (!mFrameSelection)
4837 0 : return NS_OK;//nothing to do
4838 0 : return mFrameSelection->NotifySelectionListeners(GetType());
4839 : }
4840 :
4841 :
4842 :
4843 : /*
4844 : * Collapse sets the whole selection to be one point.
4845 : */
4846 : NS_IMETHODIMP
4847 0 : nsTypedSelection::Collapse(nsIDOMNode* aParentNode, PRInt32 aOffset)
4848 : {
4849 0 : nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
4850 0 : return Collapse(parentNode, aOffset);
4851 : }
4852 :
4853 : NS_IMETHODIMP
4854 0 : nsTypedSelection::CollapseNative(nsINode* aParentNode, PRInt32 aOffset)
4855 : {
4856 0 : return Collapse(aParentNode, aOffset);
4857 : }
4858 :
4859 : nsresult
4860 0 : nsTypedSelection::Collapse(nsINode* aParentNode, PRInt32 aOffset)
4861 : {
4862 0 : if (!aParentNode)
4863 0 : return NS_ERROR_INVALID_ARG;
4864 0 : if (!mFrameSelection)
4865 0 : return NS_ERROR_NOT_INITIALIZED; // Can't do selection
4866 :
4867 0 : nsCOMPtr<nsINode> kungfuDeathGrip = aParentNode;
4868 :
4869 0 : mFrameSelection->InvalidateDesiredX();
4870 0 : if (!IsValidSelectionPoint(mFrameSelection, aParentNode))
4871 0 : return NS_ERROR_FAILURE;
4872 : nsresult result;
4873 : // Delete all of the current ranges
4874 0 : nsRefPtr<nsPresContext> presContext;
4875 0 : GetPresContext(getter_AddRefs(presContext));
4876 0 : Clear(presContext);
4877 :
4878 : // Turn off signal for table selection
4879 0 : mFrameSelection->ClearTableCellSelection();
4880 :
4881 0 : nsRefPtr<nsRange> range = new nsRange();
4882 0 : result = range->SetEnd(aParentNode, aOffset);
4883 0 : if (NS_FAILED(result))
4884 0 : return result;
4885 0 : result = range->SetStart(aParentNode, aOffset);
4886 0 : if (NS_FAILED(result))
4887 0 : return result;
4888 :
4889 : #ifdef DEBUG_SELECTION
4890 : if (aParentNode) {
4891 : nsCOMPtr<nsIContent> content = do_QueryInterface(aParentNode);
4892 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(aParentNode);
4893 : printf ("Sel. Collapse to %p %s %d\n", aParentNode,
4894 : content ? nsAtomCString(content->Tag()).get()
4895 : : (doc ? "DOCUMENT" : "???"),
4896 : aOffset);
4897 : }
4898 : #endif
4899 :
4900 0 : result = AddItem(range);
4901 0 : if (NS_FAILED(result))
4902 0 : return result;
4903 0 : setAnchorFocusRange(0);
4904 0 : selectFrames(presContext, range, true);
4905 0 : return mFrameSelection->NotifySelectionListeners(GetType());
4906 : }
4907 :
4908 : /*
4909 : * Sets the whole selection to be one point
4910 : * at the start of the current selection
4911 : */
4912 : NS_IMETHODIMP
4913 0 : nsTypedSelection::CollapseToStart()
4914 : {
4915 : PRInt32 cnt;
4916 0 : nsresult rv = GetRangeCount(&cnt);
4917 0 : if (NS_FAILED(rv) || cnt <= 0)
4918 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
4919 :
4920 : // Get the first range
4921 0 : nsRange* firstRange = mRanges[0].mRange;
4922 0 : if (!firstRange)
4923 0 : return NS_ERROR_FAILURE;
4924 :
4925 0 : return Collapse(firstRange->GetStartParent(), firstRange->StartOffset());
4926 : }
4927 :
4928 : /*
4929 : * Sets the whole selection to be one point
4930 : * at the end of the current selection
4931 : */
4932 : NS_IMETHODIMP
4933 0 : nsTypedSelection::CollapseToEnd()
4934 : {
4935 : PRInt32 cnt;
4936 0 : nsresult rv = GetRangeCount(&cnt);
4937 0 : if (NS_FAILED(rv) || cnt <= 0)
4938 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
4939 :
4940 : // Get the last range
4941 0 : nsRange* lastRange = mRanges[cnt - 1].mRange;
4942 0 : if (!lastRange)
4943 0 : return NS_ERROR_FAILURE;
4944 :
4945 0 : return Collapse(lastRange->GetEndParent(), lastRange->EndOffset());
4946 : }
4947 :
4948 : /*
4949 : * IsCollapsed -- is the whole selection just one point, or unset?
4950 : */
4951 : NS_IMETHODIMP
4952 0 : nsTypedSelection::GetIsCollapsed(bool* aIsCollapsed)
4953 : {
4954 0 : if (!aIsCollapsed)
4955 0 : return NS_ERROR_NULL_POINTER;
4956 :
4957 0 : PRInt32 cnt = (PRInt32)mRanges.Length();;
4958 0 : if (cnt == 0)
4959 : {
4960 0 : *aIsCollapsed = true;
4961 0 : return NS_OK;
4962 : }
4963 :
4964 0 : if (cnt != 1)
4965 : {
4966 0 : *aIsCollapsed = false;
4967 0 : return NS_OK;
4968 : }
4969 :
4970 0 : *aIsCollapsed = mRanges[0].mRange->Collapsed();
4971 0 : return NS_OK;
4972 : }
4973 :
4974 : NS_IMETHODIMP
4975 0 : nsTypedSelection::GetRangeCount(PRInt32* aRangeCount)
4976 : {
4977 0 : *aRangeCount = (PRInt32)mRanges.Length();
4978 :
4979 0 : return NS_OK;
4980 : }
4981 :
4982 : NS_IMETHODIMP
4983 0 : nsTypedSelection::GetRangeAt(PRInt32 aIndex, nsIDOMRange** aReturn)
4984 : {
4985 0 : *aReturn = mRanges.SafeElementAt(aIndex, sEmptyData).mRange;
4986 0 : if (!*aReturn) {
4987 0 : return NS_ERROR_DOM_INDEX_SIZE_ERR;
4988 : }
4989 :
4990 0 : NS_ADDREF(*aReturn);
4991 :
4992 0 : return NS_OK;
4993 : }
4994 :
4995 : nsRange*
4996 0 : nsTypedSelection::GetRangeAt(PRInt32 aIndex)
4997 : {
4998 0 : return mRanges.SafeElementAt(aIndex, sEmptyData).mRange;
4999 : }
5000 :
5001 : /*
5002 : utility function
5003 : */
5004 : nsresult
5005 0 : nsTypedSelection::CopyRangeToAnchorFocus(nsRange *aRange)
5006 : {
5007 : // XXXbz could we just clone into mAnchorFocusRange, or do consumers
5008 : // expect that pointer to not change across this call?
5009 0 : NS_ENSURE_STATE(mAnchorFocusRange);
5010 :
5011 0 : nsINode* startNode = aRange->GetStartParent();
5012 0 : nsINode* endNode = aRange->GetEndParent();
5013 0 : PRInt32 startOffset = aRange->StartOffset();
5014 0 : PRInt32 endOffset = aRange->EndOffset();;
5015 0 : if (NS_FAILED(mAnchorFocusRange->SetStart(startNode,startOffset)))
5016 : {
5017 : // XXXbz what is this doing exactly?
5018 0 : if (NS_FAILED(mAnchorFocusRange->SetEnd(endNode,endOffset)))
5019 0 : return NS_ERROR_FAILURE;//???
5020 0 : if (NS_FAILED(mAnchorFocusRange->SetStart(startNode,startOffset)))
5021 0 : return NS_ERROR_FAILURE;//???
5022 : }
5023 0 : else if (NS_FAILED(mAnchorFocusRange->SetEnd(endNode,endOffset)))
5024 0 : return NS_ERROR_FAILURE;//???
5025 0 : return NS_OK;
5026 : }
5027 :
5028 : void
5029 0 : nsTypedSelection::ReplaceAnchorFocusRange(nsRange *aRange)
5030 : {
5031 0 : nsRefPtr<nsPresContext> presContext;
5032 0 : GetPresContext(getter_AddRefs(presContext));
5033 0 : if (presContext) {
5034 0 : selectFrames(presContext, mAnchorFocusRange, false);
5035 0 : CopyRangeToAnchorFocus(aRange);
5036 0 : selectFrames(presContext, mAnchorFocusRange, true);
5037 : }
5038 0 : }
5039 :
5040 : /*
5041 : Notes which might come in handy for extend:
5042 :
5043 : We can tell the direction of the selection by asking for the anchors selection
5044 : if the begin is less than the end then we know the selection is to the "right".
5045 : else it is a backwards selection.
5046 : a = anchor
5047 : 1 = old cursor
5048 : 2 = new cursor
5049 :
5050 : if (a <= 1 && 1 <=2) a,1,2 or (a1,2)
5051 : if (a < 2 && 1 > 2) a,2,1
5052 : if (1 < a && a <2) 1,a,2
5053 : if (a > 2 && 2 >1) 1,2,a
5054 : if (2 < a && a <1) 2,a,1
5055 : if (a > 1 && 1 >2) 2,1,a
5056 : then execute
5057 : a 1 2 select from 1 to 2
5058 : a 2 1 deselect from 2 to 1
5059 : 1 a 2 deselect from 1 to a select from a to 2
5060 : 1 2 a deselect from 1 to 2
5061 : 2 1 a = continue selection from 2 to 1
5062 : */
5063 :
5064 :
5065 : /*
5066 : * Extend extends the selection away from the anchor.
5067 : * We don't need to know the direction, because we always change the focus.
5068 : */
5069 : NS_IMETHODIMP
5070 0 : nsTypedSelection::Extend(nsIDOMNode* aParentNode, PRInt32 aOffset)
5071 : {
5072 0 : nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParentNode);
5073 0 : return Extend(parentNode, aOffset);
5074 : }
5075 :
5076 : nsresult
5077 0 : nsTypedSelection::Extend(nsINode* aParentNode, PRInt32 aOffset)
5078 : {
5079 0 : if (!aParentNode)
5080 0 : return NS_ERROR_INVALID_ARG;
5081 :
5082 : // First, find the range containing the old focus point:
5083 0 : if (!mAnchorFocusRange)
5084 0 : return NS_ERROR_NOT_INITIALIZED;
5085 :
5086 0 : if (!mFrameSelection)
5087 0 : return NS_ERROR_NOT_INITIALIZED; // Can't do selection
5088 :
5089 : nsresult res;
5090 0 : if (!IsValidSelectionPoint(mFrameSelection, aParentNode))
5091 0 : return NS_ERROR_FAILURE;
5092 :
5093 : //mFrameSelection->InvalidateDesiredX();
5094 :
5095 0 : nsINode* anchorNode = GetAnchorNode();
5096 0 : nsINode* focusNode = GetFocusNode();
5097 0 : PRInt32 anchorOffset = GetAnchorOffset();
5098 0 : PRInt32 focusOffset = GetFocusOffset();
5099 :
5100 0 : if (focusNode == aParentNode && focusOffset == aOffset)
5101 0 : return NS_OK; //same node nothing to do!
5102 :
5103 0 : nsRefPtr<nsRange> range;
5104 0 : res = mAnchorFocusRange->CloneRange(getter_AddRefs(range));
5105 0 : if (NS_FAILED(res))
5106 0 : return res;
5107 : //range = mAnchorFocusRange;
5108 :
5109 0 : nsINode* startNode = range->GetStartParent();
5110 0 : nsINode* endNode = range->GetEndParent();
5111 0 : PRInt32 startOffset = range->StartOffset();
5112 0 : PRInt32 endOffset = range->EndOffset();;
5113 :
5114 0 : nsDirection dir = GetDirection();
5115 :
5116 : //compare anchor to old cursor.
5117 :
5118 0 : if (NS_FAILED(res))
5119 0 : return res;
5120 : // We pass |disconnected| to the following ComparePoints calls in order
5121 : // to avoid assertions, and there is no special handling required, since
5122 : // ComparePoints returns 1 in the disconnected case.
5123 0 : bool disconnected = false;
5124 : PRInt32 result1 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
5125 : focusNode, focusOffset,
5126 0 : &disconnected);
5127 : //compare old cursor to new cursor
5128 : PRInt32 result2 = nsContentUtils::ComparePoints(focusNode, focusOffset,
5129 : aParentNode, aOffset,
5130 0 : &disconnected);
5131 : //compare anchor to new cursor
5132 : PRInt32 result3 = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
5133 : aParentNode, aOffset,
5134 0 : &disconnected);
5135 :
5136 0 : if (result2 == 0) //not selecting anywhere
5137 0 : return NS_OK;
5138 :
5139 0 : nsRefPtr<nsPresContext> presContext;
5140 0 : GetPresContext(getter_AddRefs(presContext));
5141 0 : nsRefPtr<nsRange> difRange = new nsRange();
5142 0 : if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2 a,1,2
5143 : //select from 1 to 2 unless they are collapsed
5144 0 : res = range->SetEnd(aParentNode, aOffset);
5145 0 : if (NS_FAILED(res))
5146 0 : return res;
5147 0 : dir = eDirNext;
5148 0 : res = difRange->SetEnd(range->GetEndParent(), range->EndOffset());
5149 0 : res |= difRange->SetStart(focusNode, focusOffset);
5150 0 : if (NS_FAILED(res))
5151 0 : return res;
5152 0 : selectFrames(presContext, difRange , true);
5153 0 : res = CopyRangeToAnchorFocus(range);
5154 0 : if (NS_FAILED(res))
5155 0 : return res;
5156 : }
5157 0 : else if (result1 == 0 && result3 > 0){//2, a1
5158 : //select from 2 to 1a
5159 0 : dir = eDirPrevious;
5160 0 : res = range->SetStart(aParentNode, aOffset);
5161 0 : if (NS_FAILED(res))
5162 0 : return res;
5163 0 : selectFrames(presContext, range, true);
5164 0 : res = CopyRangeToAnchorFocus(range);
5165 0 : if (NS_FAILED(res))
5166 0 : return res;
5167 : }
5168 0 : else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21
5169 : //deselect from 2 to 1
5170 0 : res = difRange->SetEnd(focusNode, focusOffset);
5171 0 : res |= difRange->SetStart(aParentNode, aOffset);
5172 0 : if (NS_FAILED(res))
5173 0 : return res;
5174 :
5175 0 : res = range->SetEnd(aParentNode, aOffset);
5176 0 : if (NS_FAILED(res))
5177 0 : return res;
5178 0 : res = CopyRangeToAnchorFocus(range);
5179 0 : if (NS_FAILED(res))
5180 0 : return res;
5181 0 : selectFrames(presContext, difRange, false); // deselect now
5182 0 : difRange->SetEnd(range->GetEndParent(), range->EndOffset());
5183 0 : selectFrames(presContext, difRange, true); // must reselect last node maybe more
5184 : }
5185 0 : else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2
5186 0 : if (GetDirection() == eDirPrevious){
5187 0 : res = range->SetStart(endNode, endOffset);
5188 0 : if (NS_FAILED(res))
5189 0 : return res;
5190 : }
5191 0 : dir = eDirNext;
5192 0 : res = range->SetEnd(aParentNode, aOffset);
5193 0 : if (NS_FAILED(res))
5194 0 : return res;
5195 0 : if (focusNode != anchorNode || focusOffset != anchorOffset) {//if collapsed diff dont do anything
5196 0 : res = difRange->SetStart(focusNode, focusOffset);
5197 0 : res |= difRange->SetEnd(anchorNode, anchorOffset);
5198 0 : if (NS_FAILED(res))
5199 0 : return res;
5200 0 : res = CopyRangeToAnchorFocus(range);
5201 0 : if (NS_FAILED(res))
5202 0 : return res;
5203 : //deselect from 1 to a
5204 0 : selectFrames(presContext, difRange , false);
5205 : }
5206 : else
5207 : {
5208 0 : res = CopyRangeToAnchorFocus(range);
5209 0 : if (NS_FAILED(res))
5210 0 : return res;
5211 : }
5212 : //select from a to 2
5213 0 : selectFrames(presContext, range , true);
5214 : }
5215 0 : else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a
5216 : //deselect from 1 to 2
5217 0 : res = difRange->SetEnd(aParentNode, aOffset);
5218 0 : res |= difRange->SetStart(focusNode, focusOffset);
5219 0 : if (NS_FAILED(res))
5220 0 : return res;
5221 0 : dir = eDirPrevious;
5222 0 : res = range->SetStart(aParentNode, aOffset);
5223 0 : if (NS_FAILED(res))
5224 0 : return res;
5225 :
5226 0 : res = CopyRangeToAnchorFocus(range);
5227 0 : if (NS_FAILED(res))
5228 0 : return res;
5229 0 : selectFrames(presContext, difRange , false);
5230 0 : difRange->SetStart(range->GetStartParent(), range->StartOffset());
5231 0 : selectFrames(presContext, difRange, true);//must reselect last node
5232 : }
5233 0 : else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1
5234 0 : if (GetDirection() == eDirNext){
5235 0 : range->SetEnd(startNode, startOffset);
5236 : }
5237 0 : dir = eDirPrevious;
5238 0 : res = range->SetStart(aParentNode, aOffset);
5239 0 : if (NS_FAILED(res))
5240 0 : return res;
5241 : //deselect from a to 1
5242 0 : if (focusNode != anchorNode || focusOffset!= anchorOffset) {//if collapsed diff dont do anything
5243 0 : res = difRange->SetStart(anchorNode, anchorOffset);
5244 0 : res |= difRange->SetEnd(focusNode, focusOffset);
5245 0 : res |= CopyRangeToAnchorFocus(range);
5246 0 : if (NS_FAILED(res))
5247 0 : return res;
5248 0 : selectFrames(presContext, difRange, false);
5249 : }
5250 : else
5251 : {
5252 0 : res = CopyRangeToAnchorFocus(range);
5253 0 : if (NS_FAILED(res))
5254 0 : return res;
5255 : }
5256 : //select from 2 to a
5257 0 : selectFrames(presContext, range , true);
5258 : }
5259 0 : else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a
5260 : //select from 2 to 1
5261 0 : res = range->SetStart(aParentNode, aOffset);
5262 0 : if (NS_FAILED(res))
5263 0 : return res;
5264 0 : dir = eDirPrevious;
5265 0 : res = difRange->SetEnd(focusNode, focusOffset);
5266 0 : res |= difRange->SetStart(range->GetStartParent(), range->StartOffset());
5267 0 : if (NS_FAILED(res))
5268 0 : return res;
5269 :
5270 0 : selectFrames(presContext, difRange, true);
5271 0 : res = CopyRangeToAnchorFocus(range);
5272 0 : if (NS_FAILED(res))
5273 0 : return res;
5274 : }
5275 :
5276 : DEBUG_OUT_RANGE(range);
5277 : #ifdef DEBUG_SELECTION
5278 : if (eDirNext == mDirection)
5279 : printf(" direction = 1 LEFT TO RIGHT\n");
5280 : else
5281 : printf(" direction = 0 RIGHT TO LEFT\n");
5282 : #endif
5283 0 : SetDirection(dir);
5284 : #ifdef DEBUG_SELECTION
5285 : if (aParentNode)
5286 : {
5287 : nsCOMPtr<nsIContent>content;
5288 : content = do_QueryInterface(aParentNode);
5289 :
5290 : printf ("Sel. Extend to %p %s %d\n", content.get(),
5291 : nsAtomCString(content->Tag()).get(), aOffset);
5292 : }
5293 : else {
5294 : printf ("Sel. Extend set to null parent.\n");
5295 : }
5296 : #endif
5297 0 : return mFrameSelection->NotifySelectionListeners(GetType());
5298 : }
5299 :
5300 : static nsresult
5301 0 : GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset)
5302 : {
5303 0 : NS_ASSERTION((aChild && aParent), "bad args");
5304 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
5305 0 : nsCOMPtr<nsIContent> cChild = do_QueryInterface(aChild);
5306 :
5307 0 : if (!cChild || !content)
5308 0 : return NS_ERROR_NULL_POINTER;
5309 :
5310 0 : aOffset = content->IndexOf(cChild);
5311 :
5312 0 : return NS_OK;
5313 : }
5314 :
5315 : NS_IMETHODIMP
5316 0 : nsTypedSelection::SelectAllChildren(nsIDOMNode* aParentNode)
5317 : {
5318 0 : NS_ENSURE_ARG_POINTER(aParentNode);
5319 :
5320 0 : if (mFrameSelection)
5321 : {
5322 0 : mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
5323 : }
5324 0 : nsresult result = Collapse(aParentNode, 0);
5325 0 : if (NS_SUCCEEDED(result))
5326 : {
5327 0 : nsCOMPtr<nsIDOMNode>lastChild;
5328 0 : result = aParentNode->GetLastChild(getter_AddRefs(lastChild));
5329 0 : if ((NS_SUCCEEDED(result)) && lastChild)
5330 : {
5331 0 : PRInt32 numBodyChildren=0;
5332 0 : GetChildOffset(lastChild, aParentNode, numBodyChildren);
5333 0 : if (mFrameSelection)
5334 : {
5335 0 : mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
5336 : }
5337 0 : result = Extend(aParentNode, numBodyChildren+1);
5338 : }
5339 : }
5340 0 : return result;
5341 : }
5342 :
5343 : NS_IMETHODIMP
5344 0 : nsTypedSelection::ContainsNode(nsIDOMNode* aNode, bool aAllowPartial,
5345 : bool* aYes)
5346 : {
5347 : nsresult rv;
5348 0 : if (!aYes)
5349 0 : return NS_ERROR_NULL_POINTER;
5350 0 : *aYes = false;
5351 :
5352 0 : nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
5353 0 : if (mRanges.Length() == 0 || !node)
5354 0 : return NS_OK;
5355 :
5356 : // XXXbz this duplicates the GetNodeLength code in nsRange.cpp
5357 : PRUint32 nodeLength;
5358 0 : bool isData = node->IsNodeOfType(nsINode::eDATA_NODE);
5359 0 : if (isData) {
5360 0 : nodeLength = static_cast<nsIContent*>(node.get())->TextLength();
5361 : } else {
5362 0 : nodeLength = node->GetChildCount();
5363 : }
5364 :
5365 0 : nsTArray<nsRange*> overlappingRanges;
5366 : rv = GetRangesForIntervalArray(node, 0, node, nodeLength,
5367 0 : false, &overlappingRanges);
5368 0 : NS_ENSURE_SUCCESS(rv, rv);
5369 0 : if (overlappingRanges.Length() == 0)
5370 0 : return NS_OK; // no ranges overlap
5371 :
5372 : // if the caller said partial intersections are OK, we're done
5373 0 : if (aAllowPartial) {
5374 0 : *aYes = true;
5375 0 : return NS_OK;
5376 : }
5377 :
5378 : // text nodes always count as inside
5379 0 : if (isData) {
5380 0 : *aYes = true;
5381 0 : return NS_OK;
5382 : }
5383 :
5384 : // The caller wants to know if the node is entirely within the given range,
5385 : // so we have to check all intersecting ranges.
5386 0 : for (PRUint32 i = 0; i < overlappingRanges.Length(); i++) {
5387 : bool nodeStartsBeforeRange, nodeEndsAfterRange;
5388 0 : if (NS_SUCCEEDED(nsRange::CompareNodeToRange(node, overlappingRanges[i],
5389 : &nodeStartsBeforeRange,
5390 : &nodeEndsAfterRange))) {
5391 0 : if (!nodeStartsBeforeRange && !nodeEndsAfterRange) {
5392 0 : *aYes = true;
5393 0 : return NS_OK;
5394 : }
5395 : }
5396 : }
5397 0 : return NS_OK;
5398 : }
5399 :
5400 :
5401 : nsresult
5402 0 : nsTypedSelection::GetPresContext(nsPresContext **aPresContext)
5403 : {
5404 0 : if (!mFrameSelection)
5405 0 : return NS_ERROR_FAILURE;//nothing to do
5406 0 : nsIPresShell *shell = mFrameSelection->GetShell();
5407 :
5408 0 : if (!shell)
5409 0 : return NS_ERROR_NULL_POINTER;
5410 :
5411 0 : NS_IF_ADDREF(*aPresContext = shell->GetPresContext());
5412 0 : return NS_OK;
5413 : }
5414 :
5415 : nsresult
5416 0 : nsTypedSelection::GetPresShell(nsIPresShell **aPresShell)
5417 : {
5418 0 : if (mPresShellWeak)
5419 : {
5420 0 : nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShellWeak);
5421 0 : if (presShell)
5422 0 : NS_ADDREF(*aPresShell = presShell);
5423 0 : return NS_OK;
5424 : }
5425 0 : nsresult rv = NS_OK;
5426 0 : if (!mFrameSelection)
5427 0 : return NS_ERROR_FAILURE;//nothing to do
5428 :
5429 0 : nsIPresShell *shell = mFrameSelection->GetShell();
5430 :
5431 0 : mPresShellWeak = do_GetWeakReference(shell); // the presshell owns us, so no addref
5432 0 : if (mPresShellWeak)
5433 0 : NS_ADDREF(*aPresShell = shell);
5434 0 : return rv;
5435 : }
5436 :
5437 : nsIFrame *
5438 0 : nsTypedSelection::GetSelectionAnchorGeometry(SelectionRegion aRegion,
5439 : nsRect *aRect)
5440 : {
5441 0 : if (!mFrameSelection)
5442 0 : return nsnull; // nothing to do
5443 :
5444 0 : NS_ENSURE_TRUE(aRect, nsnull);
5445 :
5446 0 : aRect->SetRect(0, 0, 0, 0);
5447 :
5448 0 : switch (aRegion) {
5449 : case nsISelectionController::SELECTION_ANCHOR_REGION:
5450 : case nsISelectionController::SELECTION_FOCUS_REGION:
5451 0 : return GetSelectionEndPointGeometry(aRegion, aRect);
5452 : break;
5453 : case nsISelectionController::SELECTION_WHOLE_SELECTION:
5454 : break;
5455 : default:
5456 0 : return nsnull;
5457 : }
5458 :
5459 0 : NS_ASSERTION(aRegion == nsISelectionController::SELECTION_WHOLE_SELECTION,
5460 : "should only be SELECTION_WHOLE_SELECTION here");
5461 :
5462 0 : nsRect anchorRect;
5463 : nsIFrame* anchorFrame = GetSelectionEndPointGeometry(
5464 0 : nsISelectionController::SELECTION_ANCHOR_REGION, &anchorRect);
5465 0 : if (!anchorFrame)
5466 0 : return nsnull;
5467 :
5468 0 : nsRect focusRect;
5469 : nsIFrame* focusFrame = GetSelectionEndPointGeometry(
5470 0 : nsISelectionController::SELECTION_FOCUS_REGION, &focusRect);
5471 0 : if (!focusFrame)
5472 0 : return nsnull;
5473 :
5474 0 : NS_ASSERTION(anchorFrame->PresContext() == focusFrame->PresContext(),
5475 : "points of selection in different documents?");
5476 : // make focusRect relative to anchorFrame
5477 0 : focusRect += focusFrame->GetOffsetTo(anchorFrame);
5478 :
5479 0 : aRect->UnionRectEdges(anchorRect, focusRect);
5480 0 : return anchorFrame;
5481 : }
5482 :
5483 : nsIFrame *
5484 0 : nsTypedSelection::GetSelectionEndPointGeometry(SelectionRegion aRegion,
5485 : nsRect *aRect)
5486 : {
5487 0 : if (!mFrameSelection)
5488 0 : return nsnull; // nothing to do
5489 :
5490 0 : NS_ENSURE_TRUE(aRect, nsnull);
5491 :
5492 0 : aRect->SetRect(0, 0, 0, 0);
5493 :
5494 0 : nsINode *node = nsnull;
5495 0 : PRInt32 nodeOffset = 0;
5496 0 : nsIFrame *frame = nsnull;
5497 :
5498 0 : switch (aRegion) {
5499 : case nsISelectionController::SELECTION_ANCHOR_REGION:
5500 0 : node = GetAnchorNode();
5501 0 : nodeOffset = GetAnchorOffset();
5502 0 : break;
5503 : case nsISelectionController::SELECTION_FOCUS_REGION:
5504 0 : node = GetFocusNode();
5505 0 : nodeOffset = GetFocusOffset();
5506 0 : break;
5507 : default:
5508 0 : return nsnull;
5509 : }
5510 :
5511 0 : if (!node)
5512 0 : return nsnull;
5513 :
5514 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(node);
5515 0 : NS_ENSURE_TRUE(content.get(), nsnull);
5516 0 : PRInt32 frameOffset = 0;
5517 0 : frame = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset,
5518 : mFrameSelection->GetHint(),
5519 0 : &frameOffset);
5520 0 : if (!frame)
5521 0 : return nsnull;
5522 :
5523 : // Figure out what node type we have, then get the
5524 : // appropriate rect for it's nodeOffset.
5525 0 : bool isText = node->IsNodeOfType(nsINode::eTEXT);
5526 :
5527 0 : nsPoint pt(0, 0);
5528 0 : if (isText) {
5529 0 : nsIFrame* childFrame = nsnull;
5530 0 : frameOffset = 0;
5531 : nsresult rv =
5532 : frame->GetChildFrameContainingOffset(nodeOffset,
5533 0 : mFrameSelection->GetHint(),
5534 0 : &frameOffset, &childFrame);
5535 0 : if (NS_FAILED(rv))
5536 0 : return nsnull;
5537 0 : if (!childFrame)
5538 0 : return nsnull;
5539 :
5540 0 : frame = childFrame;
5541 :
5542 : // Get the x coordinate of the offset into the text frame.
5543 0 : rv = GetCachedFrameOffset(frame, nodeOffset, pt);
5544 0 : if (NS_FAILED(rv))
5545 0 : return nsnull;
5546 : }
5547 :
5548 : // Return the rect relative to the frame, with zero width.
5549 0 : if (isText) {
5550 0 : aRect->x = pt.x;
5551 0 : } else if (mFrameSelection->GetHint() == nsFrameSelection::HINTLEFT) {
5552 : // It's the frame's right edge we're interested in.
5553 0 : aRect->x = frame->GetRect().width;
5554 : }
5555 0 : aRect->height = frame->GetRect().height;
5556 :
5557 0 : return frame;
5558 : }
5559 :
5560 : NS_IMETHODIMP
5561 0 : nsTypedSelection::ScrollSelectionIntoViewEvent::Run()
5562 : {
5563 0 : if (!mTypedSelection)
5564 0 : return NS_OK; // event revoked
5565 :
5566 : PRInt32 flags = nsTypedSelection::SCROLL_DO_FLUSH |
5567 0 : nsTypedSelection::SCROLL_SYNCHRONOUS;
5568 0 : if (mFirstAncestorOnly) {
5569 0 : flags |= nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY;
5570 : }
5571 :
5572 0 : mTypedSelection->mScrollEvent.Forget();
5573 : mTypedSelection->ScrollIntoView(mRegion, mVerticalScroll,
5574 0 : mHorizontalScroll, flags);
5575 0 : return NS_OK;
5576 : }
5577 :
5578 : nsresult
5579 0 : nsTypedSelection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion,
5580 : bool aFirstAncestorOnly,
5581 : PRInt16 aVPercent,
5582 : PRInt16 aHPercent)
5583 : {
5584 : // If we've already posted an event, revoke it and place a new one at the
5585 : // end of the queue to make sure that any new pending reflow events are
5586 : // processed before we scroll. This will insure that we scroll to the
5587 : // correct place on screen.
5588 0 : mScrollEvent.Revoke();
5589 :
5590 : nsRefPtr<ScrollSelectionIntoViewEvent> ev =
5591 : new ScrollSelectionIntoViewEvent(this, aRegion, aVPercent, aHPercent,
5592 0 : aFirstAncestorOnly);
5593 0 : nsresult rv = NS_DispatchToCurrentThread(ev);
5594 0 : NS_ENSURE_SUCCESS(rv, rv);
5595 :
5596 0 : mScrollEvent = ev;
5597 0 : return NS_OK;
5598 : }
5599 :
5600 : NS_IMETHODIMP
5601 0 : nsTypedSelection::ScrollIntoView(SelectionRegion aRegion, bool aIsSynchronous,
5602 : PRInt16 aVPercent, PRInt16 aHPercent)
5603 : {
5604 : return ScrollIntoView(aRegion, aVPercent, aHPercent,
5605 0 : aIsSynchronous ? nsTypedSelection::SCROLL_SYNCHRONOUS : 0);
5606 : }
5607 :
5608 : nsresult
5609 0 : nsTypedSelection::ScrollIntoView(SelectionRegion aRegion,
5610 : PRInt16 aVPercent, PRInt16 aHPercent,
5611 : PRInt32 aFlags)
5612 : {
5613 : nsresult result;
5614 0 : if (!mFrameSelection)
5615 0 : return NS_OK;//nothing to do
5616 :
5617 0 : if (mFrameSelection->GetBatching())
5618 0 : return NS_OK;
5619 :
5620 0 : if (!(aFlags & nsTypedSelection::SCROLL_SYNCHRONOUS))
5621 : return PostScrollSelectionIntoViewEvent(aRegion,
5622 : !!(aFlags & nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY),
5623 0 : aVPercent, aHPercent);
5624 :
5625 : //
5626 : // Shut the caret off before scrolling to avoid
5627 : // leaving caret turds on the screen!
5628 : //
5629 0 : nsCOMPtr<nsIPresShell> presShell;
5630 0 : result = GetPresShell(getter_AddRefs(presShell));
5631 0 : if (NS_FAILED(result) || !presShell)
5632 0 : return result;
5633 0 : nsRefPtr<nsCaret> caret = presShell->GetCaret();
5634 0 : if (caret)
5635 : {
5636 : // Now that text frame character offsets are always valid (though not
5637 : // necessarily correct), the worst that will happen if we don't flush here
5638 : // is that some callers might scroll to the wrong place. Those should
5639 : // either manually flush if they're in a safe position for it or use the
5640 : // async version of this method.
5641 0 : if (aFlags & nsTypedSelection::SCROLL_DO_FLUSH) {
5642 0 : presShell->FlushPendingNotifications(Flush_Layout);
5643 :
5644 : // Reget the presshell, since it might have gone away.
5645 0 : result = GetPresShell(getter_AddRefs(presShell));
5646 0 : if (NS_FAILED(result) || !presShell)
5647 0 : return result;
5648 : }
5649 :
5650 0 : StCaretHider caretHider(caret); // stack-based class hides and shows the caret
5651 :
5652 : //
5653 : // Scroll the selection region into view.
5654 : //
5655 :
5656 0 : nsRect rect;
5657 0 : nsIFrame* frame = GetSelectionAnchorGeometry(aRegion, &rect);
5658 0 : if (!frame)
5659 0 : return NS_ERROR_FAILURE;
5660 :
5661 0 : presShell->ScrollFrameRectIntoView(frame, rect, aVPercent, aHPercent,
5662 0 : (aFlags & nsTypedSelection::SCROLL_FIRST_ANCESTOR_ONLY) ? nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY: 0);
5663 0 : return NS_OK;
5664 : }
5665 0 : return result;
5666 : }
5667 :
5668 :
5669 :
5670 : NS_IMETHODIMP
5671 0 : nsTypedSelection::AddSelectionListener(nsISelectionListener* aNewListener)
5672 : {
5673 0 : if (!aNewListener)
5674 0 : return NS_ERROR_NULL_POINTER;
5675 0 : return mSelectionListeners.AppendObject(aNewListener) ? NS_OK : NS_ERROR_FAILURE; // addrefs
5676 : }
5677 :
5678 :
5679 :
5680 : NS_IMETHODIMP
5681 0 : nsTypedSelection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove)
5682 : {
5683 0 : if (!aListenerToRemove )
5684 0 : return NS_ERROR_NULL_POINTER;
5685 0 : return mSelectionListeners.RemoveObject(aListenerToRemove) ? NS_OK : NS_ERROR_FAILURE; // releases
5686 : }
5687 :
5688 :
5689 : nsresult
5690 0 : nsTypedSelection::NotifySelectionListeners()
5691 : {
5692 0 : if (!mFrameSelection)
5693 0 : return NS_OK;//nothing to do
5694 :
5695 0 : if (mFrameSelection->GetBatching()) {
5696 0 : mFrameSelection->SetDirty();
5697 0 : return NS_OK;
5698 : }
5699 0 : nsCOMArray<nsISelectionListener> selectionListeners(mSelectionListeners);
5700 0 : PRInt32 cnt = selectionListeners.Count();
5701 0 : if (cnt != mSelectionListeners.Count()) {
5702 0 : return NS_ERROR_OUT_OF_MEMORY; // nsCOMArray is fallible
5703 : }
5704 0 : nsCOMPtr<nsIDOMDocument> domdoc;
5705 0 : nsCOMPtr<nsIPresShell> shell;
5706 0 : nsresult rv = GetPresShell(getter_AddRefs(shell));
5707 0 : if (NS_SUCCEEDED(rv) && shell)
5708 0 : domdoc = do_QueryInterface(shell->GetDocument());
5709 0 : short reason = mFrameSelection->PopReason();
5710 0 : for (PRInt32 i = 0; i < cnt; i++) {
5711 0 : selectionListeners[i]->NotifySelectionChanged(domdoc, this, reason);
5712 : }
5713 0 : return NS_OK;
5714 : }
5715 :
5716 : NS_IMETHODIMP
5717 0 : nsTypedSelection::StartBatchChanges()
5718 : {
5719 0 : if (mFrameSelection)
5720 0 : mFrameSelection->StartBatchChanges();
5721 :
5722 0 : return NS_OK;
5723 : }
5724 :
5725 :
5726 :
5727 : NS_IMETHODIMP
5728 0 : nsTypedSelection::EndBatchChanges()
5729 : {
5730 0 : if (mFrameSelection)
5731 0 : mFrameSelection->EndBatchChanges();
5732 :
5733 0 : return NS_OK;
5734 : }
5735 :
5736 :
5737 :
5738 : NS_IMETHODIMP
5739 0 : nsTypedSelection::DeleteFromDocument()
5740 : {
5741 0 : if (!mFrameSelection)
5742 0 : return NS_OK;//nothing to do
5743 0 : return mFrameSelection->DeleteFromDocument();
5744 : }
5745 :
5746 : NS_IMETHODIMP
5747 0 : nsTypedSelection::Modify(const nsAString& aAlter, const nsAString& aDirection,
5748 : const nsAString& aGranularity)
5749 : {
5750 : // Silently exit if there's no selection or no focus node.
5751 0 : if (!mFrameSelection || !GetAnchorFocusRange() || !GetFocusNode()) {
5752 0 : return NS_OK;
5753 : }
5754 :
5755 0 : if (!aAlter.LowerCaseEqualsLiteral("move") &&
5756 0 : !aAlter.LowerCaseEqualsLiteral("extend")) {
5757 0 : return NS_ERROR_INVALID_ARG;
5758 : }
5759 :
5760 0 : if (!aDirection.LowerCaseEqualsLiteral("forward") &&
5761 0 : !aDirection.LowerCaseEqualsLiteral("backward") &&
5762 0 : !aDirection.LowerCaseEqualsLiteral("left") &&
5763 0 : !aDirection.LowerCaseEqualsLiteral("right")) {
5764 0 : return NS_ERROR_INVALID_ARG;
5765 : }
5766 :
5767 : // Line moves are always visual.
5768 0 : bool visual = aDirection.LowerCaseEqualsLiteral("left") ||
5769 0 : aDirection.LowerCaseEqualsLiteral("right") ||
5770 0 : aGranularity.LowerCaseEqualsLiteral("line");
5771 :
5772 0 : bool forward = aDirection.LowerCaseEqualsLiteral("forward") ||
5773 0 : aDirection.LowerCaseEqualsLiteral("right");
5774 :
5775 0 : bool extend = aAlter.LowerCaseEqualsLiteral("extend");
5776 :
5777 : // The PRUint32 casts below prevent an enum mismatch warning.
5778 : nsSelectionAmount amount;
5779 : PRUint32 keycode;
5780 0 : if (aGranularity.LowerCaseEqualsLiteral("character")) {
5781 0 : amount = eSelectCluster;
5782 : keycode = forward ? (PRUint32) nsIDOMKeyEvent::DOM_VK_RIGHT :
5783 0 : (PRUint32) nsIDOMKeyEvent::DOM_VK_LEFT;
5784 : }
5785 0 : else if (aGranularity.LowerCaseEqualsLiteral("word")) {
5786 0 : amount = eSelectWordNoSpace;
5787 : keycode = forward ? (PRUint32) nsIDOMKeyEvent::DOM_VK_RIGHT :
5788 0 : (PRUint32) nsIDOMKeyEvent::DOM_VK_LEFT;
5789 : }
5790 0 : else if (aGranularity.LowerCaseEqualsLiteral("line")) {
5791 0 : amount = eSelectLine;
5792 : keycode = forward ? (PRUint32) nsIDOMKeyEvent::DOM_VK_DOWN :
5793 0 : (PRUint32) nsIDOMKeyEvent::DOM_VK_UP;
5794 : }
5795 0 : else if (aGranularity.LowerCaseEqualsLiteral("lineboundary")) {
5796 0 : amount = eSelectLine;
5797 : keycode = forward ? (PRUint32) nsIDOMKeyEvent::DOM_VK_END :
5798 0 : (PRUint32) nsIDOMKeyEvent::DOM_VK_HOME;
5799 : }
5800 0 : else if (aGranularity.LowerCaseEqualsLiteral("sentence") ||
5801 0 : aGranularity.LowerCaseEqualsLiteral("sentenceboundary") ||
5802 0 : aGranularity.LowerCaseEqualsLiteral("paragraph") ||
5803 0 : aGranularity.LowerCaseEqualsLiteral("paragraphboundary") ||
5804 0 : aGranularity.LowerCaseEqualsLiteral("documentboundary")) {
5805 0 : return NS_ERROR_NOT_IMPLEMENTED;
5806 : }
5807 : else {
5808 0 : return NS_ERROR_INVALID_ARG;
5809 : }
5810 :
5811 : // If the anchor doesn't equal the focus and we try to move without first
5812 : // collapsing the selection, MoveCaret will collapse the selection and quit.
5813 : // To avoid this, we need to collapse the selection first.
5814 0 : nsresult rv = NS_OK;
5815 0 : if (!extend) {
5816 0 : nsINode* focusNode = GetFocusNode();
5817 : // We should have checked earlier that there was a focus node.
5818 0 : NS_ENSURE_TRUE(focusNode, NS_ERROR_UNEXPECTED);
5819 0 : PRInt32 focusOffset = GetFocusOffset();
5820 0 : Collapse(focusNode, focusOffset);
5821 : }
5822 :
5823 : // If the base level of the focused frame is odd, we may have to swap the
5824 : // direction of the keycode.
5825 : nsIFrame *frame;
5826 : PRInt32 offset;
5827 0 : rv = GetPrimaryFrameForFocusNode(&frame, &offset, visual);
5828 0 : if (NS_SUCCEEDED(rv) && frame) {
5829 0 : nsBidiLevel baseLevel = nsBidiPresUtils::GetFrameBaseLevel(frame);
5830 :
5831 0 : if (baseLevel & 1) {
5832 0 : if (!visual && keycode == nsIDOMKeyEvent::DOM_VK_RIGHT) {
5833 0 : keycode = nsIDOMKeyEvent::DOM_VK_LEFT;
5834 : }
5835 0 : else if (!visual && keycode == nsIDOMKeyEvent::DOM_VK_LEFT) {
5836 0 : keycode = nsIDOMKeyEvent::DOM_VK_RIGHT;
5837 : }
5838 0 : else if (visual && keycode == nsIDOMKeyEvent::DOM_VK_HOME) {
5839 0 : keycode = nsIDOMKeyEvent::DOM_VK_END;
5840 : }
5841 0 : else if (visual && keycode == nsIDOMKeyEvent::DOM_VK_END) {
5842 0 : keycode = nsIDOMKeyEvent::DOM_VK_HOME;
5843 : }
5844 : }
5845 : }
5846 :
5847 : // MoveCaret will return an error if it can't move in the specified
5848 : // direction, but we just ignore this error unless it's a line move, in which
5849 : // case we call nsISelectionController::CompleteMove to move the cursor to
5850 : // the beginning/end of the line.
5851 0 : rv = mFrameSelection->MoveCaret(keycode, extend, amount, visual);
5852 :
5853 0 : if (aGranularity.LowerCaseEqualsLiteral("line") && NS_FAILED(rv)) {
5854 : nsCOMPtr<nsISelectionController> shell =
5855 0 : do_QueryInterface(mFrameSelection->GetShell());
5856 0 : if (!shell)
5857 0 : return NS_OK;
5858 0 : shell->CompleteMove(forward, extend);
5859 : }
5860 0 : return NS_OK;
5861 : }
5862 :
5863 : /** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction
5864 : * @param aLangRTL is true if the new language is right-to-left or false if the new language is left-to-right
5865 : */
5866 : NS_IMETHODIMP
5867 0 : nsTypedSelection::SelectionLanguageChange(bool aLangRTL)
5868 : {
5869 0 : if (!mFrameSelection)
5870 0 : return NS_ERROR_NOT_INITIALIZED; // Can't do selection
5871 : nsresult result;
5872 0 : nsIFrame *focusFrame = 0;
5873 :
5874 0 : result = GetPrimaryFrameForFocusNode(&focusFrame, nsnull, false);
5875 0 : if (NS_FAILED(result)) {
5876 0 : return result;
5877 : }
5878 0 : if (!focusFrame) {
5879 0 : return NS_ERROR_FAILURE;
5880 : }
5881 :
5882 : PRInt32 frameStart, frameEnd;
5883 0 : focusFrame->GetOffsets(frameStart, frameEnd);
5884 0 : nsRefPtr<nsPresContext> context;
5885 : PRUint8 levelBefore, levelAfter;
5886 0 : result = GetPresContext(getter_AddRefs(context));
5887 0 : if (NS_FAILED(result) || !context)
5888 0 : return result?result:NS_ERROR_FAILURE;
5889 :
5890 0 : PRUint8 level = NS_GET_EMBEDDING_LEVEL(focusFrame);
5891 0 : PRInt32 focusOffset = GetFocusOffset();
5892 0 : if ((focusOffset != frameStart) && (focusOffset != frameEnd))
5893 : // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor
5894 : // is equal to the frame level
5895 0 : levelBefore = levelAfter = level;
5896 : else {
5897 : // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters
5898 : // before and after the cursor
5899 0 : nsCOMPtr<nsIContent> focusContent = do_QueryInterface(GetFocusNode());
5900 : /*
5901 : nsFrameSelection::HINT hint;
5902 :
5903 : if ((focusOffset == frameStart && level) // beginning of an RTL frame
5904 : || (focusOffset == frameEnd && !level)) { // end of an LTR frame
5905 : hint = nsFrameSelection::HINTRIGHT;
5906 : }
5907 : else { // end of an RTL frame or beginning of an LTR frame
5908 : hint = nsFrameSelection::HINTLEFT;
5909 : }
5910 : mFrameSelection->SetHint(hint);
5911 : */
5912 0 : nsPrevNextBidiLevels levels = mFrameSelection->
5913 0 : GetPrevNextBidiLevels(focusContent, focusOffset, false);
5914 :
5915 0 : levelBefore = levels.mLevelBefore;
5916 0 : levelAfter = levels.mLevelAfter;
5917 : }
5918 :
5919 0 : if ((levelBefore & 1) == (levelAfter & 1)) {
5920 : // if cursor is between two characters with the same orientation, changing the keyboard language
5921 : // must toggle the cursor level between the level of the character with the lowest level
5922 : // (if the new language corresponds to the orientation of that character) and this level plus 1
5923 : // (if the new language corresponds to the opposite orientation)
5924 0 : if ((level != levelBefore) && (level != levelAfter))
5925 0 : level = NS_MIN(levelBefore, levelAfter);
5926 0 : if ((level & 1) == aLangRTL)
5927 0 : mFrameSelection->SetCaretBidiLevel(level);
5928 : else
5929 0 : mFrameSelection->SetCaretBidiLevel(level + 1);
5930 : }
5931 : else {
5932 : // if cursor is between characters with opposite orientations, changing the keyboard language must change
5933 : // the cursor level to that of the adjacent character with the orientation corresponding to the new language.
5934 0 : if ((levelBefore & 1) == aLangRTL)
5935 0 : mFrameSelection->SetCaretBidiLevel(levelBefore);
5936 : else
5937 0 : mFrameSelection->SetCaretBidiLevel(levelAfter);
5938 : }
5939 :
5940 : // The caret might have moved, so invalidate the desired X position
5941 : // for future usages of up-arrow or down-arrow
5942 0 : mFrameSelection->InvalidateDesiredX();
5943 :
5944 0 : return NS_OK;
5945 : }
5946 :
5947 : NS_IMETHODIMP_(nsDirection)
5948 0 : nsTypedSelection::GetSelectionDirection() {
5949 0 : return mDirection;
5950 : }
5951 :
5952 : NS_IMETHODIMP_(void)
5953 0 : nsTypedSelection::SetSelectionDirection(nsDirection aDirection) {
5954 0 : mDirection = aDirection;
5955 0 : }
5956 :
5957 :
5958 : // nsAutoCopyListener
5959 :
5960 : nsAutoCopyListener* nsAutoCopyListener::sInstance = nsnull;
5961 :
5962 0 : NS_IMPL_ISUPPORTS1(nsAutoCopyListener, nsISelectionListener)
5963 :
5964 : /*
5965 : * What we do now:
5966 : * On every selection change, we copy to the clipboard anew, creating a
5967 : * HTML buffer, a transferable, an nsISupportsString and
5968 : * a huge mess every time. This is basically what nsPresShell::DoCopy does
5969 : * to move the selection into the clipboard for Edit->Copy.
5970 : *
5971 : * What we should do, to make our end of the deal faster:
5972 : * Create a singleton transferable with our own magic converter. When selection
5973 : * changes (use a quick cache to detect ``real'' changes), we put the new
5974 : * nsISelection in the transferable. Our magic converter will take care of
5975 : * transferable->whatever-other-format when the time comes to actually
5976 : * hand over the clipboard contents.
5977 : *
5978 : * Other issues:
5979 : * - which X clipboard should we populate?
5980 : * - should we use a different one than Edit->Copy, so that inadvertant
5981 : * selections (or simple clicks, which currently cause a selection
5982 : * notification, regardless of if they're in the document which currently has
5983 : * selection!) don't lose the contents of the ``application''? Or should we
5984 : * just put some intelligence in the ``is this a real selection?'' code to
5985 : * protect our selection against clicks in other documents that don't create
5986 : * selections?
5987 : * - maybe we should just never clear the X clipboard? That would make this
5988 : * problem just go away, which is very tempting.
5989 : */
5990 :
5991 : NS_IMETHODIMP
5992 0 : nsAutoCopyListener::NotifySelectionChanged(nsIDOMDocument *aDoc,
5993 : nsISelection *aSel, PRInt16 aReason)
5994 : {
5995 0 : if (!(aReason & nsISelectionListener::MOUSEUP_REASON ||
5996 : aReason & nsISelectionListener::SELECTALL_REASON ||
5997 0 : aReason & nsISelectionListener::KEYPRESS_REASON))
5998 0 : return NS_OK; //dont care if we are still dragging
5999 :
6000 : bool collapsed;
6001 0 : if (!aDoc || !aSel ||
6002 0 : NS_FAILED(aSel->GetIsCollapsed(&collapsed)) || collapsed) {
6003 : #ifdef DEBUG_CLIPBOARD
6004 : fprintf(stderr, "CLIPBOARD: no selection/collapsed selection\n");
6005 : #endif
6006 : /* clear X clipboard? */
6007 0 : return NS_OK;
6008 : }
6009 :
6010 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
6011 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
6012 :
6013 : // call the copy code
6014 0 : return nsCopySupport::HTMLCopy(aSel, doc, nsIClipboard::kSelectionClipboard);
6015 4392 : }
|