Bug 1549696: factor out functionality to determine fixup node in nsDocumentEncoder. r=hsivonen
authorMirko Brodesser <mbrodesser@mozilla.com>
Wed, 08 May 2019 11:09:51 +0000
changeset 531860 b5344f8392c0bae8dd62280f3b608b1f907f6568
parent 531859 b771a88efe40227ba5cce8f1880d755018c1871e
child 531861 72f13244bf42d994d1a559457c6081b938f8ccb2
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsivonen
bugs1549696
milestone68.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 1549696: factor out functionality to determine fixup node in nsDocumentEncoder. r=hsivonen In order to reduce code duplication and make the code more legible. Differential Revision: https://phabricator.services.mozilla.com/D30199
dom/base/nsDocumentEncoder.cpp
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -57,23 +57,23 @@ class nsDocumentEncoder : public nsIDocu
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsDocumentEncoder)
   NS_DECL_NSIDOCUMENTENCODER
 
  protected:
   virtual ~nsDocumentEncoder();
 
   void Initialize(bool aClearCachedSerializer = true);
-  nsresult SerializeNodeStart(nsINode* aNode, int32_t aStartOffset,
+  nsresult SerializeNodeStart(nsINode& aOriginalNode, int32_t aStartOffset,
                               int32_t aEndOffset, nsAString& aStr,
-                              nsINode* aOriginalNode = nullptr);
+                              nsINode* aFixupNode = nullptr);
   nsresult SerializeToStringRecursive(nsINode* aNode, nsAString& aStr,
                                       bool aDontSerializeRoot,
                                       uint32_t aMaxLength = 0);
-  nsresult SerializeNodeEnd(nsINode* aNode, nsAString& aStr);
+  nsresult SerializeNodeEnd(nsINode& aNode, nsAString& aStr);
   // This serializes the content of aNode.
   nsresult SerializeToStringIterative(nsINode* aNode, nsAString& aStr);
   nsresult SerializeRangeToString(nsRange* aRange, nsAString& aOutputString);
   nsresult SerializeRangeNodes(nsRange* aRange, nsINode* aNode,
                                nsAString& aString, int32_t aDepth);
   nsresult SerializeRangeContextStart(const nsTArray<nsINode*>& aAncestorArray,
                                       nsAString& aString);
   nsresult SerializeRangeContextEnd(nsAString& aString);
@@ -291,52 +291,77 @@ nsDocumentEncoder::SetCharset(const nsAC
 NS_IMETHODIMP
 nsDocumentEncoder::GetMimeType(nsAString& aMimeType) {
   aMimeType = mMimeType;
   return NS_OK;
 }
 
 bool nsDocumentEncoder::IncludeInContext(nsINode* aNode) { return false; }
 
-nsresult nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
+class FixupNodeDeterminer {
+ public:
+  FixupNodeDeterminer(nsIDocumentEncoderNodeFixup* aNodeFixup,
+                      nsINode* aFixupNode, nsINode& aOriginalNode)
+      : mIsSerializationOfFixupChildrenNeeded{false},
+        mNodeFixup(aNodeFixup),
+        mOriginalNode(aOriginalNode) {
+    if (mNodeFixup) {
+      if (aFixupNode) {
+        mFixupNode = aFixupNode;
+      } else {
+        mNodeFixup->FixupNode(&mOriginalNode,
+                              &mIsSerializationOfFixupChildrenNeeded,
+                              getter_AddRefs(mFixupNode));
+      }
+    }
+  }
+
+  bool IsSerializationOfFixupChildrenNeeded() const {
+    return mIsSerializationOfFixupChildrenNeeded;
+  }
+
+  /**
+   * @return The fixup node, if available, otherwise the original node. The
+   * former is kept alive by this object.
+   */
+  nsINode& GetFixupNodeFallBackToOriginalNode() const {
+    return mFixupNode ? *mFixupNode : mOriginalNode;
+  }
+
+ private:
+  bool mIsSerializationOfFixupChildrenNeeded;
+  nsIDocumentEncoderNodeFixup* mNodeFixup;
+  nsCOMPtr<nsINode> mFixupNode;
+  nsINode& mOriginalNode;
+};
+
+nsresult nsDocumentEncoder::SerializeNodeStart(nsINode& aOriginalNode,
                                                int32_t aStartOffset,
                                                int32_t aEndOffset,
                                                nsAString& aStr,
-                                               nsINode* aOriginalNode) {
-  if (mNeedsPreformatScanning && aNode->IsElement()) {
-    mSerializer->ScanElementForPreformat(aNode->AsElement());
+                                               nsINode* aFixupNode) {
+  if (mNeedsPreformatScanning && aOriginalNode.IsElement()) {
+    mSerializer->ScanElementForPreformat(aOriginalNode.AsElement());
   }
 
-  if (!IsVisibleNode(aNode)) return NS_OK;
-
-  nsINode* node = nullptr;
-  nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip;
-
-  // Caller didn't do fixup, so we'll do it ourselves
-  if (!aOriginalNode) {
-    aOriginalNode = aNode;
-    if (mNodeFixup) {
-      bool dummy;
-      mNodeFixup->FixupNode(aNode, &dummy,
-                            getter_AddRefs(fixedNodeKungfuDeathGrip));
-      node = fixedNodeKungfuDeathGrip;
-    }
+  if (!IsVisibleNode(&aOriginalNode)) {
+    return NS_OK;
   }
 
-  // Either there was no fixed-up node,
-  // or the caller did fixup themselves and aNode is already fixed
-  if (!node) node = aNode;
+  FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, aFixupNode,
+                                          aOriginalNode};
+  nsINode* node = &fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode();
 
   if (node->IsElement()) {
     if ((mFlags & (nsIDocumentEncoder::OutputPreformatted |
                    nsIDocumentEncoder::OutputDropInvisibleBreak)) &&
         nsLayoutUtils::IsInvisibleBreak(node)) {
       return NS_OK;
     }
-    Element* originalElement = Element::FromNodeOrNull(aOriginalNode);
+    Element* originalElement = aOriginalNode.AsElement();
     mSerializer->AppendElementStart(node->AsElement(), originalElement, aStr);
     return NS_OK;
   }
 
   switch (node->NodeType()) {
     case nsINode::TEXT_NODE: {
       mSerializer->AppendText(static_cast<nsIContent*>(node), aStartOffset,
                               aEndOffset, aStr);
@@ -362,52 +387,47 @@ nsresult nsDocumentEncoder::SerializeNod
       mSerializer->AppendDoctype(static_cast<DocumentType*>(node), aStr);
       break;
     }
   }
 
   return NS_OK;
 }
 
-nsresult nsDocumentEncoder::SerializeNodeEnd(nsINode* aNode, nsAString& aStr) {
-  if (mNeedsPreformatScanning && aNode->IsElement()) {
-    mSerializer->ForgetElementForPreformat(aNode->AsElement());
+nsresult nsDocumentEncoder::SerializeNodeEnd(nsINode& aNode, nsAString& aStr) {
+  if (mNeedsPreformatScanning && aNode.IsElement()) {
+    mSerializer->ForgetElementForPreformat(aNode.AsElement());
   }
 
-  if (!IsVisibleNode(aNode)) return NS_OK;
+  if (!IsVisibleNode(&aNode)) {
+    return NS_OK;
+  }
 
-  if (aNode->IsElement()) {
-    mSerializer->AppendElementEnd(aNode->AsElement(), aStr);
+  if (aNode.IsElement()) {
+    mSerializer->AppendElementEnd(aNode.AsElement(), aStr);
   }
   return NS_OK;
 }
 
 nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
                                                        nsAString& aStr,
                                                        bool aDontSerializeRoot,
                                                        uint32_t aMaxLength) {
   if (aMaxLength > 0 && aStr.Length() >= aMaxLength) {
     return NS_OK;
   }
 
   if (!IsVisibleNode(aNode)) return NS_OK;
 
   nsresult rv = NS_OK;
-  bool serializeClonedChildren = false;
-  nsINode* maybeFixedNode = nullptr;
 
-  // Keep the node from FixupNode alive.
-  nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip;
-  if (mNodeFixup) {
-    mNodeFixup->FixupNode(aNode, &serializeClonedChildren,
-                          getter_AddRefs(fixedNodeKungfuDeathGrip));
-    maybeFixedNode = fixedNodeKungfuDeathGrip;
-  }
-
-  if (!maybeFixedNode) maybeFixedNode = aNode;
+  MOZ_ASSERT(aNode, "aNode shouldn't be nullptr.");
+  FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, nullptr, *aNode};
+  nsINode* maybeFixedNode =
+      &fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode();
 
   if ((mFlags & SkipInvisibleContent) &&
       !(mFlags & OutputNonTextContentAsPlaceholder)) {
     if (aNode->IsContent()) {
       if (nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame()) {
         if (!frame->IsSelectable(nullptr)) {
           aDontSerializeRoot = true;
         }
@@ -416,48 +436,50 @@ nsresult nsDocumentEncoder::SerializeToS
   }
 
   if (!aDontSerializeRoot) {
     int32_t endOffset = -1;
     if (aMaxLength > 0) {
       MOZ_ASSERT(aMaxLength >= aStr.Length());
       endOffset = aMaxLength - aStr.Length();
     }
-    rv = SerializeNodeStart(maybeFixedNode, 0, endOffset, aStr, aNode);
+    rv = SerializeNodeStart(*aNode, 0, endOffset, aStr, maybeFixedNode);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsINode* node = serializeClonedChildren ? maybeFixedNode : aNode;
+  nsINode* node = fixupNodeDeterminer.IsSerializationOfFixupChildrenNeeded()
+                      ? maybeFixedNode
+                      : aNode;
 
   for (nsINode* child = nsNodeUtils::GetFirstChildOfTemplateOrNode(node); child;
        child = child->GetNextSibling()) {
     rv = SerializeToStringRecursive(child, aStr, false, aMaxLength);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (!aDontSerializeRoot) {
-    rv = SerializeNodeEnd(maybeFixedNode, aStr);
+    rv = SerializeNodeEnd(*maybeFixedNode, aStr);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return FlushText(aStr, false);
 }
 
 nsresult nsDocumentEncoder::SerializeToStringIterative(nsINode* aNode,
                                                        nsAString& aStr) {
   nsresult rv;
 
   nsINode* node = nsNodeUtils::GetFirstChildOfTemplateOrNode(aNode);
   while (node) {
     nsINode* current = node;
-    rv = SerializeNodeStart(current, 0, -1, aStr, current);
+    rv = SerializeNodeStart(*current, 0, -1, aStr, current);
     NS_ENSURE_SUCCESS(rv, rv);
     node = nsNodeUtils::GetFirstChildOfTemplateOrNode(current);
     while (!node && current && current != aNode) {
-      rv = SerializeNodeEnd(current, aStr);
+      rv = SerializeNodeEnd(*current, aStr);
       NS_ENSURE_SUCCESS(rv, rv);
       // Check if we have siblings.
       node = current->GetNextSibling();
       if (!node) {
         // Perhaps parent node has siblings.
         current = current->GetParentNode();
 
         // Handle template element. If the parent is a template's content,
@@ -572,37 +594,37 @@ nsresult nsDocumentEncoder::SerializeRan
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     // due to implementation it is impossible for text node to be both start and
     // end of range.  We would have handled that case without getting here.
     // XXXsmaug What does this all mean?
     if (IsTextNode(aNode)) {
       if (startNode == content) {
         int32_t startOffset = aRange->StartOffset();
-        rv = SerializeNodeStart(aNode, startOffset, -1, aString);
+        rv = SerializeNodeStart(*aNode, startOffset, -1, aString);
         NS_ENSURE_SUCCESS(rv, rv);
       } else {
         int32_t endOffset = aRange->EndOffset();
-        rv = SerializeNodeStart(aNode, 0, endOffset, aString);
+        rv = SerializeNodeStart(*aNode, 0, endOffset, aString);
         NS_ENSURE_SUCCESS(rv, rv);
       }
-      rv = SerializeNodeEnd(aNode, aString);
+      rv = SerializeNodeEnd(*aNode, aString);
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       if (aNode != mCommonParent) {
         if (IncludeInContext(aNode)) {
           // halt the incrementing of mStartDepth/mEndDepth.  This is
           // so paste client will include this node in paste.
           mHaltRangeHint = true;
         }
         if ((startNode == content) && !mHaltRangeHint) mStartDepth++;
         if ((endNode == content) && !mHaltRangeHint) mEndDepth++;
 
         // serialize the start of this node
-        rv = SerializeNodeStart(aNode, 0, -1, aString);
+        rv = SerializeNodeStart(*aNode, 0, -1, aString);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       // do some calculations that will tell us which children of this
       // node are in the range.
       int32_t startOffset = 0, endOffset = -1;
       if (startNode == content && mStartRootIndex >= aDepth)
         startOffset = mStartOffsets[mStartRootIndex - aDepth];
@@ -648,17 +670,17 @@ nsresult nsDocumentEncoder::SerializeRan
           NS_ENSURE_SUCCESS(rv, rv);
 
           childAsNode = childAsNode->GetNextSibling();
         }
       }
 
       // serialize the end of this node
       if (aNode != mCommonParent) {
-        rv = SerializeNodeEnd(aNode, aString);
+        rv = SerializeNodeEnd(*aNode, aString);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
   }
   return NS_OK;
 }
 
 nsresult nsDocumentEncoder::SerializeRangeContextStart(
@@ -677,17 +699,17 @@ nsresult nsDocumentEncoder::SerializeRan
 
   while (i > 0) {
     nsINode* node = aAncestorArray.ElementAt(--i);
 
     if (!node) break;
 
     // Either a general inclusion or as immediate context
     if (IncludeInContext(node) || i < j) {
-      rv = SerializeNodeStart(node, 0, -1, aString);
+      rv = SerializeNodeStart(*node, 0, -1, aString);
       serializedContext->AppendElement(node);
       if (NS_FAILED(rv)) break;
     }
   }
 
   return rv;
 }
 
@@ -697,17 +719,17 @@ nsresult nsDocumentEncoder::SerializeRan
   }
 
   MOZ_RELEASE_ASSERT(!mRangeContexts.IsEmpty(),
                      "Tried to end context without starting one.");
   AutoTArray<nsINode*, 8>& serializedContext = mRangeContexts.LastElement();
 
   nsresult rv = NS_OK;
   for (nsINode* node : Reversed(serializedContext)) {
-    rv = SerializeNodeEnd(node, aString);
+    rv = SerializeNodeEnd(*node, aString);
 
     if (NS_FAILED(rv)) break;
   }
 
   mRangeContexts.RemoveLastElement();
   return rv;
 }
 
@@ -754,20 +776,20 @@ nsresult nsDocumentEncoder::SerializeRan
       // Check that the parent is visible if we don't a frame.
       // IsVisibleNode() will do it when there's a frame.
       nsCOMPtr<nsIContent> content = do_QueryInterface(startContainer);
       if (content && !content->GetPrimaryFrame()) {
         nsIContent* parent = content->GetParent();
         if (!parent || !IsVisibleNode(parent)) return NS_OK;
       }
     }
-    rv = SerializeNodeStart(startContainer, startOffset, endOffset,
+    rv = SerializeNodeStart(*startContainer, startOffset, endOffset,
                             aOutputString);
     NS_ENSURE_SUCCESS(rv, rv);
-    rv = SerializeNodeEnd(startContainer, aOutputString);
+    rv = SerializeNodeEnd(*startContainer, aOutputString);
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     rv = SerializeRangeNodes(aRange, mCommonParent, aOutputString, 0);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   rv = SerializeRangeContextEnd(aOutputString);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -856,34 +878,34 @@ nsDocumentEncoder::EncodeToStringWithMax
       // needed Bug 137450: Problem copying/pasting a table from a web page to
       // Excel. Each separate block of <tr></tr> produced above will be wrapped
       // by the immediate context. This assumes that you can't select cells that
       // are multiple selections from two tables simultaneously.
       node = range->GetStartContainer();
       NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
       if (node != prevNode) {
         if (prevNode) {
-          rv = SerializeNodeEnd(prevNode, output);
+          rv = SerializeNodeEnd(*prevNode, output);
           NS_ENSURE_SUCCESS(rv, rv);
         }
         nsCOMPtr<nsIContent> content = do_QueryInterface(node);
         if (content && content->IsHTMLElement(nsGkAtoms::tr) &&
             !ParentIsTR(content)) {
           if (!prevNode) {
             // Went from a non-<tr> to a <tr>
             mCommonAncestors.Clear();
             nsContentUtils::GetAncestors(node->GetParentNode(),
                                          mCommonAncestors);
             rv = SerializeRangeContextStart(mCommonAncestors, output);
             NS_ENSURE_SUCCESS(rv, rv);
             // Don't let SerializeRangeToString serialize the context again
             mDisableContextSerialize = true;
           }
 
-          rv = SerializeNodeStart(node, 0, -1, output);
+          rv = SerializeNodeStart(*node, 0, -1, output);
           NS_ENSURE_SUCCESS(rv, rv);
           prevNode = node;
         } else if (prevNode) {
           // Went from a <tr> to a non-<tr>
           mDisableContextSerialize = false;
           rv = SerializeRangeContextEnd(output);
           NS_ENSURE_SUCCESS(rv, rv);
           prevNode = nullptr;
@@ -894,17 +916,17 @@ nsDocumentEncoder::EncodeToStringWithMax
       NS_ENSURE_SUCCESS(rv, rv);
       if (i == 0) {
         firstRangeStartDepth = mStartDepth;
       }
     }
     mStartDepth = firstRangeStartDepth;
 
     if (prevNode) {
-      rv = SerializeNodeEnd(prevNode, output);
+      rv = SerializeNodeEnd(*prevNode, output);
       NS_ENSURE_SUCCESS(rv, rv);
       mDisableContextSerialize = false;
       rv = SerializeRangeContextEnd(output);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // Just to be safe
     mDisableContextSerialize = false;
@@ -1261,22 +1283,22 @@ nsHTMLCopyEncoder::EncodeToStringWithCon
     if (mEndDepth) mEndDepth--;
     // and the count
     count--;
   }
 
   i = count;
   while (i > 0) {
     node = mCommonAncestors.ElementAt(--i);
-    SerializeNodeStart(node, 0, -1, aContextString);
+    SerializeNodeStart(*node, 0, -1, aContextString);
   }
   // i = 0; guaranteed by above
   while (i < count) {
     node = mCommonAncestors.ElementAt(i++);
-    SerializeNodeEnd(node, aContextString);
+    SerializeNodeEnd(*node, aContextString);
   }
 
   // encode range info : the start and end depth of the selection, where the
   // depth is distance down in the parent hierarchy.  Later we will need to add
   // leading/trailing whitespace info to this.
   nsAutoString infoString;
   infoString.AppendInt(mStartDepth);
   infoString.Append(char16_t(','));