Author: lryan
Date: Thu Nov 20 15:12:25 2008
New Revision: 719420
URL: http://svn.apache.org/viewvc?rev=719420&view=rev
Log:
Make parsing more resilient to fragment parsing cases. In particular this fixes
an issue with script tags being migrated into head when only a fragment is
being parsed
Added:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/nekohtml/NekoParsersTest.java
- copied, changed from r719146,
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/nekohtml/NekoSimplifiedHtmlParserTest.java
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-fragment-expected.html
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-fragment.html
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-headnobody-expected.html
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-headnobody.html
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/nekohtml/NekoHtmlParser.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/nekohtml/NekoSimplifiedHtmlParser.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/HtmlParserTest.java
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java?rev=719420&r1=719419&r2=719420&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/GadgetHtmlParser.java
Thu Nov 20 15:12:25 2008
@@ -27,6 +27,8 @@
import com.google.inject.Inject;
import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Node;
/**
* Parser for arbitrary HTML content
@@ -99,4 +101,37 @@
* @throws GadgetException
*/
protected abstract Document parseDomImpl(String source) throws
GadgetException;
+
+ /**
+ * Normalize head and body tags in the passed fragment before including it
+ * in the document
+ * @param document
+ * @param fragment
+ */
+ protected void normalizeFragment(Document document, DocumentFragment
fragment) {
+ Node htmlNode = DomUtil.getFirstNamedChildNode(fragment, "HTML");
+ if (htmlNode != null) {
+ document.appendChild(htmlNode);
+ } else {
+ Node bodyNode = DomUtil.getFirstNamedChildNode(fragment, "body");
+ Node headNode = DomUtil.getFirstNamedChildNode(fragment, "head");
+ if (bodyNode != null || headNode != null) {
+ // We have either a head or body so put fragment into HTML tag
+ Node root = document.appendChild(document.createElement("html"));
+ if (headNode != null && bodyNode == null) {
+ fragment.removeChild(headNode);
+ root.appendChild(headNode);
+ Node body = root.appendChild(document.createElement("body"));
+ body.appendChild(fragment);
+ } else {
+ root.appendChild(fragment);
+ }
+ } else {
+ // No head or body so put fragment into a body
+ Node root = document.appendChild(document.createElement("html"));
+ Node body = root.appendChild(document.createElement("body"));
+ body.appendChild(fragment);
+ }
+ }
+ }
}
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/nekohtml/NekoHtmlParser.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/nekohtml/NekoHtmlParser.java?rev=719420&r1=719419&r2=719420&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/nekohtml/NekoHtmlParser.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/nekohtml/NekoHtmlParser.java
Thu Nov 20 15:12:25 2008
@@ -18,7 +18,6 @@
package org.apache.shindig.gadgets.parse.nekohtml;
import org.apache.shindig.gadgets.GadgetException;
-import org.apache.shindig.gadgets.parse.DomUtil;
import org.apache.shindig.gadgets.parse.GadgetHtmlParser;
import org.apache.shindig.gadgets.parse.HtmlSerializer;
@@ -31,7 +30,6 @@
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
-import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
@@ -72,18 +70,12 @@
InputSource input = new InputSource(new StringReader(source));
DOMFragmentParser parser = new DOMFragmentParser();
parser.setProperty("http://cyberneko.org/html/properties/names/elems",
"default");
-
+ parser.setFeature("http://cyberneko.org/html/features/document-fragment",
true);
Document htmlDoc = documentProvider.createDocument(null, null, null);
DocumentFragment fragment = htmlDoc.createDocumentFragment();
parser.parse(input, fragment);
- Node htmlNode = DomUtil.getFirstNamedChildNode(fragment, "HTML");
- if (htmlNode != null) {
- htmlDoc.appendChild(htmlNode);
- } else {
- Node root = htmlDoc.appendChild(htmlDoc.createElement("HTML"));
- root.appendChild(fragment);
- }
+ normalizeFragment(htmlDoc, fragment);
return htmlDoc;
}
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/nekohtml/NekoSimplifiedHtmlParser.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/nekohtml/NekoSimplifiedHtmlParser.java?rev=719420&r1=719419&r2=719420&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/nekohtml/NekoSimplifiedHtmlParser.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/parse/nekohtml/NekoSimplifiedHtmlParser.java
Thu Nov 20 15:12:25 2008
@@ -17,7 +17,6 @@
*/
package org.apache.shindig.gadgets.parse.nekohtml;
-import org.apache.shindig.gadgets.parse.DomUtil;
import org.apache.shindig.gadgets.parse.GadgetHtmlParser;
import org.apache.shindig.gadgets.parse.HtmlSerializer;
@@ -38,6 +37,7 @@
import org.apache.xerces.xni.parser.XMLInputSource;
import org.apache.xml.serialize.HTMLSerializer;
import org.apache.xml.serialize.OutputFormat;
+import org.cyberneko.html.HTMLConfiguration;
import org.cyberneko.html.HTMLElements;
import org.cyberneko.html.HTMLEntities;
import org.cyberneko.html.HTMLScanner;
@@ -79,6 +79,11 @@
tagBalancer.setDocumentHandler(handler);
htmlScanner.setDocumentHandler(tagBalancer);
+ HTMLConfiguration config = new HTMLConfiguration();
+ config.setProperty("http://cyberneko.org/html/properties/names/elems",
"match");
+
config.setFeature("http://cyberneko.org/html/features/balance-tags/document-fragment",
true);
+ tagBalancer.reset(config);
+ htmlScanner.reset(config);
XMLInputSource inputSource = new XMLInputSource(null, null, null);
inputSource.setEncoding("UTF-8");
inputSource.setCharacterStream(new StringReader(source));
@@ -87,13 +92,7 @@
htmlScanner.scanDocument(true);
Document document = handler.getDocument();
DocumentFragment fragment = handler.getFragment();
- Node htmlNode = DomUtil.getFirstNamedChildNode(fragment, "HTML");
- if (htmlNode != null) {
- document.appendChild(htmlNode);
- } else {
- Node root = document.appendChild(document.createElement("HTML"));
- root.appendChild(fragment);
- }
+ normalizeFragment(document, fragment);
HtmlSerializer.attach(document, new Serializer(), source);
return document;
} catch (IOException ioe) {
@@ -101,7 +100,6 @@
}
}
-
/**
* Handler for XNI events from Neko
*/
@@ -152,7 +150,6 @@
public void doctypeDecl(String rootElement, String publicId, String
systemId,
Augmentations augs) throws XNIException {
- // Recreate the document with the specific doctype
document = documentFactory.createDocument(null, null,
documentFactory.createDocumentType(rootElement, publicId, systemId));
elementStack.clear();
@@ -339,6 +336,7 @@
this._printer.printText(s);
}
};
+
try {
serializer.serialize(doc);
return sw.toString();
Modified:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/HtmlParserTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/HtmlParserTest.java?rev=719420&r1=719419&r2=719420&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/HtmlParserTest.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/HtmlParserTest.java
Thu Nov 20 15:12:25 2008
@@ -17,130 +17,74 @@
*/
package org.apache.shindig.gadgets.parse;
-import junit.framework.TestCase;
-import org.apache.shindig.gadgets.parse.caja.CajaHtmlParser;
import org.apache.shindig.gadgets.parse.nekohtml.NekoHtmlParser;
+import org.apache.shindig.gadgets.rewrite.XPathWrapper;
+
import org.w3c.dom.Document;
-import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
+import junit.framework.TestCase;
+
/**
* Note these tests are of marginal use. Consider removing. More useful tests
would exercise
* the capability of the parser to handle strange HTML.
*/
public class HtmlParserTest extends TestCase {
- private final GadgetHtmlParser cajaParser = new CajaHtmlParser(
- new ParseModule.DOMImplementationProvider().get());
-
private final GadgetHtmlParser nekoParser = new NekoHtmlParser(
new ParseModule.DOMImplementationProvider().get());
public void testParseSimpleString() throws Exception {
- parseSimpleString(cajaParser);
parseSimpleString(nekoParser);
}
private void parseSimpleString(GadgetHtmlParser htmlParser) throws Exception
{
Document doc = htmlParser.parseDom("content");
-
- Node node = doc.getDocumentElement().getFirstChild().getNextSibling();
- assertNotNull(node);
- assertEquals("content", node.getTextContent());
- assertNull(node.getAttributes());
- assertNullOrEmpty(node.getChildNodes());
- assertEquals(Node.TEXT_NODE, node.getNodeType());
+ XPathWrapper wrapper = new XPathWrapper(doc);
+ assertEquals("content", wrapper.getValue("/html/body"));
}
public void testParseTagWithStringContents() throws Exception {
parseTagWithStringContents(nekoParser);
- parseTagWithStringContents(cajaParser);
}
void parseTagWithStringContents(GadgetHtmlParser htmlParser) throws
Exception {
Document doc = htmlParser.parseDom("<span>content</span>");
-
- Node node = doc.getDocumentElement().getFirstChild().getNextSibling();
- assertEquals("content", node.getTextContent());
- assertEquals("span", node.getNodeName().toLowerCase());
+ XPathWrapper wrapper = new XPathWrapper(doc);
+ assertEquals("content", wrapper.getValue("/html/body/span"));
}
public void testParseTagWithAttributes() throws Exception {
parseTagWithAttributes(nekoParser);
- parseTagWithAttributes(cajaParser);
}
void parseTagWithAttributes(GadgetHtmlParser htmlParser) throws Exception {
Document doc = htmlParser.parseDom("<div id=\"foo\">content</div>");
-
- Node node = doc.getDocumentElement().getFirstChild().getNextSibling();
- assertNotNull(node);
- assertNotNull(node.getAttributes());
- assertEquals(1, node.getAttributes().getLength());
- assertEquals("id", node.getAttributes().item(0).getNodeName());
- assertEquals("foo", node.getAttributes().item(0).getNodeValue());
- assertNotNull(node.getChildNodes());
- assertEquals(1, node.getChildNodes().getLength());
- assertEquals("content", node.getChildNodes().item(0).getTextContent());
+ XPathWrapper wrapper = new XPathWrapper(doc);
+ assertEquals("content", wrapper.getValue("/html/body/div"));
+ assertEquals("foo", wrapper.getValue("/html/body/div/@id"));
}
public void testParseStringUnescapesProperly() throws Exception {
parseStringUnescapesProperly(nekoParser);
- parseStringUnescapesProperly(cajaParser);
}
void parseStringUnescapesProperly(GadgetHtmlParser htmlParser) throws
Exception {
Document doc =
htmlParser.parseDom("<content&'chrome'>");
-
- Node node = doc.getDocumentElement().getFirstChild().getNextSibling();
- assertNotNull(node);
- assertEquals("<content&'chrome'>", node.getTextContent());
- assertNull(node.getAttributes());
- assertNullOrEmpty(node.getChildNodes());
+ XPathWrapper wrapper = new XPathWrapper(doc);
+ assertEquals("<content&'chrome'>", wrapper.getValue("/html/body"));
}
public void testParseNestedContentWithNoCloseForBrAndHr() throws Exception {
parseNestedContentWithNoCloseForBrAndHr(nekoParser);
- parseNestedContentWithNoCloseForBrAndHr(cajaParser);
}
void parseNestedContentWithNoCloseForBrAndHr(GadgetHtmlParser htmlParser)
throws Exception {
- Document doc = htmlParser.parseDom("<div><br> and <hr></div>");
-
- Node divNode = doc.getDocumentElement().getFirstChild().getNextSibling();
- assertEquals("div", divNode.getNodeName().toLowerCase());
- assertNotNull(divNode.getAttributes());
- assertEquals(0, divNode.getAttributes().getLength());
- assertNotNull(divNode.getChildNodes());
- assertEquals(3, divNode.getChildNodes().getLength());
-
- {
- // <br>
- Node divChild = divNode.getChildNodes().item(0);
- assertNotNull(divChild);
- assertEquals("br", divChild.getNodeName().toLowerCase());
- assertNotNull(divChild.getAttributes());
- assertEquals(0, divChild.getAttributes().getLength());
- assertEquals(0, divChild.getChildNodes().getLength());
- }
-
- {
- // text
- Node divChild = divNode.getChildNodes().item(1);
- assertEquals(" and ", divChild.getTextContent());
- assertNull(divChild.getAttributes());
- assertNullOrEmpty(divChild.getChildNodes());
- }
-
- {
- // <hr> should be parsed lieniently
- Node divChild = divNode.getChildNodes().item(2);
- assertNotNull(divChild);
- assertEquals("hr", divChild.getNodeName().toLowerCase());
- assertNotNull(divChild.getAttributes());
- assertEquals(0, divChild.getAttributes().getLength());
- assertEquals(0, divChild.getChildNodes().getLength());
- }
+ Document doc = htmlParser.parseDom("<div>x and y<br> and <hr>z</div>");
+ XPathWrapper wrapper = new XPathWrapper(doc);
+ assertEquals("x and y and z", wrapper.getValue("/html/body/div"));
+ assertEquals(1, wrapper.getNodeList("/html/body/div/br").getLength());
+ assertEquals(1, wrapper.getNodeList("/html/body/div/hr").getLength());
}
// TODO: figure out to what extent it makes sense to test "invalid"
Copied:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/nekohtml/NekoParsersTest.java
(from r719146,
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/nekohtml/NekoSimplifiedHtmlParserTest.java)
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/nekohtml/NekoParsersTest.java?p2=incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/nekohtml/NekoParsersTest.java&p1=incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/nekohtml/NekoSimplifiedHtmlParserTest.java&r1=719146&r2=719420&rev=719420&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/nekohtml/NekoSimplifiedHtmlParserTest.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/parse/nekohtml/NekoParsersTest.java
Thu Nov 20 15:12:25 2008
@@ -17,30 +17,56 @@
*/
package org.apache.shindig.gadgets.parse.nekohtml;
-import org.apache.commons.io.IOUtils;
+import org.apache.shindig.gadgets.parse.GadgetHtmlParser;
import org.apache.shindig.gadgets.parse.HtmlSerializer;
import org.apache.shindig.gadgets.parse.ParseModule;
-import junit.framework.TestCase;
+import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
+import junit.framework.TestCase;
+
/**
* Test behavior of simplified HTML parser
*/
-public class NekoSimplifiedHtmlParserTest extends TestCase {
+public class NekoParsersTest extends TestCase {
+
+ private NekoSimplifiedHtmlParser simple = new NekoSimplifiedHtmlParser(
+ new ParseModule.DOMImplementationProvider().get());
+ private NekoHtmlParser full = new NekoHtmlParser(
+ new ParseModule.DOMImplementationProvider().get());
public void testParser() throws Exception {
String content = IOUtils.toString(this.getClass().getClassLoader().
getResourceAsStream("org/apache/shindig/gadgets/parse/nekohtml/test.html"));
String expected = IOUtils.toString(this.getClass().getClassLoader().
getResourceAsStream("org/apache/shindig/gadgets/parse/nekohtml/test-expected.html"));
- parseAndCompareBalanced(content, expected);
+ parseAndCompareBalanced(content, expected, full);
+ parseAndCompareBalanced(content, expected, simple);
}
- private void parseAndCompareBalanced(String content, String expected) throws
Exception {
- NekoSimplifiedHtmlParser builder = new NekoSimplifiedHtmlParser(
- new ParseModule.DOMImplementationProvider().get());
- Document document = builder.parseDom(content);
+ public void testNotADocument() throws Exception {
+ String content = IOUtils.toString(this.getClass().getClassLoader().
+
getResourceAsStream("org/apache/shindig/gadgets/parse/nekohtml/test-fragment.html"));
+ String expected = IOUtils.toString(this.getClass().getClassLoader().
+
getResourceAsStream("org/apache/shindig/gadgets/parse/nekohtml/test-fragment-expected.html"));
+ parseAndCompareBalanced(content, expected, full);
+ parseAndCompareBalanced(content, expected, simple);
+ }
+
+ public void testNoBody() throws Exception {
+ String content = IOUtils.toString(this.getClass().getClassLoader().
+
getResourceAsStream("org/apache/shindig/gadgets/parse/nekohtml/test-headnobody.html"));
+ String expected = IOUtils.toString(this.getClass().getClassLoader().
+ getResourceAsStream(
+
"org/apache/shindig/gadgets/parse/nekohtml/test-headnobody-expected.html"));
+ parseAndCompareBalanced(content, expected, full);
+ parseAndCompareBalanced(content, expected, simple);
+ }
+
+ private void parseAndCompareBalanced(String content, String expected,
GadgetHtmlParser parser)
+ throws Exception {
+ Document document = parser.parseDom(content);
assertEquals(expected, HtmlSerializer.serialize(document));
}
}
Added:
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-fragment-expected.html
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-fragment-expected.html?rev=719420&view=auto
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-fragment-expected.html
(added)
+++
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-fragment-expected.html
Thu Nov 20 15:12:25 2008
@@ -0,0 +1,3 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
+<html><head></head><body><script>document.write("dont add to head or
else")</script>
+<style type="text/css"> can go in head</style></body></html>
\ No newline at end of file
Added:
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-fragment.html
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-fragment.html?rev=719420&view=auto
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-fragment.html
(added)
+++
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-fragment.html
Thu Nov 20 15:12:25 2008
@@ -0,0 +1,2 @@
+<script>document.write("dont add to head or else")</script>
+<style type="text/css"> can go in head</style>
\ No newline at end of file
Added:
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-headnobody-expected.html
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-headnobody-expected.html?rev=719420&view=auto
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-headnobody-expected.html
(added)
+++
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-headnobody-expected.html
Thu Nov 20 15:12:25 2008
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
+<html><head>
+ <!-- A head tag but no body tag is not good -->
+</head><body>
+<script>document.write("dont add to head or else")</script>
+<style type="text/css"> can go in head</style></body></html>
\ No newline at end of file
Added:
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-headnobody.html
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-headnobody.html?rev=719420&view=auto
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-headnobody.html
(added)
+++
incubator/shindig/trunk/java/gadgets/src/test/resources/org/apache/shindig/gadgets/parse/nekohtml/test-headnobody.html
Thu Nov 20 15:12:25 2008
@@ -0,0 +1,5 @@
+<head>
+ <!-- A head tag but no body tag is not good -->
+</head>
+<script>document.write("dont add to head or else")</script>
+<style type="text/css"> can go in head</style>
\ No newline at end of file