Bug 1482019 - part 1: Create non-virtual method HTMLEditor::GetSelectedNode() for implementing nsIHTMLEditor::GetSelectedElement() r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 10 Aug 2018 16:51:52 +0900
changeset 431872 9b46ca68f3b0fa45e59cc39e5e922fbb0858d105
parent 431871 a61cdb1a654ca07c48ec5dc1c3d87c2bf12f83ce
child 431873 40fbaeccbd895386d7f64ba330608576d2296cef
push id34451
push userebalazs@mozilla.com
push dateThu, 16 Aug 2018 09:25:15 +0000
treeherdermozilla-central@161817e6d127 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1482019
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1482019 - part 1: Create non-virtual method HTMLEditor::GetSelectedNode() for implementing nsIHTMLEditor::GetSelectedElement() r=m_kato For making each diff compact, this bug needs some patches. First of all, this patch moves implementation of nsIHTMLEditor::GetSelectedElement() to new non-virtual method HTMLEditor::GetSelectedNode().
editor/libeditor/HTMLEditor.cpp
editor/libeditor/HTMLEditor.h
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -2601,37 +2601,57 @@ HTMLEditor::GetElementOrParentByTagName(
   parent.forget(aReturn);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HTMLEditor::GetSelectedElement(const nsAString& aTagName,
                                nsISupports** aReturn)
 {
-  NS_ENSURE_TRUE(aReturn , NS_ERROR_NULL_POINTER);
-
-  // default is null - no element found
+  if (NS_WARN_IF(!aReturn)) {
+    return NS_ERROR_INVALID_ARG;
+  }
   *aReturn = nullptr;
 
-  // First look for a single element in selection
   RefPtr<Selection> selection = GetSelection();
-  NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER);
-
-  bool isCollapsed = selection->IsCollapsed();
+  if (NS_WARN_IF(!selection)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ErrorResult error;
+  RefPtr<nsINode> selectedNode = GetSelectedNode(*selection, aTagName, error);
+  if (NS_WARN_IF(error.Failed())) {
+    return error.StealNSResult();
+  }
+  selectedNode.forget(aReturn);
+  return NS_OK;
+}
+
+already_AddRefed<nsINode>
+HTMLEditor::GetSelectedNode(Selection& aSelection,
+                            const nsAString& aTagName,
+                            ErrorResult& aRv)
+{
+  MOZ_ASSERT(!aRv.Failed());
+
+  bool isCollapsed = aSelection.IsCollapsed();
 
   nsAutoString domTagName;
   nsAutoString TagName(aTagName);
   ToLowerCase(TagName);
   // Empty string indicates we should match any element tag
   bool anyTag = (TagName.IsEmpty());
   bool isLinkTag = IsLinkTag(TagName);
   bool isNamedAnchorTag = IsNamedAnchorTag(TagName);
 
-  RefPtr<nsRange> range = selection->GetRangeAt(0);
-  NS_ENSURE_STATE(range);
+  RefPtr<nsRange> range = aSelection.GetRangeAt(0);
+  if (NS_WARN_IF(!range)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
 
   nsCOMPtr<nsINode> startContainer = range->GetStartContainer();
   nsIContent* startNode = range->GetChildAtStartOffset();
 
   nsCOMPtr<nsINode> endContainer = range->GetEndContainer();
   nsIContent* endNode = range->GetChildAtEndOffset();
 
   // Optimization for a single selected element
@@ -2641,30 +2661,29 @@ HTMLEditor::GetSelectedElement(const nsA
     if (selectedNode) {
       domTagName = selectedNode->NodeName();
       ToLowerCase(domTagName);
 
       // Test for appropriate node type requested
       if (anyTag || (TagName == domTagName) ||
           (isLinkTag && HTMLEditUtils::IsLink(selectedNode)) ||
           (isNamedAnchorTag && HTMLEditUtils::IsNamedAnchor(selectedNode))) {
-        selectedNode.forget(aReturn);
-        return NS_OK;
+        return selectedNode.forget();
       }
     }
   }
 
   bool bNodeFound = false;
   nsCOMPtr<Element> selectedElement;
   if (isLinkTag) {
     // Link tag is a special case - we return the anchor node
     //  found for any selection that is totally within a link,
     //  included a collapsed selection (just a caret in a link)
-    const RangeBoundary& anchor = selection->AnchorRef();
-    const RangeBoundary& focus = selection->FocusRef();
+    const RangeBoundary& anchor = aSelection.AnchorRef();
+    const RangeBoundary& focus = aSelection.FocusRef();
     // Link node must be the same for both ends of selection
     if (anchor.IsSet()) {
       RefPtr<Element> parentLinkOfAnchor =
         GetElementOrParentByTagName(NS_LITERAL_STRING("href"),
                                     anchor.Container());
       // XXX: ERROR_HANDLING  can parentLinkOfAnchor be null?
       if (parentLinkOfAnchor) {
         if (isCollapsed) {
@@ -2677,40 +2696,42 @@ HTMLEditor::GetSelectedElement(const nsA
                                         focus.Container());
           if (parentLinkOfFocus == parentLinkOfAnchor) {
             bNodeFound = true;
           }
         }
 
         // We found a link node parent
         if (bNodeFound) {
-          parentLinkOfAnchor.forget(aReturn);
-          return NS_OK;
+          return parentLinkOfAnchor.forget();
         }
       } else if (anchor.GetChildAtOffset() && focus.GetChildAtOffset()) {
         // Check if link node is the only thing selected
         if (HTMLEditUtils::IsLink(anchor.GetChildAtOffset()) &&
             anchor.Container() == focus.Container() &&
             focus.GetChildAtOffset() ==
               anchor.GetChildAtOffset()->GetNextSibling()) {
           selectedElement = do_QueryInterface(anchor.GetChildAtOffset());
           bNodeFound = true;
         }
       }
     }
   }
 
   if (!isCollapsed) {
-    RefPtr<nsRange> currange = selection->GetRangeAt(0);
+    RefPtr<nsRange> currange = aSelection.GetRangeAt(0);
     if (currange) {
       nsresult rv;
       nsCOMPtr<nsIContentIterator> iter =
         do_CreateInstance("@mozilla.org/content/post-content-iterator;1",
                           &rv);
-      NS_ENSURE_SUCCESS(rv, rv);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        aRv.Throw(rv);
+        return nullptr;
+      }
 
       iter->Init(currange);
       // loop through the content iterator for each content node
       while (!iter->IsDone()) {
         // Query interface to cast nsIContent to Element
         //  then get tagType to compare to  aTagName
         // Clone node of each desired type and append it to the aDomFrag
         selectedElement = Element::FromNodeOrNull(iter->GetCurrentNode());
@@ -2751,18 +2772,17 @@ HTMLEditor::GetSelectedElement(const nsA
       }
     } else {
       // Should never get here?
       isCollapsed = true;
       NS_WARNING("isCollapsed was FALSE, but no elements found in selection\n");
     }
   }
 
-  selectedElement.forget(aReturn);
-  return NS_OK;
+  return selectedElement.forget();
 }
 
 already_AddRefed<Element>
 HTMLEditor::GetSelectedElement(const nsAString& aTagName)
 {
   nsCOMPtr<nsISupports> domElement;
   GetSelectedElement(aTagName, getter_AddRefs(domElement));
   nsCOMPtr<Element> element = do_QueryInterface(domElement);
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -791,16 +791,49 @@ protected: // Shouldn't be used by frien
   /**
    * CollapseSelectionAfter() collapses Selection after aElement.
    * If aElement is an orphan node or not in editing host, returns error.
    */
   nsresult CollapseSelectionAfter(Selection& aSelection,
                                   Element& aElement);
 
   /**
+   * GetSelectedNode() returns an element node which is in first range of
+   * aSelection.  The rule is a little bit complicated and the rules do not
+   * make sense except in a few cases.  If you want to use this newly,
+   * you should create new method instead.  This needs to be here for
+   * comm-central.
+   * The rules are:
+   *   1. If Selection selects an element node, i.e., both containers are
+   *      same node and start offset and end offset is start offset + 1.
+   *      (XXX However, if last child is selected, this path is not used.)
+   *   2. If the argument is "href", look for anchor elements whose href
+   *      attribute is not empty from container of anchor/focus of Selection
+   *      to <body> element.  Then, both result are same one, returns the node.
+   *      (i.e., this allows collapsed selection.)
+   *   3. If the Selection is collapsed, returns null.
+   *   4. Otherwise, listing up all nodes with content iterator (post-order).
+   *     4-1. When first element node does *not* match with the argument,
+   *          *returns* the element.
+   *     4-2. When first element node matches with the argument, returns
+   *          *next* element node.
+   * XXX This may return non-element node in some cases.  Perhaps, it's a
+   *     bug.
+   *
+   * @param aSelection          The Selection.
+   * @param aTagName            The name of element which you looking for.
+   * @param aRv                 Returns error code.
+   * @return                    An element in first range of aSelection.
+   */
+  already_AddRefed<nsINode>
+  GetSelectedNode(Selection& aSelection,
+                  const nsAString& aTagName,
+                  ErrorResult& aRv);
+
+  /**
    * PasteInternal() pasts text with replacing selected content.
    * This tries to dispatch ePaste event first.  If its defaultPrevent() is
    * called, this does nothing but returns NS_OK.
    *
    * @param aClipboardType  nsIClipboard::kGlobalClipboard or
    *                        nsIClipboard::kSelectionClipboard.
    */
   nsresult PasteInternal(int32_t aClipboardType);