Title: [211024] trunk
Revision
211024
Author
[email protected]
Date
2017-01-21 21:48:28 -0800 (Sat, 21 Jan 2017)

Log Message

_javascript_ for-of does not work on a lot of collection types (e.g. HTMLCollection)
https://bugs.webkit.org/show_bug.cgi?id=167091

Reviewed by Darin Adler.

Source/_javascript_Core:

Update Array methods to throw a TypeError when (this === null || this === undefined)
instead of when (this == null). This is because (this == null) returns true for types
that masquerades as undefined (such as document.all) and this prevented use of the
Array API on such types. The specification only stays to use ToObject(), which throws
when the input is undefined or null.

The corresponding specification is at:
- https://www.ecma-international.org/ecma-262/7.0/index.html#sec-array.prototype.values
- https://www.ecma-international.org/ecma-262/7.0/index.html#sec-toobject

* builtins/ArrayPrototype.js:
(values):
(keys):
(entries):
(reduce):
(reduceRight):
(every):
(forEach):
(filter):
(map):
(some):
(fill):
(find):
(findIndex):
(includes):
(sort):
(concatSlowPath):
(copyWithin):

Source/WebCore:

As per the Web IDL specification [1], https://heycam.github.io/webidl/#es-iterator
an interface should get an iterator if it has:
- an indexed property getter and an integer-typed attribute named "length".

We now comply with this part of the Web IDL specification. This adds an iterator
to the following interfaces:
- AudioTrackList, ClientRectList, CSSRuleList, CSSStyleDeclaration, CSSValueList,
  MimeTypeArray, WebKitNamedFlowCollection, Plugin, PluginArray, DOMStringList,
  FileList, HTMLAllCollection, HTMLCollection, HTMLFormElement, HTMLOptionsCollection,
  HTMLSelectElement, MediaList, NamedNodeMap, SourceBufferList, StyleSheetList,
  TextTrackCueList, TextTrackList, TouchList, VideoTrackList, VTTRegionList.

As a result, it is now possible to use `for ... of` for those types.

Tests: fast/dom/FileList-iterator.html
       fast/dom/collection-iterators.html
       fast/dom/document-all-undefined.html
       fast/events/touch/ios/touchlist-iterator.html

* bindings/scripts/CodeGeneratorJS.pm:
(GetAttributeWithName):
(InterfaceNeedsIterator):
(GenerateImplementation):
(addIterableProperties):

LayoutTests:

* fast/dom/FileList-iterator-expected.txt: Added.
* fast/dom/FileList-iterator.html: Added.
* fast/dom/collection-iterators-expected.txt: Added.
* fast/dom/collection-iterators.html: Added.
* fast/events/touch/ios/touchlist-iterator-expected.txt: Added.
* fast/events/touch/ios/touchlist-iterator.html: Added.
Add layout test coverage for all types that gained an iterator.

* fast/dom/document-all-undefined-expected.txt: Added.
* fast/dom/document-all-undefined.html: Added.
Add layout test to cover the fact that HTMLAllCollection masquerades as
undefined, as per:
- https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-all

* inspector/model/remote-object-get-properties-expected.txt:
Rebaseline now that there is an extra Symbol.iterator property.

* platform/wk2/TestExpectations:
Skip that requires beginDragWithFiles() as this is unimplemented in
WebKitTestRunner.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (211023 => 211024)


--- trunk/LayoutTests/ChangeLog	2017-01-22 05:19:17 UTC (rev 211023)
+++ trunk/LayoutTests/ChangeLog	2017-01-22 05:48:28 UTC (rev 211024)
@@ -1,5 +1,33 @@
 2017-01-21  Chris Dumez  <[email protected]>
 
+        _javascript_ for-of does not work on a lot of collection types (e.g. HTMLCollection)
+        https://bugs.webkit.org/show_bug.cgi?id=167091
+
+        Reviewed by Darin Adler.
+
+        * fast/dom/FileList-iterator-expected.txt: Added.
+        * fast/dom/FileList-iterator.html: Added.
+        * fast/dom/collection-iterators-expected.txt: Added.
+        * fast/dom/collection-iterators.html: Added.
+        * fast/events/touch/ios/touchlist-iterator-expected.txt: Added.
+        * fast/events/touch/ios/touchlist-iterator.html: Added.
+        Add layout test coverage for all types that gained an iterator.
+
+        * fast/dom/document-all-undefined-expected.txt: Added.
+        * fast/dom/document-all-undefined.html: Added.
+        Add layout test to cover the fact that HTMLAllCollection masquerades as
+        undefined, as per:
+        - https://html.spec.whatwg.org/multipage/obsolete.html#dom-document-all
+
+        * inspector/model/remote-object-get-properties-expected.txt:
+        Rebaseline now that there is an extra Symbol.iterator property.
+
+        * platform/wk2/TestExpectations:
+        Skip that requires beginDragWithFiles() as this is unimplemented in
+        WebKitTestRunner.
+
+2017-01-21  Chris Dumez  <[email protected]>
+
         innerText should replace existing text node
         https://bugs.webkit.org/show_bug.cgi?id=167116
 

Added: trunk/LayoutTests/fast/dom/FileList-iterator-expected.txt (0 => 211024)


--- trunk/LayoutTests/fast/dom/FileList-iterator-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/FileList-iterator-expected.txt	2017-01-22 05:48:28 UTC (rev 211024)
@@ -0,0 +1,17 @@
+
+Tests that FileList objects have an iterator.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS fileList.__proto__ is FileList.prototype
+PASS Symbol.iterator in fileList is true
+PASS forOfSucceeded is true
+PASS 'entries' in fileList is false
+PASS 'keys' in fileList is false
+PASS 'forEach' in fileList is false
+PASS 'values' in fileList is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/dom/FileList-iterator.html (0 => 211024)


--- trunk/LayoutTests/fast/dom/FileList-iterator.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/FileList-iterator.html	2017-01-22 05:48:28 UTC (rev 211024)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src=""
+</head>
+<body>
+<input type="file" _ondrop_="dropped(event)"></input>
+<div id="console"></div>
+<script>
+description("Tests that FileList objects have an iterator.");
+jsTestIsAsync = true;
+
+function runTest()
+{
+    var inputElement = document.getElementsByTagName('input')[0];
+    eventSender.beginDragWithFiles(['test.txt']);
+    eventSender.mouseMoveTo(inputElement.offsetLeft + inputElement.offsetWidth / 2,
+                            inputElement.offsetTop + inputElement.offsetHeight / 2);
+    eventSender.mouseUp();
+}
+
+function dropped(event)
+{
+    fileList = event.dataTransfer.files;
+    shouldBe("fileList.__proto__", "FileList.prototype");
+    shouldBeTrue("Symbol.iterator in fileList");
+    forOfSucceeded = false;
+    try {
+        for (var file of fileList) { }
+        forOfSucceeded = true;
+    } catch (e) {
+        forOfSucceeded = false;
+    }
+    shouldBeTrue("forOfSucceeded");
+
+    shouldBeFalse("'entries' in fileList");
+    shouldBeFalse("'keys' in fileList");
+    shouldBeFalse("'forEach' in fileList");
+    shouldBeFalse("'values' in fileList");
+
+    finishJSTest();
+}
+
+runTest();
+
+</script>
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/dom/collection-iterators-expected.txt (0 => 211024)


--- trunk/LayoutTests/fast/dom/collection-iterators-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/collection-iterators-expected.txt	2017-01-22 05:48:28 UTC (rev 211024)
@@ -0,0 +1,214 @@
+Tests that interfaces with an indexed getter and an integer-type length attribute get an iterator.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+* AudioTrackList
+PASS obj.__proto__ is AudioTrackList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* ClientRectList
+PASS obj.__proto__ is ClientRectList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* CSSRuleList
+PASS obj.__proto__ is CSSRuleList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* CSSStyleDeclaration
+PASS obj.__proto__ is CSSStyleDeclaration.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* CSSValueList
+PASS obj.__proto__ is CSSValueList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* MimeTypeArray
+PASS obj.__proto__ is MimeTypeArray.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* WebKitNamedFlowCollection
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* Plugin
+PASS obj.__proto__ is Plugin.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* PluginArray
+PASS obj.__proto__ is PluginArray.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* DOMStringList
+PASS obj.__proto__ is DOMStringList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* HTMLAllCollection
+PASS obj.__proto__ is HTMLAllCollection.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* HTMLCollection
+PASS obj.__proto__ is HTMLCollection.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* HTMLFormElement
+PASS obj.__proto__ is HTMLFormElement.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* HTMLOptionsCollection
+PASS obj.__proto__ is HTMLOptionsCollection.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* HTMLSelectElement
+PASS obj.__proto__ is HTMLSelectElement.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* MediaList
+PASS obj.__proto__ is MediaList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* NamedNodeMap
+PASS obj.__proto__ is NamedNodeMap.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* SourceBufferList
+PASS obj.__proto__ is SourceBufferList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* StyleSheetList
+PASS obj.__proto__ is StyleSheetList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* TextTrackCueList
+PASS obj.__proto__ is TextTrackCueList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* TextTrackList
+PASS obj.__proto__ is TextTrackList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* VideoTrackList
+PASS obj.__proto__ is VideoTrackList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* VTTRegionList
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/dom/collection-iterators.html (0 => 211024)


--- trunk/LayoutTests/fast/dom/collection-iterators.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/collection-iterators.html	2017-01-22 05:48:28 UTC (rev 211024)
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style media="screen and (min-width: 480px)">
+body {
+    background-position: 10px 20px;
+}
+</style>
+</head>
+<body>
+<script src=""
+<script>
+description("Tests that interfaces with an indexed getter and an integer-type length attribute get an iterator.");
+
+function checkHasIterator(interfaceName, _obj)
+{
+    obj = _obj;
+    debug("* " + interfaceName);
+    if (interfaceName in window)
+        shouldBe("obj.__proto__", "" + interfaceName + ".prototype");
+    shouldBeTrue("Symbol.iterator in obj");
+    forOfSuccess = true;
+    try {
+        for (var p of obj) { }
+    } catch (e) {
+        debug("Exception: " + e);
+        forOfSuccess = false;
+    }
+    if (forOfSuccess)
+        testPassed("for..of did not throw an exception");
+    else
+        testFailed("for..of threw an exception");
+
+    shouldBeFalse("'entries' in obj");
+    shouldBeFalse("'keys' in obj");
+    shouldBeFalse("'forEach' in obj");
+    shouldBeFalse("'values' in obj");
+
+    debug("");
+}
+
+var media = document.createElement("video");
+checkHasIterator("AudioTrackList", media.audioTracks);
+checkHasIterator("ClientRectList", document.body.getClientRects());
+checkHasIterator("CSSRuleList", window.getMatchedCSSRules(document.body));
+checkHasIterator("CSSStyleDeclaration", window.getComputedStyle(document.body));
+checkHasIterator("CSSValueList", window.getComputedStyle(document.body).getPropertyCSSValue('background-position'));
+checkHasIterator("MimeTypeArray", navigator.mimeTypes);
+checkHasIterator("WebKitNamedFlowCollection", document.webkitGetNamedFlows());
+if (navigator.plugins.length)
+    checkHasIterator("Plugin", navigator.plugins[0]);
+checkHasIterator("PluginArray", navigator.plugins);
+checkHasIterator("DOMStringList", location.ancestorOrigins);
+checkHasIterator("HTMLAllCollection", document.all);
+checkHasIterator("HTMLCollection", document.getElementsByTagName("body"));
+checkHasIterator("HTMLFormElement", document.createElement("form"));
+checkHasIterator("HTMLOptionsCollection", document.createElement("select").options);
+checkHasIterator("HTMLSelectElement", document.createElement("select"));
+checkHasIterator("MediaList", document.getElementsByTagName("style")[0].sheet.media);
+checkHasIterator("NamedNodeMap", document.body.attributes);
+if ('SourceBufferList' in window)
+    checkHasIterator("SourceBufferList", (new MediaSource()).sourceBuffers);
+checkHasIterator("StyleSheetList", document.styleSheets);
+checkHasIterator("TextTrackCueList", document.createElement("video").addTextTrack("subtitles").cues);
+checkHasIterator("TextTrackList", media.textTracks);
+checkHasIterator("VideoTrackList", media.videoTracks);
+checkHasIterator("VTTRegionList", document.createElement("video").addTextTrack("subtitles").regions);
+</script>
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/dom/document-all-undefined-expected.txt (0 => 211024)


--- trunk/LayoutTests/fast/dom/document-all-undefined-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/document-all-undefined-expected.txt	2017-01-22 05:48:28 UTC (rev 211024)
@@ -0,0 +1,15 @@
+Tests that document.all masquerades as undefined.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS ToBoolean() returned false for document.all
+PASS document.all == undefined is true
+PASS document.all === undefined is false
+PASS document.all == null is true
+PASS document.all === null is false
+PASS typeof document.all is "undefined"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/dom/document-all-undefined.html (0 => 211024)


--- trunk/LayoutTests/fast/dom/document-all-undefined.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/document-all-undefined.html	2017-01-22 05:48:28 UTC (rev 211024)
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Tests that document.all masquerades as undefined.");
+
+if (document.all)
+    testFailed("ToBoolean() should return false for document.all");
+else
+    testPassed("ToBoolean() returned false for document.all");
+
+shouldBeTrue("document.all == undefined");
+shouldBeFalse("document.all === undefined");
+shouldBeTrue("document.all == null");
+shouldBeFalse("document.all === null");
+shouldBeEqualToString("typeof document.all", "undefined");
+</script>
+<script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/events/touch/ios/touchlist-iterator-expected.txt (0 => 211024)


--- trunk/LayoutTests/fast/events/touch/ios/touchlist-iterator-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/touch/ios/touchlist-iterator-expected.txt	2017-01-22 05:48:28 UTC (rev 211024)
@@ -0,0 +1,15 @@
+Tests that TouchList objects have an iterator
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Symbol.iterator in touchList is true
+PASS touch is touch1
+PASS 'entries' in touchList is false
+PASS 'keys' in touchList is false
+PASS 'forEach' in touchList is false
+PASS 'values' in touchList is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/events/touch/ios/touchlist-iterator.html (0 => 211024)


--- trunk/LayoutTests/fast/events/touch/ios/touchlist-iterator.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/touch/ios/touchlist-iterator.html	2017-01-22 05:48:28 UTC (rev 211024)
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Tests that TouchList objects have an iterator");
+
+var touch1 = new Touch(window, document.body, 1, 2, 3, 4);
+var touchList = new TouchList(touch1);
+shouldBeTrue("Symbol.iterator in touchList");
+for (var touch of touchList) {
+    shouldBe("touch", "touch1");
+}
+
+shouldBeFalse("'entries' in touchList");
+shouldBeFalse("'keys' in touchList");
+shouldBeFalse("'forEach' in touchList");
+shouldBeFalse("'values' in touchList");
+</script>
+<script src=""
+</body>
+</html>

Modified: trunk/LayoutTests/inspector/model/remote-object-get-properties-expected.txt (211023 => 211024)


--- trunk/LayoutTests/inspector/model/remote-object-get-properties-expected.txt	2017-01-22 05:19:17 UTC (rev 211023)
+++ trunk/LayoutTests/inspector/model/remote-object-get-properties-expected.txt	2017-01-22 05:48:28 UTC (rev 211024)
@@ -529,6 +529,7 @@
     remove
     item
     namedItem
+    Symbol(Symbol.iterator)
     toString
     toLocaleString
     valueOf

Added: trunk/LayoutTests/platform/ios-simulator/fast/dom/collection-iterators-expected.txt (0 => 211024)


--- trunk/LayoutTests/platform/ios-simulator/fast/dom/collection-iterators-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/platform/ios-simulator/fast/dom/collection-iterators-expected.txt	2017-01-22 05:48:28 UTC (rev 211024)
@@ -0,0 +1,196 @@
+Tests that interfaces with an indexed getter and an integer-type length attribute get an iterator.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+* AudioTrackList
+PASS obj.__proto__ is AudioTrackList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* ClientRectList
+PASS obj.__proto__ is ClientRectList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* CSSRuleList
+PASS obj.__proto__ is CSSRuleList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* CSSStyleDeclaration
+PASS obj.__proto__ is CSSStyleDeclaration.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* CSSValueList
+PASS obj.__proto__ is CSSValueList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* MimeTypeArray
+PASS obj.__proto__ is MimeTypeArray.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* WebKitNamedFlowCollection
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* PluginArray
+PASS obj.__proto__ is PluginArray.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* DOMStringList
+PASS obj.__proto__ is DOMStringList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* HTMLAllCollection
+PASS obj.__proto__ is HTMLAllCollection.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* HTMLCollection
+PASS obj.__proto__ is HTMLCollection.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* HTMLFormElement
+PASS obj.__proto__ is HTMLFormElement.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* HTMLOptionsCollection
+PASS obj.__proto__ is HTMLOptionsCollection.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* HTMLSelectElement
+PASS obj.__proto__ is HTMLSelectElement.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* MediaList
+PASS obj.__proto__ is MediaList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* NamedNodeMap
+PASS obj.__proto__ is NamedNodeMap.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* StyleSheetList
+PASS obj.__proto__ is StyleSheetList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* TextTrackCueList
+PASS obj.__proto__ is TextTrackCueList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* TextTrackList
+PASS obj.__proto__ is TextTrackList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* VideoTrackList
+PASS obj.__proto__ is VideoTrackList.prototype
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+* VTTRegionList
+PASS Symbol.iterator in obj is true
+PASS for..of did not throw an exception
+PASS 'entries' in obj is false
+PASS 'keys' in obj is false
+PASS 'forEach' in obj is false
+PASS 'values' in obj is false
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Modified: trunk/LayoutTests/platform/wk2/TestExpectations (211023 => 211024)


--- trunk/LayoutTests/platform/wk2/TestExpectations	2017-01-22 05:19:17 UTC (rev 211023)
+++ trunk/LayoutTests/platform/wk2/TestExpectations	2017-01-22 05:48:28 UTC (rev 211024)
@@ -599,6 +599,7 @@
 # https://bugs.webkit.org/show_bug.cgi?id=64285
 editing/pasteboard/file-drag-to-editable.html
 editing/pasteboard/file-input-files-access.html
+fast/dom/FileList-iterator.html
 fast/dom/Window/window-postmessage-clone-frames.html
 fast/dom/Window/window-postmessage-clone.html
 fast/events/data-transfer-files-attribute-identity.html

Modified: trunk/Source/_javascript_Core/ChangeLog (211023 => 211024)


--- trunk/Source/_javascript_Core/ChangeLog	2017-01-22 05:19:17 UTC (rev 211023)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-01-22 05:48:28 UTC (rev 211024)
@@ -1,3 +1,39 @@
+2017-01-21  Chris Dumez  <[email protected]>
+
+        _javascript_ for-of does not work on a lot of collection types (e.g. HTMLCollection)
+        https://bugs.webkit.org/show_bug.cgi?id=167091
+
+        Reviewed by Darin Adler.
+
+        Update Array methods to throw a TypeError when (this === null || this === undefined)
+        instead of when (this == null). This is because (this == null) returns true for types
+        that masquerades as undefined (such as document.all) and this prevented use of the
+        Array API on such types. The specification only stays to use ToObject(), which throws
+        when the input is undefined or null.
+
+        The corresponding specification is at:
+        - https://www.ecma-international.org/ecma-262/7.0/index.html#sec-array.prototype.values
+        - https://www.ecma-international.org/ecma-262/7.0/index.html#sec-toobject
+
+        * builtins/ArrayPrototype.js:
+        (values):
+        (keys):
+        (entries):
+        (reduce):
+        (reduceRight):
+        (every):
+        (forEach):
+        (filter):
+        (map):
+        (some):
+        (fill):
+        (find):
+        (findIndex):
+        (includes):
+        (sort):
+        (concatSlowPath):
+        (copyWithin):
+
 2017-01-21  Yusuke Suzuki  <[email protected]>
 
         [JSC] export JSC::importModule API for WebCore dynamic import

Modified: trunk/Source/_javascript_Core/builtins/ArrayPrototype.js (211023 => 211024)


--- trunk/Source/_javascript_Core/builtins/ArrayPrototype.js	2017-01-22 05:19:17 UTC (rev 211023)
+++ trunk/Source/_javascript_Core/builtins/ArrayPrototype.js	2017-01-22 05:48:28 UTC (rev 211024)
@@ -39,7 +39,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.values requires that |this| not be null or undefined");
 
     return new @createArrayIterator(@Object(this), "value", @arrayIteratorValueNext);
@@ -49,7 +49,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.keys requires that |this| not be null or undefined");
 
     return new @createArrayIterator(@Object(this), "key", @arrayIteratorKeyNext);
@@ -59,7 +59,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.entries requires that |this| not be null or undefined");
 
     return new @createArrayIterator(@Object(this), "key+value", @arrayIteratorKeyValueNext);
@@ -69,7 +69,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.reduce requires that |this| not be null or undefined");
 
     var array = @Object(this);
@@ -105,7 +105,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.reduceRight requires that |this| not be null or undefined");
 
     var array = @Object(this);
@@ -141,7 +141,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.every requires that |this| not be null or undefined");
     
     var array = @Object(this);
@@ -166,7 +166,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.forEach requires that |this| not be null or undefined");
     
     var array = @Object(this);
@@ -187,7 +187,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.filter requires that |this| not be null or undefined");
     
     var array = @Object(this);
@@ -236,7 +236,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.map requires that |this| not be null or undefined");
     
     var array = @Object(this);
@@ -282,7 +282,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.some requires that |this| not be null or undefined");
     
     var array = @Object(this);
@@ -305,7 +305,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.fill requires that |this| not be null or undefined");
 
     var array = @Object(this);
@@ -345,7 +345,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.find requires that |this| not be null or undefined");
     
     var array = @Object(this);
@@ -367,7 +367,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.findIndex requires that |this| not be null or undefined");
     
     var array = @Object(this);
@@ -388,7 +388,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.includes requires that |this| not be null or undefined");
 
     var array = @Object(this);
@@ -625,7 +625,7 @@
         bucketSort(array, 0, strings, 0);
     }
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.sort requires that |this| not be null or undefined");
 
     var array = @Object(this);
@@ -651,7 +651,7 @@
 {
     "use strict";
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.prototype.concat requires that |this| not be null or undefined");
 
     var currentElement = @Object(this);
@@ -741,7 +741,7 @@
         return (maybeNegativeZero < positive) ? maybeNegativeZero : positive;
     }
 
-    if (this == null)
+    if (this === null || this === @undefined)
         @throwTypeError("Array.copyWithin requires that |this| not be null or undefined");
 
     var array = @Object(this);

Modified: trunk/Source/WebCore/ChangeLog (211023 => 211024)


--- trunk/Source/WebCore/ChangeLog	2017-01-22 05:19:17 UTC (rev 211023)
+++ trunk/Source/WebCore/ChangeLog	2017-01-22 05:48:28 UTC (rev 211024)
@@ -1,5 +1,37 @@
 2017-01-21  Chris Dumez  <[email protected]>
 
+        _javascript_ for-of does not work on a lot of collection types (e.g. HTMLCollection)
+        https://bugs.webkit.org/show_bug.cgi?id=167091
+
+        Reviewed by Darin Adler.
+
+        As per the Web IDL specification [1], https://heycam.github.io/webidl/#es-iterator
+        an interface should get an iterator if it has:
+        - an indexed property getter and an integer-typed attribute named "length".
+
+        We now comply with this part of the Web IDL specification. This adds an iterator
+        to the following interfaces:
+        - AudioTrackList, ClientRectList, CSSRuleList, CSSStyleDeclaration, CSSValueList,
+          MimeTypeArray, WebKitNamedFlowCollection, Plugin, PluginArray, DOMStringList,
+          FileList, HTMLAllCollection, HTMLCollection, HTMLFormElement, HTMLOptionsCollection,
+          HTMLSelectElement, MediaList, NamedNodeMap, SourceBufferList, StyleSheetList,
+          TextTrackCueList, TextTrackList, TouchList, VideoTrackList, VTTRegionList.
+
+        As a result, it is now possible to use `for ... of` for those types.
+
+        Tests: fast/dom/FileList-iterator.html
+               fast/dom/collection-iterators.html
+               fast/dom/document-all-undefined.html
+               fast/events/touch/ios/touchlist-iterator.html
+
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (GetAttributeWithName):
+        (InterfaceNeedsIterator):
+        (GenerateImplementation):
+        (addIterableProperties):
+
+2017-01-21  Chris Dumez  <[email protected]>
+
         innerText should replace existing text node
         https://bugs.webkit.org/show_bug.cgi?id=167116
 

Modified: trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm (211023 => 211024)


--- trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm	2017-01-22 05:19:17 UTC (rev 211023)
+++ trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm	2017-01-22 05:48:28 UTC (rev 211024)
@@ -2967,6 +2967,29 @@
     return "DOMJIT::IDLResultTypeFilter<${IDLType}>::value";
 }
 
+sub GetAttributeWithName
+{
+    my ($interface, $attributeName) = @_;
+    
+    foreach my $attribute (@{$interface->attributes}) {
+        return $attribute if $attribute->name eq $attributeName;
+    }
+}
+
+# https://heycam.github.io/webidl/#es-iterator
+sub InterfaceNeedsIterator
+{
+    my ($interface) = @_;
+    
+    return 1 if $interface->iterable;
+    if (GetIndexedGetterFunction($interface)) {
+        my $lengthAttribute = GetAttributeWithName($interface, "length");
+        return 1 if $lengthAttribute and $codeGenerator->IsIntegerType($lengthAttribute->type);
+    }
+    # FIXME: This should return 1 for maplike and setlike once we support them.
+    return 0;
+}
+
 sub GenerateImplementation
 {
     my ($object, $interface, $enumerations, $dictionaries) = @_;
@@ -3320,9 +3343,16 @@
             push(@implContent, "#endif\n") if $conditionalString;
         }
 
-        if ($interface->iterable) {
-            addIterableProperties($interface, $className);
+        if (InterfaceNeedsIterator($interface)) {
+            if (IsKeyValueIterableInterface($interface)) {
+                my $functionName = GetFunctionName($interface, $className, @{$interface->iterable->functions}[0]);
+                push(@implContent, "    putDirect(vm, vm.propertyNames->iteratorSymbol, JSFunction::create(vm, globalObject(), 0, ASCIILiteral(\"[Symbol.Iterator]\"), $functionName), DontEnum);\n");
+            } else {
+                AddToImplIncludes("<builtins/BuiltinNames.h>");
+                push(@implContent, "    putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject()->arrayPrototype()->getDirect(vm, vm.propertyNames->builtinNames().valuesPrivateName()), DontEnum);\n");
+            }
         }
+        push(@implContent, "    addValueIterableMethods(*globalObject(), *this);\n") if $interface->iterable and !IsKeyValueIterableInterface($interface);
 
         addUnscopableProperties($interface);
 
@@ -5158,24 +5188,6 @@
     }
 }
 
-sub addIterableProperties()
-{
-    my $interface = shift;
-    my $className = shift;
-
-    if (NeedsRuntimeCheck($interface->iterable)) {
-        my $enable_function = GetRuntimeEnableFunctionName($interface->iterable);
-        push(@implContent, "    if (${enable_function}())\n    ");
-    }
-
-    if (IsKeyValueIterableInterface($interface)) {
-        my $functionName = GetFunctionName($interface, $className, @{$interface->iterable->functions}[0]);
-        push(@implContent, "    putDirect(vm, vm.propertyNames->iteratorSymbol, JSFunction::create(vm, globalObject(), 0, ASCIILiteral(\"[Symbol.Iterator]\"), $functionName), DontEnum);\n");
-    } else {
-        push(@implContent, "    addValueIterableMethods(*globalObject(), *this);\n");
-    }
-}
-
 my %nativeType = (
     "ByteString" => "String",
     "DOMString" => "String",

Modified: trunk/Source/WebCore/bindings/scripts/test/JS/JSTestNode.cpp (211023 => 211024)


--- trunk/Source/WebCore/bindings/scripts/test/JS/JSTestNode.cpp	2017-01-22 05:19:17 UTC (rev 211023)
+++ trunk/Source/WebCore/bindings/scripts/test/JS/JSTestNode.cpp	2017-01-22 05:48:28 UTC (rev 211024)
@@ -144,8 +144,7 @@
         VM::DeletePropertyModeScope scope(vm, VM::DeletePropertyMode::IgnoreConfigurable);
         JSObject::deleteProperty(this, globalObject()->globalExec(), propertyName);
     }
-    if (RuntimeEnabledFeatures::sharedFeatures().domIteratorEnabled())
-        putDirect(vm, vm.propertyNames->iteratorSymbol, JSFunction::create(vm, globalObject(), 0, ASCIILiteral("[Symbol.Iterator]"), jsTestNodePrototypeFunctionSymbolIterator), DontEnum);
+    putDirect(vm, vm.propertyNames->iteratorSymbol, JSFunction::create(vm, globalObject(), 0, ASCIILiteral("[Symbol.Iterator]"), jsTestNodePrototypeFunctionSymbolIterator), DontEnum);
 }
 
 const ClassInfo JSTestNode::s_info = { "TestNode", &Base::s_info, 0, CREATE_METHOD_TABLE(JSTestNode) };

Modified: trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp (211023 => 211024)


--- trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp	2017-01-22 05:19:17 UTC (rev 211023)
+++ trunk/Source/WebCore/bindings/scripts/test/JS/JSTestObj.cpp	2017-01-22 05:48:28 UTC (rev 211024)
@@ -54,6 +54,7 @@
 #include "Settings.h"
 #include "URL.h"
 #include "WebCoreJSClientData.h"
+#include <builtins/BuiltinNames.h>
 #include <inspector/ScriptArguments.h>
 #include <inspector/ScriptCallStackFactory.h>
 #include <runtime/Error.h>
@@ -1897,6 +1898,7 @@
 #endif
     putDirect(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().privateMethodPrivateName(), JSFunction::create(vm, globalObject(), 0, String(), jsTestObjPrototypeFunctionPrivateMethod), ReadOnly | DontEnum);
     putDirect(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().publicAndPrivateMethodPrivateName(), JSFunction::create(vm, globalObject(), 0, String(), jsTestObjPrototypeFunctionPublicAndPrivateMethod), ReadOnly | DontEnum);
+    putDirect(vm, vm.propertyNames->iteratorSymbol, globalObject()->arrayPrototype()->getDirect(vm, vm.propertyNames->builtinNames().valuesPrivateName()), DontEnum);
     addValueIterableMethods(*globalObject(), *this);
     JSObject& unscopables = *constructEmptyObject(globalObject()->globalExec(), globalObject()->nullPrototypeObjectStructure());
     unscopables.putDirect(vm, Identifier::fromString(&vm, "voidMethod"), jsBoolean(true));
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to