Skip to content
Open
264 changes: 250 additions & 14 deletions dom.bs
Original file line number Diff line number Diff line change
Expand Up @@ -815,7 +815,7 @@ method steps are:
<ol>
<li><p>If <a>this</a>'s <a>dispatch flag</a> is set, then return.

<li><p><a>Initialize</a> <a>this</a> with <var>type</var>, <var>bubbles</var>, and
<li><p><a for=Event>Initialize</a> <a>this</a> with <var>type</var>, <var>bubbles</var>, and
<var>cancelable</var>.
</ol>
</div>
Expand Down Expand Up @@ -886,7 +886,7 @@ method steps are:
<ol>
<li><p>If <a>this</a>'s <a>dispatch flag</a> is set, then return.

<li><p><a>Initialize</a> <a>this</a> with <var>type</var>, <var>bubbles</var>, and
<li><p><a for=Event>Initialize</a> <a>this</a> with <var>type</var>, <var>bubbles</var>, and
<var>cancelable</var>.

<li><p>Set <a>this</a>'s {{CustomEvent/detail}} attribute to <var>detail</var>.
Expand Down Expand Up @@ -5997,19 +5997,13 @@ and <a for=Node>node document</a> is <a>this</a>.
method steps are:

<ol>
<li>If <var>target</var> does not match the
<code>[=XML/Name=]</code> production,
then <a>throw</a> an "{{InvalidCharacterError!!exception}}" {{DOMException}}. <!-- DOM3 does not check for "xml" -->
<li><p>Let <var>pi</var> be a new {{ProcessingInstruction}}
<a for=/>node</a>, with <a for=Node>node document</a> set to <a>this</a>.

<li>If <var>data</var> contains the string
"<code>?></code>", then <a>throw</a> an
"{{InvalidCharacterError!!exception}}" {{DOMException}}. <!-- Gecko does this. -->
<li><p><a for=ProcessingInstruction>Initialize</a> <var>pi</var> with <var>target</var> and
<var>data</var>.

<li>Return a new {{ProcessingInstruction}}
<a for=/>node</a>, with
<a for=ProcessingInstruction>target</a> set to <var>target</var>,
<a for=CharacterData>data</a> set to <var>data</var>, and
<a for=Node>node document</a> set to <a>this</a>.
<li><p>Return <var>pi</var>.
</ol>

<hr>
Expand Down Expand Up @@ -8221,7 +8215,8 @@ string called <dfn export id=concept-cd-data for=CharacterData>data</dfn>.

<div algorithm>
<p>To <dfn export id=concept-cd-replace>replace data</dfn> of a <a for=/>node</a> <var>node</var>
with an integer <var>offset</var>, integer <var>count</var>, and string <var>data</var>:
with an integer <var>offset</var>, integer <var>count</var>, string <var>data</var>, and an optional
boolean <var>piAttributesAlreadyUpdated</var> (default false):

<ol>
<li><p>Let <var>length</var> be <var>node</var>'s <a for=Node>length</a>.
Expand Down Expand Up @@ -8263,6 +8258,10 @@ with an integer <var>offset</var>, integer <var>count</var>, and string <var>dat
<a for=range>end offset</a> by <var>data</var>'s <a for=string>length</a> and decrease it by
<var>count</var>.

<li><p>If <var>node</var> is a {{ProcessingInstruction}} <a for=/>node</a> and
<var>piAttributesAlreadyUpdated</var> is false, then <a>update attributes from data</a> for
<var>node</var>.

<li><p>If <var>node</var>'s <a for=tree>parent</a> is non-null, then run the
<a>children changed steps</a> for <var>node</var>'s <a for=tree>parent</a>.
</ol>
Expand Down Expand Up @@ -8518,17 +8517,254 @@ interface CDATASection : Text {
<pre class=idl>
[Exposed=Window]
interface ProcessingInstruction : CharacterData {
constructor(DOMString target, optional DOMString data = "");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I initially thought this would be a bit ugly compared to using IDL records or some such so you can set the attributes directly, but I think overall it's probably okay if we have to keep around the dual-data-structure anyway.


readonly attribute DOMString target;

boolean hasAttributes();
sequence&lt;DOMString> getAttributeNames();
DOMString? getAttribute(DOMString name);
undefined setAttribute(DOMString name, DOMString value);
undefined removeAttribute(DOMString name);
boolean toggleAttribute(DOMString name, optional boolean force);
boolean hasAttribute(DOMString name);
};</pre>

<dl class=domintro>
<dt><code><var ignore>pi</var> = new <a constructor lt="ProcessingInstruction()">ProcessingInstruction(<var>target</var> [, <var>data</var> = ""])</a></code>
<dd>Returns a new {{ProcessingInstruction}} <a for=/>node</a> whose
<a for=ProcessingInstruction>target</a> is <var>target</var> and
<a for=CharacterData>data</a> is <var>data</var>.

<dt><code><var>pi</var> . {{ProcessingInstruction/target}}</code>
<dd>Returns the <a for=ProcessingInstruction>target</a>.

<dt><code><var>pi</var> . <a method for=ProcessingInstruction lt=hasAttributes()>hasAttributes</a>()</code>
<dd><p>Returns true if <var>pi</var> has attributes; otherwise false.

<dt><code><var>pi</var> . <a method for=ProcessingInstruction lt=getAttributeNames()>getAttributeNames</a>()</code>
<dd><p>Returns the names of all of <var>pi</var>'s attributes. Cannot contain duplicates.

<dt><code><var>pi</var> . <a method for=ProcessingInstruction lt=getAttribute()>getAttribute</a>(<var>name</var>)</code>
<dd><p>Returns the value of <var>pi</var>'s attribute named <var>name</var>, and null if there is
no such attribute.

<dt><code><var>pi</var> . <a method for=ProcessingInstruction lt=setAttribute()>setAttribute</a>(<var>name</var>, <var>value</var>)</code>
<dd><p>Sets <var>pi</var>'s attribute named <var>name</var> to <var>value</var>.

<dt><code><var>pi</var> . <a method for=ProcessingInstruction lt=removeAttribute()>removeAttribute</a>(<var>name</var>)</code>
<dd><p>Removes <var>pi</var>'s attribute named <var>name</var>.

<dt><code><var>pi</var> . <a method for=ProcessingInstruction lt=toggleAttribute()>toggleAttribute</a>(<var>name</var> [, <var>force</var>])</code>
<dd>
<p>If <var>force</var> is not given, "toggles" <var>name</var>, removing it if it is
present and adding it if it is not present. If <var>force</var> is true, adds
<var>name</var>. If <var>force</var> is false, removes <var>name</var>.

<p>Returns true if <var>name</var> is now present; otherwise false.

<dt><code><var>pi</var> . <a method for=ProcessingInstruction lt=hasAttribute()>hasAttribute</a>(<var>name</var>)</code>
<dd><p>Returns true if <var>pi</var> has an attribute named <var>name</var>; otherwise false.
</dl>

<div algorithm>
<p>The
<dfn constructor for=ProcessingInstruction lt=ProcessingInstruction(target)><code>new ProcessingInstruction(<var>target</var>, <var>data</var>)</code></dfn>
constructor steps are:

<ol>
<li><p>Set <a>this</a>'s <a for=Node>node document</a> to <a>current global object</a>'s
<a>associated <code>Document</code></a>.

<li><p><a for=ProcessingInstruction>Initialize</a> <a>this</a> with <var>target</var> and
<var>data</var>.

<li><p><a>Update attributes from data</a> for <a>this</a>.
</ol>
</div>

<p>{{ProcessingInstruction}} <a for=/>nodes</a> have an associated
<dfn export id=concept-pi-target for=ProcessingInstruction>target</dfn>.

<p>{{ProcessingInstruction}} <a for=/>nodes</a> have an associated
<dfn export id=concept-pi-attribute-map for=ProcessingInstruction>attribute map</dfn>, which is a
<a for=/>map</a>, initially empty.

<div algorithm>
<p>The <dfn attribute for=ProcessingInstruction>target</dfn> getter steps are to return
<a>this</a>'s <a for=ProcessingInstruction>target</a>.
</div>

<div algorithm>
<p>The <dfn method for=ProcessingInstruction><code>hasAttributes()</code></dfn> method steps are to
return false if <a>this</a>'s <a for=ProcessingInstruction>attribute map</a>
<a for=map>is empty</a>; otherwise true.
</div>

<div algorithm>
<p>The <dfn method for=ProcessingInstruction><code>getAttributeNames()</code></dfn> method steps
are to return the result of <a for=map>getting the keys</a> of <a>this</a>'s
<a for=ProcessingInstruction>attribute map</a>.
</div>

<div algorithm>
<p>The <dfn method for=ProcessingInstruction><code>getAttribute(<var>name</var>)</code></dfn> method
steps are to return <a>this</a>'s <a for=ProcessingInstruction>attribute map</a>[<var>name</var>]
<a for=map>with default</a> null.
</div>

<div algorithm>
<p>The
<dfn method for=ProcessingInstruction><code>setAttribute(<var>name</var>, <var>value</var>)</code></dfn>
method steps are:

<ol>
<li><p>If <var>name</var> is not a <a>valid attribute local name</a>, then <a>throw</a> an
"{{InvalidCharacterError!!exception}}" {{DOMException}}.

<li><p><a for=map>Set</a> <a>this</a>'s
<a for=ProcessingInstruction>attribute map</a>[<var>name</var>] to <var>value</var>.

<li><p><a>Update data from attributes</a> for <a>this</a>.
</ol>
</div>

<div algorithm>
<p>The <dfn method for=ProcessingInstruction><code>removeAttribute(<var>name</var>)</code></dfn>
method steps are:

<ol>
<li><p><a for=map>Remove</a> <a>this</a>'s
<a for=ProcessingInstruction>attribute map</a>[<var>name</var>].

<li><p><a>Update data from attributes</a> for <a>this</a>.
</ol>
</div>

<div algorithm>
<p>The
<dfn method for=ProcessingInstruction><code>toggleAttribute(<var>name</var>, <var>force</var>)</code></dfn>
method steps are:

<ol>
<li><p>If <var>name</var> is not a <a>valid attribute local name</a>, then <a>throw</a> an
"{{InvalidCharacterError!!exception}}" {{DOMException}}.

<li><p>Let <var>attributes</var> be <a>this</a>'s <a for=ProcessingInstruction>attribute map</a>.

<li>
<p>If <var>attributes</var>[<var>name</var>] does not <a for=set>exist</a>:

<ol>
<li><p>If <var>force</var> is not given or is true, set <var>attributes</var>[<var>name</var>]
to the empty string, <a>update data from attributes</a> for <a>this</a>, and then return true.

<li><p>Return false.
</ol>

<li><p>Otherwise, if <var>force</var> is not given or is false, <a for=map>remove</a>
<var>attributes</var>[<var>name</var>], <a>update data from attributes</a> for <a>this</a>, and
then return false.

<li><p>Return true.
</ol>
</div>

<div algorithm>
<p>The <dfn method for=ProcessingInstruction><code>hasAttribute(<var>name</var>)</code></dfn>
method steps are to return true if <a>this</a>'s
<a for=ProcessingInstruction>attribute map</a>[<var>name</var>] <a for=set>exists</a>; otherwise
false.
</div>

<div algorithm>
<p>To <dfn export for=ProcessingInstruction id=concept-pi-initialize>initialize</dfn> a
{{ProcessingInstruction}} <a for=/>node</a> <var>pi</var> with <var>target</var> and
<var>data</var>:

<ol>
<li><p>If <var>target</var> does not match the <code>[=XML/Name=]</code> production, then
<a>throw</a> an "{{InvalidCharacterError!!exception}}" {{DOMException}}.
<!-- DOM3 does not check for "xml" -->

<li><p>If <var>data</var> contains the string "<code>?></code>", then <a>throw</a> an
"{{InvalidCharacterError!!exception}}" {{DOMException}}. <!-- Gecko does this. -->

<li><p>Set <var>pi</var>'s <a for=ProcessingInstruction>target</a> to <var>target</var>.

<li><p>Set <var>pi</var>'s <a for=CharacterData>data</a> to <var>data</var>.

<li><p><a>Update attributes from data</a> for <var>pi</var>.
</ol>
</div>

<div algorithm>
<p>To <dfn>update attributes from data</dfn> for a {{ProcessingInstruction}} <a for=/>node</a>
<var>pi</var>:

<ol>
<li><p><a for=map>Clear</a> <var>pi</var>'s <a for=ProcessingInstruction>attribute map</a>.

<li><p>Let <var>context</var> be the result of <a>creating an element</a> given
<var>pi</var>'s <a>node document</a>, "<code>html</code>", and the <a>HTML namespace</a>.

<li><p>Let <var>markup</var> be the concatentation of "<code>&lt;attrs </code>",
<var>pi</var>'s <a for=CharacterData>data</a>, and
"<code>&gt;&lt;/attrs&gt;</code>".

<li><p>Let <var>fragment</var> be the result of invoking the
<a>fragment parsing algorithm steps</a> with <var>context</var> and <var>markup</var>. If this
throws an exception, then return.

<li>
<p>If <var>fragment</var>'s <a>first child</a> is an <a for=/>element</a>:

<ol>
<li><p>Let <var>element</var> be the <a>first child</a> of <var>fragment</var>.

<li><p>Assert: <var>element</var> is an <a for=/>element</a> with <a for=Element>local name</a>
"<code>attrs</code>".

<li><p><a for=list>For each</a> <var>attribute</var> of <var>element</var>'s
<a for=Element>attribute list</a>, set <var>pi</var>'s
<a for=ProcessingInstruction>attribute map</a>[<var>attribute</var>'s <a for=Attr>local name</a>]
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the local name here means ignoring the namespace, but with input like <?target attr="value" xmlns:bla="..." bla:attr="blavalue"?> in XML, the second attribute would clobber the first and the result would be a single attribute with value "blavalue". That seems worse than for example erroring if the namespace is not null.

This seems like something @zcorpan might have opinions on :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens for these cases today with xml-stylesheet? We also have to think about what happens here when you serialize and parse again. This strikes me as another reason not to rely on the XML parser.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://software.hixie.ch/utilities/js/live-dom-viewer/saved/14573

I'd expect the qualified name, so that bla:href is not treated as the href pseudo-attribute.

element.getAttribute() also uses qualified name.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't actually understand how this works with the XML parser. If you have x:href and href as pseudo-attributes in whatever order and no xmlns attribute, x:href should be an irrecoverable error, no?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just x:href would be an error, yes, but if xmlns:x="..." is also used, it would parse.

@zcorpan thanks for the test case, that shows that the line I've commented on does need to change. We could use the qualified name, but we could just as well skip any attributes that aren't in the null namespace, possibly also making use of namespaces here non-conforming.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, https://software.hixie.ch/utilities/js/live-dom-viewer/saved/14573 uses single quotes, which made me wonder. I put <?xml-stylesheet href='style.css'?> into an XHTML document and that already works in Chrome, Firefox and Safari.

Not sure why that works.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But <?xml-stylesheet x:href="" href="..."?> "works" fine today in browsers.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore the "huh" about single quotes, that's valid XML syntax and I just never knew...

But <?xml-stylesheet x:href="" href="..."?> "works" fine today in browsers.

Oh, I didn't realize that worked. It looks like the reason in Chromium is that this code path uses a SAX parser with a "start element" handler that just ignores namespaces. That's not possible to specify using the XML fragment parser like I attempted here.

One option is to measure how common this is and then change implementations to be stricter about this.

But this could all be moot if we don't use the XML parser at all, so let me try to summarize that discussion first.

to <var>attribute</var>'s <a for=Attr>value</a>.
</ol>
</ol>
</div>

<div algorithm>
<p>To <dfn>update data from attributes</dfn> for a {{ProcessingInstruction}} <a for=/>node</a>
<var>pi</var>:

<ol>
<li><p>Let <var>data</var> be the empty string.

<li>
<p><a for=map>For each</a> <var>name</var> → <var>value</var> of <var>pi</var>'s
<a for=ProcessingInstruction>attribute map</a>:

<ol>
<li><p>If <var>data</var> is not the empty string, then append U+0020 SPACE to <var>data</var>.

<li><p>Append <var>name</var> to <var>data</var>.

<li><p>Append U+003D (=) to <var>data</var>.

<li><p>Append U+0022 (") to <var>data</var>.

<li><p>Append the result of
<a href="https://html.spec.whatwg.org/#escapingString">escaping a string</a> given
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is because https://html.spec.whatwg.org/#escapingString isn't exported and obviously this needs to be fixed. I'm just not sure yet if the escaping should be the same for HTML and XML, or if it should be split like parsing.

<var>value</var> to <var>data</var>.

<li><p>Append U+0022 (") to <var>data</var>.
</ol>

<li><p><a>Replace data</a> of <var>pi</var> with 0, <var>pi</var>'s <a for=Node>length</a>,
<var>data</var>, and true.
</ol>
</div>


<h3 id=interface-comment>Interface {{Comment}}</h3>

Expand Down