At Mozilla we are looking at making more parts of the web platform pluggable by web applications so I had a look at the current state of
registerProtocolHandler() and friends. The goal behind this feature is to make
mailto URLs play nice with Yahoo! Mail, Gmail, etc. Or in other words, it makes navigation extensible.
The state of this feature is rather poor. It is only supported by Chromium and Gecko. Chromium also supports
unregisterProtocolHandler(). Neither Gecko nor Chromium support
Perhaps if we implement the missing methods and improve the user interface around it this will see somewhat wider adoption. However, to make the interface really work we would have to have built-in knowledge about each URL scheme. As users really have no concept of that. Firefox uses “Add title (domain) as an application for scheme links?” which even for
mailto is probably confusing. Chrome seems a little better, using “Allow domain to open all type links?” For the
mailto scheme it uses email as type.
(There is also
registerContentHandler() which is for making the bit pluggable where your browser has no idea what to do with the resource it just retrieved. This is only supported by Gecko and only for feed-related MIME types.)
If you have any great user interface ideas let me know! I thought I would share the above since I could not find a decent summary anywhere else.
I have been reinstating “features” related to attribute handling in DOM. We thought we could get rid of them, but usage counters from Chrome and compatibility data from Gecko showed we could not. This is very sad so I thought I would share the pain.
A simple design for attributes would consist of each having a name and a value (both strings) and a simple map-like API on every element would be sufficient to deal with them. The
setAttribute(name, value), and
removeAttribute(name) methods. As well as a way to iterate through the names and values.
However, back in the day
getAttribute(name) was required to return the empty string rather than null for a missing attribute, so
hasAttribute(name) also exists. Fixing the specification to make
getAttribute() return null was highly controversial back then. I even misguidedly ranted against developers who were making use of this feature as it prevented Opera from becoming standards compliant. “Please leave your sense of logic at the door, thanks!” was not a popular phrase back then.
Unfortunately namespaced attributes are a thing. And instead of simply adding a namespace field to our existing name and value, a namespace, namespace prefix, and local name field were added. Indeed, the local name is not necessarily equal to the name of an attribute. The idea was to have some kind of modality where before namespace and after namespace attributes would not really interact. That never happened of course. To deal with namespaces we have
setAttributeNS(namespace, name, value) (indeed, name, not localName, so bad),
removeAttributeNS(namespace, localName), and
The real kicker is that the first four methods ignore the namespace fields, but can create attributes you cannot access with the
*NS methods. There is no universal attribute API, though if you stay clear from namespaces everywhere you are probably mostly fine (except perhaps with SVG and such).
This was still too simple. There is also
attributes which returns a
NamedNodeMap (only used for attributes these days). And
hasAttributes() which can tell you whether that map is empty or not. These two used to be on all nodes (to limit the amount of casting in Java), but we are moving them to element since that is where they make sense.
NamedNodeMap contains a collection of zero or more
Attr objects so you can inspect their individual fields. The map has a
length property, an
attributes.name works, as well as
attributes. Good times.
Attr objects also allow manipulation of an attribute's value. Due to mutation observers this requires an element field on attributes to point back to the element the attribute belongs to. Namespace prefix also used to be mutable field, but fortunately this was poorly implemented and recently killed.
The real reason attributes are so complicated, and more complicated still, ignoring namespaces for the moment, are DTDs. The SGML crowd was not brave enough to cut the lifeline when they did XML. Then XML got popular enough to end up in browsers and the DOM. This meant that attributes cannot contain just text, but also entity references. And therefore attributes became a type of node. Entity references were really never implemented and we managed to remove that cruft from the platform fortunately. However, attributes are still a type of node.
The last things we are investigating is whether attributes can stop having child nodes and perhaps stop being a node altogether. Meanwhile, we had to add
createAttribute(localName) on document,
removeAttributeNode(attr) on element, and
NamedNodeMap back as sites use these. Oh wait, and all their
*NS counterparts of course, bar
Added together, we have twenty-five methods to deal with attributes rather than three. And attributes require six internal fields rather than two. And this is assuming we can get rid of child nodes and attributes being nodes, both semi-implemented today.
The way any environment works, simplified, is by going through a stack of tasks. Whenever the user moves the mouse, or
XMLHttpRequest fetches, new tasks are queued to eventually dispatch events and then run event handlers and listeners. Asynchronous steps run in parallel with this.
When new standards are written, this is often done wrong. A set of asynchronous steps cannot refer to global state that might change, such as a document's base URL. They also cannot change state, such as properties on an object. Remember, these steps run in parallel, so if you change
obj.prop === obj.prop would no longer be guaranteed. Bad. Instead you queue a task. Effectively scheduling some code to run in the environment at some point in the future when it has the bandwidth. The Fetch layer queues tasks whenever new network chunks arrive. The UI layer queues tasks whenever the user moves the mouse. Etc.
(Not all of this is properly defined as of yet, please fill in the gaps as you run across them. Note Asynchronous Steps Explicitly has advice for how to go about that.)
When I was a kid we went to Switzerland sometimes to enjoy the mountains in the summer and my mom used to tell me that all of Switzerland was celebrating my birthday. I live in Switzerland now 😊.
The change to the URL Standard to use Unicode IDNA Compatibility Processing was made a while back. The reasoning is that it provides an interface compatible with IDNA2003, including almost identical processing, but is based on the IDNA2008 dataset. That means that lowercasing still happens. Mapping code points still happens. URLs will remain cool. The URL Standard has the integration details.
Objects live in Vats. Realms are a legacy accident due to poor browser engineering and do not control the object addressing space. Vats can only communicate through message passing. Messages clone objects, sometimes transfering their underlying data. Ever-so-slowly ECMAScript is growing more powerful to describe the web platform. This kind of modeling is what excites me these days.
Thanks to Allen Wirfs-Brock for correcting my errors in writing this and providing these cool analogies:
A Vat is an address space for objects. An object within a Vat can only reference ("point to") other objects in the same Vat. Realms are just a decoration on some object. Their Realm associations (if any) don’t impact the ability of objects within a Vat to reference each other.
Vat’s are like isolated continents. In theory you can walk from any point to any other point on a continent, but you can’t walk to any other continent. A Realm is like a country with national boundary. Those boundaries don’t actually change the theoretical ability to walk between any two continental points, but parties might use those boundaries as part of a scheme to artificially restrict movement.
(Mark Miller did this long ago in E and the web has a somewhat sloppier version of it, through workers, structured cloning, and having multiple globals, but on the upside is deployed widely.)
I was asked how one contributes to standards. Before anything else, it is worth watching Domenic Denicola’s presentation on making friends and influencing standards bodies. It is awesome and will teach you a great deal.
I think the core thing to understand when considering contributing to web standards is that they are created by communities. Typically there are a few people leading the charge and many people contributing with critique, research, and tests. Usually there is a combination of mailing lists, IRC, and the occasional face-to-face meeting, to keep everyone roughly synchronized.
You want to figure out what community to participate in:
Unfortunately there’s a myriad of other smaller lists for particular APIs. Usually the standard you care about has relevant pointers. If it doesn’t, please file a bug or let someone else know as it definitely should.
Studying the output of the community (the standard and tests) and its ongoing progress (the mailing list) is a good way to get a feel for how things work and what you should pay attention to. It can help to read the WHATWG FAQ too as it documents answers to many common questions. Having familiarized yourself with the material and the environment you should feel more than ready to start participating more actively, in particularly if you see something worthy of improvement.
There appears to be trend where specifications monkey patch a base specification. A monkey patch being a subtle change to an existing algorithm only observable if you have seen both the new and the base specification. Some examples: Custom Elements attempts to redefine the
eval(). (Using dated TR/ URLs here as an exception so these examples remain useful going forward.)
Apparently it is not clear that this is bad design. We should avoid monkey patching (hereafter patching). It has at least these problems:
img element definition it would not be clear for someone reading the adopting algorithm that adopting is actually more involved.
If you encounter patching, please file a bug. If you are writing a specification and temporarily want to patch a base specification to help implementations along, file a bug on the base specification so the community is informed of what you are trying to do.
I love figuring out the web platform and making it better.