8138725: Add options for Javadoc generation

Mon, 18 Jul 2016 23:53:12 +0300

author
aefimov
date
Mon, 18 Jul 2016 23:53:12 +0300
changeset 3315
6f0746b6de9f
parent 3314
7b6c1bfeeb03
child 3316
e3d17d092a61

8138725: Add options for Javadoc generation
Reviewed-by: jjg

src/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/doclets/internal/toolkit/AbstractDoclet.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/AbstractBuilder.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/doclets/internal/toolkit/resources/doclets.properties file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/doclets/internal/toolkit/util/FatalError.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/doclint/Checker.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/doclint/HtmlTag.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/parser/JavacParser.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javadoc/DocEnv.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javadoc/DocImpl.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javadoc/JavaScriptScanner.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javadoc/RootDocImpl.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javadoc/resources/javadoc.properties file | annotate | diff | comparison | revisions
test/tools/doclint/html/OtherTagsTest.out file | annotate | diff | comparison | revisions
test/tools/javadoc/TestScriptInComment.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java	Tue Jul 12 14:52:08 2016 -0700
     1.2 +++ b/src/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java	Mon Jul 18 23:53:12 2016 +0300
     1.3 @@ -1,5 +1,5 @@
     1.4  /*
     1.5 - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
     1.6 + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
     1.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.8   *
     1.9   * This code is free software; you can redistribute it and/or modify it
    1.10 @@ -38,6 +38,7 @@
    1.11  import com.sun.tools.javac.file.JavacFileManager;
    1.12  import com.sun.tools.javac.util.Context;
    1.13  import com.sun.tools.javac.util.StringUtils;
    1.14 +import com.sun.tools.javadoc.JavaScriptScanner;
    1.15  import com.sun.tools.javadoc.RootDocImpl;
    1.16  
    1.17  /**
    1.18 @@ -181,6 +182,11 @@
    1.19      public Set<String> doclintOpts = new LinkedHashSet<String>();
    1.20  
    1.21      /**
    1.22 +     * Whether or not to check for JavaScript in doc comments.
    1.23 +     */
    1.24 +    private boolean allowScriptInComments;
    1.25 +
    1.26 +    /**
    1.27       * Unique Resource Handler for this package.
    1.28       */
    1.29      public final MessageRetriever standardmessage;
    1.30 @@ -283,8 +289,11 @@
    1.31                  doclintOpts.add(null);
    1.32              } else if (opt.startsWith("-xdoclint:")) {
    1.33                  doclintOpts.add(opt.substring(opt.indexOf(":") + 1));
    1.34 +            } else if (opt.equals("--allow-script-in-comments")) {
    1.35 +                allowScriptInComments = true;
    1.36              }
    1.37          }
    1.38 +
    1.39          if (root.specifiedClasses().length > 0) {
    1.40              Map<String,PackageDoc> map = new HashMap<String,PackageDoc>();
    1.41              PackageDoc pd;
    1.42 @@ -301,9 +310,30 @@
    1.43  
    1.44          if (root instanceof RootDocImpl) {
    1.45              ((RootDocImpl) root).initDocLint(doclintOpts, tagletManager.getCustomTagNames());
    1.46 +            JavaScriptScanner jss = ((RootDocImpl) root).initJavaScriptScanner(isAllowScriptInComments());
    1.47 +            if (jss != null) {
    1.48 +                // In a more object-oriented world, this would be done by methods on the Option objects.
    1.49 +                // Note that -windowtitle silently removes any and all HTML elements, and so does not need
    1.50 +                // to be handled here.
    1.51 +                checkJavaScript(jss, "-header", header);
    1.52 +                checkJavaScript(jss, "-footer", footer);
    1.53 +                checkJavaScript(jss, "-top", top);
    1.54 +                checkJavaScript(jss, "-bottom", bottom);
    1.55 +                checkJavaScript(jss, "-doctitle", doctitle);
    1.56 +                checkJavaScript(jss, "-packagesheader", packagesheader);
    1.57 +            }
    1.58          }
    1.59      }
    1.60  
    1.61 +    private void checkJavaScript(JavaScriptScanner jss, final String opt, String value) {
    1.62 +        jss.parse(value, new JavaScriptScanner.Reporter() {
    1.63 +            public void report() {
    1.64 +                root.printError(getText("doclet.JavaScript_in_option", opt));
    1.65 +                throw new FatalError();
    1.66 +            }
    1.67 +        });
    1.68 +    }
    1.69 +
    1.70      /**
    1.71       * Returns the "length" of a given option. If an option takes no
    1.72       * arguments, its length is one. If it takes one argument, it's
    1.73 @@ -337,7 +367,8 @@
    1.74              option.equals("-nonavbar") ||
    1.75              option.equals("-nooverview") ||
    1.76              option.equals("-xdoclint") ||
    1.77 -            option.startsWith("-xdoclint:")) {
    1.78 +            option.startsWith("-xdoclint:") ||
    1.79 +            option.equals("--allow-script-in-comments")) {
    1.80              return 1;
    1.81          } else if (option.equals("-help")) {
    1.82              // Uugh: first, this should not be hidden inside optionLength,
    1.83 @@ -595,4 +626,13 @@
    1.84      public Content newContent() {
    1.85          return new ContentBuilder();
    1.86      }
    1.87 +
    1.88 +    /**
    1.89 +     * Returns whether or not to allow JavaScript in comments.
    1.90 +     * Default is off; can be set true from a command line option.
    1.91 +     * @return the allowScriptInComments
    1.92 +     */
    1.93 +    public boolean isAllowScriptInComments() {
    1.94 +        return allowScriptInComments;
    1.95 +    }
    1.96  }
     2.1 --- a/src/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java	Tue Jul 12 14:52:08 2016 -0700
     2.2 +++ b/src/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java	Mon Jul 18 23:53:12 2016 +0300
     2.3 @@ -1,5 +1,5 @@
     2.4  /*
     2.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
     2.6 + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
     2.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     2.8   *
     2.9   * This code is free software; you can redistribute it and/or modify it
    2.10 @@ -190,6 +190,8 @@
    2.11                  }
    2.12              } catch (IOException e) {
    2.13                  throw new DocletAbortException(e);
    2.14 +            } catch (FatalError fe) {
    2.15 +                throw fe;
    2.16              } catch (DocletAbortException de) {
    2.17                  throw de;
    2.18              } catch (Exception e) {
     3.1 --- a/src/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java	Tue Jul 12 14:52:08 2016 -0700
     3.2 +++ b/src/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java	Mon Jul 18 23:53:12 2016 +0300
     3.3 @@ -1,5 +1,5 @@
     3.4  /*
     3.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
     3.6 + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
     3.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     3.8   *
     3.9   * This code is free software; you can redistribute it and/or modify it
    3.10 @@ -164,7 +164,9 @@
    3.11  
    3.12      public final Content descfrmInterfaceLabel;
    3.13  
    3.14 -    private final Writer writer;
    3.15 +    private final DocFile file;
    3.16 +
    3.17 +    private Writer writer;
    3.18  
    3.19      private Content script;
    3.20  
    3.21 @@ -180,7 +182,7 @@
    3.22       */
    3.23      public HtmlWriter(Configuration configuration, DocPath path)
    3.24              throws IOException, UnsupportedEncodingException {
    3.25 -        writer = DocFile.createFileForOutput(configuration, path).openWriter();
    3.26 +        file = DocFile.createFileForOutput(configuration, path);
    3.27          this.configuration = configuration;
    3.28          this.memberDetailsListPrinted = false;
    3.29          profileTableHeader = new String[] {
    3.30 @@ -239,6 +241,7 @@
    3.31      }
    3.32  
    3.33      public void write(Content c) throws IOException {
    3.34 +        writer = file.openWriter();
    3.35          c.write(writer, true);
    3.36      }
    3.37  
     4.1 --- a/src/share/classes/com/sun/tools/doclets/internal/toolkit/AbstractDoclet.java	Tue Jul 12 14:52:08 2016 -0700
     4.2 +++ b/src/share/classes/com/sun/tools/doclets/internal/toolkit/AbstractDoclet.java	Mon Jul 18 23:53:12 2016 +0300
     4.3 @@ -1,5 +1,5 @@
     4.4  /*
     4.5 - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
     4.6 + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
     4.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4.8   *
     4.9   * This code is free software; you can redistribute it and/or modify it
    4.10 @@ -83,6 +83,8 @@
    4.11          } catch (Configuration.Fault f) {
    4.12              root.printError(f.getMessage());
    4.13              return false;
    4.14 +        } catch (FatalError fe) {
    4.15 +            return false;
    4.16          } catch (DocletAbortException e) {
    4.17              Throwable cause = e.getCause();
    4.18              if (cause != null) {
     5.1 --- a/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/AbstractBuilder.java	Tue Jul 12 14:52:08 2016 -0700
     5.2 +++ b/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/AbstractBuilder.java	Mon Jul 18 23:53:12 2016 +0300
     5.3 @@ -1,5 +1,5 @@
     5.4  /*
     5.5 - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
     5.6 + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
     5.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     5.8   *
     5.9   * This code is free software; you can redistribute it and/or modify it
    5.10 @@ -140,7 +140,14 @@
    5.11              configuration.root.printError("Unknown element: " + component);
    5.12              throw new DocletAbortException(e);
    5.13          } catch (InvocationTargetException e) {
    5.14 -            throw new DocletAbortException(e.getCause());
    5.15 +            Throwable cause = e.getCause();
    5.16 +            if (cause instanceof FatalError) {
    5.17 +                throw (FatalError) cause;
    5.18 +            } else if (cause instanceof DocletAbortException) {
    5.19 +                throw (DocletAbortException) cause;
    5.20 +            } else {
    5.21 +                throw new DocletAbortException(cause);
    5.22 +            }
    5.23          } catch (Exception e) {
    5.24              e.printStackTrace();
    5.25              configuration.root.printError("Exception " +
     6.1 --- a/src/share/classes/com/sun/tools/doclets/internal/toolkit/resources/doclets.properties	Tue Jul 12 14:52:08 2016 -0700
     6.2 +++ b/src/share/classes/com/sun/tools/doclets/internal/toolkit/resources/doclets.properties	Mon Jul 18 23:53:12 2016 +0300
     6.3 @@ -29,6 +29,8 @@
     6.4  doclet.Building_Tree=Building tree for all the packages and classes...
     6.5  doclet.Building_Index=Building index for all the packages and classes...
     6.6  doclet.Building_Index_For_All_Classes=Building index for all classes...
     6.7 +doclet.JavaScript_in_option=Argument for {0} contains JavaScript.\n\
     6.8 +Use --allow-script-in-comments to allow use of JavaScript.
     6.9  doclet.sourcetab_warning=The argument for -sourcetab must be an integer greater than 0.
    6.10  doclet.Packages=Packages
    6.11  doclet.Profiles=Profiles
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/FatalError.java	Mon Jul 18 23:53:12 2016 +0300
     7.3 @@ -0,0 +1,39 @@
     7.4 +/*
     7.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
     7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     7.7 + *
     7.8 + * This code is free software; you can redistribute it and/or modify it
     7.9 + * under the terms of the GNU General Public License version 2 only, as
    7.10 + * published by the Free Software Foundation.  Oracle designates this
    7.11 + * particular file as subject to the "Classpath" exception as provided
    7.12 + * by Oracle in the LICENSE file that accompanied this code.
    7.13 + *
    7.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    7.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    7.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    7.17 + * version 2 for more details (a copy is included in the LICENSE file that
    7.18 + * accompanied this code).
    7.19 + *
    7.20 + * You should have received a copy of the GNU General Public License version
    7.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    7.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    7.23 + *
    7.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    7.25 + * or visit www.oracle.com if you need additional information or have any
    7.26 + * questions.
    7.27 + */
    7.28 +
    7.29 +package com.sun.tools.doclets.internal.toolkit.util;
    7.30 +
    7.31 +/**
    7.32 + *  <p><b>This is NOT part of any supported API.
    7.33 + *  If you write code that depends on this, you do so at your own risk.
    7.34 + *  This code and its internal interfaces are subject to change or
    7.35 + *  deletion without notice.</b>
    7.36 + */
    7.37 +@Deprecated
    7.38 +public class FatalError extends Error {
    7.39 +    private static final long serialVersionUID = -9131058909576418984L;
    7.40 +
    7.41 +    public FatalError() { }
    7.42 +}
     8.1 --- a/src/share/classes/com/sun/tools/doclint/Checker.java	Tue Jul 12 14:52:08 2016 -0700
     8.2 +++ b/src/share/classes/com/sun/tools/doclint/Checker.java	Mon Jul 18 23:53:12 2016 +0300
     8.3 @@ -1,5 +1,5 @@
     8.4  /*
     8.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
     8.6 + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
     8.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     8.8   *
     8.9   * This code is free software; you can redistribute it and/or modify it
    8.10 @@ -126,7 +126,7 @@
    8.11          }
    8.12      }
    8.13  
    8.14 -    private Deque<TagStackItem> tagStack; // TODO: maybe want to record starting tree as well
    8.15 +    private final Deque<TagStackItem> tagStack; // TODO: maybe want to record starting tree as well
    8.16      private HtmlTag currHeaderTag;
    8.17  
    8.18      private final int implicitHeaderLevel;
    8.19 @@ -401,7 +401,16 @@
    8.20                  break;
    8.21  
    8.22              case OTHER:
    8.23 -                env.messages.error(HTML, tree, "dc.tag.not.allowed", treeName);
    8.24 +                switch (t) {
    8.25 +                    case SCRIPT:
    8.26 +                        // <script> may or may not be allowed, depending on --allow-script-in-comments
    8.27 +                        // but we allow it here, and rely on a separate scanner to detect all uses
    8.28 +                        // of JavaScript, including <script> tags, and use in attributes, etc.
    8.29 +                        break;
    8.30 +
    8.31 +                    default:
    8.32 +                        env.messages.error(HTML, tree, "dc.tag.not.allowed", treeName);
    8.33 +                }
    8.34                  return;
    8.35          }
    8.36  
    8.37 @@ -519,22 +528,27 @@
    8.38                  if (!first)
    8.39                      env.messages.error(HTML, tree, "dc.attr.repeated", name);
    8.40              }
    8.41 -            AttrKind k = currTag.getAttrKind(name);
    8.42 -            switch (k) {
    8.43 -                case OK:
    8.44 -                    break;
    8.45  
    8.46 -                case INVALID:
    8.47 -                    env.messages.error(HTML, tree, "dc.attr.unknown", name);
    8.48 -                    break;
    8.49 +            // for now, doclint allows all attribute names beginning with "on" as event handler names,
    8.50 +            // without checking the validity or applicability of the name
    8.51 +            if (!name.toString().startsWith("on")) {
    8.52 +                AttrKind k = currTag.getAttrKind(name);
    8.53 +                switch (k) {
    8.54 +                    case OK:
    8.55 +                        break;
    8.56  
    8.57 -                case OBSOLETE:
    8.58 -                    env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete", name);
    8.59 -                    break;
    8.60 +                    case INVALID:
    8.61 +                        env.messages.error(HTML, tree, "dc.attr.unknown", name);
    8.62 +                        break;
    8.63  
    8.64 -                case USE_CSS:
    8.65 -                    env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete.use.css", name);
    8.66 -                    break;
    8.67 +                    case OBSOLETE:
    8.68 +                        env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete", name);
    8.69 +                        break;
    8.70 +
    8.71 +                    case USE_CSS:
    8.72 +                        env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete.use.css", name);
    8.73 +                        break;
    8.74 +                }
    8.75              }
    8.76  
    8.77              if (attr != null) {
    8.78 @@ -643,6 +657,9 @@
    8.79      }
    8.80  
    8.81      private void checkURI(AttributeTree tree, String uri) {
    8.82 +        // allow URIs beginning with javascript:, which would otherwise be rejected by the URI API.
    8.83 +        if (uri.startsWith("javascript:"))
    8.84 +            return;
    8.85          try {
    8.86              URI u = new URI(uri);
    8.87          } catch (URISyntaxException e) {
     9.1 --- a/src/share/classes/com/sun/tools/doclint/HtmlTag.java	Tue Jul 12 14:52:08 2016 -0700
     9.2 +++ b/src/share/classes/com/sun/tools/doclint/HtmlTag.java	Mon Jul 18 23:53:12 2016 +0300
     9.3 @@ -1,5 +1,5 @@
     9.4  /*
     9.5 - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
     9.6 + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
     9.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     9.8   *
     9.9   * This code is free software; you can redistribute it and/or modify it
    9.10 @@ -183,7 +183,8 @@
    9.11          }
    9.12      },
    9.13  
    9.14 -    SCRIPT(BlockType.OTHER, EndKind.REQUIRED),
    9.15 +    SCRIPT(BlockType.OTHER, EndKind.REQUIRED,
    9.16 +            attrs(AttrKind.OK, SRC)),
    9.17  
    9.18      SMALL(BlockType.INLINE, EndKind.REQUIRED,
    9.19              EnumSet.of(Flag.EXPECT_CONTENT)),
    10.1 --- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Tue Jul 12 14:52:08 2016 -0700
    10.2 +++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java	Mon Jul 18 23:53:12 2016 +0300
    10.3 @@ -1,5 +1,5 @@
    10.4  /*
    10.5 - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
    10.6 + * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
    10.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    10.8   *
    10.9   * This code is free software; you can redistribute it and/or modify it
   10.10 @@ -585,7 +585,7 @@
   10.11      /**
   10.12       * Ident = IDENTIFIER
   10.13       */
   10.14 -    Name ident() {
   10.15 +    public Name ident() {
   10.16          if (token.kind == IDENTIFIER) {
   10.17              Name name = token.name();
   10.18              nextToken();
    11.1 --- a/src/share/classes/com/sun/tools/javadoc/DocEnv.java	Tue Jul 12 14:52:08 2016 -0700
    11.2 +++ b/src/share/classes/com/sun/tools/javadoc/DocEnv.java	Mon Jul 18 23:53:12 2016 +0300
    11.3 @@ -1,5 +1,5 @@
    11.4  /*
    11.5 - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
    11.6 + * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
    11.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    11.8   *
    11.9   * This code is free software; you can redistribute it and/or modify it
   11.10 @@ -86,7 +86,7 @@
   11.11      JavadocEnter enter;
   11.12  
   11.13      /** The name table. */
   11.14 -    Names names;
   11.15 +    private final Names names;
   11.16  
   11.17      /** The encoding name. */
   11.18      private String encoding;
   11.19 @@ -109,6 +109,7 @@
   11.20      JavaFileManager fileManager;
   11.21      Context context;
   11.22      DocLint doclint;
   11.23 +    JavaScriptScanner javaScriptScanner;
   11.24  
   11.25      WeakHashMap<JCTree, TreePath> treePaths = new WeakHashMap<JCTree, TreePath>();
   11.26  
   11.27 @@ -834,6 +835,15 @@
   11.28          doclint.init(t, doclintOpts.toArray(new String[doclintOpts.size()]), false);
   11.29      }
   11.30  
   11.31 +    JavaScriptScanner initJavaScriptScanner(boolean allowScriptInComments) {
   11.32 +        if (allowScriptInComments) {
   11.33 +            javaScriptScanner = null;
   11.34 +        } else {
   11.35 +            javaScriptScanner = new JavaScriptScanner();
   11.36 +        }
   11.37 +        return javaScriptScanner;
   11.38 +    }
   11.39 +
   11.40      boolean showTagMessages() {
   11.41          return (doclint == null);
   11.42      }
    12.1 --- a/src/share/classes/com/sun/tools/javadoc/DocImpl.java	Tue Jul 12 14:52:08 2016 -0700
    12.2 +++ b/src/share/classes/com/sun/tools/javadoc/DocImpl.java	Mon Jul 18 23:53:12 2016 +0300
    12.3 @@ -1,5 +1,5 @@
    12.4  /*
    12.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
    12.6 + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
    12.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    12.8   *
    12.9   * This code is free software; you can redistribute it and/or modify it
   12.10 @@ -36,6 +36,7 @@
   12.11  
   12.12  import com.sun.javadoc.*;
   12.13  import com.sun.source.util.TreePath;
   12.14 +import com.sun.tools.doclets.internal.toolkit.util.FatalError;
   12.15  import com.sun.tools.javac.tree.JCTree;
   12.16  import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
   12.17  import com.sun.tools.javac.util.Position;
   12.18 @@ -127,6 +128,15 @@
   12.19      Comment comment() {
   12.20          if (comment == null) {
   12.21              String d = documentation();
   12.22 +            if (env.javaScriptScanner != null) {
   12.23 +                env.javaScriptScanner.parse(d, new JavaScriptScanner.Reporter() {
   12.24 +                    @Override
   12.25 +                    public void report() {
   12.26 +                        env.error(DocImpl.this, "javadoc.JavaScript_in_comment");
   12.27 +                        throw new FatalError();
   12.28 +                    }
   12.29 +                });
   12.30 +            }
   12.31              if (env.doclint != null
   12.32                      && treePath != null
   12.33                      && d.equals(getCommentText(treePath))) {
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/src/share/classes/com/sun/tools/javadoc/JavaScriptScanner.java	Mon Jul 18 23:53:12 2016 +0300
    13.3 @@ -0,0 +1,1103 @@
    13.4 +/*
    13.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    13.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    13.7 + *
    13.8 + * This code is free software; you can redistribute it and/or modify it
    13.9 + * under the terms of the GNU General Public License version 2 only, as
   13.10 + * published by the Free Software Foundation.  Oracle designates this
   13.11 + * particular file as subject to the "Classpath" exception as provided
   13.12 + * by Oracle in the LICENSE file that accompanied this code.
   13.13 + *
   13.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   13.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   13.17 + * version 2 for more details (a copy is included in the LICENSE file that
   13.18 + * accompanied this code).
   13.19 + *
   13.20 + * You should have received a copy of the GNU General Public License version
   13.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   13.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   13.23 + *
   13.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   13.25 + * or visit www.oracle.com if you need additional information or have any
   13.26 + * questions.
   13.27 + */
   13.28 +
   13.29 +package com.sun.tools.javadoc;
   13.30 +
   13.31 +import java.util.Arrays;
   13.32 +import java.util.HashMap;
   13.33 +import java.util.HashSet;
   13.34 +import java.util.Locale;
   13.35 +import java.util.Map;
   13.36 +import java.util.Set;
   13.37 +
   13.38 +import com.sun.tools.javadoc.JavaScriptScanner.TagParser.Kind;
   13.39 +
   13.40 +import static com.sun.tools.javac.util.LayoutCharacters.EOI;
   13.41 +
   13.42 +/**
   13.43 + * Parser to detect use of JavaScript in documentation comments.
   13.44 + */
   13.45 +@Deprecated
   13.46 +public class JavaScriptScanner {
   13.47 +    public static interface Reporter {
   13.48 +        void report();
   13.49 +    }
   13.50 +
   13.51 +    static class ParseException extends Exception {
   13.52 +        private static final long serialVersionUID = 0;
   13.53 +        ParseException(String key) {
   13.54 +            super(key);
   13.55 +        }
   13.56 +    }
   13.57 +
   13.58 +    private Reporter reporter;
   13.59 +
   13.60 +    /** The input buffer, index of most recent character read,
   13.61 +     *  index of one past last character in buffer.
   13.62 +     */
   13.63 +    protected char[] buf;
   13.64 +    protected int bp;
   13.65 +    protected int buflen;
   13.66 +
   13.67 +    /** The current character.
   13.68 +     */
   13.69 +    protected char ch;
   13.70 +
   13.71 +    private boolean newline = true;
   13.72 +
   13.73 +    Map<String, TagParser> tagParsers;
   13.74 +    Set<String> eventAttrs;
   13.75 +    Set<String> uriAttrs;
   13.76 +
   13.77 +    public JavaScriptScanner() {
   13.78 +        initTagParsers();
   13.79 +        initEventAttrs();
   13.80 +        initURIAttrs();
   13.81 +    }
   13.82 +
   13.83 +    public void parse(String comment, Reporter r) {
   13.84 +        reporter = r;
   13.85 +        String c = comment;
   13.86 +        buf = new char[c.length() + 1];
   13.87 +        c.getChars(0, c.length(), buf, 0);
   13.88 +        buf[buf.length - 1] = EOI;
   13.89 +        buflen = buf.length - 1;
   13.90 +        bp = -1;
   13.91 +        newline = true;
   13.92 +        nextChar();
   13.93 +
   13.94 +        blockContent();
   13.95 +        blockTags();
   13.96 +    }
   13.97 +
   13.98 +    private void checkHtmlTag(String tag) {
   13.99 +        if (tag.equalsIgnoreCase("script")) {
  13.100 +            reporter.report();
  13.101 +        }
  13.102 +    }
  13.103 +
  13.104 +    private void checkHtmlAttr(String name, String value) {
  13.105 +        String n = name.toLowerCase(Locale.ENGLISH);
  13.106 +        if (eventAttrs.contains(n)
  13.107 +                || uriAttrs.contains(n)
  13.108 +                    && value != null && value.toLowerCase(Locale.ENGLISH).trim().startsWith("javascript:")) {
  13.109 +            reporter.report();
  13.110 +        }
  13.111 +    }
  13.112 +
  13.113 +    void nextChar() {
  13.114 +        ch = buf[bp < buflen ? ++bp : buflen];
  13.115 +        switch (ch) {
  13.116 +            case '\f': case '\n': case '\r':
  13.117 +                newline = true;
  13.118 +        }
  13.119 +    }
  13.120 +
  13.121 +    /**
  13.122 +     * Read block content, consisting of text, html and inline tags.
  13.123 +     * Terminated by the end of input, or the beginning of the next block tag:
  13.124 +     * i.e. @ as the first non-whitespace character on a line.
  13.125 +     */
  13.126 +    @SuppressWarnings("fallthrough")
  13.127 +    protected void blockContent() {
  13.128 +
  13.129 +        loop:
  13.130 +        while (bp < buflen) {
  13.131 +            switch (ch) {
  13.132 +                case '\n': case '\r': case '\f':
  13.133 +                    newline = true;
  13.134 +                    // fallthrough
  13.135 +
  13.136 +                case ' ': case '\t':
  13.137 +                    nextChar();
  13.138 +                    break;
  13.139 +
  13.140 +                case '&':
  13.141 +                    entity(null);
  13.142 +                    break;
  13.143 +
  13.144 +                case '<':
  13.145 +                    html();
  13.146 +                    break;
  13.147 +
  13.148 +                case '>':
  13.149 +                    newline = false;
  13.150 +                    nextChar();
  13.151 +                    break;
  13.152 +
  13.153 +                case '{':
  13.154 +                    inlineTag(null);
  13.155 +                    break;
  13.156 +
  13.157 +                case '@':
  13.158 +                    if (newline) {
  13.159 +                        break loop;
  13.160 +                    }
  13.161 +                    // fallthrough
  13.162 +
  13.163 +                default:
  13.164 +                    newline = false;
  13.165 +                    nextChar();
  13.166 +            }
  13.167 +        }
  13.168 +    }
  13.169 +
  13.170 +    /**
  13.171 +     * Read a series of block tags, including their content.
  13.172 +     * Standard tags parse their content appropriately.
  13.173 +     * Non-standard tags are represented by {@link UnknownBlockTag}.
  13.174 +     */
  13.175 +    protected void blockTags() {
  13.176 +        while (ch == '@')
  13.177 +            blockTag();
  13.178 +    }
  13.179 +
  13.180 +    /**
  13.181 +     * Read a single block tag, including its content.
  13.182 +     * Standard tags parse their content appropriately.
  13.183 +     * Non-standard tags are represented by {@link UnknownBlockTag}.
  13.184 +     */
  13.185 +    protected void blockTag() {
  13.186 +        int p = bp;
  13.187 +        try {
  13.188 +            nextChar();
  13.189 +            if (isIdentifierStart(ch)) {
  13.190 +                String name = readTagName();
  13.191 +                TagParser tp = tagParsers.get(name);
  13.192 +                if (tp == null) {
  13.193 +                    blockContent();
  13.194 +                } else {
  13.195 +                    switch (tp.getKind()) {
  13.196 +                        case BLOCK:
  13.197 +                            tp.parse(p);
  13.198 +                            return;
  13.199 +                        case INLINE:
  13.200 +                            return;
  13.201 +                    }
  13.202 +                }
  13.203 +            }
  13.204 +            blockContent();
  13.205 +        } catch (ParseException e) {
  13.206 +            blockContent();
  13.207 +        }
  13.208 +    }
  13.209 +
  13.210 +    protected void inlineTag(Void list) {
  13.211 +        newline = false;
  13.212 +        nextChar();
  13.213 +        if (ch == '@') {
  13.214 +            inlineTag();
  13.215 +        }
  13.216 +    }
  13.217 +
  13.218 +    /**
  13.219 +     * Read a single inline tag, including its content.
  13.220 +     * Standard tags parse their content appropriately.
  13.221 +     * Non-standard tags are represented by {@link UnknownBlockTag}.
  13.222 +     * Malformed tags may be returned as {@link Erroneous}.
  13.223 +     */
  13.224 +    protected void inlineTag() {
  13.225 +        int p = bp - 1;
  13.226 +        try {
  13.227 +            nextChar();
  13.228 +            if (isIdentifierStart(ch)) {
  13.229 +                String name = readTagName();
  13.230 +                TagParser tp = tagParsers.get(name);
  13.231 +
  13.232 +                if (tp == null) {
  13.233 +                    skipWhitespace();
  13.234 +                    inlineText(WhitespaceRetentionPolicy.REMOVE_ALL);
  13.235 +                    nextChar();
  13.236 +                } else {
  13.237 +                    skipWhitespace();
  13.238 +                    if (tp.getKind() == TagParser.Kind.INLINE) {
  13.239 +                        tp.parse(p);
  13.240 +                    } else { // handle block tags (ex: @see) in inline content
  13.241 +                        inlineText(WhitespaceRetentionPolicy.REMOVE_ALL); // skip content
  13.242 +                        nextChar();
  13.243 +                    }
  13.244 +                }
  13.245 +            }
  13.246 +        } catch (ParseException e) {
  13.247 +        }
  13.248 +    }
  13.249 +
  13.250 +    private static enum WhitespaceRetentionPolicy {
  13.251 +        RETAIN_ALL,
  13.252 +        REMOVE_FIRST_SPACE,
  13.253 +        REMOVE_ALL
  13.254 +    }
  13.255 +
  13.256 +    /**
  13.257 +     * Read plain text content of an inline tag.
  13.258 +     * Matching pairs of { } are skipped; the text is terminated by the first
  13.259 +     * unmatched }. It is an error if the beginning of the next tag is detected.
  13.260 +     */
  13.261 +    private void inlineText(WhitespaceRetentionPolicy whitespacePolicy) throws ParseException {
  13.262 +        switch (whitespacePolicy) {
  13.263 +            case REMOVE_ALL:
  13.264 +                skipWhitespace();
  13.265 +                break;
  13.266 +            case REMOVE_FIRST_SPACE:
  13.267 +                if (ch == ' ')
  13.268 +                    nextChar();
  13.269 +                break;
  13.270 +            case RETAIN_ALL:
  13.271 +            default:
  13.272 +                // do nothing
  13.273 +                break;
  13.274 +
  13.275 +        }
  13.276 +        int pos = bp;
  13.277 +        int depth = 1;
  13.278 +
  13.279 +        loop:
  13.280 +        while (bp < buflen) {
  13.281 +            switch (ch) {
  13.282 +                case '\n': case '\r': case '\f':
  13.283 +                    newline = true;
  13.284 +                    break;
  13.285 +
  13.286 +                case ' ': case '\t':
  13.287 +                    break;
  13.288 +
  13.289 +                case '{':
  13.290 +                    newline = false;
  13.291 +                    depth++;
  13.292 +                    break;
  13.293 +
  13.294 +                case '}':
  13.295 +                    if (--depth == 0) {
  13.296 +                        return;
  13.297 +                    }
  13.298 +                    newline = false;
  13.299 +                    break;
  13.300 +
  13.301 +                case '@':
  13.302 +                    if (newline)
  13.303 +                        break loop;
  13.304 +                    newline = false;
  13.305 +                    break;
  13.306 +
  13.307 +                default:
  13.308 +                    newline = false;
  13.309 +                    break;
  13.310 +            }
  13.311 +            nextChar();
  13.312 +        }
  13.313 +        throw new ParseException("dc.unterminated.inline.tag");
  13.314 +    }
  13.315 +
  13.316 +    /**
  13.317 +     * Read Java class name, possibly followed by member
  13.318 +     * Matching pairs of {@literal < >} are skipped. The text is terminated by the first
  13.319 +     * unmatched }. It is an error if the beginning of the next tag is detected.
  13.320 +     */
  13.321 +    // TODO: boolean allowMember should be enum FORBID, ALLOW, REQUIRE
  13.322 +    // TODO: improve quality of parse to forbid bad constructions.
  13.323 +    // TODO: update to use ReferenceParser
  13.324 +    @SuppressWarnings("fallthrough")
  13.325 +    protected void reference(boolean allowMember) throws ParseException {
  13.326 +        int pos = bp;
  13.327 +        int depth = 0;
  13.328 +
  13.329 +        // scan to find the end of the signature, by looking for the first
  13.330 +        // whitespace not enclosed in () or <>, or the end of the tag
  13.331 +        loop:
  13.332 +        while (bp < buflen) {
  13.333 +            switch (ch) {
  13.334 +                case '\n': case '\r': case '\f':
  13.335 +                    newline = true;
  13.336 +                    // fallthrough
  13.337 +
  13.338 +                case ' ': case '\t':
  13.339 +                    if (depth == 0)
  13.340 +                        break loop;
  13.341 +                    break;
  13.342 +
  13.343 +                case '(':
  13.344 +                case '<':
  13.345 +                    newline = false;
  13.346 +                    depth++;
  13.347 +                    break;
  13.348 +
  13.349 +                case ')':
  13.350 +                case '>':
  13.351 +                    newline = false;
  13.352 +                    --depth;
  13.353 +                    break;
  13.354 +
  13.355 +                case '}':
  13.356 +                    if (bp == pos)
  13.357 +                        return;
  13.358 +                    newline = false;
  13.359 +                    break loop;
  13.360 +
  13.361 +                case '@':
  13.362 +                    if (newline)
  13.363 +                        break loop;
  13.364 +                    // fallthrough
  13.365 +
  13.366 +                default:
  13.367 +                    newline = false;
  13.368 +
  13.369 +            }
  13.370 +            nextChar();
  13.371 +        }
  13.372 +
  13.373 +        if (depth != 0)
  13.374 +            throw new ParseException("dc.unterminated.signature");
  13.375 +    }
  13.376 +
  13.377 +    /**
  13.378 +     * Read Java identifier
  13.379 +     * Matching pairs of { } are skipped; the text is terminated by the first
  13.380 +     * unmatched }. It is an error if the beginning of the next tag is detected.
  13.381 +     */
  13.382 +    @SuppressWarnings("fallthrough")
  13.383 +    protected void identifier() throws ParseException {
  13.384 +        skipWhitespace();
  13.385 +        int pos = bp;
  13.386 +
  13.387 +        if (isJavaIdentifierStart(ch)) {
  13.388 +            readJavaIdentifier();
  13.389 +            return;
  13.390 +        }
  13.391 +
  13.392 +        throw new ParseException("dc.identifier.expected");
  13.393 +    }
  13.394 +
  13.395 +    /**
  13.396 +     * Read a quoted string.
  13.397 +     * It is an error if the beginning of the next tag is detected.
  13.398 +     */
  13.399 +    @SuppressWarnings("fallthrough")
  13.400 +    protected void quotedString() {
  13.401 +        int pos = bp;
  13.402 +        nextChar();
  13.403 +
  13.404 +        loop:
  13.405 +        while (bp < buflen) {
  13.406 +            switch (ch) {
  13.407 +                case '\n': case '\r': case '\f':
  13.408 +                    newline = true;
  13.409 +                    break;
  13.410 +
  13.411 +                case ' ': case '\t':
  13.412 +                    break;
  13.413 +
  13.414 +                case '"':
  13.415 +                    nextChar();
  13.416 +                    // trim trailing white-space?
  13.417 +                    return;
  13.418 +
  13.419 +                case '@':
  13.420 +                    if (newline)
  13.421 +                        break loop;
  13.422 +
  13.423 +            }
  13.424 +            nextChar();
  13.425 +        }
  13.426 +    }
  13.427 +
  13.428 +    /**
  13.429 +     * Read a term ie. one word.
  13.430 +     * It is an error if the beginning of the next tag is detected.
  13.431 +     */
  13.432 +    @SuppressWarnings("fallthrough")
  13.433 +    protected void inlineWord() {
  13.434 +        int pos = bp;
  13.435 +        int depth = 0;
  13.436 +        loop:
  13.437 +        while (bp < buflen) {
  13.438 +            switch (ch) {
  13.439 +                case '\n':
  13.440 +                    newline = true;
  13.441 +                    // fallthrough
  13.442 +
  13.443 +                case '\r': case '\f': case ' ': case '\t':
  13.444 +                    return;
  13.445 +
  13.446 +                case '@':
  13.447 +                    if (newline)
  13.448 +                        break loop;
  13.449 +
  13.450 +                case '{':
  13.451 +                    depth++;
  13.452 +                    break;
  13.453 +
  13.454 +                case '}':
  13.455 +                    if (depth == 0 || --depth == 0)
  13.456 +                        return;
  13.457 +                    break;
  13.458 +            }
  13.459 +            newline = false;
  13.460 +            nextChar();
  13.461 +        }
  13.462 +    }
  13.463 +
  13.464 +    /**
  13.465 +     * Read general text content of an inline tag, including HTML entities and elements.
  13.466 +     * Matching pairs of { } are skipped; the text is terminated by the first
  13.467 +     * unmatched }. It is an error if the beginning of the next tag is detected.
  13.468 +     */
  13.469 +    @SuppressWarnings("fallthrough")
  13.470 +    private void inlineContent() {
  13.471 +
  13.472 +        skipWhitespace();
  13.473 +        int pos = bp;
  13.474 +        int depth = 1;
  13.475 +
  13.476 +        loop:
  13.477 +        while (bp < buflen) {
  13.478 +
  13.479 +            switch (ch) {
  13.480 +                case '\n': case '\r': case '\f':
  13.481 +                    newline = true;
  13.482 +                    // fall through
  13.483 +
  13.484 +                case ' ': case '\t':
  13.485 +                    nextChar();
  13.486 +                    break;
  13.487 +
  13.488 +                case '&':
  13.489 +                    entity(null);
  13.490 +                    break;
  13.491 +
  13.492 +                case '<':
  13.493 +                    newline = false;
  13.494 +                    html();
  13.495 +                    break;
  13.496 +
  13.497 +                case '{':
  13.498 +                    newline = false;
  13.499 +                    depth++;
  13.500 +                    nextChar();
  13.501 +                    break;
  13.502 +
  13.503 +                case '}':
  13.504 +                    newline = false;
  13.505 +                    if (--depth == 0) {
  13.506 +                        nextChar();
  13.507 +                        return;
  13.508 +                    }
  13.509 +                    nextChar();
  13.510 +                    break;
  13.511 +
  13.512 +                case '@':
  13.513 +                    if (newline)
  13.514 +                        break loop;
  13.515 +                    // fallthrough
  13.516 +
  13.517 +                default:
  13.518 +                    nextChar();
  13.519 +                    break;
  13.520 +            }
  13.521 +        }
  13.522 +
  13.523 +    }
  13.524 +
  13.525 +    protected void entity(Void list) {
  13.526 +        newline = false;
  13.527 +        entity();
  13.528 +    }
  13.529 +
  13.530 +    /**
  13.531 +     * Read an HTML entity.
  13.532 +     * {@literal &identifier; } or {@literal &#digits; } or {@literal &#xhex-digits; }
  13.533 +     */
  13.534 +    protected void entity() {
  13.535 +        nextChar();
  13.536 +        String name = null;
  13.537 +        if (ch == '#') {
  13.538 +            int namep = bp;
  13.539 +            nextChar();
  13.540 +            if (isDecimalDigit(ch)) {
  13.541 +                nextChar();
  13.542 +                while (isDecimalDigit(ch))
  13.543 +                    nextChar();
  13.544 +                name = new String(buf, namep, bp - namep);
  13.545 +            } else if (ch == 'x' || ch == 'X') {
  13.546 +                nextChar();
  13.547 +                if (isHexDigit(ch)) {
  13.548 +                    nextChar();
  13.549 +                    while (isHexDigit(ch))
  13.550 +                        nextChar();
  13.551 +                    name = new String(buf, namep, bp - namep);
  13.552 +                }
  13.553 +            }
  13.554 +        } else if (isIdentifierStart(ch)) {
  13.555 +            name = readIdentifier();
  13.556 +        }
  13.557 +
  13.558 +        if (name != null) {
  13.559 +            if (ch != ';')
  13.560 +                return;
  13.561 +            nextChar();
  13.562 +        }
  13.563 +    }
  13.564 +
  13.565 +    /**
  13.566 +     * Read the start or end of an HTML tag, or an HTML comment
  13.567 +     * {@literal <identifier attrs> } or {@literal </identifier> }
  13.568 +     */
  13.569 +    protected void html() {
  13.570 +        int p = bp;
  13.571 +        nextChar();
  13.572 +        if (isIdentifierStart(ch)) {
  13.573 +            String name = readIdentifier();
  13.574 +            checkHtmlTag(name);
  13.575 +            htmlAttrs();
  13.576 +            if (ch == '/') {
  13.577 +                nextChar();
  13.578 +            }
  13.579 +            if (ch == '>') {
  13.580 +                nextChar();
  13.581 +                return;
  13.582 +            }
  13.583 +        } else if (ch == '/') {
  13.584 +            nextChar();
  13.585 +            if (isIdentifierStart(ch)) {
  13.586 +                readIdentifier();
  13.587 +                skipWhitespace();
  13.588 +                if (ch == '>') {
  13.589 +                    nextChar();
  13.590 +                    return;
  13.591 +                }
  13.592 +            }
  13.593 +        } else if (ch == '!') {
  13.594 +            nextChar();
  13.595 +            if (ch == '-') {
  13.596 +                nextChar();
  13.597 +                if (ch == '-') {
  13.598 +                    nextChar();
  13.599 +                    while (bp < buflen) {
  13.600 +                        int dash = 0;
  13.601 +                        while (ch == '-') {
  13.602 +                            dash++;
  13.603 +                            nextChar();
  13.604 +                        }
  13.605 +                        // Strictly speaking, a comment should not contain "--"
  13.606 +                        // so dash > 2 is an error, dash == 2 implies ch == '>'
  13.607 +                        // See http://www.w3.org/TR/html-markup/syntax.html#syntax-comments
  13.608 +                        // for more details.
  13.609 +                        if (dash >= 2 && ch == '>') {
  13.610 +                            nextChar();
  13.611 +                            return;
  13.612 +                        }
  13.613 +
  13.614 +                        nextChar();
  13.615 +                    }
  13.616 +                }
  13.617 +            }
  13.618 +        }
  13.619 +
  13.620 +        bp = p + 1;
  13.621 +        ch = buf[bp];
  13.622 +    }
  13.623 +
  13.624 +    /**
  13.625 +     * Read a series of HTML attributes, terminated by {@literal > }.
  13.626 +     * Each attribute is of the form {@literal identifier[=value] }.
  13.627 +     * "value" may be unquoted, single-quoted, or double-quoted.
  13.628 +     */
  13.629 +    protected void htmlAttrs() {
  13.630 +        skipWhitespace();
  13.631 +
  13.632 +        loop:
  13.633 +        while (isIdentifierStart(ch)) {
  13.634 +            int namePos = bp;
  13.635 +            String name = readAttributeName();
  13.636 +            skipWhitespace();
  13.637 +            StringBuilder value = new StringBuilder();
  13.638 +            if (ch == '=') {
  13.639 +                nextChar();
  13.640 +                skipWhitespace();
  13.641 +                if (ch == '\'' || ch == '"') {
  13.642 +                    char quote = ch;
  13.643 +                    nextChar();
  13.644 +                    while (bp < buflen && ch != quote) {
  13.645 +                        if (newline && ch == '@') {
  13.646 +                            // No point trying to read more.
  13.647 +                            // In fact, all attrs get discarded by the caller
  13.648 +                            // and superseded by a malformed.html node because
  13.649 +                            // the html tag itself is not terminated correctly.
  13.650 +                            break loop;
  13.651 +                        }
  13.652 +                        value.append(ch);
  13.653 +                        nextChar();
  13.654 +                    }
  13.655 +                    nextChar();
  13.656 +                } else {
  13.657 +                    while (bp < buflen && !isUnquotedAttrValueTerminator(ch)) {
  13.658 +                        value.append(ch);
  13.659 +                        nextChar();
  13.660 +                    }
  13.661 +                }
  13.662 +                skipWhitespace();
  13.663 +            }
  13.664 +            checkHtmlAttr(name, value.toString());
  13.665 +        }
  13.666 +    }
  13.667 +
  13.668 +    protected void attrValueChar(Void list) {
  13.669 +        switch (ch) {
  13.670 +            case '&':
  13.671 +                entity(list);
  13.672 +                break;
  13.673 +
  13.674 +            case '{':
  13.675 +                inlineTag(list);
  13.676 +                break;
  13.677 +
  13.678 +            default:
  13.679 +                nextChar();
  13.680 +        }
  13.681 +    }
  13.682 +
  13.683 +    protected boolean isIdentifierStart(char ch) {
  13.684 +        return Character.isUnicodeIdentifierStart(ch);
  13.685 +    }
  13.686 +
  13.687 +    protected String readIdentifier() {
  13.688 +        int start = bp;
  13.689 +        nextChar();
  13.690 +        while (bp < buflen && Character.isUnicodeIdentifierPart(ch))
  13.691 +            nextChar();
  13.692 +        return new String(buf, start, bp - start);
  13.693 +    }
  13.694 +
  13.695 +    protected String readAttributeName() {
  13.696 +        int start = bp;
  13.697 +        nextChar();
  13.698 +        while (bp < buflen && (Character.isUnicodeIdentifierPart(ch) || ch == '-'))
  13.699 +            nextChar();
  13.700 +        return new String(buf, start, bp - start);
  13.701 +    }
  13.702 +
  13.703 +    protected String readTagName() {
  13.704 +        int start = bp;
  13.705 +        nextChar();
  13.706 +        while (bp < buflen
  13.707 +                && (Character.isUnicodeIdentifierPart(ch) || ch == '.'
  13.708 +                || ch == '-' || ch == ':')) {
  13.709 +            nextChar();
  13.710 +        }
  13.711 +        return new String(buf, start, bp - start);
  13.712 +    }
  13.713 +
  13.714 +    protected boolean isJavaIdentifierStart(char ch) {
  13.715 +        return Character.isJavaIdentifierStart(ch);
  13.716 +    }
  13.717 +
  13.718 +    protected String readJavaIdentifier() {
  13.719 +        int start = bp;
  13.720 +        nextChar();
  13.721 +        while (bp < buflen && Character.isJavaIdentifierPart(ch))
  13.722 +            nextChar();
  13.723 +        return new String(buf, start, bp - start);
  13.724 +    }
  13.725 +
  13.726 +    protected boolean isDecimalDigit(char ch) {
  13.727 +        return ('0' <= ch && ch <= '9');
  13.728 +    }
  13.729 +
  13.730 +    protected boolean isHexDigit(char ch) {
  13.731 +        return ('0' <= ch && ch <= '9')
  13.732 +                || ('a' <= ch && ch <= 'f')
  13.733 +                || ('A' <= ch && ch <= 'F');
  13.734 +    }
  13.735 +
  13.736 +    protected boolean isUnquotedAttrValueTerminator(char ch) {
  13.737 +        switch (ch) {
  13.738 +            case '\f': case '\n': case '\r': case '\t':
  13.739 +            case ' ':
  13.740 +            case '"': case '\'': case '`':
  13.741 +            case '=': case '<': case '>':
  13.742 +                return true;
  13.743 +            default:
  13.744 +                return false;
  13.745 +        }
  13.746 +    }
  13.747 +
  13.748 +    protected boolean isWhitespace(char ch) {
  13.749 +        return Character.isWhitespace(ch);
  13.750 +    }
  13.751 +
  13.752 +    protected void skipWhitespace() {
  13.753 +        while (isWhitespace(ch)) {
  13.754 +            nextChar();
  13.755 +        }
  13.756 +    }
  13.757 +
  13.758 +    /**
  13.759 +     * @param start position of first character of string
  13.760 +     * @param end position of character beyond last character to be included
  13.761 +     */
  13.762 +    String newString(int start, int end) {
  13.763 +        return new String(buf, start, end - start);
  13.764 +    }
  13.765 +
  13.766 +    static abstract class TagParser {
  13.767 +        enum Kind { INLINE, BLOCK }
  13.768 +
  13.769 +        final Kind kind;
  13.770 +        final String name;
  13.771 +
  13.772 +
  13.773 +        TagParser(Kind k, String tk) {
  13.774 +            kind = k;
  13.775 +            name = tk;
  13.776 +        }
  13.777 +
  13.778 +        TagParser(Kind k, String tk, boolean retainWhiteSpace) {
  13.779 +            this(k, tk);
  13.780 +        }
  13.781 +
  13.782 +        Kind getKind() {
  13.783 +            return kind;
  13.784 +        }
  13.785 +
  13.786 +        String getName() {
  13.787 +            return name;
  13.788 +        }
  13.789 +
  13.790 +        abstract void parse(int pos) throws ParseException;
  13.791 +    }
  13.792 +
  13.793 +    /**
  13.794 +     * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javadoc.html#javadoctags">Javadoc Tags</a>
  13.795 +     */
  13.796 +    @SuppressWarnings("deprecation")
  13.797 +    private void initTagParsers() {
  13.798 +        TagParser[] parsers = {
  13.799 +            // @author name-text
  13.800 +            new TagParser(Kind.BLOCK, "author") {
  13.801 +                @Override
  13.802 +                public void parse(int pos) {
  13.803 +                    blockContent();
  13.804 +                }
  13.805 +            },
  13.806 +
  13.807 +            // {@code text}
  13.808 +            new TagParser(Kind.INLINE, "code", true) {
  13.809 +                @Override
  13.810 +                public void parse(int pos) throws ParseException {
  13.811 +                    inlineText(WhitespaceRetentionPolicy.REMOVE_FIRST_SPACE);
  13.812 +                    nextChar();
  13.813 +                }
  13.814 +            },
  13.815 +
  13.816 +            // @deprecated deprecated-text
  13.817 +            new TagParser(Kind.BLOCK, "deprecated") {
  13.818 +                @Override
  13.819 +                public void parse(int pos) {
  13.820 +                    blockContent();
  13.821 +                }
  13.822 +            },
  13.823 +
  13.824 +            // {@docRoot}
  13.825 +            new TagParser(Kind.INLINE, "docRoot") {
  13.826 +                @Override
  13.827 +                public void parse(int pos) throws ParseException {
  13.828 +                    if (ch == '}') {
  13.829 +                        nextChar();
  13.830 +                        return;
  13.831 +                    }
  13.832 +                    inlineText(WhitespaceRetentionPolicy.REMOVE_ALL); // skip unexpected content
  13.833 +                    nextChar();
  13.834 +                    throw new ParseException("dc.unexpected.content");
  13.835 +                }
  13.836 +            },
  13.837 +
  13.838 +            // @exception class-name description
  13.839 +            new TagParser(Kind.BLOCK, "exception") {
  13.840 +                @Override
  13.841 +                public void parse(int pos) throws ParseException {
  13.842 +                    skipWhitespace();
  13.843 +                    reference(false);
  13.844 +                    blockContent();
  13.845 +                }
  13.846 +            },
  13.847 +
  13.848 +            // @hidden hidden-text
  13.849 +            new TagParser(Kind.BLOCK, "hidden") {
  13.850 +                @Override
  13.851 +                public void parse(int pos) {
  13.852 +                    blockContent();
  13.853 +                }
  13.854 +            },
  13.855 +
  13.856 +            // @index search-term options-description
  13.857 +            new TagParser(Kind.INLINE, "index") {
  13.858 +                @Override
  13.859 +                public void parse(int pos) throws ParseException {
  13.860 +                    skipWhitespace();
  13.861 +                    if (ch == '}') {
  13.862 +                        throw new ParseException("dc.no.content");
  13.863 +                    }
  13.864 +                    if (ch == '"') quotedString(); else inlineWord();
  13.865 +                    skipWhitespace();
  13.866 +                    if (ch != '}') {
  13.867 +                        inlineContent();
  13.868 +                    } else {
  13.869 +                        nextChar();
  13.870 +                    }
  13.871 +                }
  13.872 +            },
  13.873 +
  13.874 +            // {@inheritDoc}
  13.875 +            new TagParser(Kind.INLINE, "inheritDoc") {
  13.876 +                @Override
  13.877 +                public void parse(int pos) throws ParseException {
  13.878 +                    if (ch == '}') {
  13.879 +                        nextChar();
  13.880 +                        return;
  13.881 +                    }
  13.882 +                    inlineText(WhitespaceRetentionPolicy.REMOVE_ALL); // skip unexpected content
  13.883 +                    nextChar();
  13.884 +                    throw new ParseException("dc.unexpected.content");
  13.885 +                }
  13.886 +            },
  13.887 +
  13.888 +            // {@link package.class#member label}
  13.889 +            new TagParser(Kind.INLINE, "link") {
  13.890 +                @Override
  13.891 +                public void parse(int pos) throws ParseException {
  13.892 +                    reference(true);
  13.893 +                    inlineContent();
  13.894 +                }
  13.895 +            },
  13.896 +
  13.897 +            // {@linkplain package.class#member label}
  13.898 +            new TagParser(Kind.INLINE, "linkplain") {
  13.899 +                @Override
  13.900 +                public void parse(int pos) throws ParseException {
  13.901 +                    reference(true);
  13.902 +                    inlineContent();
  13.903 +                }
  13.904 +            },
  13.905 +
  13.906 +            // {@literal text}
  13.907 +            new TagParser(Kind.INLINE, "literal", true) {
  13.908 +                @Override
  13.909 +                public void parse(int pos) throws ParseException {
  13.910 +                    inlineText(WhitespaceRetentionPolicy.REMOVE_FIRST_SPACE);
  13.911 +                    nextChar();
  13.912 +                }
  13.913 +            },
  13.914 +
  13.915 +            // @param parameter-name description
  13.916 +            new TagParser(Kind.BLOCK, "param") {
  13.917 +                @Override
  13.918 +                public void parse(int pos) throws ParseException {
  13.919 +                    skipWhitespace();
  13.920 +
  13.921 +                    boolean typaram = false;
  13.922 +                    if (ch == '<') {
  13.923 +                        typaram = true;
  13.924 +                        nextChar();
  13.925 +                    }
  13.926 +
  13.927 +                    identifier();
  13.928 +
  13.929 +                    if (typaram) {
  13.930 +                        if (ch != '>')
  13.931 +                            throw new ParseException("dc.gt.expected");
  13.932 +                        nextChar();
  13.933 +                    }
  13.934 +
  13.935 +                    skipWhitespace();
  13.936 +                    blockContent();
  13.937 +                }
  13.938 +            },
  13.939 +
  13.940 +            // @return description
  13.941 +            new TagParser(Kind.BLOCK, "return") {
  13.942 +                @Override
  13.943 +                public void parse(int pos) {
  13.944 +                    blockContent();
  13.945 +                }
  13.946 +            },
  13.947 +
  13.948 +            // @see reference | quoted-string | HTML
  13.949 +            new TagParser(Kind.BLOCK, "see") {
  13.950 +                @Override
  13.951 +                public void parse(int pos) throws ParseException {
  13.952 +                    skipWhitespace();
  13.953 +                    switch (ch) {
  13.954 +                        case '"':
  13.955 +                            quotedString();
  13.956 +                            skipWhitespace();
  13.957 +                            if (ch == '@'
  13.958 +                                    || ch == EOI && bp == buf.length - 1) {
  13.959 +                                return;
  13.960 +                            }
  13.961 +                            break;
  13.962 +
  13.963 +                        case '<':
  13.964 +                            blockContent();
  13.965 +                            return;
  13.966 +
  13.967 +                        case '@':
  13.968 +                            if (newline)
  13.969 +                                throw new ParseException("dc.no.content");
  13.970 +                            break;
  13.971 +
  13.972 +                        case EOI:
  13.973 +                            if (bp == buf.length - 1)
  13.974 +                                throw new ParseException("dc.no.content");
  13.975 +                            break;
  13.976 +
  13.977 +                        default:
  13.978 +                            if (isJavaIdentifierStart(ch) || ch == '#') {
  13.979 +                                reference(true);
  13.980 +                                blockContent();
  13.981 +                            }
  13.982 +                    }
  13.983 +                    throw new ParseException("dc.unexpected.content");
  13.984 +                }
  13.985 +            },
  13.986 +
  13.987 +            // @serialData data-description
  13.988 +            new TagParser(Kind.BLOCK, "@serialData") {
  13.989 +                @Override
  13.990 +                public void parse(int pos) {
  13.991 +                    blockContent();
  13.992 +                }
  13.993 +            },
  13.994 +
  13.995 +            // @serialField field-name field-type description
  13.996 +            new TagParser(Kind.BLOCK, "serialField") {
  13.997 +                @Override
  13.998 +                public void parse(int pos) throws ParseException {
  13.999 +                    skipWhitespace();
 13.1000 +                    identifier();
 13.1001 +                    skipWhitespace();
 13.1002 +                    reference(false);
 13.1003 +                    if (isWhitespace(ch)) {
 13.1004 +                        skipWhitespace();
 13.1005 +                        blockContent();
 13.1006 +                    }
 13.1007 +                }
 13.1008 +            },
 13.1009 +
 13.1010 +            // @serial field-description | include | exclude
 13.1011 +            new TagParser(Kind.BLOCK, "serial") {
 13.1012 +                @Override
 13.1013 +                public void parse(int pos) {
 13.1014 +                    blockContent();
 13.1015 +                }
 13.1016 +            },
 13.1017 +
 13.1018 +            // @since since-text
 13.1019 +            new TagParser(Kind.BLOCK, "since") {
 13.1020 +                @Override
 13.1021 +                public void parse(int pos) {
 13.1022 +                    blockContent();
 13.1023 +                }
 13.1024 +            },
 13.1025 +
 13.1026 +            // @throws class-name description
 13.1027 +            new TagParser(Kind.BLOCK, "throws") {
 13.1028 +                @Override
 13.1029 +                public void parse(int pos) throws ParseException {
 13.1030 +                    skipWhitespace();
 13.1031 +                    reference(false);
 13.1032 +                    blockContent();
 13.1033 +                }
 13.1034 +            },
 13.1035 +
 13.1036 +            // {@value package.class#field}
 13.1037 +            new TagParser(Kind.INLINE, "value") {
 13.1038 +                @Override
 13.1039 +                public void parse(int pos) throws ParseException {
 13.1040 +                    reference(true);
 13.1041 +                    skipWhitespace();
 13.1042 +                    if (ch == '}') {
 13.1043 +                        nextChar();
 13.1044 +                        return;
 13.1045 +                    }
 13.1046 +                    nextChar();
 13.1047 +                    throw new ParseException("dc.unexpected.content");
 13.1048 +                }
 13.1049 +            },
 13.1050 +
 13.1051 +            // @version version-text
 13.1052 +            new TagParser(Kind.BLOCK, "version") {
 13.1053 +                @Override
 13.1054 +                public void parse(int pos) {
 13.1055 +                    blockContent();
 13.1056 +                }
 13.1057 +            },
 13.1058 +        };
 13.1059 +
 13.1060 +        tagParsers = new HashMap<>();
 13.1061 +        for (TagParser p: parsers)
 13.1062 +            tagParsers.put(p.getName(), p);
 13.1063 +
 13.1064 +    }
 13.1065 +
 13.1066 +    private void initEventAttrs() {
 13.1067 +        eventAttrs = new HashSet<>(Arrays.asList(
 13.1068 +            // See https://www.w3.org/TR/html-markup/global-attributes.html#common.attrs.event-handler
 13.1069 +            "onabort",  "onblur",  "oncanplay",  "oncanplaythrough",
 13.1070 +            "onchange",  "onclick",  "oncontextmenu",  "ondblclick",
 13.1071 +            "ondrag",  "ondragend",  "ondragenter",  "ondragleave",
 13.1072 +            "ondragover",  "ondragstart",  "ondrop",  "ondurationchange",
 13.1073 +            "onemptied",  "onended",  "onerror",  "onfocus",  "oninput",
 13.1074 +            "oninvalid",  "onkeydown",  "onkeypress",  "onkeyup",
 13.1075 +            "onload",  "onloadeddata",  "onloadedmetadata",  "onloadstart",
 13.1076 +            "onmousedown",  "onmousemove",  "onmouseout",  "onmouseover",
 13.1077 +            "onmouseup",  "onmousewheel",  "onpause",  "onplay",
 13.1078 +            "onplaying",  "onprogress",  "onratechange",  "onreadystatechange",
 13.1079 +            "onreset",  "onscroll",  "onseeked",  "onseeking",
 13.1080 +            "onselect",  "onshow",  "onstalled",  "onsubmit",  "onsuspend",
 13.1081 +            "ontimeupdate",  "onvolumechange",  "onwaiting",
 13.1082 +
 13.1083 +            // See https://www.w3.org/TR/html4/sgml/dtd.html
 13.1084 +            // Most of the attributes that take a %Script are also defined as event handlers
 13.1085 +            // in HTML 5. The one exception is onunload.
 13.1086 +            // "onchange",  "onclick",   "ondblclick",  "onfocus",
 13.1087 +            // "onkeydown",  "onkeypress",  "onkeyup",  "onload",
 13.1088 +            // "onmousedown",  "onmousemove",  "onmouseout",  "onmouseover",
 13.1089 +            // "onmouseup",  "onreset",  "onselect",  "onsubmit",
 13.1090 +            "onunload"
 13.1091 +        ));
 13.1092 +    }
 13.1093 +
 13.1094 +    private void initURIAttrs() {
 13.1095 +        uriAttrs = new HashSet<>(Arrays.asList(
 13.1096 +            // See https://www.w3.org/TR/html4/sgml/dtd.html
 13.1097 +            //     https://www.w3.org/TR/html5/
 13.1098 +            // These are all the attributes that take a %URI or a valid URL potentially surrounded
 13.1099 +            // by spaces
 13.1100 +            "action",  "cite",  "classid",  "codebase",  "data",
 13.1101 +            "datasrc",  "for",  "href",  "longdesc",  "profile",
 13.1102 +            "src",  "usemap"
 13.1103 +        ));
 13.1104 +    }
 13.1105 +
 13.1106 +}
    14.1 --- a/src/share/classes/com/sun/tools/javadoc/RootDocImpl.java	Tue Jul 12 14:52:08 2016 -0700
    14.2 +++ b/src/share/classes/com/sun/tools/javadoc/RootDocImpl.java	Mon Jul 18 23:53:12 2016 +0300
    14.3 @@ -1,5 +1,5 @@
    14.4  /*
    14.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
    14.6 + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
    14.7   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    14.8   *
    14.9   * This code is free software; you can redistribute it and/or modify it
   14.10 @@ -381,6 +381,10 @@
   14.11          env.initDoclint(opts, customTagNames);
   14.12      }
   14.13  
   14.14 +    public JavaScriptScanner initJavaScriptScanner(boolean allowScriptInComments) {
   14.15 +        return env.initJavaScriptScanner(allowScriptInComments);
   14.16 +    }
   14.17 +
   14.18      public boolean isFunctionalInterface(AnnotationDesc annotationDesc) {
   14.19          return annotationDesc.annotationType().qualifiedName().equals(
   14.20                  env.syms.functionalInterfaceType.toString()) && env.source.allowLambda();
    15.1 --- a/src/share/classes/com/sun/tools/javadoc/resources/javadoc.properties	Tue Jul 12 14:52:08 2016 -0700
    15.2 +++ b/src/share/classes/com/sun/tools/javadoc/resources/javadoc.properties	Mon Jul 18 23:53:12 2016 +0300
    15.3 @@ -1,5 +1,5 @@
    15.4  #
    15.5 -# Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
    15.6 +# Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
    15.7  # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    15.8  #
    15.9  # This code is free software; you can redistribute it and/or modify it
   15.10 @@ -110,6 +110,8 @@
   15.11  javadoc.Body_missing_from_html_file=Body tag missing from HTML file
   15.12  javadoc.End_body_missing_from_html_file=Close body tag missing from HTML file
   15.13  javadoc.Multiple_package_comments=Multiple sources of package comments found for package "{0}"
   15.14 +javadoc.JavaScript_in_comment=JavaScript found in documentation comment.\n\
   15.15 +    Use --allow-script-in-comments to allow use of JavaScript.
   15.16  javadoc.class_not_found=Class {0} not found.
   15.17  javadoc.error=error
   15.18  javadoc.warning=warning
    16.1 --- a/test/tools/doclint/html/OtherTagsTest.out	Tue Jul 12 14:52:08 2016 -0700
    16.2 +++ b/test/tools/doclint/html/OtherTagsTest.out	Mon Jul 18 23:53:12 2016 +0300
    16.3 @@ -19,10 +19,7 @@
    16.4  OtherTagsTest.java:20: error: element not allowed in documentation comments: <noframes>
    16.5       *  <noframes> </noframes>
    16.6          ^
    16.7 -OtherTagsTest.java:21: error: element not allowed in documentation comments: <script>
    16.8 -     *  <script> </script>
    16.9 -        ^
   16.10  OtherTagsTest.java:22: error: element not allowed in documentation comments: <title>
   16.11       *  <title> </title>
   16.12          ^
   16.13 -9 errors
   16.14 +8 errors
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/test/tools/javadoc/TestScriptInComment.java	Mon Jul 18 23:53:12 2016 +0300
    17.3 @@ -0,0 +1,314 @@
    17.4 +/*
    17.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
    17.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    17.7 + *
    17.8 + * This code is free software; you can redistribute it and/or modify it
    17.9 + * under the terms of the GNU General Public License version 2 only, as
   17.10 + * published by the Free Software Foundation.  Oracle designates this
   17.11 + * particular file as subject to the "Classpath" exception as provided
   17.12 + * by Oracle in the LICENSE file that accompanied this code.
   17.13 + *
   17.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
   17.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   17.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   17.17 + * version 2 for more details (a copy is included in the LICENSE file that
   17.18 + * accompanied this code).
   17.19 + *
   17.20 + * You should have received a copy of the GNU General Public License version
   17.21 + * 2 along with this work; if not, write to the Free Software Foundation,
   17.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   17.23 + *
   17.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   17.25 + * or visit www.oracle.com if you need additional information or have any
   17.26 + * questions.
   17.27 + */
   17.28 +
   17.29 +/**
   17.30 + * @test
   17.31 + * @bug 8138725
   17.32 + * @summary test --allow-script-in-comments
   17.33 + * @run main TestScriptInComment
   17.34 + */
   17.35 +
   17.36 +import java.io.File;
   17.37 +import java.io.FileWriter;
   17.38 +import java.io.IOException;
   17.39 +import java.io.PrintStream;
   17.40 +import java.io.PrintWriter;
   17.41 +import java.io.StringWriter;
   17.42 +import java.util.ArrayList;
   17.43 +import java.util.Arrays;
   17.44 +import java.util.Collections;
   17.45 +import java.util.List;
   17.46 +import java.util.regex.Matcher;
   17.47 +import java.util.regex.Pattern;
   17.48 +
   17.49 +/**
   17.50 + * Combo-style test, exercising combinations of different HTML fragments that may contain
   17.51 + * JavaScript, different places to place those fragments, and whether or not to allow the use
   17.52 + * of JavaScript.
   17.53 + */
   17.54 +public class TestScriptInComment {
   17.55 +    public static void main(String... args) throws Exception {
   17.56 +        new TestScriptInComment().run();
   17.57 +    }
   17.58 +
   17.59 +    /**
   17.60 +     * Representative samples of different fragments of HTML that may contain JavaScript.
   17.61 +     * To facilitate checking the output manually in a browser, the text "#ALERT" will be
   17.62 +     * replaced by a JavaScript call of "alert(msg)", using a message string that is specific
   17.63 +     * to the test case.
   17.64 +     */
   17.65 +    enum Comment {
   17.66 +        LC("<script>#ALERT</script>", true), // script tag in Lower Case
   17.67 +        UC("<SCRIPT>#ALERT</script>", true), // script tag in Upper Case
   17.68 +        WS("< script >#ALERT</script>", false, "-Xdoclint:none"), // script tag with invalid white space
   17.69 +        SA("<script src=\"file\"> #ALERT </script>", true), // script tag with an attribute
   17.70 +        ON("<a onclick='#ALERT'>x</a>", true), // event handler attribute
   17.71 +        URI("<a href='javascript:#ALERT'>x</a>", true); // javadcript URI
   17.72 +
   17.73 +        /**
   17.74 +         * Creates an HTML fragment to be injected into a template.
   17.75 +         * @param text the HTML fragment to put into a doc comment or option.
   17.76 +         * @param hasScript whether or not this fragment does contain legal JavaScript
   17.77 +         * @param opts any additional options to be specified when javadoc is run
   17.78 +         */
   17.79 +        Comment(String text, boolean hasScript, String... opts) {
   17.80 +            this.text = text;
   17.81 +            this.hasScript = hasScript;
   17.82 +            this.opts = Arrays.asList(opts);
   17.83 +        }
   17.84 +
   17.85 +        final String text;
   17.86 +        final boolean hasScript;
   17.87 +        final List<String> opts;
   17.88 +    };
   17.89 +
   17.90 +    /**
   17.91 +     * Representative samples of positions in which javadoc may find JavaScript.
   17.92 +     * Each template contains a series of strings, which are written to files or inferred as options.
   17.93 +     * The first source file implies a corresponding output file which should not be written
   17.94 +     * if the comment contains JavaScript and JavaScript is not allowed.
   17.95 +     */
   17.96 +    enum Template {
   17.97 +        OVR("<html><body> overview #COMMENT </body></html>", "package p; public class C { }"),
   17.98 +        PKGINFO("#COMMENT package p;", "package p; public class C { }"),
   17.99 +        PKGHTML("<html><body>#COMMENT package p;</body></html>", "package p; public class C { }"),
  17.100 +        CLS("package p; #COMMENT public class C { }"),
  17.101 +        CON("package p; public class C { #COMMENT public C() { } }"),
  17.102 +        FLD("package p; public class C { #COMMENT public int f; }"),
  17.103 +        MTH("package p; public class C { #COMMENT public void m() { } }"),
  17.104 +        TOP("-top", "lorem #COMMENT ipsum", "package p; public class C { }"),
  17.105 +        HDR("-header", "lorem #COMMENT ipsum", "package p; public class C { }"),
  17.106 +        FTR("-footer", "lorem #COMMENT ipsum", "package p; public class C { }"),
  17.107 +        BTM("-bottom", "lorem #COMMENT ipsum", "package p; public class C { }"),
  17.108 +        DTTL("-doctitle", "lorem #COMMENT ipsum", "package p; public class C { }"),
  17.109 +        PHDR("-packagesheader", "lorem #COMMENT ipsum", "package p; public class C { }");
  17.110 +
  17.111 +        Template(String... args) {
  17.112 +            opts = new ArrayList<String>();
  17.113 +            sources = new ArrayList<String>();
  17.114 +            int i = 0;
  17.115 +            while (args[i].startsWith("-")) {
  17.116 +                // all options being tested have a single argument that follow the option
  17.117 +                opts.add(args[i++]);
  17.118 +                opts.add(args[i++]);
  17.119 +            }
  17.120 +            while(i < args.length) {
  17.121 +                sources.add(args[i++]);
  17.122 +            }
  17.123 +        }
  17.124 +
  17.125 +        // groups: 1 <html> or not;  2: package name;  3: class name
  17.126 +        private final Pattern pat =
  17.127 +                Pattern.compile("(?i)(<html>)?.*?(?:package ([a-z]+);.*?(?:class ([a-z]+).*)?)?");
  17.128 +
  17.129 +        /**
  17.130 +         * Infer the file in which to write the given source.
  17.131 +         * @param dir the base source directory
  17.132 +         * @param src the source text
  17.133 +         * @return the file in which the source should be written
  17.134 +         */
  17.135 +        File getSrcFile(File srcDir, String src) {
  17.136 +            String f;
  17.137 +            Matcher m = pat.matcher(src);
  17.138 +            if (!m.matches())
  17.139 +                throw new Error("match failed");
  17.140 +            if (m.group(3) != null) {
  17.141 +                f = m.group(2) + "/" + m.group(3) + ".java";
  17.142 +            } else if (m.group(2) != null) {
  17.143 +                f = m.group(2) + "/" + (m.group(1) == null ? "package-info.java" : "package.html");
  17.144 +            } else {
  17.145 +                f = "overview.html";
  17.146 +            }
  17.147 +            return new File(srcDir, f);
  17.148 +        }
  17.149 +
  17.150 +        /**
  17.151 +         * Get the options to give to javadoc.
  17.152 +         * @param srcDir the srcDir to use -overview is needed
  17.153 +         * @return
  17.154 +         */
  17.155 +        List<String> getOpts(File srcDir) {
  17.156 +            if (!opts.isEmpty()) {
  17.157 +                return opts;
  17.158 +            } else if (sources.get(0).contains("overview")) {
  17.159 +                return Arrays.asList("-overview", getSrcFile(srcDir, sources.get(0)).getPath());
  17.160 +            } else {
  17.161 +                return Collections.emptyList();
  17.162 +            }
  17.163 +        }
  17.164 +
  17.165 +        /**
  17.166 +         * Gets the output file corresponding to the first source file.
  17.167 +         * This file should not be written if the comment contains JavaScript and JavaScripot is
  17.168 +         * not allowed.
  17.169 +         * @param dir the base output directory
  17.170 +         * @return the output file
  17.171 +         */
  17.172 +        File getOutFile(File outDir) {
  17.173 +            String f;
  17.174 +            Matcher m = pat.matcher(sources.get(0));
  17.175 +            if (!m.matches())
  17.176 +                throw new Error("match failed");
  17.177 +            if (m.group(3) != null) {
  17.178 +                f = m.group(2) + "/" + m.group(3) + ".html";
  17.179 +            } else if (m.group(2) != null) {
  17.180 +                f = m.group(2) + "/package-summary.html";
  17.181 +            } else {
  17.182 +                f = "overview-summary.html";
  17.183 +            }
  17.184 +            return new File(outDir, f);
  17.185 +        }
  17.186 +
  17.187 +        final List<String> opts;
  17.188 +        final List<String> sources;
  17.189 +    };
  17.190 +
  17.191 +    enum Option {
  17.192 +        OFF(null),
  17.193 +        ON("--allow-script-in-comments");
  17.194 +
  17.195 +        Option(String text) {
  17.196 +            this.text = text;
  17.197 +        }
  17.198 +
  17.199 +        final String text;
  17.200 +    };
  17.201 +
  17.202 +    private PrintStream out = System.err;
  17.203 +
  17.204 +    public void run() throws Exception {
  17.205 +        int count = 0;
  17.206 +        for (Template template: Template.values()) {
  17.207 +            for (Comment comment: Comment.values()) {
  17.208 +                for (Option option: Option.values()) {
  17.209 +                    if (test(template, comment, option)) {
  17.210 +                        count++;
  17.211 +                    }
  17.212 +                }
  17.213 +            }
  17.214 +        }
  17.215 +
  17.216 +        out.println(count + " test cases run");
  17.217 +        if (errors > 0) {
  17.218 +            throw new Exception(errors + " errors occurred");
  17.219 +        }
  17.220 +    }
  17.221 +
  17.222 +    boolean test(Template template, Comment comment, Option option) throws IOException {
  17.223 +        if (option == Option.ON && !comment.hasScript) {
  17.224 +            // skip --allowScriptInComments if comment does not contain JavaScript
  17.225 +            return false;
  17.226 +        }
  17.227 +
  17.228 +        String test = template + "-" + comment + "-" + option;
  17.229 +        out.println("Test: " + test);
  17.230 +
  17.231 +        File dir = new File(test);
  17.232 +        dir.mkdirs();
  17.233 +        File srcDir = new File(dir, "src");
  17.234 +        File outDir = new File(dir, "out");
  17.235 +
  17.236 +        String alert = "alert(\"" + test + "\");";
  17.237 +        for (String src: template.sources) {
  17.238 +            writeFile(template.getSrcFile(srcDir, src),
  17.239 +                src.replace("#COMMENT",
  17.240 +                        "/** " + comment.text.replace("#ALERT", alert) + " **/"));
  17.241 +        }
  17.242 +
  17.243 +        List<String> opts = new ArrayList<String>();
  17.244 +        opts.add("-sourcepath");
  17.245 +        opts.add(srcDir.getPath());
  17.246 +        opts.add("-d");
  17.247 +        opts.add(outDir.getPath());
  17.248 +        if (option.text != null)
  17.249 +            opts.add(option.text);
  17.250 +        for (String opt: template.getOpts(srcDir)) {
  17.251 +            opts.add(opt.replace("#COMMENT", comment.text.replace("#ALERT", alert)));
  17.252 +        }
  17.253 +        opts.addAll(comment.opts);
  17.254 +        opts.add("-noindex");   // index not required; save time/space writing files
  17.255 +        opts.add("p");
  17.256 +
  17.257 +        StringWriter sw = new StringWriter();
  17.258 +        PrintWriter pw = new PrintWriter(sw);
  17.259 +        int rc = javadoc(opts, pw);
  17.260 +        pw.close();
  17.261 +        String log = sw.toString();
  17.262 +        writeFile(new File(dir, "log.txt"), log);
  17.263 +
  17.264 +        out.println("opts: " + opts);
  17.265 +        out.println("  rc: " + rc);
  17.266 +        out.println(" log:");
  17.267 +        out.println(log);
  17.268 +
  17.269 +        String ERROR = "Use --allow-script-in-comment";
  17.270 +        File outFile = template.getOutFile(outDir);
  17.271 +
  17.272 +        boolean expectErrors = comment.hasScript && (option == Option.OFF);
  17.273 +
  17.274 +        if (expectErrors) {
  17.275 +            check(rc != 0, "unexpected exit code: " + rc);
  17.276 +            check(log.contains(ERROR), "expected error message not found");
  17.277 +            check(!outFile.exists(), "output file found unexpectedly");
  17.278 +        } else {
  17.279 +            check(rc == 0, "unexpected exit code: " + rc);
  17.280 +            check(!log.contains(ERROR), "error message found");
  17.281 +            check(outFile.exists(), "output file not found");
  17.282 +        }
  17.283 +
  17.284 +        out.println();
  17.285 +        return true;
  17.286 +    }
  17.287 +
  17.288 +    int javadoc(List<String> opts, PrintWriter pw) {
  17.289 +        return com.sun.tools.javadoc.Main.execute("javadoc", pw, pw, pw,
  17.290 +                "com.sun.tools.doclets.standard.Standard", opts.toArray(new String[opts.size()]));
  17.291 +    }
  17.292 +
  17.293 +    File writeFile(File f, String text) throws IOException {
  17.294 +        f.getParentFile().mkdirs();
  17.295 +        FileWriter fw = new FileWriter(f);
  17.296 +        try {
  17.297 +            fw.write(text);
  17.298 +        } finally {
  17.299 +            fw.close();
  17.300 +        }
  17.301 +        return f;
  17.302 +    }
  17.303 +
  17.304 +    void check(boolean cond, String errMessage) {
  17.305 +        if (!cond) {
  17.306 +            error(errMessage);
  17.307 +        }
  17.308 +    }
  17.309 +
  17.310 +    void error(String message) {
  17.311 +        out.println("Error: " + message);
  17.312 +        errors++;
  17.313 +    }
  17.314 +
  17.315 +    int errors = 0;
  17.316 +}
  17.317 +

mercurial