dom/events/ContentEventHandler.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial