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