LCOV - code coverage report
Current view: top level - dom/events - ContentEventHandler.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1457 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 61 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "mozilla/IMEStateManager.h"
       9             : #include "mozilla/TextComposition.h"
      10             : #include "mozilla/TextEvents.h"
      11             : #include "mozilla/dom/Element.h"
      12             : #include "mozilla/dom/HTMLUnknownElement.h"
      13             : #include "mozilla/dom/Selection.h"
      14             : #include "nsCaret.h"
      15             : #include "nsCOMPtr.h"
      16             : #include "nsContentUtils.h"
      17             : #include "nsCopySupport.h"
      18             : #include "nsFocusManager.h"
      19             : #include "nsFontMetrics.h"
      20             : #include "nsFrameSelection.h"
      21             : #include "nsIContentIterator.h"
      22             : #include "nsIPresShell.h"
      23             : #include "nsISelection.h"
      24             : #include "nsIFrame.h"
      25             : #include "nsIObjectFrame.h"
      26             : #include "nsLayoutUtils.h"
      27             : #include "nsPresContext.h"
      28             : #include "nsQueryObject.h"
      29             : #include "nsRange.h"
      30             : #include "nsTextFragment.h"
      31             : #include "nsTextFrame.h"
      32             : #include "nsView.h"
      33             : 
      34             : #include <algorithm>
      35             : 
      36             : namespace mozilla {
      37             : 
      38             : using namespace dom;
      39             : using namespace widget;
      40             : 
      41             : /******************************************************************/
      42             : /* ContentEventHandler                                            */
      43             : /******************************************************************/
      44             : 
      45             : // NOTE
      46             : //
      47             : // ContentEventHandler *creates* ranges as following rules:
      48             : // 1. Start of range:
      49             : //   1.1. Cases: [textNode or text[Node or textNode[
      50             : //        When text node is start of a range, start node is the text node and
      51             : //        start offset is any number between 0 and the length of the text.
      52             : //   1.2. Case: [<element>:
      53             : //        When start of an element node is start of a range, start node is
      54             : //        parent of the element and start offset is the element's index in the
      55             : //        parent.
      56             : //   1.3. Case: <element/>[
      57             : //        When after an empty element node is start of a range, start node is
      58             : //        parent of the element and start offset is the element's index in the
      59             : //        parent + 1.
      60             : //   1.4. Case: <element>[
      61             : //        When start of a non-empty element is start of a range, start node is
      62             : //        the element and start offset is 0.
      63             : //   1.5. Case: <root>[
      64             : //        When start of a range is 0 and there are no nodes causing text,
      65             : //        start node is the root node and start offset is 0.
      66             : //   1.6. Case: [</root>
      67             : //        When start of a range is out of bounds, start node is the root node
      68             : //        and start offset is number of the children.
      69             : // 2. End of range:
      70             : //   2.1. Cases: ]textNode or text]Node or textNode]
      71             : //        When a text node is end of a range, end node is the text node and
      72             : //        end offset is any number between 0 and the length of the text.
      73             : //   2.2. Case: ]<element>
      74             : //        When before an element node (meaning before the open tag of the
      75             : //        element) is end of a range, end node is previous node causing text.
      76             : //        Note that this case shouldn't be handled directly.  If rule 2.1 and
      77             : //        2.3 are handled correctly, the loop with nsContentIterator shouldn't
      78             : //        reach the element node since the loop should've finished already at
      79             : //        handling the last node which caused some text.
      80             : //   2.3. Case: <element>]
      81             : //        When a line break is caused before a non-empty element node and it's
      82             : //        end of a range, end node is the element and end offset is 0.
      83             : //        (i.e., including open tag of the element)
      84             : //   2.4. Cases: <element/>]
      85             : //        When after an empty element node is end of a range, end node is
      86             : //        parent of the element node and end offset is the element's index in
      87             : //        the parent + 1.  (i.e., including close tag of the element or empty
      88             : //        element)
      89             : //   2.5. Case: ]</root>
      90             : //        When end of a range is out of bounds, end node is the root node and
      91             : //        end offset is number of the children.
      92             : //
      93             : // ContentEventHandler *treats* ranges as following additional rules:
      94             : // 1. When the start node is an element node which doesn't have children,
      95             : //    it includes a line break caused before itself (i.e., includes its open
      96             : //    tag).  For example, if start position is { <br>, 0 }, the line break
      97             : //    caused by <br> should be included into the flatten text.
      98             : // 2. When the end node is an element node which doesn't have children,
      99             : //    it includes the end (i.e., includes its close tag except empty element).
     100             : //    Although, currently, any close tags don't cause line break, this also
     101             : //    includes its open tag.  For example, if end position is { <br>, 0 }, the
     102             : //    line break caused by the <br> should be included into the flatten text.
     103             : 
     104           0 : ContentEventHandler::ContentEventHandler(nsPresContext* aPresContext)
     105             :   : mPresContext(aPresContext)
     106             :   , mPresShell(aPresContext->GetPresShell())
     107             :   , mSelection(nullptr)
     108             :   , mFirstSelectedRange(nullptr)
     109           0 :   , mRootContent(nullptr)
     110             : {
     111           0 : }
     112             : 
     113             : nsresult
     114           0 : ContentEventHandler::InitBasic()
     115             : {
     116           0 :   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
     117             : 
     118             :   // If text frame which has overflowing selection underline is dirty,
     119             :   // we need to flush the pending reflow here.
     120           0 :   mPresShell->FlushPendingNotifications(FlushType::Layout);
     121             : 
     122             :   // Flushing notifications can cause mPresShell to be destroyed (bug 577963).
     123           0 :   NS_ENSURE_TRUE(!mPresShell->IsDestroying(), NS_ERROR_FAILURE);
     124             : 
     125           0 :   return NS_OK;
     126             : }
     127             : 
     128             : nsresult
     129           0 : ContentEventHandler::InitRootContent(Selection* aNormalSelection)
     130             : {
     131           0 :   MOZ_ASSERT(aNormalSelection);
     132             : 
     133             :   // Root content should be computed with normal selection because normal
     134             :   // selection is typically has at least one range but the other selections
     135             :   // not so.  If there is a range, computing its root is easy, but if
     136             :   // there are no ranges, we need to use ancestor limit instead.
     137           0 :   MOZ_ASSERT(aNormalSelection->Type() == SelectionType::eNormal);
     138             : 
     139           0 :   if (!aNormalSelection->RangeCount()) {
     140             :     // If there is no selection range, we should compute the selection root
     141             :     // from ancestor limiter or root content of the document.
     142             :     nsresult rv =
     143           0 :       aNormalSelection->GetAncestorLimiter(getter_AddRefs(mRootContent));
     144           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     145           0 :       return NS_ERROR_FAILURE;
     146             :     }
     147           0 :     if (!mRootContent) {
     148           0 :       mRootContent = mPresShell->GetDocument()->GetRootElement();
     149           0 :       if (NS_WARN_IF(!mRootContent)) {
     150           0 :         return NS_ERROR_NOT_AVAILABLE;
     151             :       }
     152             :     }
     153           0 :     return NS_OK;
     154             :   }
     155             : 
     156           0 :   RefPtr<nsRange> range(aNormalSelection->GetRangeAt(0));
     157           0 :   if (NS_WARN_IF(!range)) {
     158           0 :     return NS_ERROR_UNEXPECTED;
     159             :   }
     160             : 
     161             :   // If there is a selection, we should retrieve the selection root from
     162             :   // the range since when the window is inactivated, the ancestor limiter
     163             :   // of selection was cleared by blur event handler of EditorBase but the
     164             :   // selection range still keeps storing the nodes.  If the active element of
     165             :   // the deactive window is <input> or <textarea>, we can compute the
     166             :   // selection root from them.
     167           0 :   nsINode* startNode = range->GetStartContainer();
     168           0 :   nsINode* endNode = range->GetEndContainer();
     169           0 :   if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
     170           0 :     return NS_ERROR_FAILURE;
     171             :   }
     172             : 
     173             :   // See bug 537041 comment 5, the range could have removed node.
     174           0 :   if (NS_WARN_IF(startNode->GetUncomposedDoc() != mPresShell->GetDocument())) {
     175           0 :     return NS_ERROR_FAILURE;
     176             :   }
     177             : 
     178           0 :   NS_ASSERTION(startNode->GetUncomposedDoc() == endNode->GetUncomposedDoc(),
     179             :                "firstNormalSelectionRange crosses the document boundary");
     180             : 
     181           0 :   mRootContent = startNode->GetSelectionRootContent(mPresShell);
     182           0 :   if (NS_WARN_IF(!mRootContent)) {
     183           0 :     return NS_ERROR_FAILURE;
     184             :   }
     185             : 
     186           0 :   return NS_OK;
     187             : }
     188             : 
     189             : nsresult
     190           0 : ContentEventHandler::InitCommon(SelectionType aSelectionType)
     191             : {
     192           0 :   if (mSelection && mSelection->Type() == aSelectionType) {
     193           0 :     return NS_OK;
     194             :   }
     195             : 
     196           0 :   mSelection = nullptr;
     197           0 :   mFirstSelectedRange = nullptr;
     198           0 :   mRootContent = nullptr;
     199             : 
     200           0 :   nsresult rv = InitBasic();
     201           0 :   NS_ENSURE_SUCCESS(rv, rv);
     202             : 
     203             :   nsCOMPtr<nsISelectionController> selectionController =
     204           0 :     mPresShell->GetSelectionControllerForFocusedContent();
     205           0 :   if (NS_WARN_IF(!selectionController)) {
     206           0 :     return NS_ERROR_NOT_AVAILABLE;
     207             :   }
     208           0 :   nsCOMPtr<nsISelection> selection;
     209           0 :   rv = selectionController->GetSelection(ToRawSelectionType(aSelectionType),
     210           0 :                                          getter_AddRefs(selection));
     211           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     212           0 :     return NS_ERROR_UNEXPECTED;
     213             :   }
     214             : 
     215           0 :   mSelection = static_cast<Selection*>(selection.get());
     216           0 :   if (NS_WARN_IF(!mSelection)) {
     217           0 :     return NS_ERROR_NOT_AVAILABLE;
     218             :   }
     219             : 
     220           0 :   RefPtr<Selection> normalSelection;
     221           0 :   if (mSelection->Type() == SelectionType::eNormal) {
     222           0 :     normalSelection = mSelection;
     223             :   } else {
     224           0 :     nsCOMPtr<nsISelection> domSelection;
     225             :     nsresult rv =
     226           0 :       selectionController->GetSelection(
     227             :                              nsISelectionController::SELECTION_NORMAL,
     228           0 :                              getter_AddRefs(domSelection));
     229           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     230           0 :       return NS_ERROR_UNEXPECTED;
     231             :     }
     232           0 :     if (NS_WARN_IF(!domSelection)) {
     233           0 :       return NS_ERROR_NOT_AVAILABLE;
     234             :     }
     235           0 :     normalSelection = domSelection->AsSelection();
     236           0 :     if (NS_WARN_IF(!normalSelection)) {
     237           0 :       return NS_ERROR_NOT_AVAILABLE;
     238             :     }
     239             :   }
     240             : 
     241           0 :   rv = InitRootContent(normalSelection);
     242           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     243           0 :     return rv;
     244             :   }
     245             : 
     246           0 :   if (mSelection->RangeCount()) {
     247           0 :     mFirstSelectedRange = mSelection->GetRangeAt(0);
     248           0 :     if (NS_WARN_IF(!mFirstSelectedRange)) {
     249           0 :       return NS_ERROR_UNEXPECTED;
     250             :     }
     251           0 :     return NS_OK;
     252             :   }
     253             : 
     254             :   // Even if there are no selection ranges, it's usual case if aSelectionType
     255             :   // is a special selection.
     256           0 :   if (aSelectionType != SelectionType::eNormal) {
     257           0 :     MOZ_ASSERT(!mFirstSelectedRange);
     258           0 :     return NS_OK;
     259             :   }
     260             : 
     261             :   // But otherwise, we need to assume that there is a selection range at the
     262             :   // beginning of the root content if aSelectionType is eNormal.
     263           0 :   rv = nsRange::CreateRange(mRootContent, 0, mRootContent, 0,
     264           0 :                             getter_AddRefs(mFirstSelectedRange));
     265           0 :   if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!mFirstSelectedRange)) {
     266           0 :     return NS_ERROR_UNEXPECTED;
     267             :   }
     268           0 :   return NS_OK;
     269             : }
     270             : 
     271             : nsresult
     272           0 : ContentEventHandler::Init(WidgetQueryContentEvent* aEvent)
     273             : {
     274           0 :   NS_ASSERTION(aEvent, "aEvent must not be null");
     275           0 :   MOZ_ASSERT(aEvent->mMessage == eQuerySelectedText ||
     276             :              aEvent->mInput.mSelectionType == SelectionType::eNormal);
     277             : 
     278           0 :   if (NS_WARN_IF(!aEvent->mInput.IsValidOffset()) ||
     279           0 :       NS_WARN_IF(!aEvent->mInput.IsValidEventMessage(aEvent->mMessage))) {
     280           0 :     return NS_ERROR_FAILURE;
     281             :   }
     282             : 
     283             :   // Note that we should ignore WidgetQueryContentEvent::Input::mSelectionType
     284             :   // if the event isn't eQuerySelectedText.
     285             :   SelectionType selectionType =
     286           0 :     aEvent->mMessage == eQuerySelectedText ? aEvent->mInput.mSelectionType :
     287           0 :                                              SelectionType::eNormal;
     288           0 :   if (NS_WARN_IF(selectionType == SelectionType::eNone)) {
     289           0 :     return NS_ERROR_FAILURE;
     290             :   }
     291             : 
     292           0 :   nsresult rv = InitCommon(selectionType);
     293           0 :   NS_ENSURE_SUCCESS(rv, rv);
     294             : 
     295             :   // Be aware, WidgetQueryContentEvent::mInput::mOffset should be made absolute
     296             :   // offset before sending it to ContentEventHandler because querying selection
     297             :   // every time may be expensive.  So, if the caller caches selection, it
     298             :   // should initialize the event with the cached value.
     299           0 :   if (aEvent->mInput.mRelativeToInsertionPoint) {
     300           0 :     MOZ_ASSERT(selectionType == SelectionType::eNormal);
     301             :     RefPtr<TextComposition> composition =
     302           0 :       IMEStateManager::GetTextCompositionFor(aEvent->mWidget);
     303           0 :     if (composition) {
     304           0 :       uint32_t compositionStart = composition->NativeOffsetOfStartComposition();
     305           0 :       if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(compositionStart))) {
     306           0 :         return NS_ERROR_FAILURE;
     307             :       }
     308             :     } else {
     309           0 :       LineBreakType lineBreakType = GetLineBreakType(aEvent);
     310           0 :       uint32_t selectionStart = 0;
     311           0 :       rv = GetStartOffset(mFirstSelectedRange, &selectionStart, lineBreakType);
     312           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
     313           0 :         return NS_ERROR_FAILURE;
     314             :       }
     315           0 :       if (NS_WARN_IF(!aEvent->mInput.MakeOffsetAbsolute(selectionStart))) {
     316           0 :         return NS_ERROR_FAILURE;
     317             :       }
     318             :     }
     319             :   }
     320             : 
     321           0 :   aEvent->mSucceeded = false;
     322             : 
     323           0 :   aEvent->mReply.mContentsRoot = mRootContent.get();
     324             : 
     325           0 :   aEvent->mReply.mHasSelection = !mSelection->IsCollapsed();
     326             : 
     327           0 :   nsRect r;
     328           0 :   nsIFrame* frame = nsCaret::GetGeometry(mSelection, &r);
     329           0 :   if (!frame) {
     330           0 :     frame = mRootContent->GetPrimaryFrame();
     331           0 :     if (NS_WARN_IF(!frame)) {
     332           0 :       return NS_ERROR_FAILURE;
     333             :     }
     334             :   }
     335           0 :   aEvent->mReply.mFocusedWidget = frame->GetNearestWidget();
     336             : 
     337           0 :   return NS_OK;
     338             : }
     339             : 
     340             : nsresult
     341           0 : ContentEventHandler::Init(WidgetSelectionEvent* aEvent)
     342             : {
     343           0 :   NS_ASSERTION(aEvent, "aEvent must not be null");
     344             : 
     345           0 :   nsresult rv = InitCommon();
     346           0 :   NS_ENSURE_SUCCESS(rv, rv);
     347             : 
     348           0 :   aEvent->mSucceeded = false;
     349             : 
     350           0 :   return NS_OK;
     351             : }
     352             : 
     353             : nsIContent*
     354           0 : ContentEventHandler::GetFocusedContent()
     355             : {
     356           0 :   nsIDocument* doc = mPresShell->GetDocument();
     357           0 :   if (!doc) {
     358           0 :     return nullptr;
     359             :   }
     360           0 :   nsCOMPtr<nsPIDOMWindowOuter> window = doc->GetWindow();
     361           0 :   nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
     362           0 :   return nsFocusManager::GetFocusedDescendant(window, true,
     363           0 :                                               getter_AddRefs(focusedWindow));
     364             : }
     365             : 
     366             : bool
     367           0 : ContentEventHandler::IsPlugin(nsIContent* aContent)
     368             : {
     369           0 :   return aContent &&
     370           0 :          aContent->GetDesiredIMEState().mEnabled == IMEState::PLUGIN;
     371             : }
     372             : 
     373             : nsresult
     374           0 : ContentEventHandler::QueryContentRect(nsIContent* aContent,
     375             :                                       WidgetQueryContentEvent* aEvent)
     376             : {
     377           0 :   NS_PRECONDITION(aContent, "aContent must not be null");
     378             : 
     379           0 :   nsIFrame* frame = aContent->GetPrimaryFrame();
     380           0 :   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
     381             : 
     382             :   // get rect for first frame
     383           0 :   nsRect resultRect(nsPoint(0, 0), frame->GetRect().Size());
     384           0 :   nsresult rv = ConvertToRootRelativeOffset(frame, resultRect);
     385           0 :   NS_ENSURE_SUCCESS(rv, rv);
     386             : 
     387             :   // account for any additional frames
     388           0 :   while ((frame = frame->GetNextContinuation()) != nullptr) {
     389           0 :     nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size());
     390           0 :     rv = ConvertToRootRelativeOffset(frame, frameRect);
     391           0 :     NS_ENSURE_SUCCESS(rv, rv);
     392           0 :     resultRect.UnionRect(resultRect, frameRect);
     393             :   }
     394             : 
     395             :   aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
     396           0 :       resultRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
     397             :   // Returning empty rect may cause native IME confused, let's make sure to
     398             :   // return non-empty rect.
     399           0 :   EnsureNonEmptyRect(aEvent->mReply.mRect);
     400           0 :   aEvent->mSucceeded = true;
     401             : 
     402           0 :   return NS_OK;
     403             : }
     404             : 
     405             : // Editor places a bogus BR node under its root content if the editor doesn't
     406             : // have any text. This happens even for single line editors.
     407             : // When we get text content and when we change the selection,
     408             : // we don't want to include the bogus BRs at the end.
     409           0 : static bool IsContentBR(nsIContent* aContent)
     410             : {
     411           0 :   return aContent->IsHTMLElement(nsGkAtoms::br) &&
     412           0 :          !aContent->AttrValueIs(kNameSpaceID_None,
     413             :                                 nsGkAtoms::type,
     414             :                                 nsGkAtoms::moz,
     415           0 :                                 eIgnoreCase) &&
     416           0 :          !aContent->AttrValueIs(kNameSpaceID_None,
     417             :                                 nsGkAtoms::mozeditorbogusnode,
     418             :                                 nsGkAtoms::_true,
     419           0 :                                 eIgnoreCase);
     420             : }
     421             : 
     422           0 : static bool IsMozBR(nsIContent* aContent)
     423             : {
     424           0 :   return aContent->IsHTMLElement(nsGkAtoms::br) && !IsContentBR(aContent);
     425             : }
     426             : 
     427           0 : static void ConvertToNativeNewlines(nsString& aString)
     428             : {
     429             : #if defined(XP_WIN)
     430             :   aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
     431             : #endif
     432           0 : }
     433             : 
     434           0 : static void AppendString(nsAString& aString, nsIContent* aContent)
     435             : {
     436           0 :   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
     437             :                "aContent is not a text node!");
     438           0 :   const nsTextFragment* text = aContent->GetText();
     439           0 :   if (!text) {
     440           0 :     return;
     441             :   }
     442           0 :   text->AppendTo(aString);
     443             : }
     444             : 
     445           0 : static void AppendSubString(nsAString& aString, nsIContent* aContent,
     446             :                             uint32_t aXPOffset, uint32_t aXPLength)
     447             : {
     448           0 :   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
     449             :                "aContent is not a text node!");
     450           0 :   const nsTextFragment* text = aContent->GetText();
     451           0 :   if (!text) {
     452           0 :     return;
     453             :   }
     454           0 :   text->AppendTo(aString, int32_t(aXPOffset), int32_t(aXPLength));
     455             : }
     456             : 
     457             : #if defined(XP_WIN)
     458             : static uint32_t CountNewlinesInXPLength(nsIContent* aContent,
     459             :                                         uint32_t aXPLength)
     460             : {
     461             :   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
     462             :                "aContent is not a text node!");
     463             :   const nsTextFragment* text = aContent->GetText();
     464             :   if (!text) {
     465             :     return 0;
     466             :   }
     467             :   // For automated tests, we should abort on debug build.
     468             :   MOZ_ASSERT(aXPLength == UINT32_MAX || aXPLength <= text->GetLength(),
     469             :              "aXPLength is out-of-bounds");
     470             :   const uint32_t length = std::min(aXPLength, text->GetLength());
     471             :   uint32_t newlines = 0;
     472             :   for (uint32_t i = 0; i < length; ++i) {
     473             :     if (text->CharAt(i) == '\n') {
     474             :       ++newlines;
     475             :     }
     476             :   }
     477             :   return newlines;
     478             : }
     479             : 
     480             : static uint32_t CountNewlinesInNativeLength(nsIContent* aContent,
     481             :                                             uint32_t aNativeLength)
     482             : {
     483             :   NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
     484             :                "aContent is not a text node!");
     485             :   const nsTextFragment* text = aContent->GetText();
     486             :   if (!text) {
     487             :     return 0;
     488             :   }
     489             :   // For automated tests, we should abort on debug build.
     490             :   MOZ_ASSERT(
     491             :     (aNativeLength == UINT32_MAX || aNativeLength <= text->GetLength() * 2),
     492             :     "aNativeLength is unexpected value");
     493             :   const uint32_t xpLength = text->GetLength();
     494             :   uint32_t newlines = 0;
     495             :   for (uint32_t i = 0, nativeOffset = 0;
     496             :        i < xpLength && nativeOffset < aNativeLength;
     497             :        ++i, ++nativeOffset) {
     498             :     // For automated tests, we should abort on debug build.
     499             :     MOZ_ASSERT(i < text->GetLength(), "i is out-of-bounds");
     500             :     if (text->CharAt(i) == '\n') {
     501             :       ++newlines;
     502             :       ++nativeOffset;
     503             :     }
     504             :   }
     505             :   return newlines;
     506             : }
     507             : #endif
     508             : 
     509             : /* static */ uint32_t
     510           0 : ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
     511             :                                          uint32_t aStartOffset,
     512             :                                          uint32_t aEndOffset)
     513             : {
     514           0 :   MOZ_ASSERT(aEndOffset >= aStartOffset,
     515             :              "aEndOffset must be equals or larger than aStartOffset");
     516           0 :   if (NS_WARN_IF(!aContent->IsNodeOfType(nsINode::eTEXT))) {
     517           0 :     return 0;
     518             :   }
     519           0 :   if (aStartOffset == aEndOffset) {
     520           0 :     return 0;
     521             :   }
     522           0 :   return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aEndOffset) -
     523           0 :            GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aStartOffset);
     524             : }
     525             : 
     526             : /* static */ uint32_t
     527           0 : ContentEventHandler::GetNativeTextLength(nsIContent* aContent,
     528             :                                          uint32_t aMaxLength)
     529             : {
     530           0 :   if (NS_WARN_IF(!aContent->IsNodeOfType(nsINode::eTEXT))) {
     531           0 :     return 0;
     532             :   }
     533           0 :   return GetTextLength(aContent, LINE_BREAK_TYPE_NATIVE, aMaxLength);
     534             : }
     535             : 
     536             : /* static */ uint32_t
     537           0 : ContentEventHandler::GetNativeTextLengthBefore(nsIContent* aContent,
     538             :                                                nsINode* aRootNode)
     539             : {
     540           0 :   if (NS_WARN_IF(aContent->IsNodeOfType(nsINode::eTEXT))) {
     541           0 :     return 0;
     542             :   }
     543           0 :   return ShouldBreakLineBefore(aContent, aRootNode) ?
     544           0 :            GetBRLength(LINE_BREAK_TYPE_NATIVE) : 0;
     545             : }
     546             : 
     547             : /* static inline */ uint32_t
     548           0 : ContentEventHandler::GetBRLength(LineBreakType aLineBreakType)
     549             : {
     550             : #if defined(XP_WIN)
     551             :   // Length of \r\n
     552             :   return (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ? 2 : 1;
     553             : #else
     554           0 :   return 1;
     555             : #endif
     556             : }
     557             : 
     558             : /* static */ uint32_t
     559           0 : ContentEventHandler::GetTextLength(nsIContent* aContent,
     560             :                                    LineBreakType aLineBreakType,
     561             :                                    uint32_t aMaxLength)
     562             : {
     563           0 :   MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
     564             : 
     565             :   uint32_t textLengthDifference =
     566             : #if defined(XP_WIN)
     567             :     // On Windows, the length of a native newline ("\r\n") is twice the length
     568             :     // of the XP newline ("\n"), so XP length is equal to the length of the
     569             :     // native offset plus the number of newlines encountered in the string.
     570             :     (aLineBreakType == LINE_BREAK_TYPE_NATIVE) ?
     571             :       CountNewlinesInXPLength(aContent, aMaxLength) : 0;
     572             : #else
     573             :     // On other platforms, the native and XP newlines are the same.
     574           0 :     0;
     575             : #endif
     576             : 
     577           0 :   const nsTextFragment* text = aContent->GetText();
     578           0 :   if (!text) {
     579           0 :     return 0;
     580             :   }
     581           0 :   uint32_t length = std::min(text->GetLength(), aMaxLength);
     582           0 :   return length + textLengthDifference;
     583             : }
     584             : 
     585           0 : static uint32_t ConvertToXPOffset(nsIContent* aContent, uint32_t aNativeOffset)
     586             : {
     587             : #if defined(XP_WIN)
     588             :   // On Windows, the length of a native newline ("\r\n") is twice the length of
     589             :   // the XP newline ("\n"), so XP offset is equal to the length of the native
     590             :   // offset minus the number of newlines encountered in the string.
     591             :   return aNativeOffset - CountNewlinesInNativeLength(aContent, aNativeOffset);
     592             : #else
     593             :   // On other platforms, the native and XP newlines are the same.
     594           0 :   return aNativeOffset;
     595             : #endif
     596             : }
     597             : 
     598             : /* static */ bool
     599           0 : ContentEventHandler::ShouldBreakLineBefore(nsIContent* aContent,
     600             :                                            nsINode* aRootNode)
     601             : {
     602             :   // We don't need to append linebreak at the start of the root element.
     603           0 :   if (aContent == aRootNode) {
     604           0 :     return false;
     605             :   }
     606             : 
     607             :   // If it's not an HTML element (including other markup language's elements),
     608             :   // we shouldn't insert like break before that for now.  Becoming this is a
     609             :   // problem must be edge case.  E.g., when ContentEventHandler is used with
     610             :   // MathML or SVG elements.
     611           0 :   if (!aContent->IsHTMLElement()) {
     612           0 :     return false;
     613             :   }
     614             : 
     615             :   // If the element is <br>, we need to check if the <br> is caused by web
     616             :   // content.  Otherwise, i.e., it's caused by internal reason of Gecko,
     617             :   // it shouldn't be exposed as a line break to flatten text.
     618           0 :   if (aContent->IsHTMLElement(nsGkAtoms::br)) {
     619           0 :     return IsContentBR(aContent);
     620             :   }
     621             : 
     622             :   // Note that ideally, we should refer the style of the primary frame of
     623             :   // aContent for deciding if it's an inline.  However, it's difficult
     624             :   // IMEContentObserver to notify IME of text change caused by style change.
     625             :   // Therefore, currently, we should check only from the tag for now.
     626           0 :   if (aContent->IsAnyOfHTMLElements(nsGkAtoms::a,
     627             :                                     nsGkAtoms::abbr,
     628             :                                     nsGkAtoms::acronym,
     629             :                                     nsGkAtoms::b,
     630             :                                     nsGkAtoms::bdi,
     631             :                                     nsGkAtoms::bdo,
     632             :                                     nsGkAtoms::big,
     633             :                                     nsGkAtoms::cite,
     634             :                                     nsGkAtoms::code,
     635             :                                     nsGkAtoms::data,
     636             :                                     nsGkAtoms::del,
     637             :                                     nsGkAtoms::dfn,
     638             :                                     nsGkAtoms::em,
     639             :                                     nsGkAtoms::font,
     640             :                                     nsGkAtoms::i,
     641             :                                     nsGkAtoms::ins,
     642             :                                     nsGkAtoms::kbd,
     643             :                                     nsGkAtoms::mark,
     644             :                                     nsGkAtoms::s,
     645             :                                     nsGkAtoms::samp,
     646             :                                     nsGkAtoms::small,
     647             :                                     nsGkAtoms::span,
     648             :                                     nsGkAtoms::strike,
     649             :                                     nsGkAtoms::strong,
     650             :                                     nsGkAtoms::sub,
     651             :                                     nsGkAtoms::sup,
     652             :                                     nsGkAtoms::time,
     653             :                                     nsGkAtoms::tt,
     654             :                                     nsGkAtoms::u,
     655             :                                     nsGkAtoms::var)) {
     656           0 :     return false;
     657             :   }
     658             : 
     659             :   // If the element is unknown element, we shouldn't insert line breaks before
     660             :   // it since unknown elements should be ignored.
     661           0 :   RefPtr<HTMLUnknownElement> unknownHTMLElement = do_QueryObject(aContent);
     662           0 :   return !unknownHTMLElement;
     663             : }
     664             : 
     665             : nsresult
     666           0 : ContentEventHandler::GenerateFlatTextContent(nsIContent* aContent,
     667             :                                              nsString& aString,
     668             :                                              LineBreakType aLineBreakType)
     669             : {
     670           0 :   MOZ_ASSERT(aString.IsEmpty());
     671             : 
     672           0 :   RefPtr<nsRange> range = new nsRange(mRootContent);
     673           0 :   ErrorResult rv;
     674           0 :   range->SelectNodeContents(*aContent, rv);
     675           0 :   if (NS_WARN_IF(rv.Failed())) {
     676           0 :     return rv.StealNSResult();
     677             :   }
     678           0 :   return GenerateFlatTextContent(range, aString, aLineBreakType);
     679             : }
     680             : 
     681             : nsresult
     682           0 : ContentEventHandler::GenerateFlatTextContent(nsRange* aRange,
     683             :                                              nsString& aString,
     684             :                                              LineBreakType aLineBreakType)
     685             : {
     686           0 :   MOZ_ASSERT(aString.IsEmpty());
     687             : 
     688           0 :   if (aRange->Collapsed()) {
     689           0 :     return NS_OK;
     690             :   }
     691             : 
     692           0 :   nsINode* startNode = aRange->GetStartContainer();
     693           0 :   nsINode* endNode = aRange->GetEndContainer();
     694           0 :   if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
     695           0 :     return NS_ERROR_FAILURE;
     696             :   }
     697             : 
     698           0 :   if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) {
     699           0 :     nsIContent* content = startNode->AsContent();
     700           0 :     AppendSubString(aString, content, aRange->StartOffset(),
     701           0 :                     aRange->EndOffset() - aRange->StartOffset());
     702           0 :     ConvertToNativeNewlines(aString);
     703           0 :     return NS_OK;
     704             :   }
     705             : 
     706           0 :   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
     707           0 :   nsresult rv = iter->Init(aRange);
     708           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     709           0 :     return rv;
     710             :   }
     711           0 :   for (; !iter->IsDone(); iter->Next()) {
     712           0 :     nsINode* node = iter->GetCurrentNode();
     713           0 :     if (NS_WARN_IF(!node)) {
     714           0 :       break;
     715             :     }
     716           0 :     if (!node->IsContent()) {
     717           0 :       continue;
     718             :     }
     719           0 :     nsIContent* content = node->AsContent();
     720             : 
     721           0 :     if (content->IsNodeOfType(nsINode::eTEXT)) {
     722           0 :       if (content == startNode) {
     723           0 :         AppendSubString(aString, content, aRange->StartOffset(),
     724           0 :                         content->TextLength() - aRange->StartOffset());
     725           0 :       } else if (content == endNode) {
     726           0 :         AppendSubString(aString, content, 0, aRange->EndOffset());
     727             :       } else {
     728           0 :         AppendString(aString, content);
     729             :       }
     730           0 :     } else if (ShouldBreakLineBefore(content, mRootContent)) {
     731           0 :       aString.Append(char16_t('\n'));
     732             :     }
     733             :   }
     734           0 :   if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
     735           0 :     ConvertToNativeNewlines(aString);
     736             :   }
     737           0 :   return NS_OK;
     738             : }
     739             : 
     740             : static FontRange*
     741           0 : AppendFontRange(nsTArray<FontRange>& aFontRanges, uint32_t aBaseOffset)
     742             : {
     743           0 :   FontRange* fontRange = aFontRanges.AppendElement();
     744           0 :   fontRange->mStartOffset = aBaseOffset;
     745           0 :   return fontRange;
     746             : }
     747             : 
     748             : /* static */ uint32_t
     749           0 : ContentEventHandler::GetTextLengthInRange(nsIContent* aContent,
     750             :                                           uint32_t aXPStartOffset,
     751             :                                           uint32_t aXPEndOffset,
     752             :                                           LineBreakType aLineBreakType)
     753             : {
     754           0 :   MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
     755             : 
     756           0 :   return aLineBreakType == LINE_BREAK_TYPE_NATIVE ?
     757             :     GetNativeTextLength(aContent, aXPStartOffset, aXPEndOffset) :
     758           0 :     aXPEndOffset - aXPStartOffset;
     759             : }
     760             : 
     761             : /* static */ void
     762           0 : ContentEventHandler::AppendFontRanges(FontRangeArray& aFontRanges,
     763             :                                       nsIContent* aContent,
     764             :                                       int32_t aBaseOffset,
     765             :                                       int32_t aXPStartOffset,
     766             :                                       int32_t aXPEndOffset,
     767             :                                       LineBreakType aLineBreakType)
     768             : {
     769           0 :   MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT));
     770             : 
     771           0 :   nsIFrame* frame = aContent->GetPrimaryFrame();
     772           0 :   if (!frame) {
     773             :     // It is a non-rendered content, create an empty range for it.
     774           0 :     AppendFontRange(aFontRanges, aBaseOffset);
     775           0 :     return;
     776             :   }
     777             : 
     778           0 :   int32_t baseOffset = aBaseOffset;
     779             : #ifdef DEBUG
     780             :   {
     781           0 :     nsTextFrame* text = do_QueryFrame(frame);
     782           0 :     MOZ_ASSERT(text, "Not a text frame");
     783             :   }
     784             : #endif
     785           0 :   auto* curr = static_cast<nsTextFrame*>(frame);
     786           0 :   while (curr) {
     787           0 :     int32_t frameXPStart = std::max(curr->GetContentOffset(), aXPStartOffset);
     788           0 :     int32_t frameXPEnd = std::min(curr->GetContentEnd(), aXPEndOffset);
     789           0 :     if (frameXPStart >= frameXPEnd) {
     790           0 :       curr = curr->GetNextContinuation();
     791           0 :       continue;
     792             :     }
     793             : 
     794           0 :     gfxSkipCharsIterator iter = curr->EnsureTextRun(nsTextFrame::eInflated);
     795           0 :     gfxTextRun* textRun = curr->GetTextRun(nsTextFrame::eInflated);
     796             : 
     797           0 :     nsTextFrame* next = nullptr;
     798           0 :     if (frameXPEnd < aXPEndOffset) {
     799           0 :       next = curr->GetNextContinuation();
     800           0 :       while (next && next->GetTextRun(nsTextFrame::eInflated) == textRun) {
     801           0 :         frameXPEnd = std::min(next->GetContentEnd(), aXPEndOffset);
     802           0 :         next = frameXPEnd < aXPEndOffset ?
     803             :           next->GetNextContinuation() : nullptr;
     804             :       }
     805             :     }
     806             : 
     807             :     gfxTextRun::Range skipRange(iter.ConvertOriginalToSkipped(frameXPStart),
     808           0 :                                 iter.ConvertOriginalToSkipped(frameXPEnd));
     809           0 :     gfxTextRun::GlyphRunIterator runIter(textRun, skipRange);
     810           0 :     int32_t lastXPEndOffset = frameXPStart;
     811           0 :     while (runIter.NextRun()) {
     812           0 :       gfxFont* font = runIter.GetGlyphRun()->mFont.get();
     813             :       int32_t startXPOffset =
     814           0 :         iter.ConvertSkippedToOriginal(runIter.GetStringStart());
     815             :       // It is possible that the first glyph run has exceeded the frame,
     816             :       // because the whole frame is filled by skipped chars.
     817           0 :       if (startXPOffset >= frameXPEnd) {
     818           0 :         break;
     819             :       }
     820             : 
     821           0 :       if (startXPOffset > lastXPEndOffset) {
     822             :         // Create range for skipped leading chars.
     823           0 :         AppendFontRange(aFontRanges, baseOffset);
     824           0 :         baseOffset += GetTextLengthInRange(
     825           0 :           aContent, lastXPEndOffset, startXPOffset, aLineBreakType);
     826           0 :         lastXPEndOffset = startXPOffset;
     827             :       }
     828             : 
     829           0 :       FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
     830           0 :       fontRange->mFontName = font->GetName();
     831           0 :       fontRange->mFontSize = font->GetAdjustedSize();
     832             : 
     833             :       // The converted original offset may exceed the range,
     834             :       // hence we need to clamp it.
     835             :       int32_t endXPOffset =
     836           0 :         iter.ConvertSkippedToOriginal(runIter.GetStringEnd());
     837           0 :       endXPOffset = std::min(frameXPEnd, endXPOffset);
     838           0 :       baseOffset += GetTextLengthInRange(aContent, startXPOffset, endXPOffset,
     839           0 :                                          aLineBreakType);
     840           0 :       lastXPEndOffset = endXPOffset;
     841             :     }
     842           0 :     if (lastXPEndOffset < frameXPEnd) {
     843             :       // Create range for skipped trailing chars. It also handles case
     844             :       // that the whole frame contains only skipped chars.
     845           0 :       AppendFontRange(aFontRanges, baseOffset);
     846           0 :       baseOffset += GetTextLengthInRange(
     847           0 :         aContent, lastXPEndOffset, frameXPEnd, aLineBreakType);
     848             :     }
     849             : 
     850           0 :     curr = next;
     851             :   }
     852             : }
     853             : 
     854             : nsresult
     855           0 : ContentEventHandler::GenerateFlatFontRanges(nsRange* aRange,
     856             :                                             FontRangeArray& aFontRanges,
     857             :                                             uint32_t& aLength,
     858             :                                             LineBreakType aLineBreakType)
     859             : {
     860           0 :   MOZ_ASSERT(aFontRanges.IsEmpty(), "aRanges must be empty array");
     861             : 
     862           0 :   if (aRange->Collapsed()) {
     863           0 :     return NS_OK;
     864             :   }
     865             : 
     866           0 :   nsINode* startNode = aRange->GetStartContainer();
     867           0 :   nsINode* endNode = aRange->GetEndContainer();
     868           0 :   if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode)) {
     869           0 :     return NS_ERROR_FAILURE;
     870             :   }
     871             : 
     872             :   // baseOffset is the flattened offset of each content node.
     873           0 :   int32_t baseOffset = 0;
     874           0 :   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
     875           0 :   nsresult rv = iter->Init(aRange);
     876           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
     877           0 :     return rv;
     878             :   }
     879           0 :   for (; !iter->IsDone(); iter->Next()) {
     880           0 :     nsINode* node = iter->GetCurrentNode();
     881           0 :     if (NS_WARN_IF(!node)) {
     882           0 :       break;
     883             :     }
     884           0 :     if (!node->IsContent()) {
     885           0 :       continue;
     886             :     }
     887           0 :     nsIContent* content = node->AsContent();
     888             : 
     889           0 :     if (content->IsNodeOfType(nsINode::eTEXT)) {
     890           0 :       int32_t startOffset = content != startNode ? 0 : aRange->StartOffset();
     891           0 :       int32_t endOffset = content != endNode ?
     892           0 :         content->TextLength() : aRange->EndOffset();
     893             :       AppendFontRanges(aFontRanges, content, baseOffset,
     894           0 :                        startOffset, endOffset, aLineBreakType);
     895           0 :       baseOffset += GetTextLengthInRange(content, startOffset, endOffset,
     896           0 :                                          aLineBreakType);
     897           0 :     } else if (ShouldBreakLineBefore(content, mRootContent)) {
     898           0 :       if (aFontRanges.IsEmpty()) {
     899           0 :         MOZ_ASSERT(baseOffset == 0);
     900           0 :         FontRange* fontRange = AppendFontRange(aFontRanges, baseOffset);
     901           0 :         nsIFrame* frame = content->GetPrimaryFrame();
     902           0 :         if (frame) {
     903           0 :           const nsFont& font = frame->GetParent()->StyleFont()->mFont;
     904           0 :           const FontFamilyList& fontList = font.fontlist;
     905           0 :           const FontFamilyName& fontName = fontList.IsEmpty() ?
     906             :             FontFamilyName(fontList.GetDefaultFontType()) :
     907           0 :             fontList.GetFontlist()[0];
     908           0 :           fontName.AppendToString(fontRange->mFontName, false);
     909           0 :           fontRange->mFontSize =
     910           0 :             frame->PresContext()->AppUnitsToDevPixels(font.size);
     911             :         }
     912             :       }
     913           0 :       baseOffset += GetBRLength(aLineBreakType);
     914             :     }
     915             :   }
     916             : 
     917           0 :   aLength = baseOffset;
     918           0 :   return NS_OK;
     919             : }
     920             : 
     921             : nsresult
     922           0 : ContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
     923             :                                              bool aForward,
     924             :                                              uint32_t* aXPOffset)
     925             : {
     926             :   // XXX This method assumes that the frame boundaries must be cluster
     927             :   // boundaries. It's false, but no problem now, maybe.
     928           0 :   if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
     929           0 :       *aXPOffset == 0 || *aXPOffset == aContent->TextLength()) {
     930           0 :     return NS_OK;
     931             :   }
     932             : 
     933           0 :   NS_ASSERTION(*aXPOffset <= aContent->TextLength(),
     934             :                "offset is out of range.");
     935             : 
     936           0 :   RefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
     937             :   int32_t offsetInFrame;
     938             :   CaretAssociationHint hint =
     939           0 :     aForward ? CARET_ASSOCIATE_BEFORE : CARET_ASSOCIATE_AFTER;
     940           0 :   nsIFrame* frame = fs->GetFrameForNodeOffset(aContent, int32_t(*aXPOffset),
     941           0 :                                               hint, &offsetInFrame);
     942           0 :   if (frame) {
     943             :     int32_t startOffset, endOffset;
     944           0 :     nsresult rv = frame->GetOffsets(startOffset, endOffset);
     945           0 :     NS_ENSURE_SUCCESS(rv, rv);
     946           0 :     if (*aXPOffset == static_cast<uint32_t>(startOffset) ||
     947           0 :         *aXPOffset == static_cast<uint32_t>(endOffset)) {
     948           0 :       return NS_OK;
     949             :     }
     950           0 :     if (!frame->IsTextFrame()) {
     951           0 :       return NS_ERROR_FAILURE;
     952             :     }
     953           0 :     nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
     954           0 :     int32_t newOffsetInFrame = *aXPOffset - startOffset;
     955           0 :     newOffsetInFrame += aForward ? -1 : 1;
     956             :     // PeekOffsetCharacter() should respect cluster but ignore user-select
     957             :     // style.  If it returns "FOUND", we should use the result.  Otherwise,
     958             :     // we shouldn't use the result because the offset was moved to reversed
     959             :     // direction.
     960           0 :     nsTextFrame::PeekOffsetCharacterOptions options;
     961           0 :     options.mRespectClusters = true;
     962           0 :     options.mIgnoreUserStyleAll = true;
     963           0 :     if (textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame,
     964           0 :                                        options) == nsIFrame::FOUND) {
     965           0 :       *aXPOffset = startOffset + newOffsetInFrame;
     966           0 :       return NS_OK;
     967             :     }
     968             :   }
     969             : 
     970             :   // If the frame isn't available, we only can check surrogate pair...
     971           0 :   const nsTextFragment* text = aContent->GetText();
     972           0 :   NS_ENSURE_TRUE(text, NS_ERROR_FAILURE);
     973           0 :   if (NS_IS_LOW_SURROGATE(text->CharAt(*aXPOffset)) &&
     974           0 :       NS_IS_HIGH_SURROGATE(text->CharAt(*aXPOffset - 1))) {
     975           0 :     *aXPOffset += aForward ? 1 : -1;
     976             :   }
     977           0 :   return NS_OK;
     978             : }
     979             : 
     980             : nsresult
     981           0 : ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange,
     982             :                                                 uint32_t aOffset,
     983             :                                                 uint32_t aLength,
     984             :                                                 LineBreakType aLineBreakType,
     985             :                                                 bool aExpandToClusterBoundaries,
     986             :                                                 uint32_t* aNewOffset,
     987             :                                                 nsIContent** aLastTextNode)
     988             : {
     989           0 :   if (aNewOffset) {
     990           0 :     *aNewOffset = aOffset;
     991             :   }
     992           0 :   if (aLastTextNode) {
     993           0 :     *aLastTextNode = nullptr;
     994             :   }
     995             : 
     996             :   // Special case like <br contenteditable>
     997           0 :   if (!mRootContent->HasChildren()) {
     998           0 :     nsresult rv = aRange->CollapseTo(mRootContent, 0);
     999           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1000           0 :       return rv;
    1001             :     }
    1002             :   }
    1003             : 
    1004           0 :   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
    1005           0 :   nsresult rv = iter->Init(mRootContent);
    1006           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1007           0 :     return rv;
    1008             :   }
    1009             : 
    1010           0 :   uint32_t offset = 0;
    1011           0 :   uint32_t endOffset = aOffset + aLength;
    1012           0 :   bool startSet = false;
    1013           0 :   for (; !iter->IsDone(); iter->Next()) {
    1014           0 :     nsINode* node = iter->GetCurrentNode();
    1015           0 :     if (NS_WARN_IF(!node)) {
    1016           0 :       break;
    1017             :     }
    1018             :     // FYI: mRootContent shouldn't cause any text. So, we can skip it simply.
    1019           0 :     if (node == mRootContent || !node->IsContent()) {
    1020           0 :       continue;
    1021             :     }
    1022           0 :     nsIContent* content = node->AsContent();
    1023             : 
    1024           0 :     if (aLastTextNode && content->IsNodeOfType(nsINode::eTEXT)) {
    1025           0 :       NS_IF_RELEASE(*aLastTextNode);
    1026           0 :       NS_ADDREF(*aLastTextNode = content);
    1027             :     }
    1028             : 
    1029             :     uint32_t textLength =
    1030           0 :       content->IsNodeOfType(nsINode::eTEXT) ?
    1031             :         GetTextLength(content, aLineBreakType) :
    1032           0 :         (ShouldBreakLineBefore(content, mRootContent) ?
    1033           0 :            GetBRLength(aLineBreakType) : 0);
    1034           0 :     if (!textLength) {
    1035           0 :       continue;
    1036             :     }
    1037             : 
    1038             :     // When the start offset is in between accumulated offset and the last
    1039             :     // offset of the node, the node is the start node of the range.
    1040           0 :     if (!startSet && aOffset <= offset + textLength) {
    1041           0 :       nsINode* startNode = nullptr;
    1042           0 :       int32_t startNodeOffset = -1;
    1043           0 :       if (content->IsNodeOfType(nsINode::eTEXT)) {
    1044             :         // Rule #1.1: [textNode or text[Node or textNode[
    1045           0 :         uint32_t xpOffset = aOffset - offset;
    1046           0 :         if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
    1047           0 :           xpOffset = ConvertToXPOffset(content, xpOffset);
    1048             :         }
    1049             : 
    1050           0 :         if (aExpandToClusterBoundaries) {
    1051           0 :           uint32_t oldXPOffset = xpOffset;
    1052           0 :           rv = ExpandToClusterBoundary(content, false, &xpOffset);
    1053           0 :           if (NS_WARN_IF(NS_FAILED(rv))) {
    1054           0 :             return rv;
    1055             :           }
    1056           0 :           if (aNewOffset) {
    1057             :             // This is correct since a cluster shouldn't include line break.
    1058           0 :             *aNewOffset -= (oldXPOffset - xpOffset);
    1059             :           }
    1060             :         }
    1061           0 :         startNode = content;
    1062           0 :         startNodeOffset = static_cast<int32_t>(xpOffset);
    1063           0 :       } else if (aOffset < offset + textLength) {
    1064             :         // Rule #1.2 [<element>
    1065           0 :         startNode = content->GetParent();
    1066           0 :         if (NS_WARN_IF(!startNode)) {
    1067           0 :           return NS_ERROR_FAILURE;
    1068             :         }
    1069           0 :         startNodeOffset = startNode->IndexOf(content);
    1070           0 :         if (NS_WARN_IF(startNodeOffset == -1)) {
    1071             :           // The content is being removed from the parent!
    1072           0 :           return NS_ERROR_FAILURE;
    1073             :         }
    1074           0 :       } else if (!content->HasChildren()) {
    1075             :         // Rule #1.3: <element/>[
    1076           0 :         startNode = content->GetParent();
    1077           0 :         if (NS_WARN_IF(!startNode)) {
    1078           0 :           return NS_ERROR_FAILURE;
    1079             :         }
    1080           0 :         startNodeOffset = startNode->IndexOf(content) + 1;
    1081           0 :         if (NS_WARN_IF(startNodeOffset == 0)) {
    1082             :           // The content is being removed from the parent!
    1083           0 :           return NS_ERROR_FAILURE;
    1084             :         }
    1085             :       } else {
    1086             :         // Rule #1.4: <element>[
    1087           0 :         startNode = content;
    1088           0 :         startNodeOffset = 0;
    1089             :       }
    1090           0 :       NS_ASSERTION(startNode, "startNode must not be nullptr");
    1091           0 :       NS_ASSERTION(startNodeOffset >= 0,
    1092             :                    "startNodeOffset must not be negative");
    1093           0 :       rv = aRange->SetStart(startNode, startNodeOffset);
    1094           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1095           0 :         return rv;
    1096             :       }
    1097           0 :       startSet = true;
    1098             : 
    1099           0 :       if (!aLength) {
    1100           0 :         rv = aRange->SetEnd(startNode, startNodeOffset);
    1101           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    1102           0 :           return rv;
    1103             :         }
    1104           0 :         return NS_OK;
    1105             :       }
    1106             :     }
    1107             : 
    1108             :     // When the end offset is in the content, the node is the end node of the
    1109             :     // range.
    1110           0 :     if (endOffset <= offset + textLength) {
    1111           0 :       MOZ_ASSERT(startSet,
    1112             :         "The start of the range should've been set already");
    1113           0 :       if (content->IsNodeOfType(nsINode::eTEXT)) {
    1114             :         // Rule #2.1: ]textNode or text]Node or textNode]
    1115           0 :         uint32_t xpOffset = endOffset - offset;
    1116           0 :         if (aLineBreakType == LINE_BREAK_TYPE_NATIVE) {
    1117           0 :           uint32_t xpOffsetCurrent = ConvertToXPOffset(content, xpOffset);
    1118           0 :           if (xpOffset && GetBRLength(aLineBreakType) > 1) {
    1119           0 :             MOZ_ASSERT(GetBRLength(aLineBreakType) == 2);
    1120           0 :             uint32_t xpOffsetPre = ConvertToXPOffset(content, xpOffset - 1);
    1121             :             // If previous character's XP offset is same as current character's,
    1122             :             // it means that the end offset is between \r and \n.  So, the
    1123             :             // range end should be after the \n.
    1124           0 :             if (xpOffsetPre == xpOffsetCurrent) {
    1125           0 :               xpOffset = xpOffsetCurrent + 1;
    1126             :             } else {
    1127           0 :               xpOffset = xpOffsetCurrent;
    1128             :             }
    1129             :           }
    1130             :         }
    1131           0 :         if (aExpandToClusterBoundaries) {
    1132           0 :           rv = ExpandToClusterBoundary(content, true, &xpOffset);
    1133           0 :           if (NS_WARN_IF(NS_FAILED(rv))) {
    1134           0 :             return rv;
    1135             :           }
    1136             :         }
    1137           0 :         NS_ASSERTION(xpOffset <= INT32_MAX,
    1138             :           "The end node offset is too large");
    1139           0 :         rv = aRange->SetEnd(content, static_cast<int32_t>(xpOffset));
    1140           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    1141           0 :           return rv;
    1142             :         }
    1143           0 :         return NS_OK;
    1144             :       }
    1145             : 
    1146           0 :       if (endOffset == offset) {
    1147             :         // Rule #2.2: ]<element>
    1148             :         // NOTE: Please don't crash on release builds because it must be
    1149             :         //       overreaction but we shouldn't allow this bug when some
    1150             :         //       automated tests find this.
    1151           0 :         MOZ_ASSERT(false, "This case should've already been handled at "
    1152             :                           "the last node which caused some text");
    1153             :         return NS_ERROR_FAILURE;
    1154             :       }
    1155             : 
    1156           0 :       if (content->HasChildren() &&
    1157           0 :           ShouldBreakLineBefore(content, mRootContent)) {
    1158             :         // Rule #2.3: </element>]
    1159           0 :         rv = aRange->SetEnd(content, 0);
    1160           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    1161           0 :           return rv;
    1162             :         }
    1163           0 :         return NS_OK;
    1164             :       }
    1165             : 
    1166             :       // Rule #2.4: <element/>]
    1167           0 :       nsINode* endNode = content->GetParent();
    1168           0 :       if (NS_WARN_IF(!endNode)) {
    1169           0 :         return NS_ERROR_FAILURE;
    1170             :       }
    1171           0 :       int32_t indexInParent = endNode->IndexOf(content);
    1172           0 :       if (NS_WARN_IF(indexInParent == -1)) {
    1173             :         // The content is being removed from the parent!
    1174           0 :         return NS_ERROR_FAILURE;
    1175             :       }
    1176           0 :       rv = aRange->SetEnd(endNode, indexInParent + 1);
    1177           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1178           0 :         return rv;
    1179             :       }
    1180           0 :       return NS_OK;
    1181             :     }
    1182             : 
    1183           0 :     offset += textLength;
    1184             :   }
    1185             : 
    1186           0 :   if (!startSet) {
    1187           0 :     MOZ_ASSERT(!mRootContent->IsNodeOfType(nsINode::eTEXT));
    1188           0 :     if (!offset) {
    1189             :       // Rule #1.5: <root>[</root>
    1190             :       // When there are no nodes causing text, the start of the DOM range
    1191             :       // should be start of the root node since clicking on such editor (e.g.,
    1192             :       // <div contenteditable><span></span></div>) sets caret to the start of
    1193             :       // the editor (i.e., before <span> in the example).
    1194           0 :       rv = aRange->SetStart(mRootContent, 0);
    1195           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1196           0 :         return rv;
    1197             :       }
    1198           0 :       if (!aLength) {
    1199           0 :         rv = aRange->SetEnd(mRootContent, 0);
    1200           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    1201           0 :           return rv;
    1202             :         }
    1203           0 :         return NS_OK;
    1204             :       }
    1205             :     } else {
    1206             :       // Rule #1.5: [</root>
    1207           0 :       rv = aRange->SetStart(mRootContent,
    1208           0 :                      static_cast<int32_t>(mRootContent->GetChildCount()));
    1209           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1210           0 :         return rv;
    1211             :       }
    1212             :     }
    1213           0 :     if (aNewOffset) {
    1214           0 :       *aNewOffset = offset;
    1215             :     }
    1216             :   }
    1217             :   // Rule #2.5: ]</root>
    1218           0 :   rv = aRange->SetEnd(mRootContent,
    1219           0 :                       static_cast<int32_t>(mRootContent->GetChildCount()));
    1220           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1221           0 :     return rv;
    1222             :   }
    1223           0 :   return NS_OK;
    1224             : }
    1225             : 
    1226             : /* static */ LineBreakType
    1227           0 : ContentEventHandler::GetLineBreakType(WidgetQueryContentEvent* aEvent)
    1228             : {
    1229           0 :   return GetLineBreakType(aEvent->mUseNativeLineBreak);
    1230             : }
    1231             : 
    1232             : /* static */ LineBreakType
    1233           0 : ContentEventHandler::GetLineBreakType(WidgetSelectionEvent* aEvent)
    1234             : {
    1235           0 :   return GetLineBreakType(aEvent->mUseNativeLineBreak);
    1236             : }
    1237             : 
    1238             : /* static */ LineBreakType
    1239           0 : ContentEventHandler::GetLineBreakType(bool aUseNativeLineBreak)
    1240             : {
    1241           0 :   return aUseNativeLineBreak ?
    1242           0 :     LINE_BREAK_TYPE_NATIVE : LINE_BREAK_TYPE_XP;
    1243             : }
    1244             : 
    1245             : nsresult
    1246           0 : ContentEventHandler::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
    1247             : {
    1248           0 :   switch (aEvent->mMessage) {
    1249             :     case eQuerySelectedText:
    1250           0 :       return OnQuerySelectedText(aEvent);
    1251             :     case eQueryTextContent:
    1252           0 :       return OnQueryTextContent(aEvent);
    1253             :     case eQueryCaretRect:
    1254           0 :       return OnQueryCaretRect(aEvent);
    1255             :     case eQueryTextRect:
    1256           0 :       return OnQueryTextRect(aEvent);
    1257             :     case eQueryTextRectArray:
    1258           0 :       return OnQueryTextRectArray(aEvent);
    1259             :     case eQueryEditorRect:
    1260           0 :       return OnQueryEditorRect(aEvent);
    1261             :     case eQueryContentState:
    1262           0 :       return OnQueryContentState(aEvent);
    1263             :     case eQuerySelectionAsTransferable:
    1264           0 :       return OnQuerySelectionAsTransferable(aEvent);
    1265             :     case eQueryCharacterAtPoint:
    1266           0 :       return OnQueryCharacterAtPoint(aEvent);
    1267             :     case eQueryDOMWidgetHittest:
    1268           0 :       return OnQueryDOMWidgetHittest(aEvent);
    1269             :     default:
    1270           0 :       return NS_ERROR_NOT_IMPLEMENTED;
    1271             :   }
    1272             :   return NS_OK;
    1273             : }
    1274             : 
    1275             : // Similar to nsFrameSelection::GetFrameForNodeOffset,
    1276             : // but this is more flexible for OnQueryTextRect to use
    1277           0 : static nsresult GetFrameForTextRect(nsINode* aNode,
    1278             :                                     int32_t aNodeOffset,
    1279             :                                     bool aHint,
    1280             :                                     nsIFrame** aReturnFrame)
    1281             : {
    1282           0 :   NS_ENSURE_TRUE(aNode && aNode->IsNodeOfType(nsINode::eCONTENT),
    1283             :                  NS_ERROR_UNEXPECTED);
    1284           0 :   nsIContent* content = static_cast<nsIContent*>(aNode);
    1285           0 :   nsIFrame* frame = content->GetPrimaryFrame();
    1286           0 :   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
    1287           0 :   int32_t childNodeOffset = 0;
    1288           0 :   return frame->GetChildFrameContainingOffset(aNodeOffset, aHint,
    1289           0 :                                               &childNodeOffset, aReturnFrame);
    1290             : }
    1291             : 
    1292             : nsresult
    1293           0 : ContentEventHandler::OnQuerySelectedText(WidgetQueryContentEvent* aEvent)
    1294             : {
    1295           0 :   nsresult rv = Init(aEvent);
    1296           0 :   if (NS_FAILED(rv)) {
    1297           0 :     return rv;
    1298             :   }
    1299             : 
    1300           0 :   if (!mFirstSelectedRange) {
    1301           0 :     MOZ_ASSERT(aEvent->mInput.mSelectionType != SelectionType::eNormal);
    1302           0 :     MOZ_ASSERT(aEvent->mReply.mOffset == WidgetQueryContentEvent::NOT_FOUND);
    1303           0 :     MOZ_ASSERT(aEvent->mReply.mString.IsEmpty());
    1304           0 :     MOZ_ASSERT(!aEvent->mReply.mHasSelection);
    1305           0 :     aEvent->mSucceeded = true;
    1306           0 :     return NS_OK;
    1307             :   }
    1308             : 
    1309           0 :   nsINode* const startNode = mFirstSelectedRange->GetStartContainer();
    1310           0 :   nsINode* const endNode = mFirstSelectedRange->GetEndContainer();
    1311             : 
    1312             :   // Make sure the selection is within the root content range.
    1313           0 :   if (!nsContentUtils::ContentIsDescendantOf(startNode, mRootContent) ||
    1314           0 :       !nsContentUtils::ContentIsDescendantOf(endNode, mRootContent)) {
    1315           0 :     return NS_ERROR_NOT_AVAILABLE;
    1316             :   }
    1317             : 
    1318           0 :   NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
    1319             :                "The reply string must be empty");
    1320             : 
    1321           0 :   LineBreakType lineBreakType = GetLineBreakType(aEvent);
    1322           0 :   rv = GetStartOffset(mFirstSelectedRange,
    1323           0 :                       &aEvent->mReply.mOffset, lineBreakType);
    1324           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1325             : 
    1326           0 :   nsCOMPtr<nsINode> anchorNode, focusNode;
    1327           0 :   int32_t anchorOffset = 0, focusOffset = 0;
    1328           0 :   if (mSelection->RangeCount()) {
    1329             :     // If there is only one selection range, the anchor/focus node and offset
    1330             :     // are the information of the range.  Therefore, we have the direction
    1331             :     // information.
    1332           0 :     if (mSelection->RangeCount() == 1) {
    1333           0 :       anchorNode = mSelection->GetAnchorNode();
    1334           0 :       focusNode = mSelection->GetFocusNode();
    1335           0 :       if (NS_WARN_IF(!anchorNode) || NS_WARN_IF(!focusNode)) {
    1336           0 :         return NS_ERROR_FAILURE;
    1337             :       }
    1338           0 :       anchorOffset = static_cast<int32_t>(mSelection->AnchorOffset());
    1339           0 :       focusOffset = static_cast<int32_t>(mSelection->FocusOffset());
    1340           0 :       if (NS_WARN_IF(anchorOffset < 0) || NS_WARN_IF(focusOffset < 0)) {
    1341           0 :         return NS_ERROR_FAILURE;
    1342             :       }
    1343             : 
    1344           0 :       int16_t compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
    1345           0 :                                                       focusNode, focusOffset);
    1346           0 :       aEvent->mReply.mReversed = compare > 0;
    1347             :     }
    1348             :     // However, if there are 2 or more selection ranges, we have no information
    1349             :     // of that.
    1350             :     else {
    1351           0 :       aEvent->mReply.mReversed = false;
    1352             :     }
    1353             : 
    1354           0 :     if (!mFirstSelectedRange->Collapsed()) {
    1355           0 :       rv = GenerateFlatTextContent(mFirstSelectedRange, aEvent->mReply.mString,
    1356           0 :                                    lineBreakType);
    1357           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    1358           0 :         return rv;
    1359             :       }
    1360             :     } else {
    1361           0 :       aEvent->mReply.mString.Truncate();
    1362             :     }
    1363             :   } else {
    1364           0 :     NS_ASSERTION(mFirstSelectedRange->Collapsed(),
    1365             :       "When mSelection doesn't have selection, mFirstSelectedRange must be "
    1366             :       "collapsed");
    1367           0 :     anchorNode = focusNode = mFirstSelectedRange->GetStartContainer();
    1368           0 :     if (NS_WARN_IF(!anchorNode)) {
    1369           0 :       return NS_ERROR_FAILURE;
    1370             :     }
    1371           0 :     anchorOffset = focusOffset =
    1372           0 :       static_cast<int32_t>(mFirstSelectedRange->StartOffset());
    1373           0 :     if (NS_WARN_IF(anchorOffset < 0)) {
    1374           0 :       return NS_ERROR_FAILURE;
    1375             :     }
    1376             : 
    1377           0 :     aEvent->mReply.mReversed = false;
    1378           0 :     aEvent->mReply.mString.Truncate();
    1379             :   }
    1380             : 
    1381             : 
    1382           0 :   nsIFrame* frame = nullptr;
    1383           0 :   rv = GetFrameForTextRect(focusNode, focusOffset, true, &frame);
    1384           0 :   if (NS_SUCCEEDED(rv) && frame) {
    1385           0 :     aEvent->mReply.mWritingMode = frame->GetWritingMode();
    1386             :   } else {
    1387           0 :     aEvent->mReply.mWritingMode = WritingMode();
    1388             :   }
    1389             : 
    1390           0 :   aEvent->mSucceeded = true;
    1391           0 :   return NS_OK;
    1392             : }
    1393             : 
    1394             : nsresult
    1395           0 : ContentEventHandler::OnQueryTextContent(WidgetQueryContentEvent* aEvent)
    1396             : {
    1397           0 :   nsresult rv = Init(aEvent);
    1398           0 :   if (NS_FAILED(rv)) {
    1399           0 :     return rv;
    1400             :   }
    1401             : 
    1402           0 :   NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
    1403             :                "The reply string must be empty");
    1404             : 
    1405           0 :   LineBreakType lineBreakType = GetLineBreakType(aEvent);
    1406             : 
    1407           0 :   RefPtr<nsRange> range = new nsRange(mRootContent);
    1408           0 :   rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
    1409             :                                   aEvent->mInput.mLength, lineBreakType, false,
    1410           0 :                                   &aEvent->mReply.mOffset);
    1411           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1412             : 
    1413           0 :   rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
    1414           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1415             : 
    1416           0 :   if (aEvent->mWithFontRanges) {
    1417             :     uint32_t fontRangeLength;
    1418           0 :     rv = GenerateFlatFontRanges(range, aEvent->mReply.mFontRanges,
    1419           0 :                                 fontRangeLength, lineBreakType);
    1420           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1421           0 :       return rv;
    1422             :     }
    1423             : 
    1424           0 :     MOZ_ASSERT(fontRangeLength == aEvent->mReply.mString.Length(),
    1425             :                "Font ranges doesn't match the string");
    1426             :   }
    1427             : 
    1428           0 :   aEvent->mSucceeded = true;
    1429             : 
    1430           0 :   return NS_OK;
    1431             : }
    1432             : 
    1433             : void
    1434           0 : ContentEventHandler::EnsureNonEmptyRect(nsRect& aRect) const
    1435             : {
    1436             :   // See the comment in ContentEventHandler.h why this doesn't set them to
    1437             :   // one device pixel.
    1438           0 :   aRect.height = std::max(1, aRect.height);
    1439           0 :   aRect.width = std::max(1, aRect.width);
    1440           0 : }
    1441             : 
    1442             : void
    1443           0 : ContentEventHandler::EnsureNonEmptyRect(LayoutDeviceIntRect& aRect) const
    1444             : {
    1445           0 :   aRect.height = std::max(1, aRect.height);
    1446           0 :   aRect.width = std::max(1, aRect.width);
    1447           0 : }
    1448             : 
    1449             : ContentEventHandler::NodePosition
    1450           0 : ContentEventHandler::GetNodePositionHavingFlatText(
    1451             :                        const NodePosition& aNodePosition)
    1452             : {
    1453             :   return GetNodePositionHavingFlatText(aNodePosition.mNode,
    1454           0 :                                        aNodePosition.mOffset);
    1455             : }
    1456             : 
    1457             : ContentEventHandler::NodePosition
    1458           0 : ContentEventHandler::GetNodePositionHavingFlatText(nsINode* aNode,
    1459             :                                                    int32_t aNodeOffset)
    1460             : {
    1461           0 :   if (aNode->IsNodeOfType(nsINode::eTEXT)) {
    1462           0 :     return NodePosition(aNode, aNodeOffset);
    1463             :   }
    1464             : 
    1465           0 :   int32_t childCount = static_cast<int32_t>(aNode->GetChildCount());
    1466             : 
    1467             :   // If it's a empty element node, returns itself.
    1468           0 :   if (!childCount) {
    1469           0 :     MOZ_ASSERT(!aNodeOffset || aNodeOffset == 1);
    1470           0 :     return NodePosition(aNode, aNodeOffset);
    1471             :   }
    1472             : 
    1473             :   // If there is a node at given position, return the start of it.
    1474           0 :   if (aNodeOffset < childCount) {
    1475           0 :     return NodePosition(aNode->GetChildAt(aNodeOffset), 0);
    1476             :   }
    1477             : 
    1478             :   // If the offset represents "after" the node, we need to return the last
    1479             :   // child of it.  For example, if a range is |<p>[<br>]</p>|, then, the
    1480             :   // end point is {<p>, 1}.  In such case, callers need the <br> node.
    1481           0 :   if (aNodeOffset == childCount) {
    1482           0 :     NodePosition result;
    1483           0 :     result.mNode = aNode->GetChildAt(childCount - 1);
    1484           0 :     result.mOffset = result.mNode->IsNodeOfType(nsINode::eTEXT) ?
    1485           0 :       static_cast<int32_t>(result.mNode->AsContent()->TextLength()) : 1;
    1486             :   }
    1487             : 
    1488           0 :   NS_WARNING("aNodeOffset is invalid value");
    1489           0 :   return NodePosition();
    1490             : }
    1491             : 
    1492             : ContentEventHandler::FrameAndNodeOffset
    1493           0 : ContentEventHandler::GetFirstFrameInRangeForTextRect(nsRange* aRange)
    1494             : {
    1495           0 :   NodePosition nodePosition;
    1496           0 :   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
    1497           0 :   for (iter->Init(aRange); !iter->IsDone(); iter->Next()) {
    1498           0 :     nsINode* node = iter->GetCurrentNode();
    1499           0 :     if (NS_WARN_IF(!node)) {
    1500           0 :       break;
    1501             :     }
    1502             : 
    1503           0 :     if (!node->IsContent()) {
    1504           0 :       continue;
    1505             :     }
    1506             : 
    1507           0 :     if (node->IsNodeOfType(nsINode::eTEXT)) {
    1508             :       // If the range starts at the end of a text node, we need to find
    1509             :       // next node which causes text.
    1510             :       int32_t offsetInNode =
    1511           0 :         node == aRange->GetStartContainer() ? aRange->StartOffset() : 0;
    1512           0 :       if (static_cast<uint32_t>(offsetInNode) < node->Length()) {
    1513           0 :         nodePosition.mNode = node;
    1514           0 :         nodePosition.mOffset = offsetInNode;
    1515           0 :         break;
    1516             :       }
    1517           0 :       continue;
    1518             :     }
    1519             : 
    1520             :     // If the element node causes a line break before it, it's the first
    1521             :     // node causing text.
    1522           0 :     if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
    1523           0 :         IsMozBR(node->AsContent())) {
    1524           0 :       nodePosition.mNode = node;
    1525           0 :       nodePosition.mOffset = 0;
    1526             :     }
    1527             :   }
    1528             : 
    1529           0 :   if (!nodePosition.IsValid()) {
    1530           0 :     return FrameAndNodeOffset();
    1531             :   }
    1532             : 
    1533           0 :   nsIFrame* firstFrame = nullptr;
    1534           0 :   GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
    1535           0 :                       true, &firstFrame);
    1536           0 :   return FrameAndNodeOffset(firstFrame, nodePosition.mOffset);
    1537             : }
    1538             : 
    1539             : ContentEventHandler::FrameAndNodeOffset
    1540           0 : ContentEventHandler::GetLastFrameInRangeForTextRect(nsRange* aRange)
    1541             : {
    1542           0 :   NodePosition nodePosition;
    1543           0 :   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
    1544           0 :   iter->Init(aRange);
    1545             : 
    1546           0 :   nsINode* endNode = aRange->GetEndContainer();
    1547           0 :   uint32_t endOffset = static_cast<uint32_t>(aRange->EndOffset());
    1548             :   // If the end point is start of a text node or specified by its parent and
    1549             :   // index, the node shouldn't be included into the range.  For example,
    1550             :   // with this case, |<p>abc[<br>]def</p>|, the range ends at 3rd children of
    1551             :   // <p> (see the range creation rules, "2.4. Cases: <element/>]"). This causes
    1552             :   // following frames:
    1553             :   // +----+-----+
    1554             :   // | abc|[<br>|
    1555             :   // +----+-----+
    1556             :   // +----+
    1557             :   // |]def|
    1558             :   // +----+
    1559             :   // So, if this method includes the 2nd text frame's rect to its result, the
    1560             :   // caller will return too tall rect which includes 2 lines in this case isn't
    1561             :   // expected by native IME  (e.g., popup of IME will be positioned at bottom
    1562             :   // of "d" instead of right-bottom of "c").  Therefore, this method shouldn't
    1563             :   // include the last frame when its content isn't really in aRange.
    1564           0 :   nsINode* nextNodeOfRangeEnd = nullptr;
    1565           0 :   if (endNode->IsNodeOfType(nsINode::eTEXT)) {
    1566             :     // Don't set nextNodeOfRangeEnd to the start node of aRange because if
    1567             :     // endNode is same as start node of the range, the text node shouldn't be
    1568             :     // next of range end even if the offset is 0.  This could occur with empty
    1569             :     // text node.
    1570           0 :     if (!endOffset && aRange->GetStartContainer() != endNode) {
    1571           0 :       nextNodeOfRangeEnd = endNode;
    1572             :     }
    1573           0 :   } else if (endOffset < endNode->GetChildCount()) {
    1574           0 :     nextNodeOfRangeEnd = endNode->GetChildAt(endOffset);
    1575             :   }
    1576             : 
    1577           0 :   for (iter->Last(); !iter->IsDone(); iter->Prev()) {
    1578           0 :     nsINode* node = iter->GetCurrentNode();
    1579           0 :     if (NS_WARN_IF(!node)) {
    1580           0 :       break;
    1581             :     }
    1582             : 
    1583           0 :     if (!node->IsContent() || node == nextNodeOfRangeEnd) {
    1584           0 :       continue;
    1585             :     }
    1586             : 
    1587           0 :     if (node->IsNodeOfType(nsINode::eTEXT)) {
    1588           0 :       nodePosition.mNode = node;
    1589           0 :       if (node == aRange->GetEndContainer()) {
    1590           0 :         nodePosition.mOffset = aRange->EndOffset();
    1591             :       } else {
    1592           0 :         nodePosition.mOffset = node->Length();
    1593             :       }
    1594             :       // If the text node is empty or the last node of the range but the index
    1595             :       // is 0, we should store current position but continue looking for
    1596             :       // previous node (If there are no nodes before it, we should use current
    1597             :       // node position for returning its frame).
    1598           0 :       if (!nodePosition.mOffset) {
    1599           0 :         continue;
    1600             :       }
    1601           0 :       break;
    1602             :     }
    1603             : 
    1604           0 :     if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
    1605           0 :         IsMozBR(node->AsContent())) {
    1606           0 :       nodePosition.mNode = node;
    1607           0 :       nodePosition.mOffset = 0;
    1608           0 :       break;
    1609             :     }
    1610             :   }
    1611             : 
    1612           0 :   if (!nodePosition.IsValid()) {
    1613           0 :     return FrameAndNodeOffset();
    1614             :   }
    1615             : 
    1616           0 :   nsIFrame* lastFrame = nullptr;
    1617           0 :   GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
    1618           0 :                       true, &lastFrame);
    1619           0 :   if (!lastFrame) {
    1620           0 :     return FrameAndNodeOffset();
    1621             :   }
    1622             : 
    1623             :   // If the last frame is a text frame, we need to check if the range actually
    1624             :   // includes at least one character in the range.  Therefore, if it's not a
    1625             :   // text frame, we need to do nothing anymore.
    1626           0 :   if (!lastFrame->IsTextFrame()) {
    1627           0 :     return FrameAndNodeOffset(lastFrame, nodePosition.mOffset);
    1628             :   }
    1629             : 
    1630             :   int32_t start, end;
    1631           0 :   if (NS_WARN_IF(NS_FAILED(lastFrame->GetOffsets(start, end)))) {
    1632           0 :     return FrameAndNodeOffset();
    1633             :   }
    1634             : 
    1635             :   // If the start offset in the node is same as the computed offset in the
    1636             :   // node and it's not 0, the frame shouldn't be added to the text rect.  So,
    1637             :   // this should return previous text frame and its last offset if there is
    1638             :   // at least one text frame.
    1639           0 :   if (nodePosition.mOffset && nodePosition.mOffset == start) {
    1640           0 :     GetFrameForTextRect(nodePosition.mNode, --nodePosition.mOffset,
    1641           0 :                         true, &lastFrame);
    1642           0 :     if (NS_WARN_IF(!lastFrame)) {
    1643           0 :       return FrameAndNodeOffset();
    1644             :     }
    1645             :   }
    1646             : 
    1647           0 :   return FrameAndNodeOffset(lastFrame, nodePosition.mOffset);
    1648             : }
    1649             : 
    1650             : ContentEventHandler::FrameRelativeRect
    1651           0 : ContentEventHandler::GetLineBreakerRectBefore(nsIFrame* aFrame)
    1652             : {
    1653             :   // Note that this method should be called only with an element's frame whose
    1654             :   // open tag causes a line break or moz-<br> for computing empty last line's
    1655             :   // rect.
    1656           0 :   MOZ_ASSERT(ShouldBreakLineBefore(aFrame->GetContent(), mRootContent) ||
    1657             :              IsMozBR(aFrame->GetContent()));
    1658             : 
    1659           0 :   nsIFrame* frameForFontMetrics = aFrame;
    1660             : 
    1661             :   // If it's not a <br> frame, this method computes the line breaker's rect
    1662             :   // outside the frame.  Therefore, we need to compute with parent frame's
    1663             :   // font metrics in such case.
    1664           0 :   if (!aFrame->IsBrFrame() && aFrame->GetParent()) {
    1665           0 :     frameForFontMetrics = aFrame->GetParent();
    1666             :   }
    1667             : 
    1668             :   // Note that <br> element's rect is decided with line-height but we need
    1669             :   // a rect only with font height.  Additionally, <br> frame's width and
    1670             :   // height are 0 in quirks mode if it's not an empty line.  So, we cannot
    1671             :   // use frame rect information even if it's a <br> frame.
    1672             : 
    1673           0 :   FrameRelativeRect result(aFrame);
    1674             : 
    1675             :   RefPtr<nsFontMetrics> fontMetrics =
    1676           0 :     nsLayoutUtils::GetInflatedFontMetricsForFrame(frameForFontMetrics);
    1677           0 :   if (NS_WARN_IF(!fontMetrics)) {
    1678           0 :     return FrameRelativeRect();
    1679             :   }
    1680             : 
    1681           0 :   const WritingMode kWritingMode = frameForFontMetrics->GetWritingMode();
    1682           0 :   nscoord baseline = aFrame->GetCaretBaseline();
    1683           0 :   if (kWritingMode.IsVertical()) {
    1684           0 :     if (kWritingMode.IsLineInverted()) {
    1685           0 :       result.mRect.x = baseline - fontMetrics->MaxDescent();
    1686             :     } else {
    1687           0 :       result.mRect.x = baseline - fontMetrics->MaxAscent();
    1688             :     }
    1689           0 :     result.mRect.width = fontMetrics->MaxHeight();
    1690             :   } else {
    1691           0 :     result.mRect.y = baseline - fontMetrics->MaxAscent();
    1692           0 :     result.mRect.height = fontMetrics->MaxHeight();
    1693             :   }
    1694             : 
    1695             :   // If aFrame isn't a <br> frame, caret should be at outside of it because
    1696             :   // the line break is before its open tag.  For example, case of
    1697             :   // |<div><p>some text</p></div>|, caret is before <p> element and in <div>
    1698             :   // element, the caret should be left of top-left corner of <p> element like:
    1699             :   //
    1700             :   // +-<div>-------------------  <div>'s border box
    1701             :   // | I +-<p>-----------------  <p>'s border box
    1702             :   // | I |
    1703             :   // | I |
    1704             :   // |   |
    1705             :   //   ^- caret
    1706             :   //
    1707             :   // However, this is a hack for unusual scenario.  This hack shouldn't be
    1708             :   // used as far as possible.
    1709           0 :   if (!aFrame->IsBrFrame()) {
    1710           0 :     if (kWritingMode.IsVertical()) {
    1711           0 :       if (kWritingMode.IsLineInverted()) {
    1712             :         // above of top-left corner of aFrame.
    1713           0 :         result.mRect.x = 0;
    1714             :       } else {
    1715             :         // above of top-right corner of aFrame.
    1716           0 :         result.mRect.x = aFrame->GetRect().XMost() - result.mRect.width;
    1717             :       }
    1718           0 :       result.mRect.y = -mPresContext->AppUnitsPerDevPixel();
    1719             :     } else {
    1720             :       // left of top-left corner of aFrame.
    1721           0 :       result.mRect.x = -mPresContext->AppUnitsPerDevPixel();
    1722           0 :       result.mRect.y = 0;
    1723             :     }
    1724             :   }
    1725           0 :   return result;
    1726             : }
    1727             : 
    1728             : ContentEventHandler::FrameRelativeRect
    1729           0 : ContentEventHandler::GuessLineBreakerRectAfter(nsIContent* aTextContent)
    1730             : {
    1731             :   // aTextContent should be a text node.
    1732           0 :   MOZ_ASSERT(aTextContent->IsNodeOfType(nsINode::eTEXT));
    1733             : 
    1734           0 :   FrameRelativeRect result;
    1735           0 :   int32_t length = static_cast<int32_t>(aTextContent->Length());
    1736           0 :   if (NS_WARN_IF(length < 0)) {
    1737           0 :     return result;
    1738             :   }
    1739             :   // Get the last nsTextFrame which is caused by aTextContent.  Note that
    1740             :   // a text node can cause multiple text frames, e.g., the text is too long
    1741             :   // and wrapped by its parent block or the text has line breakers and its
    1742             :   // white-space property respects the line breakers (e.g., |pre|).
    1743           0 :   nsIFrame* lastTextFrame = nullptr;
    1744           0 :   nsresult rv = GetFrameForTextRect(aTextContent, length, true, &lastTextFrame);
    1745           0 :   if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!lastTextFrame)) {
    1746           0 :     return result;
    1747             :   }
    1748           0 :   const nsRect kLastTextFrameRect = lastTextFrame->GetRect();
    1749           0 :   if (lastTextFrame->GetWritingMode().IsVertical()) {
    1750             :     // Below of the last text frame.
    1751           0 :     result.mRect.SetRect(0, kLastTextFrameRect.height,
    1752           0 :                          kLastTextFrameRect.width, 0);
    1753             :   } else {
    1754             :     // Right of the last text frame (not bidi-aware).
    1755           0 :     result.mRect.SetRect(kLastTextFrameRect.width, 0,
    1756           0 :                          0, kLastTextFrameRect.height);
    1757             :   }
    1758           0 :   result.mBaseFrame = lastTextFrame;
    1759           0 :   return result;
    1760             : }
    1761             : 
    1762             : ContentEventHandler::FrameRelativeRect
    1763           0 : ContentEventHandler::GuessFirstCaretRectIn(nsIFrame* aFrame)
    1764             : {
    1765           0 :   const WritingMode kWritingMode = aFrame->GetWritingMode();
    1766             : 
    1767             :   // Computes the font height, but if it's not available, we should use
    1768             :   // default font size of Firefox.  The default font size in default settings
    1769             :   // is 16px.
    1770             :   RefPtr<nsFontMetrics> fontMetrics =
    1771           0 :     nsLayoutUtils::GetInflatedFontMetricsForFrame(aFrame);
    1772             :   const nscoord kMaxHeight =
    1773           0 :     fontMetrics ? fontMetrics->MaxHeight() :
    1774           0 :                   16 * mPresContext->AppUnitsPerDevPixel();
    1775             : 
    1776           0 :   nsRect caretRect;
    1777           0 :   const nsRect kContentRect = aFrame->GetContentRect() - aFrame->GetPosition();
    1778           0 :   caretRect.y = kContentRect.y;
    1779           0 :   if (!kWritingMode.IsVertical()) {
    1780           0 :     if (kWritingMode.IsBidiLTR()) {
    1781           0 :       caretRect.x = kContentRect.x;
    1782             :     } else {
    1783             :       // Move 1px left for the space of caret itself.
    1784           0 :       const nscoord kOnePixel = mPresContext->AppUnitsPerDevPixel();
    1785           0 :       caretRect.x = kContentRect.XMost() - kOnePixel;
    1786             :     }
    1787           0 :     caretRect.height = kMaxHeight;
    1788             :     // However, don't add kOnePixel here because it may cause 2px width at
    1789             :     // aligning the edge to device pixels.
    1790           0 :     caretRect.width = 1;
    1791             :   } else {
    1792           0 :     if (kWritingMode.IsVerticalLR()) {
    1793           0 :       caretRect.x = kContentRect.x;
    1794             :     } else {
    1795           0 :       caretRect.x = kContentRect.XMost() - kMaxHeight;
    1796             :     }
    1797           0 :     caretRect.width = kMaxHeight;
    1798             :     // Don't add app units for a device pixel because it may cause 2px height
    1799             :     // at aligning the edge to device pixels.
    1800           0 :     caretRect.height = 1;
    1801             :   }
    1802           0 :   return FrameRelativeRect(caretRect, aFrame);
    1803             : }
    1804             : 
    1805             : nsresult
    1806           0 : ContentEventHandler::OnQueryTextRectArray(WidgetQueryContentEvent* aEvent)
    1807             : {
    1808           0 :   nsresult rv = Init(aEvent);
    1809           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    1810           0 :     return rv;
    1811             :   }
    1812             : 
    1813           0 :   LineBreakType lineBreakType = GetLineBreakType(aEvent);
    1814           0 :   const uint32_t kBRLength = GetBRLength(lineBreakType);
    1815             : 
    1816           0 :   RefPtr<nsRange> range = new nsRange(mRootContent);
    1817             : 
    1818           0 :   bool isVertical = false;
    1819           0 :   LayoutDeviceIntRect rect;
    1820           0 :   uint32_t offset = aEvent->mInput.mOffset;
    1821           0 :   const uint32_t kEndOffset = offset + aEvent->mInput.mLength;
    1822           0 :   bool wasLineBreaker = false;
    1823             :   // lastCharRect stores the last charRect value (see below for the detail of
    1824             :   // charRect).
    1825           0 :   nsRect lastCharRect;
    1826             :   // lastFrame is base frame of lastCharRect.
    1827           0 :   nsIFrame* lastFrame = nullptr;
    1828           0 :   while (offset < kEndOffset) {
    1829           0 :     nsCOMPtr<nsIContent> lastTextContent;
    1830           0 :     rv = SetRangeFromFlatTextOffset(range, offset, 1, lineBreakType, true,
    1831           0 :                                     nullptr, getter_AddRefs(lastTextContent));
    1832           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    1833           0 :       return rv;
    1834             :     }
    1835             : 
    1836             :     // If the range is collapsed, offset has already reached the end of the
    1837             :     // contents.
    1838           0 :     if (range->Collapsed()) {
    1839           0 :       break;
    1840             :     }
    1841             : 
    1842             :     // Get the first frame which causes some text after the offset.
    1843           0 :     FrameAndNodeOffset firstFrame = GetFirstFrameInRangeForTextRect(range);
    1844             : 
    1845             :     // If GetFirstFrameInRangeForTextRect() does not return valid frame, that
    1846             :     // means that there are no visible frames having text or the offset reached
    1847             :     // the end of contents.
    1848           0 :     if (!firstFrame.IsValid()) {
    1849           0 :       nsAutoString allText;
    1850           0 :       rv = GenerateFlatTextContent(mRootContent, allText, lineBreakType);
    1851             :       // If the offset doesn't reach the end of contents yet but there is no
    1852             :       // frames for the node, that means that current offset's node is hidden
    1853             :       // by CSS or something.  Ideally, we should handle it with the last
    1854             :       // visible text node's last character's rect, but it's not usual cases
    1855             :       // in actual web services.  Therefore, currently, we should make this
    1856             :       // case fail.
    1857           0 :       if (NS_WARN_IF(NS_FAILED(rv)) || offset < allText.Length()) {
    1858           0 :         return NS_ERROR_FAILURE;
    1859             :       }
    1860             :       // Otherwise, we should append caret rect at the end of the contents
    1861             :       // later.
    1862           0 :       break;
    1863             :     }
    1864             : 
    1865           0 :     nsIContent* firstContent = firstFrame.mFrame->GetContent();
    1866           0 :     if (NS_WARN_IF(!firstContent)) {
    1867           0 :       return NS_ERROR_FAILURE;
    1868             :     }
    1869             : 
    1870           0 :     bool startsBetweenLineBreaker = false;
    1871           0 :     nsAutoString chars;
    1872             :     // XXX not bidi-aware this class...
    1873           0 :     isVertical = firstFrame->GetWritingMode().IsVertical();
    1874             : 
    1875           0 :     nsIFrame* baseFrame = firstFrame;
    1876             :     // charRect should have each character rect or line breaker rect relative
    1877             :     // to the base frame.
    1878           0 :     AutoTArray<nsRect, 16> charRects;
    1879             : 
    1880             :     // If the first frame is a text frame, the result should be computed with
    1881             :     // the frame's API.
    1882           0 :     if (firstFrame->IsTextFrame()) {
    1883           0 :       rv = firstFrame->GetCharacterRectsInRange(firstFrame.mOffsetInNode,
    1884           0 :                                                 kEndOffset - offset, charRects);
    1885           0 :       if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(charRects.IsEmpty())) {
    1886           0 :         return rv;
    1887             :       }
    1888             :       // Assign the characters whose rects are computed by the call of
    1889             :       // nsTextFrame::GetCharacterRectsInRange().
    1890           0 :       AppendSubString(chars, firstContent, firstFrame.mOffsetInNode,
    1891           0 :                       charRects.Length());
    1892           0 :       if (NS_WARN_IF(chars.Length() != charRects.Length())) {
    1893           0 :         return NS_ERROR_UNEXPECTED;
    1894             :       }
    1895           0 :       if (kBRLength > 1 && chars[0] == '\n' &&
    1896           0 :           offset == aEvent->mInput.mOffset && offset) {
    1897             :         // If start of range starting from previous offset of query range is
    1898             :         // same as the start of query range, the query range starts from
    1899             :         // between a line breaker (i.e., the range starts between "\r" and
    1900             :         // "\n").
    1901           0 :         RefPtr<nsRange> rangeToPrevOffset = new nsRange(mRootContent);
    1902           0 :         rv = SetRangeFromFlatTextOffset(rangeToPrevOffset,
    1903           0 :                                         aEvent->mInput.mOffset - 1, 1,
    1904           0 :                                         lineBreakType, true, nullptr);
    1905           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    1906           0 :           return rv;
    1907             :         }
    1908           0 :         startsBetweenLineBreaker =
    1909           0 :           range->GetStartContainer() ==
    1910           0 :             rangeToPrevOffset->GetStartContainer() &&
    1911           0 :           range->StartOffset() == rangeToPrevOffset->StartOffset();
    1912             :       }
    1913             :     }
    1914             :     // Other contents should cause a line breaker rect before it.
    1915             :     // Note that moz-<br> element does not cause any text, however,
    1916             :     // it represents empty line at the last of current block.  Therefore,
    1917             :     // we need to compute its rect too.
    1918           0 :     else if (ShouldBreakLineBefore(firstContent, mRootContent) ||
    1919           0 :              IsMozBR(firstContent)) {
    1920           0 :       nsRect brRect;
    1921             :       // If the frame is not a <br> frame, we need to compute the caret rect
    1922             :       // with last character's rect before firstContent if there is.
    1923             :       // For example, if caret is after "c" of |<p>abc</p><p>def</p>|, IME may
    1924             :       // query a line breaker's rect after "c".  Then, if we compute it only
    1925             :       // with the 2nd <p>'s block frame, the result will be:
    1926             :       //  +-<p>--------------------------------+
    1927             :       //  |abc                                 |
    1928             :       //  +------------------------------------+
    1929             :       //
    1930             :       // I+-<p>--------------------------------+
    1931             :       //  |def                                 |
    1932             :       //  +------------------------------------+
    1933             :       // However, users expect popup windows of IME should be positioned at
    1934             :       // right-bottom of "c" like this:
    1935             :       //  +-<p>--------------------------------+
    1936             :       //  |abcI                                |
    1937             :       //  +------------------------------------+
    1938             :       //
    1939             :       //  +-<p>--------------------------------+
    1940             :       //  |def                                 |
    1941             :       //  +------------------------------------+
    1942             :       // Therefore, if the first frame isn't a <br> frame and there is a text
    1943             :       // node before the first node in the queried range, we should compute the
    1944             :       // first rect with the previous character's rect.
    1945             :       // If we already compute a character's rect in the queried range, we can
    1946             :       // compute it with the cached last character's rect.  (However, don't
    1947             :       // use this path if it's a <br> frame because trusting <br> frame's rect
    1948             :       // is better than guessing the rect from the previous character.)
    1949           0 :       if (!firstFrame->IsBrFrame() && aEvent->mInput.mOffset != offset) {
    1950           0 :         baseFrame = lastFrame;
    1951           0 :         brRect = lastCharRect;
    1952           0 :         if (!wasLineBreaker) {
    1953           0 :           if (isVertical) {
    1954             :             // Right of the last character.
    1955           0 :             brRect.y = brRect.YMost() + 1;
    1956           0 :             brRect.height = 1;
    1957             :           } else {
    1958             :             // Under the last character.
    1959           0 :             brRect.x = brRect.XMost() + 1;
    1960           0 :             brRect.width = 1;
    1961             :           }
    1962             :         }
    1963             :       }
    1964             :       // If it's not a <br> frame and it's the first character rect at the
    1965             :       // queried range, we need to the previous character of the start of
    1966             :       // the queried range if there is a text node.
    1967           0 :       else if (!firstFrame->IsBrFrame() && lastTextContent) {
    1968             :         FrameRelativeRect brRectRelativeToLastTextFrame =
    1969           0 :           GuessLineBreakerRectAfter(lastTextContent);
    1970           0 :         if (NS_WARN_IF(!brRectRelativeToLastTextFrame.IsValid())) {
    1971           0 :           return NS_ERROR_FAILURE;
    1972             :         }
    1973             :         // Look for the last text frame for lastTextContent.
    1974           0 :         nsIFrame* primaryFrame = lastTextContent->GetPrimaryFrame();
    1975           0 :         if (NS_WARN_IF(!primaryFrame)) {
    1976           0 :           return NS_ERROR_FAILURE;
    1977             :         }
    1978           0 :         baseFrame = primaryFrame->LastContinuation();
    1979           0 :         if (NS_WARN_IF(!baseFrame)) {
    1980           0 :           return NS_ERROR_FAILURE;
    1981             :         }
    1982           0 :         brRect = brRectRelativeToLastTextFrame.RectRelativeTo(baseFrame);
    1983             :       }
    1984             :       // Otherwise, we need to compute the line breaker's rect only with the
    1985             :       // first frame's rect.  But this may be unexpected.  For example,
    1986             :       // |<div contenteditable>[<p>]abc</p></div>|.  In this case, caret is
    1987             :       // before "a", therefore, users expect the rect left of "a".  However,
    1988             :       // we don't have enough information about the next character here and
    1989             :       // this isn't usual case (e.g., IME typically tries to query the rect
    1990             :       // of "a" or caret rect for computing its popup position).  Therefore,
    1991             :       // we shouldn't do more complicated hack here unless we'll get some bug
    1992             :       // reports actually.
    1993             :       else {
    1994           0 :         FrameRelativeRect relativeBRRect = GetLineBreakerRectBefore(firstFrame);
    1995           0 :         brRect = relativeBRRect.RectRelativeTo(firstFrame);
    1996             :       }
    1997           0 :       charRects.AppendElement(brRect);
    1998           0 :       chars.AssignLiteral("\n");
    1999           0 :       if (kBRLength > 1 && offset == aEvent->mInput.mOffset && offset) {
    2000             :         // If the first frame for the previous offset of the query range and
    2001             :         // the first frame for the start of query range are same, that means
    2002             :         // the start offset is between the first line breaker (i.e., the range
    2003             :         // starts between "\r" and "\n").
    2004           0 :         rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset - 1, 1,
    2005           0 :                                         lineBreakType, true, nullptr);
    2006           0 :         if (NS_WARN_IF(NS_FAILED(rv))) {
    2007           0 :           return NS_ERROR_UNEXPECTED;
    2008             :         }
    2009             :         FrameAndNodeOffset frameForPrevious =
    2010           0 :           GetFirstFrameInRangeForTextRect(range);
    2011           0 :         startsBetweenLineBreaker = frameForPrevious.mFrame == firstFrame.mFrame;
    2012             :       }
    2013             :     } else {
    2014             :       NS_WARNING("The frame is neither a text frame nor a frame whose content "
    2015           0 :                  "causes a line break");
    2016           0 :       return NS_ERROR_FAILURE;
    2017             :     }
    2018             : 
    2019           0 :     for (size_t i = 0; i < charRects.Length() && offset < kEndOffset; i++) {
    2020           0 :       nsRect charRect = charRects[i];
    2021             :       // Store lastCharRect before applying CSS transform because it may be
    2022             :       // used for computing a line breaker rect.  Then, the computed line
    2023             :       // breaker rect will be applied CSS transform again.  Therefore,
    2024             :       // the value of lastCharRect should be raw rect value relative to the
    2025             :       // base frame.
    2026           0 :       lastCharRect = charRect;
    2027           0 :       lastFrame = baseFrame;
    2028           0 :       rv = ConvertToRootRelativeOffset(baseFrame, charRect);
    2029           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2030           0 :         return rv;
    2031             :       }
    2032             : 
    2033             :       rect = LayoutDeviceIntRect::FromUnknownRect(
    2034           0 :                charRect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
    2035             :       // Returning empty rect may cause native IME confused, let's make sure to
    2036             :       // return non-empty rect.
    2037           0 :       EnsureNonEmptyRect(rect);
    2038             : 
    2039           0 :       aEvent->mReply.mRectArray.AppendElement(rect);
    2040           0 :       offset++;
    2041             : 
    2042             :       // If it's not a line breaker or the line breaker length is same as
    2043             :       // XP line breaker's, we need to do nothing for current character.
    2044           0 :       wasLineBreaker = chars[i] == '\n';
    2045           0 :       if (!wasLineBreaker || kBRLength == 1) {
    2046           0 :         continue;
    2047             :       }
    2048             : 
    2049           0 :       MOZ_ASSERT(kBRLength == 2);
    2050             : 
    2051             :       // If it's already reached the end of query range, we don't need to do
    2052             :       // anymore.
    2053           0 :       if (offset == kEndOffset) {
    2054           0 :         break;
    2055             :       }
    2056             : 
    2057             :       // If the query range starts from between a line breaker, i.e., it starts
    2058             :       // between "\r" and "\n", the appended rect was for the "\n".  Therefore,
    2059             :       // we don't need to append same rect anymore for current "\r\n".
    2060           0 :       if (startsBetweenLineBreaker) {
    2061           0 :         continue;
    2062             :       }
    2063             : 
    2064             :       // The appended rect was for "\r" of "\r\n".  Therefore, we need to
    2065             :       // append same rect for "\n" too because querying rect of "\r" and "\n"
    2066             :       // should return same rect.  E.g., IME may query previous character's
    2067             :       // rect of first character of a line.
    2068           0 :       aEvent->mReply.mRectArray.AppendElement(rect);
    2069           0 :       offset++;
    2070             :     }
    2071             :   }
    2072             : 
    2073             :   // If the query range is longer than actual content length, we should append
    2074             :   // caret rect at the end of the content as the last character rect because
    2075             :   // native IME may want to query character rect at the end of contents for
    2076             :   // deciding the position of a popup window (e.g., suggest window for next
    2077             :   // word).  Note that when this method hasn't appended character rects, it
    2078             :   // means that the offset is too large or the query range is collapsed.
    2079           0 :   if (offset < kEndOffset || aEvent->mReply.mRectArray.IsEmpty()) {
    2080             :     // If we've already retrieved some character rects before current offset,
    2081             :     // we can guess the last rect from the last character's rect unless it's a
    2082             :     // line breaker.  (If it's a line breaker, the caret rect is in next line.)
    2083           0 :     if (!aEvent->mReply.mRectArray.IsEmpty() && !wasLineBreaker) {
    2084           0 :       rect = aEvent->mReply.mRectArray.LastElement();
    2085           0 :       if (isVertical) {
    2086           0 :         rect.y = rect.YMost() + 1;
    2087           0 :         rect.height = 1;
    2088           0 :         MOZ_ASSERT(rect.width);
    2089             :       } else {
    2090           0 :         rect.x = rect.XMost() + 1;
    2091           0 :         rect.width = 1;
    2092           0 :         MOZ_ASSERT(rect.height);
    2093             :       }
    2094           0 :       aEvent->mReply.mRectArray.AppendElement(rect);
    2095             :     } else {
    2096             :       // Note that don't use eQueryCaretRect here because if caret is at the
    2097             :       // end of the content, it returns actual caret rect instead of computing
    2098             :       // the rect itself.  It means that the result depends on caret position.
    2099             :       // So, we shouldn't use it for consistency result in automated tests.
    2100           0 :       WidgetQueryContentEvent queryTextRect(eQueryTextRect, *aEvent);
    2101           0 :       WidgetQueryContentEvent::Options options(*aEvent);
    2102           0 :       queryTextRect.InitForQueryTextRect(offset, 1, options);
    2103           0 :       rv = OnQueryTextRect(&queryTextRect);
    2104           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2105           0 :         return rv;
    2106             :       }
    2107           0 :       if (NS_WARN_IF(!queryTextRect.mSucceeded)) {
    2108           0 :         return NS_ERROR_FAILURE;
    2109             :       }
    2110           0 :       MOZ_ASSERT(!queryTextRect.mReply.mRect.IsEmpty());
    2111           0 :       if (queryTextRect.mReply.mWritingMode.IsVertical()) {
    2112           0 :         queryTextRect.mReply.mRect.height = 1;
    2113             :       } else {
    2114           0 :         queryTextRect.mReply.mRect.width = 1;
    2115             :       }
    2116           0 :       aEvent->mReply.mRectArray.AppendElement(queryTextRect.mReply.mRect);
    2117             :     }
    2118             :   }
    2119             : 
    2120           0 :   aEvent->mSucceeded = true;
    2121           0 :   return NS_OK;
    2122             : }
    2123             : 
    2124             : nsresult
    2125           0 : ContentEventHandler::OnQueryTextRect(WidgetQueryContentEvent* aEvent)
    2126             : {
    2127           0 :   nsresult rv = Init(aEvent);
    2128           0 :   if (NS_FAILED(rv)) {
    2129           0 :     return rv;
    2130             :   }
    2131             : 
    2132             :   // If mLength is 0 (this may be caused by bug of native IME), we should
    2133             :   // redirect this event to OnQueryCaretRect().
    2134           0 :   if (!aEvent->mInput.mLength) {
    2135           0 :     return OnQueryCaretRect(aEvent);
    2136             :   }
    2137             : 
    2138           0 :   LineBreakType lineBreakType = GetLineBreakType(aEvent);
    2139           0 :   RefPtr<nsRange> range = new nsRange(mRootContent);
    2140           0 :   nsCOMPtr<nsIContent> lastTextContent;
    2141           0 :   rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
    2142             :                                   aEvent->mInput.mLength, lineBreakType, true,
    2143             :                                   &aEvent->mReply.mOffset,
    2144           0 :                                   getter_AddRefs(lastTextContent));
    2145           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2146           0 :   rv = GenerateFlatTextContent(range, aEvent->mReply.mString, lineBreakType);
    2147           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2148             : 
    2149             :   // used to iterate over all contents and their frames
    2150           0 :   nsCOMPtr<nsIContentIterator> iter = NS_NewContentIterator();
    2151           0 :   iter->Init(range);
    2152             : 
    2153             :   // Get the first frame which causes some text after the offset.
    2154           0 :   FrameAndNodeOffset firstFrame = GetFirstFrameInRangeForTextRect(range);
    2155             : 
    2156             :   // If GetFirstFrameInRangeForTextRect() does not return valid frame, that
    2157             :   // means that there are no visible frames having text or the offset reached
    2158             :   // the end of contents.
    2159           0 :   if (!firstFrame.IsValid()) {
    2160           0 :     nsAutoString allText;
    2161           0 :     rv = GenerateFlatTextContent(mRootContent, allText, lineBreakType);
    2162             :     // If the offset doesn't reach the end of contents but there is no frames
    2163             :     // for the node, that means that current offset's node is hidden by CSS or
    2164             :     // something.  Ideally, we should handle it with the last visible text
    2165             :     // node's last character's rect, but it's not usual cases in actual web
    2166             :     // services.  Therefore, currently, we should make this case fail.
    2167           0 :     if (NS_WARN_IF(NS_FAILED(rv)) ||
    2168           0 :         static_cast<uint32_t>(aEvent->mInput.mOffset) < allText.Length()) {
    2169           0 :       return NS_ERROR_FAILURE;
    2170             :     }
    2171             : 
    2172             :     // Look for the last frame which should be included text rects.
    2173           0 :     IgnoredErrorResult erv;
    2174           0 :     range->SelectNodeContents(*mRootContent, erv);
    2175           0 :     if (NS_WARN_IF(erv.Failed())) {
    2176           0 :       return NS_ERROR_UNEXPECTED;
    2177             :     }
    2178           0 :     nsRect rect;
    2179           0 :     FrameAndNodeOffset lastFrame = GetLastFrameInRangeForTextRect(range);
    2180             :     // If there is at least one frame which can be used for computing a rect
    2181             :     // for a character or a line breaker, we should use it for guessing the
    2182             :     // caret rect at the end of the contents.
    2183           0 :     if (lastFrame) {
    2184           0 :       if (NS_WARN_IF(!lastFrame->GetContent())) {
    2185           0 :         return NS_ERROR_FAILURE;
    2186             :       }
    2187           0 :       FrameRelativeRect relativeRect;
    2188             :       // If there is a <br> frame at the end, it represents an empty line at
    2189             :       // the end with moz-<br> or content <br> in a block level element.
    2190           0 :       if (lastFrame->IsBrFrame()) {
    2191           0 :         relativeRect = GetLineBreakerRectBefore(lastFrame);
    2192             :       }
    2193             :       // If there is a text frame at the end, use its information.
    2194           0 :       else if (lastFrame->IsTextFrame()) {
    2195           0 :         relativeRect = GuessLineBreakerRectAfter(lastFrame->GetContent());
    2196             :       }
    2197             :       // If there is an empty frame which is neither a text frame nor a <br>
    2198             :       // frame at the end, guess caret rect in it.
    2199             :       else {
    2200           0 :         relativeRect = GuessFirstCaretRectIn(lastFrame);
    2201             :       }
    2202           0 :       if (NS_WARN_IF(!relativeRect.IsValid())) {
    2203           0 :         return NS_ERROR_FAILURE;
    2204             :       }
    2205           0 :       rect = relativeRect.RectRelativeTo(lastFrame);
    2206           0 :       rv = ConvertToRootRelativeOffset(lastFrame, rect);
    2207           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2208           0 :         return rv;
    2209             :       }
    2210           0 :       aEvent->mReply.mWritingMode = lastFrame->GetWritingMode();
    2211             :     }
    2212             :     // Otherwise, if there are no contents in mRootContent, guess caret rect in
    2213             :     // its frame (with its font height and content box).
    2214             :     else {
    2215           0 :       nsIFrame* rootContentFrame = mRootContent->GetPrimaryFrame();
    2216           0 :       if (NS_WARN_IF(!rootContentFrame)) {
    2217           0 :         return NS_ERROR_FAILURE;
    2218             :       }
    2219           0 :       FrameRelativeRect relativeRect = GuessFirstCaretRectIn(rootContentFrame);
    2220           0 :       if (NS_WARN_IF(!relativeRect.IsValid())) {
    2221           0 :         return NS_ERROR_FAILURE;
    2222             :       }
    2223           0 :       rect = relativeRect.RectRelativeTo(rootContentFrame);
    2224           0 :       rv = ConvertToRootRelativeOffset(rootContentFrame, rect);
    2225           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2226           0 :         return rv;
    2227             :       }
    2228           0 :       aEvent->mReply.mWritingMode = rootContentFrame->GetWritingMode();
    2229             :     }
    2230             :     aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
    2231           0 :       rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
    2232           0 :     EnsureNonEmptyRect(aEvent->mReply.mRect);
    2233           0 :     aEvent->mSucceeded = true;
    2234           0 :     return NS_OK;
    2235             :   }
    2236             : 
    2237           0 :   nsRect rect, frameRect;
    2238           0 :   nsPoint ptOffset;
    2239             : 
    2240             :   // If the first frame is a text frame, the result should be computed with
    2241             :   // the frame's rect but not including the rect before start point of the
    2242             :   // queried range.
    2243           0 :   if (firstFrame->IsTextFrame()) {
    2244           0 :     rect.SetRect(nsPoint(0, 0), firstFrame->GetRect().Size());
    2245           0 :     rv = ConvertToRootRelativeOffset(firstFrame, rect);
    2246           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2247           0 :       return rv;
    2248             :     }
    2249           0 :     frameRect = rect;
    2250             :     // Exclude the rect before start point of the queried range.
    2251           0 :     firstFrame->GetPointFromOffset(firstFrame.mOffsetInNode, &ptOffset);
    2252           0 :     if (firstFrame->GetWritingMode().IsVertical()) {
    2253           0 :       rect.y += ptOffset.y;
    2254           0 :       rect.height -= ptOffset.y;
    2255             :     } else {
    2256           0 :       rect.x += ptOffset.x;
    2257           0 :       rect.width -= ptOffset.x;
    2258             :     }
    2259             :   }
    2260             :   // If first frame causes a line breaker but it's not a <br> frame, we cannot
    2261             :   // compute proper rect only with the frame because typically caret is at
    2262             :   // right of the last character of it.  For example, if caret is after "c" of
    2263             :   // |<p>abc</p><p>def</p>|, IME may query a line breaker's rect after "c".
    2264             :   // Then, if we compute it only with the 2nd <p>'s block frame, the result
    2265             :   // will be:
    2266             :   //  +-<p>--------------------------------+
    2267             :   //  |abc                                 |
    2268             :   //  +------------------------------------+
    2269             :   //
    2270             :   // I+-<p>--------------------------------+
    2271             :   //  |def                                 |
    2272             :   //  +------------------------------------+
    2273             :   // However, users expect popup windows of IME should be positioned at
    2274             :   // right-bottom of "c" like this:
    2275             :   //  +-<p>--------------------------------+
    2276             :   //  |abcI                                |
    2277             :   //  +------------------------------------+
    2278             :   //
    2279             :   //  +-<p>--------------------------------+
    2280             :   //  |def                                 |
    2281             :   //  +------------------------------------+
    2282             :   // Therefore, if the first frame isn't a <br> frame and there is a text
    2283             :   // node before the first node in the queried range, we should compute the
    2284             :   // first rect with the previous character's rect.
    2285           0 :   else if (!firstFrame->IsBrFrame() && lastTextContent) {
    2286             :     FrameRelativeRect brRectAfterLastChar =
    2287           0 :       GuessLineBreakerRectAfter(lastTextContent);
    2288           0 :     if (NS_WARN_IF(!brRectAfterLastChar.IsValid())) {
    2289           0 :       return NS_ERROR_FAILURE;
    2290             :     }
    2291           0 :     rect = brRectAfterLastChar.mRect;
    2292           0 :     rv = ConvertToRootRelativeOffset(brRectAfterLastChar.mBaseFrame, rect);
    2293           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2294           0 :       return rv;
    2295             :     }
    2296           0 :     frameRect = rect;
    2297             :   }
    2298             :   // Otherwise, we need to compute the line breaker's rect only with the
    2299             :   // first frame's rect.  But this may be unexpected.  For example,
    2300             :   // |<div contenteditable>[<p>]abc</p></div>|.  In this case, caret is before
    2301             :   // "a", therefore, users expect the rect left of "a".  However, we don't
    2302             :   // have enough information about the next character here and this isn't
    2303             :   // usual case (e.g., IME typically tries to query the rect of "a" or caret
    2304             :   // rect for computing its popup position).  Therefore, we shouldn't do
    2305             :   // more complicated hack here unless we'll get some bug reports actually.
    2306             :   else {
    2307           0 :     FrameRelativeRect relativeRect = GetLineBreakerRectBefore(firstFrame);
    2308           0 :     if (NS_WARN_IF(!relativeRect.IsValid())) {
    2309           0 :       return NS_ERROR_FAILURE;
    2310             :     }
    2311           0 :     rect = relativeRect.RectRelativeTo(firstFrame);
    2312           0 :     rv = ConvertToRootRelativeOffset(firstFrame, rect);
    2313           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2314           0 :       return rv;
    2315             :     }
    2316           0 :     frameRect = rect;
    2317             :   }
    2318             :   // UnionRect() requires non-empty rect.  So, let's make sure to get non-emtpy
    2319             :   // rect from the first frame.
    2320           0 :   EnsureNonEmptyRect(rect);
    2321             : 
    2322             :   // Get the last frame which causes some text in the range.
    2323           0 :   FrameAndNodeOffset lastFrame = GetLastFrameInRangeForTextRect(range);
    2324           0 :   if (NS_WARN_IF(!lastFrame.IsValid())) {
    2325           0 :     return NS_ERROR_FAILURE;
    2326             :   }
    2327             : 
    2328             :   // iterate over all covered frames
    2329           0 :   for (nsIFrame* frame = firstFrame; frame != lastFrame;) {
    2330           0 :     frame = frame->GetNextContinuation();
    2331           0 :     if (!frame) {
    2332           0 :       do {
    2333           0 :         iter->Next();
    2334           0 :         nsINode* node = iter->GetCurrentNode();
    2335           0 :         if (!node) {
    2336           0 :           break;
    2337             :         }
    2338           0 :         if (!node->IsNodeOfType(nsINode::eCONTENT)) {
    2339           0 :           continue;
    2340             :         }
    2341           0 :         nsIFrame* primaryFrame = node->AsContent()->GetPrimaryFrame();
    2342             :         // The node may be hidden by CSS.
    2343           0 :         if (!primaryFrame) {
    2344           0 :           continue;
    2345             :         }
    2346             :         // We should take only text frame's rect and br frame's rect.  We can
    2347             :         // always use frame rect of text frame and GetLineBreakerRectBefore()
    2348             :         // can return exactly correct rect only for <br> frame for now.  On the
    2349             :         // other hand, GetLineBreakRectBefore() returns guessed caret rect for
    2350             :         // the other frames.  We shouldn't include such odd rect to the result.
    2351           0 :         if (primaryFrame->IsTextFrame() || primaryFrame->IsBrFrame()) {
    2352           0 :           frame = primaryFrame;
    2353             :         }
    2354           0 :       } while (!frame && !iter->IsDone());
    2355           0 :       if (!frame) {
    2356           0 :         break;
    2357             :       }
    2358             :     }
    2359           0 :     if (frame->IsTextFrame()) {
    2360           0 :       frameRect.SetRect(nsPoint(0, 0), frame->GetRect().Size());
    2361             :     } else {
    2362           0 :       MOZ_ASSERT(frame->IsBrFrame());
    2363           0 :       FrameRelativeRect relativeRect = GetLineBreakerRectBefore(frame);
    2364           0 :       if (NS_WARN_IF(!relativeRect.IsValid())) {
    2365           0 :         return NS_ERROR_FAILURE;
    2366             :       }
    2367           0 :       frameRect = relativeRect.RectRelativeTo(frame);
    2368             :     }
    2369           0 :     rv = ConvertToRootRelativeOffset(frame, frameRect);
    2370           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2371           0 :       return rv;
    2372             :     }
    2373             :     // UnionRect() requires non-empty rect.  So, let's make sure to get
    2374             :     // non-emtpy rect from the frame.
    2375           0 :     EnsureNonEmptyRect(frameRect);
    2376           0 :     if (frame != lastFrame) {
    2377             :       // not last frame, so just add rect to previous result
    2378           0 :       rect.UnionRect(rect, frameRect);
    2379             :     }
    2380             :   }
    2381             : 
    2382             :   // Get the ending frame rect.
    2383             :   // FYI: If first frame and last frame are same, frameRect is already set
    2384             :   //      to the rect excluding the text before the query range.
    2385           0 :   if (firstFrame.mFrame != lastFrame.mFrame) {
    2386           0 :     frameRect.SetRect(nsPoint(0, 0), lastFrame->GetRect().Size());
    2387           0 :     rv = ConvertToRootRelativeOffset(lastFrame, frameRect);
    2388           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2389           0 :       return rv;
    2390             :     }
    2391             :   }
    2392             : 
    2393             :   // Shrink the last frame for cutting off the text after the query range.
    2394           0 :   if (lastFrame->IsTextFrame()) {
    2395           0 :     lastFrame->GetPointFromOffset(lastFrame.mOffsetInNode, &ptOffset);
    2396           0 :     if (lastFrame->GetWritingMode().IsVertical()) {
    2397           0 :       frameRect.height -= lastFrame->GetRect().height - ptOffset.y;
    2398             :     } else {
    2399           0 :       frameRect.width -= lastFrame->GetRect().width - ptOffset.x;
    2400             :     }
    2401             :     // UnionRect() requires non-empty rect.  So, let's make sure to get
    2402             :     // non-empty rect from the last frame.
    2403           0 :     EnsureNonEmptyRect(frameRect);
    2404             : 
    2405           0 :     if (firstFrame.mFrame == lastFrame.mFrame) {
    2406           0 :       rect.IntersectRect(rect, frameRect);
    2407             :     } else {
    2408           0 :       rect.UnionRect(rect, frameRect);
    2409             :     }
    2410             :   }
    2411             : 
    2412             :   aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
    2413           0 :       rect.ToOutsidePixels(mPresContext->AppUnitsPerDevPixel()));
    2414             :   // Returning empty rect may cause native IME confused, let's make sure to
    2415             :   // return non-empty rect.
    2416           0 :   EnsureNonEmptyRect(aEvent->mReply.mRect);
    2417           0 :   aEvent->mReply.mWritingMode = lastFrame->GetWritingMode();
    2418           0 :   aEvent->mSucceeded = true;
    2419           0 :   return NS_OK;
    2420             : }
    2421             : 
    2422             : nsresult
    2423           0 : ContentEventHandler::OnQueryEditorRect(WidgetQueryContentEvent* aEvent)
    2424             : {
    2425           0 :   nsresult rv = Init(aEvent);
    2426           0 :   if (NS_FAILED(rv)) {
    2427           0 :     return rv;
    2428             :   }
    2429             : 
    2430           0 :   nsIContent* focusedContent = GetFocusedContent();
    2431           0 :   rv = QueryContentRect(IsPlugin(focusedContent) ?
    2432           0 :                           focusedContent : mRootContent.get(), aEvent);
    2433           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2434           0 :   return NS_OK;
    2435             : }
    2436             : 
    2437             : nsresult
    2438           0 : ContentEventHandler::OnQueryCaretRect(WidgetQueryContentEvent* aEvent)
    2439             : {
    2440           0 :   nsresult rv = Init(aEvent);
    2441           0 :   if (NS_FAILED(rv)) {
    2442           0 :     return rv;
    2443             :   }
    2444             : 
    2445             :   // When the selection is collapsed and the queried offset is current caret
    2446             :   // position, we should return the "real" caret rect.
    2447           0 :   if (mSelection->IsCollapsed()) {
    2448           0 :     nsRect caretRect;
    2449           0 :     nsIFrame* caretFrame = nsCaret::GetGeometry(mSelection, &caretRect);
    2450           0 :     if (caretFrame) {
    2451             :       uint32_t offset;
    2452           0 :       rv = GetStartOffset(mFirstSelectedRange,
    2453           0 :                           &offset, GetLineBreakType(aEvent));
    2454           0 :       NS_ENSURE_SUCCESS(rv, rv);
    2455           0 :       if (offset == aEvent->mInput.mOffset) {
    2456           0 :         rv = ConvertToRootRelativeOffset(caretFrame, caretRect);
    2457           0 :         NS_ENSURE_SUCCESS(rv, rv);
    2458             :         nscoord appUnitsPerDevPixel =
    2459           0 :           caretFrame->PresContext()->AppUnitsPerDevPixel();
    2460             :         aEvent->mReply.mRect = LayoutDeviceIntRect::FromUnknownRect(
    2461           0 :           caretRect.ToOutsidePixels(appUnitsPerDevPixel));
    2462             :         // Returning empty rect may cause native IME confused, let's make sure
    2463             :         // to return non-empty rect.
    2464           0 :         EnsureNonEmptyRect(aEvent->mReply.mRect);
    2465           0 :         aEvent->mReply.mWritingMode = caretFrame->GetWritingMode();
    2466           0 :         aEvent->mReply.mOffset = aEvent->mInput.mOffset;
    2467           0 :         aEvent->mSucceeded = true;
    2468           0 :         return NS_OK;
    2469             :       }
    2470             :     }
    2471             :   }
    2472             : 
    2473             :   // Otherwise, we should guess the caret rect from the character's rect.
    2474           0 :   WidgetQueryContentEvent queryTextRectEvent(eQueryTextRect, *aEvent);
    2475           0 :   WidgetQueryContentEvent::Options options(*aEvent);
    2476           0 :   queryTextRectEvent.InitForQueryTextRect(aEvent->mInput.mOffset, 1, options);
    2477           0 :   rv = OnQueryTextRect(&queryTextRectEvent);
    2478           0 :   if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!queryTextRectEvent.mSucceeded)) {
    2479           0 :     return NS_ERROR_FAILURE;
    2480             :   }
    2481           0 :   queryTextRectEvent.mReply.mString.Truncate();
    2482           0 :   aEvent->mReply = queryTextRectEvent.mReply;
    2483           0 :   if (aEvent->GetWritingMode().IsVertical()) {
    2484           0 :     aEvent->mReply.mRect.height = 1;
    2485             :   } else {
    2486           0 :     aEvent->mReply.mRect.width = 1;
    2487             :   }
    2488             :   // Returning empty rect may cause native IME confused, let's make sure to
    2489             :   // return non-empty rect.
    2490           0 :   aEvent->mSucceeded = true;
    2491           0 :   return NS_OK;
    2492             : }
    2493             : 
    2494             : nsresult
    2495           0 : ContentEventHandler::OnQueryContentState(WidgetQueryContentEvent* aEvent)
    2496             : {
    2497           0 :   nsresult rv = Init(aEvent);
    2498           0 :   if (NS_FAILED(rv)) {
    2499           0 :     return rv;
    2500             :   }
    2501           0 :   aEvent->mSucceeded = true;
    2502           0 :   return NS_OK;
    2503             : }
    2504             : 
    2505             : nsresult
    2506           0 : ContentEventHandler::OnQuerySelectionAsTransferable(
    2507             :                        WidgetQueryContentEvent* aEvent)
    2508             : {
    2509           0 :   nsresult rv = Init(aEvent);
    2510           0 :   if (NS_FAILED(rv)) {
    2511           0 :     return rv;
    2512             :   }
    2513             : 
    2514           0 :   if (!aEvent->mReply.mHasSelection) {
    2515           0 :     aEvent->mSucceeded = true;
    2516           0 :     aEvent->mReply.mTransferable = nullptr;
    2517           0 :     return NS_OK;
    2518             :   }
    2519             : 
    2520           0 :   nsCOMPtr<nsIDocument> doc = mPresShell->GetDocument();
    2521           0 :   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
    2522             : 
    2523           0 :   rv = nsCopySupport::GetTransferableForSelection(
    2524           0 :          mSelection, doc, getter_AddRefs(aEvent->mReply.mTransferable));
    2525           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2526             : 
    2527           0 :   aEvent->mSucceeded = true;
    2528           0 :   return NS_OK;
    2529             : }
    2530             : 
    2531             : nsresult
    2532           0 : ContentEventHandler::OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent)
    2533             : {
    2534           0 :   nsresult rv = Init(aEvent);
    2535           0 :   if (NS_FAILED(rv)) {
    2536           0 :     return rv;
    2537             :   }
    2538             : 
    2539           0 :   aEvent->mReply.mOffset = aEvent->mReply.mTentativeCaretOffset =
    2540             :     WidgetQueryContentEvent::NOT_FOUND;
    2541             : 
    2542           0 :   nsIFrame* rootFrame = mPresShell->GetRootFrame();
    2543           0 :   NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
    2544           0 :   nsIWidget* rootWidget = rootFrame->GetNearestWidget();
    2545           0 :   NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
    2546             : 
    2547             :   // The root frame's widget might be different, e.g., the event was fired on
    2548             :   // a popup but the rootFrame is the document root.
    2549           0 :   if (rootWidget != aEvent->mWidget) {
    2550           0 :     NS_PRECONDITION(aEvent->mWidget, "The event must have the widget");
    2551           0 :     nsView* view = nsView::GetViewFor(aEvent->mWidget);
    2552           0 :     NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
    2553           0 :     rootFrame = view->GetFrame();
    2554           0 :     NS_ENSURE_TRUE(rootFrame, NS_ERROR_FAILURE);
    2555           0 :     rootWidget = rootFrame->GetNearestWidget();
    2556           0 :     NS_ENSURE_TRUE(rootWidget, NS_ERROR_FAILURE);
    2557             :   }
    2558             : 
    2559             :   WidgetQueryContentEvent eventOnRoot(true, eQueryCharacterAtPoint,
    2560           0 :                                       rootWidget);
    2561           0 :   eventOnRoot.mUseNativeLineBreak = aEvent->mUseNativeLineBreak;
    2562           0 :   eventOnRoot.mRefPoint = aEvent->mRefPoint;
    2563           0 :   if (rootWidget != aEvent->mWidget) {
    2564           0 :     eventOnRoot.mRefPoint += aEvent->mWidget->WidgetToScreenOffset() -
    2565           0 :       rootWidget->WidgetToScreenOffset();
    2566             :   }
    2567             :   nsPoint ptInRoot =
    2568           0 :     nsLayoutUtils::GetEventCoordinatesRelativeTo(&eventOnRoot, rootFrame);
    2569             : 
    2570           0 :   nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot);
    2571           0 :   if (!targetFrame || !targetFrame->GetContent() ||
    2572           0 :       !nsContentUtils::ContentIsDescendantOf(targetFrame->GetContent(),
    2573           0 :                                              mRootContent)) {
    2574             :     // There is no character at the point.
    2575           0 :     aEvent->mSucceeded = true;
    2576           0 :     return NS_OK;
    2577             :   }
    2578           0 :   nsPoint ptInTarget = ptInRoot + rootFrame->GetOffsetToCrossDoc(targetFrame);
    2579           0 :   int32_t rootAPD = rootFrame->PresContext()->AppUnitsPerDevPixel();
    2580           0 :   int32_t targetAPD = targetFrame->PresContext()->AppUnitsPerDevPixel();
    2581           0 :   ptInTarget = ptInTarget.ScaleToOtherAppUnits(rootAPD, targetAPD);
    2582             : 
    2583             :   nsIFrame::ContentOffsets tentativeCaretOffsets =
    2584           0 :     targetFrame->GetContentOffsetsFromPoint(ptInTarget);
    2585           0 :   if (!tentativeCaretOffsets.content ||
    2586           0 :       !nsContentUtils::ContentIsDescendantOf(tentativeCaretOffsets.content,
    2587           0 :                                              mRootContent)) {
    2588             :     // There is no character nor tentative caret point at the point.
    2589           0 :     aEvent->mSucceeded = true;
    2590           0 :     return NS_OK;
    2591             :   }
    2592             : 
    2593           0 :   rv = GetFlatTextLengthInRange(NodePosition(mRootContent, 0),
    2594           0 :                                 NodePosition(tentativeCaretOffsets),
    2595             :                                 mRootContent,
    2596             :                                 &aEvent->mReply.mTentativeCaretOffset,
    2597           0 :                                 GetLineBreakType(aEvent));
    2598           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2599           0 :     return rv;
    2600             :   }
    2601             : 
    2602           0 :   if (!targetFrame->IsTextFrame()) {
    2603             :     // There is no character at the point but there is tentative caret point.
    2604           0 :     aEvent->mSucceeded = true;
    2605           0 :     return NS_OK;
    2606             :   }
    2607             : 
    2608           0 :   MOZ_ASSERT(
    2609             :     aEvent->mReply.mTentativeCaretOffset != WidgetQueryContentEvent::NOT_FOUND,
    2610             :     "The point is inside a character bounding box.  Why tentative caret point "
    2611             :     "hasn't been found?");
    2612             : 
    2613           0 :   nsTextFrame* textframe = static_cast<nsTextFrame*>(targetFrame);
    2614             :   nsIFrame::ContentOffsets contentOffsets =
    2615           0 :     textframe->GetCharacterOffsetAtFramePoint(ptInTarget);
    2616           0 :   NS_ENSURE_TRUE(contentOffsets.content, NS_ERROR_FAILURE);
    2617             :   uint32_t offset;
    2618           0 :   rv = GetFlatTextLengthInRange(NodePosition(mRootContent, 0),
    2619           0 :                                 NodePosition(contentOffsets),
    2620             :                                 mRootContent, &offset,
    2621           0 :                                 GetLineBreakType(aEvent));
    2622           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2623           0 :     return rv;
    2624             :   }
    2625             : 
    2626           0 :   WidgetQueryContentEvent textRect(true, eQueryTextRect, aEvent->mWidget);
    2627           0 :   WidgetQueryContentEvent::Options options(*aEvent);
    2628           0 :   textRect.InitForQueryTextRect(offset, 1, options);
    2629           0 :   rv = OnQueryTextRect(&textRect);
    2630           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2631           0 :   NS_ENSURE_TRUE(textRect.mSucceeded, NS_ERROR_FAILURE);
    2632             : 
    2633             :   // currently, we don't need to get the actual text.
    2634           0 :   aEvent->mReply.mOffset = offset;
    2635           0 :   aEvent->mReply.mRect = textRect.mReply.mRect;
    2636           0 :   aEvent->mSucceeded = true;
    2637           0 :   return NS_OK;
    2638             : }
    2639             : 
    2640             : nsresult
    2641           0 : ContentEventHandler::OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent)
    2642             : {
    2643           0 :   NS_ASSERTION(aEvent, "aEvent must not be null");
    2644             : 
    2645           0 :   nsresult rv = InitBasic();
    2646           0 :   if (NS_FAILED(rv)) {
    2647           0 :     return rv;
    2648             :   }
    2649             : 
    2650           0 :   aEvent->mSucceeded = false;
    2651           0 :   aEvent->mReply.mWidgetIsHit = false;
    2652             : 
    2653           0 :   NS_ENSURE_TRUE(aEvent->mWidget, NS_ERROR_FAILURE);
    2654             : 
    2655           0 :   nsIDocument* doc = mPresShell->GetDocument();
    2656           0 :   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
    2657           0 :   nsIFrame* docFrame = mPresShell->GetRootFrame();
    2658           0 :   NS_ENSURE_TRUE(docFrame, NS_ERROR_FAILURE);
    2659             : 
    2660             :   LayoutDeviceIntPoint eventLoc =
    2661           0 :     aEvent->mRefPoint + aEvent->mWidget->WidgetToScreenOffset();
    2662           0 :   CSSIntRect docFrameRect = docFrame->GetScreenRect();
    2663             :   CSSIntPoint eventLocCSS(
    2664           0 :     mPresContext->DevPixelsToIntCSSPixels(eventLoc.x) - docFrameRect.x,
    2665           0 :     mPresContext->DevPixelsToIntCSSPixels(eventLoc.y) - docFrameRect.y);
    2666             : 
    2667             :   Element* contentUnderMouse =
    2668           0 :     doc->ElementFromPointHelper(eventLocCSS.x, eventLocCSS.y, false, false);
    2669           0 :   if (contentUnderMouse) {
    2670           0 :     nsIWidget* targetWidget = nullptr;
    2671           0 :     nsIFrame* targetFrame = contentUnderMouse->GetPrimaryFrame();
    2672           0 :     nsIObjectFrame* pluginFrame = do_QueryFrame(targetFrame);
    2673           0 :     if (pluginFrame) {
    2674           0 :       targetWidget = pluginFrame->GetWidget();
    2675           0 :     } else if (targetFrame) {
    2676           0 :       targetWidget = targetFrame->GetNearestWidget();
    2677             :     }
    2678           0 :     if (aEvent->mWidget == targetWidget) {
    2679           0 :       aEvent->mReply.mWidgetIsHit = true;
    2680             :     }
    2681             :   }
    2682             : 
    2683           0 :   aEvent->mSucceeded = true;
    2684           0 :   return NS_OK;
    2685             : }
    2686             : 
    2687             : /* static */ nsresult
    2688           0 : ContentEventHandler::GetFlatTextLengthInRange(
    2689             :                        const NodePosition& aStartPosition,
    2690             :                        const NodePosition& aEndPosition,
    2691             :                        nsIContent* aRootContent,
    2692             :                        uint32_t* aLength,
    2693             :                        LineBreakType aLineBreakType,
    2694             :                        bool aIsRemovingNode /* = false */)
    2695             : {
    2696           0 :   if (NS_WARN_IF(!aRootContent) || NS_WARN_IF(!aStartPosition.IsValid()) ||
    2697           0 :       NS_WARN_IF(!aEndPosition.IsValid()) || NS_WARN_IF(!aLength)) {
    2698           0 :     return NS_ERROR_INVALID_ARG;
    2699             :   }
    2700             : 
    2701           0 :   if (aStartPosition == aEndPosition) {
    2702           0 :     *aLength = 0;
    2703           0 :     return NS_OK;
    2704             :   }
    2705             : 
    2706             :   // Don't create nsContentIterator instance until it's really necessary since
    2707             :   // destroying without initializing causes unexpected NS_ASSERTION() call.
    2708           0 :   nsCOMPtr<nsIContentIterator> iter;
    2709             : 
    2710             :   // Working with ContentIterator, we may need to adjust the end position for
    2711             :   // including it forcibly.
    2712           0 :   NodePosition endPosition(aEndPosition);
    2713             : 
    2714             :   // This may be called for retrieving the text of removed nodes.  Even in this
    2715             :   // case, the node thinks it's still in the tree because UnbindFromTree() will
    2716             :   // be called after here.  However, the node was already removed from the
    2717             :   // array of children of its parent.  So, be careful to handle this case.
    2718           0 :   if (aIsRemovingNode) {
    2719           0 :     DebugOnly<nsIContent*> parent = aStartPosition.mNode->GetParent();
    2720           0 :     MOZ_ASSERT(parent && parent->IndexOf(aStartPosition.mNode) == -1,
    2721             :       "At removing the node, the node shouldn't be in the array of children "
    2722             :       "of its parent");
    2723           0 :     MOZ_ASSERT(aStartPosition.mNode == endPosition.mNode,
    2724             :       "At removing the node, start and end node should be same");
    2725           0 :     MOZ_ASSERT(aStartPosition.mOffset == 0,
    2726             :       "When the node is being removed, the start offset should be 0");
    2727           0 :     MOZ_ASSERT(static_cast<uint32_t>(endPosition.mOffset) ==
    2728             :                  endPosition.mNode->GetChildCount(),
    2729             :       "When the node is being removed, the end offset should be child count");
    2730           0 :     iter = NS_NewPreContentIterator();
    2731           0 :     nsresult rv = iter->Init(aStartPosition.mNode);
    2732           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2733           0 :       return rv;
    2734             :     }
    2735             :   } else {
    2736           0 :     RefPtr<nsRange> prev = new nsRange(aRootContent);
    2737           0 :     nsresult rv = aStartPosition.SetToRangeStart(prev);
    2738           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
    2739           0 :       return rv;
    2740             :     }
    2741             : 
    2742             :     // When the end position is immediately after non-root element's open tag,
    2743             :     // we need to include a line break caused by the open tag.
    2744           0 :     if (endPosition.mNode != aRootContent &&
    2745           0 :         endPosition.IsImmediatelyAfterOpenTag()) {
    2746           0 :       if (endPosition.mNode->HasChildren()) {
    2747             :         // When the end node has some children, move the end position to before
    2748             :         // the open tag of its first child.
    2749           0 :         nsINode* firstChild = endPosition.mNode->GetFirstChild();
    2750           0 :         if (NS_WARN_IF(!firstChild)) {
    2751           0 :           return NS_ERROR_FAILURE;
    2752             :         }
    2753           0 :         endPosition = NodePositionBefore(firstChild, 0);
    2754             :       } else {
    2755             :         // When the end node is empty, move the end position after the node.
    2756           0 :         nsIContent* parentContent = endPosition.mNode->GetParent();
    2757           0 :         if (NS_WARN_IF(!parentContent)) {
    2758           0 :           return NS_ERROR_FAILURE;
    2759             :         }
    2760           0 :         int32_t indexInParent = parentContent->IndexOf(endPosition.mNode);
    2761           0 :         if (NS_WARN_IF(indexInParent < 0)) {
    2762           0 :           return NS_ERROR_FAILURE;
    2763             :         }
    2764           0 :         endPosition = NodePositionBefore(parentContent, indexInParent + 1);
    2765             :       }
    2766             :     }
    2767             : 
    2768           0 :     if (endPosition.OffsetIsValid()) {
    2769             :       // Offset is within node's length; set end of range to that offset
    2770           0 :       rv = endPosition.SetToRangeEnd(prev);
    2771           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2772           0 :         return rv;
    2773             :       }
    2774           0 :       iter = NS_NewPreContentIterator();
    2775           0 :       rv = iter->Init(prev);
    2776           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2777           0 :         return rv;
    2778             :       }
    2779           0 :     } else if (endPosition.mNode != aRootContent) {
    2780             :       // Offset is past node's length; set end of range to end of node
    2781           0 :       rv = endPosition.SetToRangeEndAfter(prev);
    2782           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2783           0 :         return rv;
    2784             :       }
    2785           0 :       iter = NS_NewPreContentIterator();
    2786           0 :       rv = iter->Init(prev);
    2787           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2788           0 :         return rv;
    2789             :       }
    2790             :     } else {
    2791             :       // Offset is past the root node; set end of range to end of root node
    2792           0 :       iter = NS_NewPreContentIterator();
    2793           0 :       rv = iter->Init(aRootContent);
    2794           0 :       if (NS_WARN_IF(NS_FAILED(rv))) {
    2795           0 :         return rv;
    2796             :       }
    2797             :     }
    2798             :   }
    2799             : 
    2800           0 :   *aLength = 0;
    2801           0 :   for (; !iter->IsDone(); iter->Next()) {
    2802           0 :     nsINode* node = iter->GetCurrentNode();
    2803           0 :     if (NS_WARN_IF(!node)) {
    2804           0 :       break;
    2805             :     }
    2806           0 :     if (!node->IsContent()) {
    2807           0 :       continue;
    2808             :     }
    2809           0 :     nsIContent* content = node->AsContent();
    2810             : 
    2811           0 :     if (node->IsNodeOfType(nsINode::eTEXT)) {
    2812             :       // Note: our range always starts from offset 0
    2813           0 :       if (node == endPosition.mNode) {
    2814           0 :         *aLength += GetTextLength(content, aLineBreakType,
    2815           0 :                                   endPosition.mOffset);
    2816             :       } else {
    2817           0 :         *aLength += GetTextLength(content, aLineBreakType);
    2818             :       }
    2819           0 :     } else if (ShouldBreakLineBefore(content, aRootContent)) {
    2820             :       // If the start position is start of this node but doesn't include the
    2821             :       // open tag, don't append the line break length.
    2822           0 :       if (node == aStartPosition.mNode && !aStartPosition.IsBeforeOpenTag()) {
    2823           0 :         continue;
    2824             :       }
    2825             :       // If the end position is before the open tag, don't append the line
    2826             :       // break length.
    2827           0 :       if (node == endPosition.mNode && endPosition.IsBeforeOpenTag()) {
    2828           0 :         continue;
    2829             :       }
    2830           0 :       *aLength += GetBRLength(aLineBreakType);
    2831             :     }
    2832             :   }
    2833           0 :   return NS_OK;
    2834             : }
    2835             : 
    2836             : nsresult
    2837           0 : ContentEventHandler::GetStartOffset(nsRange* aRange,
    2838             :                                     uint32_t* aOffset,
    2839             :                                     LineBreakType aLineBreakType)
    2840             : {
    2841           0 :   MOZ_ASSERT(aRange);
    2842           0 :   return GetFlatTextLengthInRange(
    2843           0 :            NodePosition(mRootContent, 0),
    2844           0 :            NodePosition(aRange->GetStartContainer(), aRange->StartOffset()),
    2845           0 :            mRootContent, aOffset, aLineBreakType);
    2846             : }
    2847             : 
    2848             : nsresult
    2849           0 : ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aRange)
    2850             : {
    2851           0 :   MOZ_ASSERT(aRange);
    2852           0 :   MOZ_ASSERT(aRange->Collapsed());
    2853             : 
    2854           0 :   if (!aRange || !aRange->Collapsed()) {
    2855           0 :     return NS_ERROR_INVALID_ARG;
    2856             :   }
    2857             : 
    2858           0 :   nsCOMPtr<nsINode> container = aRange->GetStartContainer();
    2859           0 :   int32_t offsetInParentNode = aRange->StartOffset();
    2860           0 :   if (NS_WARN_IF(!container) || NS_WARN_IF(offsetInParentNode < 0)) {
    2861           0 :     return NS_ERROR_INVALID_ARG;
    2862             :   }
    2863             : 
    2864             :   // If the node is text node, we don't need to modify aRange.
    2865           0 :   if (container->IsNodeOfType(nsINode::eTEXT)) {
    2866           0 :     return NS_OK;
    2867             :   }
    2868             : 
    2869             :   // If the container is not a text node but it has a text node at the offset,
    2870             :   // we should adjust the range into the text node.
    2871             :   // NOTE: This is emulating similar situation of EditorBase.
    2872           0 :   nsINode* childNode = nullptr;
    2873           0 :   int32_t offsetInChildNode = -1;
    2874           0 :   if (!offsetInParentNode && container->HasChildren()) {
    2875             :     // If the range is the start of the container, adjusted the range to the
    2876             :     // start of the first child.
    2877           0 :     childNode = container->GetFirstChild();
    2878           0 :     offsetInChildNode = 0;
    2879           0 :   } else if (static_cast<uint32_t>(offsetInParentNode) <
    2880           0 :                container->GetChildCount()) {
    2881             :     // If the range is next to a child node, adjust the range to the end of
    2882             :     // the previous child.
    2883           0 :     childNode = container->GetChildAt(offsetInParentNode - 1);
    2884           0 :     offsetInChildNode = childNode->Length();
    2885             :   }
    2886             : 
    2887             :   // But if the found node isn't a text node, we cannot modify the range.
    2888           0 :   if (!childNode || !childNode->IsNodeOfType(nsINode::eTEXT) ||
    2889           0 :       NS_WARN_IF(offsetInChildNode < 0)) {
    2890           0 :     return NS_OK;
    2891             :   }
    2892             : 
    2893           0 :   nsresult rv = aRange->CollapseTo(childNode, offsetInChildNode);
    2894           0 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    2895           0 :     return rv;
    2896             :   }
    2897           0 :   return NS_OK;
    2898             : }
    2899             : 
    2900             : nsresult
    2901           0 : ContentEventHandler::GetStartFrameAndOffset(const nsRange* aRange,
    2902             :                                             nsIFrame*& aFrame,
    2903             :                                             int32_t& aOffsetInFrame)
    2904             : {
    2905           0 :   MOZ_ASSERT(aRange);
    2906             : 
    2907           0 :   aFrame = nullptr;
    2908           0 :   aOffsetInFrame = -1;
    2909             : 
    2910           0 :   nsINode* node = aRange->GetStartContainer();
    2911           0 :   if (NS_WARN_IF(!node) ||
    2912           0 :       NS_WARN_IF(!node->IsNodeOfType(nsINode::eCONTENT))) {
    2913           0 :     return NS_ERROR_FAILURE;
    2914             :   }
    2915           0 :   nsIContent* content = static_cast<nsIContent*>(node);
    2916           0 :   RefPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
    2917           0 :   aFrame = fs->GetFrameForNodeOffset(content, aRange->StartOffset(),
    2918             :                                      fs->GetHint(), &aOffsetInFrame);
    2919           0 :   if (NS_WARN_IF(!aFrame)) {
    2920           0 :     return NS_ERROR_FAILURE;
    2921             :   }
    2922           0 :   return NS_OK;
    2923             : }
    2924             : 
    2925             : nsresult
    2926           0 : ContentEventHandler::ConvertToRootRelativeOffset(nsIFrame* aFrame,
    2927             :                                                  nsRect& aRect)
    2928             : {
    2929           0 :   NS_ASSERTION(aFrame, "aFrame must not be null");
    2930             : 
    2931           0 :   nsPresContext* thisPC = aFrame->PresContext();
    2932           0 :   nsPresContext* rootPC = thisPC->GetRootPresContext();
    2933           0 :   if (NS_WARN_IF(!rootPC)) {
    2934           0 :     return NS_ERROR_FAILURE;
    2935             :   }
    2936           0 :   nsIFrame* rootFrame = rootPC->PresShell()->GetRootFrame();
    2937           0 :   if (NS_WARN_IF(!rootFrame)) {
    2938           0 :     return NS_ERROR_FAILURE;
    2939             :   }
    2940             : 
    2941           0 :   aRect = nsLayoutUtils::TransformFrameRectToAncestor(aFrame, aRect, rootFrame);
    2942             : 
    2943             :   // TransformFrameRectToAncestor returned the rect in the ancestor's appUnits,
    2944             :   // but we want it in aFrame's units (in case of different full-zoom factors),
    2945             :   // so convert back.
    2946           0 :   aRect = aRect.ScaleToOtherAppUnitsRoundOut(rootPC->AppUnitsPerDevPixel(),
    2947             :                                              thisPC->AppUnitsPerDevPixel());
    2948             : 
    2949           0 :   return NS_OK;
    2950             : }
    2951             : 
    2952           0 : static void AdjustRangeForSelection(nsIContent* aRoot,
    2953             :                                     nsINode** aNode,
    2954             :                                     int32_t* aNodeOffset)
    2955             : {
    2956           0 :   nsINode* node = *aNode;
    2957           0 :   int32_t nodeOffset = *aNodeOffset;
    2958           0 :   if (aRoot == node || NS_WARN_IF(!node->GetParent()) ||
    2959           0 :       !node->IsNodeOfType(nsINode::eTEXT)) {
    2960           0 :     return;
    2961             :   }
    2962             : 
    2963             :   // When the offset is at the end of the text node, set it to after the
    2964             :   // text node, to make sure the caret is drawn on a new line when the last
    2965             :   // character of the text node is '\n' in <textarea>.
    2966             :   int32_t textLength =
    2967           0 :     static_cast<int32_t>(static_cast<nsIContent*>(node)->TextLength());
    2968           0 :   MOZ_ASSERT(nodeOffset <= textLength, "Offset is past length of text node");
    2969           0 :   if (nodeOffset != textLength) {
    2970           0 :     return;
    2971             :   }
    2972             : 
    2973           0 :   nsIContent* aRootParent = aRoot->GetParent();
    2974           0 :   if (NS_WARN_IF(!aRootParent)) {
    2975           0 :     return;
    2976             :   }
    2977             :   // If the root node is not an anonymous div of <textarea>, we don't need to
    2978             :   // do this hack.  If you did this, ContentEventHandler couldn't distinguish
    2979             :   // if the range includes open tag of the next node in some cases, e.g.,
    2980             :   // textNode]<p></p> vs. textNode<p>]</p>
    2981           0 :   if (!aRootParent->IsHTMLElement(nsGkAtoms::textarea)) {
    2982           0 :     return;
    2983             :   }
    2984             : 
    2985           0 :   *aNode = node->GetParent();
    2986           0 :   MOZ_ASSERT((*aNode)->IndexOf(node) != -1);
    2987           0 :   *aNodeOffset = (*aNode)->IndexOf(node) + 1;
    2988             : }
    2989             : 
    2990             : nsresult
    2991           0 : ContentEventHandler::OnSelectionEvent(WidgetSelectionEvent* aEvent)
    2992             : {
    2993           0 :   aEvent->mSucceeded = false;
    2994             : 
    2995             :   // Get selection to manipulate
    2996             :   // XXX why do we need to get them from ISM? This method should work fine
    2997             :   //     without ISM.
    2998           0 :   nsCOMPtr<nsISelection> sel;
    2999             :   nsresult rv =
    3000           0 :     IMEStateManager::GetFocusSelectionAndRoot(getter_AddRefs(sel),
    3001           0 :                                               getter_AddRefs(mRootContent));
    3002           0 :   mSelection = sel ? sel->AsSelection() : nullptr;
    3003           0 :   if (rv != NS_ERROR_NOT_AVAILABLE) {
    3004           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3005             :   } else {
    3006           0 :     rv = Init(aEvent);
    3007           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3008             :   }
    3009             : 
    3010             :   // Get range from offset and length
    3011           0 :   RefPtr<nsRange> range = new nsRange(mRootContent);
    3012           0 :   rv = SetRangeFromFlatTextOffset(range, aEvent->mOffset, aEvent->mLength,
    3013             :                                   GetLineBreakType(aEvent),
    3014           0 :                                   aEvent->mExpandToClusterBoundary);
    3015           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3016             : 
    3017           0 :   nsINode* startNode = range->GetStartContainer();
    3018           0 :   nsINode* endNode = range->GetEndContainer();
    3019           0 :   int32_t startNodeOffset = range->StartOffset();
    3020           0 :   int32_t endNodeOffset = range->EndOffset();
    3021           0 :   AdjustRangeForSelection(mRootContent, &startNode, &startNodeOffset);
    3022           0 :   AdjustRangeForSelection(mRootContent, &endNode, &endNodeOffset);
    3023           0 :   if (NS_WARN_IF(!startNode) || NS_WARN_IF(!endNode) ||
    3024           0 :       NS_WARN_IF(startNodeOffset < 0) || NS_WARN_IF(endNodeOffset < 0)) {
    3025           0 :     return NS_ERROR_UNEXPECTED;
    3026             :   }
    3027             : 
    3028           0 :   mSelection->StartBatchChanges();
    3029             : 
    3030             :   // Clear selection first before setting
    3031           0 :   rv = mSelection->RemoveAllRanges();
    3032             :   // Need to call EndBatchChanges at the end even if call failed
    3033           0 :   if (NS_SUCCEEDED(rv)) {
    3034           0 :     if (aEvent->mReversed) {
    3035           0 :       rv = mSelection->Collapse(endNode, endNodeOffset);
    3036             :     } else {
    3037           0 :       rv = mSelection->Collapse(startNode, startNodeOffset);
    3038             :     }
    3039           0 :     if (NS_SUCCEEDED(rv) &&
    3040           0 :         (startNode != endNode || startNodeOffset != endNodeOffset)) {
    3041           0 :       if (aEvent->mReversed) {
    3042           0 :         rv = mSelection->Extend(startNode, startNodeOffset);
    3043             :       } else {
    3044           0 :         rv = mSelection->Extend(endNode, endNodeOffset);
    3045             :       }
    3046             :     }
    3047             :   }
    3048             : 
    3049             :   // Pass the eSetSelection events reason along with the BatchChange-end
    3050             :   // selection change notifications.
    3051           0 :   mSelection->EndBatchChangesInternal(aEvent->mReason);
    3052           0 :   NS_ENSURE_SUCCESS(rv, rv);
    3053             : 
    3054           0 :   mSelection->ScrollIntoViewInternal(
    3055             :     nsISelectionController::SELECTION_FOCUS_REGION,
    3056           0 :     false, nsIPresShell::ScrollAxis(), nsIPresShell::ScrollAxis());
    3057           0 :   aEvent->mSucceeded = true;
    3058           0 :   return NS_OK;
    3059             : }
    3060             : 
    3061             : nsRect
    3062           0 : ContentEventHandler::FrameRelativeRect::RectRelativeTo(
    3063             :                                           nsIFrame* aDestFrame) const
    3064             : {
    3065           0 :   if (!mBaseFrame || NS_WARN_IF(!aDestFrame)) {
    3066           0 :     return nsRect();
    3067             :   }
    3068             : 
    3069           0 :   if (NS_WARN_IF(aDestFrame->PresContext() != mBaseFrame->PresContext())) {
    3070           0 :     return nsRect();
    3071             :   }
    3072             : 
    3073           0 :   if (aDestFrame == mBaseFrame) {
    3074           0 :     return mRect;
    3075             :   }
    3076             : 
    3077           0 :   nsIFrame* rootFrame = mBaseFrame->PresContext()->PresShell()->GetRootFrame();
    3078             :   nsRect baseFrameRectInRootFrame =
    3079           0 :     nsLayoutUtils::TransformFrameRectToAncestor(mBaseFrame, nsRect(),
    3080           0 :                                                 rootFrame);
    3081             :   nsRect destFrameRectInRootFrame =
    3082           0 :     nsLayoutUtils::TransformFrameRectToAncestor(aDestFrame, nsRect(),
    3083           0 :                                                 rootFrame);
    3084             :   nsPoint difference =
    3085           0 :     destFrameRectInRootFrame.TopLeft() - baseFrameRectInRootFrame.TopLeft();
    3086           0 :   return mRect - difference;
    3087             : }
    3088             : 
    3089             : } // namespace mozilla

Generated by: LCOV version 1.13