ContentEventHandler.cpp (135706B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "ContentEventHandler.h" 8 9 #include <algorithm> 10 11 #include "mozilla/Assertions.h" 12 #include "mozilla/CheckedInt.h" 13 #include "mozilla/ContentIterator.h" 14 #include "mozilla/IMEStateManager.h" 15 #include "mozilla/IntegerRange.h" 16 #include "mozilla/Maybe.h" 17 #include "mozilla/MiscEvents.h" 18 #include "mozilla/PresShell.h" 19 #include "mozilla/RangeBoundary.h" 20 #include "mozilla/RangeUtils.h" 21 #include "mozilla/SelectionMovementUtils.h" 22 #include "mozilla/TextComposition.h" 23 #include "mozilla/TextEditor.h" 24 #include "mozilla/TextEvents.h" 25 #include "mozilla/ViewportUtils.h" 26 #include "mozilla/dom/CharacterDataBuffer.h" 27 #include "mozilla/dom/Element.h" 28 #include "mozilla/dom/HTMLBRElement.h" 29 #include "mozilla/dom/HTMLUnknownElement.h" 30 #include "mozilla/dom/Selection.h" 31 #include "mozilla/dom/StaticRange.h" 32 #include "mozilla/dom/Text.h" 33 #include "nsCOMPtr.h" 34 #include "nsCaret.h" 35 #include "nsContentUtils.h" 36 #include "nsCopySupport.h" 37 #include "nsElementTable.h" 38 #include "nsFocusManager.h" 39 #include "nsFontMetrics.h" 40 #include "nsFrameSelection.h" 41 #include "nsHTMLTags.h" 42 #include "nsIFrame.h" 43 #include "nsLayoutUtils.h" 44 #include "nsPresContext.h" 45 #include "nsQueryObject.h" 46 #include "nsRange.h" 47 #include "nsTextFrame.h" 48 49 // Work around conflicting define in rpcndr.h 50 #if defined(small) 51 # undef small 52 #endif // defined(small) 53 54 #if defined(XP_WIN) && 0 55 # define TRANSLATE_NEW_LINES 56 #endif 57 58 namespace mozilla { 59 60 using namespace dom; 61 using namespace widget; 62 63 /******************************************************************/ 64 /* ContentEventHandler::SimpleRangeBase */ 65 /******************************************************************/ 66 template <> 67 ContentEventHandler::SimpleRangeBase< 68 RefPtr<nsINode>, RangeBoundary>::SimpleRangeBase() = default; 69 70 template <> 71 ContentEventHandler::SimpleRangeBase<nsINode*, 72 RawRangeBoundary>::SimpleRangeBase() 73 : mRoot(nullptr) { 74 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 75 mAssertNoGC.emplace(); 76 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 77 } 78 79 template <> 80 template <typename OtherNodeType, typename OtherRangeBoundaryType> 81 ContentEventHandler::SimpleRangeBase<RefPtr<nsINode>, RangeBoundary>:: 82 SimpleRangeBase( 83 const SimpleRangeBase<OtherNodeType, OtherRangeBoundaryType>& aOther) 84 : mRoot(aOther.GetRoot()), 85 mStart{aOther.Start().AsRaw()}, 86 mEnd{aOther.End().AsRaw()} 87 // Don't use the copy constructor of mAssertNoGC 88 {} 89 90 template <> 91 template <typename OtherNodeType, typename OtherRangeBoundaryType> 92 ContentEventHandler::SimpleRangeBase<nsINode*, RawRangeBoundary>:: 93 SimpleRangeBase( 94 const SimpleRangeBase<OtherNodeType, OtherRangeBoundaryType>& aOther) 95 : mRoot(aOther.GetRoot()), 96 mStart{aOther.Start().AsRaw()}, 97 mEnd{aOther.End().AsRaw()} { 98 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 99 mAssertNoGC.emplace(); 100 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 101 } 102 103 template <> 104 ContentEventHandler::SimpleRangeBase<RefPtr<nsINode>, RangeBoundary>:: 105 SimpleRangeBase( 106 SimpleRangeBase<RefPtr<nsINode>, RangeBoundary>&& aOther) noexcept 107 : mRoot(std::move(aOther.GetRoot())), 108 mStart(std::move(aOther.mStart)), 109 mEnd(std::move(aOther.mEnd)) {} 110 111 template <> 112 ContentEventHandler::SimpleRangeBase<nsINode*, RawRangeBoundary>:: 113 SimpleRangeBase( 114 SimpleRangeBase<nsINode*, RawRangeBoundary>&& aOther) noexcept 115 : mRoot(std::move(aOther.GetRoot())), 116 mStart(std::move(aOther.mStart)), 117 mEnd(std::move(aOther.mEnd)) { 118 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 119 mAssertNoGC.emplace(); 120 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 121 } 122 123 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 124 template <> 125 ContentEventHandler::SimpleRangeBase< 126 RefPtr<nsINode>, RangeBoundary>::~SimpleRangeBase() = default; 127 128 template <> 129 ContentEventHandler::SimpleRangeBase<nsINode*, 130 RawRangeBoundary>::~SimpleRangeBase() { 131 MOZ_DIAGNOSTIC_ASSERT(!mMutationGuard.Mutated(0)); 132 } 133 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 134 135 template <typename NodeType, typename RangeBoundaryType> 136 void ContentEventHandler::SimpleRangeBase< 137 NodeType, RangeBoundaryType>::AssertStartIsBeforeOrEqualToEnd() { 138 MOZ_ASSERT(*nsContentUtils::ComparePoints(mStart, mEnd) <= 0); 139 } 140 141 template <typename NodeType, typename RangeBoundaryType> 142 nsresult 143 ContentEventHandler::SimpleRangeBase<NodeType, RangeBoundaryType>::SetStart( 144 const RawRangeBoundary& aStart) { 145 nsINode* newRoot = RangeUtils::ComputeRootNode(aStart.GetContainer()); 146 if (!newRoot) { 147 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; 148 } 149 150 if (!aStart.IsSetAndValid()) { 151 return NS_ERROR_DOM_INDEX_SIZE_ERR; 152 } 153 154 // Collapse if not positioned yet, or if positioned in another document. 155 if (!IsPositioned() || newRoot != mRoot) { 156 mRoot = newRoot; 157 mStart.CopyFrom(aStart, RangeBoundaryIsMutationObserved::Yes); 158 mEnd.CopyFrom(aStart, RangeBoundaryIsMutationObserved::Yes); 159 return NS_OK; 160 } 161 162 mStart.CopyFrom(aStart, RangeBoundaryIsMutationObserved::Yes); 163 AssertStartIsBeforeOrEqualToEnd(); 164 return NS_OK; 165 } 166 167 template <typename NodeType, typename RangeBoundaryType> 168 nsresult 169 ContentEventHandler::SimpleRangeBase<NodeType, RangeBoundaryType>::SetEnd( 170 const RawRangeBoundary& aEnd) { 171 nsINode* newRoot = RangeUtils::ComputeRootNode(aEnd.GetContainer()); 172 if (!newRoot) { 173 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; 174 } 175 176 if (!aEnd.IsSetAndValid()) { 177 return NS_ERROR_DOM_INDEX_SIZE_ERR; 178 } 179 180 // Collapse if not positioned yet, or if positioned in another document. 181 if (!IsPositioned() || newRoot != mRoot) { 182 mRoot = newRoot; 183 mStart.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); 184 mEnd.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); 185 return NS_OK; 186 } 187 188 mEnd.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); 189 AssertStartIsBeforeOrEqualToEnd(); 190 return NS_OK; 191 } 192 193 template <typename NodeType, typename RangeBoundaryType> 194 nsresult 195 ContentEventHandler::SimpleRangeBase<NodeType, RangeBoundaryType>::SetEndAfter( 196 nsINode* aEndContainer) { 197 return SetEnd(RangeUtils::GetRawRangeBoundaryAfter(aEndContainer)); 198 } 199 200 template <typename NodeType, typename RangeBoundaryType> 201 void ContentEventHandler::SimpleRangeBase< 202 NodeType, RangeBoundaryType>::SetStartAndEnd(const nsRange* aRange) { 203 DebugOnly<nsresult> rv = 204 SetStartAndEnd(aRange->StartRef().AsRaw(), aRange->EndRef().AsRaw()); 205 MOZ_ASSERT(!aRange->IsPositioned() || NS_SUCCEEDED(rv)); 206 } 207 208 template <typename NodeType, typename RangeBoundaryType> 209 nsresult ContentEventHandler::SimpleRangeBase< 210 NodeType, RangeBoundaryType>::SetStartAndEnd(const RawRangeBoundary& aStart, 211 const RawRangeBoundary& aEnd) { 212 nsINode* newStartRoot = RangeUtils::ComputeRootNode(aStart.GetContainer()); 213 if (!newStartRoot) { 214 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; 215 } 216 if (!aStart.IsSetAndValid()) { 217 return NS_ERROR_DOM_INDEX_SIZE_ERR; 218 } 219 220 if (aStart.GetContainer() == aEnd.GetContainer()) { 221 if (!aEnd.IsSetAndValid()) { 222 return NS_ERROR_DOM_INDEX_SIZE_ERR; 223 } 224 MOZ_ASSERT(*aStart.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets) <= 225 *aEnd.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets)); 226 mRoot = newStartRoot; 227 mStart.CopyFrom(aStart, RangeBoundaryIsMutationObserved::Yes); 228 mEnd.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); 229 return NS_OK; 230 } 231 232 nsINode* newEndRoot = RangeUtils::ComputeRootNode(aEnd.GetContainer()); 233 if (!newEndRoot) { 234 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; 235 } 236 if (!aEnd.IsSetAndValid()) { 237 return NS_ERROR_DOM_INDEX_SIZE_ERR; 238 } 239 240 // If they have different root, this should be collapsed at the end point. 241 if (newStartRoot != newEndRoot) { 242 mRoot = newEndRoot; 243 mStart.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); 244 mEnd.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); 245 return NS_OK; 246 } 247 248 // Otherwise, set the range as specified. 249 mRoot = newStartRoot; 250 mStart.CopyFrom(aStart, RangeBoundaryIsMutationObserved::Yes); 251 mEnd.CopyFrom(aEnd, RangeBoundaryIsMutationObserved::Yes); 252 AssertStartIsBeforeOrEqualToEnd(); 253 return NS_OK; 254 } 255 256 template <typename NodeType, typename RangeBoundaryType> 257 nsresult ContentEventHandler::SimpleRangeBase<NodeType, RangeBoundaryType>:: 258 SelectNodeContents(const nsINode* aNodeToSelectContents) { 259 nsINode* const newRoot = 260 RangeUtils::ComputeRootNode(const_cast<nsINode*>(aNodeToSelectContents)); 261 if (!newRoot) { 262 return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; 263 } 264 mRoot = newRoot; 265 mStart = 266 RangeBoundaryType(const_cast<nsINode*>(aNodeToSelectContents), nullptr); 267 mEnd = RangeBoundaryType(const_cast<nsINode*>(aNodeToSelectContents), 268 aNodeToSelectContents->GetLastChild()); 269 return NS_OK; 270 } 271 272 /******************************************************************/ 273 /* ContentEventHandler */ 274 /******************************************************************/ 275 276 // NOTE 277 // 278 // ContentEventHandler *creates* ranges as following rules: 279 // 1. Start of range: 280 // 1.1. Cases: [textNode or text[Node or textNode[ 281 // When text node is start of a range, start node is the text node and 282 // start offset is any number between 0 and the length of the text. 283 // 1.2. Case: [<element>: 284 // When start of an element node is start of a range, start node is 285 // parent of the element and start offset is the element's index in the 286 // parent. 287 // 1.3. Case: <element/>[ 288 // When after an empty element node is start of a range, start node is 289 // parent of the element and start offset is the element's index in the 290 // parent + 1. 291 // 1.4. Case: <element>[ 292 // When start of a non-empty element is start of a range, start node is 293 // the element and start offset is 0. 294 // 1.5. Case: <root>[ 295 // When start of a range is 0 and there are no nodes causing text, 296 // start node is the root node and start offset is 0. 297 // 1.6. Case: [</root> 298 // When start of a range is out of bounds, start node is the root node 299 // and start offset is number of the children. 300 // 2. End of range: 301 // 2.1. Cases: ]textNode or text]Node or textNode] 302 // When a text node is end of a range, end node is the text node and 303 // end offset is any number between 0 and the length of the text. 304 // 2.2. Case: ]<element> 305 // When before an element node (meaning before the open tag of the 306 // element) is end of a range, end node is previous node causing text. 307 // Note that this case shouldn't be handled directly. If rule 2.1 and 308 // 2.3 are handled correctly, the loop with ContentIterator shouldn't 309 // reach the element node since the loop should've finished already at 310 // handling the last node which caused some text. 311 // 2.3. Case: <element>] 312 // When a line break is caused before a non-empty element node and it's 313 // end of a range, end node is the element and end offset is 0. 314 // (i.e., including open tag of the element) 315 // 2.4. Cases: <element/>] 316 // When after an empty element node is end of a range, end node is 317 // parent of the element node and end offset is the element's index in 318 // the parent + 1. (i.e., including close tag of the element or empty 319 // element) 320 // 2.5. Case: ]</root> 321 // When end of a range is out of bounds, end node is the root node and 322 // end offset is number of the children. 323 // 324 // ContentEventHandler *treats* ranges as following additional rules: 325 // 1. When the start node is an element node which doesn't have children, 326 // it includes a line break caused before itself (i.e., includes its open 327 // tag). For example, if start position is { <br>, 0 }, the line break 328 // caused by <br> should be included into the flatten text. 329 // 2. When the end node is an element node which doesn't have children, 330 // it includes the end (i.e., includes its close tag except empty element). 331 // Although, currently, any close tags don't cause line break, this also 332 // includes its open tag. For example, if end position is { <br>, 0 }, the 333 // line break caused by the <br> should be included into the flatten text. 334 335 ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext) 336 : mDocument(aPresContext->Document()) {} 337 338 nsresult ContentEventHandler::InitBasic(bool aRequireFlush) { 339 NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE); 340 if (aRequireFlush) { 341 // If text frame which has overflowing selection underline is dirty, 342 // we need to flush the pending reflow here. 343 mDocument->FlushPendingNotifications(FlushType::Layout); 344 } 345 return NS_OK; 346 } 347 348 nsresult ContentEventHandler::InitRootContent( 349 const Selection& aNormalSelection) { 350 // Root content should be computed with normal selection because normal 351 // selection is typically has at least one range but the other selections 352 // not so. If there is a range, computing its root is easy, but if 353 // there are no ranges, we need to use ancestor limit instead. 354 MOZ_ASSERT(aNormalSelection.Type() == SelectionType::eNormal); 355 356 if (!aNormalSelection.RangeCount()) { 357 // If there is no selection range, we should compute the selection root 358 // from ancestor limiter or root content of the document. 359 mRootElement = aNormalSelection.GetAncestorLimiter(); 360 if (!mRootElement) { 361 mRootElement = mDocument->GetRootElement(); 362 if (NS_WARN_IF(!mRootElement)) { 363 return NS_ERROR_NOT_AVAILABLE; 364 } 365 } 366 return NS_OK; 367 } 368 369 RefPtr<const nsRange> range(aNormalSelection.GetRangeAt(0)); 370 if (NS_WARN_IF(!range)) { 371 return NS_ERROR_UNEXPECTED; 372 } 373 374 // If there is a selection, we should retrieve the selection root from 375 // the range since when the window is inactivated, the ancestor limiter 376 // of selection was cleared by blur event handler of EditorBase but the 377 // selection range still keeps storing the nodes. If the active element of 378 // the deactive window is <input> or <textarea>, we can compute the 379 // selection root from them. 380 nsCOMPtr<nsINode> startNode = range->GetStartContainer(); 381 nsINode* endNode = range->GetEndContainer(); 382 if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) { 383 return NS_ERROR_FAILURE; 384 } 385 386 // See bug 537041 comment 5, the range could have removed node. 387 if (NS_WARN_IF(startNode->GetComposedDoc() != mDocument)) { 388 return NS_ERROR_FAILURE; 389 } 390 391 NS_ASSERTION(startNode->GetComposedDoc() == endNode->GetComposedDoc(), 392 "firstNormalSelectionRange crosses the document boundary"); 393 394 RefPtr<PresShell> presShell = mDocument->GetPresShell(); 395 mRootElement = Element::FromNodeOrNull(startNode->GetSelectionRootContent( 396 presShell, nsINode::IgnoreOwnIndependentSelection::Yes, 397 nsINode::AllowCrossShadowBoundary::No)); 398 if (NS_WARN_IF(!mRootElement)) { 399 return NS_ERROR_FAILURE; 400 } 401 402 return NS_OK; 403 } 404 405 nsresult ContentEventHandler::InitCommon(EventMessage aEventMessage, 406 SelectionType aSelectionType, 407 bool aRequireFlush) { 408 if (mSelection && mSelection->Type() == aSelectionType) { 409 return NS_OK; 410 } 411 412 mSelection = nullptr; 413 mRootElement = nullptr; 414 mFirstSelectedSimpleRange.Clear(); 415 416 nsresult rv = InitBasic(aRequireFlush); 417 NS_ENSURE_SUCCESS(rv, rv); 418 419 RefPtr<nsFrameSelection> frameSel; 420 if (PresShell* presShell = mDocument->GetPresShell()) { 421 frameSel = presShell->GetLastFocusedFrameSelection(); 422 } 423 if (NS_WARN_IF(!frameSel)) { 424 return NS_ERROR_NOT_AVAILABLE; 425 } 426 427 mSelection = frameSel->GetSelection(aSelectionType); 428 if (NS_WARN_IF(!mSelection)) { 429 return NS_ERROR_NOT_AVAILABLE; 430 } 431 432 RefPtr<Selection> normalSelection; 433 if (mSelection->Type() == SelectionType::eNormal) { 434 normalSelection = mSelection; 435 } else { 436 normalSelection = &frameSel->NormalSelection(); 437 MOZ_ASSERT(normalSelection); 438 } 439 440 rv = InitRootContent(*normalSelection); 441 if (NS_WARN_IF(NS_FAILED(rv))) { 442 return rv; 443 } 444 445 if (mSelection->RangeCount()) { 446 mFirstSelectedSimpleRange.SetStartAndEnd(mSelection->GetRangeAt(0)); 447 return NS_OK; 448 } 449 450 // Even if there are no selection ranges, it's usual case if aSelectionType 451 // is a special selection or we're handling eQuerySelectedText. 452 if (aSelectionType != SelectionType::eNormal || 453 aEventMessage == eQuerySelectedText) { 454 MOZ_ASSERT(!mFirstSelectedSimpleRange.IsPositioned()); 455 return NS_OK; 456 } 457 458 // But otherwise, we need to assume that there is a selection range at the 459 // beginning of the root content if aSelectionType is eNormal. 460 rv = mFirstSelectedSimpleRange.CollapseTo(RawRangeBoundary(mRootElement, 0u)); 461 if (NS_WARN_IF(NS_FAILED(rv))) { 462 return NS_ERROR_UNEXPECTED; 463 } 464 return NS_OK; 465 } 466 467 nsresult ContentEventHandler::Init(WidgetQueryContentEvent* aEvent) { 468 NS_ASSERTION(aEvent, "aEvent must not be null"); 469 MOZ_ASSERT(aEvent->mMessage == eQuerySelectedText || 470 aEvent->mInput.mSelectionType == SelectionType::eNormal); 471 472 if (NS_WARN_IF(!aEvent->mInput.IsValidOffset()) || 473 NS_WARN_IF(!aEvent->mInput.IsValidEventMessage(aEvent->mMessage))) { 474 return NS_ERROR_FAILURE; 475 } 476 477 // Note that we should ignore WidgetQueryContentEvent::Input::mSelectionType 478 // if the event isn't eQuerySelectedText. 479 SelectionType selectionType = aEvent->mMessage == eQuerySelectedText 480 ? aEvent->mInput.mSelectionType 481 : SelectionType::eNormal; 482 if (NS_WARN_IF(selectionType == SelectionType::eNone)) { 483 return NS_ERROR_FAILURE; 484 } 485 486 nsresult rv = InitCommon(aEvent->mMessage, selectionType, 487 aEvent->AllowFlushingPendingNotifications()); 488 NS_ENSURE_SUCCESS(rv, rv); 489 490 // Be aware, WidgetQueryContentEvent::mInput::mOffset should be made absolute 491 // offset before sending it to ContentEventHandler because querying selection 492 // every time may be expensive. So, if the caller caches selection, it 493 // should initialize the event with the cached value. 494 if (aEvent->mInput.mRelativeToInsertionPoint) { 495 MOZ_ASSERT(selectionType == SelectionType::eNormal); 496 TextComposition* composition = 497 IMEStateManager::GetTextCompositionFor(aEvent->mWidget); 498 if (composition) { 499 uint32_t compositionStart = composition->NativeOffsetOfStartComposition(); 500 if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(compositionStart))) { 501 return NS_ERROR_FAILURE; 502 } 503 } else { 504 LineBreakType lineBreakType = GetLineBreakType(aEvent); 505 uint32_t selectionStart = 0; 506 rv = GetStartOffset(mFirstSelectedSimpleRange, &selectionStart, 507 lineBreakType); 508 if (NS_WARN_IF(NS_FAILED(rv))) { 509 return NS_ERROR_FAILURE; 510 } 511 if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(selectionStart))) { 512 return NS_ERROR_FAILURE; 513 } 514 } 515 } 516 517 // Ideally, we should emplace only when we return succeeded event. 518 // However, we need to emplace here since it's hard to store the various 519 // result. Intead, `HandleQueryContentEvent()` will reset `mReply` if 520 // corresponding handler returns error. 521 aEvent->EmplaceReply(); 522 523 aEvent->mReply->mContentsRoot = mRootElement.get(); 524 aEvent->mReply->mIsEditableContent = 525 mRootElement && mRootElement->IsEditable(); 526 527 nsRect r; 528 nsIFrame* frame = nsCaret::GetGeometry(mSelection, &r); 529 if (!frame) { 530 frame = mRootElement->GetPrimaryFrame(); 531 if (NS_WARN_IF(!frame)) { 532 return NS_ERROR_FAILURE; 533 } 534 } 535 aEvent->mReply->mFocusedWidget = frame->GetNearestWidget(); 536 537 return NS_OK; 538 } 539 540 nsresult ContentEventHandler::Init(WidgetSelectionEvent* aEvent) { 541 NS_ASSERTION(aEvent, "aEvent must not be null"); 542 543 nsresult rv = InitCommon(aEvent->mMessage); 544 NS_ENSURE_SUCCESS(rv, rv); 545 546 aEvent->mSucceeded = false; 547 548 return NS_OK; 549 } 550 551 nsIContent* ContentEventHandler::GetFocusedContent() { 552 nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow(); 553 nsCOMPtr<nsPIDOMWindowOuter> focusedWindow; 554 return nsFocusManager::GetFocusedDescendant( 555 window, nsFocusManager::eIncludeAllDescendants, 556 getter_AddRefs(focusedWindow)); 557 } 558 559 nsresult ContentEventHandler::QueryContentRect( 560 nsIContent* aContent, WidgetQueryContentEvent* aEvent) { 561 MOZ_ASSERT(aContent, "aContent must not be null"); 562 563 nsIFrame* frame = aContent->GetPrimaryFrame(); 564 NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE); 565 566 // get rect for first frame 567 nsRect resultRect(nsPoint(0, 0), frame->GetRect().Size()); 568 nsresult rv = ConvertToRootRelativeOffset(frame, resultRect); 569 NS_ENSURE_SUCCESS(rv, rv); 570 571 nsPresContext* presContext = frame->PresContext(); 572 573 // account for any additional frames 574 while ((frame = frame->GetNextContinuation())) { 575 nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size()); 576 rv = ConvertToRootRelativeOffset(frame, frameRect); 577 NS_ENSURE_SUCCESS(rv, rv); 578 resultRect.UnionRect(resultRect, frameRect); 579 } 580 581 aEvent->mReply->mRect = LayoutDeviceIntRect::FromAppUnitsToOutside( 582 resultRect, presContext->AppUnitsPerDevPixel()); 583 // Returning empty rect may cause native IME confused, let's make sure to 584 // return non-empty rect. 585 EnsureNonEmptyRect(aEvent->mReply->mRect); 586 587 return NS_OK; 588 } 589 590 // Editor places a padding <br> element under its root content if the editor 591 // doesn't have any text. This happens even for single line editors. 592 // When we get text content and when we change the selection, 593 // we don't want to include the padding <br> elements at the end. 594 static bool IsContentBR(const nsIContent& aContent) { 595 const HTMLBRElement* brElement = HTMLBRElement::FromNode(aContent); 596 return brElement && !brElement->IsPaddingForEmptyLastLine() && 597 !brElement->IsPaddingForEmptyEditor(); 598 } 599 600 static bool IsPaddingBR(const nsIContent& aContent) { 601 return aContent.IsHTMLElement(nsGkAtoms::br) && !IsContentBR(aContent); 602 } 603 604 static void ConvertToNativeNewlines(nsString& aString) { 605 #if defined(TRANSLATE_NEW_LINES) 606 aString.ReplaceSubstring(u"\n"_ns, u"\r\n"_ns); 607 #endif 608 } 609 610 static void AppendString(nsString& aString, const Text& aTextNode) { 611 const uint32_t oldXPLength = aString.Length(); 612 aTextNode.DataBuffer().AppendTo(aString); 613 if (aTextNode.HasFlag(NS_MAYBE_MASKED)) { 614 TextEditor::MaskString(aString, aTextNode, oldXPLength, 0); 615 } 616 } 617 618 static void AppendSubString(nsString& aString, const Text& aTextNode, 619 uint32_t aXPOffset, uint32_t aXPLength) { 620 const uint32_t oldXPLength = aString.Length(); 621 aTextNode.DataBuffer().AppendTo(aString, aXPOffset, aXPLength); 622 if (aTextNode.HasFlag(NS_MAYBE_MASKED)) { 623 TextEditor::MaskString(aString, aTextNode, oldXPLength, aXPOffset); 624 } 625 } 626 627 #if defined(TRANSLATE_NEW_LINES) 628 template <typename StringType> 629 static uint32_t CountNewlinesInXPLength(const StringType& aString) { 630 uint32_t count = 0; 631 const auto* end = aString.EndReading(); 632 for (const auto* iter = aString.BeginReading(); iter < end; ++iter) { 633 if (*iter == '\n') { 634 count++; 635 } 636 } 637 return count; 638 } 639 640 static uint32_t CountNewlinesInXPLength(const Text& aTextNode, 641 uint32_t aXPLength) { 642 const CharacterDataBuffer& characterDataBuffer = aTextNode.DataBuffer(); 643 // For automated tests, we should abort on debug build. 644 MOZ_ASSERT( 645 aXPLength == UINT32_MAX || aXPLength <= characterDataBuffer.GetLength(), 646 "aXPLength is out-of-bounds"); 647 const uint32_t length = std::min(aXPLength, characterDataBuffer.GetLength()); 648 if (!length) { 649 return 0; 650 } 651 if (characterDataBuffer.Is2b()) { 652 nsDependentSubstring str(characterDataBuffer.Get2b(), length); 653 return CountNewlinesInXPLength(str); 654 } 655 nsDependentCSubstring str(characterDataBuffer.Get1b(), length); 656 return CountNewlinesInXPLength(str); 657 } 658 659 template <typename StringType> 660 static uint32_t CountNewlinesInNativeLength(const StringType& aString, 661 uint32_t aNativeLength) { 662 MOZ_ASSERT( 663 (aNativeLength == UINT32_MAX || aNativeLength <= aString.Length() * 2), 664 "aNativeLength is unexpected value"); 665 uint32_t count = 0; 666 uint32_t nativeOffset = 0; 667 const auto* end = aString.EndReading(); 668 for (const auto* iter = aString.BeginReading(); 669 iter < end && nativeOffset < aNativeLength; ++iter, ++nativeOffset) { 670 if (*iter == '\n') { 671 count++; 672 nativeOffset++; 673 } 674 } 675 return count; 676 } 677 678 static uint32_t CountNewlinesInNativeLength(const Text& aTextNode, 679 uint32_t aNativeLength) { 680 const CharacterDataBuffer& characterDataBuffer = aTextNode.DataBuffer(); 681 const uint32_t xpLength = characterDataBuffer.GetLength(); 682 if (!xpLength) { 683 return 0; 684 } 685 if (characterDataBuffer.Is2b()) { 686 nsDependentSubstring str(characterDataBuffer.Get2b(), xpLength); 687 return CountNewlinesInNativeLength(str, aNativeLength); 688 } 689 nsDependentCSubstring str(characterDataBuffer.Get1b(), xpLength); 690 return CountNewlinesInNativeLength(str, aNativeLength); 691 } 692 #endif 693 694 /* static */ 695 uint32_t ContentEventHandler::GetNativeTextLength(const Text& aTextNode, 696 uint32_t aStartOffset, 697 uint32_t aEndOffset) { 698 MOZ_ASSERT(aEndOffset >= aStartOffset, 699 "aEndOffset must be equals or larger than aStartOffset"); 700 if (aStartOffset == aEndOffset) { 701 return 0; 702 } 703 return GetTextLength(aTextNode, LINE_BREAK_TYPE_NATIVE, aEndOffset) - 704 GetTextLength(aTextNode, LINE_BREAK_TYPE_NATIVE, aStartOffset); 705 } 706 707 /* static */ 708 uint32_t ContentEventHandler::GetNativeTextLength(const Text& aTextNode, 709 uint32_t aMaxLength) { 710 return GetTextLength(aTextNode, LINE_BREAK_TYPE_NATIVE, aMaxLength); 711 } 712 713 /* static inline */ 714 uint32_t ContentEventHandler::GetBRLength(LineBreakType aLineBreakType) { 715 #if defined(TRANSLATE_NEW_LINES) 716 // Length of \r\n 717 return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1; 718 #else 719 return 1; 720 #endif 721 } 722 723 /* static */ 724 uint32_t ContentEventHandler::GetTextLength(const Text& aTextNode, 725 LineBreakType aLineBreakType, 726 uint32_t aMaxLength) { 727 const uint32_t textLengthDifference = 728 #if defined(TRANSLATE_NEW_LINES) 729 // On Windows, the length of a native newline ("\r\n") is twice the length 730 // of the XP newline ("\n"), so XP length is equal to the length of the 731 // native offset plus the number of newlines encountered in the string. 732 (aLineBreakType == LINE_BREAK_TYPE_NATIVE) 733 ? CountNewlinesInXPLength(aTextNode, aMaxLength) 734 : 0; 735 #else 736 // On other platforms, the native and XP newlines are the same. 737 0; 738 #endif 739 740 const uint32_t length = 741 std::min(aTextNode.DataBuffer().GetLength(), aMaxLength); 742 return length + textLengthDifference; 743 } 744 745 static uint32_t ConvertToXPOffset(const Text& aTextNode, 746 uint32_t aNativeOffset) { 747 #if defined(TRANSLATE_NEW_LINES) 748 // On Windows, the length of a native newline ("\r\n") is twice the length of 749 // the XP newline ("\n"), so XP offset is equal to the length of the native 750 // offset minus the number of newlines encountered in the string. 751 return aNativeOffset - CountNewlinesInNativeLength(aTextNode, aNativeOffset); 752 #else 753 // On other platforms, the native and XP newlines are the same. 754 return aNativeOffset; 755 #endif 756 } 757 758 /* static */ 759 uint32_t ContentEventHandler::GetNativeTextLength(const nsAString& aText) { 760 const uint32_t textLengthDifference = 761 #if defined(TRANSLATE_NEW_LINES) 762 // On Windows, the length of a native newline ("\r\n") is twice the length 763 // of the XP newline ("\n"), so XP length is equal to the length of the 764 // native offset plus the number of newlines encountered in the string. 765 CountNewlinesInXPLength(aText); 766 #else 767 // On other platforms, the native and XP newlines are the same. 768 0; 769 #endif 770 return aText.Length() + textLengthDifference; 771 } 772 773 /* static */ 774 bool ContentEventHandler::ShouldBreakLineBefore(const nsIContent& aContent, 775 const Element* aRootElement) { 776 // We don't need to append linebreak at the start of the root element. 777 if (&aContent == aRootElement) { 778 return false; 779 } 780 781 // If it's not an HTML element (including other markup language's elements), 782 // we shouldn't insert like break before that for now. Becoming this is a 783 // problem must be edge case. E.g., when ContentEventHandler is used with 784 // MathML or SVG elements. 785 if (!aContent.IsHTMLElement()) { 786 return false; 787 } 788 789 switch ( 790 nsHTMLTags::CaseSensitiveAtomTagToId(aContent.NodeInfo()->NameAtom())) { 791 case eHTMLTag_br: 792 // If the element is <br>, we need to check if the <br> is caused by web 793 // content. Otherwise, i.e., it's caused by internal reason of Gecko, 794 // it shouldn't be exposed as a line break to flatten text. 795 return IsContentBR(aContent); 796 case eHTMLTag_a: 797 case eHTMLTag_abbr: 798 case eHTMLTag_acronym: 799 case eHTMLTag_b: 800 case eHTMLTag_bdi: 801 case eHTMLTag_bdo: 802 case eHTMLTag_big: 803 case eHTMLTag_cite: 804 case eHTMLTag_code: 805 case eHTMLTag_data: 806 case eHTMLTag_del: 807 case eHTMLTag_dfn: 808 case eHTMLTag_em: 809 case eHTMLTag_font: 810 case eHTMLTag_i: 811 case eHTMLTag_ins: 812 case eHTMLTag_kbd: 813 case eHTMLTag_mark: 814 case eHTMLTag_s: 815 case eHTMLTag_samp: 816 case eHTMLTag_small: 817 case eHTMLTag_span: 818 case eHTMLTag_strike: 819 case eHTMLTag_strong: 820 case eHTMLTag_sub: 821 case eHTMLTag_sup: 822 case eHTMLTag_time: 823 case eHTMLTag_tt: 824 case eHTMLTag_u: 825 case eHTMLTag_var: 826 // Note that ideally, we should refer the style of the primary frame of 827 // aContent for deciding if it's an inline. However, it's difficult 828 // IMEContentObserver to notify IME of text change caused by style change. 829 // Therefore, currently, we should check only from the tag for now. 830 return false; 831 case eHTMLTag_userdefined: 832 case eHTMLTag_unknown: 833 // If the element is unknown element, we shouldn't insert line breaks 834 // before it since unknown elements should be ignored. 835 return false; 836 default: 837 return true; 838 } 839 } 840 841 nsresult ContentEventHandler::GenerateFlatTextContent( 842 const Element* aElement, nsString& aString, LineBreakType aLineBreakType) { 843 MOZ_ASSERT(aString.IsEmpty()); 844 845 UnsafeSimpleRange rawRange; 846 nsresult rv = rawRange.SelectNodeContents(aElement); 847 if (NS_WARN_IF(NS_FAILED(rv))) { 848 return rv; 849 } 850 return GenerateFlatTextContent(rawRange, aString, aLineBreakType); 851 } 852 853 nsresult ContentEventHandler::GenerateFlatTextContent(const nsRange* aRange, 854 nsString& aString) { 855 MOZ_ASSERT(aString.IsEmpty()); 856 857 if (NS_WARN_IF(!aRange)) { 858 return NS_ERROR_FAILURE; 859 } 860 861 UnsafeSimpleRange rawRange; 862 rawRange.SetStartAndEnd(aRange); 863 864 return GenerateFlatTextContent(rawRange, aString, LINE_BREAK_TYPE_NATIVE); 865 } 866 867 template <typename NodeType, typename RangeBoundaryType> 868 nsresult ContentEventHandler::GenerateFlatTextContent( 869 const SimpleRangeBase<NodeType, RangeBoundaryType>& aSimpleRange, 870 nsString& aString, LineBreakType aLineBreakType) { 871 MOZ_ASSERT(aString.IsEmpty()); 872 873 if (aSimpleRange.Collapsed()) { 874 return NS_OK; 875 } 876 877 nsINode* startNode = aSimpleRange.GetStartContainer(); 878 nsINode* endNode = aSimpleRange.GetEndContainer(); 879 if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) { 880 return NS_ERROR_FAILURE; 881 } 882 883 if (startNode == endNode && startNode->IsText()) { 884 AppendSubString(aString, *startNode->AsText(), aSimpleRange.StartOffset(), 885 aSimpleRange.EndOffset() - aSimpleRange.StartOffset()); 886 ConvertToNativeNewlines(aString); 887 return NS_OK; 888 } 889 890 UnsafePreContentIterator preOrderIter; 891 nsresult rv = preOrderIter.Init(aSimpleRange.Start().AsRaw(), 892 aSimpleRange.End().AsRaw()); 893 if (NS_WARN_IF(NS_FAILED(rv))) { 894 return rv; 895 } 896 for (; !preOrderIter.IsDone(); preOrderIter.Next()) { 897 nsINode* node = preOrderIter.GetCurrentNode(); 898 if (NS_WARN_IF(!node)) { 899 break; 900 } 901 if (!node->IsContent()) { 902 continue; 903 } 904 905 if (const Text* textNode = Text::FromNode(node)) { 906 if (textNode == startNode) { 907 AppendSubString(aString, *textNode, aSimpleRange.StartOffset(), 908 textNode->TextLength() - aSimpleRange.StartOffset()); 909 } else if (textNode == endNode) { 910 AppendSubString(aString, *textNode, 0, aSimpleRange.EndOffset()); 911 } else { 912 AppendString(aString, *textNode); 913 } 914 } else if (ShouldBreakLineBefore(*node->AsContent(), mRootElement)) { 915 aString.Append(char16_t('\n')); 916 } 917 } 918 if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) { 919 ConvertToNativeNewlines(aString); 920 } 921 return NS_OK; 922 } 923 924 static FontRange* AppendFontRange(nsTArray<FontRange>& aFontRanges, 925 uint32_t aBaseOffset) { 926 FontRange* fontRange = aFontRanges.AppendElement(); 927 fontRange->mStartOffset = aBaseOffset; 928 return fontRange; 929 } 930 931 /* static */ 932 uint32_t ContentEventHandler::GetTextLengthInRange( 933 const Text& aTextNode, uint32_t aXPStartOffset, uint32_t aXPEndOffset, 934 LineBreakType aLineBreakType) { 935 return aLineBreakType == LINE_BREAK_TYPE_NATIVE 936 ? GetNativeTextLength(aTextNode, aXPStartOffset, aXPEndOffset) 937 : aXPEndOffset - aXPStartOffset; 938 } 939 940 /* static */ 941 void ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges, 942 const Text& aTextNode, 943 uint32_t aBaseOffset, 944 uint32_t aXPStartOffset, 945 uint32_t aXPEndOffset, 946 LineBreakType aLineBreakType) { 947 nsIFrame* frame = aTextNode.GetPrimaryFrame(); 948 if (!frame) { 949 // It is a non-rendered content, create an empty range for it. 950 AppendFontRange(aFontRanges, aBaseOffset); 951 return; 952 } 953 954 uint32_t baseOffset = aBaseOffset; 955 #ifdef DEBUG 956 { 957 nsTextFrame* text = do_QueryFrame(frame); 958 MOZ_ASSERT(text, "Not a text frame"); 959 } 960 #endif 961 auto* curr = static_cast<nsTextFrame*>(frame); 962 while (curr) { 963 uint32_t frameXPStart = std::max( 964 static_cast<uint32_t>(curr->GetContentOffset()), aXPStartOffset); 965 uint32_t frameXPEnd = 966 std::min(static_cast<uint32_t>(curr->GetContentEnd()), aXPEndOffset); 967 if (frameXPStart >= frameXPEnd) { 968 curr = curr->GetNextContinuation(); 969 continue; 970 } 971 972 gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated); 973 gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated); 974 975 nsTextFrame* next = nullptr; 976 if (frameXPEnd < aXPEndOffset) { 977 next = curr->GetNextContinuation(); 978 while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) { 979 frameXPEnd = std::min(static_cast<uint32_t>(next->GetContentEnd()), 980 aXPEndOffset); 981 next = 982 frameXPEnd < aXPEndOffset ? next->GetNextContinuation() : nullptr; 983 } 984 } 985 986 gfxTextRun::Range skipRange(iter.ConvertOriginalToSkipped(frameXPStart), 987 iter.ConvertOriginalToSkipped(frameXPEnd)); 988 uint32_t lastXPEndOffset = frameXPStart; 989 for (gfxTextRun::GlyphRunIterator runIter(textRun, skipRange); 990 !runIter.AtEnd(); runIter.NextRun()) { 991 gfxFont* font = runIter.GlyphRun()->mFont.get(); 992 uint32_t startXPOffset = 993 iter.ConvertSkippedToOriginal(runIter.StringStart()); 994 // It is possible that the first glyph run has exceeded the frame, 995 // because the whole frame is filled by skipped chars. 996 if (startXPOffset >= frameXPEnd) { 997 break; 998 } 999 1000 if (startXPOffset > lastXPEndOffset) { 1001 // Create range for skipped leading chars. 1002 AppendFontRange(aFontRanges, baseOffset); 1003 baseOffset += GetTextLengthInRange(aTextNode, lastXPEndOffset, 1004 startXPOffset, aLineBreakType); 1005 } 1006 1007 FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset); 1008 fontRange->mFontName.Append(NS_ConvertUTF8toUTF16(font->GetName())); 1009 1010 ParentLayerToScreenScale2D cumulativeResolution = 1011 ParentLayerToParentLayerScale( 1012 frame->PresShell()->GetCumulativeResolution()) * 1013 nsLayoutUtils::GetTransformToAncestorScaleCrossProcessForFrameMetrics( 1014 frame); 1015 float scale = 1016 std::max(cumulativeResolution.xScale, cumulativeResolution.yScale); 1017 1018 fontRange->mFontSize = font->GetAdjustedSize() * scale; 1019 1020 // The converted original offset may exceed the range, 1021 // hence we need to clamp it. 1022 uint32_t endXPOffset = iter.ConvertSkippedToOriginal(runIter.StringEnd()); 1023 endXPOffset = std::min(frameXPEnd, endXPOffset); 1024 baseOffset += GetTextLengthInRange(aTextNode, startXPOffset, endXPOffset, 1025 aLineBreakType); 1026 lastXPEndOffset = endXPOffset; 1027 } 1028 if (lastXPEndOffset < frameXPEnd) { 1029 // Create range for skipped trailing chars. It also handles case 1030 // that the whole frame contains only skipped chars. 1031 AppendFontRange(aFontRanges, baseOffset); 1032 baseOffset += GetTextLengthInRange(aTextNode, lastXPEndOffset, frameXPEnd, 1033 aLineBreakType); 1034 } 1035 1036 curr = next; 1037 } 1038 } 1039 1040 nsresult ContentEventHandler::GenerateFlatFontRanges( 1041 const UnsafeSimpleRange& aSimpleRange, FontRangeArray& aFontRanges, 1042 uint32_t& aLength, LineBreakType aLineBreakType) { 1043 MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array"); 1044 1045 if (aSimpleRange.Collapsed()) { 1046 return NS_OK; 1047 } 1048 1049 nsINode* startNode = aSimpleRange.GetStartContainer(); 1050 nsINode* endNode = aSimpleRange.GetEndContainer(); 1051 if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) { 1052 return NS_ERROR_FAILURE; 1053 } 1054 1055 // baseOffset is the flattened offset of each content node. 1056 uint32_t baseOffset = 0; 1057 UnsafePreContentIterator preOrderIter; 1058 nsresult rv = preOrderIter.Init(aSimpleRange.Start().AsRaw(), 1059 aSimpleRange.End().AsRaw()); 1060 if (NS_WARN_IF(NS_FAILED(rv))) { 1061 return rv; 1062 } 1063 for (; !preOrderIter.IsDone(); preOrderIter.Next()) { 1064 nsINode* node = preOrderIter.GetCurrentNode(); 1065 if (NS_WARN_IF(!node)) { 1066 break; 1067 } 1068 if (!node->IsContent()) { 1069 continue; 1070 } 1071 nsIContent* content = node->AsContent(); 1072 1073 if (const Text* textNode = Text::FromNode(content)) { 1074 const uint32_t startOffset = 1075 textNode != startNode ? 0 : aSimpleRange.StartOffset(); 1076 const uint32_t endOffset = textNode != endNode ? textNode->TextLength() 1077 : aSimpleRange.EndOffset(); 1078 AppendFontRanges(aFontRanges, *textNode, baseOffset, startOffset, 1079 endOffset, aLineBreakType); 1080 baseOffset += GetTextLengthInRange(*textNode, startOffset, endOffset, 1081 aLineBreakType); 1082 } else if (ShouldBreakLineBefore(*content, mRootElement)) { 1083 if (aFontRanges.IsEmpty()) { 1084 MOZ_ASSERT(baseOffset == 0); 1085 FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset); 1086 if (nsIFrame* frame = content->GetPrimaryFrame()) { 1087 const nsFont& font = frame->GetParent()->StyleFont()->mFont; 1088 const StyleFontFamilyList& fontList = font.family.families; 1089 MOZ_ASSERT(!fontList.list.IsEmpty(), "Empty font family?"); 1090 const StyleSingleFontFamily* fontName = 1091 fontList.list.IsEmpty() ? nullptr : &fontList.list.AsSpan()[0]; 1092 nsAutoCString name; 1093 if (fontName) { 1094 fontName->AppendToString(name, false); 1095 } 1096 AppendUTF8toUTF16(name, fontRange->mFontName); 1097 1098 ParentLayerToScreenScale2D cumulativeResolution = 1099 ParentLayerToParentLayerScale( 1100 frame->PresShell()->GetCumulativeResolution()) * 1101 nsLayoutUtils:: 1102 GetTransformToAncestorScaleCrossProcessForFrameMetrics(frame); 1103 1104 float scale = std::max(cumulativeResolution.xScale, 1105 cumulativeResolution.yScale); 1106 1107 fontRange->mFontSize = frame->PresContext()->CSSPixelsToDevPixels( 1108 font.size.ToCSSPixels() * scale); 1109 } 1110 } 1111 baseOffset += GetBRLength(aLineBreakType); 1112 } 1113 } 1114 1115 aLength = baseOffset; 1116 return NS_OK; 1117 } 1118 1119 nsresult ContentEventHandler::ExpandToClusterBoundary( 1120 Text& aTextNode, bool aForward, uint32_t* aXPOffset) const { 1121 // XXX This method assumes that the frame boundaries must be cluster 1122 // boundaries. It's false, but no problem now, maybe. 1123 if (*aXPOffset == 0 || *aXPOffset == aTextNode.TextLength()) { 1124 return NS_OK; 1125 } 1126 1127 NS_ASSERTION(*aXPOffset <= aTextNode.TextLength(), "offset is out of range."); 1128 1129 MOZ_DIAGNOSTIC_ASSERT(mDocument->GetPresShell()); 1130 CaretAssociationHint hint = 1131 aForward ? CaretAssociationHint::Before : CaretAssociationHint::After; 1132 FrameAndOffset frameAndOffset = SelectionMovementUtils::GetFrameForNodeOffset( 1133 &aTextNode, int32_t(*aXPOffset), hint); 1134 if (frameAndOffset) { 1135 auto [startOffset, endOffset] = frameAndOffset->GetOffsets(); 1136 if (*aXPOffset == static_cast<uint32_t>(startOffset) || 1137 *aXPOffset == static_cast<uint32_t>(endOffset)) { 1138 return NS_OK; 1139 } 1140 if (!frameAndOffset->IsTextFrame()) { 1141 return NS_ERROR_FAILURE; 1142 } 1143 nsTextFrame* textFrame = static_cast<nsTextFrame*>(frameAndOffset.mFrame); 1144 int32_t newOffsetInFrame = *aXPOffset - startOffset; 1145 newOffsetInFrame += aForward ? -1 : 1; 1146 // PeekOffsetCharacter() should respect cluster but ignore user-select 1147 // style. If it returns "FOUND", we should use the result. Otherwise, 1148 // we shouldn't use the result because the offset was moved to reversed 1149 // direction. 1150 nsTextFrame::PeekOffsetCharacterOptions options; 1151 options.mRespectClusters = true; 1152 options.mIgnoreUserStyleAll = true; 1153 if (textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame, options) == 1154 nsIFrame::FOUND) { 1155 *aXPOffset = startOffset + newOffsetInFrame; 1156 return NS_OK; 1157 } 1158 } 1159 1160 // If the frame isn't available, we only can check surrogate pair... 1161 if (aTextNode.DataBuffer().IsLowSurrogateFollowingHighSurrogateAt( 1162 *aXPOffset)) { 1163 *aXPOffset += aForward ? 1 : -1; 1164 } 1165 return NS_OK; 1166 } 1167 1168 already_AddRefed<nsRange> ContentEventHandler::GetRangeFromFlatTextOffset( 1169 WidgetContentCommandEvent* aEvent, uint32_t aOffset, uint32_t aLength) { 1170 nsresult rv = InitCommon(aEvent->mMessage); 1171 if (NS_WARN_IF(NS_FAILED(rv))) { 1172 return nullptr; 1173 } 1174 1175 Result<DOMRangeAndAdjustedOffsetInFlattenedText, nsresult> result = 1176 ConvertFlatTextOffsetToDOMRange(aOffset, aLength, LINE_BREAK_TYPE_NATIVE, 1177 false); 1178 if (NS_WARN_IF(result.isErr())) { 1179 return nullptr; 1180 } 1181 1182 DOMRangeAndAdjustedOffsetInFlattenedText domRangeAndAdjustOffset = 1183 result.unwrap(); 1184 1185 return nsRange::Create(domRangeAndAdjustOffset.mRange.Start(), 1186 domRangeAndAdjustOffset.mRange.End(), IgnoreErrors()); 1187 } 1188 1189 template <typename RangeType, typename TextNodeType> 1190 Result<ContentEventHandler::DOMRangeAndAdjustedOffsetInFlattenedTextBase< 1191 RangeType, TextNodeType>, 1192 nsresult> 1193 ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase( 1194 uint32_t aOffset, uint32_t aLength, LineBreakType aLineBreakType, 1195 bool aExpandToClusterBoundaries) { 1196 DOMRangeAndAdjustedOffsetInFlattenedTextBase<RangeType, TextNodeType> result; 1197 result.mAdjustedOffset = aOffset; 1198 1199 // Special case like <br contenteditable> 1200 if (!mRootElement->HasChildren()) { 1201 nsresult rv = result.mRange.CollapseTo(RawRangeBoundary(mRootElement, 0u)); 1202 if (NS_WARN_IF(NS_FAILED(rv))) { 1203 return Err(rv); 1204 } 1205 } 1206 1207 UnsafePreContentIterator preOrderIter; 1208 nsresult rv = preOrderIter.Init(mRootElement); 1209 if (NS_WARN_IF(NS_FAILED(rv))) { 1210 return Err(rv); 1211 } 1212 1213 uint32_t offset = 0; 1214 uint32_t endOffset = aOffset + aLength; 1215 bool startSet = false; 1216 for (; !preOrderIter.IsDone(); preOrderIter.Next()) { 1217 nsINode* node = preOrderIter.GetCurrentNode(); 1218 if (NS_WARN_IF(!node)) { 1219 break; 1220 } 1221 // FYI: mRootElement shouldn't cause any text. So, we can skip it simply. 1222 if (node == mRootElement || !node->IsContent()) { 1223 continue; 1224 } 1225 nsIContent* const content = node->AsContent(); 1226 Text* const contentAsText = Text::FromNode(content); 1227 1228 if (contentAsText) { 1229 result.mLastTextNode = contentAsText; 1230 } 1231 1232 uint32_t textLength = contentAsText 1233 ? GetTextLength(*contentAsText, aLineBreakType) 1234 : (ShouldBreakLineBefore(*content, mRootElement) 1235 ? GetBRLength(aLineBreakType) 1236 : 0); 1237 if (!textLength) { 1238 continue; 1239 } 1240 1241 // When the start offset is in between accumulated offset and the last 1242 // offset of the node, the node is the start node of the range. 1243 if (!startSet && aOffset <= offset + textLength) { 1244 nsINode* startNode = nullptr; 1245 Maybe<uint32_t> startNodeOffset; 1246 if (contentAsText) { 1247 // Rule #1.1: [textNode or text[Node or textNode[ 1248 uint32_t xpOffset = aOffset - offset; 1249 if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) { 1250 xpOffset = ConvertToXPOffset(*contentAsText, xpOffset); 1251 } 1252 1253 if (aExpandToClusterBoundaries) { 1254 const uint32_t oldXPOffset = xpOffset; 1255 nsresult rv = 1256 ExpandToClusterBoundary(*contentAsText, false, &xpOffset); 1257 if (NS_WARN_IF(NS_FAILED(rv))) { 1258 return Err(rv); 1259 } 1260 // This is correct since a cluster shouldn't include line break. 1261 result.mAdjustedOffset -= (oldXPOffset - xpOffset); 1262 } 1263 startNode = contentAsText; 1264 startNodeOffset = Some(xpOffset); 1265 } else if (aOffset < offset + textLength) { 1266 // Rule #1.2 [<element> 1267 startNode = content->GetParent(); 1268 if (NS_WARN_IF(!startNode)) { 1269 return Err(NS_ERROR_FAILURE); 1270 } 1271 startNodeOffset = startNode->ComputeIndexOf(content); 1272 if (NS_WARN_IF(startNodeOffset.isNothing())) { 1273 // The content is being removed from the parent! 1274 return Err(NS_ERROR_FAILURE); 1275 } 1276 } else if (!content->HasChildren()) { 1277 // Rule #1.3: <element/>[ 1278 startNode = content->GetParent(); 1279 if (NS_WARN_IF(!startNode)) { 1280 return Err(NS_ERROR_FAILURE); 1281 } 1282 startNodeOffset = startNode->ComputeIndexOf(content); 1283 if (NS_WARN_IF(startNodeOffset.isNothing())) { 1284 // The content is being removed from the parent! 1285 return Err(NS_ERROR_FAILURE); 1286 } 1287 MOZ_ASSERT(*startNodeOffset != UINT32_MAX); 1288 ++(*startNodeOffset); 1289 } else { 1290 // Rule #1.4: <element>[ 1291 startNode = content; 1292 startNodeOffset = Some(0); 1293 } 1294 NS_ASSERTION(startNode, "startNode must not be nullptr"); 1295 MOZ_ASSERT(startNodeOffset.isSome(), 1296 "startNodeOffset must not be Nothing"); 1297 rv = result.mRange.SetStart(startNode, *startNodeOffset); 1298 if (NS_WARN_IF(NS_FAILED(rv))) { 1299 return Err(rv); 1300 } 1301 startSet = true; 1302 1303 if (!aLength) { 1304 rv = result.mRange.SetEnd(startNode, *startNodeOffset); 1305 if (NS_WARN_IF(NS_FAILED(rv))) { 1306 return Err(rv); 1307 } 1308 return result; 1309 } 1310 } 1311 1312 // When the end offset is in the content, the node is the end node of the 1313 // range. 1314 if (endOffset <= offset + textLength) { 1315 MOZ_ASSERT(startSet, "The start of the range should've been set already"); 1316 if (contentAsText) { 1317 // Rule #2.1: ]textNode or text]Node or textNode] 1318 uint32_t xpOffset = endOffset - offset; 1319 if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) { 1320 const uint32_t xpOffsetCurrent = 1321 ConvertToXPOffset(*contentAsText, xpOffset); 1322 if (xpOffset && GetBRLength(aLineBreakType) > 1) { 1323 MOZ_ASSERT(GetBRLength(aLineBreakType) == 2); 1324 const uint32_t xpOffsetPre = 1325 ConvertToXPOffset(*contentAsText, xpOffset - 1); 1326 // If previous character's XP offset is same as current character's, 1327 // it means that the end offset is between \r and \n. So, the 1328 // range end should be after the \n. 1329 if (xpOffsetPre == xpOffsetCurrent) { 1330 xpOffset = xpOffsetCurrent + 1; 1331 } else { 1332 xpOffset = xpOffsetCurrent; 1333 } 1334 } 1335 } 1336 if (aExpandToClusterBoundaries) { 1337 nsresult rv = 1338 ExpandToClusterBoundary(*contentAsText, true, &xpOffset); 1339 if (NS_WARN_IF(NS_FAILED(rv))) { 1340 return Err(rv); 1341 } 1342 } 1343 NS_ASSERTION(xpOffset <= INT32_MAX, "The end node offset is too large"); 1344 nsresult rv = result.mRange.SetEnd(contentAsText, xpOffset); 1345 if (NS_WARN_IF(NS_FAILED(rv))) { 1346 return Err(rv); 1347 } 1348 return result; 1349 } 1350 1351 if (endOffset == offset) { 1352 // Rule #2.2: ]<element> 1353 // NOTE: Please don't crash on release builds because it must be 1354 // overreaction but we shouldn't allow this bug when some 1355 // automated tests find this. 1356 MOZ_ASSERT(false, 1357 "This case should've already been handled at " 1358 "the last node which caused some text"); 1359 return Err(NS_ERROR_FAILURE); 1360 } 1361 1362 if (content->HasChildren() && 1363 ShouldBreakLineBefore(*content, mRootElement)) { 1364 // Rule #2.3: </element>] 1365 rv = result.mRange.SetEnd(content, 0); 1366 if (NS_WARN_IF(NS_FAILED(rv))) { 1367 return Err(rv); 1368 } 1369 return result; 1370 } 1371 1372 // Rule #2.4: <element/>] 1373 nsINode* endNode = content->GetParent(); 1374 if (NS_WARN_IF(!endNode)) { 1375 return Err(NS_ERROR_FAILURE); 1376 } 1377 const Maybe<uint32_t> indexInParent = endNode->ComputeIndexOf(content); 1378 if (NS_WARN_IF(indexInParent.isNothing())) { 1379 // The content is being removed from the parent! 1380 return Err(NS_ERROR_FAILURE); 1381 } 1382 MOZ_ASSERT(*indexInParent != UINT32_MAX); 1383 rv = result.mRange.SetEnd(endNode, *indexInParent + 1); 1384 if (NS_WARN_IF(NS_FAILED(rv))) { 1385 return Err(rv); 1386 } 1387 return result; 1388 } 1389 1390 offset += textLength; 1391 } 1392 1393 if (!startSet) { 1394 if (!offset) { 1395 // Rule #1.5: <root>[</root> 1396 // When there are no nodes causing text, the start of the DOM range 1397 // should be start of the root node since clicking on such editor (e.g., 1398 // <div contenteditable><span></span></div>) sets caret to the start of 1399 // the editor (i.e., before <span> in the example). 1400 rv = result.mRange.SetStart(mRootElement, 0); 1401 if (NS_WARN_IF(NS_FAILED(rv))) { 1402 return Err(rv); 1403 } 1404 if (!aLength) { 1405 rv = result.mRange.SetEnd(mRootElement, 0); 1406 if (NS_WARN_IF(NS_FAILED(rv))) { 1407 return Err(rv); 1408 } 1409 return result; 1410 } 1411 } else { 1412 // Rule #1.5: [</root> 1413 rv = result.mRange.SetStart(mRootElement, mRootElement->GetChildCount()); 1414 if (NS_WARN_IF(NS_FAILED(rv))) { 1415 return result; 1416 } 1417 } 1418 result.mAdjustedOffset = offset; 1419 } 1420 // Rule #2.5: ]</root> 1421 rv = result.mRange.SetEnd(mRootElement, mRootElement->GetChildCount()); 1422 if (NS_WARN_IF(NS_FAILED(rv))) { 1423 return Err(rv); 1424 } 1425 return result; 1426 } 1427 1428 /* static */ 1429 LineBreakType ContentEventHandler::GetLineBreakType( 1430 WidgetQueryContentEvent* aEvent) { 1431 return GetLineBreakType(aEvent->mUseNativeLineBreak); 1432 } 1433 1434 /* static */ 1435 LineBreakType ContentEventHandler::GetLineBreakType( 1436 WidgetSelectionEvent* aEvent) { 1437 return GetLineBreakType(aEvent->mUseNativeLineBreak); 1438 } 1439 1440 /* static */ 1441 LineBreakType ContentEventHandler::GetLineBreakType(bool aUseNativeLineBreak) { 1442 return aUseNativeLineBreak ? LINE_BREAK_TYPE_NATIVE : LINE_BREAK_TYPE_XP; 1443 } 1444 1445 nsresult ContentEventHandler::HandleQueryContentEvent( 1446 WidgetQueryContentEvent* aEvent) { 1447 nsresult rv = NS_ERROR_NOT_IMPLEMENTED; 1448 switch (aEvent->mMessage) { 1449 case eQuerySelectedText: 1450 rv = OnQuerySelectedText(aEvent); 1451 break; 1452 case eQueryTextContent: 1453 rv = OnQueryTextContent(aEvent); 1454 break; 1455 case eQueryCaretRect: 1456 rv = OnQueryCaretRect(aEvent); 1457 break; 1458 case eQueryTextRect: 1459 rv = OnQueryTextRect(aEvent); 1460 break; 1461 case eQueryTextRectArray: 1462 rv = OnQueryTextRectArray(aEvent); 1463 break; 1464 case eQueryEditorRect: 1465 rv = OnQueryEditorRect(aEvent); 1466 break; 1467 case eQueryContentState: 1468 rv = OnQueryContentState(aEvent); 1469 break; 1470 case eQuerySelectionAsTransferable: 1471 rv = OnQuerySelectionAsTransferable(aEvent); 1472 break; 1473 case eQueryCharacterAtPoint: 1474 rv = OnQueryCharacterAtPoint(aEvent); 1475 break; 1476 case eQueryDOMWidgetHittest: 1477 rv = OnQueryDOMWidgetHittest(aEvent); 1478 break; 1479 case eQueryDropTargetHittest: 1480 rv = OnQueryDropTargetHittest(aEvent); 1481 break; 1482 default: 1483 break; 1484 } 1485 if (NS_FAILED(rv)) { 1486 aEvent->mReply.reset(); // Mark the query failed. 1487 return rv; 1488 } 1489 1490 MOZ_ASSERT(aEvent->Succeeded()); 1491 return NS_OK; 1492 } 1493 1494 // Similar to nsFrameSelection::GetFrameForNodeOffset, 1495 // but this is more flexible for OnQueryTextRect to use 1496 static Result<nsIFrame*, nsresult> GetFrameForTextRect(const nsINode* aNode, 1497 int32_t aNodeOffset, 1498 bool aHint) { 1499 const nsIContent* content = nsIContent::FromNodeOrNull(aNode); 1500 if (NS_WARN_IF(!content)) { 1501 return Err(NS_ERROR_UNEXPECTED); 1502 } 1503 nsIFrame* frame = content->GetPrimaryFrame(); 1504 // The node may be invisible, e.g., `display: none`, invisible text node 1505 // around block elements, etc. Therefore, don't warn when we don't find 1506 // a primary frame. 1507 if (!frame) { 1508 return nullptr; 1509 } 1510 int32_t childNodeOffset = 0; 1511 nsIFrame* returnFrame = nullptr; 1512 nsresult rv = frame->GetChildFrameContainingOffset( 1513 aNodeOffset, aHint, &childNodeOffset, &returnFrame); 1514 if (NS_FAILED(rv)) { 1515 return Err(rv); 1516 } 1517 return returnFrame; 1518 } 1519 1520 nsresult ContentEventHandler::OnQuerySelectedText( 1521 WidgetQueryContentEvent* aEvent) { 1522 nsresult rv = Init(aEvent); 1523 if (NS_FAILED(rv)) { 1524 return rv; 1525 } 1526 1527 MOZ_ASSERT(aEvent->mReply->mOffsetAndData.isNothing()); 1528 1529 if (!mFirstSelectedSimpleRange.IsPositioned()) { 1530 MOZ_ASSERT(aEvent->mReply->mOffsetAndData.isNothing()); 1531 MOZ_ASSERT_IF(mSelection, !mSelection->RangeCount()); 1532 // This is special case that `mReply` is emplaced, but mOffsetAndData is 1533 // not emplaced but treated as succeeded because of no selection ranges 1534 // is a usual case. 1535 return NS_OK; 1536 } 1537 1538 const UnsafeSimpleRange firstSelectedSimpleRange(mFirstSelectedSimpleRange); 1539 nsINode* const startNode = firstSelectedSimpleRange.GetStartContainer(); 1540 nsINode* const endNode = firstSelectedSimpleRange.GetEndContainer(); 1541 1542 // Make sure the selection is within the root content range. 1543 if (!startNode->IsInclusiveDescendantOf(mRootElement) || 1544 !endNode->IsInclusiveDescendantOf(mRootElement)) { 1545 return NS_ERROR_NOT_AVAILABLE; 1546 } 1547 1548 LineBreakType lineBreakType = GetLineBreakType(aEvent); 1549 uint32_t startOffset = 0; 1550 if (NS_WARN_IF(NS_FAILED(GetStartOffset(firstSelectedSimpleRange, 1551 &startOffset, lineBreakType)))) { 1552 return NS_ERROR_FAILURE; 1553 } 1554 1555 const RawRangeBoundary anchorRef = mSelection->RangeCount() > 0 1556 ? mSelection->AnchorRef().AsRaw() 1557 : firstSelectedSimpleRange.Start(); 1558 const RawRangeBoundary focusRef = mSelection->RangeCount() > 0 1559 ? mSelection->FocusRef().AsRaw() 1560 : firstSelectedSimpleRange.End(); 1561 if (NS_WARN_IF(!anchorRef.IsSet()) || NS_WARN_IF(!focusRef.IsSet())) { 1562 return NS_ERROR_FAILURE; 1563 } 1564 1565 if (mSelection->RangeCount()) { 1566 // If there is only one selection range, the anchor/focus node and offset 1567 // are the information of the range. Therefore, we have the direction 1568 // information. 1569 if (mSelection->RangeCount() == 1) { 1570 // The selection's points should always be comparable, independent of the 1571 // selection (see nsISelectionController.idl). 1572 Maybe<int32_t> compare = 1573 nsContentUtils::ComparePoints(anchorRef, focusRef); 1574 if (compare.isNothing()) { 1575 return NS_ERROR_FAILURE; 1576 } 1577 1578 aEvent->mReply->mReversed = compare.value() > 0; 1579 } 1580 // However, if there are 2 or more selection ranges, we have no information 1581 // of that. 1582 else { 1583 aEvent->mReply->mReversed = false; 1584 } 1585 1586 nsString selectedString; 1587 if (!firstSelectedSimpleRange.Collapsed() && 1588 NS_WARN_IF(NS_FAILED(GenerateFlatTextContent( 1589 firstSelectedSimpleRange, selectedString, lineBreakType)))) { 1590 return NS_ERROR_FAILURE; 1591 } 1592 aEvent->mReply->mOffsetAndData.emplace(startOffset, selectedString, 1593 OffsetAndDataFor::SelectedString); 1594 } else { 1595 NS_ASSERTION(anchorRef == focusRef, 1596 "When mSelection doesn't have selection, " 1597 "mFirstSelectedRawRange must be collapsed"); 1598 1599 aEvent->mReply->mReversed = false; 1600 aEvent->mReply->mOffsetAndData.emplace(startOffset, EmptyString(), 1601 OffsetAndDataFor::SelectedString); 1602 } 1603 1604 Result<nsIFrame*, nsresult> frameForTextRectOrError = GetFrameForTextRect( 1605 focusRef.GetContainer(), 1606 focusRef.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets).valueOr(0), 1607 true); 1608 if (NS_WARN_IF(frameForTextRectOrError.isErr()) || 1609 !frameForTextRectOrError.inspect()) { 1610 aEvent->mReply->mWritingMode = WritingMode(); 1611 } else { 1612 aEvent->mReply->mWritingMode = 1613 frameForTextRectOrError.inspect()->GetWritingMode(); 1614 } 1615 1616 MOZ_ASSERT(aEvent->Succeeded()); 1617 return NS_OK; 1618 } 1619 1620 nsresult ContentEventHandler::OnQueryTextContent( 1621 WidgetQueryContentEvent* aEvent) { 1622 nsresult rv = Init(aEvent); 1623 if (NS_FAILED(rv)) { 1624 return rv; 1625 } 1626 1627 MOZ_ASSERT(aEvent->mReply->mOffsetAndData.isNothing()); 1628 1629 LineBreakType lineBreakType = GetLineBreakType(aEvent); 1630 1631 Result<UnsafeDOMRangeAndAdjustedOffsetInFlattenedText, nsresult> 1632 domRangeAndAdjustedOffsetOrError = ConvertFlatTextOffsetToUnsafeDOMRange( 1633 aEvent->mInput.mOffset, aEvent->mInput.mLength, lineBreakType, false); 1634 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError.isErr())) { 1635 NS_WARNING( 1636 "ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase() failed"); 1637 return NS_ERROR_FAILURE; 1638 } 1639 const UnsafeDOMRangeAndAdjustedOffsetInFlattenedText 1640 domRangeAndAdjustedOffset = domRangeAndAdjustedOffsetOrError.unwrap(); 1641 1642 nsString textInRange; 1643 if (NS_WARN_IF(NS_FAILED(GenerateFlatTextContent( 1644 domRangeAndAdjustedOffset.mRange, textInRange, lineBreakType)))) { 1645 return NS_ERROR_FAILURE; 1646 } 1647 1648 aEvent->mReply->mOffsetAndData.emplace( 1649 domRangeAndAdjustedOffset.mAdjustedOffset, textInRange, 1650 OffsetAndDataFor::EditorString); 1651 1652 if (aEvent->mWithFontRanges) { 1653 uint32_t fontRangeLength; 1654 if (NS_WARN_IF(NS_FAILED(GenerateFlatFontRanges( 1655 domRangeAndAdjustedOffset.mRange, aEvent->mReply->mFontRanges, 1656 fontRangeLength, lineBreakType)))) { 1657 return NS_ERROR_FAILURE; 1658 } 1659 1660 MOZ_ASSERT(fontRangeLength == aEvent->mReply->DataLength(), 1661 "Font ranges doesn't match the string"); 1662 } 1663 1664 MOZ_ASSERT(aEvent->Succeeded()); 1665 return NS_OK; 1666 } 1667 1668 void ContentEventHandler::EnsureNonEmptyRect(nsRect& aRect) const { 1669 // See the comment in ContentEventHandler.h why this doesn't set them to 1670 // one device pixel. 1671 aRect.height = std::max(1, aRect.height); 1672 aRect.width = std::max(1, aRect.width); 1673 } 1674 1675 void ContentEventHandler::EnsureNonEmptyRect(LayoutDeviceIntRect& aRect) const { 1676 aRect.height = std::max(1, aRect.height); 1677 aRect.width = std::max(1, aRect.width); 1678 } 1679 1680 template <typename NodeType, typename RangeBoundaryType> 1681 ContentEventHandler::FrameAndNodeOffset 1682 ContentEventHandler::GetFirstFrameInRangeForTextRect( 1683 const SimpleRangeBase<NodeType, RangeBoundaryType>& aSimpleRange) { 1684 RawNodePosition nodePosition; 1685 UnsafePreContentIterator preOrderIter; 1686 nsresult rv = preOrderIter.Init(aSimpleRange.Start().AsRaw(), 1687 aSimpleRange.End().AsRaw()); 1688 if (NS_WARN_IF(NS_FAILED(rv))) { 1689 return FrameAndNodeOffset(); 1690 } 1691 for (; !preOrderIter.IsDone(); preOrderIter.Next()) { 1692 nsINode* node = preOrderIter.GetCurrentNode(); 1693 if (NS_WARN_IF(!node)) { 1694 break; 1695 } 1696 1697 auto* content = nsIContent::FromNode(node); 1698 if (MOZ_UNLIKELY(!content)) { 1699 continue; 1700 } 1701 1702 // If the node is invisible (e.g., the node is or is in an invisible node or 1703 // it's a white-space only text node around a block boundary), we should 1704 // ignore it. 1705 if (!content->GetPrimaryFrame()) { 1706 continue; 1707 } 1708 1709 if (auto* textNode = Text::FromNode(content)) { 1710 // If the range starts at the end of a text node, we need to find 1711 // next node which causes text. 1712 const uint32_t offsetInNode = textNode == aSimpleRange.GetStartContainer() 1713 ? aSimpleRange.StartOffset() 1714 : 0u; 1715 if (offsetInNode < textNode->TextDataLength()) { 1716 nodePosition = {textNode, offsetInNode}; 1717 break; 1718 } 1719 continue; 1720 } 1721 1722 // If the element node causes a line break before it, it's the first 1723 // node causing text. 1724 if (ShouldBreakLineBefore(*content, mRootElement) || 1725 IsPaddingBR(*content)) { 1726 nodePosition = {content, 0u}; 1727 } 1728 } 1729 1730 if (!nodePosition.IsSetAndValid()) { 1731 return FrameAndNodeOffset(); 1732 } 1733 1734 Result<nsIFrame*, nsresult> firstFrameOrError = GetFrameForTextRect( 1735 nodePosition.GetContainer(), 1736 *nodePosition.Offset(RawNodePosition::OffsetFilter::kValidOffsets), true); 1737 if (NS_WARN_IF(firstFrameOrError.isErr()) || !firstFrameOrError.inspect()) { 1738 return FrameAndNodeOffset(); 1739 } 1740 return FrameAndNodeOffset( 1741 firstFrameOrError.inspect(), 1742 *nodePosition.Offset(RawNodePosition::OffsetFilter::kValidOffsets)); 1743 } 1744 1745 template <typename NodeType, typename RangeBoundaryType> 1746 ContentEventHandler::FrameAndNodeOffset 1747 ContentEventHandler::GetLastFrameInRangeForTextRect( 1748 const SimpleRangeBase<NodeType, RangeBoundaryType>& aSimpleRange) { 1749 RawNodePosition nodePosition; 1750 UnsafePreContentIterator preOrderIter; 1751 nsresult rv = preOrderIter.Init(aSimpleRange.Start().AsRaw(), 1752 aSimpleRange.End().AsRaw()); 1753 if (NS_WARN_IF(NS_FAILED(rv))) { 1754 return FrameAndNodeOffset(); 1755 } 1756 1757 const RangeBoundaryType& endPoint = aSimpleRange.End(); 1758 MOZ_ASSERT(endPoint.IsSetAndValid()); 1759 // If the end point is start of a text node or specified by its parent and 1760 // index, the node shouldn't be included into the range. For example, 1761 // with this case, |<p>abc[<br>]def</p>|, the range ends at 3rd children of 1762 // <p> (see the range creation rules, "2.4. Cases: <element/>]"). This causes 1763 // following frames: 1764 // +----+-----+ 1765 // | abc|[<br>| 1766 // +----+-----+ 1767 // +----+ 1768 // |]def| 1769 // +----+ 1770 // So, if this method includes the 2nd text frame's rect to its result, the 1771 // caller will return too tall rect which includes 2 lines in this case isn't 1772 // expected by native IME (e.g., popup of IME will be positioned at bottom 1773 // of "d" instead of right-bottom of "c"). Therefore, this method shouldn't 1774 // include the last frame when its content isn't really in aSimpleRange. 1775 nsINode* nextNodeOfRangeEnd = nullptr; 1776 if (endPoint.GetContainer()->IsText()) { 1777 // Don't set nextNodeOfRangeEnd to the start node of aSimpleRange because if 1778 // the container of the end is same as start node of the range, the text 1779 // node shouldn't be next of range end even if the offset is 0. This 1780 // could occur with empty text node. 1781 if (endPoint.IsStartOfContainer() && 1782 aSimpleRange.GetStartContainer() != endPoint.GetContainer()) { 1783 nextNodeOfRangeEnd = endPoint.GetContainer(); 1784 } 1785 } else if (endPoint.IsSetAndValid()) { 1786 nextNodeOfRangeEnd = endPoint.GetChildAtOffset(); 1787 } 1788 1789 for (preOrderIter.Last(); !preOrderIter.IsDone(); preOrderIter.Prev()) { 1790 nsINode* node = preOrderIter.GetCurrentNode(); 1791 if (NS_WARN_IF(!node)) { 1792 break; 1793 } 1794 1795 if (node == nextNodeOfRangeEnd) { 1796 continue; 1797 } 1798 1799 auto* content = nsIContent::FromNode(node); 1800 if (MOZ_UNLIKELY(!content)) { 1801 continue; 1802 } 1803 1804 // If the node is invisible (e.g., the node is or is in an invisible node or 1805 // it's a white-space only text node around a block boundary), we should 1806 // ignore it. 1807 if (!content->GetPrimaryFrame()) { 1808 continue; 1809 } 1810 1811 if (auto* textNode = Text::FromNode(node)) { 1812 nodePosition = {textNode, textNode == aSimpleRange.GetEndContainer() 1813 ? aSimpleRange.EndOffset() 1814 : textNode->TextDataLength()}; 1815 1816 // If the text node is empty or the last node of the range but the index 1817 // is 0, we should store current position but continue looking for 1818 // previous node (If there are no nodes before it, we should use current 1819 // node position for returning its frame). 1820 if (*nodePosition.Offset(RawNodePosition::OffsetFilter::kValidOffsets) == 1821 0) { 1822 continue; 1823 } 1824 break; 1825 } 1826 1827 if (ShouldBreakLineBefore(*content, mRootElement) || 1828 IsPaddingBR(*content)) { 1829 nodePosition = {content, 0u}; 1830 break; 1831 } 1832 } 1833 1834 if (!nodePosition.IsSet()) { 1835 return FrameAndNodeOffset(); 1836 } 1837 1838 Result<nsIFrame*, nsresult> lastFrameOrError = GetFrameForTextRect( 1839 nodePosition.GetContainer(), 1840 *nodePosition.Offset(RawNodePosition::OffsetFilter::kValidOffsets), true); 1841 if (NS_WARN_IF(lastFrameOrError.isErr()) || !lastFrameOrError.inspect()) { 1842 return FrameAndNodeOffset(); 1843 } 1844 1845 // If the last frame is a text frame, we need to check if the range actually 1846 // includes at least one character in the range. Therefore, if it's not a 1847 // text frame, we need to do nothing anymore. 1848 if (!lastFrameOrError.inspect()->IsTextFrame()) { 1849 return FrameAndNodeOffset( 1850 lastFrameOrError.inspect(), 1851 *nodePosition.Offset(RawNodePosition::OffsetFilter::kValidOffsets)); 1852 } 1853 1854 int32_t start = lastFrameOrError.inspect()->GetOffsets().first; 1855 1856 // If the start offset in the node is same as the computed offset in the 1857 // node and it's not 0, the frame shouldn't be added to the text rect. So, 1858 // this should return previous text frame and its last offset if there is 1859 // at least one text frame. 1860 if (*nodePosition.Offset(RawNodePosition::OffsetFilter::kValidOffsets) && 1861 *nodePosition.Offset(RawNodePosition::OffsetFilter::kValidOffsets) == 1862 static_cast<uint32_t>(start)) { 1863 const uint32_t newNodePositionOffset = 1864 *nodePosition.Offset(RawNodePosition::OffsetFilter::kValidOffsets); 1865 MOZ_ASSERT(newNodePositionOffset != 0); 1866 nodePosition = {nodePosition.GetContainer(), newNodePositionOffset - 1u}; 1867 lastFrameOrError = GetFrameForTextRect( 1868 nodePosition.GetContainer(), 1869 *nodePosition.Offset(RawNodePosition::OffsetFilter::kValidOffsets), 1870 true); 1871 if (NS_WARN_IF(lastFrameOrError.isErr()) || !lastFrameOrError.inspect()) { 1872 return FrameAndNodeOffset(); 1873 } 1874 } 1875 1876 return FrameAndNodeOffset( 1877 lastFrameOrError.inspect(), 1878 *nodePosition.Offset(RawNodePosition::OffsetFilter::kValidOffsets)); 1879 } 1880 1881 ContentEventHandler::FrameRelativeRect 1882 ContentEventHandler::GetLineBreakerRectBefore(nsIFrame* aFrame) { 1883 // Note that this method should be called only with an element's frame whose 1884 // open tag causes a line break or moz-<br> for computing empty last line's 1885 // rect. 1886 MOZ_ASSERT(aFrame->GetContent()); 1887 MOZ_ASSERT(ShouldBreakLineBefore(*aFrame->GetContent(), mRootElement) || 1888 IsPaddingBR(*aFrame->GetContent())); 1889 1890 nsIFrame* frameForFontMetrics = aFrame; 1891 1892 // If it's not a <br> frame, this method computes the line breaker's rect 1893 // outside the frame. Therefore, we need to compute with parent frame's 1894 // font metrics in such case. 1895 if (!aFrame->IsBrFrame() && aFrame->GetParent()) { 1896 frameForFontMetrics = aFrame->GetParent(); 1897 } 1898 1899 // Note that <br> element's rect is decided with line-height but we need 1900 // a rect only with font height. Additionally, <br> frame's width and 1901 // height are 0 in quirks mode if it's not an empty line. So, we cannot 1902 // use frame rect information even if it's a <br> frame. 1903 1904 RefPtr<nsFontMetrics> fontMetrics = 1905 nsLayoutUtils::GetInflatedFontMetricsForFrame(frameForFontMetrics); 1906 if (NS_WARN_IF(!fontMetrics)) { 1907 return FrameRelativeRect(); 1908 } 1909 1910 const WritingMode kWritingMode = frameForFontMetrics->GetWritingMode(); 1911 1912 auto caretBlockAxisMetrics = 1913 aFrame->GetCaretBlockAxisMetrics(kWritingMode, *fontMetrics); 1914 nscoord inlineOffset = 0; 1915 1916 // If aFrame isn't a <br> frame, caret should be at outside of it because 1917 // the line break is before its open tag. For example, case of 1918 // |<div><p>some text</p></div>|, caret is before <p> element and in <div> 1919 // element, the caret should be left of top-left corner of <p> element like: 1920 // 1921 // +-<div>------------------- <div>'s border box 1922 // | I +-<p>----------------- <p>'s border box 1923 // | I | 1924 // | I | 1925 // | | 1926 // ^- caret 1927 // 1928 // However, this is a hack for unusual scenario. This hack shouldn't be 1929 // used as far as possible. 1930 if (!aFrame->IsBrFrame()) { 1931 if (kWritingMode.IsVertical() && !kWritingMode.IsLineInverted()) { 1932 // above of top-right corner of aFrame. 1933 caretBlockAxisMetrics.mOffset = 1934 aFrame->GetRect().XMost() - caretBlockAxisMetrics.mExtent; 1935 } else { 1936 // above (For vertical) or left (For horizontal) of top-left corner of 1937 // aFrame. 1938 caretBlockAxisMetrics.mOffset = 0; 1939 } 1940 inlineOffset = -aFrame->PresContext()->AppUnitsPerDevPixel(); 1941 } 1942 FrameRelativeRect result(aFrame); 1943 if (kWritingMode.IsVertical()) { 1944 result.mRect.x = caretBlockAxisMetrics.mOffset; 1945 result.mRect.y = inlineOffset; 1946 result.mRect.width = caretBlockAxisMetrics.mExtent; 1947 } else { 1948 result.mRect.x = inlineOffset; 1949 result.mRect.y = caretBlockAxisMetrics.mOffset; 1950 result.mRect.height = caretBlockAxisMetrics.mExtent; 1951 } 1952 return result; 1953 } 1954 1955 ContentEventHandler::FrameRelativeRect 1956 ContentEventHandler::GuessLineBreakerRectAfter(const Text& aTextNode) { 1957 FrameRelativeRect result; 1958 const int32_t length = static_cast<int32_t>(aTextNode.TextLength()); 1959 if (NS_WARN_IF(length < 0)) { 1960 return result; 1961 } 1962 // Get the last nsTextFrame which is caused by aTextNode. Note that 1963 // a text node can cause multiple text frames, e.g., the text is too long 1964 // and wrapped by its parent block or the text has line breakers and its 1965 // white-space property respects the line breakers (e.g., |pre|). 1966 Result<nsIFrame*, nsresult> lastTextFrameOrError = 1967 GetFrameForTextRect(&aTextNode, length, true); 1968 if (NS_WARN_IF(lastTextFrameOrError.isErr()) || 1969 !lastTextFrameOrError.inspect()) { 1970 return result; 1971 } 1972 const nsRect kLastTextFrameRect = lastTextFrameOrError.inspect()->GetRect(); 1973 if (lastTextFrameOrError.inspect()->GetWritingMode().IsVertical()) { 1974 // Below of the last text frame. 1975 result.mRect.SetRect(0, kLastTextFrameRect.height, kLastTextFrameRect.width, 1976 0); 1977 } else { 1978 // Right of the last text frame (not bidi-aware). 1979 result.mRect.SetRect(kLastTextFrameRect.width, 0, 0, 1980 kLastTextFrameRect.height); 1981 } 1982 result.mBaseFrame = lastTextFrameOrError.unwrap(); 1983 return result; 1984 } 1985 1986 ContentEventHandler::FrameRelativeRect 1987 ContentEventHandler::GuessFirstCaretRectIn(nsIFrame* aFrame) { 1988 const WritingMode kWritingMode = aFrame->GetWritingMode(); 1989 nsPresContext* presContext = aFrame->PresContext(); 1990 1991 // Computes the font height, but if it's not available, we should use 1992 // default font size of Firefox. The default font size in default settings 1993 // is 16px. 1994 RefPtr<nsFontMetrics> fontMetrics = 1995 nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame); 1996 const nscoord kMaxHeight = fontMetrics 1997 ? fontMetrics->MaxHeight() 1998 : 16 * presContext->AppUnitsPerDevPixel(); 1999 2000 nsRect caretRect; 2001 const nsRect kContentRect = aFrame->GetContentRect() - aFrame->GetPosition(); 2002 caretRect.y = kContentRect.y; 2003 if (!kWritingMode.IsVertical()) { 2004 if (kWritingMode.IsBidiLTR()) { 2005 caretRect.x = kContentRect.x; 2006 } else { 2007 // Move 1px left for the space of caret itself. 2008 const nscoord kOnePixel = presContext->AppUnitsPerDevPixel(); 2009 caretRect.x = kContentRect.XMost() - kOnePixel; 2010 } 2011 caretRect.height = kMaxHeight; 2012 // However, don't add kOnePixel here because it may cause 2px width at 2013 // aligning the edge to device pixels. 2014 caretRect.width = 1; 2015 } else { 2016 if (kWritingMode.IsVerticalLR()) { 2017 caretRect.x = kContentRect.x; 2018 } else { 2019 caretRect.x = kContentRect.XMost() - kMaxHeight; 2020 } 2021 caretRect.width = kMaxHeight; 2022 // Don't add app units for a device pixel because it may cause 2px height 2023 // at aligning the edge to device pixels. 2024 caretRect.height = 1; 2025 } 2026 return FrameRelativeRect(caretRect, aFrame); 2027 } 2028 2029 // static 2030 LayoutDeviceIntRect ContentEventHandler::GetCaretRectBefore( 2031 const LayoutDeviceIntRect& aCharRect, const WritingMode& aWritingMode) { 2032 LayoutDeviceIntRect caretRectBefore(aCharRect); 2033 if (aWritingMode.IsVertical()) { 2034 caretRectBefore.height = 1; 2035 } else { 2036 // TODO: Make here bidi-aware. 2037 caretRectBefore.width = 1; 2038 } 2039 return caretRectBefore; 2040 } 2041 2042 // static 2043 nsRect ContentEventHandler::GetCaretRectBefore( 2044 const nsRect& aCharRect, const WritingMode& aWritingMode) { 2045 nsRect caretRectBefore(aCharRect); 2046 if (aWritingMode.IsVertical()) { 2047 // For making the height 1 device pixel after aligning the rect edges to 2048 // device pixels, don't add one device pixel in app units here. 2049 caretRectBefore.height = 1; 2050 } else { 2051 // TODO: Make here bidi-aware. 2052 // For making the width 1 device pixel after aligning the rect edges to 2053 // device pixels, don't add one device pixel in app units here. 2054 caretRectBefore.width = 1; 2055 } 2056 return caretRectBefore; 2057 } 2058 2059 // static 2060 LayoutDeviceIntRect ContentEventHandler::GetCaretRectAfter( 2061 const LayoutDeviceIntRect& aCharRect, const WritingMode& aWritingMode) { 2062 LayoutDeviceIntRect caretRectAfter(aCharRect); 2063 if (aWritingMode.IsVertical()) { 2064 caretRectAfter.y = aCharRect.YMost() + 1; 2065 caretRectAfter.height = 1; 2066 } else { 2067 // TODO: Make here bidi-aware. 2068 caretRectAfter.x = aCharRect.XMost() + 1; 2069 caretRectAfter.width = 1; 2070 } 2071 return caretRectAfter; 2072 } 2073 2074 // static 2075 nsRect ContentEventHandler::GetCaretRectAfter(nsPresContext& aPresContext, 2076 const nsRect& aCharRect, 2077 const WritingMode& aWritingMode) { 2078 nsRect caretRectAfter(aCharRect); 2079 const nscoord onePixel = aPresContext.AppUnitsPerDevPixel(); 2080 if (aWritingMode.IsVertical()) { 2081 caretRectAfter.y = aCharRect.YMost() + onePixel; 2082 // For making the height 1 device pixel after aligning the rect edges to 2083 // device pixels, don't add one device pixel in app units here. 2084 caretRectAfter.height = 1; 2085 } else { 2086 // TODO: Make here bidi-aware. 2087 caretRectAfter.x = aCharRect.XMost() + onePixel; 2088 // For making the width 1 device pixel after aligning the rect edges to 2089 // device pixels, don't add one device pixel in app units here. 2090 caretRectAfter.width = 1; 2091 } 2092 return caretRectAfter; 2093 } 2094 2095 nsresult ContentEventHandler::OnQueryTextRectArray( 2096 WidgetQueryContentEvent* aEvent) { 2097 nsresult rv = Init(aEvent); 2098 if (NS_WARN_IF(NS_FAILED(rv))) { 2099 return rv; 2100 } 2101 2102 MOZ_ASSERT(aEvent->mReply->mOffsetAndData.isNothing()); 2103 2104 LineBreakType lineBreakType = GetLineBreakType(aEvent); 2105 const uint32_t kBRLength = GetBRLength(lineBreakType); 2106 2107 WritingMode lastVisibleFrameWritingMode; 2108 LayoutDeviceIntRect rect; 2109 uint32_t offset = aEvent->mInput.mOffset; 2110 const uint32_t kEndOffset = aEvent->mInput.EndOffset(); 2111 bool wasLineBreaker = false; 2112 // lastCharRect stores the last charRect value (see below for the detail of 2113 // charRect). 2114 nsRect lastCharRect; 2115 // lastFrame is base frame of lastCharRect. 2116 // TODO: We should look for this if the first text is not visible. However, 2117 // users cannot put caret invisible text and users cannot type in it 2118 // at least only with user's operations. Therefore, we don't need to 2119 // fix this immediately. 2120 nsIFrame* lastFrame = nullptr; 2121 nsAutoString flattenedAllText; 2122 flattenedAllText.SetIsVoid(true); 2123 while (offset < kEndOffset) { 2124 Result<DOMRangeAndAdjustedOffsetInFlattenedText, nsresult> 2125 domRangeAndAdjustedOffsetOrError = 2126 ConvertFlatTextOffsetToDOMRange(offset, 1, lineBreakType, true); 2127 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError.isErr())) { 2128 NS_WARNING( 2129 "ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase() failed"); 2130 return domRangeAndAdjustedOffsetOrError.unwrapErr(); 2131 } 2132 const DOMRangeAndAdjustedOffsetInFlattenedText domRangeAndAdjustedOffset = 2133 domRangeAndAdjustedOffsetOrError.unwrap(); 2134 2135 // TODO: When we crossed parent block boundary now, we should fill pending 2136 // character rects with caret rect after the last visible character 2137 // rect. 2138 2139 // If the range is collapsed, offset has already reached the end of the 2140 // contents. 2141 if (domRangeAndAdjustedOffset.mRange.Collapsed()) { 2142 break; 2143 } 2144 2145 // Get the first frame which causes some text after the offset. 2146 FrameAndNodeOffset firstFrame = 2147 GetFirstFrameInRangeForTextRect(domRangeAndAdjustedOffset.mRange); 2148 2149 // If GetFirstFrameInRangeForTextRect() does not return valid frame, that 2150 // means that the offset reached the end of contents or there is no visible 2151 // frame in the range generating flattened text. 2152 if (!firstFrame.IsValid()) { 2153 if (flattenedAllText.IsVoid()) { 2154 flattenedAllText.SetIsVoid(false); 2155 if (NS_WARN_IF(NS_FAILED(GenerateFlatTextContent( 2156 mRootElement, flattenedAllText, lineBreakType)))) { 2157 NS_WARNING("ContentEventHandler::GenerateFlatTextContent() failed"); 2158 return NS_ERROR_FAILURE; 2159 } 2160 } 2161 // If we've reached end of the root, append caret rect at the end of 2162 // the root later. 2163 if (offset >= flattenedAllText.Length()) { 2164 break; 2165 } 2166 // Otherwise, we're in an invisible node. If the node is followed by a 2167 // block boundary causing a line break, we can use the boundary. 2168 // Otherwise, if the node follows a block boundary of a parent block, we 2169 // can use caret rect at previous visible frame causing flattened text. 2170 const uint32_t remainingLengthInCurrentRange = [&]() { 2171 if (domRangeAndAdjustedOffset.mLastTextNode) { 2172 if (domRangeAndAdjustedOffset.RangeStartsFromLastTextNode()) { 2173 if (!domRangeAndAdjustedOffset.RangeStartsFromEndOfContainer()) { 2174 return domRangeAndAdjustedOffset.mLastTextNode->TextDataLength() - 2175 domRangeAndAdjustedOffset.mRange.StartOffset(); 2176 } 2177 return 0u; 2178 } 2179 // Must be there are not nodes which may cause generating text. 2180 // Therefore, we can skip all nodes before the last found text node 2181 // and all text in the last text node. 2182 return domRangeAndAdjustedOffset.mLastTextNode->TextDataLength(); 2183 } 2184 if (domRangeAndAdjustedOffset.RangeStartsFromContent() && 2185 ShouldBreakLineBefore( 2186 *domRangeAndAdjustedOffset.mRange.GetStartContainer() 2187 ->AsContent(), 2188 mRootElement)) { 2189 if (kBRLength != 1u && offset - aEvent->mInput.mOffset < kBRLength) { 2190 // Don't return kBRLength if start position is less than the length 2191 // of a line-break because the offset may be between CRLF on 2192 // Windows. In the case, we will be again here and gets same 2193 // result and we need to pay the penalty only once. Therefore, we 2194 // can keep going without complicated check. 2195 return 1u; 2196 } 2197 return kBRLength; 2198 } 2199 return 0u; 2200 }(); 2201 offset += std::max(1u, remainingLengthInCurrentRange); 2202 continue; 2203 } 2204 2205 nsIContent* firstContent = firstFrame.mFrame->GetContent(); 2206 if (NS_WARN_IF(!firstContent)) { 2207 return NS_ERROR_FAILURE; 2208 } 2209 2210 bool startsBetweenLineBreaker = false; 2211 nsAutoString chars; 2212 lastVisibleFrameWritingMode = firstFrame->GetWritingMode(); 2213 2214 nsIFrame* baseFrame = firstFrame; 2215 // charRect should have each character rect or line breaker rect relative 2216 // to the base frame. 2217 AutoTArray<nsRect, 16> charRects; 2218 2219 // If the first frame is a text frame, the result should be computed with 2220 // the frame's API. 2221 if (firstFrame->IsTextFrame()) { 2222 rv = firstFrame->GetCharacterRectsInRange(firstFrame.mOffsetInNode, 2223 kEndOffset - offset, charRects); 2224 if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(charRects.IsEmpty())) { 2225 return rv; 2226 } 2227 // Assign the characters whose rects are computed by the call of 2228 // nsTextFrame::GetCharacterRectsInRange(). 2229 AppendSubString(chars, *firstContent->AsText(), firstFrame.mOffsetInNode, 2230 charRects.Length()); 2231 if (NS_WARN_IF(chars.Length() != charRects.Length())) { 2232 return NS_ERROR_UNEXPECTED; 2233 } 2234 if (kBRLength > 1 && chars[0] == '\n' && 2235 offset == aEvent->mInput.mOffset && offset) { 2236 // If start of range starting from previous offset of query range is 2237 // same as the start of query range, the query range starts from 2238 // between a line breaker (i.e., the range starts between "\r" and 2239 // "\n"). 2240 Result<UnsafeDOMRangeAndAdjustedOffsetInFlattenedText, nsresult> 2241 domRangeAndAdjustedOffsetOrError = 2242 ConvertFlatTextOffsetToUnsafeDOMRange( 2243 aEvent->mInput.mOffset - 1, 1, lineBreakType, true); 2244 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError.isErr())) { 2245 NS_WARNING( 2246 "ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase() " 2247 "failed"); 2248 return domRangeAndAdjustedOffsetOrError.unwrapErr(); 2249 } 2250 const UnsafeDOMRangeAndAdjustedOffsetInFlattenedText 2251 domRangeAndAdjustedOffsetOfPreviousChar = 2252 domRangeAndAdjustedOffsetOrError.unwrap(); 2253 startsBetweenLineBreaker = 2254 domRangeAndAdjustedOffset.mRange.GetStartContainer() == 2255 domRangeAndAdjustedOffsetOfPreviousChar.mRange 2256 .GetStartContainer() && 2257 domRangeAndAdjustedOffset.mRange.StartOffset() == 2258 domRangeAndAdjustedOffsetOfPreviousChar.mRange.StartOffset(); 2259 } 2260 } 2261 // Other contents should cause a line breaker rect before it. 2262 // Note that moz-<br> element does not cause any text, however, 2263 // it represents empty line at the last of current block. Therefore, 2264 // we need to compute its rect too. 2265 else if (ShouldBreakLineBefore(*firstContent, mRootElement) || 2266 IsPaddingBR(*firstContent)) { 2267 nsRect brRect; 2268 // If the frame is not a <br> frame, we need to compute the caret rect 2269 // with last character's rect before firstContent if there is. 2270 // For example, if caret is after "c" of |<p>abc</p><p>def</p>|, IME may 2271 // query a line breaker's rect after "c". Then, if we compute it only 2272 // with the 2nd <p>'s block frame, the result will be: 2273 // +-<p>--------------------------------+ 2274 // |abc | 2275 // +------------------------------------+ 2276 // 2277 // I+-<p>--------------------------------+ 2278 // |def | 2279 // +------------------------------------+ 2280 // However, users expect popup windows of IME should be positioned at 2281 // right-bottom of "c" like this: 2282 // +-<p>--------------------------------+ 2283 // |abcI | 2284 // +------------------------------------+ 2285 // 2286 // +-<p>--------------------------------+ 2287 // |def | 2288 // +------------------------------------+ 2289 // Therefore, if the first frame isn't a <br> frame and there is a text 2290 // node before the first node in the queried range, we should compute the 2291 // first rect with the previous character's rect. 2292 // If we already compute a character's rect in the queried range, we can 2293 // compute it with the cached last character's rect. (However, don't 2294 // use this path if it's a <br> frame because trusting <br> frame's rect 2295 // is better than guessing the rect from the previous character.) 2296 if (!firstFrame->IsBrFrame() && !aEvent->mReply->mRectArray.IsEmpty()) { 2297 baseFrame = lastFrame; 2298 brRect = lastCharRect; 2299 if (!wasLineBreaker) { 2300 brRect = GetCaretRectAfter(*baseFrame->PresContext(), brRect, 2301 lastVisibleFrameWritingMode); 2302 } 2303 } 2304 // If it's not a <br> frame and it's the first character rect at the 2305 // queried range, we need the previous character rect of the start of 2306 // the queried range if there is a visible text node. 2307 else if (!firstFrame->IsBrFrame() && 2308 domRangeAndAdjustedOffset.mLastTextNode && 2309 domRangeAndAdjustedOffset.mLastTextNode->GetPrimaryFrame()) { 2310 FrameRelativeRect brRectRelativeToLastTextFrame = 2311 GuessLineBreakerRectAfter(*domRangeAndAdjustedOffset.mLastTextNode); 2312 if (NS_WARN_IF(!brRectRelativeToLastTextFrame.IsValid())) { 2313 return NS_ERROR_FAILURE; 2314 } 2315 // Look for the last text frame for the last text node. 2316 nsIFrame* primaryFrame = 2317 domRangeAndAdjustedOffset.mLastTextNode->GetPrimaryFrame(); 2318 if (NS_WARN_IF(!primaryFrame)) { 2319 return NS_ERROR_FAILURE; 2320 } 2321 baseFrame = primaryFrame->LastContinuation(); 2322 if (NS_WARN_IF(!baseFrame)) { 2323 return NS_ERROR_FAILURE; 2324 } 2325 brRect = brRectRelativeToLastTextFrame.RectRelativeTo(baseFrame); 2326 } 2327 // Otherwise, we need to compute the line breaker's rect only with the 2328 // first frame's rect. But this may be unexpected. For example, 2329 // |<div contenteditable>[<p>]abc</p></div>|. In this case, caret is 2330 // before "a", therefore, users expect the rect left of "a". However, 2331 // we don't have enough information about the next character here and 2332 // this isn't usual case (e.g., IME typically tries to query the rect 2333 // of "a" or caret rect for computing its popup position). Therefore, 2334 // we shouldn't do more complicated hack here unless we'll get some bug 2335 // reports actually. 2336 else { 2337 FrameRelativeRect relativeBRRect = GetLineBreakerRectBefore(firstFrame); 2338 brRect = relativeBRRect.RectRelativeTo(firstFrame); 2339 } 2340 charRects.AppendElement(brRect); 2341 chars.AssignLiteral("\n"); 2342 if (kBRLength > 1 && offset == aEvent->mInput.mOffset && offset) { 2343 // If the first frame for the previous offset of the query range and 2344 // the first frame for the start of query range are same, that means 2345 // the start offset is between the first line breaker (i.e., the range 2346 // starts between "\r" and "\n"). 2347 Result<UnsafeDOMRangeAndAdjustedOffsetInFlattenedText, nsresult> 2348 domRangeAndAdjustedOffsetOrError = 2349 ConvertFlatTextOffsetToUnsafeDOMRange( 2350 aEvent->mInput.mOffset - 1, 1, lineBreakType, true); 2351 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError.isErr())) { 2352 NS_WARNING( 2353 "ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase() " 2354 "failed"); 2355 return NS_ERROR_UNEXPECTED; 2356 } 2357 const UnsafeDOMRangeAndAdjustedOffsetInFlattenedText 2358 domRangeAndAdjustedOffset = 2359 domRangeAndAdjustedOffsetOrError.unwrap(); 2360 FrameAndNodeOffset frameForPrevious = 2361 GetFirstFrameInRangeForTextRect(domRangeAndAdjustedOffset.mRange); 2362 startsBetweenLineBreaker = frameForPrevious.mFrame == firstFrame.mFrame; 2363 } 2364 } else { 2365 NS_WARNING( 2366 "The frame is neither a text frame nor a frame whose content " 2367 "causes a line break"); 2368 return NS_ERROR_FAILURE; 2369 } 2370 2371 for (size_t i = 0; i < charRects.Length() && offset < kEndOffset; i++) { 2372 nsRect charRect = charRects[i]; 2373 // Store lastCharRect before applying CSS transform because it may be 2374 // used for computing a line breaker rect. Then, the computed line 2375 // breaker rect will be applied CSS transform again. Therefore, 2376 // the value of lastCharRect should be raw rect value relative to the 2377 // base frame. 2378 lastCharRect = charRect; 2379 lastFrame = baseFrame; 2380 rv = ConvertToRootRelativeOffset(baseFrame, charRect); 2381 if (NS_WARN_IF(NS_FAILED(rv))) { 2382 return rv; 2383 } 2384 2385 nsPresContext* presContext = baseFrame->PresContext(); 2386 rect = LayoutDeviceIntRect::FromAppUnitsToOutside( 2387 charRect, presContext->AppUnitsPerDevPixel()); 2388 if (nsPresContext* rootContext = 2389 presContext->GetInProcessRootContentDocumentPresContext()) { 2390 rect = RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual( 2391 rect, rootContext->PresShell())); 2392 } 2393 // Returning empty rect may cause native IME confused, let's make sure to 2394 // return non-empty rect. 2395 EnsureNonEmptyRect(rect); 2396 2397 // If we found some invisible characters followed by current visible 2398 // character, make their rects same as caret rect before the first visible 2399 // character because IME may want to put their UI next to the rect of the 2400 // invisible character for next input. 2401 // Note that chars do not contain the invisible characters. 2402 if (i == 0u && MOZ_LIKELY(offset > aEvent->mInput.mOffset)) { 2403 const uint32_t offsetInRange = 2404 offset - CheckedInt<uint32_t>(aEvent->mInput.mOffset).value(); 2405 if (offsetInRange > aEvent->mReply->mRectArray.Length()) { 2406 LayoutDeviceIntRect caretRectBefore = 2407 GetCaretRectBefore(rect, lastVisibleFrameWritingMode); 2408 for ([[maybe_unused]] uint32_t index : IntegerRange<uint32_t>( 2409 offsetInRange - aEvent->mReply->mRectArray.Length())) { 2410 aEvent->mReply->mRectArray.AppendElement(caretRectBefore); 2411 } 2412 MOZ_ASSERT(aEvent->mReply->mRectArray.Length() == offsetInRange); 2413 } 2414 } 2415 2416 aEvent->mReply->mRectArray.AppendElement(rect); 2417 offset++; 2418 2419 // If it's not a line breaker or the line breaker length is same as 2420 // XP line breaker's, we need to do nothing for current character. 2421 wasLineBreaker = chars[i] == '\n'; 2422 if (!wasLineBreaker || kBRLength == 1) { 2423 continue; 2424 } 2425 2426 MOZ_ASSERT(kBRLength == 2); 2427 2428 // If it's already reached the end of query range, we don't need to do 2429 // anymore. 2430 if (offset == kEndOffset) { 2431 break; 2432 } 2433 2434 // If the query range starts from between a line breaker, i.e., it starts 2435 // between "\r" and "\n", the appended rect was for the "\n". Therefore, 2436 // we don't need to append same rect anymore for current "\r\n". 2437 if (startsBetweenLineBreaker) { 2438 continue; 2439 } 2440 2441 // The appended rect was for "\r" of "\r\n". Therefore, we need to 2442 // append same rect for "\n" too because querying rect of "\r" and "\n" 2443 // should return same rect. E.g., IME may query previous character's 2444 // rect of first character of a line. 2445 aEvent->mReply->mRectArray.AppendElement(rect); 2446 offset++; 2447 } 2448 } 2449 2450 // If we've not handled some invisible character rects, fill them as caret 2451 // rect after the last visible character. 2452 if (!aEvent->mReply->mRectArray.IsEmpty()) { 2453 const uint32_t offsetInRange = 2454 offset - CheckedInt<uint32_t>(aEvent->mInput.mOffset).value(); 2455 if (offsetInRange > aEvent->mReply->mRectArray.Length()) { 2456 LayoutDeviceIntRect caretRectAfter = 2457 GetCaretRectAfter(aEvent->mReply->mRectArray.LastElement(), 2458 lastVisibleFrameWritingMode); 2459 for ([[maybe_unused]] uint32_t index : IntegerRange<uint32_t>( 2460 offsetInRange - aEvent->mReply->mRectArray.Length())) { 2461 aEvent->mReply->mRectArray.AppendElement(caretRectAfter); 2462 } 2463 MOZ_ASSERT(aEvent->mReply->mRectArray.Length() == offsetInRange); 2464 } 2465 } 2466 2467 // If the query range is longer than actual content length, we should append 2468 // caret rect at the end of the content as the last character rect because 2469 // native IME may want to query character rect at the end of contents for 2470 // deciding the position of a popup window (e.g., suggest window for next 2471 // word). Note that when this method hasn't appended character rects, it 2472 // means that the offset is too large or the query range is collapsed. 2473 if (offset < kEndOffset || aEvent->mReply->mRectArray.IsEmpty()) { 2474 // If we've already retrieved some character rects before current offset, 2475 // we can guess the last rect from the last character's rect unless it's a 2476 // line breaker. (If it's a line breaker, the caret rect is in next line.) 2477 if (!aEvent->mReply->mRectArray.IsEmpty() && !wasLineBreaker) { 2478 rect = GetCaretRectAfter(aEvent->mReply->mRectArray.LastElement(), 2479 lastVisibleFrameWritingMode); 2480 aEvent->mReply->mRectArray.AppendElement(rect); 2481 } else { 2482 // Note that don't use eQueryCaretRect here because if caret is at the 2483 // end of the content, it returns actual caret rect instead of computing 2484 // the rect itself. It means that the result depends on caret position. 2485 // So, we shouldn't use it for consistency result in automated tests. 2486 WidgetQueryContentEvent queryTextRectEvent(eQueryTextRect, *aEvent); 2487 WidgetQueryContentEvent::Options options(*aEvent); 2488 queryTextRectEvent.InitForQueryTextRect(offset, 1, options); 2489 if (NS_WARN_IF(NS_FAILED(OnQueryTextRect(&queryTextRectEvent))) || 2490 NS_WARN_IF(queryTextRectEvent.Failed())) { 2491 return NS_ERROR_FAILURE; 2492 } 2493 if (queryTextRectEvent.mReply->mWritingMode.IsVertical()) { 2494 queryTextRectEvent.mReply->mRect.height = 1; 2495 } else { 2496 queryTextRectEvent.mReply->mRect.width = 1; 2497 } 2498 aEvent->mReply->mRectArray.AppendElement( 2499 queryTextRectEvent.mReply->mRect); 2500 } 2501 } 2502 2503 MOZ_ASSERT(aEvent->Succeeded()); 2504 return NS_OK; 2505 } 2506 2507 nsresult ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent) { 2508 // If mLength is 0 (this may be caused by bug of native IME), we should 2509 // redirect this event to OnQueryCaretRect(). 2510 if (!aEvent->mInput.mLength) { 2511 return OnQueryCaretRect(aEvent); 2512 } 2513 2514 nsresult rv = Init(aEvent); 2515 if (NS_FAILED(rv)) { 2516 return rv; 2517 } 2518 2519 MOZ_ASSERT(aEvent->mReply->mOffsetAndData.isNothing()); 2520 2521 LineBreakType lineBreakType = GetLineBreakType(aEvent); 2522 Result<DOMRangeAndAdjustedOffsetInFlattenedText, nsresult> 2523 domRangeAndAdjustedOffsetOrError = ConvertFlatTextOffsetToDOMRange( 2524 aEvent->mInput.mOffset, aEvent->mInput.mLength, lineBreakType, true); 2525 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError.isErr())) { 2526 NS_WARNING("ContentEventHandler::ConvertFlatTextOffsetToDOMRange() failed"); 2527 return NS_ERROR_FAILURE; 2528 } 2529 DOMRangeAndAdjustedOffsetInFlattenedText domRangeAndAdjustedOffset = 2530 domRangeAndAdjustedOffsetOrError.unwrap(); 2531 nsString string; 2532 if (NS_WARN_IF(NS_FAILED(GenerateFlatTextContent( 2533 domRangeAndAdjustedOffset.mRange, string, lineBreakType)))) { 2534 return NS_ERROR_FAILURE; 2535 } 2536 aEvent->mReply->mOffsetAndData.emplace( 2537 domRangeAndAdjustedOffset.mAdjustedOffset, string, 2538 OffsetAndDataFor::EditorString); 2539 2540 // used to iterate over all contents and their frames 2541 PostContentIterator postOrderIter; 2542 rv = postOrderIter.Init(domRangeAndAdjustedOffset.mRange.Start().AsRaw(), 2543 domRangeAndAdjustedOffset.mRange.End().AsRaw()); 2544 if (NS_WARN_IF(NS_FAILED(rv))) { 2545 return NS_ERROR_FAILURE; 2546 } 2547 2548 // Get the first frame which causes some text after the offset. 2549 FrameAndNodeOffset firstFrame = 2550 GetFirstFrameInRangeForTextRect(domRangeAndAdjustedOffset.mRange); 2551 2552 // If GetFirstFrameInRangeForTextRect() does not return valid frame, that 2553 // means that there are no visible frames having text or the offset reached 2554 // the end of contents. 2555 if (!firstFrame.IsValid()) { 2556 nsAutoString allText; 2557 rv = GenerateFlatTextContent(mRootElement, allText, lineBreakType); 2558 // If the offset doesn't reach the end of contents but there is no frames 2559 // for the node, that means that current offset's node is hidden by CSS or 2560 // something. Ideally, we should handle it with the last visible text 2561 // node's last character's rect, but it's not usual cases in actual web 2562 // services. Therefore, currently, we should make this case fail. 2563 if (NS_WARN_IF(NS_FAILED(rv)) || 2564 static_cast<uint32_t>(aEvent->mInput.mOffset) < allText.Length()) { 2565 return NS_ERROR_FAILURE; 2566 } 2567 2568 // Look for the last frame which should be included text rects. 2569 rv = domRangeAndAdjustedOffset.mRange.SelectNodeContents(mRootElement); 2570 if (NS_WARN_IF(NS_FAILED(rv))) { 2571 return NS_ERROR_UNEXPECTED; 2572 } 2573 nsRect rect; 2574 FrameAndNodeOffset lastFrame = 2575 GetLastFrameInRangeForTextRect(domRangeAndAdjustedOffset.mRange); 2576 // If there is at least one frame which can be used for computing a rect 2577 // for a character or a line breaker, we should use it for guessing the 2578 // caret rect at the end of the contents. 2579 nsPresContext* presContext; 2580 if (lastFrame) { 2581 presContext = lastFrame->PresContext(); 2582 if (NS_WARN_IF(!lastFrame->GetContent())) { 2583 return NS_ERROR_FAILURE; 2584 } 2585 FrameRelativeRect relativeRect; 2586 // If there is a <br> frame at the end, it represents an empty line at 2587 // the end with moz-<br> or content <br> in a block level element. 2588 if (lastFrame->IsBrFrame()) { 2589 relativeRect = GetLineBreakerRectBefore(lastFrame); 2590 } 2591 // If there is a text frame at the end, use its information. 2592 else if (lastFrame->IsTextFrame()) { 2593 const Text* textNode = Text::FromNode(lastFrame->GetContent()); 2594 MOZ_ASSERT(textNode); 2595 if (textNode) { 2596 relativeRect = GuessLineBreakerRectAfter(*textNode); 2597 } 2598 } 2599 // If there is an empty frame which is neither a text frame nor a <br> 2600 // frame at the end, guess caret rect in it. 2601 else { 2602 relativeRect = GuessFirstCaretRectIn(lastFrame); 2603 } 2604 if (NS_WARN_IF(!relativeRect.IsValid())) { 2605 return NS_ERROR_FAILURE; 2606 } 2607 rect = relativeRect.RectRelativeTo(lastFrame); 2608 rv = ConvertToRootRelativeOffset(lastFrame, rect); 2609 if (NS_WARN_IF(NS_FAILED(rv))) { 2610 return rv; 2611 } 2612 aEvent->mReply->mWritingMode = lastFrame->GetWritingMode(); 2613 } 2614 // Otherwise, if there are no contents in mRootElement, guess caret rect in 2615 // its frame (with its font height and content box). 2616 else { 2617 nsIFrame* rootContentFrame = mRootElement->GetPrimaryFrame(); 2618 if (NS_WARN_IF(!rootContentFrame)) { 2619 return NS_ERROR_FAILURE; 2620 } 2621 presContext = rootContentFrame->PresContext(); 2622 FrameRelativeRect relativeRect = GuessFirstCaretRectIn(rootContentFrame); 2623 if (NS_WARN_IF(!relativeRect.IsValid())) { 2624 return NS_ERROR_FAILURE; 2625 } 2626 rect = relativeRect.RectRelativeTo(rootContentFrame); 2627 rv = ConvertToRootRelativeOffset(rootContentFrame, rect); 2628 if (NS_WARN_IF(NS_FAILED(rv))) { 2629 return rv; 2630 } 2631 aEvent->mReply->mWritingMode = rootContentFrame->GetWritingMode(); 2632 } 2633 aEvent->mReply->mRect = LayoutDeviceIntRect::FromAppUnitsToOutside( 2634 rect, presContext->AppUnitsPerDevPixel()); 2635 if (nsPresContext* rootContext = 2636 presContext->GetInProcessRootContentDocumentPresContext()) { 2637 aEvent->mReply->mRect = 2638 RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual( 2639 aEvent->mReply->mRect, rootContext->PresShell())); 2640 } 2641 EnsureNonEmptyRect(aEvent->mReply->mRect); 2642 2643 MOZ_ASSERT(aEvent->Succeeded()); 2644 return NS_OK; 2645 } 2646 2647 nsRect rect, frameRect; 2648 nsPoint ptOffset; 2649 2650 // If the first frame is a text frame, the result should be computed with 2651 // the frame's rect but not including the rect before start point of the 2652 // queried range. 2653 if (firstFrame->IsTextFrame()) { 2654 rect.SetRect(nsPoint(0, 0), firstFrame->GetRect().Size()); 2655 rv = ConvertToRootRelativeOffset(firstFrame, rect); 2656 if (NS_WARN_IF(NS_FAILED(rv))) { 2657 return rv; 2658 } 2659 frameRect = rect; 2660 // Exclude the rect before start point of the queried range. 2661 firstFrame->GetPointFromOffset(firstFrame.mOffsetInNode, &ptOffset); 2662 if (firstFrame->GetWritingMode().IsVertical()) { 2663 rect.y += ptOffset.y; 2664 rect.height -= ptOffset.y; 2665 } else { 2666 rect.x += ptOffset.x; 2667 rect.width -= ptOffset.x; 2668 } 2669 } 2670 // If first frame causes a line breaker but it's not a <br> frame, we cannot 2671 // compute proper rect only with the frame because typically caret is at 2672 // right of the last character of it. For example, if caret is after "c" of 2673 // |<p>abc</p><p>def</p>|, IME may query a line breaker's rect after "c". 2674 // Then, if we compute it only with the 2nd <p>'s block frame, the result 2675 // will be: 2676 // +-<p>--------------------------------+ 2677 // |abc | 2678 // +------------------------------------+ 2679 // 2680 // I+-<p>--------------------------------+ 2681 // |def | 2682 // +------------------------------------+ 2683 // However, users expect popup windows of IME should be positioned at 2684 // right-bottom of "c" like this: 2685 // +-<p>--------------------------------+ 2686 // |abcI | 2687 // +------------------------------------+ 2688 // 2689 // +-<p>--------------------------------+ 2690 // |def | 2691 // +------------------------------------+ 2692 // Therefore, if the first frame isn't a <br> frame and there is a visible 2693 // text node before the first node in the queried range, we should compute the 2694 // first rect with the previous character's rect. 2695 else if (!firstFrame->IsBrFrame() && 2696 domRangeAndAdjustedOffset.mLastTextNode && 2697 domRangeAndAdjustedOffset.mLastTextNode->GetPrimaryFrame()) { 2698 FrameRelativeRect brRectAfterLastChar = 2699 GuessLineBreakerRectAfter(*domRangeAndAdjustedOffset.mLastTextNode); 2700 if (NS_WARN_IF(!brRectAfterLastChar.IsValid())) { 2701 return NS_ERROR_FAILURE; 2702 } 2703 rect = brRectAfterLastChar.mRect; 2704 rv = ConvertToRootRelativeOffset(brRectAfterLastChar.mBaseFrame, rect); 2705 if (NS_WARN_IF(NS_FAILED(rv))) { 2706 return rv; 2707 } 2708 frameRect = rect; 2709 } 2710 // Otherwise, we need to compute the line breaker's rect only with the 2711 // first frame's rect. But this may be unexpected. For example, 2712 // |<div contenteditable>[<p>]abc</p></div>|. In this case, caret is before 2713 // "a", therefore, users expect the rect left of "a". However, we don't 2714 // have enough information about the next character here and this isn't 2715 // usual case (e.g., IME typically tries to query the rect of "a" or caret 2716 // rect for computing its popup position). Therefore, we shouldn't do 2717 // more complicated hack here unless we'll get some bug reports actually. 2718 else { 2719 FrameRelativeRect relativeRect = GetLineBreakerRectBefore(firstFrame); 2720 if (NS_WARN_IF(!relativeRect.IsValid())) { 2721 return NS_ERROR_FAILURE; 2722 } 2723 rect = relativeRect.RectRelativeTo(firstFrame); 2724 rv = ConvertToRootRelativeOffset(firstFrame, rect); 2725 if (NS_WARN_IF(NS_FAILED(rv))) { 2726 return rv; 2727 } 2728 frameRect = rect; 2729 } 2730 // UnionRect() requires non-empty rect. So, let's make sure to get non-emtpy 2731 // rect from the first frame. 2732 EnsureNonEmptyRect(rect); 2733 2734 // Get the last frame which causes some text in the range. 2735 FrameAndNodeOffset lastFrame = 2736 GetLastFrameInRangeForTextRect(domRangeAndAdjustedOffset.mRange); 2737 if (NS_WARN_IF(!lastFrame.IsValid())) { 2738 return NS_ERROR_FAILURE; 2739 } 2740 2741 // iterate over all covered frames 2742 for (nsIFrame* frame = firstFrame; frame != lastFrame;) { 2743 frame = frame->GetNextContinuation(); 2744 if (!frame) { 2745 do { 2746 postOrderIter.Next(); 2747 nsINode* node = postOrderIter.GetCurrentNode(); 2748 if (!node) { 2749 break; 2750 } 2751 if (!node->IsContent()) { 2752 continue; 2753 } 2754 nsIFrame* primaryFrame = node->AsContent()->GetPrimaryFrame(); 2755 // The node may be hidden by CSS. 2756 if (!primaryFrame) { 2757 continue; 2758 } 2759 // We should take only text frame's rect and br frame's rect. We can 2760 // always use frame rect of text frame and GetLineBreakerRectBefore() 2761 // can return exactly correct rect only for <br> frame for now. On the 2762 // other hand, GetLineBreakRectBefore() returns guessed caret rect for 2763 // the other frames. We shouldn't include such odd rect to the result. 2764 if (primaryFrame->IsTextFrame() || primaryFrame->IsBrFrame()) { 2765 frame = primaryFrame; 2766 } 2767 } while (!frame && !postOrderIter.IsDone()); 2768 if (!frame) { 2769 break; 2770 } 2771 } 2772 if (frame->IsTextFrame()) { 2773 frameRect.SetRect(nsPoint(0, 0), frame->GetRect().Size()); 2774 } else { 2775 MOZ_ASSERT(frame->IsBrFrame()); 2776 FrameRelativeRect relativeRect = GetLineBreakerRectBefore(frame); 2777 if (NS_WARN_IF(!relativeRect.IsValid())) { 2778 return NS_ERROR_FAILURE; 2779 } 2780 frameRect = relativeRect.RectRelativeTo(frame); 2781 } 2782 rv = ConvertToRootRelativeOffset(frame, frameRect); 2783 if (NS_WARN_IF(NS_FAILED(rv))) { 2784 return rv; 2785 } 2786 // UnionRect() requires non-empty rect. So, let's make sure to get 2787 // non-emtpy rect from the frame. 2788 EnsureNonEmptyRect(frameRect); 2789 if (frame != lastFrame) { 2790 // not last frame, so just add rect to previous result 2791 rect.UnionRect(rect, frameRect); 2792 } 2793 } 2794 2795 // Get the ending frame rect. 2796 // FYI: If first frame and last frame are same, frameRect is already set 2797 // to the rect excluding the text before the query range. 2798 if (firstFrame.mFrame != lastFrame.mFrame) { 2799 frameRect.SetRect(nsPoint(0, 0), lastFrame->GetRect().Size()); 2800 rv = ConvertToRootRelativeOffset(lastFrame, frameRect); 2801 if (NS_WARN_IF(NS_FAILED(rv))) { 2802 return rv; 2803 } 2804 } 2805 2806 // Shrink the last frame for cutting off the text after the query range. 2807 if (lastFrame->IsTextFrame()) { 2808 lastFrame->GetPointFromOffset(lastFrame.mOffsetInNode, &ptOffset); 2809 if (lastFrame->GetWritingMode().IsVertical()) { 2810 frameRect.height -= lastFrame->GetRect().height - ptOffset.y; 2811 } else { 2812 frameRect.width -= lastFrame->GetRect().width - ptOffset.x; 2813 } 2814 // UnionRect() requires non-empty rect. So, let's make sure to get 2815 // non-empty rect from the last frame. 2816 EnsureNonEmptyRect(frameRect); 2817 2818 if (firstFrame.mFrame == lastFrame.mFrame) { 2819 rect.IntersectRect(rect, frameRect); 2820 } else { 2821 rect.UnionRect(rect, frameRect); 2822 } 2823 } 2824 2825 nsPresContext* presContext = lastFrame->PresContext(); 2826 aEvent->mReply->mRect = LayoutDeviceIntRect::FromAppUnitsToOutside( 2827 rect, presContext->AppUnitsPerDevPixel()); 2828 if (nsPresContext* rootContext = 2829 presContext->GetInProcessRootContentDocumentPresContext()) { 2830 aEvent->mReply->mRect = 2831 RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual( 2832 aEvent->mReply->mRect, rootContext->PresShell())); 2833 } 2834 // Returning empty rect may cause native IME confused, let's make sure to 2835 // return non-empty rect. 2836 EnsureNonEmptyRect(aEvent->mReply->mRect); 2837 aEvent->mReply->mWritingMode = lastFrame->GetWritingMode(); 2838 2839 MOZ_ASSERT(aEvent->Succeeded()); 2840 return NS_OK; 2841 } 2842 2843 nsresult ContentEventHandler::OnQueryEditorRect( 2844 WidgetQueryContentEvent* aEvent) { 2845 nsresult rv = Init(aEvent); 2846 if (NS_FAILED(rv)) { 2847 return rv; 2848 } 2849 2850 if (NS_WARN_IF(NS_FAILED(QueryContentRect(mRootElement, aEvent)))) { 2851 return NS_ERROR_FAILURE; 2852 } 2853 2854 MOZ_ASSERT(aEvent->Succeeded()); 2855 return NS_OK; 2856 } 2857 2858 nsresult ContentEventHandler::OnQueryCaretRect( 2859 WidgetQueryContentEvent* aEvent) { 2860 nsresult rv = Init(aEvent); 2861 if (NS_FAILED(rv)) { 2862 return rv; 2863 } 2864 2865 // When the selection is collapsed and the queried offset is current caret 2866 // position, we should return the "real" caret rect. 2867 if (mSelection->IsCollapsed()) { 2868 nsRect caretRect; 2869 nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect); 2870 if (caretFrame) { 2871 uint32_t offset; 2872 rv = GetStartOffset(mFirstSelectedSimpleRange, &offset, 2873 GetLineBreakType(aEvent)); 2874 NS_ENSURE_SUCCESS(rv, rv); 2875 if (offset == aEvent->mInput.mOffset) { 2876 rv = ConvertToRootRelativeOffset(caretFrame, caretRect); 2877 NS_ENSURE_SUCCESS(rv, rv); 2878 nsPresContext* presContext = caretFrame->PresContext(); 2879 aEvent->mReply->mRect = LayoutDeviceIntRect::FromAppUnitsToOutside( 2880 caretRect, presContext->AppUnitsPerDevPixel()); 2881 if (nsPresContext* rootContext = 2882 presContext->GetInProcessRootContentDocumentPresContext()) { 2883 aEvent->mReply->mRect = 2884 RoundedOut(ViewportUtils::DocumentRelativeLayoutToVisual( 2885 aEvent->mReply->mRect, rootContext->PresShell())); 2886 } 2887 // Returning empty rect may cause native IME confused, let's make sure 2888 // to return non-empty rect. 2889 EnsureNonEmptyRect(aEvent->mReply->mRect); 2890 aEvent->mReply->mWritingMode = caretFrame->GetWritingMode(); 2891 aEvent->mReply->mOffsetAndData.emplace( 2892 aEvent->mInput.mOffset, EmptyString(), 2893 OffsetAndDataFor::SelectedString); 2894 2895 MOZ_ASSERT(aEvent->Succeeded()); 2896 return NS_OK; 2897 } 2898 } 2899 } 2900 2901 // Otherwise, we should guess the caret rect from the character's rect. 2902 WidgetQueryContentEvent queryTextRectEvent(eQueryTextRect, *aEvent); 2903 WidgetQueryContentEvent::Options options(*aEvent); 2904 queryTextRectEvent.InitForQueryTextRect(aEvent->mInput.mOffset, 1, options); 2905 if (NS_WARN_IF(NS_FAILED(OnQueryTextRect(&queryTextRectEvent))) || 2906 NS_WARN_IF(queryTextRectEvent.Failed())) { 2907 return NS_ERROR_FAILURE; 2908 } 2909 queryTextRectEvent.mReply->TruncateData(); 2910 aEvent->mReply->mOffsetAndData = 2911 std::move(queryTextRectEvent.mReply->mOffsetAndData); 2912 aEvent->mReply->mWritingMode = 2913 std::move(queryTextRectEvent.mReply->mWritingMode); 2914 aEvent->mReply->mRect = GetCaretRectBefore(queryTextRectEvent.mReply->mRect, 2915 aEvent->mReply->mWritingMode); 2916 2917 MOZ_ASSERT(aEvent->Succeeded()); 2918 return NS_OK; 2919 } 2920 2921 nsresult ContentEventHandler::OnQueryContentState( 2922 WidgetQueryContentEvent* aEvent) { 2923 if (NS_FAILED(Init(aEvent))) { 2924 return NS_ERROR_FAILURE; 2925 } 2926 MOZ_ASSERT(aEvent->mReply.isSome()); 2927 MOZ_ASSERT(aEvent->Succeeded()); 2928 return NS_OK; 2929 } 2930 2931 nsresult ContentEventHandler::OnQuerySelectionAsTransferable( 2932 WidgetQueryContentEvent* aEvent) { 2933 nsresult rv = Init(aEvent); 2934 if (NS_FAILED(rv)) { 2935 return rv; 2936 } 2937 2938 MOZ_ASSERT(aEvent->mReply.isSome()); 2939 2940 if (mSelection->IsCollapsed()) { 2941 MOZ_ASSERT(!aEvent->mReply->mTransferable); 2942 return NS_OK; 2943 } 2944 2945 if (NS_WARN_IF(NS_FAILED(nsCopySupport::GetTransferableForSelection( 2946 mSelection, mDocument, 2947 getter_AddRefs(aEvent->mReply->mTransferable))))) { 2948 return NS_ERROR_FAILURE; 2949 } 2950 2951 MOZ_ASSERT(aEvent->Succeeded()); 2952 return NS_OK; 2953 } 2954 2955 nsresult ContentEventHandler::OnQueryCharacterAtPoint( 2956 WidgetQueryContentEvent* aEvent) { 2957 nsresult rv = Init(aEvent); 2958 if (NS_FAILED(rv)) { 2959 return rv; 2960 } 2961 2962 MOZ_ASSERT(aEvent->mReply->mOffsetAndData.isNothing()); 2963 MOZ_ASSERT(aEvent->mReply->mTentativeCaretOffset.isNothing()); 2964 2965 PresShell* presShell = mDocument->GetPresShell(); 2966 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); 2967 nsIFrame* rootFrame = presShell->GetRootFrame(); 2968 NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE); 2969 nsIWidget* rootWidget = rootFrame->GetNearestWidget(); 2970 NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE); 2971 2972 // The root frame's widget might be different, e.g., the event was fired on 2973 // a popup but the rootFrame is the document root. 2974 if (rootWidget != aEvent->mWidget) { 2975 MOZ_ASSERT(aEvent->mWidget, "The event must have the widget"); 2976 rootFrame = aEvent->mWidget->GetFrame(); 2977 NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE); 2978 rootWidget = rootFrame->GetNearestWidget(); 2979 NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE); 2980 } 2981 2982 WidgetQueryContentEvent queryCharAtPointOnRootWidgetEvent( 2983 true, eQueryCharacterAtPoint, rootWidget); 2984 queryCharAtPointOnRootWidgetEvent.mUseNativeLineBreak = 2985 aEvent->mUseNativeLineBreak; 2986 queryCharAtPointOnRootWidgetEvent.mRefPoint = aEvent->mRefPoint; 2987 if (rootWidget != aEvent->mWidget) { 2988 queryCharAtPointOnRootWidgetEvent.mRefPoint += 2989 aEvent->mWidget->WidgetToScreenOffset() - 2990 rootWidget->WidgetToScreenOffset(); 2991 } 2992 nsPoint ptInRoot = nsLayoutUtils::GetEventCoordinatesRelativeTo( 2993 &queryCharAtPointOnRootWidgetEvent, RelativeTo{rootFrame}); 2994 2995 nsIFrame* targetFrame = 2996 nsLayoutUtils::GetFrameForPoint(RelativeTo{rootFrame}, ptInRoot); 2997 if (!targetFrame || !targetFrame->GetContent() || 2998 !targetFrame->GetContent()->IsInclusiveDescendantOf(mRootElement)) { 2999 // There is no character at the point. 3000 MOZ_ASSERT(aEvent->Succeeded()); 3001 return NS_OK; 3002 } 3003 nsPoint ptInTarget = ptInRoot + rootFrame->GetOffsetToCrossDoc(targetFrame); 3004 int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel(); 3005 int32_t targetAPD = targetFrame->PresContext()->AppUnitsPerDevPixel(); 3006 ptInTarget = ptInTarget.ScaleToOtherAppUnits(rootAPD, targetAPD); 3007 3008 nsIFrame::ContentOffsets tentativeCaretOffsets = 3009 targetFrame->GetContentOffsetsFromPoint(ptInTarget); 3010 if (!tentativeCaretOffsets.content || 3011 !tentativeCaretOffsets.content->IsInclusiveDescendantOf(mRootElement)) { 3012 // There is no character nor tentative caret point at the point. 3013 MOZ_ASSERT(aEvent->Succeeded()); 3014 return NS_OK; 3015 } 3016 3017 uint32_t tentativeCaretOffset = 0; 3018 if (NS_WARN_IF(NS_FAILED(GetFlatTextLengthInRange( 3019 RawNodePosition(mRootElement, 0u), 3020 RawNodePosition(tentativeCaretOffsets), mRootElement, 3021 &tentativeCaretOffset, GetLineBreakType(aEvent))))) { 3022 return NS_ERROR_FAILURE; 3023 } 3024 3025 aEvent->mReply->mTentativeCaretOffset.emplace(tentativeCaretOffset); 3026 if (!targetFrame->IsTextFrame()) { 3027 // There is no character at the point but there is tentative caret point. 3028 MOZ_ASSERT(aEvent->Succeeded()); 3029 return NS_OK; 3030 } 3031 3032 nsTextFrame* textframe = static_cast<nsTextFrame*>(targetFrame); 3033 nsIFrame::ContentOffsets contentOffsets = 3034 textframe->GetCharacterOffsetAtFramePoint(ptInTarget); 3035 NS_ENSURE_TRUE(contentOffsets.content, NS_ERROR_FAILURE); 3036 uint32_t offset = 0; 3037 if (NS_WARN_IF(NS_FAILED(GetFlatTextLengthInRange( 3038 RawNodePosition(mRootElement, 0u), RawNodePosition(contentOffsets), 3039 mRootElement, &offset, GetLineBreakType(aEvent))))) { 3040 return NS_ERROR_FAILURE; 3041 } 3042 3043 WidgetQueryContentEvent queryTextRectEvent(true, eQueryTextRect, 3044 aEvent->mWidget); 3045 WidgetQueryContentEvent::Options options(*aEvent); 3046 queryTextRectEvent.InitForQueryTextRect(offset, 1, options); 3047 if (NS_WARN_IF(NS_FAILED(OnQueryTextRect(&queryTextRectEvent))) || 3048 NS_WARN_IF(queryTextRectEvent.Failed())) { 3049 return NS_ERROR_FAILURE; 3050 } 3051 3052 aEvent->mReply->mOffsetAndData = 3053 std::move(queryTextRectEvent.mReply->mOffsetAndData); 3054 aEvent->mReply->mRect = queryTextRectEvent.mReply->mRect; 3055 3056 MOZ_ASSERT(aEvent->Succeeded()); 3057 return NS_OK; 3058 } 3059 3060 nsresult ContentEventHandler::QueryHittestImpl(WidgetQueryContentEvent* aEvent, 3061 bool aFlushLayout, 3062 bool aPerformRetargeting, 3063 Element** aContentUnderMouse) { 3064 NS_ASSERTION(aEvent, "aEvent must not be null"); 3065 3066 nsresult rv = InitBasic(); 3067 if (NS_FAILED(rv)) { 3068 return rv; 3069 } 3070 3071 NS_ENSURE_TRUE(aEvent->mWidget, NS_ERROR_FAILURE); 3072 3073 PresShell* presShell = mDocument->GetPresShell(); 3074 NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); 3075 nsIFrame* docFrame = presShell->GetRootFrame(); 3076 NS_ENSURE_TRUE(docFrame, NS_ERROR_FAILURE); 3077 3078 LayoutDeviceIntPoint eventLoc = 3079 aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset(); 3080 CSSIntRect docFrameRect = docFrame->GetScreenRect(); 3081 CSSIntPoint eventLocCSS( 3082 docFrame->PresContext()->DevPixelsToIntCSSPixels(eventLoc.x) - 3083 docFrameRect.x, 3084 docFrame->PresContext()->DevPixelsToIntCSSPixels(eventLoc.y) - 3085 docFrameRect.y); 3086 RefPtr<Element> contentUnderMouse = mDocument->ElementFromPointHelper( 3087 eventLocCSS.x, eventLocCSS.y, false, false, ViewportType::Visual, 3088 aPerformRetargeting); 3089 3090 contentUnderMouse.forget(aContentUnderMouse); 3091 return NS_OK; 3092 } 3093 3094 nsresult ContentEventHandler::OnQueryDOMWidgetHittest( 3095 WidgetQueryContentEvent* aEvent) { 3096 aEvent->mReply->mWidgetIsHit = false; 3097 RefPtr<Element> contentUnderMouse; 3098 nsresult rv = QueryHittestImpl(aEvent, true /* flushLayout */, 3099 true /* performRetargeting */, 3100 getter_AddRefs(contentUnderMouse)); 3101 NS_ENSURE_SUCCESS(rv, rv); 3102 if (contentUnderMouse) { 3103 if (nsIFrame* targetFrame = contentUnderMouse->GetPrimaryFrame()) { 3104 if (aEvent->mWidget == targetFrame->GetNearestWidget()) { 3105 aEvent->mReply->mWidgetIsHit = true; 3106 } 3107 } 3108 } 3109 3110 MOZ_ASSERT(aEvent->Succeeded()); 3111 return NS_OK; 3112 } 3113 3114 nsresult ContentEventHandler::OnQueryDropTargetHittest( 3115 WidgetQueryContentEvent* aEvent) { 3116 RefPtr<Element> contentUnderMouse; 3117 nsresult rv = QueryHittestImpl(aEvent, true /* flushLayout */, 3118 false /* performRetargeting */, 3119 getter_AddRefs(contentUnderMouse)); 3120 NS_ENSURE_SUCCESS(rv, rv); 3121 aEvent->EmplaceReply(); 3122 aEvent->mReply->mDropElement = contentUnderMouse; 3123 aEvent->mReply->mDropFrame = 3124 mDocument->GetPresShell()->GetCurrentEventFrame(); 3125 return NS_OK; 3126 } 3127 3128 /* static */ 3129 nsresult ContentEventHandler::GetFlatTextLengthInRange( 3130 const RawNodePosition& aStartPosition, const RawNodePosition& aEndPosition, 3131 const Element* aRootElement, uint32_t* aLength, 3132 LineBreakType aLineBreakType, bool aIsRemovingNode /* = false */) { 3133 if (NS_WARN_IF(!aRootElement) || NS_WARN_IF(!aStartPosition.IsSet()) || 3134 NS_WARN_IF(!aEndPosition.IsSet()) || NS_WARN_IF(!aLength)) { 3135 return NS_ERROR_INVALID_ARG; 3136 } 3137 3138 if (aStartPosition == aEndPosition) { 3139 *aLength = 0; 3140 return NS_OK; 3141 } 3142 3143 UnsafePreContentIterator preOrderIter; 3144 3145 // Working with ContentIterator, we may need to adjust the end position for 3146 // including it forcibly. 3147 RawNodePosition endPosition(aEndPosition); 3148 3149 // This may be called for retrieving the text of removed nodes. So, be careful 3150 // to handle this case. FIXME: Do we need this special-case now? 3151 if (aIsRemovingNode) { 3152 MOZ_ASSERT(aStartPosition.GetContainer() == endPosition.GetContainer(), 3153 "At removing the node, start and end node should be same"); 3154 MOZ_ASSERT(*aStartPosition.Offset( 3155 RawNodePosition::OffsetFilter::kValidOrInvalidOffsets) == 0, 3156 "When the node is being removed, the start offset should be 0"); 3157 MOZ_ASSERT( 3158 static_cast<uint32_t>(*endPosition.Offset( 3159 RawNodePosition::OffsetFilter::kValidOrInvalidOffsets)) == 3160 endPosition.GetContainer()->GetChildCount(), 3161 "When the node is being removed, the end offset should be child count"); 3162 nsresult rv = preOrderIter.Init(aStartPosition.GetContainer()); 3163 if (NS_WARN_IF(NS_FAILED(rv))) { 3164 return rv; 3165 } 3166 } else { 3167 SimpleRange prevSimpleRange; 3168 nsresult rv = prevSimpleRange.SetStart(aStartPosition.AsRaw()); 3169 if (NS_WARN_IF(NS_FAILED(rv))) { 3170 return rv; 3171 } 3172 3173 // When the end position is immediately after non-root element's open tag, 3174 // we need to include a line break caused by the open tag. 3175 if (endPosition.GetContainer() != aRootElement && 3176 endPosition.IsImmediatelyAfterOpenTag()) { 3177 if (endPosition.GetContainer()->HasChildren()) { 3178 // When the end node has some children, move the end position to before 3179 // the open tag of its first child. 3180 nsIContent* const firstChild = 3181 endPosition.GetContainer()->GetFirstChild(); 3182 if (NS_WARN_IF(!firstChild)) { 3183 return NS_ERROR_FAILURE; 3184 } 3185 endPosition = RawNodePosition::Before(*firstChild); 3186 } else { 3187 // When the end node is empty, move the end position after the node. 3188 if (NS_WARN_IF(!endPosition.GetContainer()->IsContent())) { 3189 return NS_ERROR_FAILURE; 3190 } 3191 nsIContent* const parentContent = 3192 endPosition.GetContainer()->GetParent(); 3193 if (NS_WARN_IF(!parentContent)) { 3194 return NS_ERROR_FAILURE; 3195 } 3196 endPosition = 3197 RawNodePosition::After(*endPosition.GetContainer()->AsContent()); 3198 } 3199 } 3200 3201 if (endPosition.IsSetAndValid()) { 3202 // Offset is within node's length; set end of range to that offset 3203 rv = prevSimpleRange.SetEnd(endPosition.AsRaw()); 3204 if (NS_WARN_IF(NS_FAILED(rv))) { 3205 return rv; 3206 } 3207 rv = preOrderIter.Init(prevSimpleRange.Start().AsRaw(), 3208 prevSimpleRange.End().AsRaw()); 3209 if (NS_WARN_IF(NS_FAILED(rv))) { 3210 return rv; 3211 } 3212 } else if (endPosition.GetContainer() != aRootElement) { 3213 // Offset is past node's length; set end of range to end of node 3214 rv = prevSimpleRange.SetEndAfter(endPosition.GetContainer()); 3215 if (NS_WARN_IF(NS_FAILED(rv))) { 3216 return rv; 3217 } 3218 rv = preOrderIter.Init(prevSimpleRange.Start().AsRaw(), 3219 prevSimpleRange.End().AsRaw()); 3220 if (NS_WARN_IF(NS_FAILED(rv))) { 3221 return rv; 3222 } 3223 } else { 3224 // Offset is past the root node; set end of range to end of root node 3225 rv = preOrderIter.Init(const_cast<Element*>(aRootElement)); 3226 if (NS_WARN_IF(NS_FAILED(rv))) { 3227 return rv; 3228 } 3229 } 3230 } 3231 3232 *aLength = 0; 3233 for (; !preOrderIter.IsDone(); preOrderIter.Next()) { 3234 nsINode* node = preOrderIter.GetCurrentNode(); 3235 if (NS_WARN_IF(!node)) { 3236 break; 3237 } 3238 if (!node->IsContent()) { 3239 continue; 3240 } 3241 nsIContent* content = node->AsContent(); 3242 3243 if (const Text* textNode = Text::FromNode(content)) { 3244 // Note: our range always starts from offset 0 3245 if (node == endPosition.GetContainer()) { 3246 // NOTE: We should have an offset here, as endPosition.GetContainer() is 3247 // a nsINode::eTEXT, which always has an offset. 3248 *aLength += GetTextLength( 3249 *textNode, aLineBreakType, 3250 *endPosition.Offset( 3251 RawNodePosition::OffsetFilter::kValidOrInvalidOffsets)); 3252 } else { 3253 *aLength += GetTextLength(*textNode, aLineBreakType); 3254 } 3255 } else if (ShouldBreakLineBefore(*content, aRootElement)) { 3256 // If the start position is start of this node but doesn't include the 3257 // open tag, don't append the line break length. 3258 if (node == aStartPosition.GetContainer() && 3259 !aStartPosition.IsBeforeOpenTag()) { 3260 continue; 3261 } 3262 // If the end position is before the open tag, don't append the line 3263 // break length. 3264 if (node == endPosition.GetContainer() && endPosition.IsBeforeOpenTag()) { 3265 continue; 3266 } 3267 *aLength += GetBRLength(aLineBreakType); 3268 } 3269 } 3270 return NS_OK; 3271 } 3272 3273 template <typename SimpleRangeType> 3274 nsresult ContentEventHandler::GetStartOffset( 3275 const SimpleRangeType& aSimpleRange, uint32_t* aOffset, 3276 LineBreakType aLineBreakType) { 3277 // To match the "no skip start" hack in ContentIterator::Init, when range 3278 // offset is 0 and the range node is not a container, we have to assume the 3279 // range _includes_ the node, which means the start offset should _not_ 3280 // include the node. 3281 // 3282 // For example, for this content: <br>abc, and range (<br>, 0)-("abc", 1), the 3283 // range includes the linebreak from <br>, so the start offset should _not_ 3284 // include <br>, and the start offset should be 0. 3285 // 3286 // However, for this content: <p/>abc, and range (<p>, 0)-("abc", 1), the 3287 // range does _not_ include the linebreak from <p> because <p> is a container, 3288 // so the start offset _should_ include <p>, and the start offset should be 1. 3289 3290 nsINode* startNode = aSimpleRange.GetStartContainer(); 3291 bool startIsContainer = true; 3292 if (startNode->IsHTMLElement()) { 3293 nsAtom* name = startNode->NodeInfo()->NameAtom(); 3294 startIsContainer = 3295 nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name)); 3296 } 3297 RawNodePosition startPos(startNode, aSimpleRange.StartOffset()); 3298 startPos.mAfterOpenTag = startIsContainer; 3299 return GetFlatTextLengthInRange(RawNodePosition(mRootElement, 0u), startPos, 3300 mRootElement, aOffset, aLineBreakType); 3301 } 3302 3303 nsresult ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode( 3304 SimpleRange& aSimpleRange) { 3305 MOZ_ASSERT(aSimpleRange.Collapsed()); 3306 3307 if (!aSimpleRange.Collapsed()) { 3308 return NS_ERROR_INVALID_ARG; 3309 } 3310 3311 const RangeBoundary& startPoint = aSimpleRange.Start(); 3312 if (NS_WARN_IF(!startPoint.IsSet())) { 3313 return NS_ERROR_INVALID_ARG; 3314 } 3315 3316 // If the node does not have children like a text node, we don't need to 3317 // modify aSimpleRange. 3318 if (!startPoint.GetContainer()->HasChildren()) { 3319 return NS_OK; 3320 } 3321 3322 // If the container is not a text node but it has a text node at the offset, 3323 // we should adjust the range into the text node. 3324 // NOTE: This is emulating similar situation of EditorBase. 3325 if (startPoint.IsStartOfContainer()) { 3326 // If the range is the start of the container, adjusted the range to the 3327 // start of the first child. 3328 if (!startPoint.GetContainer()->GetFirstChild()->IsText()) { 3329 return NS_OK; 3330 } 3331 nsresult rv = aSimpleRange.CollapseTo( 3332 RawRangeBoundary(startPoint.GetContainer()->GetFirstChild(), 0u)); 3333 if (NS_WARN_IF(NS_FAILED(rv))) { 3334 return rv; 3335 } 3336 return NS_OK; 3337 } 3338 3339 if (!startPoint.IsSetAndValid()) { 3340 return NS_OK; 3341 } 3342 3343 // If start of the range is next to a child node, adjust the range to the 3344 // end of the previous child (i.e., startPoint.Ref()). 3345 if (!startPoint.Ref()->IsText()) { 3346 return NS_OK; 3347 } 3348 nsresult rv = aSimpleRange.CollapseTo( 3349 RawRangeBoundary(startPoint.Ref(), startPoint.Ref()->Length())); 3350 if (NS_WARN_IF(NS_FAILED(rv))) { 3351 return rv; 3352 } 3353 return NS_OK; 3354 } 3355 3356 nsresult ContentEventHandler::ConvertToRootRelativeOffset(nsIFrame* aFrame, 3357 nsRect& aRect) { 3358 NS_ASSERTION(aFrame, "aFrame must not be null"); 3359 3360 nsPresContext* thisPC = aFrame->PresContext(); 3361 nsPresContext* rootPC = thisPC->GetRootPresContext(); 3362 if (NS_WARN_IF(!rootPC)) { 3363 return NS_ERROR_FAILURE; 3364 } 3365 nsIFrame* rootFrame = rootPC->PresShell()->GetRootFrame(); 3366 if (NS_WARN_IF(!rootFrame)) { 3367 return NS_ERROR_FAILURE; 3368 } 3369 3370 aRect = nsLayoutUtils::TransformFrameRectToAncestor(aFrame, aRect, rootFrame); 3371 3372 // TransformFrameRectToAncestor returned the rect in the ancestor's appUnits, 3373 // but we want it in aFrame's units (in case of different full-zoom factors), 3374 // so convert back. 3375 aRect = aRect.ScaleToOtherAppUnitsRoundOut(rootPC->AppUnitsPerDevPixel(), 3376 thisPC->AppUnitsPerDevPixel()); 3377 3378 return NS_OK; 3379 } 3380 3381 static void AdjustRangeForSelection(const Element* aRootElement, 3382 nsINode** aNode, 3383 Maybe<uint32_t>* aNodeOffset) { 3384 nsINode* node = *aNode; 3385 Maybe<uint32_t> nodeOffset = *aNodeOffset; 3386 if (aRootElement == node || NS_WARN_IF(!node->GetParent()) || 3387 !node->IsText()) { 3388 return; 3389 } 3390 3391 // When the offset is at the end of the text node, set it to after the 3392 // text node, to make sure the caret is drawn on a new line when the last 3393 // character of the text node is '\n' in <textarea>. 3394 const uint32_t textLength = node->AsContent()->TextLength(); 3395 MOZ_ASSERT(nodeOffset.isNothing() || *nodeOffset <= textLength, 3396 "Offset is past length of text node"); 3397 if (nodeOffset.isNothing() || *nodeOffset != textLength) { 3398 return; 3399 } 3400 3401 Element* rootParentElement = aRootElement->GetParentElement(); 3402 if (NS_WARN_IF(!rootParentElement)) { 3403 return; 3404 } 3405 // If the root node is not an anonymous div of <textarea>, we don't need to 3406 // do this hack. If you did this, ContentEventHandler couldn't distinguish 3407 // if the range includes open tag of the next node in some cases, e.g., 3408 // textNode]<p></p> vs. textNode<p>]</p> 3409 if (!rootParentElement->IsHTMLElement(nsGkAtoms::textarea)) { 3410 return; 3411 } 3412 3413 // If the node is being removed from its parent, it holds the ex-parent, 3414 // but the parent have already removed the child from its child chain. 3415 // Therefore `ComputeIndexOf` may fail, but I don't want to make Beta/Nightly 3416 // crash at accessing `Maybe::operator*` so that here checks `isSome`, but 3417 // crashing only in debug builds may help to debug something complicated 3418 // situation, therefore, `MOZ_ASSERT` is put here. 3419 *aNode = node->GetParent(); 3420 Maybe<uint32_t> index = (*aNode)->ComputeIndexOf(node); 3421 MOZ_ASSERT(index.isSome()); 3422 if (index.isSome()) { 3423 MOZ_ASSERT(*index != UINT32_MAX); 3424 *aNodeOffset = Some(*index + 1u); 3425 } else { 3426 *aNodeOffset = Some(0u); 3427 } 3428 } 3429 3430 nsresult ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent) { 3431 aEvent->mSucceeded = false; 3432 3433 // Get selection to manipulate 3434 // XXX why do we need to get them from ISM? This method should work fine 3435 // without ISM. 3436 nsresult rv = IMEStateManager::GetFocusSelectionAndRootElement( 3437 getter_AddRefs(mSelection), getter_AddRefs(mRootElement)); 3438 if (rv != NS_ERROR_NOT_AVAILABLE) { 3439 NS_ENSURE_SUCCESS(rv, rv); 3440 } else { 3441 rv = Init(aEvent); 3442 NS_ENSURE_SUCCESS(rv, rv); 3443 } 3444 3445 // Get range from offset and length 3446 nsINode* startNode = nullptr; 3447 nsINode* endNode = nullptr; 3448 Maybe<uint32_t> startNodeOffset; 3449 Maybe<uint32_t> endNodeOffset; 3450 { 3451 Result<UnsafeDOMRangeAndAdjustedOffsetInFlattenedText, nsresult> 3452 domRangeAndAdjustedOffsetOrError = 3453 ConvertFlatTextOffsetToUnsafeDOMRange( 3454 aEvent->mOffset, aEvent->mLength, GetLineBreakType(aEvent), 3455 aEvent->mExpandToClusterBoundary); 3456 if (MOZ_UNLIKELY(domRangeAndAdjustedOffsetOrError.isErr())) { 3457 NS_WARNING( 3458 "ContentEventHandler::ConvertFlatTextOffsetToDOMRangeBase() failed"); 3459 return domRangeAndAdjustedOffsetOrError.unwrapErr(); 3460 } 3461 const UnsafeDOMRangeAndAdjustedOffsetInFlattenedText 3462 domRangeAndAdjustedOffset = domRangeAndAdjustedOffsetOrError.unwrap(); 3463 startNode = domRangeAndAdjustedOffset.mRange.GetStartContainer(); 3464 endNode = domRangeAndAdjustedOffset.mRange.GetEndContainer(); 3465 startNodeOffset = Some(domRangeAndAdjustedOffset.mRange.StartOffset()); 3466 endNodeOffset = Some(domRangeAndAdjustedOffset.mRange.EndOffset()); 3467 AdjustRangeForSelection(mRootElement, &startNode, &startNodeOffset); 3468 AdjustRangeForSelection(mRootElement, &endNode, &endNodeOffset); 3469 if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode) || 3470 NS_WARN_IF(startNodeOffset.isNothing()) || 3471 NS_WARN_IF(endNodeOffset.isNothing())) { 3472 return NS_ERROR_UNEXPECTED; 3473 } 3474 } 3475 3476 if (aEvent->mReversed) { 3477 nsCOMPtr<nsINode> startNodeStrong(startNode); 3478 nsCOMPtr<nsINode> endNodeStrong(endNode); 3479 ErrorResult error; 3480 MOZ_KnownLive(mSelection) 3481 ->SetBaseAndExtentInLimiter(*endNodeStrong, *endNodeOffset, 3482 *startNodeStrong, *startNodeOffset, error); 3483 if (NS_WARN_IF(error.Failed())) { 3484 return error.StealNSResult(); 3485 } 3486 } else { 3487 nsCOMPtr<nsINode> startNodeStrong(startNode); 3488 nsCOMPtr<nsINode> endNodeStrong(endNode); 3489 ErrorResult error; 3490 MOZ_KnownLive(mSelection) 3491 ->SetBaseAndExtentInLimiter(*startNodeStrong, *startNodeOffset, 3492 *endNodeStrong, *endNodeOffset, error); 3493 if (NS_WARN_IF(error.Failed())) { 3494 return error.StealNSResult(); 3495 } 3496 } 3497 3498 // `ContentEventHandler` is a `MOZ_STACK_CLASS`, so `mSelection` is known to 3499 // be alive. 3500 MOZ_KnownLive(mSelection) 3501 ->ScrollIntoView(nsISelectionController::SELECTION_FOCUS_REGION); 3502 aEvent->mSucceeded = true; 3503 return NS_OK; 3504 } 3505 3506 nsRect ContentEventHandler::FrameRelativeRect::RectRelativeTo( 3507 nsIFrame* aDestFrame) const { 3508 if (!mBaseFrame || NS_WARN_IF(!aDestFrame)) { 3509 return nsRect(); 3510 } 3511 3512 if (NS_WARN_IF(aDestFrame->PresContext() != mBaseFrame->PresContext())) { 3513 return nsRect(); 3514 } 3515 3516 if (aDestFrame == mBaseFrame) { 3517 return mRect; 3518 } 3519 3520 nsIFrame* rootFrame = mBaseFrame->PresShell()->GetRootFrame(); 3521 nsRect baseFrameRectInRootFrame = nsLayoutUtils::TransformFrameRectToAncestor( 3522 mBaseFrame, nsRect(), rootFrame); 3523 nsRect destFrameRectInRootFrame = nsLayoutUtils::TransformFrameRectToAncestor( 3524 aDestFrame, nsRect(), rootFrame); 3525 nsPoint difference = 3526 destFrameRectInRootFrame.TopLeft() - baseFrameRectInRootFrame.TopLeft(); 3527 return mRect - difference; 3528 } 3529 3530 } // namespace mozilla