Mypal/parser/html/java/htmlparser/gwt-src/nu/validator/htmlparser/gwt/BrowserTreeBuilder.java

478 lines
18 KiB
Java

/*
* Copyright (c) 2007 Henri Sivonen
* Copyright (c) 2008-2009 Mozilla Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package nu.validator.htmlparser.gwt;
import java.util.LinkedList;
import nu.validator.htmlparser.common.DocumentMode;
import nu.validator.htmlparser.impl.CoalescingTreeBuilder;
import nu.validator.htmlparser.impl.HtmlAttributes;
import org.xml.sax.SAXException;
import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;
class BrowserTreeBuilder extends CoalescingTreeBuilder<JavaScriptObject> {
private JavaScriptObject document;
private JavaScriptObject script;
private JavaScriptObject placeholder;
private boolean readyToRun;
private final LinkedList<ScriptHolder> scriptStack = new LinkedList<ScriptHolder>();
private class ScriptHolder {
private final JavaScriptObject script;
private final JavaScriptObject placeholder;
/**
* @param script
* @param placeholder
*/
public ScriptHolder(JavaScriptObject script,
JavaScriptObject placeholder) {
this.script = script;
this.placeholder = placeholder;
}
/**
* Returns the script.
*
* @return the script
*/
public JavaScriptObject getScript() {
return script;
}
/**
* Returns the placeholder.
*
* @return the placeholder
*/
public JavaScriptObject getPlaceholder() {
return placeholder;
}
}
protected BrowserTreeBuilder(JavaScriptObject document) {
super();
this.document = document;
installExplorerCreateElementNS(document);
}
private static native boolean installExplorerCreateElementNS(
JavaScriptObject doc) /*-{
if (!doc.createElementNS) {
doc.createElementNS = function (uri, local) {
if ("http://www.w3.org/1999/xhtml" == uri) {
return doc.createElement(local);
} else if ("http://www.w3.org/1998/Math/MathML" == uri) {
if (!doc.mathplayerinitialized) {
var obj = document.createElement("object");
obj.setAttribute("id", "mathplayer");
obj.setAttribute("classid", "clsid:32F66A20-7614-11D4-BD11-00104BD3F987");
document.getElementsByTagName("head")[0].appendChild(obj);
document.namespaces.add("m", "http://www.w3.org/1998/Math/MathML", "#mathplayer");
doc.mathplayerinitialized = true;
}
return doc.createElement("m:" + local);
} else if ("http://www.w3.org/2000/svg" == uri) {
if (!doc.renesisinitialized) {
var obj = document.createElement("object");
obj.setAttribute("id", "renesis");
obj.setAttribute("classid", "clsid:AC159093-1683-4BA2-9DCF-0C350141D7F2");
document.getElementsByTagName("head")[0].appendChild(obj);
document.namespaces.add("s", "http://www.w3.org/2000/svg", "#renesis");
doc.renesisinitialized = true;
}
return doc.createElement("s:" + local);
} else {
// throw
}
}
}
}-*/;
private static native boolean hasAttributeNS(JavaScriptObject element,
String uri, String localName) /*-{
return element.hasAttributeNS(uri, localName);
}-*/;
private static native void setAttributeNS(JavaScriptObject element,
String uri, String localName, String value) /*-{
element.setAttributeNS(uri, localName, value);
}-*/;
@Override protected void addAttributesToElement(JavaScriptObject element,
HtmlAttributes attributes) throws SAXException {
try {
for (int i = 0; i < attributes.getLength(); i++) {
String localName = attributes.getLocalNameNoBoundsCheck(i);
String uri = attributes.getURINoBoundsCheck(i);
if (!hasAttributeNS(element, uri, localName)) {
setAttributeNS(element, uri, localName,
attributes.getValueNoBoundsCheck(i));
}
}
} catch (JavaScriptException e) {
fatal(e);
}
}
private static native void appendChild(JavaScriptObject parent,
JavaScriptObject child) /*-{
parent.appendChild(child);
}-*/;
private static native JavaScriptObject createTextNode(JavaScriptObject doc,
String text) /*-{
return doc.createTextNode(text);
}-*/;
private static native JavaScriptObject getLastChild(JavaScriptObject node) /*-{
return node.lastChild;
}-*/;
private static native void extendTextNode(JavaScriptObject node, String text) /*-{
node.data += text;
}-*/;
@Override protected void appendCharacters(JavaScriptObject parent,
String text) throws SAXException {
try {
if (parent == placeholder) {
appendChild(script, createTextNode(document, text));
}
JavaScriptObject lastChild = getLastChild(parent);
if (lastChild != null && getNodeType(lastChild) == 3) {
extendTextNode(lastChild, text);
return;
}
appendChild(parent, createTextNode(document, text));
} catch (JavaScriptException e) {
fatal(e);
}
}
private static native boolean hasChildNodes(JavaScriptObject element) /*-{
return element.hasChildNodes();
}-*/;
private static native JavaScriptObject getFirstChild(
JavaScriptObject element) /*-{
return element.firstChild;
}-*/;
@Override protected void appendChildrenToNewParent(
JavaScriptObject oldParent, JavaScriptObject newParent)
throws SAXException {
try {
while (hasChildNodes(oldParent)) {
appendChild(newParent, getFirstChild(oldParent));
}
} catch (JavaScriptException e) {
fatal(e);
}
}
private static native JavaScriptObject createComment(JavaScriptObject doc,
String text) /*-{
return doc.createComment(text);
}-*/;
@Override protected void appendComment(JavaScriptObject parent,
String comment) throws SAXException {
try {
if (parent == placeholder) {
appendChild(script, createComment(document, comment));
}
appendChild(parent, createComment(document, comment));
} catch (JavaScriptException e) {
fatal(e);
}
}
@Override protected void appendCommentToDocument(String comment)
throws SAXException {
try {
appendChild(document, createComment(document, comment));
} catch (JavaScriptException e) {
fatal(e);
}
}
private static native JavaScriptObject createElementNS(
JavaScriptObject doc, String ns, String local) /*-{
return doc.createElementNS(ns, local);
}-*/;
@Override protected JavaScriptObject createElement(String ns, String name,
HtmlAttributes attributes) throws SAXException {
try {
JavaScriptObject rv = createElementNS(document, ns, name);
for (int i = 0; i < attributes.getLength(); i++) {
setAttributeNS(rv, attributes.getURINoBoundsCheck(i),
attributes.getLocalNameNoBoundsCheck(i),
attributes.getValueNoBoundsCheck(i));
}
if ("script" == name) {
if (placeholder != null) {
scriptStack.addLast(new ScriptHolder(script, placeholder));
}
script = rv;
placeholder = createElementNS(document,
"http://n.validator.nu/placeholder/", "script");
rv = placeholder;
for (int i = 0; i < attributes.getLength(); i++) {
setAttributeNS(rv, attributes.getURINoBoundsCheck(i),
attributes.getLocalNameNoBoundsCheck(i),
attributes.getValueNoBoundsCheck(i));
}
}
return rv;
} catch (JavaScriptException e) {
fatal(e);
throw new RuntimeException("Unreachable");
}
}
@Override protected JavaScriptObject createHtmlElementSetAsRoot(
HtmlAttributes attributes) throws SAXException {
try {
JavaScriptObject rv = createElementNS(document,
"http://www.w3.org/1999/xhtml", "html");
for (int i = 0; i < attributes.getLength(); i++) {
setAttributeNS(rv, attributes.getURINoBoundsCheck(i),
attributes.getLocalNameNoBoundsCheck(i),
attributes.getValueNoBoundsCheck(i));
}
appendChild(document, rv);
return rv;
} catch (JavaScriptException e) {
fatal(e);
throw new RuntimeException("Unreachable");
}
}
private static native JavaScriptObject getParentNode(
JavaScriptObject element) /*-{
return element.parentNode;
}-*/;
@Override protected void appendElement(JavaScriptObject child,
JavaScriptObject newParent) throws SAXException {
try {
if (newParent == placeholder) {
appendChild(script, cloneNodeDeep(child));
}
appendChild(newParent, child);
} catch (JavaScriptException e) {
fatal(e);
}
}
@Override protected boolean hasChildren(JavaScriptObject element)
throws SAXException {
try {
return hasChildNodes(element);
} catch (JavaScriptException e) {
fatal(e);
throw new RuntimeException("Unreachable");
}
}
private static native void insertBeforeNative(JavaScriptObject parent,
JavaScriptObject child, JavaScriptObject sibling) /*-{
parent.insertBefore(child, sibling);
}-*/;
private static native int getNodeType(JavaScriptObject node) /*-{
return node.nodeType;
}-*/;
private static native JavaScriptObject cloneNodeDeep(JavaScriptObject node) /*-{
return node.cloneNode(true);
}-*/;
/**
* Returns the document.
*
* @return the document
*/
JavaScriptObject getDocument() {
JavaScriptObject rv = document;
document = null;
return rv;
}
private static native JavaScriptObject createDocumentFragment(
JavaScriptObject doc) /*-{
return doc.createDocumentFragment();
}-*/;
JavaScriptObject getDocumentFragment() {
JavaScriptObject rv = createDocumentFragment(document);
JavaScriptObject rootElt = getFirstChild(document);
while (hasChildNodes(rootElt)) {
appendChild(rv, getFirstChild(rootElt));
}
document = null;
return rv;
}
/**
* @see nu.validator.htmlparser.impl.TreeBuilder#createJavaScriptObject(String,
* java.lang.String, org.xml.sax.Attributes, java.lang.Object)
*/
@Override protected JavaScriptObject createElement(String ns, String name,
HtmlAttributes attributes, JavaScriptObject form)
throws SAXException {
try {
JavaScriptObject rv = createElement(ns, name, attributes);
// rv.setUserData("nu.validator.form-pointer", form, null);
return rv;
} catch (JavaScriptException e) {
fatal(e);
return null;
}
}
/**
* @see nu.validator.htmlparser.impl.TreeBuilder#start()
*/
@Override protected void start(boolean fragment) throws SAXException {
script = null;
placeholder = null;
readyToRun = false;
}
protected void documentMode(DocumentMode mode, String publicIdentifier,
String systemIdentifier, boolean html4SpecificAdditionalErrorChecks)
throws SAXException {
// document.setUserData("nu.validator.document-mode", mode, null);
}
/**
* @see nu.validator.htmlparser.impl.TreeBuilder#elementPopped(java.lang.String,
* java.lang.String, java.lang.Object)
*/
@Override protected void elementPopped(String ns, String name,
JavaScriptObject node) throws SAXException {
if (node == placeholder) {
readyToRun = true;
requestSuspension();
}
}
private static native void replace(JavaScriptObject oldNode,
JavaScriptObject newNode) /*-{
oldNode.parentNode.replaceChild(newNode, oldNode);
}-*/;
private static native JavaScriptObject getPreviousSibling(JavaScriptObject node) /*-{
return node.previousSibling;
}-*/;
void maybeRunScript() {
if (readyToRun) {
readyToRun = false;
replace(placeholder, script);
if (scriptStack.isEmpty()) {
script = null;
placeholder = null;
} else {
ScriptHolder scriptHolder = scriptStack.removeLast();
script = scriptHolder.getScript();
placeholder = scriptHolder.getPlaceholder();
}
}
}
@Override protected void insertFosterParentedCharacters(String text,
JavaScriptObject table, JavaScriptObject stackParent)
throws SAXException {
try {
JavaScriptObject parent = getParentNode(table);
if (parent != null) { // always an element if not null
JavaScriptObject previousSibling = getPreviousSibling(table);
if (previousSibling != null
&& getNodeType(previousSibling) == 3) {
extendTextNode(previousSibling, text);
return;
}
insertBeforeNative(parent, createTextNode(document, text), table);
return;
}
JavaScriptObject lastChild = getLastChild(stackParent);
if (lastChild != null && getNodeType(lastChild) == 3) {
extendTextNode(lastChild, text);
return;
}
appendChild(stackParent, createTextNode(document, text));
} catch (JavaScriptException e) {
fatal(e);
}
}
@Override protected void insertFosterParentedChild(JavaScriptObject child,
JavaScriptObject table, JavaScriptObject stackParent)
throws SAXException {
JavaScriptObject parent = getParentNode(table);
try {
if (parent != null && getNodeType(parent) == 1) {
insertBeforeNative(parent, child, table);
} else {
appendChild(stackParent, child);
}
} catch (JavaScriptException e) {
fatal(e);
}
}
private static native void removeChild(JavaScriptObject parent,
JavaScriptObject child) /*-{
parent.removeChild(child);
}-*/;
@Override protected void detachFromParent(JavaScriptObject element)
throws SAXException {
try {
JavaScriptObject parent = getParentNode(element);
if (parent != null) {
removeChild(parent, element);
}
} catch (JavaScriptException e) {
fatal(e);
}
}
}