Follow-on fix for bug 457825. Use sheet principal for agent and user sheets. r=dbaron...
[wine-gecko.git] / content / events / src / nsQueryContentEventHandler.cpp
blob541ecbb0be67bad8c0247c65e5390e3897a32379
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
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/
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.
16 * The Original Code is mozilla.org code.
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.
23 * Contributor(s):
24 * Masayuki Nakano <masayuki@d-toybox.com>
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.
38 * ***** END LICENSE BLOCK ***** */
40 #include "nsQueryContentEventHandler.h"
41 #include "nsCOMPtr.h"
42 #include "nsPresContext.h"
43 #include "nsIPresShell.h"
44 #include "nsISelection.h"
45 #include "nsIDOMText.h"
46 #include "nsIDOMRange.h"
47 #include "nsRange.h"
48 #include "nsGUIEvent.h"
49 #include "nsCaret.h"
50 #include "nsFrameSelection.h"
51 #include "nsIFrame.h"
52 #include "nsIView.h"
53 #include "nsIContentIterator.h"
54 #include "nsTextFragment.h"
55 #include "nsTextFrame.h"
57 nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
59 /******************************************************************/
60 /* nsQueryContentEventHandler */
61 /******************************************************************/
63 nsQueryContentEventHandler::nsQueryContentEventHandler(
64 nsPresContext* aPresContext) :
65 mPresContext(aPresContext),
66 mPresShell(aPresContext->GetPresShell()), mSelection(nsnull),
67 mFirstSelectedRange(nsnull), mRootContent(nsnull)
71 nsresult
72 nsQueryContentEventHandler::Init(nsQueryContentEvent* aEvent)
74 NS_ASSERTION(aEvent, "aEvent must not be null");
76 if (mSelection)
77 return NS_OK;
79 aEvent->mSucceeded = PR_FALSE;
81 if (!mPresShell)
82 return NS_ERROR_NOT_AVAILABLE;
84 nsresult rv = mPresShell->GetSelectionForCopy(getter_AddRefs(mSelection));
85 NS_ENSURE_SUCCESS(rv, rv);
86 NS_ASSERTION(mSelection,
87 "GetSelectionForCopy succeeded, but the result is null");
89 nsCOMPtr<nsIDOMRange> firstRange;
90 rv = mSelection->GetRangeAt(0, getter_AddRefs(firstRange));
91 // This shell doesn't support selection.
92 if (NS_FAILED(rv))
93 return NS_ERROR_NOT_AVAILABLE;
94 mFirstSelectedRange = do_QueryInterface(firstRange);
95 NS_ENSURE_TRUE(mFirstSelectedRange, NS_ERROR_FAILURE);
97 nsINode* startNode = mFirstSelectedRange->GetStartParent();
98 NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
99 mRootContent = startNode->GetSelectionRootContent(mPresShell);
100 NS_ENSURE_TRUE(mRootContent, NS_ERROR_FAILURE);
102 aEvent->mReply.mContentsRoot = mRootContent.get();
104 nsRefPtr<nsCaret> caret;
105 rv = mPresShell->GetCaret(getter_AddRefs(caret));
106 NS_ENSURE_SUCCESS(rv, rv);
107 NS_ASSERTION(caret, "GetCaret succeeded, but the result is null");
108 PRBool isCollapsed;
109 nsRect r;
110 nsIView* view = nsnull;
111 rv = caret->GetCaretCoordinates(nsCaret::eRenderingViewCoordinates,
112 mSelection, &r, &isCollapsed, &view);
113 NS_ENSURE_SUCCESS(rv, rv);
114 NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
115 aEvent->mReply.mFocusedWidget = view->GetWidget();
117 return NS_OK;
120 static void ConvertToNativeNewlines(nsAFlatString& aString)
122 #if defined(XP_MACOSX)
123 aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r"));
124 #elif defined(XP_WIN)
125 aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
126 #endif
129 static void ConvertToXPNewlines(nsAFlatString& aString)
131 #if defined(XP_MACOSX)
132 aString.ReplaceSubstring(NS_LITERAL_STRING("\r"), NS_LITERAL_STRING("\n"));
133 #elif defined(XP_WIN)
134 aString.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), NS_LITERAL_STRING("\n"));
135 #endif
138 static void AppendString(nsAString& aString, nsIContent* aContent)
140 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
141 "aContent is not a text node!");
142 const nsTextFragment* text = aContent->GetText();
143 if (!text)
144 return;
145 text->AppendTo(aString);
148 static void AppendSubString(nsAString& aString, nsIContent* aContent,
149 PRUint32 aXPOffset, PRUint32 aXPLength)
151 NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
152 "aContent is not a text node!");
153 const nsTextFragment* text = aContent->GetText();
154 if (!text)
155 return;
156 text->AppendTo(aString, PRInt32(aXPOffset), PRInt32(aXPLength));
159 static PRUint32 GetNativeTextLength(nsIContent* aContent)
161 nsAutoString str;
162 if (aContent->IsNodeOfType(nsINode::eTEXT))
163 AppendString(str, aContent);
164 else if (aContent->IsNodeOfType(nsINode::eHTML) &&
165 aContent->Tag() == nsGkAtoms::br)
166 str.Assign(PRUnichar('\n'));
167 ConvertToNativeNewlines(str);
168 return str.Length();
171 static PRUint32 ConvertToXPOffset(nsIContent* aContent, PRUint32 aNativeOffset)
174 nsAutoString str;
175 AppendString(str, aContent);
176 ConvertToNativeNewlines(str);
177 NS_ASSERTION(aNativeOffset <= str.Length(),
178 "aOffsetForNativeLF is too large!");
179 str.Truncate(aNativeOffset);
180 ConvertToXPNewlines(str);
181 return str.Length();
184 nsresult
185 nsQueryContentEventHandler::GenerateFlatTextContent(nsIRange* aRange,
186 nsAFlatString& aString)
188 nsCOMPtr<nsIContentIterator> iter;
189 nsresult rv = NS_NewContentIterator(getter_AddRefs(iter));
190 NS_ENSURE_SUCCESS(rv, rv);
191 NS_ASSERTION(iter, "NS_NewContentIterator succeeded, but the result is null");
192 nsCOMPtr<nsIDOMRange> domRange(do_QueryInterface(aRange));
193 NS_ASSERTION(domRange, "aRange doesn't have nsIDOMRange!");
194 iter->Init(domRange);
196 NS_ASSERTION(aString.IsEmpty(), "aString must be empty string");
198 nsINode* startNode = aRange->GetStartParent();
199 nsINode* endNode = aRange->GetEndParent();
201 if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) {
202 nsIContent* content = static_cast<nsIContent*>(startNode);
203 AppendSubString(aString, content, aRange->StartOffset(),
204 aRange->EndOffset() - aRange->StartOffset());
205 ConvertToNativeNewlines(aString);
206 return NS_OK;
209 nsAutoString tmpStr;
210 for (; !iter->IsDone(); iter->Next()) {
211 nsINode* node = iter->GetCurrentNode();
212 if (!node || !node->IsNodeOfType(nsINode::eCONTENT))
213 continue;
214 nsIContent* content = static_cast<nsIContent*>(node);
216 if (content->IsNodeOfType(nsINode::eTEXT)) {
217 if (content == startNode)
218 AppendSubString(aString, content, aRange->StartOffset(),
219 content->TextLength() - aRange->StartOffset());
220 else if (content == endNode)
221 AppendSubString(aString, content, 0, aRange->EndOffset());
222 else
223 AppendString(aString, content);
224 } else if (content->IsNodeOfType(nsINode::eHTML) &&
225 content->Tag() == nsGkAtoms::br)
226 aString.Append(PRUnichar('\n'));
228 ConvertToNativeNewlines(aString);
229 return NS_OK;
232 nsresult
233 nsQueryContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
234 PRBool aForward,
235 PRUint32* aXPOffset)
237 NS_ASSERTION(*aXPOffset >= 0 && *aXPOffset <= aContent->TextLength(),
238 "offset is out of range.");
240 // XXX This method assumes that the frame boundaries must be cluster
241 // boundaries. It's false, but no problem now, maybe.
242 if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
243 *aXPOffset == 0 || *aXPOffset == aContent->TextLength())
244 return NS_OK;
245 nsCOMPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
246 PRInt32 offsetInFrame;
247 nsFrameSelection::HINT hint =
248 aForward ? nsFrameSelection::HINTLEFT : nsFrameSelection::HINTRIGHT;
249 nsIFrame* frame = fs->GetFrameForNodeOffset(aContent, PRInt32(*aXPOffset),
250 hint, &offsetInFrame);
251 if (!frame) {
252 // This content doesn't have any frames, we only can check surrogate pair...
253 const nsTextFragment* text = aContent->GetText();
254 NS_ENSURE_TRUE(text, NS_ERROR_FAILURE);
255 if (NS_IS_LOW_SURROGATE(text->CharAt(*aXPOffset)) &&
256 NS_IS_HIGH_SURROGATE(text->CharAt(*aXPOffset - 1)))
257 *aXPOffset += aForward ? 1 : -1;
258 return NS_OK;
260 PRInt32 startOffset, endOffset;
261 nsresult rv = frame->GetOffsets(startOffset, endOffset);
262 NS_ENSURE_SUCCESS(rv, rv);
263 if (*aXPOffset == PRUint32(startOffset) || *aXPOffset == PRUint32(endOffset))
264 return NS_OK;
265 if (frame->GetType() != nsGkAtoms::textFrame)
266 return NS_ERROR_FAILURE;
267 nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
268 PRInt32 newOffsetInFrame = offsetInFrame;
269 newOffsetInFrame += aForward ? -1 : 1;
270 textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame);
271 *aXPOffset = startOffset + newOffsetInFrame;
272 return NS_OK;
275 nsresult
276 nsQueryContentEventHandler::SetRangeFromFlatTextOffset(
277 nsIRange* aRange,
278 PRUint32 aNativeOffset,
279 PRUint32 aNativeLength,
280 PRBool aExpandToClusterBoundaries)
282 nsCOMPtr<nsIContentIterator> iter;
283 nsresult rv = NS_NewContentIterator(getter_AddRefs(iter));
284 NS_ENSURE_SUCCESS(rv, rv);
285 NS_ASSERTION(iter, "NS_NewContentIterator succeeded, but the result is null");
286 rv = iter->Init(mRootContent);
287 NS_ENSURE_SUCCESS(rv, rv);
288 nsCOMPtr<nsIDOMRange> domRange(do_QueryInterface(aRange));
289 NS_ASSERTION(domRange, "aRange doesn't have nsIDOMRange!");
291 PRUint32 nativeOffset = 0;
292 PRUint32 nativeEndOffset = aNativeOffset + aNativeLength;
293 nsCOMPtr<nsIContent> content;
294 for (; !iter->IsDone(); iter->Next()) {
295 nsINode* node = iter->GetCurrentNode();
296 if (!node || !node->IsNodeOfType(nsINode::eCONTENT))
297 continue;
298 nsIContent* content = static_cast<nsIContent*>(node);
300 PRUint32 nativeTextLength;
301 nativeTextLength = GetNativeTextLength(content);
302 if (nativeTextLength == 0)
303 continue;
305 if (nativeOffset <= aNativeOffset &&
306 aNativeOffset < nativeOffset + nativeTextLength) {
307 nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
308 NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");
310 PRUint32 xpOffset =
311 content->IsNodeOfType(nsINode::eTEXT) ?
312 ConvertToXPOffset(content, aNativeOffset - nativeOffset) : 0;
314 if (aExpandToClusterBoundaries) {
315 rv = ExpandToClusterBoundary(content, PR_FALSE, &xpOffset);
316 NS_ENSURE_SUCCESS(rv, rv);
319 rv = domRange->SetStart(domNode, PRInt32(xpOffset));
320 NS_ENSURE_SUCCESS(rv, rv);
321 if (aNativeLength == 0) {
322 // Ensure that the end offset and the start offset are same.
323 rv = domRange->SetEnd(domNode, PRInt32(xpOffset));
324 NS_ENSURE_SUCCESS(rv, rv);
325 return NS_OK;
328 if (nativeEndOffset <= nativeOffset + nativeTextLength) {
329 nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
330 NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");
332 PRUint32 xpOffset;
333 if (content->IsNodeOfType(nsINode::eTEXT)) {
334 xpOffset = ConvertToXPOffset(content, nativeEndOffset - nativeOffset);
335 if (aExpandToClusterBoundaries) {
336 rv = ExpandToClusterBoundary(content, PR_TRUE, &xpOffset);
337 NS_ENSURE_SUCCESS(rv, rv);
339 } else {
340 // Use first position of next node, because the end node is ignored
341 // by ContentIterator when the offset is zero.
342 xpOffset = 0;
343 iter->Next();
344 if (iter->IsDone())
345 break;
346 domNode = do_QueryInterface(iter->GetCurrentNode());
349 rv = domRange->SetEnd(domNode, PRInt32(xpOffset));
350 NS_ENSURE_SUCCESS(rv, rv);
351 return NS_OK;
354 nativeOffset += nativeTextLength;
357 if (nativeOffset < aNativeOffset)
358 return NS_ERROR_FAILURE;
360 nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mRootContent));
361 NS_ASSERTION(domNode, "lastContent doesn't have nsIDOMNode!");
362 if (!content) {
363 rv = domRange->SetStart(domNode, 0);
364 NS_ENSURE_SUCCESS(rv, rv);
366 rv = domRange->SetEnd(domNode, PRInt32(mRootContent->GetChildCount()));
367 NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed");
368 return rv;
371 nsresult
372 nsQueryContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent)
374 nsresult rv = Init(aEvent);
375 if (NS_FAILED(rv))
376 return rv;
378 NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
379 "The reply string must be empty");
381 rv = GetFlatTextOffsetOfRange(mFirstSelectedRange, &aEvent->mReply.mOffset);
382 NS_ENSURE_SUCCESS(rv, rv);
384 PRBool isCollapsed;
385 rv = mSelection->GetIsCollapsed(&isCollapsed);
386 NS_ENSURE_SUCCESS(rv, rv);
388 if (!isCollapsed) {
389 nsCOMPtr<nsIDOMRange> domRange;
390 rv = mSelection->GetRangeAt(0, getter_AddRefs(domRange));
391 NS_ENSURE_SUCCESS(rv, rv);
392 NS_ASSERTION(domRange, "GetRangeAt succeeded, but the result is null");
394 nsCOMPtr<nsIRange> range(do_QueryInterface(domRange));
395 NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
396 rv = GenerateFlatTextContent(range, aEvent->mReply.mString);
397 NS_ENSURE_SUCCESS(rv, rv);
400 aEvent->mSucceeded = PR_TRUE;
401 return NS_OK;
404 nsresult
405 nsQueryContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent)
407 nsresult rv = Init(aEvent);
408 if (NS_FAILED(rv))
409 return rv;
411 NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
412 "The reply string must be empty");
414 nsCOMPtr<nsIRange> range = new nsRange();
415 NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
416 rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
417 aEvent->mInput.mLength, PR_FALSE);
418 NS_ENSURE_SUCCESS(rv, rv);
420 rv = GenerateFlatTextContent(range, aEvent->mReply.mString);
421 NS_ENSURE_SUCCESS(rv, rv);
423 aEvent->mSucceeded = PR_TRUE;
425 return NS_OK;
428 nsresult
429 nsQueryContentEventHandler::QueryRectFor(nsQueryContentEvent* aEvent,
430 nsIRange* aRange,
431 nsCaret* aCaret)
433 PRInt32 offsetInFrame;
434 nsIFrame* frame;
435 nsresult rv = GetStartFrameAndOffset(aRange, &frame, &offsetInFrame);
436 NS_ENSURE_SUCCESS(rv, rv);
438 nsPoint posInFrame;
439 rv = frame->GetPointFromOffset(aRange->StartOffset(), &posInFrame);
440 NS_ENSURE_SUCCESS(rv, rv);
442 aEvent->mReply.mRect.y = posInFrame.y;
443 aEvent->mReply.mRect.height = frame->GetSize().height;
445 if (aEvent->message == NS_QUERY_CHARACTER_RECT) {
446 nsPoint nextPos;
447 rv = frame->GetPointFromOffset(aRange->EndOffset(), &nextPos);
448 NS_ENSURE_SUCCESS(rv, rv);
449 aEvent->mReply.mRect.x = PR_MIN(posInFrame.x, nextPos.x);
450 aEvent->mReply.mRect.width = PR_ABS(posInFrame.x - nextPos.x);
451 } else {
452 aEvent->mReply.mRect.x = posInFrame.x;
453 aEvent->mReply.mRect.width = aCaret->GetCaretRect().width;
456 // The coordinates are app units here, they will be converted to system
457 // coordinates by view manager.
458 rv = ConvertToRootViewRelativeOffset(frame, aEvent->mReply.mRect);
459 NS_ENSURE_SUCCESS(rv, rv);
461 aEvent->mSucceeded = PR_TRUE;
462 return NS_OK;
465 nsresult
466 nsQueryContentEventHandler::OnQueryCharacterRect(nsQueryContentEvent* aEvent)
468 nsresult rv = Init(aEvent);
469 if (NS_FAILED(rv))
470 return rv;
472 nsCOMPtr<nsIRange> range = new nsRange();
473 NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
474 rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 1, PR_TRUE);
475 NS_ENSURE_SUCCESS(rv, rv);
477 if (range->Collapsed()) {
478 // There is no character at the offset.
479 return NS_OK;
482 return QueryRectFor(aEvent, range, nsnull);
485 nsresult
486 nsQueryContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
488 nsresult rv = Init(aEvent);
489 if (NS_FAILED(rv))
490 return rv;
492 nsRefPtr<nsCaret> caret;
493 rv = mPresShell->GetCaret(getter_AddRefs(caret));
494 NS_ENSURE_SUCCESS(rv, rv);
495 NS_ASSERTION(caret, "GetCaret succeeded, but the result is null");
497 // When the selection is collapsed and the queried offset is current caret
498 // position, we should return the "real" caret rect.
499 PRBool selectionIsCollapsed;
500 rv = mSelection->GetIsCollapsed(&selectionIsCollapsed);
501 NS_ENSURE_SUCCESS(rv, rv);
503 if (selectionIsCollapsed) {
504 PRUint32 offset;
505 rv = GetFlatTextOffsetOfRange(mFirstSelectedRange, &offset);
506 NS_ENSURE_SUCCESS(rv, rv);
507 if (offset == aEvent->mInput.mOffset) {
508 PRBool isCollapsed;
509 rv = caret->GetCaretCoordinates(nsCaret::eTopLevelWindowCoordinates,
510 mSelection, &aEvent->mReply.mRect,
511 &isCollapsed, nsnull);
512 NS_ENSURE_SUCCESS(rv, rv);
513 aEvent->mSucceeded = PR_TRUE;
514 return NS_OK;
518 // Otherwise, we should set the guessed caret rect.
519 nsCOMPtr<nsIRange> range = new nsRange();
520 NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
521 rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0, PR_TRUE);
522 NS_ENSURE_SUCCESS(rv, rv);
524 return QueryRectFor(aEvent, range, caret);
527 nsresult
528 nsQueryContentEventHandler::GetFlatTextOffsetOfRange(nsIRange* aRange,
529 PRUint32* aNativeOffset)
531 NS_ASSERTION(aNativeOffset, "param is invalid");
533 nsCOMPtr<nsIRange> prev = new nsRange();
534 NS_ENSURE_TRUE(prev, NS_ERROR_OUT_OF_MEMORY);
535 nsCOMPtr<nsIDOMRange> domPrev(do_QueryInterface(prev));
536 NS_ASSERTION(domPrev, "nsRange doesn't have nsIDOMRange??");
537 nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(mRootContent));
538 domPrev->SetStart(rootDOMNode, 0);
540 nsINode* startNode = aRange->GetStartParent();
541 NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
543 PRInt32 startOffset = aRange->StartOffset();
544 nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(startNode));
545 NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode");
546 domPrev->SetEnd(startDOMNode, startOffset);
548 nsAutoString prevStr;
549 nsresult rv = GenerateFlatTextContent(prev, prevStr);
550 NS_ENSURE_SUCCESS(rv, rv);
551 *aNativeOffset = prevStr.Length();
552 return NS_OK;
555 nsresult
556 nsQueryContentEventHandler::GetStartFrameAndOffset(nsIRange* aRange,
557 nsIFrame** aFrame,
558 PRInt32* aOffsetInFrame)
560 NS_ASSERTION(aRange && aFrame && aOffsetInFrame, "params are invalid");
562 nsIContent* content = nsnull;
563 nsINode* node = aRange->GetStartParent();
564 if (node && node->IsNodeOfType(nsINode::eCONTENT))
565 content = static_cast<nsIContent*>(node);
566 NS_ASSERTION(content, "the start node doesn't have nsIContent!");
568 nsCOMPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
569 *aFrame = fs->GetFrameForNodeOffset(content, aRange->StartOffset(),
570 fs->GetHint(), aOffsetInFrame);
571 NS_ENSURE_TRUE((*aFrame), NS_ERROR_FAILURE);
572 NS_ASSERTION((*aFrame)->GetType() == nsGkAtoms::textFrame,
573 "The frame is not textframe");
574 return NS_OK;
577 nsresult
578 nsQueryContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
579 nsRect& aRect)
581 NS_ASSERTION(aFrame, "aFrame must not be null");
583 nsIView* view = nsnull;
584 nsPoint posInView;
585 aFrame->GetOffsetFromView(posInView, &view);
586 if (!view)
587 return NS_ERROR_FAILURE;
588 aRect += posInView + view->GetOffsetTo(nsnull);
589 return NS_OK;