--- markup/html/scripting-parser/parser.html 2008/04/27 09:16:11 1.9 +++ markup/html/scripting-parser/parser.html 2008/04/27 11:21:09 1.12 @@ -47,7 +47,12 @@ var p = new Parser (new InputStream (v)); var doc = p.doc; p.parse (); + log (dumpTree (doc, '')); + + if (p.hasAsyncScript) { + log ('Some script codes are executed asynchronously; it means that the document might be rendered in different ways depending on the network condition and other factors'); + } } } // update2 @@ -73,6 +78,8 @@ this.openElements = [doc]; this.input = i; this.scriptsExecutedAfterParsing = []; + this.scriptsExecutedSoon = []; + this.scriptsExecutedAsynchronously = []; } // Parser Parser.prototype.getNextToken = function () { @@ -329,8 +336,41 @@ // "When a script completes loading" rules start applying. - // TODO: Handles "list of scripts that will execute as soon as possible" - // and "list of scripts that will execute asynchronously" + while (this.scriptsExecutedSoon.length > 0 || + this.scriptsExecutedAsynchronously.length > 0) { + // Handle "list of scripts that will execute as soon as possible". + while (this.scriptsExecutedSoon.length > 0) { + var e = this.scriptsExecutedSoon.shift (); + + // If it has completed loading + log ('Execute an external script not inserted by parser...'); + executeScript (this.doc, e); + + // NOTE: It MAY be executed before the end of the parsing, according + // to the spec. + this.hasAsyncScript = true; + } + + // Handle "list of scripts that will execute asynchronously". + while (this.scriptsExecutedAsynchronously.length > 0) { + var e = this.scriptsExecutedAsynchronously.shift (); + + // Step 1. + // We assume that all scripts have been loaded at this time. + + // Step 2. + log ('Execute an asynchronous script...'); + executeScript (this.doc, e); + + // Step 3. + // + + // Step 4. + // + + this.hasAsyncScript = true; + } + } // Handle "list of scripts that will execute when the document has finished // parsing". @@ -425,18 +465,22 @@ p.scriptsExecutedAfterParsing.push (e); log ('Running a script: aborted (defer)'); } else if (e.async && e.src != null) { - // TODO - } else if (e.async && e.src == null - /* && list of scripts that will execute asynchronously is not empty */) { - // TODO + p.scriptsExecutedAsynchronously.push (e); + log ('Running a script: aborted (async src)'); + } else if (e.async && e.src == null && + p.scriptsExecutedAsynchronously.length > 0) { + p.scriptsExecutedAsynchronously.push (e); + log ('Running a script: aborted (async)'); + // ISSUE: What is the difference with the case above? } else if (e.src != null && e.manakaiParserInserted) { if (p.scriptExecutedWhenParserResumes) { log ('Error: There is a script that will execute as soon as the parser resumes.'); } p.scriptExecutedWhenParserResumes = e; - log ('Running a script: aborted (src)'); + log ('Running a script: aborted (src parser-inserted)'); } else if (e.src != null) { - // TODO + p.scriptsExecutedSoon.push (e); + log ('Running a script: aborted (src)'); } else { executeScript (doc, e); // even if other scripts are already executing. } @@ -484,9 +528,9 @@ var m; if (m = uri.match (/^javascript:\s*(?:'([^']*)'|"([^"]+)")\s*$/i)) { if (m[1]) { - return m[1]; + return unescapeJSLiteral (m[1]); } else if (m[2]) { - return m[2]; + return unescapeJSLiteral (m[2]); } else { return null; } @@ -507,12 +551,19 @@ matched = true; var args = []; t.replace (/('[^']*'|"[^"]*")/g, function (s, v) { - args.push (v.substring (1, v.length - 1)); + args.push (unescapeJSLiteral (v.substring (1, v.length - 1))); return ''; }); doc.write.apply (doc, args); return ''; }); + s = s.replace (/^\s*var\s+s\s*=\s*document\.createElement\s*\(\s*['"]script['"]\s*\)\s*;\s*s\.src\s*=\s*(?:'(javascript:[^']*)'|"(javascript:[^"]*)")\s*;\s*document\.documentElement\.appendChild\s*\(\s*s\s*\)\s*;\s*/, + function (s, t, u) { + matched = true; + var args = [unescapeJSLiteral (t ? t : u)]; + doc._insertExternalScript.apply (doc, args); + return ''; + }); if (s == '') break; if (!matched) { log ('Script parse error: "' + s + '"'); @@ -521,6 +572,12 @@ } } // parseAndRunScript + function unescapeJSLiteral (s) { + return s.replace (/\\u([0-9A-Fa-f]{4})/g, function (t, v) { + return String.fromCharCode (parseInt ('0x' + v)); + }); + } // unescapeJSLiteral + function JSText (data) { this.data = data; } // JSText @@ -631,6 +688,22 @@ return; }; // document.write + JSDocument.prototype._insertExternalScript = function (uri) { + var s = new JSElement (this, 'script'); + s.src = uri; + this.documentElement.appendChild (s); + }; // _insertExternalScript + + JSDocument.prototype.__defineGetter__ ('documentElement', function () { + var cn = this.childNodes; + for (var i = 0; i < cn.length; i++) { + if (cn[i] instanceof JSElement) { + return cn[i] + } + } + return null; + }); + JSElement.prototype.__defineGetter__ ('text', function () { var r = ''; for (var i = 0; i < this.childNodes.length; i++) { @@ -700,10 +773,10 @@ <p> -

Log

+

Log

-

Note

+

Notes

This is a simplified implementation of HTML5 @@ -725,19 +798,40 @@

  • Only supports script type text/javascript. type and language attributes are ignored. -
  • Only supports document.write. -The script code must be match to the regular expression -^\s*(?:document\.write\s*\(v\s*(?:,\s*v\s*)*\)\s*;\s*)*$ -where v is "[^"]*"|'[^']*'. +
  • Only supports limited statements. It must consist of zero or more +of statements looking similar to the following statements, possibly +introduced, followed, or separated by white space characters: + +Note that strings may be delimited by 's instead of +"s.
  • Only supports javascript: URI scheme in the src attribute of the script element. In addition, the URI must be conform to the regular expression ^javascript:\s*(?:"[^"]*"|'[^']*')\s*$. +
  • Only supports \uHHHH escapes in JavaScript +string literals. +
  • Does not handle stop parsing phase correctly if the document is +replaced by document.open () call. In other word, delayed +(deferred or asynchronous) script executions and event firings might be +treated in a wrong way if a document.open () invocation +is implicitly done by document.write () in a delayed script.

    For some reason, this parser does not work in browsers that do not support JavaScript 1.5. + + + + \ No newline at end of file