Bug 1409976 - Part 1: Add support for `slotchange` event. r=smaug
authorJessica Jong <jjong@mozilla.com>
Tue, 19 Dec 2017 23:14:55 +0800
changeset 448956 1d6f43b1c0effb345b345b043e656c1733ee0ff9
parent 448955 fcfbf4c2a330c0e7d95f3f22fdf3fb65fc6f1cee
child 448957 51a3a1b8513ce4bf424dce4d14cb2465529de4f4
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1409976
milestone59.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 1409976 - Part 1: Add support for `slotchange` event. r=smaug MozReview-Commit-ID: FoJXlXfqDxU
dom/base/DocGroup.cpp
dom/base/DocGroup.h
dom/base/nsDOMMutationObserver.cpp
dom/base/nsDOMMutationObserver.h
dom/html/HTMLSlotElement.cpp
dom/html/HTMLSlotElement.h
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -3,20 +3,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/Telemetry.h"
 #include "nsIDocShell.h"
+#include "nsDOMMutationObserver.h"
 
 namespace mozilla {
 namespace dom {
 
+AutoTArray<RefPtr<DocGroup>, 2>* DocGroup::sPendingDocGroups = nullptr;
+
 /* static */ nsresult
 DocGroup::GetKey(nsIPrincipal* aPrincipal, nsACString& aKey)
 {
   // Use GetBaseDomain() to handle things like file URIs, IP address URIs,
   // etc. correctly.
   nsresult rv = aPrincipal->GetBaseDomain(aKey);
   if (NS_FAILED(rv)) {
     // We don't really know what to do here.  But we should be conservative,
@@ -76,10 +79,28 @@ DocGroup::AbstractMainThreadFor(TaskCate
 }
 
 bool*
 DocGroup::GetValidAccessPtr()
 {
   return mTabGroup->GetValidAccessPtr();
 }
 
+void
+DocGroup::SignalSlotChange(const HTMLSlotElement* aSlot)
+{
+  if (mSignalSlotList.Contains(aSlot)) {
+    return;
+  }
+
+  mSignalSlotList.AppendElement(const_cast<HTMLSlotElement*>(aSlot));
+
+  if (!sPendingDocGroups) {
+    // Queue a mutation observer compound microtask.
+    nsDOMMutationObserver::QueueMutationObserverMicroTask();
+    sPendingDocGroups = new AutoTArray<RefPtr<DocGroup>, 2>;
+  }
+
+  sPendingDocGroups->AppendElement(this);
+}
+
 }
 }
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -10,16 +10,17 @@
 #include "nsISupportsImpl.h"
 #include "nsIPrincipal.h"
 #include "nsTHashtable.h"
 #include "nsString.h"
 
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/dom/CustomElementRegistry.h"
+#include "mozilla/dom/HTMLSlotElement.h"
 
 namespace mozilla {
 class AbstractThread;
 namespace dom {
 
 // Two browsing contexts are considered "related" if they are reachable from one
 // another through window.opener, window.parent, or window.frames. This is the
 // spec concept of a "unit of related browsing contexts"
@@ -94,22 +95,40 @@ public:
     mTabGroup->ValidateAccess();
   }
 
   // Return a pointer that can be continually checked to see if access to this
   // DocGroup is valid. This pointer should live at least as long as the
   // DocGroup.
   bool* GetValidAccessPtr();
 
+  // Append aSlot to the list of signal slot list, if it's not in it already
+  // list, and queue a mutation observer microtask.
+  void SignalSlotChange(const mozilla::dom::HTMLSlotElement* aSlot);
+
+  const nsTArray<RefPtr<HTMLSlotElement>>& SignalSlotList() const
+  {
+    return mSignalSlotList;
+  }
+
+  void ClearSignalSlotList()
+  {
+    mSignalSlotList.Clear();
+  }
+
+  // List of DocGroups that has non-empty signal slot list.
+  static AutoTArray<RefPtr<DocGroup>, 2>* sPendingDocGroups;
+
 private:
   DocGroup(TabGroup* aTabGroup, const nsACString& aKey);
   ~DocGroup();
 
   nsCString mKey;
   RefPtr<TabGroup> mTabGroup;
   nsTArray<nsIDocument*> mDocuments;
   RefPtr<mozilla::dom::CustomElementReactionsStack> mReactionsStack;
+  nsTArray<RefPtr<HTMLSlotElement>> mSignalSlotList;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // defined(DocGroup_h)
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -7,16 +7,17 @@
 #include "nsDOMMutationObserver.h"
 
 #include "mozilla/AnimationTarget.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/OwningNonNull.h"
 
 #include "mozilla/dom/Animation.h"
 #include "mozilla/dom/KeyframeEffectReadOnly.h"
+#include "mozilla/dom/DocGroup.h"
 
 #include "nsContentUtils.h"
 #include "nsCSSPseudoElements.h"
 #include "nsError.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsServiceManagerUtils.h"
 #include "nsTextFragment.h"
@@ -605,16 +606,38 @@ public:
   }
 
   virtual bool Suppressed() override
   {
     return nsDOMMutationObserver::AllScheduledMutationObserversAreSuppressed();
   }
 };
 
+/* static */ void
+nsDOMMutationObserver::QueueMutationObserverMicroTask()
+{
+  CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
+  if (!ccjs) {
+    return;
+  }
+
+  RefPtr<MutationObserverMicroTask> momt =
+    new MutationObserverMicroTask();
+  ccjs->DispatchMicroTaskRunnable(momt.forget());
+}
+
+void
+nsDOMMutationObserver::HandleMutations(mozilla::AutoSlowOperation& aAso)
+{
+  if (sScheduledMutationObservers ||
+      mozilla::dom::DocGroup::sPendingDocGroups) {
+    HandleMutationsInternal(aAso);
+  }
+}
+
 void
 nsDOMMutationObserver::RescheduleForRun()
 {
   if (!sScheduledMutationObservers) {
     CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
     if (!ccjs) {
       return;
     }
@@ -882,16 +905,32 @@ nsDOMMutationObserver::HandleMutation()
   mCallback->Call(this, mutations, *this);
 }
 
 void
 nsDOMMutationObserver::HandleMutationsInternal(AutoSlowOperation& aAso)
 {
   nsTArray<RefPtr<nsDOMMutationObserver> >* suppressedObservers = nullptr;
 
+  // Let signalList be a copy of unit of related similar-origin browsing
+  // contexts' signal slot list.
+  nsTArray<RefPtr<HTMLSlotElement>> signalList;
+  if (DocGroup::sPendingDocGroups) {
+    for (uint32_t i = 0; i < DocGroup::sPendingDocGroups->Length(); ++i) {
+      DocGroup* docGroup = DocGroup::sPendingDocGroups->ElementAt(i);
+      signalList.AppendElements(docGroup->SignalSlotList());
+
+      // Empty unit of related similar-origin browsing contexts' signal slot
+      // list.
+      docGroup->ClearSignalSlotList();
+    }
+    delete DocGroup::sPendingDocGroups;
+    DocGroup::sPendingDocGroups = nullptr;
+  }
+
   while (sScheduledMutationObservers) {
     AutoTArray<RefPtr<nsDOMMutationObserver>, 4>* observers =
       sScheduledMutationObservers;
     sScheduledMutationObservers = nullptr;
     for (uint32_t i = 0; i < observers->Length(); ++i) {
       RefPtr<nsDOMMutationObserver> currentObserver =
         static_cast<nsDOMMutationObserver*>((*observers)[i]);
       if (!currentObserver->Suppressed()) {
@@ -912,16 +951,21 @@ nsDOMMutationObserver::HandleMutationsIn
   if (suppressedObservers) {
     for (uint32_t i = 0; i < suppressedObservers->Length(); ++i) {
       static_cast<nsDOMMutationObserver*>(suppressedObservers->ElementAt(i))->
         RescheduleForRun();
     }
     delete suppressedObservers;
     suppressedObservers = nullptr;
   }
+
+  // Fire slotchange event for each slot in signalList.
+  for (uint32_t i = 0; i < signalList.Length(); ++i) {
+    signalList[i]->FireSlotChangeEvent();
+  }
 }
 
 nsDOMMutationRecord*
 nsDOMMutationObserver::CurrentRecord(nsAtom* aType)
 {
   NS_ASSERTION(sMutationLevel > 0, "Unexpected mutation level!");
 
   while (mCurrentMutations.Length() < sMutationLevel) {
--- a/dom/base/nsDOMMutationObserver.h
+++ b/dom/base/nsDOMMutationObserver.h
@@ -570,22 +570,19 @@ public:
   void ClearPendingRecords()
   {
     mFirstPendingMutation = nullptr;
     mLastPendingMutation = nullptr;
     mPendingMutationCount = 0;
   }
 
   // static methods
-  static void HandleMutations(mozilla::AutoSlowOperation& aAso)
-  {
-    if (sScheduledMutationObservers) {
-      HandleMutationsInternal(aAso);
-    }
-  }
+  static void QueueMutationObserverMicroTask();
+
+  static void HandleMutations(mozilla::AutoSlowOperation& aAso);
 
   static bool AllScheduledMutationObserversAreSuppressed()
   {
     if (sScheduledMutationObservers) {
       uint32_t len = sScheduledMutationObservers->Length();
       if (len > 0) {
         for (uint32_t i = 0; i < len; ++i) {
           if (!(*sScheduledMutationObservers)[i]->Suppressed()) {
--- a/dom/html/HTMLSlotElement.cpp
+++ b/dom/html/HTMLSlotElement.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "mozilla/dom/DocGroup.h"
 #include "mozilla/dom/HTMLSlotElement.h"
 #include "mozilla/dom/HTMLSlotElementBinding.h"
 #include "mozilla/dom/HTMLUnknownElement.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "nsGkAtoms.h"
 #include "nsDocument.h"
 
 nsGenericHTMLElement*
@@ -201,16 +202,36 @@ HTMLSlotElement::ClearAssignedNodes()
 {
   for (uint32_t i = 0; i < mAssignedNodes.Length(); i++) {
     mAssignedNodes[i]->AsContent()->SetAssignedSlot(nullptr);
   }
 
   mAssignedNodes.Clear();
 }
 
+void
+HTMLSlotElement::EnqueueSlotChangeEvent() const
+{
+  DocGroup* docGroup = OwnerDoc()->GetDocGroup();
+  if (!docGroup) {
+    return;
+  }
+
+  docGroup->SignalSlotChange(this);
+}
+
+void
+HTMLSlotElement::FireSlotChangeEvent()
+{
+  nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
+                                       static_cast<nsIContent*>(this),
+                                       NS_LITERAL_STRING("slotchange"), true,
+                                       false);
+}
+
 JSObject*
 HTMLSlotElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLSlotElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLSlotElement.h
+++ b/dom/html/HTMLSlotElement.h
@@ -59,16 +59,19 @@ public:
 
   // Helper methods
   const nsTArray<RefPtr<nsINode>>& AssignedNodes() const;
   void InsertAssignedNode(uint32_t aIndex, nsINode* aNode);
   void AppendAssignedNode(nsINode* aNode);
   void RemoveAssignedNode(nsINode* aNode);
   void ClearAssignedNodes();
 
+  void EnqueueSlotChangeEvent() const;
+  void FireSlotChangeEvent();
+
 protected:
   virtual ~HTMLSlotElement();
   virtual JSObject*
   WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   nsTArray<RefPtr<nsINode>> mAssignedNodes;
 };