1. 8.4 Dynamic markup insertion
      1. 8.4.1 Opening the input stream
      2. 8.4.2 Closing the input stream
      3. 8.4.3 document.write()
      4. 8.4.4 document.writeln()

8.4 Dynamic markup insertion

APIs for dynamically inserting markup into the document interact with the parser, and thus their behavior varies depending on whether they are used with HTML documents (and the HTML parser) or XML documents (and the XML parser).

Document objects have a throw-on-dynamic-markup-insertion counter, which is used in conjunction with the create an element for the token algorithm to prevent custom element constructors from being able to use document.open(), document.close(), and document.write() when they are invoked by the parser. Initially, the counter must be set to zero.

8.4.1 Opening the input stream

document = document . open( )

Causes the Document to be replaced in-place, as if it was a new Document object, but reusing the previous object, which is then returned.

The resulting Document has an HTML parser associated with it, which can be given data to parse using document.write().

The method has no effect if the Document is still being parsed.

Throws an "InvalidStateError" DOMException if the Document is an XML document.

Throws an "InvalidStateError" DOMException if the parser is currently executing a custom element constructor.

window = document . open( url, name, features )

Works like the window.open() method.

Document objects have an ignore-opens-during-unload counter, which is used to prevent scripts from invoking the document.open() method (directly or indirectly) while the document is being unloaded. Initially, the counter must be set to zero.

The document open steps, given a document, are as follows:

  1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.

  2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.

  3. Let entryDocument be the entry global object's associated Document.

  4. If document's origin is not same origin to entryDocument's origin, then throw a "SecurityError" DOMException.

  5. If document has an active parser whose script nesting level is greater than 0, then return document.

    This basically causes document.open() to be ignored when it's called in an inline script found during parsing, while still letting it have an effect when called from a non-parser task such as a timer callback or event handler.

  6. Similarly, if document's ignore-opens-during-unload counter is greater than 0, then return document.

    This basically causes document.open() to be ignored when it's called from a beforeunload, pagehide, or unload event handler while the Document is being unloaded.

  7. If document's browsing context is non-null and there is an existing attempt to navigate document's browsing context, then stop document loading given document.

    Issue #3447 looks into the distinction between an ongoing instance of the navigate algorithm versus tasks to navigate that are still queued. For the purpose of implementing this step, both an ongoing instance of the navigate algorithm and tasks queued to navigate should be counted towards "an existing attempt to navigate," at least until that issue is resolved.

  8. For each shadow-including inclusive descendant node of document, erase all event listeners and handlers given node.

  9. If document is the associated Document of document's relevant global object, then erase all event listeners and handlers given document's relevant global object.

  10. Replace all with null within document, without firing any mutation events.

  11. If document is fully active, then:

    1. Let newURL be a copy of entryDocument's URL.

    2. If entryDocument is not document, then set newURL's fragment to null.

    3. Run the URL and history update steps with document and newURL.

  12. If document's iframe load in progress flag is set, then set document's mute iframe load flag.

  13. Set document to no-quirks mode.

  14. Create a new HTML parser and associate it with document. This is a script-created parser (meaning that it can be closed by the document.open() and document.close() methods, and that the tokenizer will wait for an explicit call to document.close() before emitting an end-of-file token). The encoding confidence is irrelevant.

  15. Set the current document readiness of document to "loading".

  16. Finally, set the insertion point to point at just before the end of the input stream (which at this point will be empty).

  17. Return document.

The document open steps do not affect whether a Document is ready for post-load tasks or completely loaded.

The open(unused1, unused2) method must return the result of running the document open steps with this Document object.

The unused1 and unused2 arguments are ignored, but kept in the IDL to allow code that calls the function with one or two arguments to continue working. They are necessary due to Web IDL overload resolution algorithm rules, which would throw a TypeError exception for such calls had the arguments not been there. heycam/webidl issue #581 investigates changing the algorithm to allow for their removal. [WEBIDL]

The open(url, name, features) method must run these steps:

  1. If this Document object is not an active document, then throw an "InvalidAccessError" DOMException exception.

  2. Return the result of running the window open steps with url, name, and features.

8.4.2 Closing the input stream

document . close()

Closes the input stream that was opened by the document.open() method.

Throws an "InvalidStateError" DOMException if the Document is an XML document.

Throws an "InvalidStateError" DOMException if the parser is currently executing a custom element constructor.

The close() method must run the following steps:

  1. If the Document object is an XML document, then throw an "InvalidStateError" DOMException.

  2. If the Document object's throw-on-dynamic-markup-insertion counter is greater than zero, then throw an "InvalidStateError" DOMException.

  3. If there is no script-created parser associated with the document, then return.

  4. Insert an explicit "EOF" character at the end of the parser's input stream.

  5. If there is a pending parsing-blocking script, then return.

  6. Run the tokenizer, processing resulting tokens as they are emitted, and stopping when the tokenizer reaches the explicit "EOF" character or spins the event loop.

8.4.3 document.write()

document . write(text...)

In general, adds the given string(s) to the Document's input stream.

This method has very idiosyncratic behavior. In some cases, this method can affect the state of the HTML parser while the parser is running, resulting in a DOM that does not correspond to the source of the document (e.g. if the string written is the string "<plaintext>" or "<!--"). In other cases, the call can clear the current page first, as if document.open() had been called. In yet more cases, the method is simply ignored, or throws an exception. Users agents are explicitly allowed to avoid executing script elements inserted via this method. And to make matters even worse, the exact behavior of this method can in some cases be dependent on network latency, which can lead to failures that are very hard to debug. For all these reasons, use of this method is strongly discouraged.

Throws an "InvalidStateError" DOMException when invoked on XML documents.

Throws an "InvalidStateError" DOMException if the parser is currently executing a custom element constructor.

Document objects have an ignore-destructive-writes counter, which is used in conjunction with the processing of script elements to prevent external scripts from being able to use document.write() to blow away the document by implicitly calling document.open(). Initially, the counter must be set to zero.

The document write steps, given a Document object document and a string input, are as follows:

  1. If document is an XML document, then throw an "InvalidStateError" DOMException.

  2. If document's throw-on-dynamic-markup-insertion counter is greater than 0, then throw an "InvalidStateError" DOMException.

  3. If the insertion point is undefined, then:

    1. If document's ignore-opens-during-unload counter is greater than 0 or document's ignore-destructive-writes counter is greater than 0, then return.

    2. Run the document open steps with document.

  4. Insert input into the input stream just before the insertion point.

  5. If there is no pending parsing-blocking script, have the HTML parser process input, one code point at a time, processing resulting tokens as they are emitted, and stopping when the tokenizer reaches the insertion point or when the processing of the tokenizer is aborted by the tree construction stage (this can happen if a script end tag token is emitted by the tokenizer).

    If the document.write() method was called from script executing inline (i.e. executing because the parser parsed a set of script tags), then this is a reentrant invocation of the parser. If the parser pause flag is set, the tokenizer will abort immediately and no HTML will be parsed, per the tokenizer's parser pause flag check.

The document.write(...) method, when invoked, must run the document write steps with this Document object and a string that is the concatanation of all arguments passed.

8.4.4 document.writeln()

document . writeln(text...)

Adds the given string(s) to the Document's input stream, followed by a newline character. If necessary, calls the open() method implicitly first.

Throws an "InvalidStateError" DOMException when invoked on XML documents.

Throws an "InvalidStateError" DOMException if the parser is currently executing a custom element constructor.

The document.writeln(...) method, when invoked, must run the document write steps with this Document object and a string that is the concatanation of all arguments passed and U+000A LINE FEED.