Skip to content

Commit

Permalink
Merge pull request iipc#119 from kngenie/ARI-4691
Browse files Browse the repository at this point in the history
Never insert bodyInset in NOSCRIPT element occuring inside HEAD
  • Loading branch information
kngenie committed Feb 27, 2016
2 parents f1a6995 + dbe4f06 commit 04301cb
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ public class FastArchivalUrlReplayParseEventHandler implements
private final static String BODY_TAG = "BODY";

protected static final String FERRET_HEAD_INSERTED = "FERRET_HEAD_INSERTED";

/**
* The key in {@code context} for storing state being inside a <NOSCRIPT> element.
* (inside <HEAD> only).
*/
protected static final String STATE_IN_NOSCRIPT = "STATE_NO_SCRIPT";

private BlockCSSStringTransformer cssBlockTrans = new BlockCSSStringTransformer();
private StringTransformer jsBlockTrans = new JSStringTransformer();
Expand Down Expand Up @@ -150,6 +156,10 @@ public void handleNode(ParseContext pContext, Node node) throws IOException {
if (tagNode.isEndTag()) {
if (tagNode.getTagName().equals("HEAD")) {
context.putData(FERRET_IN_HEAD, null);
// turn off NOSCIPT state as well. It's inside HEAD only.
context.putData(STATE_IN_NOSCRIPT, null);
} else if (tagNode.getTagName().equals("NOSCRIPT")) {
context.putData(STATE_IN_NOSCRIPT, null);
}

if (checkAllowTag(pContext, tagNode)) {
Expand Down Expand Up @@ -247,6 +257,7 @@ private void handleOpenTagNode(ReplayParseContext context, TagNode tagNode)
boolean alreadyInsertedHead = (context.getData(FERRET_HEAD_INSERTED) != null);
boolean insertedJsp = context.getData(FERRET_DONE_KEY) != null;
boolean inHead = (context.getData(FERRET_IN_HEAD) != null);
boolean inNoScript = (context.getData(STATE_IN_NOSCRIPT) != null);

if (!alreadyInsertedHead) {
// If we're at the beginning of a <head> tag, and haven't inserted yet,
Expand All @@ -268,6 +279,14 @@ private void handleOpenTagNode(ReplayParseContext context, TagNode tagNode)
emitHeadInsert(context, null, false);
// Don't return continue to further processing
}
} else if (tagName.equals("NOSCRIPT") && inHead) {
// NOSCRIPT under HEAD may contain elements that usually happen
// inside BODY. As long as they are inside NOSCRIPT, we let them go
// without inserting bodyInsert. Note this is NOSCRIPT under HEAD
// only. If <NOSCRIPT> happens before <HEAD>, non-HEAD elements in
// it would trigger bodyInsert. We may want to revise this behavior
// based on real-world examples.
context.putData(STATE_IN_NOSCRIPT, "");
} else if (tagName.equals(BODY_TAG) && inHead) {
context.putData(FERRET_IN_HEAD, null);
inHead = false;
Expand All @@ -285,7 +304,7 @@ private void handleOpenTagNode(ReplayParseContext context, TagNode tagNode)
} else if (tagName.equals(BODY_TAG)) {
postEmit = bodyInsertContent(context);
context.putData(FERRET_DONE_KEY, "");
} else if (!okHeadTagMap.containsKey(tagName)) {
} else if (!inNoScript && !okHeadTagMap.containsKey(tagName)) {
// hrm... we are seeing a node that should be in
// the body.. lets emit the jsp now, *before*
// the current Tag:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1141,6 +1141,75 @@ public void testDOMEventHandlers() throws Exception {
assertEquals(expected, output);
}

/**
* NOSCRIPT in HEAD can contain arbitrary element (we've seen IMG and IFRAME
* occuring inside NOSCRIPT), and it shall not trigger injection of
* body-inserts.
* @throws Exception
*/
public void testNOSCRIPT() throws Exception {
delegator.setHeadInsertJsp("head.jsp");
delegator.setJspInsertPath("body-insert.jsp");
jspExec = new TestJSPExecutor();

final String input = "<!DOCTYPE html>\n" +
"<head>\n" +
" <noscript>\n" +
" <img height=\"1\" width=\"1\" style=\"display:none\" src=\"ping.gif\">\n" +
" </noscript>\n" +
"</head>\n" +
"<body>\n" +
" body body\n" +
"</body>\n" +
"</html>\n";
final String expected = "<!DOCTYPE html>\n" +
"<head>[[[JSP-INSERT:head.jsp]]]\n" +
" <noscript>\n" +
" <img height=\"1\" width=\"1\" style=\"display:none\" src=\"ping.gif\">\n" +
" </noscript>\n" +
"</head>\n" +
"<body>[[[JSP-INSERT:body-insert.jsp]]]\n" +
" body body\n" +
"</body>\n" +
"</html>\n";;
String out = doEndToEnd(input);
//System.out.println(out);
assertEquals(expected, out);
}

/**
* pathological case of missing {@code </NOSCRIPT>}.
* {@code </HEAD>} shall close </NOSCRIPT> as well.
* @throws Exception
*/
public void testNOSCRIPT_missingCloseTag() throws Exception {
delegator.setHeadInsertJsp("head.jsp");
delegator.setJspInsertPath("body-insert.jsp");
jspExec = new TestJSPExecutor();

final String input = "<!DOCTYPE html>\n" +
"<head>\n" +
" <noscript>\n" +
" <img height=\"1\" width=\"1\" style=\"display:none\" src=\"ping.gif\">\n" +
"</head>\n" +
"<body>\n" +
" body body\n" +
"</body>\n" +
"</html>\n";
final String expected = "<!DOCTYPE html>\n" +
"<head>[[[JSP-INSERT:head.jsp]]]\n" +
" <noscript>\n" +
" <img height=\"1\" width=\"1\" style=\"display:none\" src=\"ping.gif\">\n" +
"</head>\n" +
"<body>[[[JSP-INSERT:body-insert.jsp]]]\n" +
" body body\n" +
"</body>\n" +
"</html>\n";;
String out = doEndToEnd(input);
//System.out.println(out);
assertEquals(expected, out);
}

public String doEndToEnd(String input) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(input.getBytes(charSet));
byte[] bytes = rewrite(bais);
Expand Down

0 comments on commit 04301cb

Please sign in to comment.