Bug 834208 - Rewrite document/window named getters per spec; r?mystor draft
authorAryeh Gregor <ayg@aryeh.name>
Thu, 10 Aug 2017 18:53:55 +0300
changeset 650454 7d93b7e3c1499b0b81d970722537b98f5d88efdd
parent 650453 8edfbf52a62cc47d9be54e42846de307775ab364
child 727411 5d59982648203293f53f715dec02ef26b1a8c979
push id75397
push userbmo:ayg@aryeh.name
push dateTue, 22 Aug 2017 11:45:55 +0000
reviewersmystor
bugs834208, 1279218
milestone57.0a1
Bug 834208 - Rewrite document/window named getters per spec; r?mystor Per spec and other browsers, the two named getters return quite different things, so I split nsIdentifierMapEntry's content list in two. The content lists now also contain both names and id's, in addition to the id list (which is still used for other things like getElementById()). This means one element might be on up to three lists, but this implementation is necessary for interop. Specifically, we must return a consistent live list for document.foo and for window.foo, so they need to be stored separately and cannot be generated on the fly from a single shared list. All of the failures in the new rewritten named-item.html are due to our removal of <applet> in bug 1279218 (which is not yet in the specs or wpt tests). We also don't implement object/embed exposure on document per spec yet, but I didn't test that yet either, so there are no expected failures because of it. The crashtest change is because previously if there were multiple elements with the same id, we would return the first. Now we return a list. The wpt change to named-objects.html brings the test in line with the spec. For some reason I don't understand, all UAs (before Firefox with this patch) currently pass the test, but they do support <frameset name> as a window property in other tests, so it seems like a UA bug and the test should change back. MozReview-Commit-ID: EMgYimmYHsn
dom/base/Element.cpp
dom/base/WindowNamedPropertiesHandler.cpp
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsIDocument.h
dom/base/nsIdentifierMapEntry.h
dom/base/test/test_window_element_enumeration.html
dom/html/nsGenericHTMLElement.cpp
dom/html/nsGenericHTMLElement.h
dom/html/nsHTMLDocument.cpp
dom/html/nsHTMLDocument.h
layout/generic/crashtests/769303-1.html
testing/web-platform/meta/MANIFEST.json
testing/web-platform/meta/html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html.ini
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/named-item.html.ini
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/nameditem-02.html.ini
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/nameditem-03.html.ini
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/nameditem-07.html.ini
testing/web-platform/meta/html/dom/documents/dom-tree-accessors/nameditem-08.html.ini
testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html
testing/web-platform/tests/html/dom/documents/dom-tree-accessors/named-item.html
testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-01.html
testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-02.html
testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-03.html
testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-04.html
testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-05.html
testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-06.html
testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-07.html
testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-08.html
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1071,16 +1071,17 @@ Element::AddToIdTable(nsIAtom* aId)
   NS_ASSERTION(HasID(), "Node doesn't have an ID?");
   if (IsInShadowTree()) {
     ShadowRoot* containingShadow = GetContainingShadow();
     containingShadow->AddToIdTable(this, aId);
   } else {
     nsIDocument* doc = GetUncomposedDoc();
     if (doc && (!IsInAnonymousSubtree() || doc->IsXULDocument())) {
       doc->AddToIdTable(this, aId);
+      doc->AddToWindowTable(this, aId);
     }
   }
 }
 
 void
 Element::RemoveFromIdTable()
 {
   if (!HasID()) {
@@ -1094,16 +1095,20 @@ Element::RemoveFromIdTable()
     // been deleted during unlinking.
     if (containingShadow) {
       containingShadow->RemoveFromIdTable(this, id);
     }
   } else {
     nsIDocument* doc = GetUncomposedDoc();
     if (doc && (!IsInAnonymousSubtree() || doc->IsXULDocument())) {
       doc->RemoveFromIdTable(this, id);
+      if (!HasName() || !nsGenericHTMLElement::IsNameExposedOnWindow(this) ||
+          GetParsedAttr(nsGkAtoms::name)->GetAtomValue() != id) {
+        doc->RemoveFromWindowTable(this, id);
+      }
     }
   }
 }
 
 already_AddRefed<ShadowRoot>
 Element::CreateShadowRoot(ErrorResult& aError)
 {
   nsAutoScriptBlocker scriptBlocker;
--- a/dom/base/WindowNamedPropertiesHandler.cpp
+++ b/dom/base/WindowNamedPropertiesHandler.cpp
@@ -125,34 +125,39 @@ WindowNamedPropertiesHandler::getOwnProp
 
   // The rest of this function is for HTML documents only.
   nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(win->GetExtantDoc());
   if (!htmlDoc) {
     return true;
   }
   nsHTMLDocument* document = static_cast<nsHTMLDocument*>(htmlDoc.get());
 
-  Element* element = document->GetElementById(str);
-  if (element) {
-    JS::Rooted<JS::Value> v(aCx);
-    if (!WrapObject(aCx, element, &v)) {
+  nsIdentifierMapEntry* entry = document->mIdentifierMap.GetEntry(str);
+  if (!entry) {
+    return true;
+  }
+
+  nsBaseContentList* list = entry->GetWindowContentList();
+
+  if (!list || list->Length() == 0) {
+    return true;
+  }
+
+  JS::Rooted<JS::Value> v(aCx);
+
+  if (list->Length() == 1) {
+    // Only one valid item, return the element instead of the list.
+    if (!dom::WrapObject(aCx, list->Item(0), nullptr, &v)) {
       return false;
     }
     FillPropertyDescriptor(aDesc, aProxy, 0, v);
     return true;
   }
 
-  nsWrapperCache* cache;
-  nsISupports* result = document->ResolveName(str, &cache);
-  if (!result) {
-    return true;
-  }
-
-  JS::Rooted<JS::Value> v(aCx);
-  if (!WrapObject(aCx, result, cache, nullptr, &v)) {
+  if (!dom::WrapObject(aCx, list, nullptr, &v)) {
     return false;
   }
   FillPropertyDescriptor(aDesc, aProxy, 0, v);
   return true;
 }
 
 bool
 WindowNamedPropertiesHandler::defineProperty(JSContext* aCx,
@@ -214,17 +219,23 @@ WindowNamedPropertiesHandler::ownPropNam
   names.Clear();
   nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(win->GetExtantDoc());
   if (!htmlDoc) {
     return true;
   }
   nsHTMLDocument* document = static_cast<nsHTMLDocument*>(htmlDoc.get());
   // Document names are enumerable, so we want to get them no matter what flags
   // is.
-  document->GetSupportedNames(names);
+  for (auto iter = document->mIdentifierMap.Iter(); !iter.Done(); iter.Next()) {
+    nsIdentifierMapEntry* entry = iter.Get();
+    nsBaseContentList* list = entry->GetWindowContentList();
+    if (list && list->Length()) {
+      names.AppendElement(entry->GetKeyAsString());
+    }
+  }
 
   JS::AutoIdVector docProps(aCx);
   if (!AppendNamedPropertyIds(aCx, aProxy, names, false, docProps)) {
     return false;
   }
 
   return js::AppendUnique(aCx, aProps, docProps);
 }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -325,32 +325,36 @@ GetHttpChannelHelper(nsIChannel* aChanne
 nsIdentifierMapEntry::~nsIdentifierMapEntry()
 {
 }
 
 void
 nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
 {
   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
-                                     "mIdentifierMap mNameContentList");
-  aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mNameContentList));
+                                     "mIdentifierMap mDocumentContentList");
+  aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mDocumentContentList));
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
+                                     "mIdentifierMap mWindowContentList");
+  aCallback->NoteXPCOMChild(static_cast<nsIDOMNodeList*>(mWindowContentList));
 
   if (mImageElement) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
                                        "mIdentifierMap mImageElement element");
     nsIContent* imageElement = mImageElement;
     aCallback->NoteXPCOMChild(imageElement);
   }
 }
 
 bool
 nsIdentifierMapEntry::IsEmpty()
 {
-  return mIdContentList.IsEmpty() && !mNameContentList &&
-         !mChangeCallbacks && !mImageElement;
+  return mIdContentList.IsEmpty() && !mDocumentContentList &&
+         !mWindowContentList && !mChangeCallbacks && !mImageElement;
 }
 
 Element*
 nsIdentifierMapEntry::GetIdElement()
 {
   return mIdContentList.SafeElementAt(0);
 }
 
@@ -630,39 +634,49 @@ SimpleHTMLCollection::NamedItem(const ns
   retVal.forget(aRetVal);
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
 
 void
-nsIdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement)
-{
-  if (!mNameContentList) {
-    mNameContentList = new SimpleHTMLCollection(aNode);
-  }
-
-  mNameContentList->AppendElement(aElement);
-}
-
-void
-nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
-{
-  if (mNameContentList) {
-    mNameContentList->RemoveElement(aElement);
-  }
-}
-
-bool
-nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty()
-{
-  Element* idElement = GetIdElement();
-  return idElement &&
-         nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
+nsIdentifierMapEntry::AddDocumentElement(nsIDocument* aNode, Element* aElement)
+{
+  if (!mDocumentContentList) {
+    mDocumentContentList = new SimpleHTMLCollection(aNode);
+  }
+
+  mDocumentContentList->AppendElement(aElement);
+}
+
+void
+nsIdentifierMapEntry::RemoveDocumentElement(Element* aElement)
+{
+  if (mDocumentContentList) {
+    mDocumentContentList->RemoveElement(aElement);
+  }
+}
+
+void
+nsIdentifierMapEntry::AddWindowElement(nsIDocument* aNode, Element* aElement)
+{
+  if (!mWindowContentList) {
+    mWindowContentList = new SimpleHTMLCollection(aNode);
+  }
+
+  mWindowContentList->AppendElement(aElement);
+}
+
+void
+nsIdentifierMapEntry::RemoveWindowElement(Element* aElement)
+{
+  if (mWindowContentList) {
+    mWindowContentList->RemoveElement(aElement);
+  }
 }
 
 size_t
 nsIdentifierMapEntry::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return mKey.mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
 }
 
@@ -3061,63 +3075,88 @@ nsIDocument::GetLastModified(nsAString& 
   if (!mLastModified.IsEmpty()) {
     aLastModified.Assign(mLastModified);
   } else {
     GetFormattedTimeString(PR_Now(), aLastModified);
   }
 }
 
 void
-nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName)
-{
-  MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement),
-             "Only put elements that need to be exposed as document['name'] in "
-             "the named table.");
+nsDocument::AddToDocumentTable(Element* aElement, nsIAtom* aName)
+{
+  MOZ_ASSERT((aElement->HasName() &&
+              nsGenericHTMLElement::IsNameExposedOnDoc(aElement)) ||
+             (aElement->HasID() &&
+              nsGenericHTMLElement::IsIdExposedOnDoc(aElement)));
 
   nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName);
 
   // Null for out-of-memory
   if (entry) {
-    if (!entry->HasNameElement() &&
-        !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
+    nsBaseContentList* list = entry->GetDocumentContentList();
+    if (!list || list->Length() == 0) {
       ++mExpandoAndGeneration.generation;
     }
-    entry->AddNameElement(this, aElement);
-  }
-}
-
-void
-nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName)
+    entry->AddDocumentElement(this, aElement);
+  }
+}
+
+void
+nsDocument::AddToWindowTable(Element* aElement, nsIAtom* aName)
+{
+  MOZ_ASSERT(aElement->HasID() ||
+             (aElement->HasName() &&
+              nsGenericHTMLElement::IsNameExposedOnWindow(aElement)));
+
+  nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName);
+
+  // Null for out-of-memory
+  if (entry) {
+    entry->AddWindowElement(this, aElement);
+  }
+}
+
+void
+nsDocument::RemoveFromDocumentTable(Element* aElement, nsIAtom* aName)
 {
   // Speed up document teardown
   if (mIdentifierMap.Count() == 0)
     return;
 
   nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName);
   if (!entry) // Could be false if the element was anonymous, hence never added
     return;
 
-  entry->RemoveNameElement(aElement);
-  if (!entry->HasNameElement() &&
-      !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
+  entry->RemoveDocumentElement(aElement);
+  nsBaseContentList* list = entry->GetDocumentContentList();
+  if (!list || list->Length() == 0) {
     ++mExpandoAndGeneration.generation;
   }
 }
 
 void
+nsDocument::RemoveFromWindowTable(Element* aElement, nsIAtom* aName)
+{
+  // Speed up document teardown
+  if (mIdentifierMap.Count() == 0)
+    return;
+
+  nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName);
+  if (!entry) // Could be false if the element was anonymous, hence never added
+    return;
+
+  entry->RemoveWindowElement(aElement);
+}
+
+void
 nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId)
 {
   nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
 
   if (entry) { /* True except on OOM */
-    if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
-        !entry->HasNameElement() &&
-        !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
-      ++mExpandoAndGeneration.generation;
-    }
     entry->AddIdElement(aElement);
   }
 }
 
 void
 nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId)
 {
   NS_ASSERTION(aId, "huhwhatnow?");
@@ -3127,21 +3166,16 @@ nsDocument::RemoveFromIdTable(Element *a
     return;
   }
 
   nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
   if (!entry) // Can be null for XML elements with changing ids.
     return;
 
   entry->RemoveIdElement(aElement);
-  if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
-      !entry->HasNameElement() &&
-      !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
-    ++mExpandoAndGeneration.generation;
-  }
   if (entry->IsEmpty()) {
     mIdentifierMap.RemoveEntry(entry);
   }
 }
 
 nsIPrincipal*
 nsDocument::GetPrincipal()
 {
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -536,18 +536,20 @@ public:
    */
   virtual mozilla::dom::ScriptLoader* ScriptLoader() override;
 
   /**
    * Add/Remove an element to the document's id and name hashes
    */
   virtual void AddToIdTable(Element* aElement, nsIAtom* aId) override;
   virtual void RemoveFromIdTable(Element* aElement, nsIAtom* aId) override;
-  virtual void AddToNameTable(Element* aElement, nsIAtom* aName) override;
-  virtual void RemoveFromNameTable(Element* aElement, nsIAtom* aName) override;
+  virtual void AddToDocumentTable(Element* aElement, nsIAtom* aName) override;
+  virtual void RemoveFromDocumentTable(Element* aElement, nsIAtom* aName) override;
+  virtual void AddToWindowTable(Element* aElement, nsIAtom* aName) override;
+  virtual void RemoveFromWindowTable(Element* aElement, nsIAtom* aName) override;
 
   /**
    * Add a new observer of document change notifications. Whenever
    * content is changed, appended, inserted or removed the observers are
    * informed.
    */
   virtual void AddObserver(nsIDocumentObserver* aObserver) override;
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -1453,18 +1453,20 @@ public:
    */
   virtual mozilla::dom::ScriptLoader* ScriptLoader() = 0;
 
   /**
    * Add/Remove an element to the document's id and name hashes
    */
   virtual void AddToIdTable(Element* aElement, nsIAtom* aId) = 0;
   virtual void RemoveFromIdTable(Element* aElement, nsIAtom* aId) = 0;
-  virtual void AddToNameTable(Element* aElement, nsIAtom* aName) = 0;
-  virtual void RemoveFromNameTable(Element* aElement, nsIAtom* aName) = 0;
+  virtual void AddToDocumentTable(Element* aElement, nsIAtom* aName) = 0;
+  virtual void RemoveFromDocumentTable(Element* aElement, nsIAtom* aName) = 0;
+  virtual void AddToWindowTable(Element* aElement, nsIAtom* aName) = 0;
+  virtual void RemoveFromWindowTable(Element* aElement, nsIAtom* aName) = 0;
 
   /**
    * Returns all elements in the fullscreen stack in the insertion order.
    */
   virtual nsTArray<Element*> GetFullscreenStack() const = 0;
 
   /**
    * Asynchronously requests that the document make aElement the fullscreen
--- a/dom/base/nsIdentifierMapEntry.h
+++ b/dom/base/nsIdentifierMapEntry.h
@@ -75,17 +75,18 @@ public:
   }
   explicit nsIdentifierMapEntry(const AtomOrString* aKey)
     : mKey(aKey ? *aKey : nullptr)
   {
   }
   nsIdentifierMapEntry(nsIdentifierMapEntry&& aOther) :
     mKey(mozilla::Move(aOther.GetKey())),
     mIdContentList(mozilla::Move(aOther.mIdContentList)),
-    mNameContentList(aOther.mNameContentList.forget()),
+    mDocumentContentList(aOther.mDocumentContentList.forget()),
+    mWindowContentList(aOther.mWindowContentList.forget()),
     mChangeCallbacks(aOther.mChangeCallbacks.forget()),
     mImageElement(aOther.mImageElement.forget())
   {
   }
   ~nsIdentifierMapEntry();
 
   KeyType GetKey() const { return mKey; }
 
@@ -120,24 +121,27 @@ public:
   static PLDHashNumber HashKey(const KeyTypePointer aKey)
   {
     return aKey->mAtom ?
       aKey->mAtom->hash() : mozilla::HashString(aKey->mString);
   }
 
   enum { ALLOW_MEMMOVE = false };
 
-  void AddNameElement(nsINode* aDocument, Element* aElement);
-  void RemoveNameElement(Element* aElement);
+  void AddDocumentElement(nsIDocument* aDocument, Element* aElement);
+  void AddWindowElement(nsIDocument* aDocument, Element* aElement);
+  void RemoveDocumentElement(Element* aElement);
+  void RemoveWindowElement(Element* aElement);
   bool IsEmpty();
-  nsBaseContentList* GetNameContentList() {
-    return mNameContentList;
+
+  nsBaseContentList* GetDocumentContentList() {
+    return mDocumentContentList;
   }
-  bool HasNameElement() const {
-    return mNameContentList && mNameContentList->Length() != 0;
+  nsBaseContentList* GetWindowContentList() {
+    return mWindowContentList;
   }
 
   /**
    * Returns the element if we know the element associated with this
    * id. Otherwise returns null.
    */
   Element* GetIdElement();
   /**
@@ -165,17 +169,16 @@ public:
    * This can fire ID change callbacks.
    */
   void RemoveIdElement(Element* aElement);
   /**
    * Set the image element override for this ID. This will be returned by
    * GetIdElement(true) if non-null.
    */
   void SetImageElement(Element* aElement);
-  bool HasIdElementExposedAsHTMLDocumentProperty();
 
   bool HasContentChangeCallback() { return mChangeCallbacks != nullptr; }
   void AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
                                 void* aData, bool aForImage);
   void RemoveContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
                                 void* aData, bool aForImage);
 
   void Traverse(nsCycleCollectionTraversalCallback* aCallback);
@@ -220,14 +223,16 @@ private:
 
   void FireChangeCallbacks(Element* aOldElement, Element* aNewElement,
                            bool aImageOnly = false);
 
   AtomOrString mKey;
   // empty if there are no elements with this ID.
   // The elements are stored as weak pointers.
   AutoTArray<Element*, 1> mIdContentList;
-  RefPtr<nsBaseContentList> mNameContentList;
+  // The content lists for document and window named getters
+  RefPtr<nsBaseContentList> mDocumentContentList;
+  RefPtr<nsBaseContentList> mWindowContentList;
   nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks;
   RefPtr<Element> mImageElement;
 };
 
 #endif // #ifndef nsIdentifierMapEntry_h
--- a/dom/base/test/test_window_element_enumeration.html
+++ b/dom/base/test/test_window_element_enumeration.html
@@ -50,18 +50,18 @@ https://bugzilla.mozilla.org/show_bug.cg
        "<img> with name should not be in our enumeration list");
     isnot(names3.indexOf("two"), -1,
           "<img> with name should be in GSP own prop list");
 
     is(names1.indexOf("three"), -1,
        "<div> with id should not be in our own prop list");
     is(names2.indexOf("three"), -1,
        "<div> with id should not be in our enumeration list");
-    todo_isnot(names3.indexOf("three"), -1,
-               "<div> with id should be in GSP own prop list");
+    isnot(names3.indexOf("three"), -1,
+          "<div> with id should be in GSP own prop list");
 
     is(names1.indexOf("four"), -1,
        "<div> with name should not be in our own prop list");
     is(names2.indexOf("four"), -1,
        "<div> with name should not be in our enumeration list");
     is(names3.indexOf("four"), -1,
        "<div> with name should not be in GSP own prop list");
 </script>
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -482,19 +482,35 @@ nsGenericHTMLElement::BindToTree(nsIDocu
 {
   nsresult rv = nsGenericHTMLElementBase::BindToTree(aDocument, aParent,
                                                      aBindingParent,
                                                      aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDocument) {
     RegAccessKey();
-    if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
-      aDocument->
-        AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
+    nsIAtom* id = nullptr;
+    if (HasID()) {
+      id = DoGetID();
+      MOZ_ASSERT(id && id != nsGkAtoms::_empty);
+      if (IsIdExposedOnDoc(this)) {
+        aDocument->AddToDocumentTable(this, id);
+      }
+    }
+    if (HasName()) {
+      nsIAtom* name = GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
+      MOZ_ASSERT(name && name != nsGkAtoms::_empty);
+      // Make sure not to double-add if id and name are the same
+      if (id != name && IsNameExposedOnWindow(this)) {
+        aDocument->AddToWindowTable(this, name);
+      }
+      if ((id != name || !IsIdExposedOnDoc(this)) &&
+          IsNameExposedOnDoc(this)) {
+        aDocument->AddToDocumentTable(this, name);
+      }
     }
 
     if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue) {
       nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(aDocument);
       if (htmlDocument) {
         htmlDocument->ChangeContentEditableCount(this, +1);
       }
     }
@@ -513,17 +529,37 @@ nsGenericHTMLElement::BindToTree(nsIDocu
 
 void
 nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   if (IsInUncomposedDoc()) {
     UnregAccessKey();
   }
 
-  RemoveFromNameTable();
+  if (HasID() || (HasName() && (IsNameExposedOnWindow(this) ||
+                                IsNameExposedOnDoc(this)))) {
+    nsIDocument* doc = GetUncomposedDoc();
+    if (doc) {
+      if (HasID() && IsIdExposedOnDoc(this)) {
+        doc->RemoveFromDocumentTable(this, DoGetID());
+      }
+      if (HasName()) {
+        // It's okay if we remove it twice if the id and name are the same
+        if (IsNameExposedOnWindow(this)) {
+          doc->RemoveFromWindowTable(this, GetParsedAttr(nsGkAtoms::name)->
+                                           GetAtomValue());
+        }
+        if (IsNameExposedOnDoc(this)) {
+          doc->RemoveFromDocumentTable(this, GetParsedAttr(nsGkAtoms::name)->
+                                             GetAtomValue());
+        }
+      }
+    }
+  }
+
 
   if (GetContentEditableValue() == eTrue) {
     //XXXsmaug Fix this for Shadow DOM, bug 1066965.
     nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetUncomposedDoc());
     if (htmlDocument) {
       htmlDocument->ChangeContentEditableCount(this, -1);
     }
   }
@@ -668,18 +704,33 @@ nsGenericHTMLElement::BeforeSetAttr(int3
   if (aNamespaceID == kNameSpaceID_None) {
     if (aName == nsGkAtoms::accesskey) {
       // Have to unregister before clearing flag. See UnregAccessKey
       UnregAccessKey();
       if (!aValue) {
         UnsetFlags(NODE_HAS_ACCESSKEY);
       }
     } else if (aName == nsGkAtoms::name) {
-      // Have to do this before clearing flag. See RemoveFromNameTable
-      RemoveFromNameTable();
+      bool removeFromDocumentTable = !(HasID() && IsIdExposedOnDoc(this)) &&
+                                     HasName() && IsNameExposedOnDoc(this);
+      bool removeFromWindowTable = !HasID() && HasName() &&
+                                   IsNameExposedOnWindow(this);
+      if (removeFromDocumentTable || removeFromWindowTable) {
+        nsIDocument* doc = GetUncomposedDoc();
+        if (doc) {
+          if (removeFromDocumentTable) {
+            doc->RemoveFromDocumentTable(this, GetParsedAttr(nsGkAtoms::name)->
+                                               GetAtomValue());
+          }
+          if (removeFromWindowTable) {
+            doc->RemoveFromWindowTable(this, GetParsedAttr(nsGkAtoms::name)->
+                                             GetAtomValue());
+          }
+        }
+      }
       if (!aValue || aValue->IsEmpty()) {
         ClearHasName();
       }
     } else if (aName == nsGkAtoms::contenteditable) {
       if (aValue) {
         // Set this before the attribute is set so that any subclass code that
         // runs before the attribute is set won't think we're missing a
         // contenteditable attr when we actually have one.
@@ -778,18 +829,32 @@ nsGenericHTMLElement::AfterSetAttr(int32
     } else if (aName == nsGkAtoms::name) {
       if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase)) {
         // This may not be quite right because we can have subclass code run
         // before here. But in practice subclasses don't care about this flag,
         // and in particular selector matching does not care.  Otherwise we'd
         // want to handle it like we handle id attributes (in PreIdMaybeChange
         // and PostIdMaybeChange).
         SetHasName();
-        if (CanHaveName(NodeInfo()->NameAtom())) {
-          AddToNameTable(aValue->GetAtomValue());
+        nsIDocument* doc = GetUncomposedDoc();
+        if (doc && !IsInAnonymousSubtree()) {
+          // Make sure not to double-add if id and name are the same
+          nsIAtom* id = nullptr;
+          if (HasID()) {
+            id = DoGetID();
+          }
+          nsIAtom* name = aValue->GetAtomValue();
+          MOZ_ASSERT(name);
+          if (IsNameExposedOnDoc(this) &&
+              (id != name || !IsIdExposedOnDoc(this))) {
+            doc->AddToDocumentTable(this, aValue->GetAtomValue());
+          }
+          if (IsNameExposedOnWindow(this) && id != name) {
+            doc->AddToWindowTable(this, aValue->GetAtomValue());
+          }
         }
       }
     }
   }
 
   return nsGenericHTMLElementBase::AfterSetAttr(aNamespaceID, aName,
                                                 aValue, aOldValue, aNotify);
 }
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -686,64 +686,50 @@ public:
 
   already_AddRefed<nsINodeList> Labels();
 
   virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
 
   static bool TouchEventsEnabled(JSContext* /* unused */, JSObject* /* unused */);
 
   static inline bool
-  CanHaveName(nsIAtom* aTag)
+  IsNameExposedOnDoc(Element* aElement)
   {
-    return aTag == nsGkAtoms::img ||
-           aTag == nsGkAtoms::form ||
-           aTag == nsGkAtoms::embed ||
-           aTag == nsGkAtoms::object;
+    // XXX Not all embeds/objects are exposed per spec
+    return aElement->IsAnyOfHTMLElements(nsGkAtoms::embed,
+                                         nsGkAtoms::form,
+                                         nsGkAtoms::iframe,
+                                         nsGkAtoms::img,
+                                         nsGkAtoms::object);
   }
   static inline bool
-  ShouldExposeNameAsHTMLDocumentProperty(Element* aElement)
+  IsNameExposedOnWindow(Element* aElement)
   {
-    return aElement->IsHTMLElement() &&
-           CanHaveName(aElement->NodeInfo()->NameAtom());
+    return aElement->IsAnyOfHTMLElements(nsGkAtoms::embed,
+                                         nsGkAtoms::form,
+                                         nsGkAtoms::frameset,
+                                         nsGkAtoms::img,
+                                         nsGkAtoms::object);
   }
   static inline bool
-  ShouldExposeIdAsHTMLDocumentProperty(Element* aElement)
+  IsIdExposedOnDoc(Element* aElement)
   {
+    // XXX Not all objects are exposed per spec
     if (aElement->IsHTMLElement(nsGkAtoms::object)) {
       return true;
     }
 
     // Per spec, <img> is exposed by id only if it also has a nonempty
     // name (which doesn't have to match the id or anything).
     // HasName() is true precisely when name is nonempty.
     return aElement->IsHTMLElement(nsGkAtoms::img) && aElement->HasName();
   }
 
 protected:
   /**
-   * Add/remove this element to the documents name cache
-   */
-  void AddToNameTable(nsIAtom* aName) {
-    NS_ASSERTION(HasName(), "Node doesn't have name?");
-    nsIDocument* doc = GetUncomposedDoc();
-    if (doc && !IsInAnonymousSubtree()) {
-      doc->AddToNameTable(this, aName);
-    }
-  }
-  void RemoveFromNameTable() {
-    if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
-      nsIDocument* doc = GetUncomposedDoc();
-      if (doc) {
-        doc->RemoveFromNameTable(this, GetParsedAttr(nsGkAtoms::name)->
-                                         GetAtomValue());
-      }
-    }
-  }
-
-  /**
    * Register or unregister an access key to this element based on the
    * accesskey attribute.
    */
   void RegAccessKey()
   {
     if (HasFlag(NODE_HAS_ACCESSKEY)) {
       RegUnRegAccessKey(true);
     }
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -2256,83 +2256,79 @@ nsHTMLDocument::GetPlugins(nsIDOMHTMLCol
 }
 
 nsIHTMLCollection*
 nsHTMLDocument::Plugins()
 {
   return Embeds();
 }
 
-nsISupports*
-nsHTMLDocument::ResolveName(const nsAString& aName, nsWrapperCache **aCache)
+void
+nsHTMLDocument::NamedGetter(JSContext* aCx, const nsAString& aName, bool& aFound,
+                            JS::MutableHandle<JSObject*> aRetVal,
+                            ErrorResult& aRv)
 {
-  nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aName);
+  aFound = false;
+  aRetVal.set(nullptr);
+
+  nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName);
   if (!entry) {
-    *aCache = nullptr;
-    return nullptr;
+    return;
   }
 
-  nsBaseContentList *list = entry->GetNameContentList();
-  uint32_t length = list ? list->Length() : 0;
-
-  if (length > 0) {
-    if (length == 1) {
-      // Only one element in the list, return the element instead of returning
-      // the list.
-      nsIContent *node = list->Item(0);
-      *aCache = node;
-      return node;
-    }
-
-    // The list contains more than one element, return the whole list.
-    *aCache = list;
-    return list;
-  }
-
-  // No named items were found, see if there's one registerd by id for aName.
-  Element *e = entry->GetIdElement();
-
-  if (e && nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(e)) {
-    *aCache = e;
-    return e;
-  }
-
-  *aCache = nullptr;
-  return nullptr;
-}
-
-void
-nsHTMLDocument::NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
-                            JS::MutableHandle<JSObject*> aRetval,
-                            ErrorResult& rv)
-{
-  nsWrapperCache* cache;
-  nsISupports* supp = ResolveName(aName, &cache);
-  if (!supp) {
-    aFound = false;
-    aRetval.set(nullptr);
+  nsBaseContentList* list = entry->GetDocumentContentList();
+
+  if (!list || list->Length() == 0) {
     return;
   }
 
-  JS::Rooted<JS::Value> val(cx);
-  if (!dom::WrapObject(cx, supp, cache, nullptr, &val)) {
-    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+  JS::Rooted<JS::Value> val(aCx);
+  if (list->Length() > 1) {
+    // Return the list itself
+    if (!dom::WrapObject(aCx, list, nullptr, &val)) {
+      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+
+    aFound = true;
+    aRetVal.set(&val.toObject());
+    return;
+  }
+
+  // Only one valid item, return the element instead of the list.
+  // For iframes with non-null windows, return that.
+  auto iframe = HTMLIFrameElement::FromContent(list->Item(0));
+  if (iframe) {
+    RefPtr<nsPIDOMWindowOuter> win = iframe->GetContentWindow();
+    if (win) {
+      if (!dom::WrapObject(aCx, win, nullptr, &val)) {
+        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return;
+      }
+      aFound = true;
+      aRetVal.set(&val.toObject());
+      return;
+    }
+  }
+  // For other elements, return the element itself
+  if (!dom::WrapObject(aCx, list->Item(0), nullptr, &val)) {
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
   aFound = true;
-  aRetval.set(&val.toObject());
+  aRetVal.set(&val.toObject());
 }
 
 void
 nsHTMLDocument::GetSupportedNames(nsTArray<nsString>& aNames)
 {
   for (auto iter = mIdentifierMap.Iter(); !iter.Done(); iter.Next()) {
     nsIdentifierMapEntry* entry = iter.Get();
-    if (entry->HasNameElement() ||
-        entry->HasIdElementExposedAsHTMLDocumentProperty()) {
+    nsBaseContentList* list = entry->GetDocumentContentList();
+    if (list && list->Length()) {
       aNames.AppendElement(entry->GetKeyAsString());
     }
   }
 }
 
 //----------------------------
 
 // forms related stuff
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -94,18 +94,16 @@ public:
   // nsIDOMNode interface
   NS_FORWARD_NSIDOMNODE_TO_NSINODE
 
   // nsIDOMHTMLDocument interface
   NS_DECL_NSIDOMHTMLDOCUMENT
 
   mozilla::dom::HTMLAllCollection* All();
 
-  nsISupports* ResolveName(const nsAString& aName, nsWrapperCache **aCache);
-
   virtual void AddedForm() override;
   virtual void RemovedForm() override;
   virtual int32_t GetNumFormsSynchronous() override;
   virtual void TearingDownEditor() override;
   virtual void SetIsXHTML(bool aXHTML) override
   {
     mType = (aXHTML ? eXHTML : eHTML);
   }
@@ -167,19 +165,19 @@ public:
   // WebIDL API
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     override;
   void SetDomain(const nsAString& aDomain, mozilla::ErrorResult& rv);
   bool IsRegistrableDomainSuffixOfOrEqualTo(const nsAString& aHostSuffixString,
                                             const nsACString& aOrigHost);
   void GetCookie(nsAString& aCookie, mozilla::ErrorResult& rv);
   void SetCookie(const nsAString& aCookie, mozilla::ErrorResult& rv);
-  void NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
-                   JS::MutableHandle<JSObject*> aRetval,
-                   mozilla::ErrorResult& rv);
+  void NamedGetter(JSContext* aCx, const nsAString& aName, bool& aFound,
+                   JS::MutableHandle<JSObject*> aRetVal,
+                   mozilla::ErrorResult& aRv);
   void GetSupportedNames(nsTArray<nsString>& aNames);
   nsGenericHTMLElement *GetBody();
   void SetBody(nsGenericHTMLElement* aBody, mozilla::ErrorResult& rv);
   mozilla::dom::HTMLSharedElement *GetHead() {
     return static_cast<mozilla::dom::HTMLSharedElement*>(GetHeadElement());
   }
   nsIHTMLCollection* Images();
   nsIHTMLCollection* Embeds();
--- a/layout/generic/crashtests/769303-1.html
+++ b/layout/generic/crashtests/769303-1.html
@@ -7,16 +7,17 @@ p:before {
     content: counter(e2);
     }
 p:not([type=image]) {
     float: left;
     -moz-appearance: radio;
 }
 </style>
 <p id=test1><script>
+let test1 = window.test1;
 function initCF() {
 document.removeEventListener("DOMContentLoaded", initCF);
 test2 = test1.cloneNode(false);
 test3 = test2.cloneNode(false);
 document.documentElement.appendChild(test3);
 setTimeout("CFcrash()", 21);
 }
 document.addEventListener("DOMContentLoaded", initCF);
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -341880,61 +341880,19 @@
     ]
    ],
    "html/dom/documents/dom-tree-accessors/document.title-09.html": [
     [
      "/html/dom/documents/dom-tree-accessors/document.title-09.html",
      {}
     ]
    ],
-   "html/dom/documents/dom-tree-accessors/nameditem-01.html": [
-    [
-     "/html/dom/documents/dom-tree-accessors/nameditem-01.html",
-     {}
-    ]
-   ],
-   "html/dom/documents/dom-tree-accessors/nameditem-02.html": [
-    [
-     "/html/dom/documents/dom-tree-accessors/nameditem-02.html",
-     {}
-    ]
-   ],
-   "html/dom/documents/dom-tree-accessors/nameditem-03.html": [
-    [
-     "/html/dom/documents/dom-tree-accessors/nameditem-03.html",
-     {}
-    ]
-   ],
-   "html/dom/documents/dom-tree-accessors/nameditem-04.html": [
-    [
-     "/html/dom/documents/dom-tree-accessors/nameditem-04.html",
-     {}
-    ]
-   ],
-   "html/dom/documents/dom-tree-accessors/nameditem-05.html": [
-    [
-     "/html/dom/documents/dom-tree-accessors/nameditem-05.html",
-     {}
-    ]
-   ],
-   "html/dom/documents/dom-tree-accessors/nameditem-06.html": [
-    [
-     "/html/dom/documents/dom-tree-accessors/nameditem-06.html",
-     {}
-    ]
-   ],
-   "html/dom/documents/dom-tree-accessors/nameditem-07.html": [
-    [
-     "/html/dom/documents/dom-tree-accessors/nameditem-07.html",
-     {}
-    ]
-   ],
-   "html/dom/documents/dom-tree-accessors/nameditem-08.html": [
-    [
-     "/html/dom/documents/dom-tree-accessors/nameditem-08.html",
+   "html/dom/documents/dom-tree-accessors/named-item.html": [
+    [
+     "/html/dom/documents/dom-tree-accessors/named-item.html",
      {}
     ]
    ],
    "html/dom/documents/resource-metadata-management/document-compatmode-01.html": [
     [
      "/html/dom/documents/resource-metadata-management/document-compatmode-01.html",
      {}
     ]
@@ -482892,17 +482850,17 @@
    "fb4b7b86ff4b56d001b6ffe72f6d9c8a91046d2c",
    "support"
   ],
   "css/CSS2/tables/reference/table-anonymous-border-spacing-ref.xht": [
    "157b21a9f2954094ec5e682c3d131f320b6473a2",
    "support"
   ],
   "css/CSS2/tables/reference/table-anonymous-text-indent-ref.xht": [
-   "d12237ecbbd205dd38d956aacc6c1a50ee5631bb",
+   "40d1dfe3486108892a7d6ff61bc99196730bcb55",
    "support"
   ],
   "css/CSS2/tables/reference/table-margin-004-ref.xht": [
    "cb0ab88ee6938c670cdb7140781d13173f759b41",
    "support"
   ],
   "css/CSS2/tables/row-visibility-001.xht": [
    "5f8962c6026581ddd659cb96617f79330ccae7ed",
@@ -484084,17 +484042,17 @@
    "3b5ced13b1370794b069c5431252dc370257a242",
    "visual"
   ],
   "css/CSS2/tables/table-anonymous-objects-210.xht": [
    "516b5f1e6cec15bc53aef58f1eb41973c46e7190",
    "visual"
   ],
   "css/CSS2/tables/table-anonymous-text-indent.xht": [
-   "e6aea4bbc5ce33d6ec4ae570501fb5493dbc49f0",
+   "57bfe388308a128e32abc674b64acbf9903d6141",
    "reftest"
   ],
   "css/CSS2/tables/table-anonymous-whitespace-001.xht": [
    "50b8874d4b4b1fbcde591344235be17ce7e4138f",
    "visual"
   ],
   "css/CSS2/tables/table-background-edge-and-border-model-001.xht": [
    "58a9cbd54e49d76742a85730d929cb3d43f0eea9",
@@ -572512,21 +572470,21 @@
    "4d8c4ddb997d85ca2c971602a3096f57565c01eb",
    "testharness"
   ],
   "cssom-view/elementFromPoint-001.html": [
    "bf1c490777f450275a95ecfc6d6d2c0d055aca82",
    "testharness"
   ],
   "cssom-view/elementFromPoint-002.html": [
-   "36b8a5f50e6489f9e25c3d09dc523007d442e2b3",
+   "36d7e75021f7f6ab8f89b2654ba3c8f818af16b8",
    "testharness"
   ],
   "cssom-view/elementFromPoint-003.html": [
-   "36b8a5f50e6489f9e25c3d09dc523007d442e2b3",
+   "a689737bd60877e181a8a7ff27774ab383c3de0b",
    "testharness"
   ],
   "cssom-view/elementFromPoint.html": [
    "0f78405640523cf451b19ea0348b8216139b8168",
    "testharness"
   ],
   "cssom-view/elementFromPosition.html": [
    "d90dff8b15ec2977f341a7add9c7d627b62d9d0f",
@@ -578620,17 +578578,17 @@
    "af2c7b4ca07ae6c74d261bc745e174df8ab3ffef",
    "support"
   ],
   "fetch/api/resources/trickle.py": [
    "adb1bc80366cf924cfe13f6c73555d999d1d8e4f",
    "support"
   ],
   "fetch/api/resources/utils.js": [
-   "1027bd5e728ae008520ce151722760dbb53a2368",
+   "d9e65bbd408a906a4d14aa40d4ed20ea2ed9f585",
    "support"
   ],
   "fetch/api/response/multi-globals/current/current.html": [
    "2d9ab238e07a88d19365f78bfd5db84d32854de1",
    "support"
   ],
   "fetch/api/response/multi-globals/incumbent/incumbent.html": [
    "64b0018883b2ff64e0ea9183b7d513834c4174a4",
@@ -578652,17 +578610,17 @@
    "4fde2d5591959ef413f5f5695a8eebd6cb193fff",
    "testharness"
   ],
   "fetch/api/response/response-consume-empty.html": [
    "a9606b70a21e24dde0da19656773fc0c60fa255f",
    "testharness"
   ],
   "fetch/api/response/response-consume-stream.html": [
-   "dad05becbd8f3944aa3709ae1a3e578c05d4d935",
+   "7e990d212ef225ac8475803e01b1772086dbc08d",
    "testharness"
   ],
   "fetch/api/response/response-consume.html": [
    "936272bddf1a090267343fc7f8ab3669da98d27d",
    "testharness"
   ],
   "fetch/api/response/response-error.html": [
    "06489e75d56cbbdbfee903bea7e39c549310ba3e",
@@ -582355,46 +582313,18 @@
   "html/dom/documents/dom-tree-accessors/document.title-08.html": [
    "0cdc5e50bef41a63bd2dab80a1d23d3fcf8ccf65",
    "testharness"
   ],
   "html/dom/documents/dom-tree-accessors/document.title-09.html": [
    "d4b33d675b3581db212685d772cffdffcf132c45",
    "testharness"
   ],
-  "html/dom/documents/dom-tree-accessors/nameditem-01.html": [
-   "2e9bd7a8a43e834e76ae1695b9376d260cfea9d5",
-   "testharness"
-  ],
-  "html/dom/documents/dom-tree-accessors/nameditem-02.html": [
-   "f5112d9d4111e61a4b87d6806f22e75ea85cb8af",
-   "testharness"
-  ],
-  "html/dom/documents/dom-tree-accessors/nameditem-03.html": [
-   "6692f5b790960145e7a94b25227682237cbb4862",
-   "testharness"
-  ],
-  "html/dom/documents/dom-tree-accessors/nameditem-04.html": [
-   "efb40d8947bbc916fa28fb0c4c5e5bfa9574b9b7",
-   "testharness"
-  ],
-  "html/dom/documents/dom-tree-accessors/nameditem-05.html": [
-   "9a833f3b915892fed4d94ae294f95935f5da66d8",
-   "testharness"
-  ],
-  "html/dom/documents/dom-tree-accessors/nameditem-06.html": [
-   "355bab1b5b13483ef5f3bb890ea6ccd09700c049",
-   "testharness"
-  ],
-  "html/dom/documents/dom-tree-accessors/nameditem-07.html": [
-   "f9eddafeddf7f4e7ca99b080f98e3e379b5434d0",
-   "testharness"
-  ],
-  "html/dom/documents/dom-tree-accessors/nameditem-08.html": [
-   "5ae6d9718f6568cf9c42a7ebdf2cbae767c68852",
+  "html/dom/documents/dom-tree-accessors/named-item.html": [
+   "62dc7dce457d326aabcf3e12a7b31841ad45302e",
    "testharness"
   ],
   "html/dom/documents/loading-xml-documents/.gitkeep": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "html/dom/documents/resource-metadata-management/.gitkeep": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
@@ -596280,17 +596210,17 @@
    "fda57b55aabdc54d674851851451c6c69c514ed1",
    "support"
   ],
   "interfaces/geometry.idl": [
    "b4c2837ade7a509306755eb7b682f6648c3684e3",
    "support"
   ],
   "interfaces/html.idl": [
-   "137e061bbab6b23150d6ae9128df56a96cd1d68f",
+   "7c77578dededdc99f2be1737373b9ebe59357938",
    "support"
   ],
   "interfaces/mediacapture-main.idl": [
    "3d59aebd6219a0312ade8c543bb389030d670d82",
    "support"
   ],
   "interfaces/remoteplayback.idl": [
    "f7c100f4275f2e32fbca3bb8d9c4900df879ffe4",
@@ -596412,17 +596342,17 @@
    "c1d1054447e116becb50aaf96aad00a25f0a6752",
    "testharness"
   ],
   "intersection-observer/shadow-content.html": [
    "11681640d5c8b2c62229ed5a717172f917d75ba4",
    "testharness"
   ],
   "intersection-observer/timestamp.html": [
-   "b9bf8d472d7751ec4a1ebee925d12668bedeee7a",
+   "0d57a6abc3c3056f921401f629ab231c458cf4cb",
    "testharness"
   ],
   "intersection-observer/unclipped-root.html": [
    "67dab96304c745f1b5462bb1074753b09d77fbd1",
    "testharness"
   ],
   "intersection-observer/zero-area-element-hidden.html": [
    "59d854e89ca0d7b035a87376566775ca2f3420e5",
@@ -616640,17 +616570,17 @@
    "55d32a5d2a362a2d7bd0ddc580fa24180f3d3579",
    "testharness"
   ],
   "selection/isCollapsed.html": [
    "d1984a9359d880dfb81197e7ec31b2456833809d",
    "testharness"
   ],
   "selection/removeAllRanges.html": [
-   "bd203d8878c4de59de476fe6fa7417bd2678dfcc",
+   "c1ed8afc2f1ee80f5131d1ead6930c9895a2a6f3",
    "testharness"
   ],
   "selection/removeRange.html": [
    "4a1ca00b32bca658f31bbff3f01a0d9154fb9779",
    "testharness"
   ],
   "selection/selectAllChildren.html": [
    "1951e6d34c7959c038146efde2d49a7898eaee29",
@@ -625076,37 +625006,37 @@
    "5ba51b660c7203bba3ada597c2f56fe094358e1f",
    "wdspec"
   ],
   "webdriver/tests/get_window_rect.py": [
    "2d4c13edc4e659af864750d0341c06ff969a687f",
    "wdspec"
   ],
   "webdriver/tests/maximize_window.py": [
-   "827b3a3e1a3ef628dae1480af029fc01ef5e9388",
+   "55cb76c60c7914bc024f470f6d1f0a47294b6fe3",
    "wdspec"
   ],
   "webdriver/tests/navigation.py": [
    "cec2987258d9c807a247da9e0216b3af1f171484",
    "wdspec"
   ],
   "webdriver/tests/set_window_rect.py": [
-   "8aef7d228fc9c1e8cf1029d8f1f7669602b6e877",
+   "d5ee763d1531847fcbb7700aa75f94a9f7415d41",
    "wdspec"
   ],
   "webdriver/tests/support/__init__.py": [
    "5a31a3917a5157516c10951a3b3d5ffb43b992d9",
    "support"
   ],
   "webdriver/tests/support/asserts.py": [
-   "e624c50e02a0da4d532321a080ecc5c73644bd8d",
+   "c6d7303efd64a6a632a1cd1eef10acf1d7de04cd",
    "support"
   ],
   "webdriver/tests/support/fixtures.py": [
-   "6ceec11f42cd9be53a92ad88aa07657c78779ce3",
+   "055884949ae77f5f1ac49d205c590344606dedfb",
    "support"
   ],
   "webdriver/tests/support/http_request.py": [
    "cb40c781fea2280b98135522def5e6a116d7b946",
    "support"
   ],
   "webdriver/tests/support/inline.py": [
    "bc85126e5637145e81f27d037f3a9090747130c8",
--- a/testing/web-platform/meta/html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html.ini
+++ b/testing/web-platform/meta/html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html.ini
@@ -1,14 +1,11 @@
 [named-objects.html]
   type: testharness
   [Check if window['a'\] contains all a, applet, area, embed, form, img, and object elements, and their order]
     expected: FAIL
 
   [Check if window['fs'\] return the frameset element with name='fs']
     expected: FAIL
 
-  [Check if window['b'\] returns the elements with the id='b']
-    expected: FAIL
-
   [Check if window['a'\] contains all applet, embed, form, img, and object elements, and their order]
     expected: FAIL
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/named-item.html.ini
@@ -0,0 +1,47 @@
+[named-item.html]
+  type: testharness
+  [If there is one applet, it must be returned (name) <applet name="foo">]
+    expected: FAIL
+
+  [If there is one applet with id, it must be returned <applet id="foo">]
+    expected: FAIL
+
+  [If there are two applets, a collection must be returned (name) <applet name="foo"></applet><applet name="foo">]
+    expected: FAIL
+
+  [If there are two applets, a collection must be returned (id) <applet id="foo"></applet><applet id="foo">]
+    expected: FAIL
+
+  [If there are two applets, a collection must be returned (name and id) <applet name="foo"></applet><applet id="foo">]
+    expected: FAIL
+
+  [If there are two applets, a collection must be returned (id and name) <applet id="foo"></applet><applet name="foo">]
+    expected: FAIL
+
+  [If there is an applet and another element (applet first), a collection must be returned (name) <applet name="foo"></applet><img name="foo">]
+    expected: FAIL
+
+  [If there is an applet and another element (applet last), a collection must be returned <img name="foo"><applet name="foo"></applet>]
+    expected: FAIL
+
+  [applet with different name and id <applet name="foo" id="bar">]
+    expected: FAIL
+
+  [applet with whitespace as name must be exposed <applet name=" ">]
+    expected: FAIL
+
+  [applet with whitespace as id must be exposed <applet name="foo" id=" ">]
+    expected: FAIL
+
+  [An applet with numeric name <applet name="0"></applet><applet name="42"></applet><applet name="-1"></applet><applet name="42949...]
+    expected: FAIL
+
+  [applet must be exposed in detached document (name) ]
+    expected: FAIL
+
+  [If there are a div and applet with the same id, the applet must be returned on document and both on window <div id=foo></div><applet id=foo>]
+    expected: FAIL
+
+  [applet must be exposed properly in nested browsing context <applet name="foo" id="bar"></applet>]
+    expected: FAIL
+
deleted file mode 100644
--- a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/nameditem-02.html.ini
+++ /dev/null
@@ -1,20 +0,0 @@
-[nameditem-02.html]
-  type: testharness
-  [If the only named item is an iframe, the contentWindow should be returned.]
-    expected: FAIL
-
-  [If there are two iframes, a collection should be returned.]
-    expected: FAIL
-
-  [If there are an iframe and another element (iframe first), a collection should be returned.]
-    expected: FAIL
-
-  [If there are an iframe and another element (iframe last), a collection should be returned.]
-    expected: FAIL
-
-  [If an iframe has a name and a different id, it should be returned by its name.]
-    expected: FAIL
-
-  [An iframe whose name looks like an array index should work.]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/nameditem-03.html.ini
+++ /dev/null
@@ -1,26 +0,0 @@
-[nameditem-03.html]
-  type: testharness
-  [If there are two applets, a collection should be returned. (name)]
-    expected: FAIL
-
-  [If there are two applets, a collection should be returned. (id)]
-    expected: FAIL
-
-  [If there are two applets, a collection should be returned. (name and id)]
-    expected: FAIL
-
-  [If there are two applets, a collection should be returned. (id and name)]
-    expected: FAIL
-
-  [If there is one applet, it should be returned (name)]
-    expected: FAIL
-
-  [An id shouldn't affect getting an applet by name]
-    expected: FAIL
-
-  [If there is one applet, it should be returned (id)]
-    expected: FAIL
-
-  [A name shouldn't affect getting an applet by id]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/nameditem-07.html.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[nameditem-07.html]
-  type: testharness
-  [If there are two objects, a collection should be returned. (id)]
-    expected: FAIL
-
-  [If there are two objects, a collection should be returned. (name and id)]
-    expected: FAIL
-
-  [If there are two objects, a collection should be returned. (id and name)]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/html/dom/documents/dom-tree-accessors/nameditem-08.html.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[nameditem-08.html]
-  type: testharness
-  [If there is a div and applet with same id, the applet should be returned]
-    expected: FAIL
-
-  [If there is a div and object with same id, the object should be returned]
-    expected: FAIL
-
-  [If there is a div and img with same id, the img should be returned]
-    expected: FAIL
-
--- a/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html
+++ b/testing/web-platform/tests/html/browsers/the-window-object/named-access-on-the-window-object/named-objects.html
@@ -44,22 +44,22 @@ test(function() {
   document.getElementById('form1').setAttribute("name", "");
   document.getElementById('embed1').setAttribute("name", "");
   assert_array_equals(window['a'],
                       [ document.getElementById('app1'), document.getElementById('img1'),
                         document.getElementById('obj1') ],
                       "Window['a'] should not contain the elements with empty name attribute.");
 }, "Check if window['a'] contains all applet, embed, form, img, and object elements, and their order");
 
-var t = async_test("Check that window['fs'] does not return the frameset element with name='fs' (historical)");
+var t = async_test("Check that window['fs'] returns the frameset element with name='fs'");
 function on_load () {
   t.step(function () {
     assert_equals(document.getElementById('fm2').contentWindow['fs'],
-                  undefined,
-                  "The frameset element should not be returned.");
+                  document.getElementById('fm2').contentDocument.querySelector("[name=fs]"),
+                  "The frameset element should be returned.");
   });
   t.done();
 }
 
 test(function() {
   assert_true(window['b'] instanceof HTMLCollection);
   assert_array_equals(window['b'], [document.getElementsByTagName('b')[0], document.getElementsByTagName('input')[0]]);
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/named-item.html
@@ -0,0 +1,643 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Named getter on document</title>
+<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-window-nameditem">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<div id="testDiv"></div>
+<script>
+// TODO:
+// * Restrictions on exposed object/embed
+// * Testing frames/iframes with changed window names
+// * Mutations
+function aOrAn(type) {
+  switch (type) {
+    case "applet":
+    case "embed":
+    case "iframe":
+    case "img":
+    case "object":
+      return `an ${type}`;
+
+    case "form":
+    case "frame":
+    case "frameset":
+      return `a ${type}`;
+
+    default:
+      throw `Unknown type ${type}`;
+  }
+}
+
+function AOrAn(type) {
+  switch (type) {
+    case "applet":
+    case "embed":
+    case "iframe":
+    case "img":
+    case "object":
+      return `An ${type}`;
+
+    case "form":
+    case "frame":
+    case "frameset":
+      return `A ${type}`;
+
+    default:
+      throw `Unknown type ${type}`;
+  }
+}
+
+function plural(type) {
+  switch (type) {
+    case "img":
+      return `${type}'s`;
+
+    case "applet":
+    case "embed":
+    case "form":
+    case "frame":
+    case "frameset":
+    case "iframe":
+    case "object":
+      return `${type}s`;
+
+    default:
+      throw `Unknown type ${type}`;
+  }
+}
+
+function close(type) {
+  switch (type) {
+    case "applet":
+    case "form":
+    case "frameset":
+    case "iframe":
+    case "object":
+      return `</${type}>`;
+
+    case "embed":
+    case "frame":
+    case "img":
+      return "";
+
+    default:
+      throw `Unknown type ${type}`;
+  }
+}
+
+function th(idx) {
+  switch (Math.abs(idx) % 10) {
+    case 1: return `${idx}st`;
+    case 2: return `${idx}nd`;
+    default: return `${idx}th`;
+  }
+}
+
+function truncate(str) {
+  if (str.length < 100) {
+    return str;
+  }
+  return str.substr(0, 99) + "...";
+}
+
+function isArrayIndexPropertyName(str) {
+  // No leading zeros!
+  return (str === "0" || /[1-9][0-9]*/.test(str)) &&
+         0 <= Number(str) && Number(str) < 4294967295;
+}
+
+const testDiv = document.getElementById("testDiv");
+
+const supportedTypes = ["applet", "embed", "form", "frame", "frameset",
+                        "iframe", "img", "object"];
+
+const unsupportedOnDocument = ["frame", "frameset"];
+
+const unsupportedTypes = [
+  "a",
+  "abbr",
+  "address",
+  "area",
+  "article",
+  "aside",
+  "audio",
+  "b",
+  "base",
+  "bdi",
+  "bdo",
+  "blockquote",
+  "body",
+  "br",
+  "button",
+  "canvas",
+  "caption",
+  "cite",
+  "code",
+  "col",
+  "colgroup",
+  "data",
+  "datalist",
+  "dd",
+  "del",
+  "details",
+  "dfn",
+  "dialog",
+  "div",
+  "dl",
+  "dt",
+  "em",
+  "fieldset",
+  "figcaption",
+  "figure",
+  "footer",
+  "h1",
+  "h2",
+  "h3",
+  "h4",
+  "h5",
+  "h6",
+  "head",
+  "header",
+  "hgroup",
+  "hr",
+  "html",
+  "i",
+  "input",
+  "ins",
+  "kbd",
+  "label",
+  "legend",
+  "li",
+  "link",
+  "main",
+  "map",
+  "mark",
+  "math",
+  "menu",
+  "meta",
+  "meter",
+  "nav",
+  "noscript",
+  "ol",
+  "optgroup",
+  "option",
+  "output",
+  "p",
+  "param",
+  "picture",
+  "pre",
+  "progress",
+  "q",
+  "rp",
+  "rt",
+  "ruby",
+  "s",
+  "samp",
+  "script",
+  "section",
+  "select",
+  "slot",
+  "small",
+  "source",
+  "span",
+  "strong",
+  "style",
+  "sub",
+  "summary",
+  "sup",
+  "svg",
+  "table",
+  "tbody",
+  "td",
+  "template",
+  "textarea",
+  "tfoot",
+  "th",
+  "thead",
+  "time",
+  "title",
+  "tr",
+  "track",
+  "u",
+  "ul",
+  "var",
+  "video",
+  "wbr",
+  "unsupportedelement",
+  "acronym",
+  "bgsound",
+  "dir",
+  "noframes",
+  "isindex",
+  "keygen",
+  "listing",
+  "menuitem",
+  "nextid",
+  "noembed",
+  "plaintext",
+  "rb",
+  "rtc",
+  "strike",
+  "xmp",
+  "basefont",
+  "big",
+  "blink",
+  "center",
+  "font",
+  "marquee",
+  "multicol",
+  "nobr",
+  "spacer",
+  "tt",
+];
+
+function runDefaultTest(win = window, root = testDiv) {
+  const doc = win.document;
+
+  let windowExpected = new Map();
+  let documentExpected = new Map();
+  let windowUnexpected = new Set();
+  let documentUnexpected = new Set();
+
+  const addToWindow = (key, val) => {
+    if (!windowExpected.has(key)) {
+      windowExpected.set(key, [val]);
+    } else if (Array.isArray(windowExpected.get(key))) {
+      windowExpected.get(key).push(val);
+    } // else it's present but not an array, so it's a WindowProxy, so we
+      // don't expect val to be on the window.
+  };
+
+  const addToDocument = (key, val) => {
+    if (!documentExpected.has(key)) {
+      documentExpected.set(key, [val]);
+    } else {
+      documentExpected.get(key).push(val);
+    }
+  };
+
+  for (const elem of root.querySelectorAll("*")) {
+    // We don't use the IDL attributes in case the browser doesn't support the
+    // element at all (like applet at the time of writing).
+    if (elem.hasAttribute("id")) {
+      const id = elem.getAttribute("id");
+      if (id === "") {
+        windowUnexpected.add(id);
+        documentUnexpected.add(id);
+      } else {
+        if (isArrayIndexPropertyName(id)) {
+          // WindowProxy has special behavior for these
+          // https://html.spec.whatwg.org/#windowproxy-getownproperty
+          windowUnexpected.add(id);
+        } else {
+          addToWindow(id, elem);
+        }
+        if (elem.localName == "applet" || elem.localName == "object" ||
+            (elem.localName == "img" && elem.name !== "")) {
+          addToDocument(id, elem);
+        } else {
+          documentUnexpected.add(id);
+        }
+      }
+    }
+
+    if (elem.hasAttribute("name")) {
+      const name = elem.getAttribute("name");
+      if (name === "") {
+        windowUnexpected.add(name);
+        documentUnexpected.add(name);
+      } else {
+        // XXX embed/object not tested correctly
+        if (supportedTypes.includes(elem.localName) &&
+            !unsupportedOnDocument.includes(elem.localName)) {
+          addToDocument(name, elem);
+        } else {
+          documentUnexpected.add(name);
+        }
+
+        if (isArrayIndexPropertyName(name)) {
+          // WindowProxy has special behavior for these
+          // https://html.spec.whatwg.org/#windowproxy-getownproperty
+          windowUnexpected.add(name);
+        } else if (["frame", "iframe"].includes(elem.localName)
+            && elem.contentWindow) {
+          // Replace everything else, unless we already have an iframe
+          if (!windowExpected.has(name) ||
+              Array.isArray(windowExpected.get(name))) {
+            windowExpected.set(name, elem.contentWindow);
+          }
+        } else if (supportedTypes.includes(elem.localName)) {
+          addToWindow(name, elem);
+        } else {
+          windowUnexpected.add(name);
+        }
+      }
+    }
+  }
+
+  for (const [key, val] of documentExpected) {
+    documentUnexpected.delete(key);
+    if (val.length > 1) {
+      assert_array_equals(doc[key], val, `document["${key}"]`);
+      assert_class_string(doc[key], "HTMLCollection",
+                          `document[${key}] must be HTMLCollection`);
+    } else if (val[0].localName == "iframe" && val[0].contentWindow) {
+      assert_equals(doc[key], val[0].contentWindow, `document["${key}"]`);
+    } else {
+      assert_equals(doc[key], val[0], `document["${key}"]`);
+    }
+    assert_true(key in doc, `"${key}" in document`);
+  }
+
+  for (const [key, val] of windowExpected) {
+    windowUnexpected.delete(key);
+    if (!Array.isArray(val)) {
+      // WindowProxy case
+      assert_equals(win[key], val, `window["${key}"]`);
+    } else if (val.length > 1) {
+      assert_array_equals(win[key], val, `window["${key}"]`);
+      assert_class_string(win[key], "HTMLCollection",
+                          `window[${key}] must be HTMLCollection`);
+    } else {
+      assert_equals(win[key], val[0], `window["${key}"]`);
+    }
+    assert_true(key in win, `"${key}" in window`);
+  }
+
+  for (const key of documentUnexpected) {
+    assert_equals(doc[key], undefined, `document["${key}"]`);
+    assert_false(key in doc, `"${key}" in document`);
+  }
+
+  for (const key of windowUnexpected) {
+    if (isArrayIndexPropertyName(key)) {
+      // These have special behavior that is out of scope of these tests
+      continue;
+    }
+    assert_equals(win[key], undefined, `window["${key}"]`);
+    assert_false(key in win, `"${key}" in window`);
+  }
+}
+
+let tests = [
+  {html: '<img id="a" name="b">',
+   fn: () => {
+     const img = document.querySelector("img");
+     assert_equals(document.a, img);
+     assert_equals(document['a'], img);
+     assert_equals(document.b, img);
+     assert_equals(document['b'], img);
+   },
+   desc: "img elements that have a name and id attribute must be accessible " +
+         "by both values"
+  },
+  {html: `<img id="foo" name="bar"><img id="foo">` +
+         `<img id="baz" name=" "><img id="baz">`,
+   desc: "An img must be returned by id if it has a non-empty name (named first)",
+  },
+  {html: `<img id="foo"><img id="foo" name="bar">` +
+         `<img id="baz"><img id="baz" name=" ">`,
+   desc: "An img must be returned by id if it has a non-empty name (named last)",
+  },
+  {html: '<img id="foo" name="">',
+   desc: "An img with empty name must not be returned by id",
+  },
+  {html: '<form name="foo"></form><form name="foo"></form><form name="foo"></form>',
+   fn: () => {
+     const collection = document.foo;
+     assert_equals(collection, document.foo,
+                   "Must return same HTMLCollection object originally");
+     assert_equals(collection.length, 3, "Original .length");
+     testDiv.removeChild(testDiv.lastChild);
+     assert_equals(collection, document.foo,
+                   "Must return same HTMLCollection object after one remove");
+     assert_equals(collection.length, 2, ".length after one remove");
+     testDiv.removeChild(testDiv.lastChild);
+     assert_equals(collection.length, 1, ".length after two removes");
+     testDiv.removeChild(testDiv.lastChild);
+     assert_equals(collection.length, 0, ".length after three removes");
+   },
+   desc: "Document collection is live and the same object every time",
+  },
+  {html: '<form name="foo"></form><form name="foo"></form><form name="foo"></form>',
+   fn: () => {
+     const collection = window.foo;
+     assert_equals(collection, window.foo,
+                   "Must return same HTMLCollection object originally");
+     assert_equals(collection.length, 3, "Original .length");
+     testDiv.removeChild(testDiv.lastChild);
+     assert_equals(collection, window.foo,
+                   "Must return same HTMLCollection object after one remove");
+     assert_equals(collection.length, 2, ".length after one remove");
+     testDiv.removeChild(testDiv.lastChild);
+     assert_equals(collection.length, 1, ".length after two removes");
+     testDiv.removeChild(testDiv.lastChild);
+     assert_equals(collection.length, 0, ".length after three removes");
+   },
+   desc: "Window collection is live and the same object every time",
+  },
+  // A corollary of the collection being live and the same object every time is
+  // that the window and document collections cannot be equal.  If they were,
+  // they would have to remain equal if something is added that's in the
+  // document collection but not the window collection or vice versa, which
+  // isn't possible.
+  {html: '<form name="foo"></form><form name="foo"></form>',
+   fn: () => {
+     assert_not_equals(document.foo, window.foo, "Must not be equal");
+     assert_class_string(document.foo, "HTMLCollection",
+                         "document.foo must be HTMLCollection");
+     assert_class_string(window.foo, "HTMLCollection",
+                         "window.foo must be HTMLCollection");
+   },
+   desc: "Returned collections for window and document cannot be equal",
+  },
+  {html: "",
+   fn: () => {
+     for (const type of unsupportedTypes) {
+       const elem = document.createElement(type);
+       elem.setAttribute("id", "foo");
+       testDiv.appendChild(elem);
+     }
+     runDefaultTest();
+   },
+   desc: `Assorted elements must be returned by id on window but not ` +
+         `document (one id)`,
+  },
+  {html: "",
+   fn: () => {
+     for (const type of unsupportedTypes) {
+       const elem = document.createElement(type);
+       elem.setAttribute("id", `id-${type}`);
+       testDiv.appendChild(elem);
+     }
+     runDefaultTest();
+   },
+   desc: `Assorted elements must be returned by id on window but not ` +
+         `document (many id's)`,
+  },
+  {html: "",
+   fn: () => {
+     for (const type of unsupportedTypes) {
+       const elem = document.createElement(type);
+       elem.setAttribute("name", `name-${type}`);
+       testDiv.appendChild(elem);
+     }
+     runDefaultTest();
+   },
+   desc: `Assorted elements must not be returned by name`,
+  },
+];
+
+const variableTests = [
+  {html: type => `<${type} name="foo">`,
+   desc: type => type == "iframe"
+     ? "If the only named item is an iframe, the contentWindow must be returned"
+     : unsupportedOnDocument.includes(type)
+     ? `${AOrAn(type)} is returned by name on window but not document`
+     : `If there is one ${type}, it must be returned (name)`
+  },
+  {html: type => `<${type} id="foo">`,
+   desc: type => type == "applet" || type == "object"
+     ? `If there is one ${type} with id, it must be returned`
+     : `If there is one ${type} with id, it must be returned on window but not document`
+  },
+  {html: type => `<${type} name="foo">${close(type)}<${type} name="foo">`,
+   desc: type => unsupportedOnDocument.includes(type)
+     ? `If there are two ${plural(type)}, a collection must be returned ` +
+       `from window but not document (name)`
+     : `If there are two ${plural(type)}, a collection must be returned ` +
+       `(name)`,
+  },
+  {html: type => `<${type} id="foo">${close(type)}<${type} id="foo">`,
+   desc: type => type == "applet" || type == "object"
+     ? `If there are two ${plural(type)}, a collection must be returned (id)`
+     : `If there are two ${plural(type)}, a collection must be returned ` +
+       `from window but not document (id)`,
+  },
+  {html: type => `<${type} name="foo">${close(type)}<${type} id="foo">`,
+   desc: type => type == "applet" || type == "object"
+     ? `If there are two ${plural(type)}, a collection must be returned ` +
+       `(name and id)`
+     : `${type} is returned only by name and not id (name and id)`
+  },
+  {html: type => `<${type} id="foo">${close(type)}<${type} name="foo">`,
+   desc: type => type == "applet" || type == "object"
+     ? `If there are two ${plural(type)}, a collection must be returned (id ` +
+       `and name)`
+     : `${type} is returned only by name and not id (id and name)`
+  },
+  {html: type => type == "img"
+    ? '<img name="foo"><form name="foo">'
+    : `<${type} name="foo">${close(type)}<img name="foo">`,
+   desc: type => `If there is ${aOrAn(type)} and another element (${type} ` +
+                 `first), a collection must be returned (name)`,
+  },
+  {html: type => type == "img"
+    ? '<form name="foo"><img name="foo">'
+    : `<img name="foo"><${type} name="foo">${close(type)}`,
+   desc: type => `If there is ${aOrAn(type)} and another element (${type} ` +
+                 `last), a collection must be returned`,
+  },
+  {html: type => `<${type} name="foo" id="bar">`,
+   desc: type => `${type} with different name and id`
+  },
+  {html: type => `<${type} name="" id="">`,
+   desc: type => `${type} with empty name/id must not be exposed`,
+  },
+  {html: type => `<${type} name=" ">`,
+   desc: type => `${type} with whitespace as name must be exposed`,
+  },
+  {html: type => `<${type} name="foo" id=" ">`,
+   desc: type => type == "applet" || type == "img" || type == "object"
+     ? `${type} with whitespace as id must be exposed`
+     : `${type} with whitespace as id must be exposed on window but not ` +
+       `document`,
+  },
+  {html: type => `<${type} name="0">${close(type)}` +
+                 `<${type} name="42">${close(type)}` +
+                 `<${type} name="-1">${close(type)}` +
+                 `<${type} name="4294967295">${close(type)}` +
+                 `<${type} name="4294967296">${close(type)}` +
+                 `<${type} name="4294967297">${close(type)}`,
+   desc: type => `${AOrAn(type)} with numeric name`,
+  },
+  {cond: type => !unsupportedOnDocument.includes(type),
+   html: type => "",
+   fn: type => () => {
+     const doc = document.implementation.createHTMLDocument("");
+
+     const elem = doc.createElement(type);
+     elem.name = "foo";
+     doc.body.appendChild(elem);
+     // iframe is no different, because its browsing context is null
+     assert_equals(doc.foo, elem);
+   },
+   desc: type => `${type} must be exposed in detached document (name)`,
+  },
+  {html: type => type == "img"
+    ? '<div id=foo></div><img id=foo name=bar>'
+    : `<div id=foo></div><${type} id=foo>`,
+   desc: type => type == "applet" || type == "img" || type == "object"
+     ? `If there are a div and ${type} with the same id, the ${type} must ` +
+       `be returned on document and both on window`
+     : `If there are a div and ${type} with the same id, both must be ` +
+       `returned on window and neither on document`,
+  },
+];
+
+for (const type of supportedTypes) {
+  for (const variableTest of variableTests) {
+    if (variableTest.cond && !variableTest.cond(type)) {
+      continue;
+    }
+    let obj = {};
+    obj.html = variableTest.html(type);
+    if ("fn" in variableTest) {
+      obj.fn = variableTest.fn(type);
+    }
+    obj.desc = variableTest.desc(type);
+    tests.push(obj);
+
+  }
+}
+
+for (const curTest of tests) {
+  testDiv.innerHTML = curTest.html;
+  // Put runDefaultTest inside a lambda so it doesn't receive parameters
+  test("fn" in curTest ? curTest.fn : () => runDefaultTest(),
+       `${curTest.desc} ${truncate(curTest.html)}`);
+}
+
+  // We also have one async test per type, which we'll write separately
+let asyncTests = [];
+for (const type of supportedTypes) {
+  asyncTests.push([type,
+                   async_test(`${type} must be exposed properly in nested ` +
+                              `browsing context <${type} name="foo" ` +
+                              `id="bar">${close(type)}`)]);
+}
+
+const iframe = document.createElement("iframe");
+iframe.addEventListener("load", runAsyncTests);
+document.body.appendChild(iframe);
+
+function runAsyncTests() {
+  for (const [type, t] of asyncTests) {
+    t.step(() => {
+      const doc = iframe.contentDocument;
+      const elem = doc.createElement(type);
+      elem.name = "foo";
+      elem.id = "bar";
+      doc.body.textContent = "";
+      doc.body.appendChild(elem);
+
+      runDefaultTest(iframe.contentWindow, iframe.contentDocument.body);
+    });
+    t.done();
+  }
+}
+</script>
deleted file mode 100644
--- a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-01.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Named items: img id &amp; name</title>
-<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
-<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<div id="test">
-<img id="a" name="b">
-</div>
-<script>
-test(function() {
-  assert_equals(document.a, document.getElementsByTagName("img")[0]);
-  assert_equals(document['a'], document.getElementsByTagName("img")[0]);
-  assert_equals(document.b, document.getElementsByTagName("img")[0]);
-  assert_equals(document['b'], document.getElementsByTagName("img")[0]);
-}, "img elements that have a name and id attribute, should be accessible by both values.");
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-02.html
+++ /dev/null
@@ -1,99 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Named items: iframes</title>
-<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
-<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<div id="test">
-<iframe name="test1"></iframe>
-
-<iframe name="test2"></iframe>
-<iframe name="test2"></iframe>
-
-<iframe name="test3"></iframe>
-<img name="test3">
-
-<img name="test4">
-<iframe name="test4"></iframe>
-
-<iframe id="test5"></iframe>
-
-<iframe name="test6" id="fail"></iframe>
-
-<iframe name="fail" id="test7"></iframe>
-
-<iframe name="42"></iframe>
-</div>
-<script>
-test(function() {
-  var iframe = document.getElementsByTagName("iframe")[0];
-  assert_equals(iframe.name, "test1");
-
-  assert_true("test1" in document, '"test1" in document should be true');
-  assert_equals(document.test1, iframe.contentWindow);
-}, "If the only named item is an iframe, the contentWindow should be returned.");
-
-test(function() {
-  var iframe1 = document.getElementsByTagName("iframe")[1];
-  assert_equals(iframe1.name, "test2");
-  var iframe2 = document.getElementsByTagName("iframe")[2];
-  assert_equals(iframe2.name, "test2");
-
-  assert_true("test2" in document, '"test2" in document should be true');
-  var collection = document.test2;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [iframe1, iframe2]);
-}, "If there are two iframes, a collection should be returned.");
-
-test(function() {
-  var iframe = document.getElementsByTagName("iframe")[3];
-  assert_equals(iframe.name, "test3");
-  var img = document.getElementsByTagName("img")[0];
-  assert_equals(img.name, "test3");
-
-  assert_true("test3" in document, '"test3" in document should be true');
-  var collection = document.test3;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [iframe, img]);
-}, "If there are an iframe and another element (iframe first), a collection should be returned.");
-
-test(function() {
-  var iframe = document.getElementsByTagName("iframe")[4];
-  assert_equals(iframe.name, "test4");
-  var img = document.getElementsByTagName("img")[1];
-  assert_equals(img.name, "test4");
-
-  assert_true("test4" in document, '"test4" in document should be true');
-  var collection = document.test4;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [img, iframe]);
-}, "If there are an iframe and another element (iframe last), a collection should be returned.");
-
-test(function() {
-  assert_false("test5" in document, '"test5" in document should be false');
-  assert_equals(document.test5, undefined);
-}, "If an iframe has an id and no name, it should not be returned.");
-
-test(function() {
-  var iframe = document.getElementsByTagName("iframe")[6];
-  assert_equals(iframe.name, "test6");
-
-  assert_true("test6" in document, '"test6" in document should be true');
-  assert_equals(document.test6, iframe.contentWindow);
-}, "If an iframe has a name and a different id, it should be returned by its name.");
-
-test(function() {
-  assert_false("test7" in document, '"test7" in document should be false');
-  assert_equals(document.test7, undefined);
-}, "If an iframe has an id and a different name, it should not be returned by its id.");
-
-test(function() {
-  var iframe = document.getElementsByTagName("iframe")[8];
-  assert_equals(iframe.name, "42");
-
-  assert_true(42 in document, '42 in document should be true');
-  assert_equals(document[42], iframe.contentWindow);
-}, "An iframe whose name looks like an array index should work.");
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-03.html
+++ /dev/null
@@ -1,110 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Named items: applets</title>
-<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
-<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<div id="test">
-<applet name=test1></applet>
-
-<applet name=test2></applet>
-<applet name=test2></applet>
-
-<applet id=test3></applet>
-
-<applet id=test4></applet>
-<applet id=test4></applet>
-
-<applet name=test5></applet>
-<applet id=test5></applet>
-
-<applet id=test6></applet>
-<applet name=test6></applet>
-
-<applet id=test7 name=fail></applet>
-
-<applet name=test8 id=fail></applet>
-</div>
-<script>
-test(function() {
-  var applet = document.getElementsByTagName("applet")[0];
-  assert_equals(applet.name, "test1");
-
-  assert_true("test1" in document, '"test1" in document should be true');
-  assert_equals(document.test1, applet);
-}, "If there is one applet, it should be returned (name)");
-
-test(function() {
-  var applet1 = document.getElementsByTagName("applet")[1];
-  assert_equals(applet1.name, "test2");
-  var applet2 = document.getElementsByTagName("applet")[2];
-  assert_equals(applet2.name, "test2");
-
-  assert_true("test2" in document, '"test2" in document should be true');
-  var collection = document.test2;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [applet1, applet2]);
-}, "If there are two applets, a collection should be returned. (name)");
-
-test(function() {
-  var applet = document.getElementsByTagName("applet")[3];
-  assert_equals(applet.id, "test3");
-
-  assert_true("test3" in document, '"test3" in document should be true');
-  assert_equals(document.test3, applet);
-}, "If there is one applet, it should be returned (id)");
-
-test(function() {
-  var applet1 = document.getElementsByTagName("applet")[4];
-  assert_equals(applet1.id, "test4");
-  var applet2 = document.getElementsByTagName("applet")[5];
-  assert_equals(applet2.id, "test4");
-
-  assert_true("test4" in document, '"test4" in document should be true');
-  var collection = document.test4;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [applet1, applet2]);
-}, "If there are two applets, a collection should be returned. (id)");
-
-test(function() {
-  var applet1 = document.getElementsByTagName("applet")[6];
-  assert_equals(applet1.name, "test5");
-  var applet2 = document.getElementsByTagName("applet")[7];
-  assert_equals(applet2.id, "test5");
-
-  assert_true("test5" in document, '"test5" in document should be true');
-  var collection = document.test5;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [applet1, applet2]);
-}, "If there are two applets, a collection should be returned. (name and id)");
-
-test(function() {
-  var applet1 = document.getElementsByTagName("applet")[8];
-  assert_equals(applet1.id, "test6");
-  var applet2 = document.getElementsByTagName("applet")[9];
-  assert_equals(applet2.name, "test6");
-
-  assert_true("test6" in document, '"test6" in document should be true');
-  var collection = document.test6;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [applet1, applet2]);
-}, "If there are two applets, a collection should be returned. (id and name)");
-
-test(function() {
-  var applet = document.getElementsByTagName("applet")[10];
-  assert_equals(applet.id, "test7");
-
-  assert_true("test7" in document, '"test7" in document should be true');
-  assert_equals(document.test7, applet);
-}, "A name shouldn't affect getting an applet by id");
-
-test(function() {
-  var applet = document.getElementsByTagName("applet")[11];
-  assert_equals(applet.name, "test8");
-
-  assert_true("test8" in document, '"test8" in document should be true');
-  assert_equals(document.test8, applet);
-}, "An id shouldn't affect getting an applet by name");
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-04.html
+++ /dev/null
@@ -1,104 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Named items: forms</title>
-<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
-<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<div id="test">
-<form name=test1></form>
-
-<form name=test2></form>
-<form name=test2></form>
-
-<form id=test3></form>
-
-<form id=test4></form>
-<form id=test4></form>
-
-<form name=test5></form>
-<form id=test5></form>
-
-<form id=test6></form>
-<form name=test6></form>
-
-<form id=test7 name=fail></form>
-
-<form name=test8 id=fail></form>
-</div>
-<script>
-test(function() {
-  var form = document.getElementsByTagName("form")[0];
-  assert_equals(form.name, "test1");
-
-  assert_true("test1" in document, '"test1" in document should be true');
-  assert_equals(document.test1, form);
-}, "If there is one form, it should be returned (name)");
-
-test(function() {
-  var form1 = document.getElementsByTagName("form")[1];
-  assert_equals(form1.name, "test2");
-  var form2 = document.getElementsByTagName("form")[2];
-  assert_equals(form2.name, "test2");
-
-  assert_true("test2" in document, '"test2" in document should be true');
-  var collection = document.test2;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [form1, form2]);
-}, "If there are two forms, a collection should be returned. (name)");
-
-test(function() {
-  var form = document.getElementsByTagName("form")[3];
-  assert_equals(form.id, "test3");
-
-  assert_false("test3" in document, '"test3" in document should be false');
-  assert_equals(document.test3, undefined);
-}, "If there is one form, it should not be returned (id)");
-
-test(function() {
-  var form1 = document.getElementsByTagName("form")[4];
-  assert_equals(form1.id, "test4");
-  var form2 = document.getElementsByTagName("form")[5];
-  assert_equals(form2.id, "test4");
-
-  assert_false("test4" in document, '"test4" in document should be false');
-  assert_equals(document.test4, undefined);
-}, "If there are two forms, nothing should be returned. (id)");
-
-test(function() {
-  var form1 = document.getElementsByTagName("form")[6];
-  assert_equals(form1.name, "test5");
-  var form2 = document.getElementsByTagName("form")[7];
-  assert_equals(form2.id, "test5");
-
-  assert_true("test5" in document, '"test5" in document should be true');
-  assert_equals(document.test5, form1);
-}, "If there are two forms, a collection should be returned. (name and id)");
-
-test(function() {
-  var form1 = document.getElementsByTagName("form")[8];
-  assert_equals(form1.id, "test6");
-  var form2 = document.getElementsByTagName("form")[9];
-  assert_equals(form2.name, "test6");
-
-  assert_true("test6" in document, '"test6" in document should be true');
-  assert_equals(document.test6, form2);
-}, "If there are two forms, a collection should be returned. (id and name)");
-
-test(function() {
-  var form = document.getElementsByTagName("form")[10];
-  assert_equals(form.id, "test7");
-
-  assert_false("test7" in document, '"test7" in document should be false');
-  assert_equals(document.test7, undefined);
-}, "A name shouldn't affect getting an form by id");
-
-test(function() {
-  var form = document.getElementsByTagName("form")[11];
-  assert_equals(form.name, "test8");
-
-  assert_true("test8" in document, '"test8" in document should be true');
-  assert_equals(document.test8, form);
-}, "An id shouldn't affect getting an form by name");
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-05.html
+++ /dev/null
@@ -1,104 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Named items: embeds</title>
-<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
-<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<div id="test">
-<embed name=test1></embed>
-
-<embed name=test2></embed>
-<embed name=test2></embed>
-
-<embed id=test3></embed>
-
-<embed id=test4></embed>
-<embed id=test4></embed>
-
-<embed name=test5></embed>
-<embed id=test5></embed>
-
-<embed id=test6></embed>
-<embed name=test6></embed>
-
-<embed id=test7 name=fail></embed>
-
-<embed name=test8 id=fail></embed>
-</div>
-<script>
-test(function() {
-  var embed = document.getElementsByTagName("embed")[0];
-  assert_equals(embed.name, "test1");
-
-  assert_true("test1" in document, '"test1" in document should be true');
-  assert_equals(document.test1, embed);
-}, "If there is one embed, it should be returned (name)");
-
-test(function() {
-  var embed1 = document.getElementsByTagName("embed")[1];
-  assert_equals(embed1.name, "test2");
-  var embed2 = document.getElementsByTagName("embed")[2];
-  assert_equals(embed2.name, "test2");
-
-  assert_true("test2" in document, '"test2" in document should be true');
-  var collection = document.test2;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [embed1, embed2]);
-}, "If there are two embeds, a collection should be returned. (name)");
-
-test(function() {
-  var embed = document.getElementsByTagName("embed")[3];
-  assert_equals(embed.id, "test3");
-
-  assert_false("test3" in document, '"test3" in document should be false');
-  assert_equals(document.test3, undefined);
-}, "If there is one embed, it should not be returned (id)");
-
-test(function() {
-  var embed1 = document.getElementsByTagName("embed")[4];
-  assert_equals(embed1.id, "test4");
-  var embed2 = document.getElementsByTagName("embed")[5];
-  assert_equals(embed2.id, "test4");
-
-  assert_false("test4" in document, '"test4" in document should be false');
-  assert_equals(document.test4, undefined);
-}, "If there are two embeds, nothing should be returned. (id)");
-
-test(function() {
-  var embed1 = document.getElementsByTagName("embed")[6];
-  assert_equals(embed1.name, "test5");
-  var embed2 = document.getElementsByTagName("embed")[7];
-  assert_equals(embed2.id, "test5");
-
-  assert_true("test5" in document, '"test5" in document should be true');
-  assert_equals(document.test5, embed1);
-}, "If there are two embeds, a collection should be returned. (name and id)");
-
-test(function() {
-  var embed1 = document.getElementsByTagName("embed")[8];
-  assert_equals(embed1.id, "test6");
-  var embed2 = document.getElementsByTagName("embed")[9];
-  assert_equals(embed2.name, "test6");
-
-  assert_true("test6" in document, '"test6" in document should be true');
-  assert_equals(document.test6, embed2);
-}, "If there are two embeds, a collection should be returned. (id and name)");
-
-test(function() {
-  var embed = document.getElementsByTagName("embed")[10];
-  assert_equals(embed.id, "test7");
-
-  assert_false("test7" in document, '"test7" in document should be false');
-  assert_equals(document.test7, undefined);
-}, "A name shouldn't affect getting an embed by id");
-
-test(function() {
-  var embed = document.getElementsByTagName("embed")[11];
-  assert_equals(embed.name, "test8");
-
-  assert_true("test8" in document, '"test8" in document should be true');
-  assert_equals(document.test8, embed);
-}, "An id shouldn't affect getting an embed by name");
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-06.html
+++ /dev/null
@@ -1,104 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Named items: imgs</title>
-<link rel="author" title="Ms2ger" href="mailto:ms2ger@gmail.com">
-<link rel="help" href="https://html.spec.whatwg.org/multipage/#dom-document-nameditem">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<div id="test">
-<img name=test1>
-
-<img name=test2>
-<img name=test2>
-
-<img id=test3>
-
-<img id=test4>
-<img id=test4 name="">
-
-<img name=test5>
-<img id=test5>
-
-<img id=test6>
-<img name=test6>
-
-<img id=test7 name=fail>
-
-<img name=test8 id=fail>
-</div>
-<script>
-test(function() {
-  var img = document.getElementsByTagName("img")[0];
-  assert_equals(img.name, "test1");
-
-  assert_true("test1" in document, '"test1" in document should be true');
-  assert_equals(document.test1, img);
-}, "If there is one img, it should be returned (name)");
-
-test(function() {
-  var img1 = document.getElementsByTagName("img")[1];
-  assert_equals(img1.name, "test2");
-  var img2 = document.getElementsByTagName("img")[2];
-  assert_equals(img2.name, "test2");
-
-  assert_true("test2" in document, '"test2" in document should be true');
-  var collection = document.test2;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [img1, img2]);
-}, "If there are two imgs, a collection should be returned. (name)");
-
-test(function() {
-  var img = document.getElementsByTagName("img")[3];
-  assert_equals(img.id, "test3");
-
-  assert_false("test3" in document, '"test3" in document should be false');
-  assert_equals(document.test3, undefined);
-}, "If there is one img, it should not be returned (id)");
-
-test(function() {
-  var img1 = document.getElementsByTagName("img")[4];
-  assert_equals(img1.id, "test4");
-  var img2 = document.getElementsByTagName("img")[5];
-  assert_equals(img2.id, "test4");
-
-  assert_false("test4" in document, '"test4" in document should be false');
-  assert_equals(document.test4, undefined);
-}, "If there are two imgs, nothing should be returned. (id)");
-
-test(function() {
-  var img1 = document.getElementsByTagName("img")[6];
-  assert_equals(img1.name, "test5");
-  var img2 = document.getElementsByTagName("img")[7];
-  assert_equals(img2.id, "test5");
-
-  assert_true("test5" in document, '"test5" in document should be true');
-  assert_equals(document.test5, img1);
-}, "If there are two imgs, the one with a name should be returned. (name and id)");
-
-test(function() {
-  var img1 = document.getElementsByTagName("img")[8];
-  assert_equals(img1.id, "test6");
-  var img2 = document.getElementsByTagName("img")[9];
-  assert_equals(img2.name, "test6");
-
-  assert_true("test6" in document, '"test6" in document should be true');
-  assert_equals(document.test6, img2);
-}, "If there are two imgs, the one with a name should be returned. (id and name)");
-
-test(function() {
-  var img = document.getElementsByTagName("img")[10];
-  assert_equals(img.id, "test7");
-
-  assert_true("test7" in document, '"test7" in document should be true');
-  assert_equals(document.test7, img);
-}, "A name should affect getting an img by id");
-
-test(function() {
-  var img = document.getElementsByTagName("img")[11];
-  assert_equals(img.name, "test8");
-
-  assert_true("test8" in document, '"test8" in document should be true');
-  assert_equals(document.test8, img);
-}, "An id shouldn't affect getting an img by name");
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-07.html
+++ /dev/null
@@ -1,109 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Named items: objects</title>
-<link rel="help" href="https://html.spec.whatwg.org/multipage/dom.html#dom-document-nameditem">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<div id="test">
-<object name=test1></object>
-
-<object name=test2></object>
-<object name=test2></object>
-
-<object id=test3></object>
-
-<object id=test4></object>
-<object id=test4></object>
-
-<object name=test5></object>
-<object id=test5></object>
-
-<object id=test6></object>
-<object name=test6></object>
-
-<object id=test7 name=fail></object>
-
-<object name=test8 id=fail></object>
-</div>
-<script>
-test(function() {
-  var object = document.getElementsByTagName("object")[0];
-  assert_equals(object.name, "test1");
-
-  assert_true("test1" in document, '"test1" in document should be true');
-  assert_equals(document.test1, object);
-}, "If there is one object, it should be returned (name)");
-
-test(function() {
-  var object1 = document.getElementsByTagName("object")[1];
-  assert_equals(object1.name, "test2");
-  var object2 = document.getElementsByTagName("object")[2];
-  assert_equals(object2.name, "test2");
-
-  assert_true("test2" in document, '"test2" in document should be true');
-  var collection = document.test2;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [object1, object2]);
-}, "If there are two objects, a collection should be returned. (name)");
-
-test(function() {
-  var object = document.getElementsByTagName("object")[3];
-  assert_equals(object.id, "test3");
-
-  assert_true("test3" in document, '"test3" in document should be true');
-  assert_equals(document.test3, object);
-}, "If there is one object, it should be returned (id)");
-
-test(function() {
-  var object1 = document.getElementsByTagName("object")[4];
-  assert_equals(object1.id, "test4");
-  var object2 = document.getElementsByTagName("object")[5];
-  assert_equals(object2.id, "test4");
-
-  assert_true("test4" in document, '"test4" in document should be true');
-  var collection = document.test4;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [object1, object2]);
-}, "If there are two objects, a collection should be returned. (id)");
-
-test(function() {
-  var object1 = document.getElementsByTagName("object")[6];
-  assert_equals(object1.name, "test5");
-  var object2 = document.getElementsByTagName("object")[7];
-  assert_equals(object2.id, "test5");
-
-  assert_true("test5" in document, '"test5" in document should be true');
-  var collection = document.test5;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [object1, object2]);
-}, "If there are two objects, a collection should be returned. (name and id)");
-
-test(function() {
-  var object1 = document.getElementsByTagName("object")[8];
-  assert_equals(object1.id, "test6");
-  var object2 = document.getElementsByTagName("object")[9];
-  assert_equals(object2.name, "test6");
-
-  assert_true("test6" in document, '"test6" in document should be true');
-  var collection = document.test6;
-  assert_class_string(collection, "HTMLCollection", "collection should be an HTMLCollection");
-  assert_array_equals(collection, [object1, object2]);
-}, "If there are two objects, a collection should be returned. (id and name)");
-
-test(function() {
-  var object = document.getElementsByTagName("object")[10];
-  assert_equals(object.id, "test7");
-
-  assert_true("test7" in document, '"test7" in document should be true');
-  assert_equals(document.test7, object);
-}, "A name shouldn't affect getting an object by id");
-
-test(function() {
-  var object = document.getElementsByTagName("object")[11];
-  assert_equals(object.name, "test8");
-
-  assert_true("test8" in document, '"test8" in document should be true');
-  assert_equals(document.test8, object);
-}, "An id shouldn't affect getting an object by name");
-</script>
deleted file mode 100644
--- a/testing/web-platform/tests/html/dom/documents/dom-tree-accessors/nameditem-08.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<title>Named items: duplicate id attributes for applet, object and img</title>
-<link rel="help" href="https://html.spec.whatwg.org/multipage/dom.html#dom-document-nameditem">
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<div id="log"></div>
-<div id="test">
-<div id=test1></div>
-<applet id=test1></applet>
-
-<div id=test2></div>
-<object id=test2></object>
-
-<div id=test3></div>
-<img id=test3 name=non-empty>
-</div>
-<script>
-test(function() {
-  var applet = document.querySelector("applet");
-  assert_equals(applet.id, "test1");
-
-  assert_true("test1" in document);
-  assert_equals(document.test1, applet);
-}, "If there is a div and applet with same id, the applet should be returned");
-
-test(function() {
-  var object = document.querySelector("object");
-  assert_equals(object.id, "test2");
-
-  assert_true("test2" in document);
-  assert_equals(document.test2, object);
-}, "If there is a div and object with same id, the object should be returned");
-
-test(function() {
-  var img = document.querySelector("img");
-  assert_equals(img.id, "test3");
-
-  assert_true("test3" in document);
-  assert_equals(document.test3, img);
-}, "If there is a div and img with same id, the img should be returned");
-</script>