Bug 1552313 - Implement custom element disabledFeatures and disableInternals; r=smaug,edgar
☠☠ backed out by ac493f0eacfc ☠ ☠
authorJohn Dai <jdai@mozilla.com>
Mon, 11 Nov 2019 15:42:56 +0000
changeset 501481 f26d61e06a9d8f286857630967855a6ccdaecf08
parent 501480 a8c569ac480c48c18c14438154175bfdca389769
child 501482 5f8c6c9f2d36a0667fb3bc2b036390055692c4ba
push id114170
push usermalexandru@mozilla.com
push dateTue, 12 Nov 2019 21:58:32 +0000
treeherdermozilla-inbound@9e3f44e87a1a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, edgar
bugs1552313
milestone72.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 1552313 - Implement custom element disabledFeatures and disableInternals; r=smaug,edgar - Introduce `dom.webcomponents.elementInternals.enabled` for custom element's elementInternals. - Implement disabledFeatures static field and disableInternals. - Refactor get observedAttributes sequence. Differential Revision: https://phabricator.services.mozilla.com/D52156
dom/base/CustomElementRegistry.cpp
dom/base/CustomElementRegistry.h
modules/libpref/init/StaticPrefList.yaml
testing/web-platform/meta/custom-elements/CustomElementRegistry.html.ini
testing/web-platform/meta/custom-elements/__dir__.ini
xpcom/ds/StaticAtoms.py
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -631,16 +631,71 @@ int32_t CustomElementRegistry::InferName
     }
 
     JS_GetPrototype(aCx, proto, &proto);
   }
 
   return kNameSpaceID_XHTML;
 }
 
+bool CustomElementRegistry::JSObjectToAtomArray(
+    JSContext* aCx, JS::Handle<JSObject*> aConstructor, const char16_t* aName,
+    nsTArray<RefPtr<nsAtom>>& aArray, ErrorResult& aRv) {
+  JS::RootedValue iterable(aCx, JS::UndefinedValue());
+  if (!JS_GetUCProperty(aCx, aConstructor, aName,
+                        std::char_traits<char16_t>::length(aName), &iterable)) {
+    aRv.NoteJSContextException(aCx);
+    return false;
+  }
+
+  if (!iterable.isUndefined()) {
+    if (!iterable.isObject()) {
+      aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(nsDependentString(aName));
+      return false;
+    }
+
+    JS::ForOfIterator iter(aCx);
+    if (!iter.init(iterable, JS::ForOfIterator::AllowNonIterable)) {
+      aRv.NoteJSContextException(aCx);
+      return false;
+    }
+
+    if (!iter.valueIsIterable()) {
+      aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(nsDependentString(aName));
+      return false;
+    }
+
+    JS::Rooted<JS::Value> attribute(aCx);
+    while (true) {
+      bool done;
+      if (!iter.next(&attribute, &done)) {
+        aRv.NoteJSContextException(aCx);
+        return false;
+      }
+      if (done) {
+        break;
+      }
+
+      nsAutoString attrStr;
+      if (!ConvertJSValueToString(aCx, attribute, eStringify, eStringify,
+                                  attrStr)) {
+        aRv.NoteJSContextException(aCx);
+        return false;
+      }
+
+      if (!aArray.AppendElement(NS_Atomize(attrStr))) {
+        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
 // https://html.spec.whatwg.org/multipage/scripting.html#element-definition
 void CustomElementRegistry::Define(
     JSContext* aCx, const nsAString& aName,
     CustomElementConstructor& aFunctionConstructor,
     const ElementDefinitionOptions& aOptions, ErrorResult& aRv) {
   JS::Rooted<JSObject*> constructor(aCx, aFunctionConstructor.CallableOrNull());
 
   // We need to do a dynamic unwrap in order to throw the right exception.  We
@@ -777,16 +832,18 @@ void CustomElementRegistry::Define(
     aRv.ThrowDOMException(
         NS_ERROR_DOM_NOT_SUPPORTED_ERR,
         "Cannot define a custom element while defining another custom element");
     return;
   }
 
   auto callbacksHolder = MakeUnique<LifecycleCallbacks>();
   nsTArray<RefPtr<nsAtom>> observedAttributes;
+  AutoTArray<RefPtr<nsAtom>, 2> disabledFeatures;
+  bool disableInternals = false;
   {  // Set mIsCustomDefinitionRunning.
     /**
      * 9. Set this CustomElementRegistry's element definition is running flag.
      */
     AutoSetRunningFlag as(this);
 
     /**
      * 10.1. Let prototype be Get(constructor, "prototype"). Rethrow any
@@ -836,68 +893,41 @@ void CustomElementRegistry::Define(
      *       1. Let observedAttributesIterable be Get(constructor,
      *          "observedAttributes"). Rethrow any exceptions.
      *       2. If observedAttributesIterable is not undefined, then set
      *          observedAttributes to the result of converting
      *          observedAttributesIterable to a sequence<DOMString>. Rethrow
      *          any exceptions from the conversion.
      */
     if (callbacksHolder->mAttributeChangedCallback.WasPassed()) {
-      JS::Rooted<JS::Value> observedAttributesIterable(aCx);
+      if (!JSObjectToAtomArray(aCx, constructor, u"observedAttributes",
+                               observedAttributes, aRv)) {
+        return;
+      }
+    }
 
-      if (!JS_GetProperty(aCx, constructor, "observedAttributes",
-                          &observedAttributesIterable)) {
-        aRv.NoteJSContextException(aCx);
+    /**
+     * 14.6. Let disabledFeatures be an empty sequence<DOMString>.
+     * 14.7. Let disabledFeaturesIterable be Get(constructor,
+     *       "disabledFeatures"). Rethrow any exceptions.
+     * 14.8. If disabledFeaturesIterable is not undefined, then set
+     *       disabledFeatures to the result of converting
+     *       disabledFeaturesIterable to a sequence<DOMString>.
+     *       Rethrow any exceptions from the conversion.
+     */
+    if (StaticPrefs::dom_webcomponents_elementInternals_enabled()) {
+      if (!JSObjectToAtomArray(aCx, constructor, u"disabledFeatures",
+                               disabledFeatures, aRv)) {
         return;
       }
 
-      if (!observedAttributesIterable.isUndefined()) {
-        if (!observedAttributesIterable.isObject()) {
-          aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(
-              NS_LITERAL_STRING("observedAttributes"));
-          return;
-        }
-
-        JS::ForOfIterator iter(aCx);
-        if (!iter.init(observedAttributesIterable,
-                       JS::ForOfIterator::AllowNonIterable)) {
-          aRv.NoteJSContextException(aCx);
-          return;
-        }
-
-        if (!iter.valueIsIterable()) {
-          aRv.ThrowTypeError<MSG_NOT_SEQUENCE>(
-              NS_LITERAL_STRING("observedAttributes"));
-          return;
-        }
-
-        JS::Rooted<JS::Value> attribute(aCx);
-        while (true) {
-          bool done;
-          if (!iter.next(&attribute, &done)) {
-            aRv.NoteJSContextException(aCx);
-            return;
-          }
-          if (done) {
-            break;
-          }
-
-          nsAutoString attrStr;
-          if (!ConvertJSValueToString(aCx, attribute, eStringify, eStringify,
-                                      attrStr)) {
-            aRv.NoteJSContextException(aCx);
-            return;
-          }
-
-          if (!observedAttributes.AppendElement(NS_Atomize(attrStr))) {
-            aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-            return;
-          }
-        }
-      }
+      // 14.9. Set disableInternals to true if disabledFeaturesSequence contains
+      //       "internals".
+      disableInternals = disabledFeatures.Contains(
+          static_cast<nsStaticAtom*>(nsGkAtoms::internals));
     }
   }  // Unset mIsCustomDefinitionRunning
 
   /**
    * 11. Let definition be a new custom element definition with name name,
    *     local name localName, constructor constructor, prototype prototype,
    *     observed attributes observedAttributes, and lifecycle callbacks
    *     lifecycleCallbacks.
@@ -910,17 +940,18 @@ void CustomElementRegistry::Define(
    */
   if (!mConstructors.put(constructorUnwrapped, nameAtom)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   RefPtr<CustomElementDefinition> definition = new CustomElementDefinition(
       nameAtom, localNameAtom, nameSpaceID, &aFunctionConstructor,
-      std::move(observedAttributes), std::move(callbacksHolder));
+      std::move(observedAttributes), std::move(callbacksHolder),
+      disableInternals);
 
   CustomElementDefinition* def = definition.get();
   mCustomDefinitions.Put(nameAtom, definition.forget());
 
   MOZ_ASSERT(mCustomDefinitions.Count() == mConstructors.count(),
              "Number of entries should be the same");
 
   /**
@@ -1400,18 +1431,19 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CustomElementDefinition, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CustomElementDefinition, Release)
 
 CustomElementDefinition::CustomElementDefinition(
     nsAtom* aType, nsAtom* aLocalName, int32_t aNamespaceID,
     CustomElementConstructor* aConstructor,
     nsTArray<RefPtr<nsAtom>>&& aObservedAttributes,
-    UniquePtr<LifecycleCallbacks>&& aCallbacks)
+    UniquePtr<LifecycleCallbacks>&& aCallbacks, bool aDisableInternals)
     : mType(aType),
       mLocalName(aLocalName),
       mNamespaceID(aNamespaceID),
       mConstructor(aConstructor),
       mObservedAttributes(std::move(aObservedAttributes)),
-      mCallbacks(std::move(aCallbacks)) {}
+      mCallbacks(std::move(aCallbacks)),
+      mDisableInternals(aDisableInternals) {}
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -132,17 +132,18 @@ struct CustomElementData {
 struct CustomElementDefinition {
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CustomElementDefinition)
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CustomElementDefinition)
 
   CustomElementDefinition(nsAtom* aType, nsAtom* aLocalName,
                           int32_t aNamespaceID,
                           CustomElementConstructor* aConstructor,
                           nsTArray<RefPtr<nsAtom>>&& aObservedAttributes,
-                          UniquePtr<LifecycleCallbacks>&& aCallbacks);
+                          UniquePtr<LifecycleCallbacks>&& aCallbacks,
+                          bool aDisableInternals);
 
   // The type (name) for this custom element, for <button is="x-foo"> or <x-foo>
   // this would be x-foo.
   RefPtr<nsAtom> mType;
 
   // The localname to (e.g. <button is=type> -- this would be button).
   RefPtr<nsAtom> mLocalName;
 
@@ -153,16 +154,19 @@ struct CustomElementDefinition {
   RefPtr<CustomElementConstructor> mConstructor;
 
   // The list of attributes that this custom element observes.
   nsTArray<RefPtr<nsAtom>> mObservedAttributes;
 
   // The lifecycle callbacks to call for this custom element.
   UniquePtr<LifecycleCallbacks> mCallbacks;
 
+  // Determine whether to allow to attachInternals() for this custom element.
+  bool mDisableInternals = false;
+
   // A construction stack. Use nullptr to represent an "already constructed
   // marker".
   nsTArray<RefPtr<Element>> mConstructionStack;
 
   // See step 6.1.10 of https://dom.spec.whatwg.org/#concept-create-element
   // which set up the prefix after a custom element is created. However, In
   // Gecko, the prefix isn't allowed to be changed in NodeInfo, so we store the
   // prefix information here and propagate to where NodeInfo is assigned to a
@@ -453,16 +457,20 @@ class CustomElementRegistry final : publ
 
     nsWeakPtr elem = do_GetWeakReference(aElement);
     elements->PutEntry(elem);
   }
 
  private:
   ~CustomElementRegistry();
 
+  bool JSObjectToAtomArray(JSContext* aCx, JS::Handle<JSObject*> aConstructor,
+                           const char16_t* aName,
+                           nsTArray<RefPtr<nsAtom>>& aArray, ErrorResult& aRv);
+
   static UniquePtr<CustomElementCallback> CreateCustomElementCallback(
       Document::ElementCallbackType aType, Element* aCustomElement,
       LifecycleCallbackArgs* aArgs,
       LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
       CustomElementDefinition* aDefinition);
 
   void UpgradeCandidates(nsAtom* aKey, CustomElementDefinition* aDefinition,
                          ErrorResult& aRv);
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -2615,16 +2615,22 @@
 
 # NOTE: This preference is used in unit tests. If it is removed or its default
 # value changes, please update test_sharedMap_var_caches.js accordingly.
 - name: dom.webcomponents.shadowdom.report_usage
   type: bool
   value: false
   mirror: always
 
+# Is support for elementInternals enabled?
+- name: dom.webcomponents.elementInternals.enabled
+  type: bool
+  value: @IS_NIGHTLY_BUILD@
+  mirror: always
+
 # Is support for the Web GPU API enabled?
 - name: dom.webgpu.enable
   type: RelaxedAtomicBool
   value: false
   mirror: always
 
 # Is support for HTMLInputElement.webkitEntries enabled?
 - name: dom.webkitBlink.filesystem.enabled
--- a/testing/web-platform/meta/custom-elements/CustomElementRegistry.html.ini
+++ b/testing/web-platform/meta/custom-elements/CustomElementRegistry.html.ini
@@ -12,23 +12,16 @@
     expected: FAIL
 
   [customElements.define must rethrow an exception thrown while getting formAssociated on the constructor prototype]
     expected: FAIL
 
   [customElements.define must rethrow an exception thrown while getting additional formAssociated callbacks on the constructor prototype]
     expected: FAIL
 
-  [customElements.define must rethrow an exception thrown while retrieving Symbol.iterator on disabledFeatures]
-    expected: FAIL
-
   [customElements.define must get four additional callbacks on the prototype if formAssociated is converted to true]
     expected: FAIL
 
-  [customElements.define must rethrow an exception thrown while getting disabledFeatures on the constructor prototype]
+  [customElements.define must not throw when defining another custom element in a different global object during Get(constructor, "prototype")]
     expected: FAIL
 
-  [customElements.define must rethrow an exception thrown while iterating over disabledFeatures to sequence<DOMString>]
-    expected: FAIL
-
-  [customElements.define must rethrow an exception thrown while converting the value of disabledFeatures to sequence<DOMString>]
-    expected: FAIL
-
+  [customElements.define must get "prototype" property of the constructor ]
+    expected: FAIL
\ No newline at end of file
--- a/testing/web-platform/meta/custom-elements/__dir__.ini
+++ b/testing/web-platform/meta/custom-elements/__dir__.ini
@@ -0,0 +1,1 @@
+prefs: [dom.webcomponents.elementInternals.enabled:true]
\ No newline at end of file
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -532,16 +532,17 @@ STATIC_ATOMS = [
     Atom("input", "input"),
     Atom("inputmode", "inputmode"),
     Atom("ins", "ins"),
     Atom("insertafter", "insertafter"),
     Atom("insertbefore", "insertbefore"),
     Atom("insertion", "insertion"),
     Atom("integer", "integer"),
     Atom("integrity", "integrity"),
+    Atom("internals", "internals"),
     Atom("intersection", "intersection"),
     Atom("intersectionobserverlist", "intersectionobserverlist"),
     Atom("is", "is"),
     Atom("ismap", "ismap"),
     Atom("itemid", "itemid"),
     Atom("itemprop", "itemprop"),
     Atom("itemref", "itemref"),
     Atom("itemscope", "itemscope"),