8190818: Introduce a new Script builder class
authorjjg
Thu, 16 Nov 2017 15:06:17 -0800
changeset 47847 7640bc1eb94f
parent 47846 4e78aba768f0
child 47848 6b1311fbbaba
8190818: Introduce a new Script builder class Reviewed-by: bpatel, ksrini
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FrameOutputWriter.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexRedirectWriter.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexWriter.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ContentBuilder.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Script.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Table.java
src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Content.java
test/langtools/jdk/javadoc/doclet/testGroupName/TestGroupName.java
test/langtools/jdk/javadoc/doclet/testModules/TestModules.java
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AbstractMemberWriter.java	Thu Nov 16 15:06:17 2017 -0800
@@ -592,7 +592,7 @@
         }
         Table table = getSummaryTable();
         if (table.needsScript()) {
-            writer.getScript().addContent(table.getScript());
+            writer.getMainBodyScript().append(table.getScript());
         }
         return table.toContent();
     }
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FrameOutputWriter.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/FrameOutputWriter.java	Thu Nov 16 15:06:17 2017 -0800
@@ -32,13 +32,11 @@
 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
-import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
-import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
+import jdk.javadoc.internal.doclets.formats.html.markup.Script;
 import jdk.javadoc.internal.doclets.toolkit.Content;
 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
-import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
 
 
 /**
@@ -99,12 +97,12 @@
         HtmlTree body = new HtmlTree(HtmlTag.BODY);
         body.addAttr(HtmlAttr.ONLOAD, "loadFrames()");
         String topFilePath = configuration.topFile.getPath();
-        String javaScriptRefresh = "\nif (targetPage == \"\" || targetPage == \"undefined\")\n" +
-                "     window.location.replace('" + topFilePath + "');\n";
-        RawHtml scriptContent = new RawHtml(javaScriptRefresh.replace("\n", DocletConstants.NL));
-        HtmlTree scriptTree = HtmlTree.SCRIPT();
-        scriptTree.addContent(scriptContent);
-        body.addContent(scriptTree);
+        Script script = new Script(
+                "\nif (targetPage == \"\" || targetPage == \"undefined\")\n" +
+                "     window.location.replace(")
+                .appendStringLiteral(topFilePath, '\'')
+                .append(");\n");
+        body.addContent(script.asContent());
         Content noScript = HtmlTree.NOSCRIPT(contents.noScriptMessage);
         body.addContent(noScript);
         if (configuration.allowTag(HtmlTag.MAIN)) {
@@ -135,12 +133,12 @@
         Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
         Content head = new HtmlTree(HtmlTag.HEAD);
         head.addContent(getGeneratedBy(!configuration.notimestamp));
-        Content windowTitle = HtmlTree.TITLE(new StringContent(title));
+        Content windowTitle = HtmlTree.TITLE(title);
         head.addContent(windowTitle);
         Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, configuration.charset);
         head.addContent(meta);
         addStyleSheetProperties(configuration, head);
-        head.addContent(getFramesJavaScript());
+        head.addContent(getFramesScript().asContent());
         Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
                 head, body);
         Content htmlDocument = new HtmlDocument(htmlDocType,
@@ -217,4 +215,64 @@
         frame.setStyle(HtmlStyle.rightIframe);
         contentTree.addContent(frame);
     }
+
+    /**
+     * Returns a content tree for the SCRIPT tag for the main page(index.html).
+     *
+     * @return a content for the SCRIPT tag
+     */
+    protected Script getFramesScript() {
+        return new Script("\n" +
+                "    tmpTargetPage = \"\" + window.location.search;\n" +
+                "    if (tmpTargetPage != \"\" && tmpTargetPage != \"undefined\")\n" +
+                "        tmpTargetPage = tmpTargetPage.substring(1);\n" +
+                "    if (tmpTargetPage.indexOf(\":\") != -1 || (tmpTargetPage != \"\" && !validURL(tmpTargetPage)))\n" +
+                "        tmpTargetPage = \"undefined\";\n" +
+                "    targetPage = tmpTargetPage;\n" +
+                "    function validURL(url) {\n" +
+                "        try {\n" +
+                "            url = decodeURIComponent(url);\n" +
+                "        }\n" +
+                "        catch (error) {\n" +
+                "            return false;\n" +
+                "        }\n" +
+                "        var pos = url.indexOf(\".html\");\n" +
+                "        if (pos == -1 || pos != url.length - 5)\n" +
+                "            return false;\n" +
+                "        var allowNumber = false;\n" +
+                "        var allowSep = false;\n" +
+                "        var seenDot = false;\n" +
+                "        for (var i = 0; i < url.length - 5; i++) {\n" +
+                "            var ch = url.charAt(i);\n" +
+                "            if ('a' <= ch && ch <= 'z' ||\n" +
+                "                    'A' <= ch && ch <= 'Z' ||\n" +
+                "                    ch == '$' ||\n" +
+                "                    ch == '_' ||\n" +
+                "                    ch.charCodeAt(0) > 127) {\n" +
+                "                allowNumber = true;\n" +
+                "                allowSep = true;\n" +
+                "            } else if ('0' <= ch && ch <= '9'\n" +
+                "                    || ch == '-') {\n" +
+                "                if (!allowNumber)\n" +
+                "                     return false;\n" +
+                "            } else if (ch == '/' || ch == '.') {\n" +
+                "                if (!allowSep)\n" +
+                "                    return false;\n" +
+                "                allowNumber = false;\n" +
+                "                allowSep = false;\n" +
+                "                if (ch == '.')\n" +
+                "                     seenDot = true;\n" +
+                "                if (ch == '/' && seenDot)\n" +
+                "                     return false;\n" +
+                "            } else {\n" +
+                "                return false;\n" +
+                "            }\n" +
+                "        }\n" +
+                "        return true;\n" +
+                "    }\n" +
+                "    function loadFrames() {\n" +
+                "        if (targetPage != \"\" && targetPage != \"undefined\")\n" +
+                "             top.classFrame.location = top.targetPage;\n" +
+                "    }\n");
+    }
 }
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java	Thu Nov 16 15:06:17 2017 -0800
@@ -78,6 +78,7 @@
 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
 import jdk.javadoc.internal.doclets.formats.html.markup.RawHtml;
+import jdk.javadoc.internal.doclets.formats.html.markup.Script;
 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
 import jdk.javadoc.internal.doclets.toolkit.AnnotationTypeWriter;
 import jdk.javadoc.internal.doclets.toolkit.ClassWriter;
@@ -178,6 +179,13 @@
     final static Pattern IMPROPER_HTML_CHARS = Pattern.compile(".*[&<>].*");
 
     /**
+     * The window title of this file.
+     */
+    protected String winTitle;
+
+    protected Script mainBodyScript;
+
+    /**
      * Constructor to construct the HtmlStandardWriter object.
      *
      * @param path File to be generated.
@@ -258,19 +266,18 @@
      * @return a content tree for the script
      */
     public Content getAllClassesLinkScript(String id) {
-        HtmlTree script = HtmlTree.SCRIPT();
-        String scriptCode = "<!--\n" +
-                "  allClassesLink = document.getElementById(\"" + id + "\");\n" +
+        Script script = new Script("<!--\n" +
+                "  allClassesLink = document.getElementById(")
+                .appendStringLiteral(id)
+                .append(");\n" +
                 "  if(window==top) {\n" +
                 "    allClassesLink.style.display = \"block\";\n" +
                 "  }\n" +
                 "  else {\n" +
                 "    allClassesLink.style.display = \"none\";\n" +
                 "  }\n" +
-                "  //-->\n";
-        Content scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL));
-        script.addContent(scriptContent);
-        Content div = HtmlTree.DIV(script);
+                "  //-->\n");
+        Content div = HtmlTree.DIV(script.asContent());
         Content div_noscript = HtmlTree.DIV(contents.noScriptMessage);
         Content noScript = HtmlTree.NOSCRIPT(div_noscript);
         div.addContent(noScript);
@@ -409,7 +416,7 @@
         Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
         Content head = new HtmlTree(HtmlTag.HEAD);
         head.addContent(getGeneratedBy(!configuration.notimestamp));
-        head.addContent(getTitle());
+        head.addContent(HtmlTree.TITLE(winTitle));
         Content meta = HtmlTree.META("Content-Type", CONTENT_TYPE, configuration.charset);
         head.addContent(meta);
         if (!configuration.notimestamp) {
@@ -607,13 +614,11 @@
                 tree.addContent(fixedNavDiv);
                 HtmlTree paddingDiv = HtmlTree.DIV(HtmlStyle.navPadding, Contents.SPACE);
                 tree.addContent(paddingDiv);
-                HtmlTree scriptTree = HtmlTree.SCRIPT();
-                String scriptCode = "<!--\n"
+                Script script = new Script(
+                        "<!--\n"
                         + "$('.navPadding').css('padding-top', $('.fixedNav').css(\"height\"));\n"
-                        + "//-->\n";
-                RawHtml scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL));
-                scriptTree.addContent(scriptContent);
-                tree.addContent(scriptTree);
+                        + "//-->\n");
+                tree.addContent(script.asContent());
             } else {
                 subDiv.addContent(getMarkerAnchor(SectionName.SKIP_NAVBAR_BOTTOM));
                 tree.addContent(subDiv);
@@ -2113,9 +2118,11 @@
         HtmlTree javascript = HtmlTree.SCRIPT(pathToRoot.resolve(DocPaths.JAVASCRIPT).getPath());
         head.addContent(javascript);
         if (configuration.createindex) {
-            if (pathToRoot != null && script != null) {
+            if (pathToRoot != null && mainBodyScript != null) {
                 String ptrPath = pathToRoot.isEmpty() ? "." : pathToRoot.getPath();
-                script.addContent(new RawHtml("var pathtoroot = \"" + ptrPath + "/\";loadScripts(document, \'script\');"));
+                mainBodyScript.append("var pathtoroot = ")
+                        .appendStringLiteral(ptrPath + "/")
+                        .append(";loadScripts(document, \'script\');");
             }
             addJQueryFile(head, DocPaths.JSZIP_MIN);
             addJQueryFile(head, DocPaths.JSZIPUTILS_MIN);
@@ -2548,7 +2555,52 @@
         return new TableHeader(contents.packageLabel, contents.descriptionLabel);
     }
 
-    Content getScript() {
+    /**
+     * Returns an HtmlTree for the SCRIPT tag.
+     *
+     * @return an HtmlTree for the SCRIPT tag
+     */
+    protected Script getWinTitleScript() {
+        Script script = new Script();
+        if (winTitle != null && winTitle.length() > 0) {
+            script.append("<!--\n" +
+                    "    try {\n" +
+                    "        if (location.href.indexOf('is-external=true') == -1) {\n" +
+                    "            parent.document.title=")
+                    .appendStringLiteral(winTitle)
+                    .append(";\n" +
+                    "        }\n" +
+                    "    }\n" +
+                    "    catch(err) {\n" +
+                    "    }\n" +
+                    "//-->\n");
+        }
         return script;
     }
+
+    /**
+     * Returns an HtmlTree for the BODY tag.
+     *
+     * @param includeScript  set true if printing windowtitle script
+     * @param title title for the window
+     * @return an HtmlTree for the BODY tag
+     */
+    public HtmlTree getBody(boolean includeScript, String title) {
+        HtmlTree body = new HtmlTree(HtmlTag.BODY);
+        // Set window title string which is later printed
+        this.winTitle = title;
+        // Don't print windowtitle script for overview-frame, allclasses-frame
+        // and package-frame
+        if (includeScript) {
+            this.mainBodyScript = getWinTitleScript();
+            body.addContent(mainBodyScript.asContent());
+            Content noScript = HtmlTree.NOSCRIPT(HtmlTree.DIV(contents.noScriptMessage));
+            body.addContent(noScript);
+        }
+        return body;
+    }
+
+    Script getMainBodyScript() {
+        return mainBodyScript;
+    }
 }
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexRedirectWriter.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexRedirectWriter.java	Thu Nov 16 15:06:17 2017 -0800
@@ -32,6 +32,7 @@
 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocument;
 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
+import jdk.javadoc.internal.doclets.formats.html.markup.Script;
 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
 import jdk.javadoc.internal.doclets.toolkit.Content;
 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
@@ -73,18 +74,18 @@
 
         String title = (configuration.windowtitle.length() > 0)
                 ? configuration.windowtitle
-                : configuration.getText("doclet.Generated_Docs_Untitled");
+                : resources.getText("doclet.Generated_Docs_Untitled");
 
-        Content windowTitle = HtmlTree.TITLE(new StringContent(title));
+        Content windowTitle = HtmlTree.TITLE(title);
         head.addContent(windowTitle);
         Content metaContentType = HtmlTree.META("Content", CONTENT_TYPE, configuration.charset);
         head.addContent(metaContentType);
 
         String topFilePath = configuration.topFile.getPath();
-        String javaScriptRefresh = "window.location.replace('" + topFilePath + "')";
-        HtmlTree scriptTree = HtmlTree.SCRIPT();
-        scriptTree.addContent(javaScriptRefresh);
-        head.addContent(scriptTree);
+        Script script = new Script("window.location.replace(")
+                .appendStringLiteral(topFilePath, '\'')
+                .append(")");
+        head.addContent(script.asContent());
         HtmlTree metaRefresh = new HtmlTree(HtmlTag.META);
         metaRefresh.addAttr(HtmlAttr.HTTP_EQUIV, "Refresh");
         metaRefresh.addAttr(HtmlAttr.CONTENT, "0;" + topFilePath);
@@ -98,7 +99,7 @@
 
         ContentBuilder bodyContent = new ContentBuilder();
         bodyContent.addContent(HtmlTree.NOSCRIPT(
-                HtmlTree.P(configuration.getContent("doclet.No_Script_Message"))));
+                HtmlTree.P(contents.getContent("doclet.No_Script_Message"))));
 
         bodyContent.addContent(HtmlTree.P(HtmlTree.A(topFilePath, new StringContent(topFilePath))));
 
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexWriter.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexWriter.java	Thu Nov 16 15:06:17 2017 -0800
@@ -162,7 +162,7 @@
             }
 
             if (table.needsScript()) {
-                getScript().addContent(table.getScript());
+                mainBodyScript.append(table.getScript());
             }
         }
     }
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleWriterImpl.java	Thu Nov 16 15:06:17 2017 -0800
@@ -626,7 +626,7 @@
         addPackageTableRows(table);
         li.addContent(table.toContent());
         if (table.needsScript()) {
-            script.addContent(new RawHtml(table.getScript()));
+            mainBodyScript.append(table.getScript());
         }
     }
 
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java	Thu Nov 16 15:06:17 2017 -0800
@@ -144,7 +144,7 @@
             }
 
             if (table.needsScript()) {
-                getScript().addContent(table.getScript());
+                getMainBodyScript().append(table.getScript());
             }
         }
     }
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/SourceToHTMLConverter.java	Thu Nov 16 15:06:17 2017 -0800
@@ -42,6 +42,7 @@
 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
 import jdk.javadoc.internal.doclets.toolkit.Content;
 import jdk.javadoc.internal.doclets.toolkit.Messages;
+import jdk.javadoc.internal.doclets.toolkit.Resources;
 import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
@@ -77,6 +78,7 @@
 
     private final HtmlConfiguration configuration;
     private final Messages messages;
+    private final Resources resources;
     private final Utils utils;
 
     private final DocletEnvironment docEnv;
@@ -93,6 +95,7 @@
                                   DocPath outputdir) {
         this.configuration  = configuration;
         this.messages = configuration.getMessages();
+        this.resources = configuration.resources;
         this.utils = configuration.utils;
         this.docEnv = rd;
         this.outputdir = outputdir;
@@ -193,7 +196,7 @@
             body.addContent((configuration.allowTag(HtmlTag.MAIN)) ? HtmlTree.MAIN(div) : div);
             writeToFile(body, outputdir.resolve(DocPath.forClass(utils, te)));
         } catch (IOException e) {
-            String message = configuration.resources.getText("doclet.exception.read.file", fo.getName());
+            String message = resources.getText("doclet.exception.read.file", fo.getName());
             throw new SimpleDocletException(message, e);
         }
     }
@@ -209,8 +212,7 @@
                 ? DocType.HTML5
                 : DocType.TRANSITIONAL;
         Content head = new HtmlTree(HtmlTag.HEAD);
-        head.addContent(HtmlTree.TITLE(new StringContent(
-                configuration.getText("doclet.Window_Source_title"))));
+        head.addContent(HtmlTree.TITLE(resources.getText("doclet.Window_Source_title")));
         addStyleSheetProperties(head);
         Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
                 head, body);
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ContentBuilder.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/ContentBuilder.java	Thu Nov 16 15:06:17 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, 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
@@ -39,6 +39,14 @@
 public class ContentBuilder extends Content {
     protected List<Content> contents = Collections.emptyList();
 
+    public ContentBuilder() { }
+
+    public ContentBuilder(Content... contents) {
+        for (Content c : contents) {
+            addContent(c);
+        }
+    }
+
     @Override
     public void addContent(Content content) {
         nullCheck(content);
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java	Thu Nov 16 15:06:17 2017 -0800
@@ -74,8 +74,8 @@
      */
     public HtmlTree(HtmlTag tag, Content... contents) {
         this(tag);
-        for (Content content: contents)
-            addContent(content);
+        for (Content c: contents)
+            addContent(c);
     }
 
     /**
@@ -86,8 +86,8 @@
      */
     public HtmlTree(HtmlTag tag, List<Content> contents) {
         this(tag);
-        for (Content content: contents)
-            addContent(content);
+        for (Content c: contents)
+            addContent(c);
     }
 
     /**
@@ -146,8 +146,8 @@
     @Override
     public void addContent(Content tagContent) {
         if (tagContent instanceof ContentBuilder) {
-            for (Content content: ((ContentBuilder)tagContent).contents) {
-                addContent(content);
+            for (Content c: ((ContentBuilder)tagContent).contents) {
+                addContent(c);
             }
         }
         else if (tagContent == HtmlTree.EMPTY || tagContent.isValid()) {
@@ -158,9 +158,9 @@
     }
 
     /**
-     * This method adds a string content to the htmltree. If the last content member
+     * Adds String content to the HTML tree. If the last content member
      * added is a StringContent, append the string to that StringContent or else
-     * create a new StringContent and add it to the html tree.
+     * create a new StringContent and add it to the HTML tree.
      *
      * @param stringContent string content that needs to be added
      */
@@ -177,6 +177,10 @@
             addContent(new StringContent(stringContent));
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public int charCount() {
         int n = 0;
         for (Content c : content)
@@ -185,7 +189,7 @@
     }
 
     /**
-     * Given a string, escape all special html characters and
+     * Given a string, escape all special HTML characters and
      * return the result.
      *
      * @param s The string to check.
@@ -717,24 +721,13 @@
     /**
      * Generates a SCRIPT tag with the type and src attributes.
      *
-     * @param type type of link
      * @param src the path for the script
      * @return an HtmlTree object for the SCRIPT tag
      */
     public static HtmlTree SCRIPT(String src) {
-        HtmlTree htmltree = HtmlTree.SCRIPT();
-        htmltree.addAttr(HtmlAttr.SRC, nullCheck(src));
-        return htmltree;
-    }
-
-    /**
-     * Generates a SCRIPT tag with the type attribute.
-     *
-     * @return an HtmlTree object for the SCRIPT tag
-     */
-    public static HtmlTree SCRIPT() {
         HtmlTree htmltree = new HtmlTree(HtmlTag.SCRIPT);
         htmltree.addAttr(HtmlAttr.TYPE, "text/javascript");
+        htmltree.addAttr(HtmlAttr.SRC, nullCheck(src));
         return htmltree;
     }
 
@@ -912,8 +905,8 @@
      * @param body content for the tag
      * @return an HtmlTree object for the TITLE tag
      */
-    public static HtmlTree TITLE(Content body) {
-        HtmlTree htmltree = new HtmlTree(HtmlTag.TITLE, nullCheck(body));
+    public static HtmlTree TITLE(String body) {
+        HtmlTree htmltree = new HtmlTree(HtmlTag.TITLE, new StringContent(body));
         return htmltree;
     }
 
@@ -949,6 +942,7 @@
     /**
      * {@inheritDoc}
      */
+    @Override
     public boolean isEmpty() {
         return (!hasContent() && !hasAttrs());
     }
@@ -988,6 +982,7 @@
      *
      * @return true if the HTML tree is valid
      */
+    @Override
     public boolean isValid() {
         switch (htmlTag) {
             case A :
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlWriter.java	Thu Nov 16 15:06:17 2017 -0800
@@ -29,15 +29,13 @@
 
 import jdk.javadoc.internal.doclets.formats.html.HtmlConfiguration;
 import jdk.javadoc.internal.doclets.toolkit.Content;
-import jdk.javadoc.internal.doclets.toolkit.Resources;
 import jdk.javadoc.internal.doclets.toolkit.util.DocFile;
 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
-import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
 
 
 /**
- * Class for the Html format code generation.
+ * Class for the HTML format code generation.
  * Initializes PrintWriter with FileWriter, to enable print
  * related methods to generate the code to the named File through FileWriter.
  *
@@ -51,21 +49,8 @@
  */
 public class HtmlWriter {
 
-    /**
-     * The window title of this file.
-     */
-    protected String winTitle;
-
-    /**
-     * The configuration.
-     */
-    protected HtmlConfiguration configuration;
-
     private final DocFile docFile;
 
-    protected Content script;
-
-
     /**
      * Constructor.
      *
@@ -74,13 +59,7 @@
      *             or null if none to be created
      */
     public HtmlWriter(HtmlConfiguration configuration, DocPath path) {
-        this.configuration = configuration;
         docFile = DocFile.createFileForOutput(configuration, path);
-
-        // The following should be converted to shared Content objects
-        // and moved to Contents, but that will require additional
-        // changes at the use sites.
-        Resources resources = configuration.getResources();
     }
 
     public void write(Content c) throws DocFileIOException {
@@ -90,171 +69,4 @@
             throw new DocFileIOException(docFile, DocFileIOException.Mode.WRITE, e);
         }
     }
-
-    /**
-     * Returns an HtmlTree for the SCRIPT tag.
-     *
-     * @return an HtmlTree for the SCRIPT tag
-     */
-    protected HtmlTree getWinTitleScript(){
-        HtmlTree scriptTree = HtmlTree.SCRIPT();
-        if(winTitle != null && winTitle.length() > 0) {
-            String scriptCode = "<!--\n" +
-                    "    try {\n" +
-                    "        if (location.href.indexOf('is-external=true') == -1) {\n" +
-                    "            parent.document.title=\"" + escapeJavaScriptChars(winTitle) + "\";\n" +
-                    "        }\n" +
-                    "    }\n" +
-                    "    catch(err) {\n" +
-                    "    }\n" +
-                    "//-->\n";
-            RawHtml scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL));
-            scriptTree.addContent(scriptContent);
-        }
-        return scriptTree;
-    }
-
-    /**
-     * Returns a String with escaped special JavaScript characters.
-     *
-     * @param s String that needs to be escaped
-     * @return a valid escaped JavaScript string
-     */
-    private static String escapeJavaScriptChars(String s) {
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < s.length(); i++) {
-            char ch = s.charAt(i);
-            switch (ch) {
-                case '\b':
-                    sb.append("\\b");
-                    break;
-                case '\t':
-                    sb.append("\\t");
-                    break;
-                case '\n':
-                    sb.append("\\n");
-                    break;
-                case '\f':
-                    sb.append("\\f");
-                    break;
-                case '\r':
-                    sb.append("\\r");
-                    break;
-                case '"':
-                    sb.append("\\\"");
-                    break;
-                case '\'':
-                    sb.append("\\\'");
-                    break;
-                case '\\':
-                    sb.append("\\\\");
-                    break;
-                default:
-                    if (ch < 32 || ch >= 127) {
-                        sb.append(String.format("\\u%04X", (int)ch));
-                    } else {
-                        sb.append(ch);
-                    }
-                    break;
-            }
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Returns a content tree for the SCRIPT tag for the main page(index.html).
-     *
-     * @return a content for the SCRIPT tag
-     */
-    protected Content getFramesJavaScript() {
-        HtmlTree scriptTree = HtmlTree.SCRIPT();
-        String scriptCode = "\n" +
-                "    tmpTargetPage = \"\" + window.location.search;\n" +
-                "    if (tmpTargetPage != \"\" && tmpTargetPage != \"undefined\")\n" +
-                "        tmpTargetPage = tmpTargetPage.substring(1);\n" +
-                "    if (tmpTargetPage.indexOf(\":\") != -1 || (tmpTargetPage != \"\" && !validURL(tmpTargetPage)))\n" +
-                "        tmpTargetPage = \"undefined\";\n" +
-                "    targetPage = tmpTargetPage;\n" +
-                "    function validURL(url) {\n" +
-                "        try {\n" +
-                "            url = decodeURIComponent(url);\n" +
-                "        }\n" +
-                "        catch (error) {\n" +
-                "            return false;\n" +
-                "        }\n" +
-                "        var pos = url.indexOf(\".html\");\n" +
-                "        if (pos == -1 || pos != url.length - 5)\n" +
-                "            return false;\n" +
-                "        var allowNumber = false;\n" +
-                "        var allowSep = false;\n" +
-                "        var seenDot = false;\n" +
-                "        for (var i = 0; i < url.length - 5; i++) {\n" +
-                "            var ch = url.charAt(i);\n" +
-                "            if ('a' <= ch && ch <= 'z' ||\n" +
-                "                    'A' <= ch && ch <= 'Z' ||\n" +
-                "                    ch == '$' ||\n" +
-                "                    ch == '_' ||\n" +
-                "                    ch.charCodeAt(0) > 127) {\n" +
-                "                allowNumber = true;\n" +
-                "                allowSep = true;\n" +
-                "            } else if ('0' <= ch && ch <= '9'\n" +
-                "                    || ch == '-') {\n" +
-                "                if (!allowNumber)\n" +
-                "                     return false;\n" +
-                "            } else if (ch == '/' || ch == '.') {\n" +
-                "                if (!allowSep)\n" +
-                "                    return false;\n" +
-                "                allowNumber = false;\n" +
-                "                allowSep = false;\n" +
-                "                if (ch == '.')\n" +
-                "                     seenDot = true;\n" +
-                "                if (ch == '/' && seenDot)\n" +
-                "                     return false;\n" +
-                "            } else {\n" +
-                "                return false;\n" +
-                "            }\n" +
-                "        }\n" +
-                "        return true;\n" +
-                "    }\n" +
-                "    function loadFrames() {\n" +
-                "        if (targetPage != \"\" && targetPage != \"undefined\")\n" +
-                "             top.classFrame.location = top.targetPage;\n" +
-                "    }\n";
-        RawHtml scriptContent = new RawHtml(scriptCode.replace("\n", DocletConstants.NL));
-        scriptTree.addContent(scriptContent);
-        return scriptTree;
-    }
-
-    /**
-     * Returns an HtmlTree for the BODY tag.
-     *
-     * @param includeScript  set true if printing windowtitle script
-     * @param title title for the window
-     * @return an HtmlTree for the BODY tag
-     */
-    public HtmlTree getBody(boolean includeScript, String title) {
-        HtmlTree body = new HtmlTree(HtmlTag.BODY);
-        // Set window title string which is later printed
-        this.winTitle = title;
-        // Don't print windowtitle script for overview-frame, allclasses-frame
-        // and package-frame
-        if (includeScript) {
-            this.script = getWinTitleScript();
-            body.addContent(script);
-            Content noScript = HtmlTree.NOSCRIPT(
-                    HtmlTree.DIV(configuration.getContent("doclet.No_Script_Message")));
-            body.addContent(noScript);
-        }
-        return body;
-    }
-
-    /**
-     * Returns an HtmlTree for the TITLE tag.
-     *
-     * @return an HtmlTree for the TITLE tag
-     */
-    public HtmlTree getTitle() {
-        HtmlTree title = HtmlTree.TITLE(new StringContent(winTitle));
-        return title;
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Script.java	Thu Nov 16 15:06:17 2017 -0800
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2003, 2017, 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.
+ */
+
+package jdk.javadoc.internal.doclets.formats.html.markup;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import jdk.javadoc.internal.doclets.toolkit.Content;
+import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants;
+
+/**
+ * A builder for HTML script elements.
+ *
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class Script  {
+    private final StringBuilder sb;
+
+    /**
+     * Creates an empty script.
+     */
+    public Script() {
+        sb = new StringBuilder();
+    }
+
+    /**
+     * Creates a new script containing the specified code.
+     *
+     * @param code the code
+     */
+    public Script(String code) {
+        this();
+        append(code);
+    }
+
+    /**
+     * Appends the given code to the content.
+     *
+     * @param code the code
+     * @return this object
+     */
+    public Script append(CharSequence code) {
+        sb.append(code);
+        return this;
+    }
+
+    /**
+     * Appends the given text as a string constant to the content.
+     * Characters within the string will be escaped as needed.
+     *
+     * @param text the text
+     * @return this object
+     */
+    public Script appendStringLiteral(CharSequence text) {
+        sb.append(stringLiteral(text, '"'));
+        return this;
+    }
+
+    /**
+     * Appends the given text as a string constant to the content.
+     * Characters within the string will be escaped as needed.
+     *
+     * @param text the text
+     * @param quoteChar the quote character to use
+     * @return this object
+     */
+    // The ability to specify the quote character is for backwards
+    // compatibility. Ideally, we should simplify the code so that
+    // the same quote character is always used.
+    public Script appendStringLiteral(CharSequence text, char quoteChar) {
+        sb.append(stringLiteral(text, quoteChar));
+        return this;
+    }
+
+    public Content asContent() {
+        ScriptContent scriptContent = new ScriptContent(sb);
+        HtmlTree tree = new HtmlTree(HtmlTag.SCRIPT) {
+            @Override
+            public void addContent(CharSequence s) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            public void addContent(Content c) {
+                if (c != scriptContent) {
+                    throw new IllegalArgumentException();
+                }
+                super.addContent(scriptContent);
+            }
+        };
+        tree.addAttr(HtmlAttr.TYPE, "text/javascript");
+        tree.addContent(scriptContent);
+        return tree;
+    }
+
+    /**
+     * Returns a String with escaped special JavaScript characters.
+     *
+     * @param s String that needs to be escaped
+     * @return a valid escaped JavaScript string
+     */
+    public static String stringLiteral(CharSequence s) {
+        return stringLiteral(s, '"');
+    }
+
+    /**
+     * Returns a String with escaped special JavaScript characters.
+     *
+     * @param s String that needs to be escaped
+     * @param quoteChar the quote character to use for the literal
+     * @return a valid escaped JavaScript string
+     */
+    // The ability to specify the quote character is for backwards
+    // compatibility. Ideally, we should simplify the code so that
+    // the same quote character is always used.
+    public static String stringLiteral(CharSequence s, char quoteChar) {
+        if (quoteChar != '"' && quoteChar != '\'') {
+            throw new IllegalArgumentException();
+        }
+        StringBuilder sb = new StringBuilder();
+        sb.append(quoteChar);
+        for (int i = 0; i < s.length(); i++) {
+            char ch = s.charAt(i);
+            switch (ch) {
+                case '\b':
+                    sb.append("\\b");
+                    break;
+                case '\t':
+                    sb.append("\\t");
+                    break;
+                case '\n':
+                    sb.append("\\n");
+                    break;
+                case '\f':
+                    sb.append("\\f");
+                    break;
+                case '\r':
+                    sb.append("\\r");
+                    break;
+                case '"':
+                    sb.append("\\\"");
+                    break;
+                case '\'':
+                    sb.append("\\\'");
+                    break;
+                case '\\':
+                    sb.append("\\\\");
+                    break;
+                default:
+                    if (ch < 32 || ch >= 127) {
+                        sb.append(String.format("\\u%04X", (int)ch));
+                    } else {
+                        sb.append(ch);
+                    }
+                    break;
+            }
+        }
+        sb.append(quoteChar);
+        return sb.toString();
+    }
+
+    private static class ScriptContent extends Content {
+        private final StringBuilder sb;
+
+        ScriptContent(StringBuilder sb) {
+            this.sb = sb;
+        }
+
+        @Override
+        public void addContent(Content content) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void addContent(CharSequence code) {
+            sb.append(code);
+        }
+
+        @Override
+        public boolean write(Writer writer, boolean atNewline) throws IOException {
+            String s = sb.toString();
+            writer.write(s.replace("\n", DocletConstants.NL));
+            return s.endsWith("\n");
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return false;
+        }
+    }
+}
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Table.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/Table.java	Thu Nov 16 15:06:17 2017 -0800
@@ -570,7 +570,12 @@
     }
 
     private void appendTabInfo(StringBuilder sb, int value, String id, String name) {
-        sb.append(value).append(":[\"").append(id).append("\",").append("\"").append(name).append("\"]");
+        sb.append(value)
+                .append(":[")
+                .append(Script.stringLiteral(id))
+                .append(",")
+                .append(Script.stringLiteral(name))
+                .append("]");
     }
 
     private void appendStyleInfo(StringBuilder sb, HtmlStyle... styles) {
--- a/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Content.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Content.java	Thu Nov 16 15:06:17 2017 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2017, 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
@@ -76,6 +76,10 @@
     /**
      * Writes content to a writer.
      *
+     * @param writer the writer
+     * @param atNewline whether the writer has just written a newline
+     * @return  whether the writer has just written a newline
+     * @throws IOException if an error occurs while writing the output
      */
     public abstract boolean write(Writer writer, boolean atNewline) throws IOException ;
 
@@ -107,6 +111,7 @@
     /**
      * Checks for null values.
      *
+     * @param <T> the type of the item being checked
      * @param t reference type to check for null values
      * @return the reference type if not null or else throws a null pointer exception
      */
--- a/test/langtools/jdk/javadoc/doclet/testGroupName/TestGroupName.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/test/langtools/jdk/javadoc/doclet/testGroupName/TestGroupName.java	Thu Nov 16 15:06:17 2017 -0800
@@ -64,10 +64,8 @@
         checkExit(Exit.OK);
 
         checkOutput("overview-summary.html", true,
-                "<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>");
-
-        checkOutput("overview-summary.html", false,
-                "abc < & > def");
+                "<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>",
+                ",\"abc < & > def\"],");
     }
 
     @Test
@@ -99,10 +97,8 @@
         checkExit(Exit.OK);
 
         checkOutput("overview-summary.html", true,
-                "<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>");
-
-        checkOutput("overview-summary.html", false,
-                "abc < & > def");
+                "<span><a href=\"javascript:showGroups(1);\">abc &lt; &amp; &gt; def</a></span>",
+                ",\"abc < & > def\"],");
     }
 }
 
--- a/test/langtools/jdk/javadoc/doclet/testModules/TestModules.java	Thu Nov 16 15:01:45 2017 -0800
+++ b/test/langtools/jdk/javadoc/doclet/testModules/TestModules.java	Thu Nov 16 15:06:17 2017 -0800
@@ -1100,7 +1100,7 @@
                 + "&nbsp;</span></span><span id=\"t4\" class=\"tableTab\"><span><a href=\"javascript:showGroups(4);\">"
                 + "Other Modules</a></span><span class=\"tabEnd\">&nbsp;</span></span></caption>",
                 "var groups = {\"i0\":1,\"i1\":2,\"i2\":2,\"i3\":4};\n"
-                + "var tabs = {65535:[\"t0\",\"All Modules\"],1:[\"t1\",\"Module Group A\"],2:[\"t2\",\"Module Group B &amp; C\"],4:[\"t4\",\"Other Modules\"]};\n"
+                + "var tabs = {65535:[\"t0\",\"All Modules\"],1:[\"t1\",\"Module Group A\"],2:[\"t2\",\"Module Group B & C\"],4:[\"t4\",\"Other Modules\"]};\n"
                 + "var altColor = \"altColor\";\n"
                 + "var rowColor = \"rowColor\";\n"
                 + "var tableTab = \"tableTab\";\n"