Commit af3373dc authored by Olli Pettay's avatar Olli Pettay
Browse files

Bug 1418002 - Remove HTMLContentElement, r=jessica

parent a75b9554
Loading
Loading
Loading
Loading
+8 −13
Original line number Diff line number Diff line
@@ -7,7 +7,6 @@
#include "ChildIterator.h"
#include "nsContentUtils.h"
#include "mozilla/dom/XBLChildrenElement.h"
#include "mozilla/dom/HTMLContentElement.h"
#include "mozilla/dom/ShadowRoot.h"
#include "nsIAnonymousContentCreator.h"
#include "nsIFrame.h"
@@ -18,33 +17,29 @@ namespace dom {

class MatchedNodes {
public:
  explicit MatchedNodes(HTMLContentElement* aInsertionPoint)
    : mIsContentElement(true), mContentElement(aInsertionPoint) {}

  explicit MatchedNodes()
    : mIsContentElement(false), mChildrenElement(nullptr) {}
  explicit MatchedNodes(XBLChildrenElement* aInsertionPoint)
    : mIsContentElement(false), mChildrenElement(aInsertionPoint) {}

  uint32_t Length() const
  {
    return mIsContentElement ? mContentElement->MatchedNodes().Length()
                             : mChildrenElement->InsertedChildrenLength();
    return mChildrenElement ? mChildrenElement->InsertedChildrenLength() : 0;
  }

  nsIContent* operator[](int32_t aIndex) const
  {
    return mIsContentElement ? mContentElement->MatchedNodes()[aIndex]
                             : mChildrenElement->InsertedChild(aIndex);
    return mChildrenElement ? mChildrenElement->InsertedChild(aIndex) : nullptr;
  }

  bool IsEmpty() const
  {
    return mIsContentElement ? mContentElement->MatchedNodes().IsEmpty()
                             : !mChildrenElement->HasInsertedChildren();
    return mChildrenElement && !mChildrenElement->HasInsertedChildren();
  }
protected:
  // Leftover from Shadow DOM v0.
  bool mIsContentElement;
  union {
    HTMLContentElement* mContentElement;
    XBLChildrenElement* mChildrenElement;
  };
};
@@ -57,9 +52,9 @@ GetMatchedNodesForPoint(nsIContent* aContent)
    return MatchedNodes(static_cast<XBLChildrenElement*>(aContent));
  }

  return MatchedNodes();
  // Web components case
  MOZ_ASSERT(aContent->IsHTMLElement(nsGkAtoms::content));
  return MatchedNodes(HTMLContentElement::FromContent(aContent));
  // XXX handle <slot> element?
}

nsIContent*
+2 −265
Original line number Diff line number Diff line
@@ -14,7 +14,6 @@
#include "nsIDOMHTMLElement.h"
#include "nsIStyleSheetLinkingElement.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLContentElement.h"
#include "nsXBLPrototypeBinding.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
@@ -221,39 +220,6 @@ ShadowRoot::GetElementsByClassName(const nsAString& aClasses)
  return nsContentUtils::GetElementsByClassName(this, aClasses);
}

void
ShadowRoot::AddInsertionPoint(HTMLContentElement* aInsertionPoint)
{
  TreeOrderComparator comparator;
  mInsertionPoints.InsertElementSorted(aInsertionPoint, comparator);
}

void
ShadowRoot::RemoveInsertionPoint(HTMLContentElement* aInsertionPoint)
{
  mInsertionPoints.RemoveElement(aInsertionPoint);
}

void
ShadowRoot::RemoveDestInsertionPoint(nsIContent* aInsertionPoint,
                                     nsTArray<nsIContent*>& aDestInsertionPoints)
{
  // Remove the insertion point from the destination insertion points.
  //
  // Note that while it sounds tempting to just remove all the insertion points
  // after it too, since they're usually after in tree position, it may not be
  // the case when we're redistributing after new insertion points have been
  // bound to the tree before aInsertionPoint, see bug 1409088.
  int32_t index = aDestInsertionPoints.IndexOf(aInsertionPoint);

  // It's possible that we already removed the insertion point while processing
  // other insertion point removals / fallback content redistribution (which
  // does DestInsertionPoints().Clear()).
  if (index >= 0) {
    aDestInsertionPoints.RemoveElementAt(index);
  }
}

void
ShadowRoot::DistributionChanged()
{
@@ -271,133 +237,11 @@ ShadowRoot::DistributionChanged()
  shell->DestroyFramesForAndRestyle(host);
}

const HTMLContentElement*
ShadowRoot::DistributeSingleNode(nsIContent* aContent)
{
  // Find the insertion point to which the content belongs.
  HTMLContentElement* foundInsertionPoint = nullptr;
  for (HTMLContentElement* insertionPoint : mInsertionPoints) {
    if (insertionPoint->Match(aContent)) {
      if (insertionPoint->MatchedNodes().Contains(aContent)) {
        // Node is already matched into the insertion point. We are done.
        return insertionPoint;
      }

      // Matching may cause the insertion point to drop fallback content.
      if (insertionPoint->MatchedNodes().IsEmpty() &&
          insertionPoint->HasChildren()) {
        // This match will cause the insertion point to drop all fallback
        // content and used matched nodes instead. Give up on the optimization
        // and just distribute all nodes.
        DistributeAllNodes();
        MOZ_ASSERT(insertionPoint->MatchedNodes().Contains(aContent));
        return insertionPoint;
      }
      foundInsertionPoint = insertionPoint;
      break;
    }
  }

  if (!foundInsertionPoint) {
    return nullptr;
  }

  // Find the index into the insertion point.
  nsCOMArray<nsIContent>& matchedNodes = foundInsertionPoint->MatchedNodes();
  // Find the appropriate position in the matched node list for the
  // newly distributed content.
  bool isIndexFound = false;
  ExplicitChildIterator childIterator(GetHost());
  for (uint32_t i = 0; i < matchedNodes.Length(); i++) {
    // Seek through the host's explicit children until the inserted content
    // is found or when the current matched node is reached.
    if (childIterator.Seek(aContent, matchedNodes[i])) {
      // aContent was found before the current matched node.
      foundInsertionPoint->InsertMatchedNode(i, aContent);
      isIndexFound = true;
      break;
    }
  }

  if (!isIndexFound) {
    // We have still not found an index in the insertion point,
    // thus it must be at the end.
    MOZ_ASSERT(childIterator.Seek(aContent, nullptr),
               "Trying to match a node that is not a candidate to be matched");
    foundInsertionPoint->AppendMatchedNode(aContent);
  }

  return foundInsertionPoint;
}

const HTMLContentElement*
ShadowRoot::RemoveDistributedNode(nsIContent* aContent)
{
  // Find insertion point containing the content and remove the node.
  for (HTMLContentElement* insertionPoint : mInsertionPoints) {
    if (!insertionPoint->MatchedNodes().Contains(aContent)) {
      continue;
    }

    // Removing the matched node may cause the insertion point to use
    // fallback content.
    if (insertionPoint->MatchedNodes().Length() == 1 &&
        insertionPoint->HasChildren()) {
      // Removing the matched node will cause fallback content to be
      // used instead. Give up optimization and distribute all nodes.
      DistributeAllNodes();
      return insertionPoint;
    }

    insertionPoint->RemoveMatchedNode(aContent);
    return insertionPoint;
  }

  return nullptr;
}

void
ShadowRoot::DistributeAllNodes()
{
  // Create node pool.
  nsTArray<nsIContent*> nodePool;
  ExplicitChildIterator childIterator(GetHost());
  for (nsIContent* content = childIterator.GetNextChild(); content;
       content = childIterator.GetNextChild()) {
    nodePool.AppendElement(content);
  }

  nsTArray<ShadowRoot*> shadowsToUpdate;

  for (HTMLContentElement* insertionPoint : mInsertionPoints) {
    insertionPoint->ClearMatchedNodes();
    // Assign matching nodes from node pool.
    for (uint32_t j = 0; j < nodePool.Length(); j++) {
      if (insertionPoint->Match(nodePool[j])) {
        insertionPoint->AppendMatchedNode(nodePool[j]);
        nodePool.RemoveElementAt(j--);
      }
    }

    // Keep track of instances where the content insertion point is distributed
    // (parent of insertion point has a ShadowRoot).
    nsIContent* insertionParent = insertionPoint->GetParent();
    MOZ_ASSERT(insertionParent, "The only way for an insertion point to be in the"
                                "mInsertionPoints array is to be a descendant of a"
                                "ShadowRoot, in which case, it should have a parent");

    // If the parent of the insertion point has a ShadowRoot, the nodes distributed
    // to the insertion point must be reprojected to the insertion points of the
    // parent's ShadowRoot.
    ShadowRoot* parentShadow = insertionParent->GetShadowRoot();
    if (parentShadow && !shadowsToUpdate.Contains(parentShadow)) {
      shadowsToUpdate.AppendElement(parentShadow);
    }
  }

  for (ShadowRoot* shadow : shadowsToUpdate) {
    shadow->DistributeAllNodes();
  }
  //XXX Handle <slot>.

  DistributionChanged();
}
@@ -477,13 +321,6 @@ ShadowRoot::IsPooledNode(nsIContent* aContent) const
    return true;
  }

  if (auto* content = HTMLContentElement::FromContentOrNull(container)) {
    // Fallback content will end up in pool if its parent is a child of the host.
    return content->IsInsertionPoint() &&
           content->MatchedNodes().IsEmpty() &&
           container->GetParentNode() == host;
  }

  return false;
}

@@ -499,11 +336,6 @@ ShadowRoot::AttributeChanged(nsIDocument* aDocument,
    return;
  }

  // Attributes may change insertion point matching, find its new distribution.
  if (!RedistributeElement(aElement)) {
    return;
  }

  if (!aElement->IsInComposedDoc()) {
    return;
  }
@@ -513,52 +345,10 @@ ShadowRoot::AttributeChanged(nsIDocument* aDocument,
    return;
  }

  //XXX optimize this!
  shell->DestroyFramesForAndRestyle(aElement);
}

bool
ShadowRoot::RedistributeElement(Element* aElement)
{
  auto* oldInsertionPoint = RemoveDistributedNode(aElement);
  auto* newInsertionPoint = DistributeSingleNode(aElement);

  if (oldInsertionPoint == newInsertionPoint) {
    if (oldInsertionPoint) {
      if (auto* shadow = oldInsertionPoint->GetParent()->GetShadowRoot()) {
        return shadow->RedistributeElement(aElement);
      }
    }

    return false;
  }

  while (oldInsertionPoint) {
    // Handle the case where the parent of the insertion point has a ShadowRoot.
    // The node distributed into the insertion point must be reprojected to the
    // insertion points of the parent's ShadowRoot.
    auto* shadow = oldInsertionPoint->GetParent()->GetShadowRoot();
    if (!shadow) {
      break;
    }

    oldInsertionPoint = shadow->RemoveDistributedNode(aElement);
  }

  while (newInsertionPoint) {
    // Handle the case where the parent of the insertion point has a ShadowRoot.
    // The node distributed into the insertion point must be reprojected to the
    // insertion points of the parent's ShadowRoot.
    auto* shadow = newInsertionPoint->GetParent()->GetShadowRoot();
    if (!shadow) {
      break;
    }

    newInsertionPoint = shadow->DistributeSingleNode(aElement);
  }

  return true;
}

void
ShadowRoot::ContentAppended(nsIDocument* aDocument,
                            nsIContent* aContainer,
@@ -581,31 +371,6 @@ ShadowRoot::ContentInserted(nsIDocument* aDocument,
    mInsertionPointChanged = false;
    return;
  }

  // Add insertion point to destination insertion points of fallback content.
  if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
    HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
    if (content && content->MatchedNodes().IsEmpty()) {
      aChild->DestInsertionPoints().AppendElement(aContainer);
    }
  }

  // Watch for new nodes added to the pool because the node
  // may need to be added to an insertion point.
  if (IsPooledNode(aChild)) {
    auto* insertionPoint = DistributeSingleNode(aChild);
    while (insertionPoint) {
      // Handle the case where the parent of the insertion point has a ShadowRoot.
      // The node distributed into the insertion point must be reprojected to the
      // insertion points of the parent's ShadowRoot.
      auto* parentShadow = insertionPoint->GetParent()->GetShadowRoot();
      if (!parentShadow) {
        break;
      }

      insertionPoint = parentShadow->DistributeSingleNode(aChild);
    }
  }
}

void
@@ -619,34 +384,6 @@ ShadowRoot::ContentRemoved(nsIDocument* aDocument,
    mInsertionPointChanged = false;
    return;
  }

  // Clear destination insertion points for removed
  // fallback content.
  if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
    HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
    if (content && content->MatchedNodes().IsEmpty()) {
      aChild->DestInsertionPoints().Clear();
    }
  }

  // Watch for node that is removed from the pool because
  // it may need to be removed from an insertion point.
  if (IsPooledNode(aChild)) {
    auto* insertionPoint = RemoveDistributedNode(aChild);
    while (insertionPoint) {
      // Handle the case where the parent of the insertion point has a
      // ShadowRoot.
      //
      // The removed node needs to be removed from the insertion points of the
      // parent's ShadowRoot.
      auto* parentShadow = insertionPoint->GetParent()->GetShadowRoot();
      if (!parentShadow) {
        break;
      }

      insertionPoint = parentShadow->RemoveDistributedNode(aChild);
    }
  }
}

nsresult
+0 −35
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ namespace mozilla {
namespace dom {

class Element;
class HTMLContentElement;
class ShadowRootStyleSheetList;

class ShadowRoot final : public DocumentFragment,
@@ -72,27 +71,6 @@ public:
  void DistributeAllNodes();

private:
  /**
   * Distributes a single explicit child of the pool host to the content
   * insertion points in this ShadowRoot.
   *
   * Returns the insertion point the element is distributed to after this call.
   *
   * Note that this doesn't handle distributing the node in the insertion point
   * parent's shadow root.
   */
  const HTMLContentElement* DistributeSingleNode(nsIContent* aContent);

  /**
   * Removes a single explicit child of the pool host from the content
   * insertion points in this ShadowRoot.
   *
   * Returns the old insertion point, if any.
   *
   * Note that this doesn't handle removing the node in the returned insertion
   * point parent's shadow root.
   */
  const HTMLContentElement* RemoveDistributedNode(nsIContent* aContent);

  /**
   * Redistributes a node of the pool, and returns whether the distribution
@@ -108,9 +86,6 @@ private:
  bool IsPooledNode(nsIContent* aChild) const;

public:
  void AddInsertionPoint(HTMLContentElement* aInsertionPoint);
  void RemoveInsertionPoint(HTMLContentElement* aInsertionPoint);

  void SetInsertionPointChanged() { mInsertionPointChanged = true; }

  void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; }
@@ -119,9 +94,6 @@ public:

  static ShadowRoot* FromNode(nsINode* aNode);

  static void RemoveDestInsertionPoint(nsIContent* aInsertionPoint,
                                       nsTArray<nsIContent*>& aDestInsertionPoints);

  // WebIDL methods.
  Element* GetElementById(const nsAString& aElementId);
  already_AddRefed<nsContentList>
@@ -146,13 +118,6 @@ protected:

  ShadowRootMode mMode;

  // An array of content insertion points that are a descendant of the ShadowRoot
  // sorted in tree order. Insertion points are responsible for notifying
  // the ShadowRoot when they are removed or added as a descendant. The insertion
  // points are kept alive by the parent node, thus weak references are held
  // by the array.
  nsTArray<HTMLContentElement*> mInsertionPoints;

  nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
  nsXBLPrototypeBinding* mProtoBinding;

+2 −12
Original line number Diff line number Diff line
@@ -49,7 +49,6 @@
#include "mozilla/dom/FileBlobImpl.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLTemplateElement.h"
#include "mozilla/dom/HTMLContentElement.h"
#include "mozilla/dom/IDTracker.h"
#include "mozilla/dom/IPCBlobUtils.h"
#include "mozilla/dom/Promise.h"
@@ -7536,9 +7535,8 @@ nsContentUtils::IsContentInsertionPoint(nsIContent* aContent)
  }

  // Check if the content is a web components content insertion point.
  HTMLContentElement* contentElement =
    HTMLContentElement::FromContent(aContent);
  return contentElement && contentElement->IsInsertionPoint();
  // XXX handle <slot>?
  return false;
}

// static
@@ -7555,14 +7553,6 @@ nsContentUtils::HasDistributedChildren(nsIContent* aContent)
    return true;
  }

  HTMLContentElement* contentEl = HTMLContentElement::FromContent(aContent);
  if (contentEl && contentEl->IsInsertionPoint()) {
    // Children of a content insertion point are distributed to the
    // content insertion point if the content insertion point does
    // not match any nodes (fallback content).
    return contentEl->MatchedNodes().IsEmpty();
  }

  return false;
}

+1 −1
Original line number Diff line number Diff line
@@ -140,7 +140,7 @@ skip-if = stylo # bug 1293844
[test_bug1145910.html]
skip-if = stylo # bug 1293844
[test_bug1150308.html]
skip-if = stylo # bug 1293844
skip-if = true || stylo # bug 1293844, bug  1421545
[test_bug1248459.html]
[test_bug1264380.html]
run-if = (e10s && os != "win" && !stylo) # Bug 1270043, crash at windows platforms; Bug1264380 comment 20, nsDragService::InvokeDragSessionImpl behaves differently among platform implementations in non-e10s mode which prevents us to check the validity of nsIDragService::getCurrentSession() consistently via synthesize mouse clicks in non-e10s mode. bug 1293844 for stylo.
Loading