Intent to Deprecate: DOMStringList

165 views
Skip to first unread message

Joshua Bell

unread,
Mar 2, 2015, 1:22:09 PM3/2/15
to blink-dev

Primary eng (and PM) emails

jsb...@chromium.org

phi...@opera.com


Summary

Try and remove DOMStringList from the web platform, in favor of plain JS arrays of strings.


Motivation

DOMStringList was added to Speclandia in the fading days of the "DOM objects good, script objects bad!" regime, and picked up by a few specs, including Indexed DB. The sole motivation was as it says on the tin: return a list of strings from a DOM API. http://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMStringList


Modern DOM API design would be to simply... wait for it... return a plain old JS array of strings. The interface was optimistically removed from the DOM living standard: https://dom.spec.whatwg.org/#dom-core 


"You didn't think it was gonna be that easy, did you?"

DOMStringList instances have a contains() method, which lets you write:

if (db.objectStoreNames.contains("myStore")) { ... }

And use counters (see below) show a nontrivial usage. The obvious solution would be that we add contains to Array.prototype. Unfortunately, this turns out to not be web compatible: https://bugzilla.mozilla.org/show_bug.cgi?id=1075059 Instead, ES7 proposes Array.prototype.includes which is great but doesn't let us drop DOMStringList easily.


Here's the plan:


1. Deprecate (i.e. show console warning for) DOMStringList.contains
2. Wait until ES7's Array.prototype.includes ships
3. Add DOMStringList.includes, update console warning to advise switching
4. Wait for usage numbers of contains() to drop to an acceptable number
5. Change APIs to return a plain JS array.

Additionally, we'll measure and deprecate the named items() getter. We suspect most callers will use the index getter (i.e. domStringList[name]) anyway but want to be sure. Also on the table is making DOMStringList a [NoInterfaceObject] to further hide the existence of this dead end from the web platform.

We expect this process to take considerable time, and require checking in with other browser vendors, and failure is a strong possibility. If so, we'll report back here and either push back on removing the interface from DOM or add it to the e.g. Indexed DB spec.

Also, while there's broad consensus among standards wonks that we should yoink DOMStringList, there's still some waffling over exactly how "return a JS array" would be specified for IDL readonly attributes: https://www.w3.org/Bugs/Public/show_bug.cgi?id=23682


Compatibility Risk

DOMStringList is supported by the whole gang: IE, Firefox, Safari and Chrome. We're not going to be able to remove this overnight. Standards discussion is here:


Per bugzilla, Moz has similar plans to deprecate and try and remove:
https://bugzilla.mozilla.org/show_bug.cgi?id=980563

Alternative implementation suggestion for web developers

Instead of:

  if (list.contains(item)) { ... }

Write:

  if ([].slice.call(list).indexOf(item) >= 0) { ... }

Obvious, right? *sigh* More practically, wait out ES7 and do this instead:

  if (list.includes(item)) { ... }

(And yes, ES7 implementation is not a Boolean condition. I mean, wait until Array.prototype.includes has successfully shipped.)

Usage information from UseCounter

https://www.chromestatus.com/metrics/feature/popularity#DOMStringListContains

https://www.chromestatus.com/metrics/feature/timeline/popularity/286

... shows a usage around 0.01% of pages - and sadly trending *upwards*.


Entry on chromestatus.com, crbug.com, or MDN

Tracking bug: https://code.google.com/p/chromium/issues/detail?id=460726


Requesting approval to remove too?

Nope. This is going to take a while.


Elliott Sprehn

unread,
Mar 2, 2015, 1:36:15 PM3/2/15
to Joshua Bell, blink-dev
Array is mutable, so this would need to return a new array instance every time or we'd need to introduce an immutable array instead.

Chris Harrelson

unread,
Mar 2, 2015, 1:37:22 PM3/2/15
to Joshua Bell, blink-dev
Hmm. Maybe we should just wait for ES7 before deprecating, because the syntax until then is so baroque. It doesn't really get in our way in the meantime, right?

On Mon, Mar 2, 2015 at 10:22 AM, 'Joshua Bell' via blink-dev <blin...@chromium.org> wrote:
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.

Koji Ishii

unread,
Mar 2, 2015, 8:42:33 PM3/2/15
to Chris Harrelson, Joshua Bell, blink-dev
You might be aware but in case, there's a discussion in a W3C bug 23682[1] about how to handle array and sequence in DOM. Anne said heycam plans to tackle this next.

Philip Jägenstedt

unread,
Mar 2, 2015, 10:32:25 PM3/2/15
to Joshua Bell, blink-dev
LGTM to deprecate DOMStringList.contains() and item() even though we're not sure if an eventual removal is going to work out.

Note that in addition to IDB, DOMStringList is also used in Location.ancestorOrigins, which was recently spec'd as a DOMString[].

It would also be useful to tag each DOMStringList with a creator so that we can add at least DOMStringListContainsForLocationAncestorOrigins and DOMStringListContainsForIDB counters. Perhaps one of them isn't used and can be removed immediately.

I'm skeptical of ever adding DOMStringList.includes() due to the risk of being stuck with both contains() and includes(), but we'll see what makes sense when the time comes. It's unfortunate that we have no number on the risk of adding Array.prototype.contains(). It could be that it's less risky than trying to get rid of DOMStringList, in which case what we're doing is a bit silly. However, I don't think a UseCounter can estimate that risk.

Philip

Simon Pieters

unread,
Mar 3, 2015, 4:57:42 AM3/3/15
to Joshua Bell, Philip Jägenstedt, blink-dev
On Tue, 03 Mar 2015 04:32:19 +0100, Philip Jägenstedt <phi...@opera.com>
wrote:

> LGTM to deprecate DOMStringList.contains() and item() even though we're
> not
> sure if an eventual removal is going to work out.
>
> Note that in addition to IDB, DOMStringList is also used in
> Location.ancestorOrigins, which was recently spec'd
> <https://www.w3.org/Bugs/Public/show_bug.cgi?id=22699> as a DOMString[].
>
> It would also be useful to tag each DOMStringList with a creator so that
> we
> can add at least DOMStringListContainsForLocationAncestorOrigins
> and DOMStringListContainsForIDB counters. Perhaps one of them isn't used
> and can be removed immediately.
>
> I'm skeptical of ever adding DOMStringList.includes() due to the risk of
> being stuck with both contains() and includes(), but we'll see what makes
> sense when the time comes. It's unfortunate that we have no number on the
> risk of adding Array.prototype.contains(). It could be that it's less
> risky
> than trying to get rid of DOMStringList, in which case what we're doing
> is
> a bit silly. However, I don't think a UseCounter can estimate that risk.

Maybe instead of adding DOMStringList.includes() in Blink we can ask Web
developers to do something like:


if (window.DOMStringList) {
window.DOMStringList.prototype.includes =
window.DOMStringList.prototype.contains;
}

This has the advantage that they don't have to wait and it will work in
old browsers too.

Then we deprecate contains() and item(). If possible to detect that the
method called was actually includes() rather than contains(), we can avoid
whining and counting it.

--
Simon Pieters
Opera Software

Joshua Bell

unread,
Jun 12, 2015, 5:33:16 PM6/12/15
to blink-dev
Dusting this off again...

Philip and I have some landed and ready-to-land CLs that add additional measurement and deprecation to DOMStringList to see how far we can go with just removing this wart from the platform by understanding exactly where it's used and a little bit of gentle developer coercion, before we're forced to tackle some of the gnarly stuff discussed over in https://lists.w3.org/Archives/Public/public-script-coord/2015AprJun/0067.html

* Measure list.contains(s) usage (already in)
* Measure list.item(x) usage separately from list[x]
* Measure item() and contains() usage separately for uses from Location.ancestorOrigins and IndexedDB
* Issue deprecation warning when list.item(x) is called, with a suggestion to use list[x] instead
* Issue deprecation warning when list.contains(s) is called (no suggestion)

The warnings are, of course, optimistic - if we can't drive usage down we'll do nasty things like have Indexed DB mint an ES6-style Array subclass with contains(), but we'd like to contain the nasty as much as possible.

Any last objections to the above approach?

PhistucK

unread,
Jun 12, 2015, 5:53:29 PM6/12/15
to Joshua Bell, blink-dev

On Sat, Jun 13, 2015 at 12:33 AM, 'Joshua Bell' via blink-dev <blin...@chromium.org> wrote:
The warnings are, of course, optimistic - if we can't drive usage down we'll do nasty things like have Indexed DB mint an ES6-style Array subclass with contains(), but we'd like to contain the nasty as much as possible.

​Why is this so nasty?
This is actually the first idea that comes to (my) mind.
[NoInterfaceObject]
DOMStringList extends Array
It will be an array subclass, just like you want it to eventually become. It still has deprecated contains() and item() for a while, in order not to break existing usage, but ​your warning will be actionable and the replacement is short -
- foo.item(bar) is deprecated and will be removed. Use foo[bar] instead.
- foo.contains(bar) is deprecated and will be removed. Use foo.indexOf(bar) !== -1 instead.
If you want to stay compatible with the other browsers (until they switch, too), you can suggest foo.contains? foo.contains(bar): foo.indexOf(bar) !== -1 and everything is covered, I believe.





PhistucK

Joshua Bell

unread,
Jun 12, 2015, 8:17:02 PM6/12/15
to PhistucK, blink-dev
On Fri, Jun 12, 2015 at 2:52 PM, PhistucK <phis...@gmail.com> wrote:

On Sat, Jun 13, 2015 at 12:33 AM, 'Joshua Bell' via blink-dev <blin...@chromium.org> wrote:
The warnings are, of course, optimistic - if we can't drive usage down we'll do nasty things like have Indexed DB mint an ES6-style Array subclass with contains(), but we'd like to contain the nasty as much as possible.

​Why is this so nasty?

Any special case is nasty. That said, a fall-back that's a subclass as you describe isn't too bad, but still we'd rather not surprise developers. The best case is being able to eliminate it; failing that, we'll have data to guide us.

PhistucK

unread,
Jun 13, 2015, 2:56:12 AM6/13/15
to Joshua Bell, blink-dev
Of course, but in order to eliminate it more easily (the original intent already said it will be a long way), I think my suggestion is a big first step that only increases the chances of eliminating it at last. Once everyone (or most) listens to the deprecation warning, you can easily eliminate it.


PhistucK

Alex Russell

unread,
Jun 13, 2015, 3:12:04 AM6/13/15
to Joshua Bell, blink-dev

looks good. The "bad" option of an ES6 subclass isn't that problematic (modulo Elliott's mutability question) should it come to that. Thanks for pushing this forward!

Rick Byers

unread,
Jun 15, 2015, 9:19:27 AM6/15/15
to Alex Russell, Joshua Bell, blink-dev
I'm concerned about generating deprecation warnings when we don't have a great alternative yet, especially when the messaging is "wait for some future time for the easy way to fix this".  We run the risk of training developers to ignore deprecation warnings.  

Why not, as Chris suggests, wait to consider 'contains' deprecated until we have 'includes'?

There's the separate question of at what threshold it's practical to use deprecation warnings (i.e. it's harmful to generate them on something we're really unlike to ever remove), but I defer to your judgement here.  If you think it's likely that we could successfully remove DOMStringList, then I'm fine deprecating when there are good alternatives.

Rick

Philip Jägenstedt

unread,
Jun 16, 2015, 6:36:52 PM6/16/15
to Rick Byers, Alex Russell, Joshua Bell, blink-dev
I'm also not a fan of deprecation when it's unclear if/when removal will work out, and this is a tricky case. Once Array.prototype.includes is available we could make DOMStringList an [ArrayClass] to have both 'contains' and 'includes' at the same time, but there is a risk that we'd get stuck in this intermediate state. I don't know about everyone else, but I think that if DOMStringList can't be removed entirely, then leaving it exactly as it is would be fine.

Whether or not we have an intermediate state, I don't think use counter data is going to be a reliable indicator of risk here, because the counter will still be hit by code like (list.contains && list.contains(x)) || (list.includes && list.includes(x)), and even the reverse if there's no intermediate state. With this, like so many other cases, I think we'd really need a way of figuring out which top sites are hitting these code paths.

I don't really think it'll help, but we could land "Measure item() and contains() usage separately for uses from Location.ancestorOrigins and IndexedDB" and see if that reveals anything interesting before deprecating. If we're lucky all of the problematic usage will be for ancestorOrigins, which is at least easy to grep for in httparchive :/

Philip Jägenstedt

unread,
Nov 2, 2015, 4:55:14 PM11/2/15
to Rick Byers, Alex Russell, Joshua Bell, blink-dev
The context-specific counters for item() and contains() have been in stable for a while now:


In other words, IndexedDB needs contains() and the ancestorOrigins attribute needs item() :(

There is currently some spec discussion around the ancestorOrigins attribute, which is so far only in WebKit and Blink (implemented pre-fork).

Given this, I think we should consider IndexedDB and ancestorOrigins separately.

There is reason to suspect that the 0.225% usage of DOMStringList.item() for ancestorOrigins is due to a small number of sites. Most importantly, the feature only makes sense if you expect to be embedded by others, which is a tiny fraction of all web properties out there. Also, the feature is still pretty new and only supported in WebKit and Blink, so compared to ancient, mostly interoperable features, the tail of sites using this shouldn't be long.

With this and a few other interop issues, it could really help to flush out the top sites that are hitting the use counters, but we have no method for doing so. In cases where usage is relatively low, how about making that change immediately after a branch point, and reverting at the first bug report or before the next branch point?
Reply all
Reply to author
Forward
0 new messages