1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/events/ContentEventHandler.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1272 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 sw=2 et tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "ContentEventHandler.h" 1.11 +#include "mozilla/IMEStateManager.h" 1.12 +#include "mozilla/TextEvents.h" 1.13 +#include "mozilla/dom/Element.h" 1.14 +#include "nsCaret.h" 1.15 +#include "nsCOMPtr.h" 1.16 +#include "nsContentUtils.h" 1.17 +#include "nsCopySupport.h" 1.18 +#include "nsFocusManager.h" 1.19 +#include "nsFrameSelection.h" 1.20 +#include "nsIContentIterator.h" 1.21 +#include "nsIPresShell.h" 1.22 +#include "nsISelection.h" 1.23 +#include "nsISelectionController.h" 1.24 +#include "nsISelectionPrivate.h" 1.25 +#include "nsIDOMRange.h" 1.26 +#include "nsIFrame.h" 1.27 +#include "nsIObjectFrame.h" 1.28 +#include "nsLayoutUtils.h" 1.29 +#include "nsPresContext.h" 1.30 +#include "nsRange.h" 1.31 +#include "nsTextFragment.h" 1.32 +#include "nsTextFrame.h" 1.33 +#include "nsView.h" 1.34 + 1.35 +#include <algorithm> 1.36 + 1.37 +namespace mozilla { 1.38 + 1.39 +using namespace dom; 1.40 +using namespace widget; 1.41 + 1.42 +/******************************************************************/ 1.43 +/* ContentEventHandler */ 1.44 +/******************************************************************/ 1.45 + 1.46 +ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext) 1.47 + : mPresContext(aPresContext) 1.48 + , mPresShell(aPresContext->GetPresShell()) 1.49 + , mSelection(nullptr) 1.50 + , mFirstSelectedRange(nullptr) 1.51 + , mRootContent(nullptr) 1.52 +{ 1.53 +} 1.54 + 1.55 +nsresult 1.56 +ContentEventHandler::InitBasic() 1.57 +{ 1.58 + NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE); 1.59 + 1.60 + // If text frame which has overflowing selection underline is dirty, 1.61 + // we need to flush the pending reflow here. 1.62 + mPresShell->FlushPendingNotifications(Flush_Layout); 1.63 + 1.64 + // Flushing notifications can cause mPresShell to be destroyed (bug 577963). 1.65 + NS_ENSURE_TRUE(!mPresShell->IsDestroying(), NS_ERROR_FAILURE); 1.66 + 1.67 + return NS_OK; 1.68 +} 1.69 + 1.70 +nsresult 1.71 +ContentEventHandler::InitCommon() 1.72 +{ 1.73 + if (mSelection) { 1.74 + return NS_OK; 1.75 + } 1.76 + 1.77 + nsresult rv = InitBasic(); 1.78 + NS_ENSURE_SUCCESS(rv, rv); 1.79 + 1.80 + nsCopySupport::GetSelectionForCopy(mPresShell->GetDocument(), 1.81 + getter_AddRefs(mSelection)); 1.82 + 1.83 + nsCOMPtr<nsIDOMRange> firstRange; 1.84 + rv = mSelection->GetRangeAt(0, getter_AddRefs(firstRange)); 1.85 + // This shell doesn't support selection. 1.86 + if (NS_FAILED(rv)) { 1.87 + return NS_ERROR_NOT_AVAILABLE; 1.88 + } 1.89 + mFirstSelectedRange = static_cast<nsRange*>(firstRange.get()); 1.90 + 1.91 + nsINode* startNode = mFirstSelectedRange->GetStartParent(); 1.92 + NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); 1.93 + nsINode* endNode = mFirstSelectedRange->GetEndParent(); 1.94 + NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE); 1.95 + 1.96 + // See bug 537041 comment 5, the range could have removed node. 1.97 + NS_ENSURE_TRUE(startNode->GetCurrentDoc() == mPresShell->GetDocument(), 1.98 + NS_ERROR_NOT_AVAILABLE); 1.99 + NS_ASSERTION(startNode->GetCurrentDoc() == endNode->GetCurrentDoc(), 1.100 + "mFirstSelectedRange crosses the document boundary"); 1.101 + 1.102 + mRootContent = startNode->GetSelectionRootContent(mPresShell); 1.103 + NS_ENSURE_TRUE(mRootContent, NS_ERROR_FAILURE); 1.104 + return NS_OK; 1.105 +} 1.106 + 1.107 +nsresult 1.108 +ContentEventHandler::Init(WidgetQueryContentEvent* aEvent) 1.109 +{ 1.110 + NS_ASSERTION(aEvent, "aEvent must not be null"); 1.111 + 1.112 + nsresult rv = InitCommon(); 1.113 + NS_ENSURE_SUCCESS(rv, rv); 1.114 + 1.115 + aEvent->mSucceeded = false; 1.116 + 1.117 + aEvent->mReply.mContentsRoot = mRootContent.get(); 1.118 + 1.119 + bool isCollapsed; 1.120 + rv = mSelection->GetIsCollapsed(&isCollapsed); 1.121 + NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_AVAILABLE); 1.122 + aEvent->mReply.mHasSelection = !isCollapsed; 1.123 + 1.124 + nsRefPtr<nsCaret> caret = mPresShell->GetCaret(); 1.125 + NS_ASSERTION(caret, "GetCaret returned null"); 1.126 + 1.127 + nsRect r; 1.128 + nsIFrame* frame = caret->GetGeometry(mSelection, &r); 1.129 + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); 1.130 + 1.131 + aEvent->mReply.mFocusedWidget = frame->GetNearestWidget(); 1.132 + 1.133 + return NS_OK; 1.134 +} 1.135 + 1.136 +nsresult 1.137 +ContentEventHandler::Init(WidgetSelectionEvent* aEvent) 1.138 +{ 1.139 + NS_ASSERTION(aEvent, "aEvent must not be null"); 1.140 + 1.141 + nsresult rv = InitCommon(); 1.142 + NS_ENSURE_SUCCESS(rv, rv); 1.143 + 1.144 + aEvent->mSucceeded = false; 1.145 + 1.146 + return NS_OK; 1.147 +} 1.148 + 1.149 +nsIContent* 1.150 +ContentEventHandler::GetFocusedContent() 1.151 +{ 1.152 + nsIDocument* doc = mPresShell->GetDocument(); 1.153 + if (!doc) { 1.154 + return nullptr; 1.155 + } 1.156 + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(doc->GetWindow()); 1.157 + nsCOMPtr<nsPIDOMWindow> focusedWindow; 1.158 + return nsFocusManager::GetFocusedDescendant(window, true, 1.159 + getter_AddRefs(focusedWindow)); 1.160 +} 1.161 + 1.162 +bool 1.163 +ContentEventHandler::IsPlugin(nsIContent* aContent) 1.164 +{ 1.165 + return aContent && 1.166 + aContent->GetDesiredIMEState().mEnabled == IMEState::PLUGIN; 1.167 +} 1.168 + 1.169 +nsresult 1.170 +ContentEventHandler::QueryContentRect(nsIContent* aContent, 1.171 + WidgetQueryContentEvent* aEvent) 1.172 +{ 1.173 + NS_PRECONDITION(aContent, "aContent must not be null"); 1.174 + 1.175 + nsIFrame* frame = aContent->GetPrimaryFrame(); 1.176 + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); 1.177 + 1.178 + // get rect for first frame 1.179 + nsRect resultRect(nsPoint(0, 0), frame->GetRect().Size()); 1.180 + nsresult rv = ConvertToRootViewRelativeOffset(frame, resultRect); 1.181 + NS_ENSURE_SUCCESS(rv, rv); 1.182 + 1.183 + // account for any additional frames 1.184 + while ((frame = frame->GetNextContinuation()) != nullptr) { 1.185 + nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size()); 1.186 + rv = ConvertToRootViewRelativeOffset(frame, frameRect); 1.187 + NS_ENSURE_SUCCESS(rv, rv); 1.188 + resultRect.UnionRect(resultRect, frameRect); 1.189 + } 1.190 + 1.191 + aEvent->mReply.mRect = 1.192 + resultRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()); 1.193 + aEvent->mSucceeded = true; 1.194 + 1.195 + return NS_OK; 1.196 +} 1.197 + 1.198 +// Editor places a bogus BR node under its root content if the editor doesn't 1.199 +// have any text. This happens even for single line editors. 1.200 +// When we get text content and when we change the selection, 1.201 +// we don't want to include the bogus BRs at the end. 1.202 +static bool IsContentBR(nsIContent* aContent) 1.203 +{ 1.204 + return aContent->IsHTML() && 1.205 + aContent->Tag() == nsGkAtoms::br && 1.206 + !aContent->AttrValueIs(kNameSpaceID_None, 1.207 + nsGkAtoms::type, 1.208 + nsGkAtoms::moz, 1.209 + eIgnoreCase) && 1.210 + !aContent->AttrValueIs(kNameSpaceID_None, 1.211 + nsGkAtoms::mozeditorbogusnode, 1.212 + nsGkAtoms::_true, 1.213 + eIgnoreCase); 1.214 +} 1.215 + 1.216 +static void ConvertToNativeNewlines(nsAFlatString& aString) 1.217 +{ 1.218 +#if defined(XP_MACOSX) 1.219 + // XXX Mac OS X doesn't use "\r". 1.220 + aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r")); 1.221 +#elif defined(XP_WIN) 1.222 + aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n")); 1.223 +#endif 1.224 +} 1.225 + 1.226 +static void AppendString(nsAString& aString, nsIContent* aContent) 1.227 +{ 1.228 + NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), 1.229 + "aContent is not a text node!"); 1.230 + const nsTextFragment* text = aContent->GetText(); 1.231 + if (!text) { 1.232 + return; 1.233 + } 1.234 + text->AppendTo(aString); 1.235 +} 1.236 + 1.237 +static void AppendSubString(nsAString& aString, nsIContent* aContent, 1.238 + uint32_t aXPOffset, uint32_t aXPLength) 1.239 +{ 1.240 + NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), 1.241 + "aContent is not a text node!"); 1.242 + const nsTextFragment* text = aContent->GetText(); 1.243 + if (!text) { 1.244 + return; 1.245 + } 1.246 + text->AppendTo(aString, int32_t(aXPOffset), int32_t(aXPLength)); 1.247 +} 1.248 + 1.249 +#if defined(XP_WIN) 1.250 +static uint32_t CountNewlinesInXPLength(nsIContent* aContent, 1.251 + uint32_t aXPLength) 1.252 +{ 1.253 + NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), 1.254 + "aContent is not a text node!"); 1.255 + const nsTextFragment* text = aContent->GetText(); 1.256 + if (!text) { 1.257 + return 0; 1.258 + } 1.259 + // For automated tests, we should abort on debug build. 1.260 + NS_ABORT_IF_FALSE( 1.261 + (aXPLength == UINT32_MAX || aXPLength <= text->GetLength()), 1.262 + "aXPLength is out-of-bounds"); 1.263 + const uint32_t length = std::min(aXPLength, text->GetLength()); 1.264 + uint32_t newlines = 0; 1.265 + for (uint32_t i = 0; i < length; ++i) { 1.266 + if (text->CharAt(i) == '\n') { 1.267 + ++newlines; 1.268 + } 1.269 + } 1.270 + return newlines; 1.271 +} 1.272 + 1.273 +static uint32_t CountNewlinesInNativeLength(nsIContent* aContent, 1.274 + uint32_t aNativeLength) 1.275 +{ 1.276 + NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), 1.277 + "aContent is not a text node!"); 1.278 + const nsTextFragment* text = aContent->GetText(); 1.279 + if (!text) { 1.280 + return 0; 1.281 + } 1.282 + // For automated tests, we should abort on debug build. 1.283 + MOZ_ASSERT( 1.284 + (aNativeLength == UINT32_MAX || aNativeLength <= text->GetLength() * 2), 1.285 + "aNativeLength is unexpected value"); 1.286 + const uint32_t xpLength = text->GetLength(); 1.287 + uint32_t newlines = 0; 1.288 + for (uint32_t i = 0, nativeOffset = 0; 1.289 + i < xpLength && nativeOffset < aNativeLength; 1.290 + ++i, ++nativeOffset) { 1.291 + // For automated tests, we should abort on debug build. 1.292 + NS_ABORT_IF_FALSE(i < text->GetLength(), "i is out-of-bounds"); 1.293 + if (text->CharAt(i) == '\n') { 1.294 + ++newlines; 1.295 + ++nativeOffset; 1.296 + } 1.297 + } 1.298 + return newlines; 1.299 +} 1.300 +#endif 1.301 + 1.302 +/* static */ uint32_t 1.303 +ContentEventHandler::GetNativeTextLength(nsIContent* aContent, 1.304 + uint32_t aMaxLength) 1.305 +{ 1.306 + return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength); 1.307 +} 1.308 + 1.309 +/* static */ uint32_t 1.310 +ContentEventHandler::GetTextLength(nsIContent* aContent, 1.311 + LineBreakType aLineBreakType, 1.312 + uint32_t aMaxLength) 1.313 +{ 1.314 + if (aContent->IsNodeOfType(nsINode::eTEXT)) { 1.315 + uint32_t textLengthDifference = 1.316 +#if defined(XP_MACOSX) 1.317 + // On Mac, the length of a native newline ("\r") is equal to the length of 1.318 + // the XP newline ("\n"), so the native length is the same as the XP 1.319 + // length. 1.320 + 0; 1.321 +#elif defined(XP_WIN) 1.322 + // On Windows, the length of a native newline ("\r\n") is twice the length 1.323 + // of the XP newline ("\n"), so XP length is equal to the length of the 1.324 + // native offset plus the number of newlines encountered in the string. 1.325 + (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 1.326 + CountNewlinesInXPLength(aContent, aMaxLength) : 0; 1.327 +#else 1.328 + // On other platforms, the native and XP newlines are the same. 1.329 + 0; 1.330 +#endif 1.331 + 1.332 + const nsTextFragment* text = aContent->GetText(); 1.333 + if (!text) { 1.334 + return 0; 1.335 + } 1.336 + uint32_t length = std::min(text->GetLength(), aMaxLength); 1.337 + return length + textLengthDifference; 1.338 + } else if (IsContentBR(aContent)) { 1.339 +#if defined(XP_WIN) 1.340 + // Length of \r\n 1.341 + return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1; 1.342 +#else 1.343 + return 1; 1.344 +#endif 1.345 + } 1.346 + return 0; 1.347 +} 1.348 + 1.349 +static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset) 1.350 +{ 1.351 +#if defined(XP_MACOSX) 1.352 + // On Mac, the length of a native newline ("\r") is equal to the length of 1.353 + // the XP newline ("\n"), so the native offset is the same as the XP offset. 1.354 + return aNativeOffset; 1.355 +#elif defined(XP_WIN) 1.356 + // On Windows, the length of a native newline ("\r\n") is twice the length of 1.357 + // the XP newline ("\n"), so XP offset is equal to the length of the native 1.358 + // offset minus the number of newlines encountered in the string. 1.359 + return aNativeOffset - CountNewlinesInNativeLength(aContent, aNativeOffset); 1.360 +#else 1.361 + // On other platforms, the native and XP newlines are the same. 1.362 + return aNativeOffset; 1.363 +#endif 1.364 +} 1.365 + 1.366 +static nsresult GenerateFlatTextContent(nsRange* aRange, 1.367 + nsAFlatString& aString, 1.368 + LineBreakType aLineBreakType) 1.369 +{ 1.370 + nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator(); 1.371 + iter->Init(aRange); 1.372 + 1.373 + NS_ASSERTION(aString.IsEmpty(), "aString must be empty string"); 1.374 + 1.375 + nsINode* startNode = aRange->GetStartParent(); 1.376 + NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); 1.377 + nsINode* endNode = aRange->GetEndParent(); 1.378 + NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE); 1.379 + 1.380 + if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) { 1.381 + nsIContent* content = static_cast<nsIContent*>(startNode); 1.382 + AppendSubString(aString, content, aRange->StartOffset(), 1.383 + aRange->EndOffset() - aRange->StartOffset()); 1.384 + ConvertToNativeNewlines(aString); 1.385 + return NS_OK; 1.386 + } 1.387 + 1.388 + nsAutoString tmpStr; 1.389 + for (; !iter->IsDone(); iter->Next()) { 1.390 + nsINode* node = iter->GetCurrentNode(); 1.391 + if (!node) { 1.392 + break; 1.393 + } 1.394 + if (!node->IsNodeOfType(nsINode::eCONTENT)) { 1.395 + continue; 1.396 + } 1.397 + nsIContent* content = static_cast<nsIContent*>(node); 1.398 + 1.399 + if (content->IsNodeOfType(nsINode::eTEXT)) { 1.400 + if (content == startNode) { 1.401 + AppendSubString(aString, content, aRange->StartOffset(), 1.402 + content->TextLength() - aRange->StartOffset()); 1.403 + } else if (content == endNode) { 1.404 + AppendSubString(aString, content, 0, aRange->EndOffset()); 1.405 + } else { 1.406 + AppendString(aString, content); 1.407 + } 1.408 + } else if (IsContentBR(content)) { 1.409 + aString.Append(char16_t('\n')); 1.410 + } 1.411 + } 1.412 + if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) { 1.413 + ConvertToNativeNewlines(aString); 1.414 + } 1.415 + return NS_OK; 1.416 +} 1.417 + 1.418 +nsresult 1.419 +ContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent, 1.420 + bool aForward, 1.421 + uint32_t* aXPOffset) 1.422 +{ 1.423 + // XXX This method assumes that the frame boundaries must be cluster 1.424 + // boundaries. It's false, but no problem now, maybe. 1.425 + if (!aContent->IsNodeOfType(nsINode::eTEXT) || 1.426 + *aXPOffset == 0 || *aXPOffset == aContent->TextLength()) { 1.427 + return NS_OK; 1.428 + } 1.429 + 1.430 + NS_ASSERTION(*aXPOffset <= aContent->TextLength(), 1.431 + "offset is out of range."); 1.432 + 1.433 + nsRefPtr<nsFrameSelection> fs = mPresShell->FrameSelection(); 1.434 + int32_t offsetInFrame; 1.435 + nsFrameSelection::HINT hint = 1.436 + aForward ? nsFrameSelection::HINTLEFT : nsFrameSelection::HINTRIGHT; 1.437 + nsIFrame* frame = fs->GetFrameForNodeOffset(aContent, int32_t(*aXPOffset), 1.438 + hint, &offsetInFrame); 1.439 + if (!frame) { 1.440 + // This content doesn't have any frames, we only can check surrogate pair... 1.441 + const nsTextFragment* text = aContent->GetText(); 1.442 + NS_ENSURE_TRUE(text, NS_ERROR_FAILURE); 1.443 + if (NS_IS_LOW_SURROGATE(text->CharAt(*aXPOffset)) && 1.444 + NS_IS_HIGH_SURROGATE(text->CharAt(*aXPOffset - 1))) { 1.445 + *aXPOffset += aForward ? 1 : -1; 1.446 + } 1.447 + return NS_OK; 1.448 + } 1.449 + int32_t startOffset, endOffset; 1.450 + nsresult rv = frame->GetOffsets(startOffset, endOffset); 1.451 + NS_ENSURE_SUCCESS(rv, rv); 1.452 + if (*aXPOffset == static_cast<uint32_t>(startOffset) || 1.453 + *aXPOffset == static_cast<uint32_t>(endOffset)) { 1.454 + return NS_OK; 1.455 + } 1.456 + if (frame->GetType() != nsGkAtoms::textFrame) { 1.457 + return NS_ERROR_FAILURE; 1.458 + } 1.459 + nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame); 1.460 + int32_t newOffsetInFrame = *aXPOffset - startOffset; 1.461 + newOffsetInFrame += aForward ? -1 : 1; 1.462 + textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame); 1.463 + *aXPOffset = startOffset + newOffsetInFrame; 1.464 + return NS_OK; 1.465 +} 1.466 + 1.467 +nsresult 1.468 +ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, 1.469 + uint32_t aOffset, 1.470 + uint32_t aLength, 1.471 + LineBreakType aLineBreakType, 1.472 + bool aExpandToClusterBoundaries, 1.473 + uint32_t* aNewOffset) 1.474 +{ 1.475 + if (aNewOffset) { 1.476 + *aNewOffset = aOffset; 1.477 + } 1.478 + 1.479 + nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator(); 1.480 + nsresult rv = iter->Init(mRootContent); 1.481 + NS_ENSURE_SUCCESS(rv, rv); 1.482 + 1.483 + uint32_t offset = 0; 1.484 + uint32_t endOffset = aOffset + aLength; 1.485 + bool startSet = false; 1.486 + for (; !iter->IsDone(); iter->Next()) { 1.487 + nsINode* node = iter->GetCurrentNode(); 1.488 + if (!node) { 1.489 + break; 1.490 + } 1.491 + if (!node->IsNodeOfType(nsINode::eCONTENT)) { 1.492 + continue; 1.493 + } 1.494 + nsIContent* content = static_cast<nsIContent*>(node); 1.495 + 1.496 + uint32_t textLength = GetTextLength(content, aLineBreakType); 1.497 + if (!textLength) { 1.498 + continue; 1.499 + } 1.500 + 1.501 + if (offset <= aOffset && aOffset < offset + textLength) { 1.502 + nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content)); 1.503 + NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!"); 1.504 + 1.505 + uint32_t xpOffset; 1.506 + if (!content->IsNodeOfType(nsINode::eTEXT)) { 1.507 + xpOffset = 0; 1.508 + } else { 1.509 + xpOffset = aOffset - offset; 1.510 + if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) { 1.511 + xpOffset = ConvertToXPOffset(content, xpOffset); 1.512 + } 1.513 + } 1.514 + 1.515 + if (aExpandToClusterBoundaries) { 1.516 + uint32_t oldXPOffset = xpOffset; 1.517 + rv = ExpandToClusterBoundary(content, false, &xpOffset); 1.518 + NS_ENSURE_SUCCESS(rv, rv); 1.519 + if (aNewOffset) { 1.520 + // This is correct since a cluster shouldn't include line break. 1.521 + *aNewOffset -= (oldXPOffset - xpOffset); 1.522 + } 1.523 + } 1.524 + 1.525 + rv = aRange->SetStart(domNode, int32_t(xpOffset)); 1.526 + NS_ENSURE_SUCCESS(rv, rv); 1.527 + startSet = true; 1.528 + if (aLength == 0) { 1.529 + // Ensure that the end offset and the start offset are same. 1.530 + rv = aRange->SetEnd(domNode, int32_t(xpOffset)); 1.531 + NS_ENSURE_SUCCESS(rv, rv); 1.532 + return NS_OK; 1.533 + } 1.534 + } 1.535 + if (endOffset <= offset + textLength) { 1.536 + nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content)); 1.537 + NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!"); 1.538 + 1.539 + uint32_t xpOffset; 1.540 + if (content->IsNodeOfType(nsINode::eTEXT)) { 1.541 + xpOffset = endOffset - offset; 1.542 + if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) { 1.543 + xpOffset = ConvertToXPOffset(content, xpOffset); 1.544 + } 1.545 + if (aExpandToClusterBoundaries) { 1.546 + rv = ExpandToClusterBoundary(content, true, &xpOffset); 1.547 + NS_ENSURE_SUCCESS(rv, rv); 1.548 + } 1.549 + } else { 1.550 + // Use first position of next node, because the end node is ignored 1.551 + // by ContentIterator when the offset is zero. 1.552 + xpOffset = 0; 1.553 + iter->Next(); 1.554 + if (iter->IsDone()) { 1.555 + break; 1.556 + } 1.557 + domNode = do_QueryInterface(iter->GetCurrentNode()); 1.558 + } 1.559 + 1.560 + rv = aRange->SetEnd(domNode, int32_t(xpOffset)); 1.561 + NS_ENSURE_SUCCESS(rv, rv); 1.562 + return NS_OK; 1.563 + } 1.564 + 1.565 + offset += textLength; 1.566 + } 1.567 + 1.568 + if (offset < aOffset) { 1.569 + return NS_ERROR_FAILURE; 1.570 + } 1.571 + 1.572 + nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mRootContent)); 1.573 + NS_ASSERTION(domNode, "lastContent doesn't have nsIDOMNode!"); 1.574 + if (!startSet) { 1.575 + MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT)); 1.576 + rv = aRange->SetStart(domNode, int32_t(mRootContent->GetChildCount())); 1.577 + NS_ENSURE_SUCCESS(rv, rv); 1.578 + if (aNewOffset) { 1.579 + *aNewOffset = offset; 1.580 + } 1.581 + } 1.582 + rv = aRange->SetEnd(domNode, int32_t(mRootContent->GetChildCount())); 1.583 + NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed"); 1.584 + return rv; 1.585 +} 1.586 + 1.587 +/* static */ LineBreakType 1.588 +ContentEventHandler::GetLineBreakType(WidgetQueryContentEvent* aEvent) 1.589 +{ 1.590 + return GetLineBreakType(aEvent->mUseNativeLineBreak); 1.591 +} 1.592 + 1.593 +/* static */ LineBreakType 1.594 +ContentEventHandler::GetLineBreakType(WidgetSelectionEvent* aEvent) 1.595 +{ 1.596 + return GetLineBreakType(aEvent->mUseNativeLineBreak); 1.597 +} 1.598 + 1.599 +/* static */ LineBreakType 1.600 +ContentEventHandler::GetLineBreakType(bool aUseNativeLineBreak) 1.601 +{ 1.602 + return aUseNativeLineBreak ? 1.603 + LINE_BREAK_TYPE_NATIVE : LINE_BREAK_TYPE_XP; 1.604 +} 1.605 + 1.606 +nsresult 1.607 +ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent) 1.608 +{ 1.609 + nsresult rv = Init(aEvent); 1.610 + if (NS_FAILED(rv)) { 1.611 + return rv; 1.612 + } 1.613 + 1.614 + NS_ASSERTION(aEvent->mReply.mString.IsEmpty(), 1.615 + "The reply string must be empty"); 1.616 + 1.617 + LineBreakType lineBreakType = GetLineBreakType(aEvent); 1.618 + rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, 1.619 + &aEvent->mReply.mOffset, lineBreakType); 1.620 + NS_ENSURE_SUCCESS(rv, rv); 1.621 + 1.622 + nsCOMPtr<nsIDOMNode> anchorDomNode, focusDomNode; 1.623 + rv = mSelection->GetAnchorNode(getter_AddRefs(anchorDomNode)); 1.624 + NS_ENSURE_TRUE(anchorDomNode, NS_ERROR_FAILURE); 1.625 + rv = mSelection->GetFocusNode(getter_AddRefs(focusDomNode)); 1.626 + NS_ENSURE_TRUE(focusDomNode, NS_ERROR_FAILURE); 1.627 + 1.628 + int32_t anchorOffset, focusOffset; 1.629 + rv = mSelection->GetAnchorOffset(&anchorOffset); 1.630 + NS_ENSURE_SUCCESS(rv, rv); 1.631 + rv = mSelection->GetFocusOffset(&focusOffset); 1.632 + NS_ENSURE_SUCCESS(rv, rv); 1.633 + 1.634 + nsCOMPtr<nsINode> anchorNode(do_QueryInterface(anchorDomNode)); 1.635 + nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDomNode)); 1.636 + NS_ENSURE_TRUE(anchorNode && focusNode, NS_ERROR_UNEXPECTED); 1.637 + 1.638 + int16_t compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset, 1.639 + focusNode, focusOffset); 1.640 + aEvent->mReply.mReversed = compare > 0; 1.641 + 1.642 + if (compare) { 1.643 + rv = GenerateFlatTextContent(mFirstSelectedRange, aEvent->mReply.mString, 1.644 + lineBreakType); 1.645 + NS_ENSURE_SUCCESS(rv, rv); 1.646 + } 1.647 + 1.648 + aEvent->mSucceeded = true; 1.649 + return NS_OK; 1.650 +} 1.651 + 1.652 +nsresult 1.653 +ContentEventHandler::OnQueryTextContent(WidgetQueryContentEvent* aEvent) 1.654 +{ 1.655 + nsresult rv = Init(aEvent); 1.656 + if (NS_FAILED(rv)) { 1.657 + return rv; 1.658 + } 1.659 + 1.660 + NS_ASSERTION(aEvent->mReply.mString.IsEmpty(), 1.661 + "The reply string must be empty"); 1.662 + 1.663 + LineBreakType lineBreakType = GetLineBreakType(aEvent); 1.664 + 1.665 + nsRefPtr<nsRange> range = new nsRange(mRootContent); 1.666 + rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 1.667 + aEvent->mInput.mLength, lineBreakType, false, 1.668 + &aEvent->mReply.mOffset); 1.669 + NS_ENSURE_SUCCESS(rv, rv); 1.670 + 1.671 + rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType); 1.672 + NS_ENSURE_SUCCESS(rv, rv); 1.673 + 1.674 + aEvent->mSucceeded = true; 1.675 + 1.676 + return NS_OK; 1.677 +} 1.678 + 1.679 +// Adjust to use a child node if possible 1.680 +// to make the returned rect more accurate 1.681 +static nsINode* AdjustTextRectNode(nsINode* aNode, 1.682 + int32_t& aNodeOffset) 1.683 +{ 1.684 + int32_t childCount = int32_t(aNode->GetChildCount()); 1.685 + nsINode* node = aNode; 1.686 + if (childCount) { 1.687 + if (aNodeOffset < childCount) { 1.688 + node = aNode->GetChildAt(aNodeOffset); 1.689 + aNodeOffset = 0; 1.690 + } else if (aNodeOffset == childCount) { 1.691 + node = aNode->GetChildAt(childCount - 1); 1.692 + aNodeOffset = node->IsNodeOfType(nsINode::eTEXT) ? 1.693 + static_cast<int32_t>(static_cast<nsIContent*>(node)->TextLength()) : 1; 1.694 + } 1.695 + } 1.696 + return node; 1.697 +} 1.698 + 1.699 +// Similar to nsFrameSelection::GetFrameForNodeOffset, 1.700 +// but this is more flexible for OnQueryTextRect to use 1.701 +static nsresult GetFrameForTextRect(nsINode* aNode, 1.702 + int32_t aNodeOffset, 1.703 + bool aHint, 1.704 + nsIFrame** aReturnFrame) 1.705 +{ 1.706 + NS_ENSURE_TRUE(aNode && aNode->IsNodeOfType(nsINode::eCONTENT), 1.707 + NS_ERROR_UNEXPECTED); 1.708 + nsIContent* content = static_cast<nsIContent*>(aNode); 1.709 + nsIFrame* frame = content->GetPrimaryFrame(); 1.710 + NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); 1.711 + int32_t childNodeOffset = 0; 1.712 + return frame->GetChildFrameContainingOffset(aNodeOffset, aHint, 1.713 + &childNodeOffset, aReturnFrame); 1.714 +} 1.715 + 1.716 +nsresult 1.717 +ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent) 1.718 +{ 1.719 + nsresult rv = Init(aEvent); 1.720 + if (NS_FAILED(rv)) { 1.721 + return rv; 1.722 + } 1.723 + 1.724 + LineBreakType lineBreakType = GetLineBreakType(aEvent); 1.725 + nsRefPtr<nsRange> range = new nsRange(mRootContent); 1.726 + rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 1.727 + aEvent->mInput.mLength, lineBreakType, true, 1.728 + &aEvent->mReply.mOffset); 1.729 + NS_ENSURE_SUCCESS(rv, rv); 1.730 + rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType); 1.731 + NS_ENSURE_SUCCESS(rv, rv); 1.732 + 1.733 + // used to iterate over all contents and their frames 1.734 + nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator(); 1.735 + iter->Init(range); 1.736 + 1.737 + // get the starting frame 1.738 + int32_t nodeOffset = range->StartOffset(); 1.739 + nsINode* node = iter->GetCurrentNode(); 1.740 + if (!node) { 1.741 + node = AdjustTextRectNode(range->GetStartParent(), nodeOffset); 1.742 + } 1.743 + nsIFrame* firstFrame = nullptr; 1.744 + rv = GetFrameForTextRect(node, nodeOffset, true, &firstFrame); 1.745 + NS_ENSURE_SUCCESS(rv, rv); 1.746 + 1.747 + // get the starting frame rect 1.748 + nsRect rect(nsPoint(0, 0), firstFrame->GetRect().Size()); 1.749 + rv = ConvertToRootViewRelativeOffset(firstFrame, rect); 1.750 + NS_ENSURE_SUCCESS(rv, rv); 1.751 + nsRect frameRect = rect; 1.752 + nsPoint ptOffset; 1.753 + firstFrame->GetPointFromOffset(nodeOffset, &ptOffset); 1.754 + // minus 1 to avoid creating an empty rect 1.755 + rect.x += ptOffset.x - 1; 1.756 + rect.width -= ptOffset.x - 1; 1.757 + 1.758 + // get the ending frame 1.759 + nodeOffset = range->EndOffset(); 1.760 + node = AdjustTextRectNode(range->GetEndParent(), nodeOffset); 1.761 + nsIFrame* lastFrame = nullptr; 1.762 + rv = GetFrameForTextRect(node, nodeOffset, range->Collapsed(), &lastFrame); 1.763 + NS_ENSURE_SUCCESS(rv, rv); 1.764 + 1.765 + // iterate over all covered frames 1.766 + for (nsIFrame* frame = firstFrame; frame != lastFrame;) { 1.767 + frame = frame->GetNextContinuation(); 1.768 + if (!frame) { 1.769 + do { 1.770 + iter->Next(); 1.771 + node = iter->GetCurrentNode(); 1.772 + if (!node) { 1.773 + break; 1.774 + } 1.775 + if (!node->IsNodeOfType(nsINode::eCONTENT)) { 1.776 + continue; 1.777 + } 1.778 + frame = static_cast<nsIContent*>(node)->GetPrimaryFrame(); 1.779 + } while (!frame && !iter->IsDone()); 1.780 + if (!frame) { 1.781 + // this can happen when the end offset of the range is 0. 1.782 + frame = lastFrame; 1.783 + } 1.784 + } 1.785 + frameRect.SetRect(nsPoint(0, 0), frame->GetRect().Size()); 1.786 + rv = ConvertToRootViewRelativeOffset(frame, frameRect); 1.787 + NS_ENSURE_SUCCESS(rv, rv); 1.788 + if (frame != lastFrame) { 1.789 + // not last frame, so just add rect to previous result 1.790 + rect.UnionRect(rect, frameRect); 1.791 + } 1.792 + } 1.793 + 1.794 + // get the ending frame rect 1.795 + lastFrame->GetPointFromOffset(nodeOffset, &ptOffset); 1.796 + // minus 1 to avoid creating an empty rect 1.797 + frameRect.width -= lastFrame->GetRect().width - ptOffset.x - 1; 1.798 + 1.799 + if (firstFrame == lastFrame) { 1.800 + rect.IntersectRect(rect, frameRect); 1.801 + } else { 1.802 + rect.UnionRect(rect, frameRect); 1.803 + } 1.804 + aEvent->mReply.mRect = 1.805 + rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()); 1.806 + aEvent->mSucceeded = true; 1.807 + return NS_OK; 1.808 +} 1.809 + 1.810 +nsresult 1.811 +ContentEventHandler::OnQueryEditorRect(WidgetQueryContentEvent* aEvent) 1.812 +{ 1.813 + nsresult rv = Init(aEvent); 1.814 + if (NS_FAILED(rv)) { 1.815 + return rv; 1.816 + } 1.817 + 1.818 + nsIContent* focusedContent = GetFocusedContent(); 1.819 + rv = QueryContentRect(IsPlugin(focusedContent) ? 1.820 + focusedContent : mRootContent.get(), aEvent); 1.821 + NS_ENSURE_SUCCESS(rv, rv); 1.822 + return NS_OK; 1.823 +} 1.824 + 1.825 +nsresult 1.826 +ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent) 1.827 +{ 1.828 + nsresult rv = Init(aEvent); 1.829 + if (NS_FAILED(rv)) { 1.830 + return rv; 1.831 + } 1.832 + 1.833 + LineBreakType lineBreakType = GetLineBreakType(aEvent); 1.834 + 1.835 + nsRefPtr<nsCaret> caret = mPresShell->GetCaret(); 1.836 + NS_ASSERTION(caret, "GetCaret returned null"); 1.837 + 1.838 + // When the selection is collapsed and the queried offset is current caret 1.839 + // position, we should return the "real" caret rect. 1.840 + bool selectionIsCollapsed; 1.841 + rv = mSelection->GetIsCollapsed(&selectionIsCollapsed); 1.842 + NS_ENSURE_SUCCESS(rv, rv); 1.843 + 1.844 + if (selectionIsCollapsed) { 1.845 + uint32_t offset; 1.846 + rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset, 1.847 + lineBreakType); 1.848 + NS_ENSURE_SUCCESS(rv, rv); 1.849 + if (offset == aEvent->mInput.mOffset) { 1.850 + nsRect rect; 1.851 + nsIFrame* caretFrame = caret->GetGeometry(mSelection, &rect); 1.852 + if (!caretFrame) { 1.853 + return NS_ERROR_FAILURE; 1.854 + } 1.855 + rv = ConvertToRootViewRelativeOffset(caretFrame, rect); 1.856 + NS_ENSURE_SUCCESS(rv, rv); 1.857 + aEvent->mReply.mRect = 1.858 + rect.ToOutsidePixels(caretFrame->PresContext()->AppUnitsPerDevPixel()); 1.859 + aEvent->mReply.mOffset = aEvent->mInput.mOffset; 1.860 + aEvent->mSucceeded = true; 1.861 + return NS_OK; 1.862 + } 1.863 + } 1.864 + 1.865 + // Otherwise, we should set the guessed caret rect. 1.866 + nsRefPtr<nsRange> range = new nsRange(mRootContent); 1.867 + rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0, 1.868 + lineBreakType, true, 1.869 + &aEvent->mReply.mOffset); 1.870 + NS_ENSURE_SUCCESS(rv, rv); 1.871 + 1.872 + int32_t xpOffsetInFrame; 1.873 + nsIFrame* frame; 1.874 + rv = GetStartFrameAndOffset(range, &frame, &xpOffsetInFrame); 1.875 + NS_ENSURE_SUCCESS(rv, rv); 1.876 + 1.877 + nsPoint posInFrame; 1.878 + rv = frame->GetPointFromOffset(range->StartOffset(), &posInFrame); 1.879 + NS_ENSURE_SUCCESS(rv, rv); 1.880 + 1.881 + nsRect rect; 1.882 + rect.x = posInFrame.x; 1.883 + rect.y = posInFrame.y; 1.884 + rect.width = caret->GetCaretRect().width; 1.885 + rect.height = frame->GetSize().height; 1.886 + 1.887 + rv = ConvertToRootViewRelativeOffset(frame, rect); 1.888 + NS_ENSURE_SUCCESS(rv, rv); 1.889 + 1.890 + aEvent->mReply.mRect = 1.891 + rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()); 1.892 + aEvent->mSucceeded = true; 1.893 + return NS_OK; 1.894 +} 1.895 + 1.896 +nsresult 1.897 +ContentEventHandler::OnQueryContentState(WidgetQueryContentEvent* aEvent) 1.898 +{ 1.899 + nsresult rv = Init(aEvent); 1.900 + if (NS_FAILED(rv)) { 1.901 + return rv; 1.902 + } 1.903 + aEvent->mSucceeded = true; 1.904 + return NS_OK; 1.905 +} 1.906 + 1.907 +nsresult 1.908 +ContentEventHandler::OnQuerySelectionAsTransferable( 1.909 + WidgetQueryContentEvent* aEvent) 1.910 +{ 1.911 + nsresult rv = Init(aEvent); 1.912 + if (NS_FAILED(rv)) { 1.913 + return rv; 1.914 + } 1.915 + 1.916 + if (!aEvent->mReply.mHasSelection) { 1.917 + aEvent->mSucceeded = true; 1.918 + aEvent->mReply.mTransferable = nullptr; 1.919 + return NS_OK; 1.920 + } 1.921 + 1.922 + nsCOMPtr<nsIDocument> doc = mPresShell->GetDocument(); 1.923 + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); 1.924 + 1.925 + rv = nsCopySupport::GetTransferableForSelection( 1.926 + mSelection, doc, getter_AddRefs(aEvent->mReply.mTransferable)); 1.927 + NS_ENSURE_SUCCESS(rv, rv); 1.928 + 1.929 + aEvent->mSucceeded = true; 1.930 + return NS_OK; 1.931 +} 1.932 + 1.933 +nsresult 1.934 +ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent) 1.935 +{ 1.936 + nsresult rv = Init(aEvent); 1.937 + if (NS_FAILED(rv)) { 1.938 + return rv; 1.939 + } 1.940 + 1.941 + nsIFrame* rootFrame = mPresShell->GetRootFrame(); 1.942 + NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE); 1.943 + nsIWidget* rootWidget = rootFrame->GetNearestWidget(); 1.944 + NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE); 1.945 + 1.946 + // The root frame's widget might be different, e.g., the event was fired on 1.947 + // a popup but the rootFrame is the document root. 1.948 + if (rootWidget != aEvent->widget) { 1.949 + NS_PRECONDITION(aEvent->widget, "The event must have the widget"); 1.950 + nsView* view = nsView::GetViewFor(aEvent->widget); 1.951 + NS_ENSURE_TRUE(view, NS_ERROR_FAILURE); 1.952 + rootFrame = view->GetFrame(); 1.953 + NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE); 1.954 + rootWidget = rootFrame->GetNearestWidget(); 1.955 + NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE); 1.956 + } 1.957 + 1.958 + WidgetQueryContentEvent eventOnRoot(true, NS_QUERY_CHARACTER_AT_POINT, 1.959 + rootWidget); 1.960 + eventOnRoot.mUseNativeLineBreak = aEvent->mUseNativeLineBreak; 1.961 + eventOnRoot.refPoint = aEvent->refPoint; 1.962 + if (rootWidget != aEvent->widget) { 1.963 + eventOnRoot.refPoint += LayoutDeviceIntPoint::FromUntyped( 1.964 + aEvent->widget->WidgetToScreenOffset() - 1.965 + rootWidget->WidgetToScreenOffset()); 1.966 + } 1.967 + nsPoint ptInRoot = 1.968 + nsLayoutUtils::GetEventCoordinatesRelativeTo(&eventOnRoot, rootFrame); 1.969 + 1.970 + nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot); 1.971 + if (!targetFrame || targetFrame->GetType() != nsGkAtoms::textFrame || 1.972 + !targetFrame->GetContent() || 1.973 + !nsContentUtils::ContentIsDescendantOf(targetFrame->GetContent(), 1.974 + mRootContent)) { 1.975 + // there is no character at the point. 1.976 + aEvent->mReply.mOffset = WidgetQueryContentEvent::NOT_FOUND; 1.977 + aEvent->mSucceeded = true; 1.978 + return NS_OK; 1.979 + } 1.980 + nsPoint ptInTarget = ptInRoot + rootFrame->GetOffsetToCrossDoc(targetFrame); 1.981 + int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel(); 1.982 + int32_t targetAPD = targetFrame->PresContext()->AppUnitsPerDevPixel(); 1.983 + ptInTarget = ptInTarget.ConvertAppUnits(rootAPD, targetAPD); 1.984 + 1.985 + nsTextFrame* textframe = static_cast<nsTextFrame*>(targetFrame); 1.986 + nsIFrame::ContentOffsets contentOffsets = 1.987 + textframe->GetCharacterOffsetAtFramePoint(ptInTarget); 1.988 + NS_ENSURE_TRUE(contentOffsets.content, NS_ERROR_FAILURE); 1.989 + uint32_t offset; 1.990 + rv = GetFlatTextOffsetOfRange(mRootContent, contentOffsets.content, 1.991 + contentOffsets.offset, &offset, 1.992 + GetLineBreakType(aEvent)); 1.993 + NS_ENSURE_SUCCESS(rv, rv); 1.994 + 1.995 + WidgetQueryContentEvent textRect(true, NS_QUERY_TEXT_RECT, aEvent->widget); 1.996 + textRect.InitForQueryTextRect(offset, 1, aEvent->mUseNativeLineBreak); 1.997 + rv = OnQueryTextRect(&textRect); 1.998 + NS_ENSURE_SUCCESS(rv, rv); 1.999 + NS_ENSURE_TRUE(textRect.mSucceeded, NS_ERROR_FAILURE); 1.1000 + 1.1001 + // currently, we don't need to get the actual text. 1.1002 + aEvent->mReply.mOffset = offset; 1.1003 + aEvent->mReply.mRect = textRect.mReply.mRect; 1.1004 + aEvent->mSucceeded = true; 1.1005 + return NS_OK; 1.1006 +} 1.1007 + 1.1008 +nsresult 1.1009 +ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent) 1.1010 +{ 1.1011 + NS_ASSERTION(aEvent, "aEvent must not be null"); 1.1012 + 1.1013 + nsresult rv = InitBasic(); 1.1014 + if (NS_FAILED(rv)) { 1.1015 + return rv; 1.1016 + } 1.1017 + 1.1018 + aEvent->mSucceeded = false; 1.1019 + aEvent->mReply.mWidgetIsHit = false; 1.1020 + 1.1021 + NS_ENSURE_TRUE(aEvent->widget, NS_ERROR_FAILURE); 1.1022 + 1.1023 + nsIDocument* doc = mPresShell->GetDocument(); 1.1024 + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); 1.1025 + nsIFrame* docFrame = mPresShell->GetRootFrame(); 1.1026 + NS_ENSURE_TRUE(docFrame, NS_ERROR_FAILURE); 1.1027 + 1.1028 + LayoutDeviceIntPoint eventLoc = aEvent->refPoint + 1.1029 + LayoutDeviceIntPoint::FromUntyped(aEvent->widget->WidgetToScreenOffset()); 1.1030 + nsIntRect docFrameRect = docFrame->GetScreenRect(); // Returns CSS pixels 1.1031 + CSSIntPoint eventLocCSS( 1.1032 + mPresContext->DevPixelsToIntCSSPixels(eventLoc.x) - docFrameRect.x, 1.1033 + mPresContext->DevPixelsToIntCSSPixels(eventLoc.y) - docFrameRect.y); 1.1034 + 1.1035 + Element* contentUnderMouse = 1.1036 + doc->ElementFromPointHelper(eventLocCSS.x, eventLocCSS.y, false, false); 1.1037 + if (contentUnderMouse) { 1.1038 + nsIWidget* targetWidget = nullptr; 1.1039 + nsIFrame* targetFrame = contentUnderMouse->GetPrimaryFrame(); 1.1040 + nsIObjectFrame* pluginFrame = do_QueryFrame(targetFrame); 1.1041 + if (pluginFrame) { 1.1042 + targetWidget = pluginFrame->GetWidget(); 1.1043 + } else if (targetFrame) { 1.1044 + targetWidget = targetFrame->GetNearestWidget(); 1.1045 + } 1.1046 + if (aEvent->widget == targetWidget) { 1.1047 + aEvent->mReply.mWidgetIsHit = true; 1.1048 + } 1.1049 + } 1.1050 + 1.1051 + aEvent->mSucceeded = true; 1.1052 + return NS_OK; 1.1053 +} 1.1054 + 1.1055 +/* static */ nsresult 1.1056 +ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, 1.1057 + nsINode* aNode, 1.1058 + int32_t aNodeOffset, 1.1059 + uint32_t* aOffset, 1.1060 + LineBreakType aLineBreakType) 1.1061 +{ 1.1062 + NS_ENSURE_STATE(aRootContent); 1.1063 + NS_ASSERTION(aOffset, "param is invalid"); 1.1064 + 1.1065 + nsRefPtr<nsRange> prev = new nsRange(aRootContent); 1.1066 + nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(aRootContent)); 1.1067 + prev->SetStart(rootDOMNode, 0); 1.1068 + 1.1069 + nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(aNode)); 1.1070 + NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode"); 1.1071 + 1.1072 + nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator(); 1.1073 + 1.1074 + if (aNode->Length() >= static_cast<uint32_t>(aNodeOffset)) { 1.1075 + // Offset is within node's length; set end of range to that offset 1.1076 + prev->SetEnd(startDOMNode, aNodeOffset); 1.1077 + iter->Init(prev); 1.1078 + } else if (aNode != static_cast<nsINode*>(aRootContent)) { 1.1079 + // Offset is past node's length; set end of range to end of node 1.1080 + prev->SetEndAfter(startDOMNode); 1.1081 + iter->Init(prev); 1.1082 + } else { 1.1083 + // Offset is past the root node; set end of range to end of root node 1.1084 + iter->Init(aRootContent); 1.1085 + } 1.1086 + 1.1087 + nsCOMPtr<nsINode> startNode = do_QueryInterface(startDOMNode); 1.1088 + nsINode* endNode = aNode; 1.1089 + 1.1090 + *aOffset = 0; 1.1091 + for (; !iter->IsDone(); iter->Next()) { 1.1092 + nsINode* node = iter->GetCurrentNode(); 1.1093 + if (!node) { 1.1094 + break; 1.1095 + } 1.1096 + if (!node->IsNodeOfType(nsINode::eCONTENT)) { 1.1097 + continue; 1.1098 + } 1.1099 + nsIContent* content = static_cast<nsIContent*>(node); 1.1100 + 1.1101 + if (node->IsNodeOfType(nsINode::eTEXT)) { 1.1102 + // Note: our range always starts from offset 0 1.1103 + if (node == endNode) { 1.1104 + *aOffset += GetTextLength(content, aLineBreakType, aNodeOffset); 1.1105 + } else { 1.1106 + *aOffset += GetTextLength(content, aLineBreakType); 1.1107 + } 1.1108 + } else if (IsContentBR(content)) { 1.1109 +#if defined(XP_WIN) 1.1110 + // On Windows, the length of the newline is 2. 1.1111 + *aOffset += (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1; 1.1112 +#else 1.1113 + // On other platforms, the length of the newline is 1. 1.1114 + *aOffset += 1; 1.1115 +#endif 1.1116 + } 1.1117 + } 1.1118 + return NS_OK; 1.1119 +} 1.1120 + 1.1121 +/* static */ nsresult 1.1122 +ContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent, 1.1123 + nsRange* aRange, 1.1124 + uint32_t* aOffset, 1.1125 + LineBreakType aLineBreakType) 1.1126 +{ 1.1127 + nsINode* startNode = aRange->GetStartParent(); 1.1128 + NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE); 1.1129 + int32_t startOffset = aRange->StartOffset(); 1.1130 + return GetFlatTextOffsetOfRange(aRootContent, startNode, startOffset, 1.1131 + aOffset, aLineBreakType); 1.1132 +} 1.1133 + 1.1134 +nsresult 1.1135 +ContentEventHandler::GetStartFrameAndOffset(nsRange* aRange, 1.1136 + nsIFrame** aFrame, 1.1137 + int32_t* aOffsetInFrame) 1.1138 +{ 1.1139 + NS_ASSERTION(aRange && aFrame && aOffsetInFrame, "params are invalid"); 1.1140 + 1.1141 + nsIContent* content = nullptr; 1.1142 + nsINode* node = aRange->GetStartParent(); 1.1143 + if (node && node->IsNodeOfType(nsINode::eCONTENT)) { 1.1144 + content = static_cast<nsIContent*>(node); 1.1145 + } 1.1146 + NS_ASSERTION(content, "the start node doesn't have nsIContent!"); 1.1147 + 1.1148 + nsRefPtr<nsFrameSelection> fs = mPresShell->FrameSelection(); 1.1149 + *aFrame = fs->GetFrameForNodeOffset(content, aRange->StartOffset(), 1.1150 + fs->GetHint(), aOffsetInFrame); 1.1151 + NS_ENSURE_TRUE((*aFrame), NS_ERROR_FAILURE); 1.1152 + NS_ASSERTION((*aFrame)->GetType() == nsGkAtoms::textFrame, 1.1153 + "The frame is not textframe"); 1.1154 + return NS_OK; 1.1155 +} 1.1156 + 1.1157 +nsresult 1.1158 +ContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame, 1.1159 + nsRect& aRect) 1.1160 +{ 1.1161 + NS_ASSERTION(aFrame, "aFrame must not be null"); 1.1162 + 1.1163 + nsView* view = nullptr; 1.1164 + nsPoint posInView; 1.1165 + aFrame->GetOffsetFromView(posInView, &view); 1.1166 + if (!view) { 1.1167 + return NS_ERROR_FAILURE; 1.1168 + } 1.1169 + aRect += posInView + view->GetOffsetTo(nullptr); 1.1170 + return NS_OK; 1.1171 +} 1.1172 + 1.1173 +static void AdjustRangeForSelection(nsIContent* aRoot, 1.1174 + nsINode** aNode, 1.1175 + int32_t* aNodeOffset) 1.1176 +{ 1.1177 + nsINode* node = *aNode; 1.1178 + int32_t nodeOffset = *aNodeOffset; 1.1179 + if (aRoot != node && node->GetParent()) { 1.1180 + if (node->IsNodeOfType(nsINode::eTEXT)) { 1.1181 + // When the offset is at the end of the text node, set it to after the 1.1182 + // text node, to make sure the caret is drawn on a new line when the last 1.1183 + // character of the text node is '\n' 1.1184 + int32_t nodeLength = 1.1185 + static_cast<int32_t>(static_cast<nsIContent*>(node)->TextLength()); 1.1186 + MOZ_ASSERT(nodeOffset <= nodeLength, "Offset is past length of text node"); 1.1187 + if (nodeOffset == nodeLength) { 1.1188 + node = node->GetParent(); 1.1189 + nodeOffset = node->IndexOf(*aNode) + 1; 1.1190 + } 1.1191 + } else { 1.1192 + node = node->GetParent(); 1.1193 + nodeOffset = node->IndexOf(*aNode) + (nodeOffset ? 1 : 0); 1.1194 + } 1.1195 + } 1.1196 + 1.1197 + nsIContent* brContent = node->GetChildAt(nodeOffset - 1); 1.1198 + while (brContent && brContent->IsHTML()) { 1.1199 + if (brContent->Tag() != nsGkAtoms::br || IsContentBR(brContent)) { 1.1200 + break; 1.1201 + } 1.1202 + brContent = node->GetChildAt(--nodeOffset - 1); 1.1203 + } 1.1204 + *aNode = node; 1.1205 + *aNodeOffset = std::max(nodeOffset, 0); 1.1206 +} 1.1207 + 1.1208 +nsresult 1.1209 +ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent) 1.1210 +{ 1.1211 + aEvent->mSucceeded = false; 1.1212 + 1.1213 + // Get selection to manipulate 1.1214 + // XXX why do we need to get them from ISM? This method should work fine 1.1215 + // without ISM. 1.1216 + nsresult rv = 1.1217 + IMEStateManager::GetFocusSelectionAndRoot(getter_AddRefs(mSelection), 1.1218 + getter_AddRefs(mRootContent)); 1.1219 + if (rv != NS_ERROR_NOT_AVAILABLE) { 1.1220 + NS_ENSURE_SUCCESS(rv, rv); 1.1221 + } else { 1.1222 + rv = Init(aEvent); 1.1223 + NS_ENSURE_SUCCESS(rv, rv); 1.1224 + } 1.1225 + 1.1226 + // Get range from offset and length 1.1227 + nsRefPtr<nsRange> range = new nsRange(mRootContent); 1.1228 + rv = SetRangeFromFlatTextOffset(range, aEvent->mOffset, aEvent->mLength, 1.1229 + GetLineBreakType(aEvent), 1.1230 + aEvent->mExpandToClusterBoundary); 1.1231 + NS_ENSURE_SUCCESS(rv, rv); 1.1232 + 1.1233 + nsINode* startNode = range->GetStartParent(); 1.1234 + nsINode* endNode = range->GetEndParent(); 1.1235 + int32_t startNodeOffset = range->StartOffset(); 1.1236 + int32_t endNodeOffset = range->EndOffset(); 1.1237 + AdjustRangeForSelection(mRootContent, &startNode, &startNodeOffset); 1.1238 + AdjustRangeForSelection(mRootContent, &endNode, &endNodeOffset); 1.1239 + 1.1240 + nsCOMPtr<nsIDOMNode> startDomNode(do_QueryInterface(startNode)); 1.1241 + nsCOMPtr<nsIDOMNode> endDomNode(do_QueryInterface(endNode)); 1.1242 + NS_ENSURE_TRUE(startDomNode && endDomNode, NS_ERROR_UNEXPECTED); 1.1243 + 1.1244 + nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSelection)); 1.1245 + selPrivate->StartBatchChanges(); 1.1246 + 1.1247 + // Clear selection first before setting 1.1248 + rv = mSelection->RemoveAllRanges(); 1.1249 + // Need to call EndBatchChanges at the end even if call failed 1.1250 + if (NS_SUCCEEDED(rv)) { 1.1251 + if (aEvent->mReversed) { 1.1252 + rv = mSelection->Collapse(endDomNode, endNodeOffset); 1.1253 + } else { 1.1254 + rv = mSelection->Collapse(startDomNode, startNodeOffset); 1.1255 + } 1.1256 + if (NS_SUCCEEDED(rv) && 1.1257 + (startDomNode != endDomNode || startNodeOffset != endNodeOffset)) { 1.1258 + if (aEvent->mReversed) { 1.1259 + rv = mSelection->Extend(startDomNode, startNodeOffset); 1.1260 + } else { 1.1261 + rv = mSelection->Extend(endDomNode, endNodeOffset); 1.1262 + } 1.1263 + } 1.1264 + } 1.1265 + selPrivate->EndBatchChanges(); 1.1266 + NS_ENSURE_SUCCESS(rv, rv); 1.1267 + 1.1268 + selPrivate->ScrollIntoViewInternal( 1.1269 + nsISelectionController::SELECTION_FOCUS_REGION, 1.1270 + false, nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis()); 1.1271 + aEvent->mSucceeded = true; 1.1272 + return NS_OK; 1.1273 +} 1.1274 + 1.1275 +} // namespace mozilla