diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp index b97115e7c4..fdf90401ed 100644 --- a/dom/base/CustomElementRegistry.cpp +++ b/dom/base/CustomElementRegistry.cpp @@ -416,6 +416,95 @@ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType reactionsStack->EnqueueCallbackReaction(aCustomElement, Move(callback)); } +namespace { + +class CandidateFinder +{ +public: + CandidateFinder(nsTArray&& aCandidates, nsIDocument* aDoc); + nsTArray> OrderedCandidates(); + +private: + bool Traverse(Element* aRoot, nsTArray>& aOrderedElements); + + nsCOMPtr mDoc; + nsInterfaceHashtable, Element> mCandidates; +}; + +CandidateFinder::CandidateFinder(nsTArray&& aCandidates, + nsIDocument* aDoc) + : mDoc(aDoc) + , mCandidates(aCandidates.Length()) +{ + MOZ_ASSERT(mDoc); + for (auto& candidate : aCandidates) { + nsCOMPtr elem = do_QueryReferent(candidate); + if (!elem) { + continue; + } + + Element* key = elem.get(); + mCandidates.Put(key, elem.forget()); + } +} + +nsTArray> +CandidateFinder::OrderedCandidates() +{ + if (mCandidates.Count() == 1) { + // Fast path for one candidate. + for (auto iter = mCandidates.Iter(); !iter.Done(); iter.Next()) { + nsTArray> rval({ Move(iter.Data()) }); + iter.Remove(); + return rval; + } + } + + nsTArray> orderedElements(mCandidates.Count()); + for (Element* child = mDoc->GetFirstElementChild(); child; child = child->GetNextElementSibling()) { + if (!Traverse(child->AsElement(), orderedElements)) { + break; + } + } + + return orderedElements; +} + +bool +CandidateFinder::Traverse(Element* aRoot, nsTArray>& aOrderedElements) +{ + nsCOMPtr elem = mCandidates.Get(aRoot); + if (elem) { + mCandidates.Remove(aRoot); + aOrderedElements.AppendElement(Move(elem)); + if (mCandidates.Count() == 0) { + return false; + } + } + + if (ShadowRoot* root = aRoot->GetShadowRoot()) { + // First iterate the children of the shadow root if aRoot is a shadow host. + for (Element* child = root->GetFirstElementChild(); child; + child = child->GetNextElementSibling()) { + if (!Traverse(child, aOrderedElements)) { + return false; + } + } + } + + // Iterate the explicit children of aRoot. + for (Element* child = aRoot->GetFirstElementChild(); child; + child = child->GetNextElementSibling()) { + if (!Traverse(child, aOrderedElements)) { + return false; + } + } + + return true; +} + +} + void CustomElementRegistry::UpgradeCandidates(nsIAtom* aKey, CustomElementDefinition* aDefinition, @@ -427,18 +516,13 @@ CustomElementRegistry::UpgradeCandidates(nsIAtom* aKey, return; } - // TODO: Bug 1326028 - Upgrade custom element in shadow-including tree order nsAutoPtr> candidates; mCandidatesMap.RemoveAndForget(aKey, candidates); if (candidates) { CustomElementReactionsStack* reactionsStack = docGroup->CustomElementReactionsStack(); - for (size_t i = 0; i < candidates->Length(); ++i) { - nsCOMPtr elem = do_QueryReferent(candidates->ElementAt(i)); - if (!elem) { - continue; - } - + CandidateFinder finder(Move(*candidates), mWindow->GetExtantDoc()); + for (auto& elem : finder.OrderedCandidates()) { reactionsStack->EnqueueUpgradeReaction(elem, aDefinition); } }