Source/WebCore:
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Sep 2015 00:47:09 +0000 (00:47 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 18 Sep 2015 00:47:09 +0000 (00:47 +0000)
Add HTMLSlotElement, Element.slot, and NonDocumentTypeChildNode.assignedSlot
https://bugs.webkit.org/show_bug.cgi?id=149241

Reviewed by Antti Koivisto.

Implement the slotting algorithm and related features: slot element, slot attribute, and assignedSlot
as specified by https://w3c.github.io/webcomponents/spec/shadow/#slotting-algorithm
as of 8bf56e8ea5521a7a911efd1cabeb2be0d5c3ca74.

The slotting algorithm is implemented by the newly introduced SlotAssignment class which is created on
demand by ShadowRoot when a HTMLSlotElement is inserted into the shadow root. SlotAssignment contains
a HashMap of a slot name to SlotInfo structure, which holds the number of slot elements of the said name,
the first element if it's known, and an ordered list of the assigned nodes.

When there is exactly one slot element of a given name, "element" returns the slot element in O(1).
When another slot of the same name is inserted into the same shadow tree, we increment "elementCount" and
set "element" to nullptr since we don't know which slot element comes first in the tree order without O(n)
tree traversal, which is lazily done in resolveAllSlotElements.

Observe that SlotInfo's "element" can be nullptr in two occasions: (1) when there is no slot element of
the given name (SlotAssignment::assignSlots may insert such an entry), and (2) when there are more than
one slot elements of the same name and we haven't run resolveAllSlotElements.

Resolving assigned nodes, on the other hand, is always O(n) unless all assignments are up to date, and
lazily computed by assignSlots. This is because inserting or removing a node doesn't tell us the relative
ordering of the node with respect to other nodes assigned to the same slot. For example, let's say we have
child nodes (A, B, C, D) and (A, D) are assigned to slot Alpha and (B, C) are assigned to slot Beta. If we
insert a new node E between nodes B and C and this node is assigned to slot Alpha, then we must create an
ordered list (A, E, D) for slot Alpha. Unfortunately, determining where to insert E in this list can cost
O(n) child traversal in the worst case.

Tests: fast/shadow-dom/HTMLSlotElement-interface.html
       fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot.html

* CMakeLists.txt:
* DerivedSources.cpp:
* DerivedSources.make:
* WebCore.vcxproj/WebCore.vcxproj:
* WebCore.vcxproj/WebCore.vcxproj.filters:
* WebCore.xcodeproj/project.pbxproj:
* dom/Element.cpp:
(WebCore::Element::attributeChanged): Invalidate the slot assignments when slot attribute is changed.
(WebCore::Element::childrenChanged): Ditto for when a child node is inserted or removed. We can avoid it
when there is no default slot and only text nodes are removed or added in the future.
* dom/Element.idl: Added slot attribute on Element.
* dom/Node.cpp:
(WebCore::Node::assignedSlot): Added. Returns the assigned slot if the slot is in an open shadow tree.
* dom/Node.h:
* dom/NonDocumentTypeChildNode.idl: Added assignedSlot. Only expose in JS for now to avoid generating
the binding code for HTMLSlotElement in other languages.
* dom/ShadowRoot.cpp:
(WebCore::ShadowRoot::findAssignedSlot): Added. Forwards it to the implementation in SlotAssignment.
(WebCore::ShadowRoot::addSlotElementByName): Ditto.
(WebCore::ShadowRoot::removeSlotElementByName): Ditto.
(WebCore::ShadowRoot::invalidateSlotAssignments): Ditto.
(WebCore::ShadowRoot::assignedNodesForSlot): Ditto.
* dom/ShadowRoot.h:
(WebCore::ShadowRoot): Added m_slotAssignments as a member.
* dom/SlotAssignment.cpp: Added.
(WebCore::treatNullAsEmpty): Added. See https://w3c.github.io/webcomponents/spec/shadow/#dfn-default-slot
(WebCore::SlotAssignment::findAssignedSlot): Find the slot element to which a given node is assigned.
Since there could be multiple slot elements of the same name (or lack thereof), call findFirstSlotElement
to find the first slot element.
(WebCore::SlotAssignment::addSlotElementByName): Added. Called when a new slot element is inserted into
the associated shadow tree. When a slot element's name is changed, removeSlotElementByName is called on
with the old name before addSlotElementByName is called with the new name.
(WebCore::SlotAssignment::removeSlotElementByName): Ditto for removal.
(WebCore::SlotAssignment::assignedNodesForSlot): Added. Finds the ordered list of assigned nodes for
a given slot element. When there are multiple slot elements of the same name, we return the list only if
SlotInfo::element matches the argument.
(WebCore::SlotAssignment::findFirstSlotElement): Added. Resolves SlotInfo::element if needed.
(WebCore::SlotAssignment::resolveAllSlotElements): Finds SlotInfo::element for all slots. We resolve all
slots simultaneously to avoid doing O(number of nodes) tree traversal for O(number of slots) to avoid
the worst case O(n^2) behavior when all nodes in the shadow tree are slot elements of the same name.
(WebCore::SlotAssignment::assignSlots): Added. Computes the slot assignments by traversing each child
of the shadow host and adding to the appropriate SlotInfo::assignedNodes, creating a new entry if needed.
* dom/SlotAssignment.h: Added.
(WebCore::SlotAssignment::SlotAssignment):
(WebCore::SlotAssignment::invalidate):
(WebCore::SlotAssignment::SlotInfo::SlotInfo):
(WebCore::SlotAssignment::SlotInfo::hasSlotElements):
(WebCore::SlotAssignment::SlotInfo::hasDuplicatedSlotElements):
(WebCore::SlotAssignment::SlotInfo::shouldResolveSlotElement):
* html/HTMLAttributeNames.in: Added slot attribute.
* html/HTMLSlotElement.cpp: Added.
(WebCore::HTMLSlotElement::create):
(WebCore::HTMLSlotElement::HTMLSlotElement):
(WebCore::HTMLSlotElement::insertedInto): Calls addSlotElementByName.
(WebCore::HTMLSlotElement::removedFrom): Calls removeSlotElementByName. Because the element had already
been removed from the shadow tree, we can't use containingShadowRoot() to find the ShadowRoot here.
(WebCore::HTMLSlotElement::attributeChanged): Calls removeSlotElementByName and addSlotElementByName.
(WebCore::HTMLSlotElement::getDistributedNodes): Returns an ordered list of the assigned nodes.
* html/HTMLSlotElement.h: Added.
* html/HTMLSlotElement.idl: Added.
* html/HTMLTagNames.in: Added slot element.

LayoutTests:
Add HTMLSlotElement and NonDocumentTypeChildNode.assignedSlot
https://bugs.webkit.org/show_bug.cgi?id=149241

Reviewed by Antti Koivisto.

Added new conformance tests and rebaselined tests as needed.

In particular, inspector/model/remote-object.html was rebaselined since "assignedSlot" now appears as one of the first five
properties on Comment node that this test outputs.

* fast/shadow-dom/HTMLSlotElement-interface-expected.txt: Added.
* fast/shadow-dom/HTMLSlotElement-interface.html: Added.
* fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot-expected.txt: Added.
* fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot.html: Added.
* js/dom/dom-static-property-for-in-iteration-expected.txt:
* platform/mac-mavericks/js/dom/global-constructors-attributes-expected.txt:
* platform/mac-yosemite/js/dom/global-constructors-attributes-expected.txt:
* platform/mac/inspector/model: Added.
* platform/mac/inspector/model/remote-object-expected.txt: Copied from LayoutTests/inspector/model/remote-object-expected.txt.
* platform/mac/js/dom/global-constructors-attributes-expected.txt:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@189950 268f45cc-cd09-0410-ab3c-d52691b4dbfc

31 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/shadow-dom/HTMLSlotElement-interface-expected.txt [new file with mode: 0644]
LayoutTests/fast/shadow-dom/HTMLSlotElement-interface.html [new file with mode: 0644]
LayoutTests/fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot-expected.txt [new file with mode: 0644]
LayoutTests/fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot.html [new file with mode: 0644]
LayoutTests/js/dom/dom-static-property-for-in-iteration-expected.txt
LayoutTests/platform/mac-mavericks/js/dom/global-constructors-attributes-expected.txt
LayoutTests/platform/mac-yosemite/js/dom/global-constructors-attributes-expected.txt
LayoutTests/platform/mac/inspector/model/remote-object-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/js/dom/global-constructors-attributes-expected.txt
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/DerivedSources.cpp
Source/WebCore/DerivedSources.make
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj
Source/WebCore/WebCore.vcxproj/WebCore.vcxproj.filters
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.idl
Source/WebCore/dom/Node.cpp
Source/WebCore/dom/Node.h
Source/WebCore/dom/NonDocumentTypeChildNode.idl
Source/WebCore/dom/ShadowRoot.cpp
Source/WebCore/dom/ShadowRoot.h
Source/WebCore/dom/SlotAssignment.cpp [new file with mode: 0644]
Source/WebCore/dom/SlotAssignment.h [new file with mode: 0644]
Source/WebCore/html/HTMLAttributeNames.in
Source/WebCore/html/HTMLSlotElement.cpp [new file with mode: 0644]
Source/WebCore/html/HTMLSlotElement.h [new file with mode: 0644]
Source/WebCore/html/HTMLSlotElement.idl [new file with mode: 0644]
Source/WebCore/html/HTMLTagNames.in

index 60e22d7..705ad26 100644 (file)
@@ -1,3 +1,26 @@
+2015-09-17  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Add HTMLSlotElement and NonDocumentTypeChildNode.assignedSlot
+        https://bugs.webkit.org/show_bug.cgi?id=149241
+
+        Reviewed by Antti Koivisto.
+
+        Added new conformance tests and rebaselined tests as needed.
+
+        In particular, inspector/model/remote-object.html was rebaselined since "assignedSlot" now appears as one of the first five
+        properties on Comment node that this test outputs.
+
+        * fast/shadow-dom/HTMLSlotElement-interface-expected.txt: Added.
+        * fast/shadow-dom/HTMLSlotElement-interface.html: Added.
+        * fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot-expected.txt: Added.
+        * fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot.html: Added.
+        * js/dom/dom-static-property-for-in-iteration-expected.txt:
+        * platform/mac-mavericks/js/dom/global-constructors-attributes-expected.txt:
+        * platform/mac-yosemite/js/dom/global-constructors-attributes-expected.txt:
+        * platform/mac/inspector/model: Added.
+        * platform/mac/inspector/model/remote-object-expected.txt: Copied from LayoutTests/inspector/model/remote-object-expected.txt.
+        * platform/mac/js/dom/global-constructors-attributes-expected.txt:
+
 2015-09-17  Zalan Bujtas  <zalan@apple.com>
 
         CSS WG multicol-1 tests failures with 1px differences due to baseline difference.
diff --git a/LayoutTests/fast/shadow-dom/HTMLSlotElement-interface-expected.txt b/LayoutTests/fast/shadow-dom/HTMLSlotElement-interface-expected.txt
new file mode 100644 (file)
index 0000000..c223415
--- /dev/null
@@ -0,0 +1,7 @@
+
+PASS HTMLSlotElement must be defined on window 
+PASS "name" attribute on HTMLSlotElement must reflect "name" attribute 
+PASS getDistributedNodes method on HTMLSlotElement must return the list of distributed nodes 
+PASS getDistributedNodes must update when slot and name attributes are modified 
+PASS getDistributedNodes must update when slot elements are inserted or removed 
+
diff --git a/LayoutTests/fast/shadow-dom/HTMLSlotElement-interface.html b/LayoutTests/fast/shadow-dom/HTMLSlotElement-interface.html
new file mode 100644 (file)
index 0000000..56a53b1
--- /dev/null
@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Shadow DOM: HTMLSlotElement interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="HTMLSlotElement must exist on window with name attribute and getDistributedNodes() method">
+<link rel="help" href="https://w3c.github.io/webcomponents/spec/shadow/#the-slot-element">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<link rel='stylesheet' href='../../resources/testharness.css'>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+test(function () {
+    assert_true('HTMLSlotElement' in window, 'HTMLSlotElement must be defined on window');
+    assert_equals(HTMLSlotElement.prototype.__proto__, HTMLElement.prototype, 'HTMLSlotElement should inherit from HTMLElement');
+    assert_true(document.createElement('slot') instanceof HTMLSlotElement, 'slot element should be an instance of HTMLSlotElement');
+    assert_true(document.createElement('slot') instanceof HTMLElement, 'slot element should be an instance of HTMLElement');
+}, 'HTMLSlotElement must be defined on window');
+
+test(function () {
+    assert_true('name' in HTMLSlotElement.prototype, '"name" attribute must be defined on HTMLSlotElement.prototype');
+
+    var slotElement = document.createElement('slot');
+    assert_equals(slotElement.name, '', '"name" attribute must return the empty string when "name" content attribute is not set');
+
+    slotElement.setAttribute('name', 'foo');
+    assert_equals(slotElement.name, 'foo', '"name" attribute must return the value of the "name" content attribute');
+
+    slotElement.name = 'bar';
+    assert_equals(slotElement.name, 'bar', '"name" attribute must return the assigned value');
+    assert_equals(slotElement.getAttribute('name'), 'bar', '"name" attribute must update the "name" content attribute');
+}, '"name" attribute on HTMLSlotElement must reflect "name" attribute');
+
+test(function () {
+    assert_true('getDistributedNodes' in HTMLSlotElement.prototype, '"getDistributedNodes" method must be defined on HTMLSlotElement.prototype');
+
+    var shadowHost = document.createElement('div');
+    var child = document.createElement('p');
+
+    var shadowRoot = shadowHost.attachShadow({mode: 'open'});
+    var slotElement = document.createElement('slot');
+    shadowRoot.appendChild(slotElement);
+
+    assert_array_equals(slotElement.getDistributedNodes(), [], 'getDistributedNodes must return an empty array when there are no nodes in the shadow tree');
+
+    shadowHost.appendChild(child);
+    assert_array_equals(slotElement.getDistributedNodes(), [child], 'getDistributedNodes on a default slot must return an element without slot element');
+
+    child.setAttribute('slot', 'foo');
+    assert_array_equals(slotElement.getDistributedNodes(), [], 'getDistributedNodes on a default slot must not return an element with non-empty slot attribute');
+
+    child.setAttribute('slot', '');
+    assert_array_equals(slotElement.getDistributedNodes(), [child], 'getDistributedNodes on a default slot must return an element with empty slot attribute');
+
+    slotElement.setAttribute('name', 'bar');
+    assert_array_equals(slotElement.getDistributedNodes(), [], 'getDistributedNodes on a named slot must not return an element with empty slot attribute');
+
+    slotElement.setAttribute('name', '');
+    assert_array_equals(slotElement.getDistributedNodes(), [child], 'getDistributedNodes on an empty name slot must return an element with empty slot attribute');
+
+}, 'getDistributedNodes method on HTMLSlotElement must return the list of distributed nodes');
+
+test(function () {
+    var shadowHost = document.createElement('div');
+    var p = document.createElement('p');
+    var b = document.createElement('b');
+    shadowHost.appendChild(p);
+    shadowHost.appendChild(b);
+
+    var shadowRoot = shadowHost.attachShadow({mode: 'open'});
+    var slotElement = document.createElement('slot');
+    shadowRoot.appendChild(slotElement);
+
+    assert_array_equals(slotElement.getDistributedNodes(), [p, b], 'getDistributedNodes must return the distributed nodes');
+
+    slotElement.name = 'foo';
+    assert_array_equals(slotElement.getDistributedNodes(), [], 'getDistributedNodes must be empty when there are no matching elements for the slot name');
+
+    b.slot = 'foo';
+    assert_array_equals(slotElement.getDistributedNodes(), [b], 'getDistributedNodes must return the nodes with the matching slot name');
+
+    p.slot = 'foo';
+    assert_array_equals(slotElement.getDistributedNodes(), [p, b], 'getDistributedNodes must return the nodes with the matching slot name in the tree order');
+
+    slotElement.name = null;
+    assert_array_equals(slotElement.getDistributedNodes(), [], 'getDistributedNodes must be empty for a default slot when all elements have "slot" attributes specified');
+
+}, 'getDistributedNodes must update when slot and name attributes are modified');
+
+test(function () {
+    var shadowHost = document.createElement('div');
+    var p = document.createElement('p');
+    var text = document.createTextNode('');
+    var b = document.createElement('b');
+    shadowHost.appendChild(p);
+    shadowHost.appendChild(text);
+    shadowHost.appendChild(b);
+
+    var shadowRoot = shadowHost.attachShadow({mode: 'open'});
+
+    var firstSlotElement = document.createElement('slot');
+    shadowRoot.appendChild(firstSlotElement);
+
+    var secondSlotElement = document.createElement('slot');
+    shadowRoot.appendChild(secondSlotElement);
+
+    assert_array_equals(firstSlotElement.getDistributedNodes(), [p, text, b],
+        'getDistributedNodes on a default slot must return the elements without slot attributes and text nodes');
+    assert_array_equals(secondSlotElement.getDistributedNodes(), [],
+        'getDistributedNodes on the second unnamed slot element must return an empty array');
+
+    shadowRoot.removeChild(firstSlotElement);
+    assert_array_equals(firstSlotElement.getDistributedNodes(), [],
+        'getDistributedNodes on a detached formerly-default slot must return an empty array');
+    assert_array_equals(secondSlotElement.getDistributedNodes(), [p, text, b],
+        'getDistributedNodes on the second unnamed slot element after removing the first must return the elements without slot attributes and text nodes');
+
+    shadowRoot.removeChild(secondSlotElement);
+    shadowRoot.appendChild(secondSlotElement);
+    assert_array_equals(firstSlotElement.getDistributedNodes(), [],
+        'Removing and re-inserting a default slot must not change the result of getDistributedNodes on a detached slot');
+    assert_array_equals(secondSlotElement.getDistributedNodes(), [p, text, b],
+        'Removing and re-inserting a default slot must not change the result of getDistributedNodes');
+
+    shadowRoot.insertBefore(firstSlotElement, secondSlotElement);
+    assert_array_equals(firstSlotElement.getDistributedNodes(), [p, text, b],
+        'getDistributedNodes on a newly inserted unnamed slot element must return the elements without slot attributes and text nodes');
+    assert_array_equals(secondSlotElement.getDistributedNodes(), [],
+        'getDistributedNodes on formerly-first but now second unnamed slot element must return an empty array');
+
+}, 'getDistributedNodes must update when slot elements are inserted or removed');
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot-expected.txt b/LayoutTests/fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot-expected.txt
new file mode 100644 (file)
index 0000000..a4d7477
--- /dev/null
@@ -0,0 +1,6 @@
+
+PASS assignedSlot attribute must be defined on NonDocumentTypeChildNode interface 
+PASS assignedSlot must return null when the node does not have an assigned node 
+PASS assignedSlot must return the assigned slot 
+PASS assignedSlot must return null when the assigned slot element is inside a closed shadow tree 
+
diff --git a/LayoutTests/fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot.html b/LayoutTests/fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot.html
new file mode 100644 (file)
index 0000000..4b8a6d8
--- /dev/null
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Shadow DOM: Extensions to NonDocumentTypeChildNode interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="NonDocumentTypeChildNode interface must have assignedSlot attribute">
+<link rel="help" href="https://w3c.github.io/webcomponents/spec/shadow/#the-slot-element">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<link rel='stylesheet' href='../../resources/testharness.css'>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+test(function () {
+    assert_true('assignedSlot' in Element.prototype, 'assignedSlot must be defined on Element.prototype');
+    assert_true('assignedSlot' in document.createElement('div'), 'assignedSlot must be defined on a div element');
+
+    assert_true('assignedSlot' in CharacterData.prototype, 'assignedSlot must be defined on CharacterData.prototype');
+    assert_true('assignedSlot' in document.createTextNode(''), 'assignedSlot must be defined on a text node');
+    assert_true('assignedSlot' in document.createComment(''), 'assignedSlot must be defined on a comment node');
+
+}, 'assignedSlot attribute must be defined on NonDocumentTypeChildNode interface');
+
+test(function () {
+    assert_equals(document.createElement('div').assignedSlot, null, 'assignedSlot must be null when the element is not in any tree');
+
+    var shadowHost = document.createElement('div');
+    var shadowRoot = shadowHost.attachShadow({mode: 'open'});
+
+    var childElement = document.createElement('b');
+    shadowHost.appendChild(childElement);
+    assert_equals(childElement.assignedSlot, null, 'assignedSlot on an element must be null when a node is not assigned of any slot');
+
+    var childTextNode = document.createTextNode('');
+    shadowHost.appendChild(childTextNode);
+    assert_equals(childTextNode.assignedSlot, null, 'assignedSlot on a text node must be null when a node is not assigned of any slot');
+
+    var commentNode = document.createComment('');
+    shadowHost.appendChild(commentNode);
+    assert_equals(commentNode.assignedSlot, null, 'assignedSlot on a comment node must be null when a node is not assigned of any slot');
+
+    var slot = document.createElement('slot');
+    slot.name = 'foo';
+    shadowRoot.appendChild(slot);
+    assert_equals(childElement.assignedSlot, null, 'assignedSlot on an element must be null when a node does not match any slot');
+    assert_equals(childTextNode.assignedSlot, null, 'assignedSlot on a text node must be null when a node does not match any slot');
+    assert_equals(commentNode.assignedSlot, null, 'assignedSlot on a comment must be null when a node does not match any slot');
+
+}, 'assignedSlot must return null when the node does not have an assigned node');
+
+test(function () {
+    var shadowHost = document.createElement('div');
+    var childElement = document.createElement('b');
+    shadowHost.appendChild(childElement);
+
+    var childTextNode = document.createTextNode('');
+    shadowHost.appendChild(childTextNode);
+
+    var commentNode = document.createTextNode('');
+    shadowHost.appendChild(commentNode);
+
+    var shadowRoot = shadowHost.attachShadow({mode: 'open'});
+    var slot = document.createElement('slot');
+    shadowRoot.appendChild(slot);
+
+    assert_equals(childElement.assignedSlot, slot, 'assignedSlot on an element must return the assigned default slot element');
+    assert_equals(childTextNode.assignedSlot, slot, 'assignedSlot on a text node must return the assigned default slot element');
+    assert_equals(commentNode.assignedSlot, slot, 'assignedSlot on a comment node must return the assigned default slot element');
+
+    slot.name = 'foo';
+    assert_equals(childElement.assignedSlot, null, 'assignedSlot on an element must null when the element is unassigned from a slot element');
+    assert_equals(childTextNode.assignedSlot, null, 'assignedSlot on a text node must null when the node is unassigned from a slot element');
+    assert_equals(commentNode.assignedSlot, null, 'assignedSlot on a text node must null when the node is unassigned from a slot element');
+
+    childElement.slot = 'foo';
+    assert_equals(childElement.assignedSlot, slot, 'assignedSlot on an element must return the re-assigned slot element');
+
+    slot.name = null;
+    assert_equals(childTextNode.assignedSlot, slot, 'assignedSlot on a text node must return the re-assigned slot element');
+    assert_equals(commentNode.assignedSlot, slot, 'assignedSlot on a comment node must return the re-assigned slot element');
+
+}, 'assignedSlot must return the assigned slot');
+
+test(function () {
+    var shadowHost = document.createElement('div');
+    var childElement = document.createElement('b');
+    shadowHost.appendChild(childElement);
+
+    var childTextNode = document.createTextNode('');
+    shadowHost.appendChild(childTextNode);
+
+    var commentNode = document.createTextNode('');
+    shadowHost.appendChild(commentNode);
+
+    var shadowRoot = shadowHost.attachShadow({mode: 'closed'});
+    var slot = document.createElement('slot');
+    shadowRoot.appendChild(slot);
+
+    assert_equals(childElement.assignedSlot, null, 'assignedSlot on an element must return the assigned slot element.');
+    assert_equals(childTextNode.assignedSlot, null, 'assignedSlot on a text node must return the assigned slot element.');
+    assert_equals(commentNode.assignedSlot, null, 'assignedSlot on a comment node must return the assigned slot element.');
+
+}, 'assignedSlot must return null when the assigned slot element is inside a closed shadow tree');
+
+</script>
+</body>
+</html>
index 61ddd89..b48c20d 100644 (file)
@@ -67,8 +67,10 @@ PASS a["dataset"] is [object DOMStringMap]
 PASS a["uiactions"] is 
 PASS a["webkitRegionOverset"] is undefined
 PASS a["shadowRoot"] is null
+PASS a["slot"] is 
 PASS a["previousElementSibling"] is [object HTMLDivElement]
 PASS a["nextElementSibling"] is [object HTMLScriptElement]
+PASS a["assignedSlot"] is null
 PASS a["children"] is [object HTMLCollection]
 PASS a["firstElementChild"] is null
 PASS a["lastElementChild"] is null
index 8fc101d..b355bfd 100644 (file)
@@ -668,6 +668,11 @@ PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').hasOwnProperty
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').hasOwnProperty('set') is false
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').enumerable is false
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').configurable is true
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').value is HTMLSlotElement
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').hasOwnProperty('get') is false
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').hasOwnProperty('set') is false
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').enumerable is false
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').configurable is true
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSourceElement').value is HTMLSourceElement
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSourceElement').hasOwnProperty('get') is false
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSourceElement').hasOwnProperty('set') is false
index b0574a1..c4cbaef 100644 (file)
@@ -668,6 +668,11 @@ PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').hasOwnProperty
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').hasOwnProperty('set') is false
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').enumerable is false
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').configurable is true
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').value is HTMLSlotElement
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').hasOwnProperty('get') is false
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').hasOwnProperty('set') is false
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').enumerable is false
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').configurable is true
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSourceElement').value is HTMLSourceElement
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSourceElement').hasOwnProperty('get') is false
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSourceElement').hasOwnProperty('set') is false
diff --git a/LayoutTests/platform/mac/inspector/model/remote-object-expected.txt b/LayoutTests/platform/mac/inspector/model/remote-object-expected.txt
new file mode 100644 (file)
index 0000000..f47992a
--- /dev/null
@@ -0,0 +1,4258 @@
+
+-----------------------------------------------------
+EXPRESSION: null
+{
+  "_type": "object",
+  "_subtype": "null",
+  "_description": "null",
+  "_value": null
+}
+
+-----------------------------------------------------
+EXPRESSION: undefined
+{
+  "_type": "undefined",
+  "_description": "undefined"
+}
+
+-----------------------------------------------------
+EXPRESSION: Symbol()
+{
+  "_type": "symbol",
+  "_objectId": "<filtered>",
+  "_description": "Symbol()"
+}
+
+-----------------------------------------------------
+EXPRESSION: Symbol('test')
+{
+  "_type": "symbol",
+  "_objectId": "<filtered>",
+  "_description": "Symbol(test)"
+}
+
+-----------------------------------------------------
+EXPRESSION: true
+{
+  "_type": "boolean",
+  "_description": "true",
+  "_value": true
+}
+
+-----------------------------------------------------
+EXPRESSION: false
+{
+  "_type": "boolean",
+  "_description": "false",
+  "_value": false
+}
+
+-----------------------------------------------------
+EXPRESSION: 0
+{
+  "_type": "number",
+  "_description": "0",
+  "_value": 0
+}
+
+-----------------------------------------------------
+EXPRESSION: -0
+{
+  "_type": "number",
+  "_description": "-0",
+  "_value": 0
+}
+
+-----------------------------------------------------
+EXPRESSION: 1
+{
+  "_type": "number",
+  "_description": "1",
+  "_value": 1
+}
+
+-----------------------------------------------------
+EXPRESSION: -1
+{
+  "_type": "number",
+  "_description": "-1",
+  "_value": -1
+}
+
+-----------------------------------------------------
+EXPRESSION: 1.234
+{
+  "_type": "number",
+  "_description": "1.234",
+  "_value": 1.234
+}
+
+-----------------------------------------------------
+EXPRESSION: -1.234
+{
+  "_type": "number",
+  "_description": "-1.234",
+  "_value": -1.234
+}
+
+-----------------------------------------------------
+EXPRESSION: 1e3
+{
+  "_type": "number",
+  "_description": "1000",
+  "_value": 1000
+}
+
+-----------------------------------------------------
+EXPRESSION: Number.MAX_VALUE
+{
+  "_type": "number",
+  "_description": "1.7976931348623157e+308",
+  "_value": 1.7976931348623157e+308
+}
+
+-----------------------------------------------------
+EXPRESSION: Number.MIN_VALUE
+{
+  "_type": "number",
+  "_description": "5e-324",
+  "_value": 5e-324
+}
+
+-----------------------------------------------------
+EXPRESSION: NaN
+{
+  "_type": "number",
+  "_description": "NaN",
+  "_value": null
+}
+
+-----------------------------------------------------
+EXPRESSION: Infinity
+{
+  "_type": "number",
+  "_description": "Infinity",
+  "_value": null
+}
+
+-----------------------------------------------------
+EXPRESSION: -Infinity
+{
+  "_type": "number",
+  "_description": "-Infinity",
+  "_value": null
+}
+
+-----------------------------------------------------
+EXPRESSION: ''
+{
+  "_type": "string",
+  "_description": "",
+  "_value": ""
+}
+
+-----------------------------------------------------
+EXPRESSION: '"'
+{
+  "_type": "string",
+  "_description": "\"",
+  "_value": "\""
+}
+
+-----------------------------------------------------
+EXPRESSION: "'"
+{
+  "_type": "string",
+  "_description": "'",
+  "_value": "'"
+}
+
+-----------------------------------------------------
+EXPRESSION: 'string'
+{
+  "_type": "string",
+  "_description": "string",
+  "_value": "string"
+}
+
+-----------------------------------------------------
+EXPRESSION: 'Unicode…'
+{
+  "_type": "string",
+  "_description": "Unicode…",
+  "_value": "Unicode…"
+}
+
+-----------------------------------------------------
+EXPRESSION: 'I wish I had something to put here.'
+{
+  "_type": "string",
+  "_description": "I wish I had something to put here.",
+  "_value": "I wish I had something to put here."
+}
+
+-----------------------------------------------------
+EXPRESSION: (function(){})
+{
+  "_type": "function",
+  "_objectId": "<filtered>",
+  "_description": "function (){}"
+}
+
+-----------------------------------------------------
+EXPRESSION: function foo(){}; foo
+{
+  "_type": "function",
+  "_objectId": "<filtered>",
+  "_description": "function foo(){}"
+}
+
+-----------------------------------------------------
+EXPRESSION: function myFunction(a, b) { console.log(a, b); }; myFunction
+{
+  "_type": "function",
+  "_objectId": "<filtered>",
+  "_description": "function myFunction(a, b) { console.log(a, b); }"
+}
+
+-----------------------------------------------------
+EXPRESSION: function myTarget(a, b) { console.log(a, b); }; myTarget.bind(null)
+{
+  "_type": "function",
+  "_objectId": "<filtered>",
+  "_description": "function myTarget() {\n    [native code]\n}"
+}
+
+-----------------------------------------------------
+EXPRESSION: Array.prototype.push
+{
+  "_type": "function",
+  "_objectId": "<filtered>",
+  "_description": "function push() {\n    [native code]\n}"
+}
+
+-----------------------------------------------------
+EXPRESSION: window.setTimeout
+{
+  "_type": "function",
+  "_objectId": "<filtered>",
+  "_description": "function setTimeout() {\n    [native code]\n}"
+}
+
+-----------------------------------------------------
+EXPRESSION: Object.getOwnPropertyDescriptor({ get getter() { return 1 } }, 'getter').get
+{
+  "_type": "function",
+  "_objectId": "<filtered>",
+  "_description": "function () { return 1 }"
+}
+
+-----------------------------------------------------
+EXPRESSION: Object.getOwnPropertyDescriptor({ set setter(v) { console.log(v); } }, 'setter').set
+{
+  "_type": "function",
+  "_objectId": "<filtered>",
+  "_description": "function (v) { console.log(v); }"
+}
+
+-----------------------------------------------------
+EXPRESSION: / /
+{
+  "_type": "object",
+  "_subtype": "regexp",
+  "_objectId": "<filtered>",
+  "_description": "/ /",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "regexp",
+    "_description": "/ /",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "lastIndex",
+        "_type": "number",
+        "_value": "0"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: /(?:)/
+{
+  "_type": "object",
+  "_subtype": "regexp",
+  "_objectId": "<filtered>",
+  "_description": "/(?:)/",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "regexp",
+    "_description": "/(?:)/",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "lastIndex",
+        "_type": "number",
+        "_value": "0"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: /^r(e)g[e]{1,}x+/
+{
+  "_type": "object",
+  "_subtype": "regexp",
+  "_objectId": "<filtered>",
+  "_description": "/^r(e)g[e]{1,}x+/",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "regexp",
+    "_description": "/^r(e)g[e]{1,}x+/",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "lastIndex",
+        "_type": "number",
+        "_value": "0"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: /^r(e)g[e]{1,}x+/ig
+{
+  "_type": "object",
+  "_subtype": "regexp",
+  "_objectId": "<filtered>",
+  "_description": "/^r(e)g[e]{1,}x+/gi",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "regexp",
+    "_description": "/^r(e)g[e]{1,}x+/gi",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "lastIndex",
+        "_type": "number",
+        "_value": "0"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: new RegExp('')
+{
+  "_type": "object",
+  "_subtype": "regexp",
+  "_objectId": "<filtered>",
+  "_description": "/(?:)/",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "regexp",
+    "_description": "/(?:)/",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "lastIndex",
+        "_type": "number",
+        "_value": "0"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: new RegExp('test', 'i')
+{
+  "_type": "object",
+  "_subtype": "regexp",
+  "_objectId": "<filtered>",
+  "_description": "/test/i",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "regexp",
+    "_description": "/test/i",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "lastIndex",
+        "_type": "number",
+        "_value": "0"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: []
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 0,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 0,
+    "_properties": [],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [0, -0, 1, 2]
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 4,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 4,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "1",
+        "_type": "number",
+        "_value": "-0"
+      },
+      {
+        "_name": "2",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "3",
+        "_type": "number",
+        "_value": "2"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [[1],[2],[3]]
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 3,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 3,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 1,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_name": "1",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 1,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_name": "2",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 1,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "3"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [true, 1, 1.234, 'string', /regex/]
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 5,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 5,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "boolean",
+        "_value": "true"
+      },
+      {
+        "_name": "1",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "2",
+        "_type": "number",
+        "_value": "1.234"
+      },
+      {
+        "_name": "3",
+        "_type": "string",
+        "_value": "string"
+      },
+      {
+        "_name": "4",
+        "_type": "object",
+        "_subtype": "regexp",
+        "_value": "/regex/"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [[null]]
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 1,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 1,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 1,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "object",
+              "_subtype": "null",
+              "_value": "null"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [[undefined]]
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 1,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 1,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 1,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "undefined",
+              "_value": "undefined"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [{a:1}, {b:2}, {c:2}]
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 3,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 3,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "object",
+        "_valuePreview": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "a",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_name": "1",
+        "_type": "object",
+        "_valuePreview": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "b",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_name": "2",
+        "_type": "object",
+        "_valuePreview": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "c",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [[{a:1}, {b:2}, {c:2}]]
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 1,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 1,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 3,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "object",
+              "_valuePreview": {
+                "_type": "object",
+                "_description": "Object",
+                "_lossless": true,
+                "_overflow": false,
+                "_properties": [
+                  {
+                    "_name": "a",
+                    "_type": "number",
+                    "_value": "1"
+                  }
+                ],
+                "_entries": null
+              }
+            },
+            {
+              "_name": "1",
+              "_type": "object",
+              "_valuePreview": {
+                "_type": "object",
+                "_description": "Object",
+                "_lossless": true,
+                "_overflow": false,
+                "_properties": [
+                  {
+                    "_name": "b",
+                    "_type": "number",
+                    "_value": "2"
+                  }
+                ],
+                "_entries": null
+              }
+            },
+            {
+              "_name": "2",
+              "_type": "object",
+              "_valuePreview": {
+                "_type": "object",
+                "_description": "Object",
+                "_lossless": true,
+                "_overflow": false,
+                "_properties": [
+                  {
+                    "_name": "c",
+                    "_type": "number",
+                    "_value": "2"
+                  }
+                ],
+                "_entries": null
+              }
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: arr = []; arr[0] = arr; arr
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 1,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 1,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "object",
+        "_subtype": "array",
+        "_value": "Array"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: arr = []; arr.length = 100; arr
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 100,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 100,
+    "_properties": [],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: arr = []; arr.length = 100; arr.fill(1)
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 100,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": false,
+    "_overflow": true,
+    "_size": 100,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "1",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "2",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "3",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "4",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "5",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "6",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "7",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "8",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "9",
+        "_type": "number",
+        "_value": "1"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: arr = []; arr.length = 100; arr[10] = 1; arr
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 100,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 100,
+    "_properties": [
+      {
+        "_name": "10",
+        "_type": "number",
+        "_value": "1"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: a = null; (function() { a = arguments; })(1, '2', /3/); a
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Arguments",
+  "_size": 3,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Arguments",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 3,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "1",
+        "_type": "string",
+        "_value": "2"
+      },
+      {
+        "_name": "2",
+        "_type": "object",
+        "_subtype": "regexp",
+        "_value": "/3/"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: new Int32Array(new ArrayBuffer(16))
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Int32Array",
+  "_size": 4,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Int32Array",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 4,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "1",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "2",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "3",
+        "_type": "number",
+        "_value": "0"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: var intArray = new Int32Array(new ArrayBuffer(16)); for (var i = 0; i < intArray.length; ++i) intArray[i] = i; intArray
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Int32Array",
+  "_size": 4,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Int32Array",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 4,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "1",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "2",
+        "_type": "number",
+        "_value": "2"
+      },
+      {
+        "_name": "3",
+        "_type": "number",
+        "_value": "3"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: var buffer = new ArrayBuffer(10000000); var int8View = new Int8Array(buffer); int8View
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Int8Array",
+  "_size": 10000000,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Int8Array",
+    "_lossless": false,
+    "_overflow": true,
+    "_size": 10000000,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "1",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "2",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "3",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "4",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "5",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "6",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "7",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "8",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "9",
+        "_type": "number",
+        "_value": "0"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: ({})
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: ({a: 1})
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "a",
+        "_type": "number",
+        "_value": "1"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: ({a: 1, b: 0, c: -0})
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "a",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "b",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "c",
+        "_type": "number",
+        "_value": "-0"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: ({a: 1, b: "string", c: /regex/, d: Symbol('sym')})
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "a",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "b",
+        "_type": "string",
+        "_value": "string"
+      },
+      {
+        "_name": "c",
+        "_type": "object",
+        "_subtype": "regexp",
+        "_value": "/regex/"
+      },
+      {
+        "_name": "d",
+        "_type": "symbol",
+        "_value": "Symbol(sym)"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: o = {a:1}; o.b = o; o
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "a",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "b",
+        "_type": "object",
+        "_value": "Object"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: ({a:function a(){}, b:function b(){}, get getter(){}, set setter(v){}})
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "a",
+        "_type": "function",
+        "_value": ""
+      },
+      {
+        "_name": "b",
+        "_type": "function",
+        "_value": ""
+      },
+      {
+        "_name": "getter",
+        "_type": "accessor"
+      },
+      {
+        "_name": "setter",
+        "_type": "accessor"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: function Foo() {}; new Foo
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Foo",
+  "_preview": {
+    "_type": "object",
+    "_description": "Foo",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: var Foo2 = function() {}; new Foo2
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Foo2",
+  "_preview": {
+    "_type": "object",
+    "_description": "Foo2",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: var namespace = {}; namespace.Foo3 = function() {}; new namespace.Foo3
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Foo3",
+  "_preview": {
+    "_type": "object",
+    "_description": "Foo3",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: function Bar() { this._x = 5 }; Bar.prototype = {constructor: Bar, get x() {return this._x;}}; new Bar
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Bar",
+  "_preview": {
+    "_type": "object",
+    "_description": "Bar",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "_x",
+        "_type": "number",
+        "_value": "5"
+      },
+      {
+        "_name": "constructor",
+        "_type": "function",
+        "_value": ""
+      },
+      {
+        "_name": "x",
+        "_type": "accessor"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: function Bar2() { this._x = 5 }; Bar.prototype = {get x() {return this._x;}}; new Bar2
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Bar2",
+  "_preview": {
+    "_type": "object",
+    "_description": "Bar2",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "_x",
+        "_type": "number",
+        "_value": "5"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: window.loadEvent
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Event",
+  "_preview": {
+    "_type": "object",
+    "_description": "Event",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "clipboardData",
+        "_type": "undefined",
+        "_value": "undefined"
+      },
+      {
+        "_name": "type",
+        "_type": "string",
+        "_value": "load"
+      },
+      {
+        "_name": "target",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "#document"
+      },
+      {
+        "_name": "currentTarget",
+        "_type": "object",
+        "_value": "Window"
+      },
+      {
+        "_name": "eventPhase",
+        "_type": "number",
+        "_value": "2"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: new ArrayBuffer(16)
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "ArrayBuffer",
+  "_preview": {
+    "_type": "object",
+    "_description": "ArrayBuffer",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "byteLength",
+        "_type": "number",
+        "_value": "16"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: new DataView(new ArrayBuffer(16))
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "DataView",
+  "_preview": {
+    "_type": "object",
+    "_description": "DataView",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "byteOffset",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "byteLength",
+        "_type": "number",
+        "_value": "16"
+      },
+      {
+        "_name": "buffer",
+        "_type": "object",
+        "_value": "ArrayBuffer"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: obj = {}; obj['prop'] = 1; obj[Symbol()] = 2; obj[Symbol('sym')] = 3; obj[Symbol('sym')] = 4; obj[Symbol.iterator] = Symbol(); obj
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "prop",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "Symbol()",
+        "_type": "number",
+        "_value": "2"
+      },
+      {
+        "_name": "Symbol(sym)",
+        "_type": "number",
+        "_value": "3"
+      },
+      {
+        "_name": "Symbol(sym)",
+        "_type": "number",
+        "_value": "4"
+      },
+      {
+        "_name": "Symbol(Symbol.iterator)",
+        "_type": "symbol",
+        "_value": "Symbol()"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: document.body
+{
+  "_type": "object",
+  "_subtype": "node",
+  "_objectId": "<filtered>",
+  "_description": "body",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "node",
+    "_description": "body",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "aLink",
+        "_type": "string",
+        "_value": ""
+      },
+      {
+        "_name": "background",
+        "_type": "string",
+        "_value": ""
+      },
+      {
+        "_name": "bgColor",
+        "_type": "string",
+        "_value": ""
+      },
+      {
+        "_name": "link",
+        "_type": "string",
+        "_value": ""
+      },
+      {
+        "_name": "text",
+        "_type": "string",
+        "_value": ""
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: div = document.createElement('div'); div.className = 'foo bar'; div
+{
+  "_type": "object",
+  "_subtype": "node",
+  "_objectId": "<filtered>",
+  "_description": "div.foo.bar",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "node",
+    "_description": "div.foo.bar",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "align",
+        "_type": "string",
+        "_value": ""
+      },
+      {
+        "_name": "title",
+        "_type": "string",
+        "_value": ""
+      },
+      {
+        "_name": "lang",
+        "_type": "string",
+        "_value": ""
+      },
+      {
+        "_name": "translate",
+        "_type": "boolean",
+        "_value": "true"
+      },
+      {
+        "_name": "dir",
+        "_type": "string",
+        "_value": ""
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: span = document.createElement('span'); span.id = 'foo'; span
+{
+  "_type": "object",
+  "_subtype": "node",
+  "_objectId": "<filtered>",
+  "_description": "span#foo",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "node",
+    "_description": "span#foo",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "title",
+        "_type": "string",
+        "_value": ""
+      },
+      {
+        "_name": "lang",
+        "_type": "string",
+        "_value": ""
+      },
+      {
+        "_name": "translate",
+        "_type": "boolean",
+        "_value": "true"
+      },
+      {
+        "_name": "dir",
+        "_type": "string",
+        "_value": ""
+      },
+      {
+        "_name": "tabIndex",
+        "_type": "number",
+        "_value": "-1"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: input = document.createElement('input'); input.type = 'password'; input
+{
+  "_type": "object",
+  "_subtype": "node",
+  "_objectId": "<filtered>",
+  "_description": "input",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "node",
+    "_description": "input",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "selectionStart",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "selectionEnd",
+        "_type": "number",
+        "_value": "0"
+      },
+      {
+        "_name": "selectionDirection",
+        "_type": "string",
+        "_value": "none"
+      },
+      {
+        "_name": "accept",
+        "_type": "string",
+        "_value": ""
+      },
+      {
+        "_name": "alt",
+        "_type": "string",
+        "_value": ""
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: text = document.createTextNode('text content'); text
+{
+  "_type": "object",
+  "_subtype": "node",
+  "_objectId": "<filtered>",
+  "_description": "#text",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "node",
+    "_description": "#text",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "wholeText",
+        "_type": "string",
+        "_value": "text content"
+      },
+      {
+        "_name": "splitText",
+        "_type": "function",
+        "_value": ""
+      },
+      {
+        "_name": "replaceWholeText",
+        "_type": "function",
+        "_value": ""
+      },
+      {
+        "_name": "data",
+        "_type": "string",
+        "_value": "text content"
+      },
+      {
+        "_name": "length",
+        "_type": "number",
+        "_value": "12"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: comment = document.createComment('comment content'); comment
+{
+  "_type": "object",
+  "_subtype": "node",
+  "_objectId": "<filtered>",
+  "_description": "#comment",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "node",
+    "_description": "#comment",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "data",
+        "_type": "string",
+        "_value": "comment content"
+      },
+      {
+        "_name": "length",
+        "_type": "number",
+        "_value": "15"
+      },
+      {
+        "_name": "previousElementSibling",
+        "_type": "object",
+        "_subtype": "null",
+        "_value": "null"
+      },
+      {
+        "_name": "nextElementSibling",
+        "_type": "object",
+        "_subtype": "null",
+        "_value": "null"
+      },
+      {
+        "_name": "assignedSlot",
+        "_type": "object",
+        "_subtype": "null",
+        "_value": "null"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: svgElement = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); svgElement.classList.add('test'); svgElement
+{
+  "_type": "object",
+  "_subtype": "node",
+  "_objectId": "<filtered>",
+  "_description": "rect.test",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "node",
+    "_description": "rect.test",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "x",
+        "_type": "object",
+        "_value": "SVGAnimatedLength"
+      },
+      {
+        "_name": "y",
+        "_type": "object",
+        "_value": "SVGAnimatedLength"
+      },
+      {
+        "_name": "width",
+        "_type": "object",
+        "_value": "SVGAnimatedLength"
+      },
+      {
+        "_name": "height",
+        "_type": "object",
+        "_value": "SVGAnimatedLength"
+      },
+      {
+        "_name": "rx",
+        "_type": "object",
+        "_value": "SVGAnimatedLength"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [document.body, div, span, input, text, comment, svgElement]
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 7,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 7,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<body>"
+      },
+      {
+        "_name": "1",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<div class=\"foo bar\">"
+      },
+      {
+        "_name": "2",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<span id=\"foo\">"
+      },
+      {
+        "_name": "3",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<input type=\"password\">"
+      },
+      {
+        "_name": "4",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "#text \"text content\""
+      },
+      {
+        "_name": "5",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<!--comment content-->"
+      },
+      {
+        "_name": "6",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<rect class=\"test\">"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: document.head.children
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "HTMLCollection",
+  "_size": 3,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "HTMLCollection",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 3,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<meta>"
+      },
+      {
+        "_name": "1",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<script>"
+      },
+      {
+        "_name": "2",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<script>"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: document.getElementsByClassName('my-test')
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "HTMLCollection",
+  "_size": 3,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "HTMLCollection",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 3,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<p class=\"my-test\">"
+      },
+      {
+        "_name": "1",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<p class=\"my-test\">"
+      },
+      {
+        "_name": "2",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<p class=\"my-test\">"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: document.querySelectorAll('.my-test')
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "NodeList",
+  "_size": 3,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "NodeList",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 3,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<p class=\"my-test\">"
+      },
+      {
+        "_name": "1",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<p class=\"my-test\">"
+      },
+      {
+        "_name": "2",
+        "_type": "object",
+        "_subtype": "node",
+        "_value": "<p class=\"my-test\">"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: error = null; try { [].x.x; } catch (e) { error = e; }; error
+{
+  "_type": "object",
+  "_subtype": "error",
+  "_objectId": "<filtered>",
+  "_description": "TypeError: undefined is not an object (evaluating '[].x.x')",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "error",
+    "_description": "TypeError: undefined is not an object (evaluating '[].x.x')",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "message",
+        "_type": "string",
+        "_value": "undefined is not an object (evaluating '[].x.x')"
+      },
+      {
+        "_name": "line",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "column",
+        "_type": "number",
+        "_value": "25"
+      },
+      {
+        "_name": "stack",
+        "_type": "string",
+        "_value": "eval code\neval@[native code]\n_evaluateOn\n_evaluateAndWrap\nevaluate"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: error = null; try { eval('if()'); } catch (e) { error = e; }; error
+{
+  "_type": "object",
+  "_subtype": "error",
+  "_objectId": "<filtered>",
+  "_description": "SyntaxError: Unexpected token ')'",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "error",
+    "_description": "SyntaxError: Unexpected token ')'",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "message",
+        "_type": "string",
+        "_value": "Unexpected token ')'"
+      },
+      {
+        "_name": "line",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "column",
+        "_type": "number",
+        "_value": "25"
+      },
+      {
+        "_name": "stack",
+        "_type": "string",
+        "_value": "eval@[native code]\neval code\neval@[native code]\n_evaluateOn\n_evaluateAndWrap\nevaluate"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: error = null; try { document.createTextNode('').splitText(100); } catch (e) { error = e; }; error
+{
+  "_type": "object",
+  "_subtype": "error",
+  "_objectId": "<filtered>",
+  "_description": "Error: IndexSizeError: DOM Exception 1",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "error",
+    "_description": "Error: IndexSizeError: DOM Exception 1",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "code",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "name",
+        "_type": "string",
+        "_value": "IndexSizeError"
+      },
+      {
+        "_name": "message",
+        "_type": "string",
+        "_value": "IndexSizeError: DOM Exception 1"
+      },
+      {
+        "_name": "line",
+        "_type": "number",
+        "_value": "1"
+      },
+      {
+        "_name": "column",
+        "_type": "number",
+        "_value": "58"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: new Map
+{
+  "_type": "object",
+  "_subtype": "map",
+  "_objectId": "<filtered>",
+  "_description": "Map",
+  "_size": 0,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "map",
+    "_description": "Map",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 0,
+    "_properties": [],
+    "_entries": []
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: map = new Map; map.set(1, 2); map.set('key', 'value'); map
+{
+  "_type": "object",
+  "_subtype": "map",
+  "_objectId": "<filtered>",
+  "_description": "Map",
+  "_size": 2,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "map",
+    "_description": "Map",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 2,
+    "_properties": [],
+    "_entries": [
+      {
+        "_key": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        },
+        "_value": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_key": {
+          "_type": "string",
+          "_description": "key",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        },
+        "_value": {
+          "_type": "string",
+          "_description": "value",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: map = new Map; map.set({a:1}, {b:2}); map.set(document.body, [1,2]); map
+{
+  "_type": "object",
+  "_subtype": "map",
+  "_objectId": "<filtered>",
+  "_description": "Map",
+  "_size": 2,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "map",
+    "_description": "Map",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 2,
+    "_properties": [],
+    "_entries": [
+      {
+        "_key": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "a",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        },
+        "_value": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "b",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_key": {
+          "_type": "object",
+          "_subtype": "node",
+          "_description": "body",
+          "_lossless": false,
+          "_overflow": true,
+          "_properties": [
+            {
+              "_name": "aLink",
+              "_type": "string",
+              "_value": ""
+            },
+            {
+              "_name": "background",
+              "_type": "string",
+              "_value": ""
+            },
+            {
+              "_name": "bgColor",
+              "_type": "string",
+              "_value": ""
+            },
+            {
+              "_name": "link",
+              "_type": "string",
+              "_value": ""
+            },
+            {
+              "_name": "text",
+              "_type": "string",
+              "_value": ""
+            }
+          ],
+          "_entries": null
+        },
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: map = new Map; for (var i = 0; i <= 100; i++) map.set(i, i); map
+{
+  "_type": "object",
+  "_subtype": "map",
+  "_objectId": "<filtered>",
+  "_description": "Map",
+  "_size": 101,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "map",
+    "_description": "Map",
+    "_lossless": false,
+    "_overflow": true,
+    "_size": 101,
+    "_properties": [],
+    "_entries": [
+      {
+        "_key": {
+          "_type": "number",
+          "_description": "0",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        },
+        "_value": {
+          "_type": "number",
+          "_description": "0",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_key": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        },
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_key": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        },
+        "_value": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_key": {
+          "_type": "number",
+          "_description": "3",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        },
+        "_value": {
+          "_type": "number",
+          "_description": "3",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_key": {
+          "_type": "number",
+          "_description": "4",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        },
+        "_value": {
+          "_type": "number",
+          "_description": "4",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: map = new Map; map.set(map, map); map
+{
+  "_type": "object",
+  "_subtype": "map",
+  "_objectId": "<filtered>",
+  "_description": "Map",
+  "_size": 1,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "map",
+    "_description": "Map",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 1,
+    "_properties": [],
+    "_entries": [
+      {
+        "_key": {
+          "_type": "object",
+          "_subtype": "map",
+          "_description": "Map",
+          "_lossless": false,
+          "_overflow": true,
+          "_size": 1,
+          "_properties": [],
+          "_entries": []
+        },
+        "_value": {
+          "_type": "object",
+          "_subtype": "map",
+          "_description": "Map",
+          "_lossless": false,
+          "_overflow": true,
+          "_size": 1,
+          "_properties": [],
+          "_entries": []
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: map = new WeakMap; strongKey = {id:1}; map.set(strongKey, [1,2]); map
+{
+  "_type": "object",
+  "_subtype": "weakmap",
+  "_objectId": "<filtered>",
+  "_description": "WeakMap",
+  "_size": 1,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "weakmap",
+    "_description": "WeakMap",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 1,
+    "_properties": [],
+    "_entries": [
+      {
+        "_key": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "id",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        },
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: new Set
+{
+  "_type": "object",
+  "_subtype": "set",
+  "_objectId": "<filtered>",
+  "_description": "Set",
+  "_size": 0,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "set",
+    "_description": "Set",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 0,
+    "_properties": [],
+    "_entries": []
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: set = new Set; set.add(1); set.add(2); set.add('key'); set
+{
+  "_type": "object",
+  "_subtype": "set",
+  "_objectId": "<filtered>",
+  "_description": "Set",
+  "_size": 3,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "set",
+    "_description": "Set",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 3,
+    "_properties": [],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "key",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: set = new Set; set.add({a:1}); set.add(document.body); set.add([1,2]); set
+{
+  "_type": "object",
+  "_subtype": "set",
+  "_objectId": "<filtered>",
+  "_description": "Set",
+  "_size": 3,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "set",
+    "_description": "Set",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 3,
+    "_properties": [],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "a",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "node",
+          "_description": "body",
+          "_lossless": false,
+          "_overflow": true,
+          "_properties": [
+            {
+              "_name": "aLink",
+              "_type": "string",
+              "_value": ""
+            },
+            {
+              "_name": "background",
+              "_type": "string",
+              "_value": ""
+            },
+            {
+              "_name": "bgColor",
+              "_type": "string",
+              "_value": ""
+            },
+            {
+              "_name": "link",
+              "_type": "string",
+              "_value": ""
+            },
+            {
+              "_name": "text",
+              "_type": "string",
+              "_value": ""
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: set = new Set; for (var i = 0; i <= 100; i++) set.add(i); set
+{
+  "_type": "object",
+  "_subtype": "set",
+  "_objectId": "<filtered>",
+  "_description": "Set",
+  "_size": 101,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "set",
+    "_description": "Set",
+    "_lossless": false,
+    "_overflow": true,
+    "_size": 101,
+    "_properties": [],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "0",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "3",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "4",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: set = new Set; set.add(set); set
+{
+  "_type": "object",
+  "_subtype": "set",
+  "_objectId": "<filtered>",
+  "_description": "Set",
+  "_size": 1,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "set",
+    "_description": "Set",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 1,
+    "_properties": [],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "set",
+          "_description": "Set",
+          "_lossless": false,
+          "_overflow": true,
+          "_size": 1,
+          "_properties": [],
+          "_entries": []
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: set = new WeakSet; strongKey = {id:1}; set.add(strongKey); set
+{
+  "_type": "object",
+  "_subtype": "weakset",
+  "_objectId": "<filtered>",
+  "_description": "WeakSet",
+  "_size": 1,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "weakset",
+    "_description": "WeakSet",
+    "_lossless": true,
+    "_overflow": false,
+    "_size": 1,
+    "_properties": [],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "id",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: 'a'[Symbol.iterator]()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "String Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "String Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "string",
+        "_type": "string",
+        "_value": "a",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "a",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: 'long string'[Symbol.iterator]()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "String Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "String Iterator",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "string",
+        "_type": "string",
+        "_value": "long string",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "l",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "o",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "n",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "g",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": " ",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [][Symbol.iterator]()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 0,
+          "_properties": [],
+          "_entries": null
+        },
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": []
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [1][Symbol.iterator]()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 1,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        },
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [1, 'two', 3, 'four', 5, 'size'][Symbol.iterator]()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_value": "Array",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "two",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "3",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "four",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "5",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [1, 'two', 3, 'four', 5, 'size'].keys()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_value": "Array",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "key",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "0",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "3",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "4",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: [1, 'two', 3, 'four', 5, 'size'].entries()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_value": "Array",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "key+value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "0"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "string",
+              "_value": "two"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "2"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "3"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "3"
+            },
+            {
+              "_name": "1",
+              "_type": "string",
+              "_value": "four"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "4"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "5"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: map = new Map; map.set(1, 2); map.set('key', 'value'); map.values()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Map Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Map Iterator",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "map",
+        "_type": "object",
+        "_subtype": "map",
+        "_value": "Map",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "value",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: map.keys()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Map Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Map Iterator",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "map",
+        "_type": "object",
+        "_subtype": "map",
+        "_value": "Map",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "key",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "key",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: map.entries()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Map Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Map Iterator",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "map",
+        "_type": "object",
+        "_subtype": "map",
+        "_value": "Map",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "key+value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "string",
+              "_value": "key"
+            },
+            {
+              "_name": "1",
+              "_type": "string",
+              "_value": "value"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: set = new Set; for (var i = 0; i <= 100; i++) set.add(i); set.values()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Set Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Set Iterator",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "set",
+        "_type": "object",
+        "_subtype": "set",
+        "_value": "Set",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "0",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "3",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "4",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: map.entries()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Map Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Map Iterator",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "map",
+        "_type": "object",
+        "_subtype": "map",
+        "_value": "Map",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "key+value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "string",
+              "_value": "key"
+            },
+            {
+              "_name": "1",
+              "_type": "string",
+              "_value": "value"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: x = undefined; (function() { x = arguments; })(1, 'two'); x[Symbol.iterator]()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Arguments",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "string",
+              "_value": "two"
+            }
+          ],
+          "_entries": null
+        },
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "two",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: Reflect.enumerate({a:1, b:2, c:3})
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "PropertyName Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "PropertyName Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "object",
+        "_type": "object",
+        "_valuePreview": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "a",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "b",
+              "_type": "number",
+              "_value": "2"
+            },
+            {
+              "_name": "c",
+              "_type": "number",
+              "_value": "3"
+            }
+          ],
+          "_entries": null
+        },
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "a",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "b",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "c",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: Reflect.enumerate([1, 2, 3, 4, 5, 6, 7])
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "PropertyName Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "PropertyName Iterator",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "object",
+        "_type": "object",
+        "_subtype": "array",
+        "_value": "Array",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "0",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "3",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "4",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: new Promise(function(){})
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Promise",
+  "_preview": {
+    "_type": "object",
+    "_description": "Promise",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "status",
+        "_type": "string",
+        "_value": "pending",
+        "_internal": true
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: Promise.reject()
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Promise",
+  "_preview": {
+    "_type": "object",
+    "_description": "Promise",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "status",
+        "_type": "string",
+        "_value": "rejected",
+        "_internal": true
+      },
+      {
+        "_name": "result",
+        "_type": "undefined",
+        "_value": "undefined",
+        "_internal": true
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: Promise.reject('result')
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Promise",
+  "_preview": {
+    "_type": "object",
+    "_description": "Promise",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "status",
+        "_type": "string",
+        "_value": "rejected",
+        "_internal": true
+      },
+      {
+        "_name": "result",
+        "_type": "string",
+        "_value": "result",
+        "_internal": true
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: Promise.resolve()
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Promise",
+  "_preview": {
+    "_type": "object",
+    "_description": "Promise",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "status",
+        "_type": "string",
+        "_value": "resolved",
+        "_internal": true
+      },
+      {
+        "_name": "result",
+        "_type": "undefined",
+        "_value": "undefined",
+        "_internal": true
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: Promise.resolve({result:1})
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Promise",
+  "_preview": {
+    "_type": "object",
+    "_description": "Promise",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "status",
+        "_type": "string",
+        "_value": "resolved",
+        "_internal": true
+      },
+      {
+        "_name": "result",
+        "_type": "object",
+        "_valuePreview": {
+          "_type": "object",
+          "_description": "Object",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": [
+            {
+              "_name": "result",
+              "_type": "number",
+              "_value": "1"
+            }
+          ],
+          "_entries": null
+        },
+        "_internal": true
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: Person = class Person { constructor(name){} get fullName(){} methodName(p1, p2){} }; Person
+{
+  "_type": "function",
+  "_subtype": "class",
+  "_objectId": "<filtered>",
+  "_description": "class Person",
+  "_classPrototype": {
+    "_type": "object",
+    "_objectId": "<filtered>",
+    "_description": "Person"
+  },
+  "_functionDescription": "function Person(name){}"
+}
+
+-----------------------------------------------------
+EXPRESSION: Person.prototype.methodName
+{
+  "_type": "function",
+  "_objectId": "<filtered>",
+  "_description": "function methodName(p1, p2){}"
+}
+
+-----------------------------------------------------
+EXPRESSION: Alpha = class A { methodA(){} }; Beta = class B extends Alpha { methodB(){} }; Beta
+{
+  "_type": "function",
+  "_subtype": "class",
+  "_objectId": "<filtered>",
+  "_description": "class B",
+  "_classPrototype": {
+    "_type": "object",
+    "_objectId": "<filtered>",
+    "_description": "B"
+  },
+  "_functionDescription": "function B() { super(...arguments); }"
+}
+
+-----------------------------------------------------
+EXPRESSION: [Beta]
+{
+  "_type": "object",
+  "_subtype": "array",
+  "_objectId": "<filtered>",
+  "_description": "Array",
+  "_size": 1,
+  "_preview": {
+    "_type": "object",
+    "_subtype": "array",
+    "_description": "Array",
+    "_lossless": false,
+    "_overflow": false,
+    "_size": 1,
+    "_properties": [
+      {
+        "_name": "0",
+        "_type": "function",
+        "_subtype": "class",
+        "_value": "class B"
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: Object.seal({})
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: Object.freeze({})
+{
+  "_type": "object",
+  "_objectId": "<filtered>",
+  "_description": "Object",
+  "_preview": {
+    "_type": "object",
+    "_description": "Object",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [],
+    "_entries": null
+  }
+}
+
index 2ebb546..9e99fee 100644 (file)
@@ -668,6 +668,11 @@ PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').hasOwnProperty
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').hasOwnProperty('set') is false
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').enumerable is false
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSelectElement').configurable is true
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').value is HTMLSlotElement
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').hasOwnProperty('get') is false
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').hasOwnProperty('set') is false
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').enumerable is false
+PASS Object.getOwnPropertyDescriptor(global, 'HTMLSlotElement').configurable is true
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSourceElement').value is HTMLSourceElement
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSourceElement').hasOwnProperty('get') is false
 PASS Object.getOwnPropertyDescriptor(global, 'HTMLSourceElement').hasOwnProperty('set') is false
index 3b9052e..9d65e79 100644 (file)
@@ -510,6 +510,7 @@ set(WebCore_NON_SVG_IDL_FILES
     html/HTMLQuoteElement.idl
     html/HTMLScriptElement.idl
     html/HTMLSelectElement.idl
+    html/HTMLSlotElement.idl
     html/HTMLSourceElement.idl
     html/HTMLSpanElement.idl
     html/HTMLStyleElement.idl
index 385829b..352db74 100644 (file)
@@ -1,3 +1,101 @@
+2015-09-17  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Add HTMLSlotElement, Element.slot, and NonDocumentTypeChildNode.assignedSlot
+        https://bugs.webkit.org/show_bug.cgi?id=149241
+
+        Reviewed by Antti Koivisto.
+
+        Implement the slotting algorithm and related features: slot element, slot attribute, and assignedSlot
+        as specified by https://w3c.github.io/webcomponents/spec/shadow/#slotting-algorithm
+        as of 8bf56e8ea5521a7a911efd1cabeb2be0d5c3ca74.
+
+        The slotting algorithm is implemented by the newly introduced SlotAssignment class which is created on
+        demand by ShadowRoot when a HTMLSlotElement is inserted into the shadow root. SlotAssignment contains
+        a HashMap of a slot name to SlotInfo structure, which holds the number of slot elements of the said name,
+        the first element if it's known, and an ordered list of the assigned nodes.
+
+        When there is exactly one slot element of a given name, "element" returns the slot element in O(1).
+        When another slot of the same name is inserted into the same shadow tree, we increment "elementCount" and
+        set "element" to nullptr since we don't know which slot element comes first in the tree order without O(n)
+        tree traversal, which is lazily done in resolveAllSlotElements.
+
+        Observe that SlotInfo's "element" can be nullptr in two occasions: (1) when there is no slot element of
+        the given name (SlotAssignment::assignSlots may insert such an entry), and (2) when there are more than
+        one slot elements of the same name and we haven't run resolveAllSlotElements.
+
+        Resolving assigned nodes, on the other hand, is always O(n) unless all assignments are up to date, and
+        lazily computed by assignSlots. This is because inserting or removing a node doesn't tell us the relative
+        ordering of the node with respect to other nodes assigned to the same slot. For example, let's say we have
+        child nodes (A, B, C, D) and (A, D) are assigned to slot Alpha and (B, C) are assigned to slot Beta. If we
+        insert a new node E between nodes B and C and this node is assigned to slot Alpha, then we must create an
+        ordered list (A, E, D) for slot Alpha. Unfortunately, determining where to insert E in this list can cost
+        O(n) child traversal in the worst case.
+
+        Tests: fast/shadow-dom/HTMLSlotElement-interface.html
+               fast/shadow-dom/NonDocumentTypeChildNode-interface-assignedSlot.html
+
+        * CMakeLists.txt:
+        * DerivedSources.cpp:
+        * DerivedSources.make:
+        * WebCore.vcxproj/WebCore.vcxproj:
+        * WebCore.vcxproj/WebCore.vcxproj.filters:
+        * WebCore.xcodeproj/project.pbxproj:
+        * dom/Element.cpp:
+        (WebCore::Element::attributeChanged): Invalidate the slot assignments when slot attribute is changed.
+        (WebCore::Element::childrenChanged): Ditto for when a child node is inserted or removed. We can avoid it
+        when there is no default slot and only text nodes are removed or added in the future.
+        * dom/Element.idl: Added slot attribute on Element.
+        * dom/Node.cpp:
+        (WebCore::Node::assignedSlot): Added. Returns the assigned slot if the slot is in an open shadow tree.
+        * dom/Node.h:
+        * dom/NonDocumentTypeChildNode.idl: Added assignedSlot. Only expose in JS for now to avoid generating
+        the binding code for HTMLSlotElement in other languages.
+        * dom/ShadowRoot.cpp:
+        (WebCore::ShadowRoot::findAssignedSlot): Added. Forwards it to the implementation in SlotAssignment.
+        (WebCore::ShadowRoot::addSlotElementByName): Ditto.
+        (WebCore::ShadowRoot::removeSlotElementByName): Ditto.
+        (WebCore::ShadowRoot::invalidateSlotAssignments): Ditto.
+        (WebCore::ShadowRoot::assignedNodesForSlot): Ditto.
+        * dom/ShadowRoot.h:
+        (WebCore::ShadowRoot): Added m_slotAssignments as a member.
+        * dom/SlotAssignment.cpp: Added.
+        (WebCore::treatNullAsEmpty): Added. See https://w3c.github.io/webcomponents/spec/shadow/#dfn-default-slot
+        (WebCore::SlotAssignment::findAssignedSlot): Find the slot element to which a given node is assigned.
+        Since there could be multiple slot elements of the same name (or lack thereof), call findFirstSlotElement
+        to find the first slot element.
+        (WebCore::SlotAssignment::addSlotElementByName): Added. Called when a new slot element is inserted into
+        the associated shadow tree. When a slot element's name is changed, removeSlotElementByName is called on
+        with the old name before addSlotElementByName is called with the new name.
+        (WebCore::SlotAssignment::removeSlotElementByName): Ditto for removal.
+        (WebCore::SlotAssignment::assignedNodesForSlot): Added. Finds the ordered list of assigned nodes for
+        a given slot element. When there are multiple slot elements of the same name, we return the list only if
+        SlotInfo::element matches the argument. 
+        (WebCore::SlotAssignment::findFirstSlotElement): Added. Resolves SlotInfo::element if needed.
+        (WebCore::SlotAssignment::resolveAllSlotElements): Finds SlotInfo::element for all slots. We resolve all
+        slots simultaneously to avoid doing O(number of nodes) tree traversal for O(number of slots) to avoid
+        the worst case O(n^2) behavior when all nodes in the shadow tree are slot elements of the same name. 
+        (WebCore::SlotAssignment::assignSlots): Added. Computes the slot assignments by traversing each child
+        of the shadow host and adding to the appropriate SlotInfo::assignedNodes, creating a new entry if needed.
+        * dom/SlotAssignment.h: Added.
+        (WebCore::SlotAssignment::SlotAssignment):
+        (WebCore::SlotAssignment::invalidate):
+        (WebCore::SlotAssignment::SlotInfo::SlotInfo):
+        (WebCore::SlotAssignment::SlotInfo::hasSlotElements):
+        (WebCore::SlotAssignment::SlotInfo::hasDuplicatedSlotElements):
+        (WebCore::SlotAssignment::SlotInfo::shouldResolveSlotElement):
+        * html/HTMLAttributeNames.in: Added slot attribute.
+        * html/HTMLSlotElement.cpp: Added.
+        (WebCore::HTMLSlotElement::create):
+        (WebCore::HTMLSlotElement::HTMLSlotElement):
+        (WebCore::HTMLSlotElement::insertedInto): Calls addSlotElementByName.
+        (WebCore::HTMLSlotElement::removedFrom): Calls removeSlotElementByName. Because the element had already
+        been removed from the shadow tree, we can't use containingShadowRoot() to find the ShadowRoot here.
+        (WebCore::HTMLSlotElement::attributeChanged): Calls removeSlotElementByName and addSlotElementByName.
+        (WebCore::HTMLSlotElement::getDistributedNodes): Returns an ordered list of the assigned nodes.
+        * html/HTMLSlotElement.h: Added.
+        * html/HTMLSlotElement.idl: Added.
+        * html/HTMLTagNames.in: Added slot element.
+
 2015-09-17  Chris Dumez  <cdumez@apple.com>
 
         Regression(r189881): release assertion hit in toJS(ExecState*, JSDOMGlobalObject*, DocumentFragment*)
index 30cef6a..5d8a49c 100644 (file)
 #include "JSHTMLQuoteElement.cpp"
 #include "JSHTMLScriptElement.cpp"
 #include "JSHTMLSelectElement.cpp"
+#include "JSHTMLSlotElement.cpp"
 #include "JSHTMLSourceElement.cpp"
 #include "JSHTMLSpanElement.cpp"
 #include "JSHTMLStyleElement.cpp"
index 15b8430..959153a 100644 (file)
@@ -400,6 +400,7 @@ NON_SVG_BINDING_IDLS = \
     $(WebCore)/html/HTMLQuoteElement.idl \
     $(WebCore)/html/HTMLScriptElement.idl \
     $(WebCore)/html/HTMLSelectElement.idl \
+    $(WebCore)/html/HTMLSlotElement.idl \
     $(WebCore)/html/HTMLSourceElement.idl \
     $(WebCore)/html/HTMLSpanElement.idl \
     $(WebCore)/html/HTMLStyleElement.idl \
index a624a13..b4f0b56 100644 (file)
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSlotElement.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSourceElement.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
     </ClCompile>
     <ClCompile Include="..\html\HTMLSelectElementWin.cpp" />
+    <ClCompile Include="..\html\HTMLSlotElement.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug_WinCairo|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='DebugSuffix|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release_WinCairo|x64'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Production|x64'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="..\html\HTMLSourceElement.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLQuoteElement.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLScriptElement.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSelectElement.h" />
+    <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSlotElement.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSourceElement.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSpanElement.h" />
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLStyleElement.h" />
     <ClInclude Include="..\html\HTMLQuoteElement.h" />
     <ClInclude Include="..\html\HTMLScriptElement.h" />
     <ClInclude Include="..\html\HTMLSelectElement.h" />
+    <ClInclude Include="..\html\HTMLSlotElement.h" />
     <ClInclude Include="..\html\HTMLSourceElement.h" />
     <ClInclude Include="..\html\HTMLSpanElement.h" />
     <ClInclude Include="..\html\HTMLStyleElement.h" />
index 99f3bca..4a04885 100644 (file)
     <ClCompile Include="..\html\HTMLSelectElementWin.cpp">
       <Filter>html</Filter>
     </ClCompile>
+    <ClCompile Include="..\html\HTMLSlotElementWin.cpp">
+      <Filter>html</Filter>
+    </ClCompile>
     <ClCompile Include="..\html\HTMLSourceElement.cpp">
       <Filter>html</Filter>
     </ClCompile>
     <ClCompile Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSelectElement.cpp">
       <Filter>DerivedSources</Filter>
     </ClCompile>
+    <ClCompile Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSlotElement.cpp">
+      <Filter>DerivedSources</Filter>
+    </ClCompile>
     <ClCompile Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSourceElement.cpp">
       <Filter>DerivedSources</Filter>
     </ClCompile>
     <ClInclude Include="..\html\HTMLSelectElement.h">
       <Filter>html</Filter>
     </ClInclude>
+    <ClInclude Include="..\html\HTMLSlotElement.h">
+      <Filter>html</Filter>
+    </ClInclude>
     <ClInclude Include="..\html\HTMLSourceElement.h">
       <Filter>html</Filter>
     </ClInclude>
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSelectElement.h">
       <Filter>DerivedSources</Filter>
     </ClInclude>
+    <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSlotElement.h">
+      <Filter>DerivedSources</Filter>
+    </ClInclude>
     <ClInclude Include="$(ConfigurationBuildDir)\obj$(PlatformArchitecture)\$(ProjectName)\DerivedSources\JSHTMLSourceElement.h">
       <Filter>DerivedSources</Filter>
     </ClInclude>
index b22df74..cc53d8b 100644 (file)
                9B417064125662B3006B28FC /* ApplyBlockElementCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B417062125662B3006B28FC /* ApplyBlockElementCommand.h */; };
                9B417065125662B3006B28FC /* ApplyBlockElementCommand.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B417063125662B3006B28FC /* ApplyBlockElementCommand.cpp */; };
                9B50B1DE17CD4C0F0087F63C /* FormNamedItem.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B50B1DC17CD4C0F0087F63C /* FormNamedItem.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               9B532EA31BA928570038A827 /* SlotAssignment.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B532EA11BA928570038A827 /* SlotAssignment.cpp */; };
+               9B532EA41BA928570038A827 /* SlotAssignment.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B532EA21BA928570038A827 /* SlotAssignment.h */; };
                9B55EEE91B3E8898005342BC /* EditorCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 9B55EEE81B3E8898005342BC /* EditorCocoa.mm */; };
+               9B69D3B41B98FFE900E3512B /* HTMLSlotElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B69D3B21B98FFE900E3512B /* HTMLSlotElement.cpp */; };
+               9B69D3B51B98FFE900E3512B /* HTMLSlotElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B69D3B31B98FFE900E3512B /* HTMLSlotElement.h */; };
+               9B69D3B81B99100700E3512B /* JSHTMLSlotElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9B69D3B61B99100700E3512B /* JSHTMLSlotElement.cpp */; };
+               9B69D3B91B99100700E3512B /* JSHTMLSlotElement.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B69D3B71B99100700E3512B /* JSHTMLSlotElement.h */; };
                9B6C41531344949000085B62 /* StringWithDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 9B6C41521344949000085B62 /* StringWithDirection.h */; settings = {ATTRIBUTES = (Private, ); }; };
                9BA273F4172206BB0097CE47 /* LogicalSelectionOffsetCaches.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BA273F3172206BB0097CE47 /* LogicalSelectionOffsetCaches.h */; };
                9BAB6C6C12550631001626D4 /* EditingStyle.h in Headers */ = {isa = PBXBuildFile; fileRef = 9BAB6C6A12550631001626D4 /* EditingStyle.h */; settings = {ATTRIBUTES = (Private, ); }; };
                9B417062125662B3006B28FC /* ApplyBlockElementCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApplyBlockElementCommand.h; sourceTree = "<group>"; };
                9B417063125662B3006B28FC /* ApplyBlockElementCommand.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ApplyBlockElementCommand.cpp; sourceTree = "<group>"; };
                9B50B1DC17CD4C0F0087F63C /* FormNamedItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FormNamedItem.h; sourceTree = "<group>"; };
+               9B532EA11BA928570038A827 /* SlotAssignment.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SlotAssignment.cpp; sourceTree = "<group>"; };
+               9B532EA21BA928570038A827 /* SlotAssignment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SlotAssignment.h; sourceTree = "<group>"; };
                9B55EEE81B3E8898005342BC /* EditorCocoa.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = EditorCocoa.mm; sourceTree = "<group>"; };
                9B55EEEA1B3F3FEF005342BC /* EditorCocoa.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EditorCocoa.h; sourceTree = "<group>"; };
+               9B69D3B11B98FF0A00E3512B /* HTMLSlotElement.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = HTMLSlotElement.idl; sourceTree = "<group>"; };
+               9B69D3B21B98FFE900E3512B /* HTMLSlotElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HTMLSlotElement.cpp; sourceTree = "<group>"; };
+               9B69D3B31B98FFE900E3512B /* HTMLSlotElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLSlotElement.h; sourceTree = "<group>"; };
+               9B69D3B61B99100700E3512B /* JSHTMLSlotElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSHTMLSlotElement.cpp; sourceTree = "<group>"; };
+               9B69D3B71B99100700E3512B /* JSHTMLSlotElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSHTMLSlotElement.h; sourceTree = "<group>"; };
                9B6BC9601B975966005AE1F0 /* JSShadowRoot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSShadowRoot.cpp; sourceTree = "<group>"; };
                9B6BC9611B975966005AE1F0 /* JSShadowRoot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSShadowRoot.h; sourceTree = "<group>"; };
                9B6C41521344949000085B62 /* StringWithDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringWithDirection.h; sourceTree = "<group>"; };
                                A81369BD097374F500D74463 /* HTMLSelectElement.cpp */,
                                A81369BC097374F500D74463 /* HTMLSelectElement.h */,
                                855542990AA4938800BA89F2 /* HTMLSelectElement.idl */,
+                               9B69D3B11B98FF0A00E3512B /* HTMLSlotElement.idl */,
+                               9B69D3B21B98FFE900E3512B /* HTMLSlotElement.cpp */,
+                               9B69D3B31B98FFE900E3512B /* HTMLSlotElement.h */,
                                E44613950CD6331000FADA75 /* HTMLSourceElement.cpp */,
                                E44613960CD6331000FADA75 /* HTMLSourceElement.h */,
                                E44613970CD6331000FADA75 /* HTMLSourceElement.idl */,
                A83B79080CCAFF2B000B0825 /* HTML */ = {
                        isa = PBXGroup;
                        children = (
+                               9B69D3B61B99100700E3512B /* JSHTMLSlotElement.cpp */,
+                               9B69D3B71B99100700E3512B /* JSHTMLSlotElement.h */,
                                31A795C41888BAD100382F90 /* JSANGLEInstancedArrays.cpp */,
                                31A795C51888BAD100382F90 /* JSANGLEInstancedArrays.h */,
                                BE8EF03E171C8FF9009B48C3 /* JSAudioTrack.cpp */,
                                A6D169631346B4C1000EB770 /* ShadowRoot.h */,
                                9B19B67E1B964E5200348745 /* ShadowRoot.idl */,
                                31741AAB16635E45008A5B7E /* SimulatedClickOptions.h */,
+                               9B532EA11BA928570038A827 /* SlotAssignment.cpp */,
+                               9B532EA21BA928570038A827 /* SlotAssignment.h */,
                                D01A27AB10C9BFD800026A42 /* SpaceSplitString.cpp */,
                                D01A27AC10C9BFD800026A42 /* SpaceSplitString.h */,
                                BC7FA62C0D1F0EFF00DB22A9 /* StaticNodeList.cpp */,
                                379E371713736A6600B9E919 /* QuotedPrintable.h in Headers */,
                                5A574F29131DB96D00471B88 /* QuotesData.h in Headers */,
                                B22279720D00BF220071B782 /* RadialGradientAttributes.h in Headers */,
+                               9B69D3B51B98FFE900E3512B /* HTMLSlotElement.h in Headers */,
                                F55B3DCC1251F12D003EF269 /* RadioInputType.h in Headers */,
                                B658FFA61522EFAA00DD5595 /* RadioNodeList.h in Headers */,
                                93F1991808245E59001E9ABC /* Range.h in Headers */,
                                078E093017D14D1C00420AA1 /* UserMediaRequest.h in Headers */,
                                7C3B79721908757B00B47A2D /* UserMessageHandler.h in Headers */,
                                7CE68345192143A800F4D928 /* UserMessageHandlerDescriptor.h in Headers */,
+                               9B532EA41BA928570038A827 /* SlotAssignment.h in Headers */,
                                7CE683471921821500F4D928 /* UserMessageHandlerDescriptorTypes.h in Headers */,
                                7C73FB08191EF417007DE061 /* UserMessageHandlersNamespace.h in Headers */,
                                BCA2B061105047600043BD1C /* UserScript.h in Headers */,
                                5D21A80313ECE5DF00BB7064 /* WebVTTParser.h in Headers */,
                                B10B6980140C174000BC1C26 /* WebVTTToken.h in Headers */,
                                B10B6982140C174000BC1C26 /* WebVTTTokenizer.h in Headers */,
+                               9B69D3B91B99100700E3512B /* JSHTMLSlotElement.h in Headers */,
                                CD8203101395ACE700F956C6 /* WebWindowAnimation.h in Headers */,
                                A91C9FC31B659A6700AFFD54 /* AccessibilityTreeItem.h in Headers */,
                                F55B3DE01251F12D003EF269 /* WeekInputType.h in Headers */,
                                E1A8E56617552B2A007488E7 /* CFURLExtras.cpp in Sources */,
                                97BC69DC1505F076001B74AC /* ChangeVersionWrapper.cpp in Sources */,
                                FD315FFE12B0267600C1A359 /* ChannelMergerNode.cpp in Sources */,
+                               9B532EA31BA928570038A827 /* SlotAssignment.cpp in Sources */,
                                FD31600112B0267600C1A359 /* ChannelSplitterNode.cpp in Sources */,
                                6550B69F099DF0270090D781 /* CharacterData.cpp in Sources */,
                                9326DC0C09DAD5D600AFC847 /* CharsetData.cpp in Sources */,
                                FBC220DF1237FBEB00BCF788 /* GraphicsContext3DOpenGL.cpp in Sources */,
                                96ABA42314BCB80E00D56204 /* GraphicsContext3DOpenGLCommon.cpp in Sources */,
                                B2ED97710B1F55CE00257D0F /* GraphicsContextCG.cpp in Sources */,
+                               9B69D3B81B99100700E3512B /* JSHTMLSlotElement.cpp in Sources */,
                                B277B4040B22F37C0004BEC6 /* GraphicsContextMac.mm in Sources */,
                                0F580B0C0F12A2690051D689 /* GraphicsLayer.cpp in Sources */,
                                499B3ED6128CD31400E726C2 /* GraphicsLayerCA.cpp in Sources */,
                                D66817FA166FE6D700FA07B4 /* HTMLTemplateElement.cpp in Sources */,
                                A81369D7097374F600D74463 /* HTMLTextAreaElement.cpp in Sources */,
                                9BC6C21C13CCC97B008E0337 /* HTMLTextFormControlElement.cpp in Sources */,
+                               9B69D3B41B98FFE900E3512B /* HTMLSlotElement.cpp in Sources */,
                                A871DC290A15205700B12A68 /* HTMLTitleElement.cpp in Sources */,
                                977B3877122883E900B81FF8 /* HTMLTokenizer.cpp in Sources */,
                                0707568B142262D600414161 /* HTMLTrackElement.cpp in Sources */,
index b7c86e5..8be3377 100644 (file)
@@ -1236,6 +1236,14 @@ void Element::attributeChanged(const QualifiedName& name, const AtomicString& ol
             elementData()->setHasNameAttribute(!newValue.isNull());
         else if (name == HTMLNames::pseudoAttr)
             shouldInvalidateStyle |= testShouldInvalidateStyle && isInShadowTree();
+#if ENABLE(SHADOW_DOM)
+        else if (name == HTMLNames::slotAttr) {
+            if (auto* parent = parentElement()) {
+                if (auto* shadowRoot = parent->shadowRoot())
+                    shadowRoot->invalidateSlotAssignments();
+            }
+        }
+#endif
     }
 
     parseAttribute(name, newValue);
@@ -1851,6 +1859,9 @@ void Element::childrenChanged(const ChildChange& change)
     if (ShadowRoot* shadowRoot = this->shadowRoot()) {
         if (auto* distributor = shadowRoot->distributor())
             distributor->invalidateDistribution(this);
+#if ENABLE(SHADOW_DOM)
+        shadowRoot->invalidateSlotAssignments();
+#endif
     }
 }
 
index b3350b3..f08dc04 100644 (file)
 #if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT
     [Conditional=SHADOW_DOM, RaisesException] ShadowRoot attachShadow(Dictionary options);
     [Conditional=SHADOW_DOM, ImplementedAs=bindingShadowRoot] readonly attribute ShadowRoot shadowRoot;
+    [Conditional=SHADOW_DOM, Reflect] attribute DOMString slot;
 #endif
 
     // Event Handlers
index 7069ada..ca87b04 100644 (file)
@@ -45,6 +45,7 @@
 #include "HTMLCollection.h"
 #include "HTMLElement.h"
 #include "HTMLImageElement.h"
+#include "HTMLSlotElement.h"
 #include "HTMLStyleElement.h"
 #include "InsertionPoint.h"
 #include "InspectorController.h"
@@ -1071,6 +1072,21 @@ ShadowRoot* Node::containingShadowRoot() const
     return is<ShadowRoot>(root) ? downcast<ShadowRoot>(&root) : nullptr;
 }
 
+#if ENABLE(SHADOW_DOM)
+HTMLSlotElement* Node::assignedSlot() const
+{
+    auto* parent = parentElement();
+    if (!parent)
+        return nullptr;
+
+    auto* shadowRoot = parent->shadowRoot();
+    if (!shadowRoot || shadowRoot->type() != ShadowRoot::Type::Open)
+        return nullptr;
+
+    return shadowRoot->findAssignedSlot(*this);
+}
+#endif
+
 bool Node::isInUserAgentShadowTree() const
 {
     auto* shadowRoot = containingShadowRoot();
index 2416801..0c3a1d9 100644 (file)
@@ -54,6 +54,7 @@ class FloatPoint;
 class Frame;
 class HTMLInputElement;
 class HTMLQualifiedName;
+class HTMLSlotElement;
 class IntRect;
 class KeyboardEvent;
 class MathMLQualifiedName;
@@ -287,6 +288,10 @@ public:
     ShadowRoot* containingShadowRoot() const;
     ShadowRoot* shadowRoot() const;
 
+#if ENABLE(SHADOW_DOM)
+    HTMLSlotElement* assignedSlot() const;
+#endif
+
     // Returns null, a child of ShadowRoot, or a legacy shadow root.
     Node* nonBoundaryShadowTreeRootNode();
 
index 7093023..de3b54b 100644 (file)
@@ -30,4 +30,8 @@
 ] interface NonDocumentTypeChildNode {
     readonly attribute Element previousElementSibling;
     readonly attribute Element nextElementSibling;
+
+#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT
+    [Conditional=SHADOW_DOM] readonly attribute HTMLSlotElement assignedSlot;
+#endif
 };
index 8634afb..b7fd972 100644 (file)
@@ -31,6 +31,7 @@
 #include "InsertionPoint.h"
 #include "RenderElement.h"
 #include "RuntimeEnabledFeatures.h"
+#include "SlotAssignment.h"
 #include "StyleResolver.h"
 #include "markup.h"
 
@@ -39,6 +40,9 @@ namespace WebCore {
 struct SameSizeAsShadowRoot : public DocumentFragment, public TreeScope {
     unsigned countersAndFlags[1];
     void* host;
+#if ENABLE(SHADOW_DOM)
+    void* slotAssignment;
+#endif
 };
 
 COMPILE_ASSERT(sizeof(ShadowRoot) == sizeof(SameSizeAsShadowRoot), shadowroot_should_stay_small);
@@ -128,4 +132,41 @@ void ShadowRoot::removeAllEventListeners()
         node->removeAllEventListeners();
 }
 
+#if ENABLE(SHADOW_DOM)
+
+HTMLSlotElement* ShadowRoot::findAssignedSlot(const Node& node)
+{
+    if (!m_slotAssignments)
+        return nullptr;
+    return m_slotAssignments->findAssignedSlot(node, *this);
+}
+
+void ShadowRoot::addSlotElementByName(const AtomicString& name, HTMLSlotElement& slot)
+{
+    if (!m_slotAssignments)
+        m_slotAssignments = std::make_unique<SlotAssignment>();
+
+    return m_slotAssignments->addSlotElementByName(name, slot);
+}
+
+void ShadowRoot::removeSlotElementByName(const AtomicString& name, HTMLSlotElement& slot)
+{
+    return m_slotAssignments->removeSlotElementByName(name, slot);
+}
+
+void ShadowRoot::invalidateSlotAssignments()
+{
+    if (m_slotAssignments)
+        m_slotAssignments->invalidate();
+}
+
+const Vector<Node*>* ShadowRoot::assignedNodesForSlot(const HTMLSlotElement& slot)
+{
+    if (!m_slotAssignments)
+        return nullptr;
+    return m_slotAssignments->assignedNodesForSlot(slot, *this);
+}
+
+#endif
+
 }
index 5d5771c..14474df 100644 (file)
@@ -37,6 +37,8 @@
 namespace WebCore {
 
 class ContentDistributor;
+class HTMLSlotElement;
+class SlotAssignment;
 
 class ShadowRoot : public DocumentFragment, public TreeScope {
 public:
@@ -72,6 +74,17 @@ public:
 
     virtual ContentDistributor* distributor() { return nullptr; }
 
+#if ENABLE(SHADOW_DOM)
+    HTMLSlotElement* findAssignedSlot(const Node&);
+
+    void addSlotElementByName(const AtomicString&, HTMLSlotElement&);
+    void removeSlotElementByName(const AtomicString&, HTMLSlotElement&);
+
+    void invalidateSlotAssignments();
+
+    const Vector<Node*>* assignedNodesForSlot(const HTMLSlotElement&);
+#endif
+
 protected:
     ShadowRoot(Document&, Type);
 
@@ -87,6 +100,10 @@ private:
     Type m_type;
 
     Element* m_host;
+
+#if ENABLE(SHADOW_DOM)
+    std::unique_ptr<SlotAssignment> m_slotAssignments;
+#endif
 };
 
 inline Element* ShadowRoot::activeElement() const
diff --git a/Source/WebCore/dom/SlotAssignment.cpp b/Source/WebCore/dom/SlotAssignment.cpp
new file mode 100644 (file)
index 0000000..90816d5
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SlotAssignment.h"
+
+#if ENABLE(SHADOW_DOM)
+
+#include "HTMLSlotElement.h"
+#include "TypedElementDescendantIterator.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+static const AtomicString& treatNullAsEmpty(const AtomicString& name)
+{
+    return name == nullAtom ? emptyAtom : name;
+}
+
+HTMLSlotElement* SlotAssignment::findAssignedSlot(const Node& node, ShadowRoot& shadowRoot)
+{
+    const AtomicString& name = is<Element>(node) ? downcast<Element>(node).fastGetAttribute(slotAttr) : emptyAtom;
+
+    auto it = m_slots.find(treatNullAsEmpty(name));
+    if (it == m_slots.end())
+        return nullptr;
+
+    return findFirstSlotElement(*it->value, shadowRoot);
+}
+
+void SlotAssignment::addSlotElementByName(const AtomicString& name, HTMLSlotElement& slotElement)
+{
+#ifndef NDEBUG
+    ASSERT(!m_slotElementsForConsistencyCheck.contains(&slotElement));
+    m_slotElementsForConsistencyCheck.add(&slotElement);
+#endif
+
+    auto addResult = m_slots.add(treatNullAsEmpty(name), std::unique_ptr<SlotInfo>());
+    if (addResult.isNewEntry) {
+        addResult.iterator->value = std::make_unique<SlotInfo>(slotElement);
+        return;
+    }
+
+    auto& slotInfo = *addResult.iterator->value;
+
+    if (!slotInfo.hasSlotElements())
+        slotInfo.element = &slotElement;
+    else {
+        slotInfo.element = nullptr;
+#ifndef NDEBUG
+        m_needsToResolveSlotElements = true;
+#endif
+    }
+    slotInfo.elementCount++;
+}
+
+void SlotAssignment::removeSlotElementByName(const AtomicString& name, HTMLSlotElement& slotElement)
+{
+#ifndef NDEBUG
+    ASSERT(m_slotElementsForConsistencyCheck.contains(&slotElement));
+    m_slotElementsForConsistencyCheck.remove(&slotElement);
+#endif
+
+    auto it = m_slots.find(treatNullAsEmpty(name));
+    RELEASE_ASSERT(it != m_slots.end());
+
+    auto& slotInfo = *it->value;
+    RELEASE_ASSERT(slotInfo.hasSlotElements());
+
+    slotInfo.elementCount--;
+    if (slotInfo.element == &slotElement) {
+        slotInfo.element = nullptr;
+#ifndef NDEBUG
+        m_needsToResolveSlotElements = true;
+#endif
+    }
+    ASSERT(slotInfo.element || m_needsToResolveSlotElements);
+}
+
+const Vector<Node*>* SlotAssignment::assignedNodesForSlot(const HTMLSlotElement& slotElement, ShadowRoot& shadowRoot)
+{
+    if (!m_slotAssignmentsIsValid)
+        assignSlots(shadowRoot);
+
+    const AtomicString& slotName = slotElement.fastGetAttribute(nameAttr);
+    auto it = m_slots.find(treatNullAsEmpty(slotName));
+    if (it == m_slots.end())
+        return nullptr;
+
+    auto& slotInfo = *it->value;
+
+    if (!slotInfo.assignedNodes.size())
+        return nullptr;
+
+    RELEASE_ASSERT(slotInfo.hasSlotElements());
+    if (slotInfo.hasDuplicatedSlotElements() && findFirstSlotElement(slotInfo, shadowRoot) != &slotElement)
+        return nullptr;
+
+    return &slotInfo.assignedNodes;
+}
+
+HTMLSlotElement* SlotAssignment::findFirstSlotElement(SlotInfo& slotInfo, ShadowRoot& shadowRoot)
+{
+    if (slotInfo.shouldResolveSlotElement())
+        resolveAllSlotElements(shadowRoot);
+
+#ifndef NDEBUG
+    ASSERT(!slotInfo.element || m_slotElementsForConsistencyCheck.contains(slotInfo.element));
+#endif
+
+    return slotInfo.element;
+}
+
+void SlotAssignment::resolveAllSlotElements(ShadowRoot& shadowRoot)
+{
+#ifndef NDEBUG
+    ASSERT(m_needsToResolveSlotElements);
+    m_needsToResolveSlotElements = false;
+#endif
+
+    // FIXME: It's inefficient to reset all values. We should be able to void this in common case.
+    for (auto& entry : m_slots)
+        entry.value->element = nullptr;
+
+    unsigned slotCount = m_slots.size();
+    for (auto& slotElement : descendantsOfType<HTMLSlotElement>(shadowRoot)) {
+        const AtomicString& slotName = slotElement.fastGetAttribute(nameAttr);
+
+        auto it = m_slots.find(treatNullAsEmpty(slotName));
+        RELEASE_ASSERT(it != m_slots.end());
+
+        SlotInfo& slotInfo = *it->value;
+        bool hasSeenSlotWithSameName = !!slotInfo.element;
+        if (hasSeenSlotWithSameName)
+            continue;
+
+        slotInfo.element = &slotElement;
+        slotCount--;
+        if (!slotCount)
+            break;
+    }
+}
+
+void SlotAssignment::assignSlots(ShadowRoot& shadowRoot)
+{
+    ASSERT(!m_slotAssignmentsIsValid);
+    m_slotAssignmentsIsValid = true;
+
+    auto* host = shadowRoot.host();
+    RELEASE_ASSERT(host);
+
+    for (auto& entry : m_slots)
+        entry.value->assignedNodes.shrink(0);
+
+    auto defaultSlotEntry = m_slots.find(emptyAtom);
+
+    for (Node* child = host->firstChild(); child; child = child->nextSibling()) {
+        if (is<Element>(child)) {
+            auto& slotName = downcast<Element>(*child).fastGetAttribute(slotAttr);
+            if (!slotName.isNull()) {
+                auto addResult = m_slots.add(treatNullAsEmpty(slotName), std::make_unique<SlotInfo>());
+                addResult.iterator->value->assignedNodes.append(child);
+                continue;
+            }
+        }
+        if (defaultSlotEntry != m_slots.end())
+            defaultSlotEntry->value->assignedNodes.append(child);
+    }
+
+    for (auto& entry : m_slots)
+        entry.value->assignedNodes.shrinkToFit();
+}
+
+}
+
+#endif
+
diff --git a/Source/WebCore/dom/SlotAssignment.h b/Source/WebCore/dom/SlotAssignment.h
new file mode 100644 (file)
index 0000000..048b93b
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SlotAssignment_h
+#define SlotAssignment_h
+
+#if ENABLE(SHADOW_DOM)
+
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/Vector.h>
+#include <wtf/text/AtomicString.h>
+#include <wtf/text/AtomicStringHash.h>
+
+namespace WebCore {
+
+class HTMLSlotElement;
+class Node;
+class ShadowRoot;
+
+class SlotAssignment {
+    WTF_MAKE_NONCOPYABLE(SlotAssignment);
+public:
+    SlotAssignment() { }
+
+    HTMLSlotElement* findAssignedSlot(const Node&, ShadowRoot&);
+
+    void addSlotElementByName(const AtomicString&, HTMLSlotElement&);
+    void removeSlotElementByName(const AtomicString&, HTMLSlotElement&);
+
+    const Vector<Node*>* assignedNodesForSlot(const HTMLSlotElement&, ShadowRoot&);
+
+    void invalidate() { m_slotAssignmentsIsValid = false; }
+
+private:
+    struct SlotInfo {
+        SlotInfo() { }
+        SlotInfo(HTMLSlotElement& slotElement)
+            : element(&slotElement)
+            , elementCount(1)
+        { }
+
+        bool hasSlotElements() { return !!elementCount; }
+        bool hasDuplicatedSlotElements() { return elementCount > 1; }
+        bool shouldResolveSlotElement() { return !element && elementCount; }
+
+        HTMLSlotElement* element { nullptr };
+        unsigned elementCount { 0 };
+        Vector<Node*> assignedNodes;
+    };
+
+    HTMLSlotElement* findFirstSlotElement(SlotInfo&, ShadowRoot&);
+    void resolveAllSlotElements(ShadowRoot&);
+    void assignSlots(ShadowRoot&);
+
+    HashMap<AtomicString, std::unique_ptr<SlotInfo>> m_slots;
+
+#ifndef NDEBUG
+    HashSet<HTMLSlotElement*> m_slotElementsForConsistencyCheck;
+    bool m_needsToResolveSlotElements { false };
+#endif
+
+    bool m_slotAssignmentsIsValid { false };
+};
+
+}
+
+#endif
+
+#endif /* SlotAssignment_h */
index e4d77a6..12c5f5d 100644 (file)
@@ -322,6 +322,7 @@ selected
 shape
 size
 sizes
+slot
 sortable
 sortdirection
 span
diff --git a/Source/WebCore/html/HTMLSlotElement.cpp b/Source/WebCore/html/HTMLSlotElement.cpp
new file mode 100644 (file)
index 0000000..c40315d
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "HTMLSlotElement.h"
+
+#include "ElementChildIterator.h"
+#include "HTMLNames.h"
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+Ref<HTMLSlotElement> HTMLSlotElement::create(const QualifiedName& tagName, Document& document)
+{
+    return adoptRef(*new HTMLSlotElement(tagName, document));
+}
+
+HTMLSlotElement::HTMLSlotElement(const QualifiedName& tagName, Document& document)
+    : HTMLElement(tagName, document)
+{
+    ASSERT(hasTagName(slotTag));
+}
+
+HTMLSlotElement::InsertionNotificationRequest HTMLSlotElement::insertedInto(ContainerNode& insertionPoint)
+{
+    auto insertionResult = HTMLElement::insertedInto(insertionPoint);
+    ASSERT_UNUSED(insertionResult, insertionResult == InsertionDone);
+
+    if (auto shadowRoot = containingShadowRoot())
+        shadowRoot->addSlotElementByName(fastGetAttribute(nameAttr), *this);
+
+    return InsertionDone;
+}
+
+void HTMLSlotElement::removedFrom(ContainerNode& insertionPoint)
+{
+    // Can't call containingShadowRoot() here since this node has already been disconnected from the parent.
+    if (isInShadowTree()) {
+        auto& oldShadowRoot = downcast<ShadowRoot>(insertionPoint.treeScope().rootNode());
+        oldShadowRoot.removeSlotElementByName(fastGetAttribute(nameAttr), *this);
+    }
+
+    HTMLElement::removedFrom(insertionPoint);
+}
+
+void HTMLSlotElement::attributeChanged(const QualifiedName& name, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason reason)
+{
+    HTMLElement::attributeChanged(name, oldValue, newValue, reason);
+
+    if (name == nameAttr) {
+        if (auto* shadowRoot = containingShadowRoot()) {
+            shadowRoot->removeSlotElementByName(oldValue, *this);
+            shadowRoot->addSlotElementByName(newValue, *this);
+        }
+    }
+}
+
+Vector<RefPtr<Node>> HTMLSlotElement::getDistributedNodes() const
+{
+    Vector<RefPtr<Node>> distributedNodes;
+
+    if (auto shadowRoot = containingShadowRoot()) {
+        if (auto assignedNodes = shadowRoot->assignedNodesForSlot(*this)) {
+            for (auto* node : *assignedNodes)
+                distributedNodes.append(node);
+        }
+    }
+
+    return distributedNodes;
+}
+
+}
diff --git a/Source/WebCore/html/HTMLSlotElement.h b/Source/WebCore/html/HTMLSlotElement.h
new file mode 100644 (file)
index 0000000..3c0327f
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HTMLSlotElement_h
+#define HTMLSlotElement_h
+
+#include "HTMLElement.h"
+#include "InsertionPoint.h"
+#include "Range.h"
+
+namespace WebCore {
+
+class HTMLSlotElement final : public HTMLElement {
+public:
+    static Ref<HTMLSlotElement> create(const QualifiedName&, Document&);
+
+    Vector<RefPtr<Node>> getDistributedNodes() const;
+
+private:
+    HTMLSlotElement(const QualifiedName&, Document&);
+
+    virtual InsertionNotificationRequest insertedInto(ContainerNode&) override;
+    virtual void removedFrom(ContainerNode&) override;
+    virtual void attributeChanged(const QualifiedName&, const AtomicString& oldValue, const AtomicString& newValue, AttributeModificationReason) override;
+};
+
+}
+
+#endif
diff --git a/Source/WebCore/html/HTMLSlotElement.idl b/Source/WebCore/html/HTMLSlotElement.idl
new file mode 100644 (file)
index 0000000..b056cef
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// https://w3c.github.io/webcomponents/spec/shadow/#idl-def-HTMLSlotElement
+[
+    Conditional=SHADOW_DOM,
+    JSGenerateToNativeObject
+] interface HTMLSlotElement : HTMLElement {
+
+    [Reflect] attribute DOMString name;
+    sequence<Node> getDistributedNodes();
+
+};
index 91387b0..5f54182 100644 (file)
@@ -111,6 +111,7 @@ samp interfaceName=HTMLElement
 script constructorNeedsCreatedByParser
 section interfaceName=HTMLElement
 select constructorNeedsFormElement
+slot conditional=SHADOW_DOM
 small interfaceName=HTMLElement
 source wrapperOnlyIfMediaIsAvailable, conditional=VIDEO
 span