dom/events/ContentEventHandler.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial