# HG changeset patch # User andrew # Date 1571077186 -3600 # Node ID 77f4748f44065feae19e1fc17c8c69349b1bdc8d # Parent 3ef585ecd995ef0547c911b6d5abf362d3c392c6# Parent 6b6305a82223dc6a10ff1fa630499af422b309e5 Merge jdk7u241-b01 diff -r 3ef585ecd995 -r 77f4748f4406 .hgtags --- a/.hgtags Wed Aug 14 18:40:41 2019 +0100 +++ b/.hgtags Mon Oct 14 19:19:46 2019 +0100 @@ -709,3 +709,6 @@ 54de827b5db66fb0fd25dc492db1f25aa87c8e11 jdk7u231-b01 219e16f3659b8c87bd3c1751152c95ff2fd83cf9 icedtea-2.6.19 219e16f3659b8c87bd3c1751152c95ff2fd83cf9 icedtea-2.6.20pre00 +54de827b5db66fb0fd25dc492db1f25aa87c8e11 jdk7u231-ga +54de827b5db66fb0fd25dc492db1f25aa87c8e11 jdk7u241-b00 +b317ddb0ec6dc011d1a64dcf2090465c70d57c14 jdk7u241-b01 diff -r 3ef585ecd995 -r 77f4748f4406 src/share/classes/com/sun/tools/javadoc/JavaScriptScanner.java --- a/src/share/classes/com/sun/tools/javadoc/JavaScriptScanner.java Wed Aug 14 18:40:41 2019 +0100 +++ b/src/share/classes/com/sun/tools/javadoc/JavaScriptScanner.java Mon Oct 14 19:19:46 2019 +0100 @@ -68,12 +68,10 @@ private boolean newline = true; Map tagParsers; - Set eventAttrs; Set uriAttrs; public JavaScriptScanner() { initTagParsers(); - initEventAttrs(); initURIAttrs(); } @@ -100,7 +98,11 @@ private void checkHtmlAttr(String name, String value) { String n = name.toLowerCase(Locale.ENGLISH); - if (eventAttrs.contains(n) + // https://www.w3.org/TR/html52/fullindex.html#attributes-table + // See https://www.w3.org/TR/html52/webappapis.html#events-event-handlers + // An event handler has a name, which always starts with "on" and is followed by + // the name of the event for which it is intended. + if (n.startsWith("on") || uriAttrs.contains(n) && value != null && value.toLowerCase(Locale.ENGLISH).trim().startsWith("javascript:")) { reporter.report(); @@ -1060,34 +1062,6 @@ } - private void initEventAttrs() { - eventAttrs = new HashSet(Arrays.asList( - // See https://www.w3.org/TR/html-markup/global-attributes.html#common.attrs.event-handler - "onabort", "onblur", "oncanplay", "oncanplaythrough", - "onchange", "onclick", "oncontextmenu", "ondblclick", - "ondrag", "ondragend", "ondragenter", "ondragleave", - "ondragover", "ondragstart", "ondrop", "ondurationchange", - "onemptied", "onended", "onerror", "onfocus", "oninput", - "oninvalid", "onkeydown", "onkeypress", "onkeyup", - "onload", "onloadeddata", "onloadedmetadata", "onloadstart", - "onmousedown", "onmousemove", "onmouseout", "onmouseover", - "onmouseup", "onmousewheel", "onpause", "onplay", - "onplaying", "onprogress", "onratechange", "onreadystatechange", - "onreset", "onscroll", "onseeked", "onseeking", - "onselect", "onshow", "onstalled", "onsubmit", "onsuspend", - "ontimeupdate", "onvolumechange", "onwaiting", - - // See https://www.w3.org/TR/html4/sgml/dtd.html - // Most of the attributes that take a %Script are also defined as event handlers - // in HTML 5. The one exception is onunload. - // "onchange", "onclick", "ondblclick", "onfocus", - // "onkeydown", "onkeypress", "onkeyup", "onload", - // "onmousedown", "onmousemove", "onmouseout", "onmouseover", - // "onmouseup", "onreset", "onselect", "onsubmit", - "onunload" - )); - } - private void initURIAttrs() { uriAttrs = new HashSet(Arrays.asList( // See https://www.w3.org/TR/html4/sgml/dtd.html diff -r 3ef585ecd995 -r 77f4748f4406 test/tools/javadoc/TestScriptInComment.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/tools/javadoc/TestScriptInComment.java Mon Oct 14 19:19:46 2019 +0100 @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @test + * @bug 8138725 8226765 + * @summary test --allow-script-in-comments + * @run main TestScriptInComment + */ + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Combo-style test, exercising combinations of different HTML fragments that may contain + * JavaScript, different places to place those fragments, and whether or not to allow the use + * of JavaScript. + */ +public class TestScriptInComment { + public static void main(String... args) throws Exception { + new TestScriptInComment().run(); + } + + /** + * Representative samples of different fragments of HTML that may contain JavaScript. + * To facilitate checking the output manually in a browser, the text "#ALERT" will be + * replaced by a JavaScript call of "alert(msg)", using a message string that is specific + * to the test case. + */ + enum Comment { + LC("", true), // script tag in Lower Case + UC("", true), // script tag in Upper Case + WS("< script >#ALERT", false, "-Xdoclint:none"), // script tag with invalid white space + SA("", true), // script tag with an attribute + ON("x", true), // event handler attribute + OME("1", true), // onmouseenter event handler attribute + OML("1", true), // onmouseleave event handler attribute + OFI("x", true), // onfocusin event handler attribute + OBE("x", true), // bogus/future event handler attribute + URI("x", true); // javadcript URI + + /** + * Creates an HTML fragment to be injected into a template. + * @param text the HTML fragment to put into a doc comment or option. + * @param hasScript whether or not this fragment does contain legal JavaScript + * @param opts any additional options to be specified when javadoc is run + */ + Comment(String text, boolean hasScript, String... opts) { + this.text = text; + this.hasScript = hasScript; + this.opts = Arrays.asList(opts); + } + + final String text; + final boolean hasScript; + final List opts; + }; + + /** + * Representative samples of positions in which javadoc may find JavaScript. + * Each template contains a series of strings, which are written to files or inferred as options. + * The first source file implies a corresponding output file which should not be written + * if the comment contains JavaScript and JavaScript is not allowed. + */ + enum Template { + OVR(" overview #COMMENT ", "package p; public class C { }"), + PKGINFO("#COMMENT package p;", "package p; public class C { }"), + PKGHTML("#COMMENT package p;", "package p; public class C { }"), + CLS("package p; #COMMENT public class C { }"), + CON("package p; public class C { #COMMENT public C() { } }"), + FLD("package p; public class C { #COMMENT public int f; }"), + MTH("package p; public class C { #COMMENT public void m() { } }"), + TOP("-top", "lorem #COMMENT ipsum", "package p; public class C { }"), + HDR("-header", "lorem #COMMENT ipsum", "package p; public class C { }"), + FTR("-footer", "lorem #COMMENT ipsum", "package p; public class C { }"), + BTM("-bottom", "lorem #COMMENT ipsum", "package p; public class C { }"), + DTTL("-doctitle", "lorem #COMMENT ipsum", "package p; public class C { }"), + PHDR("-packagesheader", "lorem #COMMENT ipsum", "package p; public class C { }"); + + Template(String... args) { + opts = new ArrayList(); + sources = new ArrayList(); + int i = 0; + while (args[i].startsWith("-")) { + // all options being tested have a single argument that follow the option + opts.add(args[i++]); + opts.add(args[i++]); + } + while(i < args.length) { + sources.add(args[i++]); + } + } + + // groups: 1 or not; 2: package name; 3: class name + private final Pattern pat = + Pattern.compile("(?i)()?.*?(?:package ([a-z]+);.*?(?:class ([a-z]+).*)?)?"); + + /** + * Infer the file in which to write the given source. + * @param dir the base source directory + * @param src the source text + * @return the file in which the source should be written + */ + File getSrcFile(File srcDir, String src) { + String f; + Matcher m = pat.matcher(src); + if (!m.matches()) + throw new Error("match failed"); + if (m.group(3) != null) { + f = m.group(2) + "/" + m.group(3) + ".java"; + } else if (m.group(2) != null) { + f = m.group(2) + "/" + (m.group(1) == null ? "package-info.java" : "package.html"); + } else { + f = "overview.html"; + } + return new File(srcDir, f); + } + + /** + * Get the options to give to javadoc. + * @param srcDir the srcDir to use -overview is needed + * @return + */ + List getOpts(File srcDir) { + if (!opts.isEmpty()) { + return opts; + } else if (sources.get(0).contains("overview")) { + return Arrays.asList("-overview", getSrcFile(srcDir, sources.get(0)).getPath()); + } else { + return Collections.emptyList(); + } + } + + /** + * Gets the output file corresponding to the first source file. + * This file should not be written if the comment contains JavaScript and JavaScripot is + * not allowed. + * @param dir the base output directory + * @return the output file + */ + File getOutFile(File outDir) { + String f; + Matcher m = pat.matcher(sources.get(0)); + if (!m.matches()) + throw new Error("match failed"); + if (m.group(3) != null) { + f = m.group(2) + "/" + m.group(3) + ".html"; + } else if (m.group(2) != null) { + f = m.group(2) + "/package-summary.html"; + } else { + f = "overview-summary.html"; + } + return new File(outDir, f); + } + + final List opts; + final List sources; + }; + + enum Option { + OFF(null), + ON("--allow-script-in-comments"); + + Option(String text) { + this.text = text; + } + + final String text; + }; + + private PrintStream out = System.err; + + public void run() throws Exception { + int count = 0; + for (Template template: Template.values()) { + for (Comment comment: Comment.values()) { + for (Option option: Option.values()) { + if (test(template, comment, option)) { + count++; + } + } + } + } + + out.println(count + " test cases run"); + if (errors > 0) { + throw new Exception(errors + " errors occurred"); + } + } + + boolean test(Template template, Comment comment, Option option) throws IOException { + if (option == Option.ON && !comment.hasScript) { + // skip --allowScriptInComments if comment does not contain JavaScript + return false; + } + + String test = template + "-" + comment + "-" + option; + out.println("Test: " + test); + + File dir = new File(test); + dir.mkdirs(); + File srcDir = new File(dir, "src"); + File outDir = new File(dir, "out"); + + String alert = "alert(\"" + test + "\");"; + for (String src: template.sources) { + writeFile(template.getSrcFile(srcDir, src), + src.replace("#COMMENT", + "/** " + comment.text.replace("#ALERT", alert) + " **/")); + } + + List opts = new ArrayList(); + opts.add("-sourcepath"); + opts.add(srcDir.getPath()); + opts.add("-d"); + opts.add(outDir.getPath()); + if (option.text != null) + opts.add(option.text); + for (String opt: template.getOpts(srcDir)) { + opts.add(opt.replace("#COMMENT", comment.text.replace("#ALERT", alert))); + } + opts.addAll(comment.opts); + opts.add("-noindex"); // index not required; save time/space writing files + opts.add("p"); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + int rc = javadoc(opts, pw); + pw.close(); + String log = sw.toString(); + writeFile(new File(dir, "log.txt"), log); + + out.println("opts: " + opts); + out.println(" rc: " + rc); + out.println(" log:"); + out.println(log); + + String ERROR = "Use --allow-script-in-comment"; + File outFile = template.getOutFile(outDir); + + boolean expectErrors = comment.hasScript && (option == Option.OFF); + + if (expectErrors) { + check(rc != 0, "unexpected exit code: " + rc); + check(log.contains(ERROR), "expected error message not found"); + check(!outFile.exists(), "output file found unexpectedly"); + } else { + check(rc == 0, "unexpected exit code: " + rc); + check(!log.contains(ERROR), "error message found"); + check(outFile.exists(), "output file not found"); + } + + out.println(); + return true; + } + + int javadoc(List opts, PrintWriter pw) { + return com.sun.tools.javadoc.Main.execute("javadoc", pw, pw, pw, + "com.sun.tools.doclets.standard.Standard", opts.toArray(new String[opts.size()])); + } + + File writeFile(File f, String text) throws IOException { + f.getParentFile().mkdirs(); + FileWriter fw = new FileWriter(f); + try { + fw.write(text); + } finally { + fw.close(); + } + return f; + } + + void check(boolean cond, String errMessage) { + if (!cond) { + error(errMessage); + } + } + + void error(String message) { + out.println("Error: " + message); + errors++; + } + + int errors = 0; +} +