1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=80: */
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 : * Mozilla Japan.
20 : * Portions created by the Initial Developer are Copyright (C) 2008
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Masayuki Nakano <masayuki@d-toybox.com>
25 : * Ningjie Chen <chenn@email.uc.edu>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsContentEventHandler.h"
42 : #include "nsCOMPtr.h"
43 : #include "nsPresContext.h"
44 : #include "nsIPresShell.h"
45 : #include "nsISelection.h"
46 : #include "nsIDOMRange.h"
47 : #include "nsRange.h"
48 : #include "nsGUIEvent.h"
49 : #include "nsCaret.h"
50 : #include "nsCopySupport.h"
51 : #include "nsFrameSelection.h"
52 : #include "nsIFrame.h"
53 : #include "nsIView.h"
54 : #include "nsIContentIterator.h"
55 : #include "nsTextFragment.h"
56 : #include "nsTextFrame.h"
57 : #include "nsISelectionController.h"
58 : #include "nsISelectionPrivate.h"
59 : #include "nsContentUtils.h"
60 : #include "nsLayoutUtils.h"
61 : #include "nsIMEStateManager.h"
62 : #include "nsIObjectFrame.h"
63 :
64 : nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
65 :
66 : /******************************************************************/
67 : /* nsContentEventHandler */
68 : /******************************************************************/
69 :
70 0 : nsContentEventHandler::nsContentEventHandler(
71 : nsPresContext* aPresContext) :
72 : mPresContext(aPresContext),
73 : mPresShell(aPresContext->GetPresShell()), mSelection(nsnull),
74 0 : mFirstSelectedRange(nsnull), mRootContent(nsnull)
75 : {
76 0 : }
77 :
78 : nsresult
79 0 : nsContentEventHandler::InitCommon()
80 : {
81 0 : if (mSelection)
82 0 : return NS_OK;
83 :
84 0 : NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
85 :
86 : // If text frame which has overflowing selection underline is dirty,
87 : // we need to flush the pending reflow here.
88 0 : mPresShell->FlushPendingNotifications(Flush_Layout);
89 :
90 : // Flushing notifications can cause mPresShell to be destroyed (bug 577963).
91 0 : NS_ENSURE_TRUE(!mPresShell->IsDestroying(), NS_ERROR_FAILURE);
92 :
93 : nsCopySupport::GetSelectionForCopy(mPresShell->GetDocument(),
94 0 : getter_AddRefs(mSelection));
95 :
96 0 : nsCOMPtr<nsIDOMRange> firstRange;
97 0 : nsresult rv = mSelection->GetRangeAt(0, getter_AddRefs(firstRange));
98 : // This shell doesn't support selection.
99 0 : if (NS_FAILED(rv))
100 0 : return NS_ERROR_NOT_AVAILABLE;
101 0 : mFirstSelectedRange = static_cast<nsRange*>(firstRange.get());
102 :
103 0 : nsINode* startNode = mFirstSelectedRange->GetStartParent();
104 0 : NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
105 0 : nsINode* endNode = mFirstSelectedRange->GetEndParent();
106 0 : NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
107 :
108 : // See bug 537041 comment 5, the range could have removed node.
109 0 : NS_ENSURE_TRUE(startNode->GetCurrentDoc() == mPresShell->GetDocument(),
110 : NS_ERROR_NOT_AVAILABLE);
111 0 : NS_ASSERTION(startNode->GetCurrentDoc() == endNode->GetCurrentDoc(),
112 : "mFirstSelectedRange crosses the document boundary");
113 :
114 0 : mRootContent = startNode->GetSelectionRootContent(mPresShell);
115 0 : NS_ENSURE_TRUE(mRootContent, NS_ERROR_FAILURE);
116 0 : return NS_OK;
117 : }
118 :
119 : nsresult
120 0 : nsContentEventHandler::Init(nsQueryContentEvent* aEvent)
121 : {
122 0 : NS_ASSERTION(aEvent, "aEvent must not be null");
123 :
124 0 : nsresult rv = InitCommon();
125 0 : NS_ENSURE_SUCCESS(rv, rv);
126 :
127 0 : aEvent->mSucceeded = false;
128 :
129 0 : aEvent->mReply.mContentsRoot = mRootContent.get();
130 :
131 : bool isCollapsed;
132 0 : rv = mSelection->GetIsCollapsed(&isCollapsed);
133 0 : NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_AVAILABLE);
134 0 : aEvent->mReply.mHasSelection = !isCollapsed;
135 :
136 0 : nsRefPtr<nsCaret> caret = mPresShell->GetCaret();
137 0 : NS_ASSERTION(caret, "GetCaret returned null");
138 :
139 0 : nsRect r;
140 0 : nsIFrame* frame = caret->GetGeometry(mSelection, &r);
141 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
142 :
143 0 : aEvent->mReply.mFocusedWidget = frame->GetNearestWidget();
144 :
145 0 : return NS_OK;
146 : }
147 :
148 : nsresult
149 0 : nsContentEventHandler::Init(nsSelectionEvent* aEvent)
150 : {
151 0 : NS_ASSERTION(aEvent, "aEvent must not be null");
152 :
153 0 : nsresult rv = InitCommon();
154 0 : NS_ENSURE_SUCCESS(rv, rv);
155 :
156 0 : aEvent->mSucceeded = false;
157 :
158 0 : return NS_OK;
159 : }
160 :
161 : // Editor places a bogus BR node under its root content if the editor doesn't
162 : // have any text. This happens even for single line editors.
163 : // When we get text content and when we change the selection,
164 : // we don't want to include the bogus BRs at the end.
165 0 : static bool IsContentBR(nsIContent* aContent)
166 : {
167 0 : return aContent->IsHTML() &&
168 0 : aContent->Tag() == nsGkAtoms::br &&
169 : !aContent->AttrValueIs(kNameSpaceID_None,
170 : nsGkAtoms::type,
171 : nsGkAtoms::moz,
172 0 : eIgnoreCase) &&
173 : !aContent->AttrValueIs(kNameSpaceID_None,
174 : nsGkAtoms::mozeditorbogusnode,
175 : nsGkAtoms::_true,
176 0 : eIgnoreCase);
177 : }
178 :
179 0 : static void ConvertToNativeNewlines(nsAFlatString& aString)
180 : {
181 : #if defined(XP_MACOSX)
182 : aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r"));
183 : #elif defined(XP_WIN)
184 : aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
185 : #endif
186 0 : }
187 :
188 0 : static void AppendString(nsAString& aString, nsIContent* aContent)
189 : {
190 0 : NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
191 : "aContent is not a text node!");
192 0 : const nsTextFragment* text = aContent->GetText();
193 0 : if (!text)
194 0 : return;
195 0 : text->AppendTo(aString);
196 : }
197 :
198 0 : static void AppendSubString(nsAString& aString, nsIContent* aContent,
199 : PRUint32 aXPOffset, PRUint32 aXPLength)
200 : {
201 0 : NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
202 : "aContent is not a text node!");
203 0 : const nsTextFragment* text = aContent->GetText();
204 0 : if (!text)
205 0 : return;
206 0 : text->AppendTo(aString, PRInt32(aXPOffset), PRInt32(aXPLength));
207 : }
208 :
209 : #if defined(XP_WIN)
210 : static PRUint32 CountNewlinesInXPLength(nsIContent* aContent,
211 : PRUint32 aXPLength)
212 : {
213 : NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
214 : "aContent is not a text node!");
215 : const nsTextFragment* text = aContent->GetText();
216 : if (!text)
217 : return 0;
218 : // For automated tests, we should abort on debug build.
219 : NS_ABORT_IF_FALSE(
220 : (aXPLength == PR_UINT32_MAX || aXPLength <= text->GetLength()),
221 : "aXPLength is out-of-bounds");
222 : const PRUint32 length = NS_MIN(aXPLength, text->GetLength());
223 : PRUint32 newlines = 0;
224 : for (PRUint32 i = 0; i < length; ++i) {
225 : if (text->CharAt(i) == '\n') {
226 : ++newlines;
227 : }
228 : }
229 : return newlines;
230 : }
231 :
232 : static PRUint32 CountNewlinesInNativeLength(nsIContent* aContent,
233 : PRUint32 aNativeLength)
234 : {
235 : NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
236 : "aContent is not a text node!");
237 : const nsTextFragment* text = aContent->GetText();
238 : if (!text) {
239 : return 0;
240 : }
241 : // For automated tests, we should abort on debug build.
242 : NS_ABORT_IF_FALSE(
243 : (aNativeLength == PR_UINT32_MAX || aNativeLength <= text->GetLength() * 2),
244 : "aNativeLength is unexpected value");
245 : const PRUint32 xpLength = text->GetLength();
246 : PRUint32 newlines = 0;
247 : for (PRUint32 i = 0, nativeOffset = 0;
248 : i < xpLength && nativeOffset < aNativeLength;
249 : ++i, ++nativeOffset) {
250 : // For automated tests, we should abort on debug build.
251 : NS_ABORT_IF_FALSE(i < text->GetLength(), "i is out-of-bounds");
252 : if (text->CharAt(i) == '\n') {
253 : ++newlines;
254 : ++nativeOffset;
255 : }
256 : }
257 : return newlines;
258 : }
259 : #endif
260 :
261 0 : static PRUint32 GetNativeTextLength(nsIContent* aContent, PRUint32 aMaxLength = PR_UINT32_MAX)
262 : {
263 0 : if (aContent->IsNodeOfType(nsINode::eTEXT)) {
264 : PRUint32 textLengthDifference =
265 : #if defined(XP_MACOSX)
266 : // On Mac, the length of a native newline ("\r") is equal to the length of
267 : // the XP newline ("\n"), so the native length is the same as the XP length.
268 : 0;
269 : #elif defined(XP_WIN)
270 : // On Windows, the length of a native newline ("\r\n") is twice the length of
271 : // the XP newline ("\n"), so XP length is equal to the length of the native
272 : // offset plus the number of newlines encountered in the string.
273 : CountNewlinesInXPLength(aContent, aMaxLength);
274 : #else
275 : // On other platforms, the native and XP newlines are the same.
276 0 : 0;
277 : #endif
278 :
279 0 : const nsTextFragment* text = aContent->GetText();
280 0 : if (!text)
281 0 : return 0;
282 0 : PRUint32 length = NS_MIN(text->GetLength(), aMaxLength);
283 0 : return length + textLengthDifference;
284 0 : } else if (IsContentBR(aContent)) {
285 : #if defined(XP_WIN)
286 : // Length of \r\n
287 : return 2;
288 : #else
289 0 : return 1;
290 : #endif
291 : }
292 0 : return 0;
293 : }
294 :
295 0 : static PRUint32 ConvertToXPOffset(nsIContent* aContent, PRUint32 aNativeOffset)
296 : {
297 : #if defined(XP_MACOSX)
298 : // On Mac, the length of a native newline ("\r") is equal to the length of
299 : // the XP newline ("\n"), so the native offset is the same as the XP offset.
300 : return aNativeOffset;
301 : #elif defined(XP_WIN)
302 : // On Windows, the length of a native newline ("\r\n") is twice the length of
303 : // the XP newline ("\n"), so XP offset is equal to the length of the native
304 : // offset minus the number of newlines encountered in the string.
305 : return aNativeOffset - CountNewlinesInNativeLength(aContent, aNativeOffset);
306 : #else
307 : // On other platforms, the native and XP newlines are the same.
308 0 : return aNativeOffset;
309 : #endif
310 : }
311 :
312 0 : static nsresult GenerateFlatTextContent(nsRange* aRange,
313 : nsAFlatString& aString)
314 : {
315 0 : nsCOMPtr<nsIContentIterator> iter;
316 0 : nsresult rv = NS_NewContentIterator(getter_AddRefs(iter));
317 0 : NS_ENSURE_SUCCESS(rv, rv);
318 0 : NS_ASSERTION(iter, "NS_NewContentIterator succeeded, but the result is null");
319 0 : iter->Init(aRange);
320 :
321 0 : NS_ASSERTION(aString.IsEmpty(), "aString must be empty string");
322 :
323 0 : nsINode* startNode = aRange->GetStartParent();
324 0 : NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
325 0 : nsINode* endNode = aRange->GetEndParent();
326 0 : NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
327 :
328 0 : if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) {
329 0 : nsIContent* content = static_cast<nsIContent*>(startNode);
330 0 : AppendSubString(aString, content, aRange->StartOffset(),
331 0 : aRange->EndOffset() - aRange->StartOffset());
332 0 : ConvertToNativeNewlines(aString);
333 0 : return NS_OK;
334 : }
335 :
336 0 : nsAutoString tmpStr;
337 0 : for (; !iter->IsDone(); iter->Next()) {
338 0 : nsINode* node = iter->GetCurrentNode();
339 0 : if (!node || !node->IsNodeOfType(nsINode::eCONTENT))
340 0 : continue;
341 0 : nsIContent* content = static_cast<nsIContent*>(node);
342 :
343 0 : if (content->IsNodeOfType(nsINode::eTEXT)) {
344 0 : if (content == startNode)
345 0 : AppendSubString(aString, content, aRange->StartOffset(),
346 0 : content->TextLength() - aRange->StartOffset());
347 0 : else if (content == endNode)
348 0 : AppendSubString(aString, content, 0, aRange->EndOffset());
349 : else
350 0 : AppendString(aString, content);
351 0 : } else if (IsContentBR(content))
352 0 : aString.Append(PRUnichar('\n'));
353 : }
354 0 : ConvertToNativeNewlines(aString);
355 0 : return NS_OK;
356 : }
357 :
358 : nsresult
359 0 : nsContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
360 : bool aForward,
361 : PRUint32* aXPOffset)
362 : {
363 : // XXX This method assumes that the frame boundaries must be cluster
364 : // boundaries. It's false, but no problem now, maybe.
365 0 : if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
366 0 : *aXPOffset == 0 || *aXPOffset == aContent->TextLength())
367 0 : return NS_OK;
368 :
369 0 : NS_ASSERTION(*aXPOffset >= 0 && *aXPOffset <= aContent->TextLength(),
370 : "offset is out of range.");
371 :
372 0 : nsRefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
373 : PRInt32 offsetInFrame;
374 : nsFrameSelection::HINT hint =
375 0 : aForward ? nsFrameSelection::HINTLEFT : nsFrameSelection::HINTRIGHT;
376 0 : nsIFrame* frame = fs->GetFrameForNodeOffset(aContent, PRInt32(*aXPOffset),
377 0 : hint, &offsetInFrame);
378 0 : if (!frame) {
379 : // This content doesn't have any frames, we only can check surrogate pair...
380 0 : const nsTextFragment* text = aContent->GetText();
381 0 : NS_ENSURE_TRUE(text, NS_ERROR_FAILURE);
382 0 : if (NS_IS_LOW_SURROGATE(text->CharAt(*aXPOffset)) &&
383 0 : NS_IS_HIGH_SURROGATE(text->CharAt(*aXPOffset - 1)))
384 0 : *aXPOffset += aForward ? 1 : -1;
385 0 : return NS_OK;
386 : }
387 : PRInt32 startOffset, endOffset;
388 0 : nsresult rv = frame->GetOffsets(startOffset, endOffset);
389 0 : NS_ENSURE_SUCCESS(rv, rv);
390 0 : if (*aXPOffset == PRUint32(startOffset) || *aXPOffset == PRUint32(endOffset))
391 0 : return NS_OK;
392 0 : if (frame->GetType() != nsGkAtoms::textFrame)
393 0 : return NS_ERROR_FAILURE;
394 0 : nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
395 0 : PRInt32 newOffsetInFrame = *aXPOffset - startOffset;
396 0 : newOffsetInFrame += aForward ? -1 : 1;
397 0 : textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame);
398 0 : *aXPOffset = startOffset + newOffsetInFrame;
399 0 : return NS_OK;
400 : }
401 :
402 : nsresult
403 0 : nsContentEventHandler::SetRangeFromFlatTextOffset(
404 : nsRange* aRange,
405 : PRUint32 aNativeOffset,
406 : PRUint32 aNativeLength,
407 : bool aExpandToClusterBoundaries)
408 : {
409 0 : nsCOMPtr<nsIContentIterator> iter;
410 0 : nsresult rv = NS_NewContentIterator(getter_AddRefs(iter));
411 0 : NS_ENSURE_SUCCESS(rv, rv);
412 0 : NS_ASSERTION(iter, "NS_NewContentIterator succeeded, but the result is null");
413 0 : rv = iter->Init(mRootContent);
414 0 : NS_ENSURE_SUCCESS(rv, rv);
415 :
416 0 : PRUint32 nativeOffset = 0;
417 0 : PRUint32 nativeEndOffset = aNativeOffset + aNativeLength;
418 0 : nsCOMPtr<nsIContent> content;
419 0 : for (; !iter->IsDone(); iter->Next()) {
420 0 : nsINode* node = iter->GetCurrentNode();
421 0 : if (!node || !node->IsNodeOfType(nsINode::eCONTENT))
422 0 : continue;
423 0 : nsIContent* content = static_cast<nsIContent*>(node);
424 :
425 : PRUint32 nativeTextLength;
426 0 : nativeTextLength = GetNativeTextLength(content);
427 0 : if (nativeTextLength == 0)
428 0 : continue;
429 :
430 0 : if (nativeOffset <= aNativeOffset &&
431 : aNativeOffset < nativeOffset + nativeTextLength) {
432 0 : nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
433 0 : NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");
434 :
435 : PRUint32 xpOffset =
436 0 : content->IsNodeOfType(nsINode::eTEXT) ?
437 0 : ConvertToXPOffset(content, aNativeOffset - nativeOffset) : 0;
438 :
439 0 : if (aExpandToClusterBoundaries) {
440 0 : rv = ExpandToClusterBoundary(content, false, &xpOffset);
441 0 : NS_ENSURE_SUCCESS(rv, rv);
442 : }
443 :
444 0 : rv = aRange->SetStart(domNode, PRInt32(xpOffset));
445 0 : NS_ENSURE_SUCCESS(rv, rv);
446 0 : if (aNativeLength == 0) {
447 : // Ensure that the end offset and the start offset are same.
448 0 : rv = aRange->SetEnd(domNode, PRInt32(xpOffset));
449 0 : NS_ENSURE_SUCCESS(rv, rv);
450 0 : return NS_OK;
451 : }
452 : }
453 0 : if (nativeEndOffset <= nativeOffset + nativeTextLength) {
454 0 : nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
455 0 : NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");
456 :
457 : PRUint32 xpOffset;
458 0 : if (content->IsNodeOfType(nsINode::eTEXT)) {
459 0 : xpOffset = ConvertToXPOffset(content, nativeEndOffset - nativeOffset);
460 0 : if (aExpandToClusterBoundaries) {
461 0 : rv = ExpandToClusterBoundary(content, true, &xpOffset);
462 0 : NS_ENSURE_SUCCESS(rv, rv);
463 : }
464 : } else {
465 : // Use first position of next node, because the end node is ignored
466 : // by ContentIterator when the offset is zero.
467 0 : xpOffset = 0;
468 0 : iter->Next();
469 0 : if (iter->IsDone())
470 : break;
471 0 : domNode = do_QueryInterface(iter->GetCurrentNode());
472 : }
473 :
474 0 : rv = aRange->SetEnd(domNode, PRInt32(xpOffset));
475 0 : NS_ENSURE_SUCCESS(rv, rv);
476 0 : return NS_OK;
477 : }
478 :
479 0 : nativeOffset += nativeTextLength;
480 : }
481 :
482 0 : if (nativeOffset < aNativeOffset)
483 0 : return NS_ERROR_FAILURE;
484 :
485 0 : nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mRootContent));
486 0 : NS_ASSERTION(domNode, "lastContent doesn't have nsIDOMNode!");
487 0 : if (!content) {
488 0 : rv = aRange->SetStart(domNode, 0);
489 0 : NS_ENSURE_SUCCESS(rv, rv);
490 : }
491 0 : rv = aRange->SetEnd(domNode, PRInt32(mRootContent->GetChildCount()));
492 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed");
493 0 : return rv;
494 : }
495 :
496 : nsresult
497 0 : nsContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent)
498 : {
499 0 : nsresult rv = Init(aEvent);
500 0 : if (NS_FAILED(rv))
501 0 : return rv;
502 :
503 0 : NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
504 : "The reply string must be empty");
505 :
506 : rv = GetFlatTextOffsetOfRange(mRootContent,
507 0 : mFirstSelectedRange, &aEvent->mReply.mOffset);
508 0 : NS_ENSURE_SUCCESS(rv, rv);
509 :
510 0 : nsCOMPtr<nsIDOMNode> anchorDomNode, focusDomNode;
511 0 : rv = mSelection->GetAnchorNode(getter_AddRefs(anchorDomNode));
512 0 : NS_ENSURE_TRUE(anchorDomNode, NS_ERROR_FAILURE);
513 0 : rv = mSelection->GetFocusNode(getter_AddRefs(focusDomNode));
514 0 : NS_ENSURE_TRUE(focusDomNode, NS_ERROR_FAILURE);
515 :
516 : PRInt32 anchorOffset, focusOffset;
517 0 : rv = mSelection->GetAnchorOffset(&anchorOffset);
518 0 : NS_ENSURE_SUCCESS(rv, rv);
519 0 : rv = mSelection->GetFocusOffset(&focusOffset);
520 0 : NS_ENSURE_SUCCESS(rv, rv);
521 :
522 0 : nsCOMPtr<nsINode> anchorNode(do_QueryInterface(anchorDomNode));
523 0 : nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDomNode));
524 0 : NS_ENSURE_TRUE(anchorNode && focusNode, NS_ERROR_UNEXPECTED);
525 :
526 : PRInt16 compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
527 0 : focusNode, focusOffset);
528 0 : aEvent->mReply.mReversed = compare > 0;
529 :
530 0 : if (compare) {
531 0 : rv = GenerateFlatTextContent(mFirstSelectedRange, aEvent->mReply.mString);
532 0 : NS_ENSURE_SUCCESS(rv, rv);
533 : }
534 :
535 0 : aEvent->mSucceeded = true;
536 0 : return NS_OK;
537 : }
538 :
539 : nsresult
540 0 : nsContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent)
541 : {
542 0 : nsresult rv = Init(aEvent);
543 0 : if (NS_FAILED(rv))
544 0 : return rv;
545 :
546 0 : NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
547 : "The reply string must be empty");
548 :
549 0 : nsRefPtr<nsRange> range = new nsRange();
550 : rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
551 0 : aEvent->mInput.mLength, false);
552 0 : NS_ENSURE_SUCCESS(rv, rv);
553 :
554 0 : rv = GenerateFlatTextContent(range, aEvent->mReply.mString);
555 0 : NS_ENSURE_SUCCESS(rv, rv);
556 :
557 0 : aEvent->mSucceeded = true;
558 :
559 0 : return NS_OK;
560 : }
561 :
562 : // Adjust to use a child node if possible
563 : // to make the returned rect more accurate
564 0 : static nsINode* AdjustTextRectNode(nsINode* aNode,
565 : PRInt32& aOffset)
566 : {
567 0 : PRInt32 childCount = PRInt32(aNode->GetChildCount());
568 0 : nsINode* node = aNode;
569 0 : if (childCount) {
570 0 : if (aOffset < childCount) {
571 0 : node = aNode->GetChildAt(aOffset);
572 0 : aOffset = 0;
573 0 : } else if (aOffset == childCount) {
574 0 : node = aNode->GetChildAt(childCount - 1);
575 0 : aOffset = node->IsNodeOfType(nsINode::eTEXT) ?
576 0 : static_cast<nsIContent*>(node)->TextLength() : 1;
577 : }
578 : }
579 0 : return node;
580 : }
581 :
582 : // Similar to nsFrameSelection::GetFrameForNodeOffset,
583 : // but this is more flexible for OnQueryTextRect to use
584 0 : static nsresult GetFrameForTextRect(nsINode* aNode,
585 : PRInt32 aOffset,
586 : bool aHint,
587 : nsIFrame** aReturnFrame)
588 : {
589 0 : NS_ENSURE_TRUE(aNode && aNode->IsNodeOfType(nsINode::eCONTENT),
590 : NS_ERROR_UNEXPECTED);
591 0 : nsIContent* content = static_cast<nsIContent*>(aNode);
592 0 : nsIFrame* frame = content->GetPrimaryFrame();
593 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
594 0 : PRInt32 childOffset = 0;
595 : return frame->GetChildFrameContainingOffset(aOffset, aHint, &childOffset,
596 0 : aReturnFrame);
597 : }
598 :
599 : nsresult
600 0 : nsContentEventHandler::OnQueryTextRect(nsQueryContentEvent* aEvent)
601 : {
602 0 : nsresult rv = Init(aEvent);
603 0 : if (NS_FAILED(rv))
604 0 : return rv;
605 :
606 0 : nsRefPtr<nsRange> range = new nsRange();
607 : rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
608 0 : aEvent->mInput.mLength, true);
609 0 : NS_ENSURE_SUCCESS(rv, rv);
610 :
611 : // used to iterate over all contents and their frames
612 0 : nsCOMPtr<nsIContentIterator> iter;
613 0 : rv = NS_NewContentIterator(getter_AddRefs(iter));
614 0 : NS_ENSURE_SUCCESS(rv, rv);
615 0 : iter->Init(range);
616 0 : NS_ENSURE_SUCCESS(rv, rv);
617 :
618 : // get the starting frame
619 0 : PRInt32 offset = range->StartOffset();
620 0 : nsINode* node = iter->GetCurrentNode();
621 0 : if (!node) {
622 0 : node = AdjustTextRectNode(range->GetStartParent(), offset);
623 : }
624 0 : nsIFrame* firstFrame = nsnull;
625 0 : rv = GetFrameForTextRect(node, offset, true, &firstFrame);
626 0 : NS_ENSURE_SUCCESS(rv, rv);
627 :
628 : // get the starting frame rect
629 0 : nsRect rect(nsPoint(0, 0), firstFrame->GetRect().Size());
630 0 : rv = ConvertToRootViewRelativeOffset(firstFrame, rect);
631 0 : NS_ENSURE_SUCCESS(rv, rv);
632 0 : nsRect frameRect = rect;
633 0 : nsPoint ptOffset;
634 0 : firstFrame->GetPointFromOffset(offset, &ptOffset);
635 : // minus 1 to avoid creating an empty rect
636 0 : rect.x += ptOffset.x - 1;
637 0 : rect.width -= ptOffset.x - 1;
638 :
639 : // get the ending frame
640 0 : offset = range->EndOffset();
641 0 : node = AdjustTextRectNode(range->GetEndParent(), offset);
642 0 : nsIFrame* lastFrame = nsnull;
643 0 : rv = GetFrameForTextRect(node, offset, range->Collapsed(), &lastFrame);
644 0 : NS_ENSURE_SUCCESS(rv, rv);
645 :
646 : // iterate over all covered frames
647 0 : for (nsIFrame* frame = firstFrame; frame != lastFrame;) {
648 0 : frame = frame->GetNextContinuation();
649 0 : if (!frame) {
650 0 : do {
651 0 : iter->Next();
652 0 : node = iter->GetCurrentNode();
653 0 : if (!node || !node->IsNodeOfType(nsINode::eCONTENT))
654 0 : continue;
655 0 : frame = static_cast<nsIContent*>(node)->GetPrimaryFrame();
656 0 : } while (!frame && !iter->IsDone());
657 0 : if (!frame) {
658 : // this can happen when the end offset of the range is 0.
659 0 : frame = lastFrame;
660 : }
661 : }
662 0 : frameRect.SetRect(nsPoint(0, 0), frame->GetRect().Size());
663 0 : rv = ConvertToRootViewRelativeOffset(frame, frameRect);
664 0 : NS_ENSURE_SUCCESS(rv, rv);
665 0 : if (frame != lastFrame) {
666 : // not last frame, so just add rect to previous result
667 0 : rect.UnionRect(rect, frameRect);
668 : }
669 : }
670 :
671 : // get the ending frame rect
672 0 : lastFrame->GetPointFromOffset(offset, &ptOffset);
673 : // minus 1 to avoid creating an empty rect
674 0 : frameRect.width -= lastFrame->GetRect().width - ptOffset.x - 1;
675 :
676 0 : if (firstFrame == lastFrame) {
677 0 : rect.IntersectRect(rect, frameRect);
678 : } else {
679 0 : rect.UnionRect(rect, frameRect);
680 : }
681 : aEvent->mReply.mRect =
682 0 : rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel());
683 0 : aEvent->mSucceeded = true;
684 0 : return NS_OK;
685 : }
686 :
687 : nsresult
688 0 : nsContentEventHandler::OnQueryEditorRect(nsQueryContentEvent* aEvent)
689 : {
690 0 : nsresult rv = Init(aEvent);
691 0 : if (NS_FAILED(rv))
692 0 : return rv;
693 :
694 0 : nsIFrame* frame = mRootContent->GetPrimaryFrame();
695 0 : NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
696 :
697 : // get rect for first frame
698 0 : nsRect resultRect(nsPoint(0, 0), frame->GetRect().Size());
699 0 : rv = ConvertToRootViewRelativeOffset(frame, resultRect);
700 0 : NS_ENSURE_SUCCESS(rv, rv);
701 :
702 : // account for any additional frames
703 0 : while ((frame = frame->GetNextContinuation()) != nsnull) {
704 0 : nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size());
705 0 : rv = ConvertToRootViewRelativeOffset(frame, frameRect);
706 0 : NS_ENSURE_SUCCESS(rv, rv);
707 0 : resultRect.UnionRect(resultRect, frameRect);
708 : }
709 :
710 : aEvent->mReply.mRect =
711 0 : resultRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel());
712 0 : aEvent->mSucceeded = true;
713 0 : return NS_OK;
714 : }
715 :
716 : nsresult
717 0 : nsContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
718 : {
719 0 : nsresult rv = Init(aEvent);
720 0 : if (NS_FAILED(rv))
721 0 : return rv;
722 :
723 0 : nsRefPtr<nsCaret> caret = mPresShell->GetCaret();
724 0 : NS_ASSERTION(caret, "GetCaret returned null");
725 :
726 : // When the selection is collapsed and the queried offset is current caret
727 : // position, we should return the "real" caret rect.
728 : bool selectionIsCollapsed;
729 0 : rv = mSelection->GetIsCollapsed(&selectionIsCollapsed);
730 0 : NS_ENSURE_SUCCESS(rv, rv);
731 :
732 0 : if (selectionIsCollapsed) {
733 : PRUint32 offset;
734 0 : rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset);
735 0 : NS_ENSURE_SUCCESS(rv, rv);
736 0 : if (offset == aEvent->mInput.mOffset) {
737 0 : nsRect rect;
738 0 : nsIFrame* caretFrame = caret->GetGeometry(mSelection, &rect);
739 0 : if (!caretFrame)
740 0 : return NS_ERROR_FAILURE;
741 0 : rv = ConvertToRootViewRelativeOffset(caretFrame, rect);
742 0 : NS_ENSURE_SUCCESS(rv, rv);
743 : aEvent->mReply.mRect =
744 0 : rect.ToOutsidePixels(caretFrame->PresContext()->AppUnitsPerDevPixel());
745 0 : aEvent->mSucceeded = true;
746 0 : return NS_OK;
747 : }
748 : }
749 :
750 : // Otherwise, we should set the guessed caret rect.
751 0 : nsRefPtr<nsRange> range = new nsRange();
752 0 : rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0, true);
753 0 : NS_ENSURE_SUCCESS(rv, rv);
754 :
755 : PRInt32 offsetInFrame;
756 : nsIFrame* frame;
757 0 : rv = GetStartFrameAndOffset(range, &frame, &offsetInFrame);
758 0 : NS_ENSURE_SUCCESS(rv, rv);
759 :
760 0 : nsPoint posInFrame;
761 0 : rv = frame->GetPointFromOffset(range->StartOffset(), &posInFrame);
762 0 : NS_ENSURE_SUCCESS(rv, rv);
763 :
764 0 : nsRect rect;
765 0 : rect.x = posInFrame.x;
766 0 : rect.y = posInFrame.y;
767 0 : rect.width = caret->GetCaretRect().width;
768 0 : rect.height = frame->GetSize().height;
769 :
770 0 : rv = ConvertToRootViewRelativeOffset(frame, rect);
771 0 : NS_ENSURE_SUCCESS(rv, rv);
772 :
773 : aEvent->mReply.mRect =
774 0 : rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel());
775 0 : aEvent->mSucceeded = true;
776 0 : return NS_OK;
777 : }
778 :
779 : nsresult
780 0 : nsContentEventHandler::OnQueryContentState(nsQueryContentEvent * aEvent)
781 : {
782 0 : nsresult rv = Init(aEvent);
783 0 : if (NS_FAILED(rv))
784 0 : return rv;
785 :
786 0 : aEvent->mSucceeded = true;
787 :
788 0 : return NS_OK;
789 : }
790 :
791 : nsresult
792 0 : nsContentEventHandler::OnQuerySelectionAsTransferable(nsQueryContentEvent* aEvent)
793 : {
794 0 : nsresult rv = Init(aEvent);
795 0 : if (NS_FAILED(rv))
796 0 : return rv;
797 :
798 0 : if (!aEvent->mReply.mHasSelection) {
799 0 : aEvent->mSucceeded = true;
800 0 : aEvent->mReply.mTransferable = nsnull;
801 0 : return NS_OK;
802 : }
803 :
804 0 : nsCOMPtr<nsIDocument> doc = mPresShell->GetDocument();
805 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
806 :
807 0 : rv = nsCopySupport::GetTransferableForSelection(mSelection, doc, getter_AddRefs(aEvent->mReply.mTransferable));
808 0 : NS_ENSURE_SUCCESS(rv, rv);
809 :
810 0 : aEvent->mSucceeded = true;
811 0 : return NS_OK;
812 : }
813 :
814 : nsresult
815 0 : nsContentEventHandler::OnQueryCharacterAtPoint(nsQueryContentEvent* aEvent)
816 : {
817 0 : nsresult rv = Init(aEvent);
818 0 : if (NS_FAILED(rv))
819 0 : return rv;
820 :
821 0 : nsIFrame* rootFrame = mPresShell->GetRootFrame();
822 0 : NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
823 0 : nsIWidget* rootWidget = rootFrame->GetNearestWidget();
824 0 : NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
825 :
826 : // The root frame's widget might be different, e.g., the event was fired on
827 : // a popup but the rootFrame is the document root.
828 0 : if (rootWidget != aEvent->widget) {
829 0 : NS_PRECONDITION(aEvent->widget, "The event must have the widget");
830 0 : nsIView* view = nsIView::GetViewFor(aEvent->widget);
831 0 : NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
832 0 : rootFrame = view->GetFrame();
833 0 : NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
834 0 : rootWidget = rootFrame->GetNearestWidget();
835 0 : NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
836 : }
837 :
838 : nsQueryContentEvent eventOnRoot(true, NS_QUERY_CHARACTER_AT_POINT,
839 0 : rootWidget);
840 0 : eventOnRoot.refPoint = aEvent->refPoint;
841 0 : if (rootWidget != aEvent->widget) {
842 0 : eventOnRoot.refPoint += aEvent->widget->WidgetToScreenOffset();
843 0 : eventOnRoot.refPoint -= rootWidget->WidgetToScreenOffset();
844 : }
845 : nsPoint ptInRoot =
846 0 : nsLayoutUtils::GetEventCoordinatesRelativeTo(&eventOnRoot, rootFrame);
847 :
848 0 : nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
849 0 : if (!targetFrame || targetFrame->GetType() != nsGkAtoms::textFrame ||
850 0 : !targetFrame->GetContent() ||
851 0 : !nsContentUtils::ContentIsDescendantOf(targetFrame->GetContent(),
852 0 : mRootContent)) {
853 : // there is no character at the point.
854 0 : aEvent->mReply.mOffset = nsQueryContentEvent::NOT_FOUND;
855 0 : aEvent->mSucceeded = true;
856 0 : return NS_OK;
857 : }
858 0 : nsPoint ptInTarget = ptInRoot + rootFrame->GetOffsetToCrossDoc(targetFrame);
859 0 : PRInt32 rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
860 0 : PRInt32 targetAPD = targetFrame->PresContext()->AppUnitsPerDevPixel();
861 0 : ptInTarget = ptInTarget.ConvertAppUnits(rootAPD, targetAPD);
862 :
863 0 : nsTextFrame* textframe = static_cast<nsTextFrame*>(targetFrame);
864 : nsIFrame::ContentOffsets offsets =
865 0 : textframe->GetCharacterOffsetAtFramePoint(ptInTarget);
866 0 : NS_ENSURE_TRUE(offsets.content, NS_ERROR_FAILURE);
867 : PRUint32 nativeOffset;
868 : rv = GetFlatTextOffsetOfRange(mRootContent, offsets.content, offsets.offset,
869 0 : &nativeOffset);
870 0 : NS_ENSURE_SUCCESS(rv, rv);
871 :
872 0 : nsQueryContentEvent textRect(true, NS_QUERY_TEXT_RECT, aEvent->widget);
873 0 : textRect.InitForQueryTextRect(nativeOffset, 1);
874 0 : rv = OnQueryTextRect(&textRect);
875 0 : NS_ENSURE_SUCCESS(rv, rv);
876 0 : NS_ENSURE_TRUE(textRect.mSucceeded, NS_ERROR_FAILURE);
877 :
878 : // currently, we don't need to get the actual text.
879 0 : aEvent->mReply.mOffset = nativeOffset;
880 0 : aEvent->mReply.mRect = textRect.mReply.mRect;
881 0 : aEvent->mSucceeded = true;
882 0 : return NS_OK;
883 : }
884 :
885 : nsresult
886 0 : nsContentEventHandler::OnQueryDOMWidgetHittest(nsQueryContentEvent* aEvent)
887 : {
888 0 : nsresult rv = Init(aEvent);
889 0 : if (NS_FAILED(rv))
890 0 : return rv;
891 :
892 0 : aEvent->mReply.mWidgetIsHit = false;
893 :
894 0 : NS_ENSURE_TRUE(aEvent->widget, NS_ERROR_FAILURE);
895 :
896 0 : nsIDocument* doc = mPresShell->GetDocument();
897 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
898 0 : nsIFrame* docFrame = mPresShell->GetRootFrame();
899 0 : NS_ENSURE_TRUE(docFrame, NS_ERROR_FAILURE);
900 :
901 : nsIntPoint eventLoc =
902 0 : aEvent->refPoint + aEvent->widget->WidgetToScreenOffset();
903 0 : nsIntRect docFrameRect = docFrame->GetScreenRect(); // Returns CSS pixels
904 0 : eventLoc.x = mPresContext->DevPixelsToIntCSSPixels(eventLoc.x);
905 0 : eventLoc.y = mPresContext->DevPixelsToIntCSSPixels(eventLoc.y);
906 0 : eventLoc.x -= docFrameRect.x;
907 0 : eventLoc.y -= docFrameRect.y;
908 :
909 0 : nsCOMPtr<nsIDOMElement> elementUnderMouse;
910 : doc->ElementFromPointHelper(eventLoc.x, eventLoc.y, false, false,
911 0 : getter_AddRefs(elementUnderMouse));
912 0 : nsCOMPtr<nsIContent> contentUnderMouse = do_QueryInterface(elementUnderMouse);
913 0 : if (contentUnderMouse) {
914 0 : nsIWidget* targetWidget = nsnull;
915 0 : nsIFrame* targetFrame = contentUnderMouse->GetPrimaryFrame();
916 0 : nsIObjectFrame* pluginFrame = do_QueryFrame(targetFrame);
917 0 : if (pluginFrame) {
918 0 : targetWidget = pluginFrame->GetWidget();
919 0 : } else if (targetFrame) {
920 0 : targetWidget = targetFrame->GetNearestWidget();
921 : }
922 0 : if (aEvent->widget == targetWidget)
923 0 : aEvent->mReply.mWidgetIsHit = true;
924 : }
925 :
926 0 : aEvent->mSucceeded = true;
927 0 : return NS_OK;
928 : }
929 :
930 : nsresult
931 0 : nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
932 : nsINode* aNode,
933 : PRInt32 aNodeOffset,
934 : PRUint32* aNativeOffset)
935 : {
936 0 : NS_ASSERTION(aNativeOffset, "param is invalid");
937 :
938 0 : nsRefPtr<nsRange> prev = new nsRange();
939 0 : nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(aRootContent));
940 0 : prev->SetStart(rootDOMNode, 0);
941 :
942 0 : nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(aNode));
943 0 : NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode");
944 0 : prev->SetEnd(startDOMNode, aNodeOffset);
945 :
946 0 : nsCOMPtr<nsIContentIterator> iter;
947 0 : nsresult rv = NS_NewContentIterator(getter_AddRefs(iter));
948 0 : NS_ENSURE_SUCCESS(rv, rv);
949 0 : NS_ASSERTION(iter, "NS_NewContentIterator succeeded, but the result is null");
950 0 : iter->Init(prev);
951 :
952 0 : nsCOMPtr<nsINode> startNode = do_QueryInterface(startDOMNode);
953 0 : nsINode* endNode = aNode;
954 :
955 0 : *aNativeOffset = 0;
956 0 : for (; !iter->IsDone(); iter->Next()) {
957 0 : nsINode* node = iter->GetCurrentNode();
958 0 : if (!node || !node->IsNodeOfType(nsINode::eCONTENT))
959 0 : continue;
960 0 : nsIContent* content = static_cast<nsIContent*>(node);
961 :
962 0 : if (node->IsNodeOfType(nsINode::eTEXT)) {
963 : // Note: our range always starts from offset 0
964 0 : if (node == endNode)
965 0 : *aNativeOffset += GetNativeTextLength(content, aNodeOffset);
966 : else
967 0 : *aNativeOffset += GetNativeTextLength(content);
968 0 : } else if (IsContentBR(content)) {
969 : #if defined(XP_WIN)
970 : // On Windows, the length of the newline is 2.
971 : *aNativeOffset += 2;
972 : #else
973 : // On other platforms, the length of the newline is 1.
974 0 : *aNativeOffset += 1;
975 : #endif
976 : }
977 : }
978 0 : return NS_OK;
979 : }
980 :
981 : nsresult
982 0 : nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
983 : nsRange* aRange,
984 : PRUint32* aNativeOffset)
985 : {
986 0 : nsINode* startNode = aRange->GetStartParent();
987 0 : NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
988 0 : PRInt32 startOffset = aRange->StartOffset();
989 : return GetFlatTextOffsetOfRange(aRootContent, startNode, startOffset,
990 0 : aNativeOffset);
991 : }
992 :
993 : nsresult
994 0 : nsContentEventHandler::GetStartFrameAndOffset(nsRange* aRange,
995 : nsIFrame** aFrame,
996 : PRInt32* aOffsetInFrame)
997 : {
998 0 : NS_ASSERTION(aRange && aFrame && aOffsetInFrame, "params are invalid");
999 :
1000 0 : nsIContent* content = nsnull;
1001 0 : nsINode* node = aRange->GetStartParent();
1002 0 : if (node && node->IsNodeOfType(nsINode::eCONTENT))
1003 0 : content = static_cast<nsIContent*>(node);
1004 0 : NS_ASSERTION(content, "the start node doesn't have nsIContent!");
1005 :
1006 0 : nsRefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
1007 0 : *aFrame = fs->GetFrameForNodeOffset(content, aRange->StartOffset(),
1008 0 : fs->GetHint(), aOffsetInFrame);
1009 0 : NS_ENSURE_TRUE((*aFrame), NS_ERROR_FAILURE);
1010 0 : NS_ASSERTION((*aFrame)->GetType() == nsGkAtoms::textFrame,
1011 : "The frame is not textframe");
1012 0 : return NS_OK;
1013 : }
1014 :
1015 : nsresult
1016 0 : nsContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
1017 : nsRect& aRect)
1018 : {
1019 0 : NS_ASSERTION(aFrame, "aFrame must not be null");
1020 :
1021 0 : nsIView* view = nsnull;
1022 0 : nsPoint posInView;
1023 0 : aFrame->GetOffsetFromView(posInView, &view);
1024 0 : if (!view)
1025 0 : return NS_ERROR_FAILURE;
1026 0 : aRect += posInView + view->GetOffsetTo(nsnull);
1027 0 : return NS_OK;
1028 : }
1029 :
1030 0 : static void AdjustRangeForSelection(nsIContent* aRoot,
1031 : nsINode** aNode,
1032 : PRInt32* aOffset)
1033 : {
1034 0 : nsINode* node = *aNode;
1035 0 : PRInt32 offset = *aOffset;
1036 0 : if (aRoot != node && node->GetParent() &&
1037 0 : !node->IsNodeOfType(nsINode::eTEXT)) {
1038 0 : node = node->GetParent();
1039 0 : offset = node->IndexOf(*aNode) + (offset ? 1 : 0);
1040 : }
1041 :
1042 0 : nsIContent* brContent = node->GetChildAt(offset - 1);
1043 0 : while (brContent && brContent->IsHTML()) {
1044 0 : if (brContent->Tag() != nsGkAtoms::br || IsContentBR(brContent))
1045 0 : break;
1046 0 : brContent = node->GetChildAt(--offset - 1);
1047 : }
1048 0 : *aNode = node;
1049 0 : *aOffset = NS_MAX(offset, 0);
1050 0 : }
1051 :
1052 : nsresult
1053 0 : nsContentEventHandler::OnSelectionEvent(nsSelectionEvent* aEvent)
1054 : {
1055 0 : aEvent->mSucceeded = false;
1056 :
1057 : // Get selection to manipulate
1058 : // XXX why do we need to get them from ISM? This method should work fine
1059 : // without ISM.
1060 : nsresult rv = nsIMEStateManager::
1061 0 : GetFocusSelectionAndRoot(getter_AddRefs(mSelection),
1062 0 : getter_AddRefs(mRootContent));
1063 0 : if (rv != NS_ERROR_NOT_AVAILABLE) {
1064 0 : NS_ENSURE_SUCCESS(rv, rv);
1065 : } else {
1066 0 : rv = Init(aEvent);
1067 0 : NS_ENSURE_SUCCESS(rv, rv);
1068 : }
1069 :
1070 : // Get range from offset and length
1071 0 : nsRefPtr<nsRange> range = new nsRange();
1072 0 : NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
1073 : rv = SetRangeFromFlatTextOffset(range, aEvent->mOffset, aEvent->mLength,
1074 0 : aEvent->mExpandToClusterBoundary);
1075 0 : NS_ENSURE_SUCCESS(rv, rv);
1076 :
1077 0 : nsINode* startNode = range->GetStartParent();
1078 0 : nsINode* endNode = range->GetEndParent();
1079 0 : PRInt32 startOffset = range->StartOffset();
1080 0 : PRInt32 endOffset = range->EndOffset();
1081 0 : AdjustRangeForSelection(mRootContent, &startNode, &startOffset);
1082 0 : AdjustRangeForSelection(mRootContent, &endNode, &endOffset);
1083 :
1084 0 : nsCOMPtr<nsIDOMNode> startDomNode(do_QueryInterface(startNode));
1085 0 : nsCOMPtr<nsIDOMNode> endDomNode(do_QueryInterface(endNode));
1086 0 : NS_ENSURE_TRUE(startDomNode && endDomNode, NS_ERROR_UNEXPECTED);
1087 :
1088 0 : nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection));
1089 0 : selPrivate->StartBatchChanges();
1090 :
1091 : // Clear selection first before setting
1092 0 : rv = mSelection->RemoveAllRanges();
1093 : // Need to call EndBatchChanges at the end even if call failed
1094 0 : if (NS_SUCCEEDED(rv)) {
1095 0 : if (aEvent->mReversed) {
1096 0 : rv = mSelection->Collapse(endDomNode, endOffset);
1097 : } else {
1098 0 : rv = mSelection->Collapse(startDomNode, startOffset);
1099 : }
1100 0 : if (NS_SUCCEEDED(rv) &&
1101 0 : (startDomNode != endDomNode || startOffset != endOffset)) {
1102 0 : if (aEvent->mReversed) {
1103 0 : rv = mSelection->Extend(startDomNode, startOffset);
1104 : } else {
1105 0 : rv = mSelection->Extend(endDomNode, endOffset);
1106 : }
1107 : }
1108 : }
1109 0 : selPrivate->EndBatchChanges();
1110 0 : NS_ENSURE_SUCCESS(rv, rv);
1111 :
1112 0 : selPrivate->ScrollIntoView(
1113 0 : nsISelectionController::SELECTION_FOCUS_REGION, false, -1, -1);
1114 0 : aEvent->mSucceeded = true;
1115 0 : return NS_OK;
1116 : }
|