2004-04-28 15:45 UTC Server-sent DOM events
Someone suggested to me recently that it would be quite cool if there was a way in which servers could dispatch DOM events straight into a Web page so that script in the page could then react to them, updating the page and so forth.
At the moment pages fake this by opening an
iframe
to a page that the server keeps
open, slowly trickling script
blocks
into it, and having the browser execute them as it
finds them.
This is pretty ugly, but it works. So here is a proposal for a cleaner, declarative way to do this.
To specify an event source in an HTML document authors use a new
(empty) element event-source
, with an attribute
src=""
that takes a URI to open as a stream and, if the
data found at that URI is of the appropriate type, treat as an event
source.
The event stream MIME type is application/x-dom-event-stream
.
The event stream is always be encoded as UTF-8. Line are always be
terminated by a single U+000A line feed character.
The event stream format is (in pseudo-BNF):
<stream> ::= <event>*
<event> ::= [ <comment> | <field> ]+ <newline>
<comment> ::= ';' <data> <newline>
<field> ::= <name> [ ':' <space>? <data> ]? <newline>
<name> ::= one or more UNICODE characters other than ':' and U+000A
<data> ::= zero or more UNICODE characters other than U+000A
<space> ::= a single U+0020 character (' ')
<newline> ::= a single U+000A character
The stream is parsed by reading everything line by line, in blocks
separated by blank lines (blank lines are those consisting of just
a single lone line feed character). Comment lines (those starting
with the character ';' not proceeded by any other characters) are
ignored.
For each non-blank, non-comment line, the field name is first
taken. This is everything on the line up to but not including the
first colon (':') or the line feed, whichever comes first. Then, if
there was a colon, the data for that line is taken. This is
everything after the colon, ignoring a single space after the colon
if there is one, up to the end of the line. If there was no colon
the data is the empty string.
Examples:
Field name: Field data
This is a blank field
1. These two lines: have the same data
2. These two lines:have the same data
1. But these two lines: do not
2. But these two lines: do not
If a field name occurs multiple times, the data values for those
lines are concatenated with a newline between them.
For example, the following:
Test: Line 1
Foo: Bar
Test: Line 2
...is treated as having two fields, one called Test
with the
value Line 1\nLine 2
(where \n
represents a newline), and one
called Foo
with the value Bar
.
(Since any random stream of characters matches the above
format, there is no need to define any error handling.)
Once the fields have been parsed, they are interpreted as follows
(these are case sensitive exact comparisons):
Event
is the name of the event. For example, load
,
DOMActivate
, updateTicker
.
Namespace
is the DOM3 namespace for the event. For normal DOM
events this would be http://www.w3.org/2001/xml-events
.
If it isn't specified the event namespace is null.
Class
is the interface used for the event, for instance
Event
, UIEvent
, MutationEvent
, KeyboardEvent
, etc. If it
is not specified it is defaulted based on the event name as
follows:
If Namespace
is http://www.w3.org/2001/xml-events
or null
and the Event
field exactly matches one of the events
specified by DOM3 Events in section 1.4.2 "Complete list of
event types", then the Class defaults to the interface
relevant for that event type.
For example:
Event: click
...would cause Class
to be treated as MouseEvent
.
If Namespace
is uuid:755e2d2d-a836-4539-83f4-16b51156341f
or
null and the Event
doesn't match any of the known events,
then the RemoteEvent
interface (described below) is used.
Otherwise, if the UA doesn't have special knowledge of which
class to use for the given event in the given namespace, then
the Event
interface is used.
Bubbles
specifies whether the event is to bubble. If it is
specified and has the value No
, the event does not bubble. If
it is specified and has any other value (including no
or
No\n
) then the event bubbles. If it is not specified it is
defaulted based on the event name as follows:
If Namespace
is http://www.w3.org/2001/xml-events
or null and the Event
field exactly matches one of the events
specified by DOM3 Events in section 1.4.2 "Complete list of event types", then whether
the event bubbles depends on whether the DOM3 Events spec specifies
that that event should bubble or not.
For example:
Event: load
...would cause Bubbles
to be treated as No
.
Otherwise, if the UA doesn't have special knowledge of which
class to use for the given event in the given namespace, then
the event bubbles.
Cancelable
specifies whether the event may have its default
action prevented. If it is specified and has the value No
, the
event may not have its default action prevented. If it is
specified and has any other value (including no
or No\n
)
then the event may be cancelled. If it is not specified it is
defaulted based on the event name as follows:
If Namespace
is http://www.w3.org/2001/xml-events
or null and the Event
field exactly matches one of the events
specified by DOM3 Events in section 1.4.2 "Complete list of event types", then whether
the event is cancelable depends on whether the DOM3 Events spec
specifies that that event should be cancelable or not.
For example:
Event: load
...would cause Cancelable
to be treated as No
.
Otherwise, if the UA doesn't have special knowledge of which
class to use for the given event in the given namespace, then
the event may be cancelled.
Target
is the element that the event is to be dispatched on.
If its value starts with a #
character then the remainder of
the value represents an ID, and the event must be dispatched on
the same node as would be obtained by the getElementById()
method on the ownerDocument of the event-source element
responsible for the event being dispatched.
For example,
Target: #test
...would target the element with ID test
.
If the value does not start with a #
but has the literal value
Document
, then the event is dispatched at the ownerDocument
of
the event-source
element responsible for the event being dispatched.
Otherwise, the event is dispatched at the event-source
element
itself.
Other fields depend on the interface specified (or possibly
implied) by the Class
field. If the specified interface has an
attribute that exactly matches the name of the field, and the
value of the field can be converted (using the type conversions
defined in ECMAScript) to the type of the attribute, then it
must be used. Any attributes (other than the Event
interface
attributes) that do not have matching fields are initialised to
zero, null, false, or the empty string.
For example:
; ...some other fields...
Class: MouseEvent
button: 2
...would result in a MouseEvent event that had button
set to
2
but screenX
, screenY
, etc, set to 0, false, or null as
appropriate.
If a field does not match any of the attributes on the event, it
is ignored.
Once a blank line is reached, an event of the appropriate type is
synthesized and dispatched to the appropriate node as described by
the fields above. No event is dispatched until a blank line has
been received.
The RemoteEvent interface is defined as follows:
interface RemoteEvent : Event {
readonly attribute DOMString data;
void initRemoteEvent(in DOMString typeArg,
in boolean canBubbleArg,
in boolean cancelableArg,
in DOMString dataArg);
void initRemoteEventNS(in DOMString namespaceURI,
in DOMString type,
in boolean canBubbleArg,
in boolean cancelableArg,
in DOMString dataArg);
};
Events that use the RemoteEvent interface never have any default
action associated with them.
The event-source
element may also have an
onevent=""
attribute. If present, the attribute is
treated as script representing an event handler registered as
non-capture listener of events with name event
and
the namespace uuid:755e2d2d-a836-4539-83f4-16b51156341f
or null, that are targetted at or bubble through the element.
The following event description, once followed by a blank line:
Event: stock change
data: YHOO
data: -2
data: 10
...would cause an event stock change
with the interface
RemoteEvent
to be dispatched on the event-source
element, which
would then bubble up the DOM, and whose data
attribute would
contain the string YHOO\n-2\n10
(where \n
again represents a
newline).
This could be used as follows:
<event-source src="http://stocks.example.com/ticker.php" id="stock">
<script type="text/javascript">
document.getElementById('stock').addEventListener('stock change',
function () {
var data = event.data.split(' ');
updateStocks(data[0], data[1], data[2]);
}, false);
</script>
...where updateStocks is a function defined as:
function updateStocks(symbol, delta, value) { ... }
...or some such.
What do you think? Let me know.
Pingbacks: 1