8162353: javadoc should provide a way to disable use of frames
authorjjg
Mon, 15 Aug 2016 18:00:36 -0700
changeset 40500 f293dbb81a53
parent 40499 ac4c100be583
child 40501 8455b59aa04a
8162353: javadoc should provide a way to disable use of frames Reviewed-by: bpatel, ksrini
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesFrameWriter.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConfigurationImpl.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HelpWriter.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexRedirectWriter.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexWriter.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java
langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java
langtools/test/jdk/javadoc/doclet/testFramesNoFrames/TestFramesNoFrames.java
langtools/test/tools/lib/toolbox/ModuleBuilder.java
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesFrameWriter.java	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/AllClassesFrameWriter.java	Mon Aug 15 18:00:36 2016 -0700
@@ -25,7 +25,7 @@
 
 package jdk.javadoc.internal.doclets.formats.html;
 
-import java.io.*;
+import java.io.IOException;
 
 import javax.lang.model.element.Element;
 import javax.lang.model.element.TypeElement;
@@ -89,25 +89,32 @@
      * "allclasses-frame.html" file. Generate the file in the current or the
      * destination directory.
      *
-     * @param indexbuilder IndexBuilder object for all classes index.
+     * @param indexBuilder IndexBuilder object for all classes index.
      * @throws DocletAbortException
      */
     public static void generate(ConfigurationImpl configuration,
-                                IndexBuilder indexbuilder) {
-        AllClassesFrameWriter allclassgen;
-        DocPath filename = DocPaths.ALLCLASSES_FRAME;
+            IndexBuilder indexBuilder) {
+        if (configuration.frames) {
+            generate(configuration, indexBuilder, DocPaths.ALLCLASSES_FRAME, true);
+            generate(configuration, indexBuilder, DocPaths.ALLCLASSES_NOFRAME, false);
+        } else {
+            generate(configuration, indexBuilder, DocPaths.ALLCLASSES, false);
+        }
+    }
+
+    private static void generate(ConfigurationImpl configuration, IndexBuilder indexBuilder,
+            DocPath fileName, boolean wantFrames) {
         try {
+            AllClassesFrameWriter allclassgen = new AllClassesFrameWriter(configuration,
+                    fileName, indexBuilder);
+            allclassgen.buildAllClassesFile(wantFrames);
             allclassgen = new AllClassesFrameWriter(configuration,
-                                                    filename, indexbuilder);
-            allclassgen.buildAllClassesFile(true);
-            filename = DocPaths.ALLCLASSES_NOFRAME;
-            allclassgen = new AllClassesFrameWriter(configuration,
-                                                    filename, indexbuilder);
+                                                    fileName, indexBuilder);
             allclassgen.buildAllClassesFile(false);
         } catch (IOException exc) {
             Messages messages = configuration.getMessages();
             messages.error("doclet.exception_encountered",
-                           exc.toString(), filename);
+                           exc.toString(), fileName);
             throw new DocletAbortException(exc);
         }
     }
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConfigurationImpl.java	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ConfigurationImpl.java	Mon Aug 15 18:00:36 2016 -0700
@@ -198,6 +198,12 @@
     public boolean createoverview = false;
 
     /**
+     * Specifies whether or not frames should be generated.
+     * Defaults to true; can be set by --frames; can be set to false by --no-frames; last one wins.
+     */
+    public boolean frames = true;
+
+    /**
      * This is the HTML version of the generated pages. HTML 4.01 is the default output version.
      */
     public HtmlVersion htmlVersion = HtmlVersion.HTML4;
@@ -414,18 +420,18 @@
      * package to document. It will be a class page(first in the sorted order),
      * if only classes are provided on the command line.
      *
-     * @param root Root of the program structure.
+     * @param docEnv Root of the program structure.
      */
-    protected void setTopFile(DocletEnvironment root) {
-        if (!checkForDeprecation(root)) {
+    protected void setTopFile(DocletEnvironment docEnv) {
+        if (!checkForDeprecation(docEnv)) {
             return;
         }
         if (createoverview) {
-            topFile = DocPaths.OVERVIEW_SUMMARY;
+            topFile = DocPaths.overviewSummary(frames);
         } else {
             if (packages.size() == 1 && packages.first().isUnnamed()) {
-                if (!root.getIncludedClasses().isEmpty()) {
-                    List<TypeElement> classes = new ArrayList<>(root.getIncludedClasses());
+                if (!docEnv.getIncludedClasses().isEmpty()) {
+                    List<TypeElement> classes = new ArrayList<>(docEnv.getIncludedClasses());
                     TypeElement te = getValidClass(classes);
                     topFile = DocPath.forClass(utils, te);
                 }
@@ -727,6 +733,22 @@
                     return true;
                 }
             },
+            new Option(this, "--frames") {
+                @Override
+                public boolean process(String opt,  ListIterator<String> args) {
+                    optionsProcessed.add(this);
+                    frames = true;
+                    return true;
+                }
+            },
+            new Option(this, "--no-frames") {
+                @Override
+                public boolean process(String opt,  ListIterator<String> args) {
+                    optionsProcessed.add(this);
+                    frames = false;
+                    return true;
+                }
+            },
             new Hidden(this, "-packagesheader", 1) {
                 @Override
                 public boolean process(String opt,  ListIterator<String> args) {
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HelpWriter.java	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HelpWriter.java	Mon Aug 15 18:00:36 2016 -0700
@@ -141,7 +141,7 @@
                     ? HtmlTree.SECTION(overviewHeading)
                     : HtmlTree.LI(HtmlStyle.blockList, overviewHeading);
             Content line3 = contents.getContent("doclet.Help_line_3",
-                    getHyperLink(DocPaths.OVERVIEW_SUMMARY,
+                    getHyperLink(DocPaths.overviewSummary(configuration.frames),
                     configuration.getText("doclet.Overview")));
             Content overviewPara = HtmlTree.P(line3);
             htmlTree.addContent(overviewPara);
@@ -362,26 +362,31 @@
         } else {
             ul.addContent(htmlTree);
         }
-        Content frameHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING,
-                contents.getContent("doclet.Help_line_25"));
-        htmlTree = (configuration.allowTag(HtmlTag.SECTION))
-                ? HtmlTree.SECTION(frameHead)
-                : HtmlTree.LI(HtmlStyle.blockList, frameHead);
-        Content line26 = contents.getContent("doclet.Help_line_26");
-        Content framePara = HtmlTree.P(line26);
-        htmlTree.addContent(framePara);
+
+        if (configuration.frames) {
+            Content frameHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING,
+                    contents.getContent("doclet.Help_line_25"));
+            htmlTree = (configuration.allowTag(HtmlTag.SECTION))
+                    ? HtmlTree.SECTION(frameHead)
+                    : HtmlTree.LI(HtmlStyle.blockList, frameHead);
+            Content line26 = contents.getContent("doclet.Help_line_26");
+            Content framePara = HtmlTree.P(line26);
+            htmlTree.addContent(framePara);
+        }
+
         if (configuration.allowTag(HtmlTag.SECTION)) {
             ul.addContent(HtmlTree.LI(HtmlStyle.blockList, htmlTree));
         } else {
             ul.addContent(htmlTree);
         }
+
         Content allclassesHead = HtmlTree.HEADING(HtmlConstants.CONTENT_HEADING,
                 contents.allClassesLabel);
         htmlTree = (configuration.allowTag(HtmlTag.SECTION))
                 ? HtmlTree.SECTION(allclassesHead)
                 : HtmlTree.LI(HtmlStyle.blockList, allclassesHead);
         Content line27 = contents.getContent("doclet.Help_line_27",
-                getHyperLink(DocPaths.ALLCLASSES_NOFRAME,
+                getHyperLink(DocPaths.AllClasses(configuration.frames),
                 resources.getText("doclet.All_Classes")));
         Content allclassesPara = HtmlTree.P(line27);
         htmlTree.addContent(allclassesPara);
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDoclet.java	Mon Aug 15 18:00:36 2016 -0700
@@ -106,7 +106,7 @@
      *
      * For new format.
      *
-     * @see jdk.doclet.RootDoc
+     * @see jdk.doclet.DocletEnvironment
      */
     @Override // defined by AbstractDoclet
     protected void generateOtherFiles(DocletEnvironment docEnv, ClassTree classtree)
@@ -149,7 +149,9 @@
         AllClassesFrameWriter.generate(configuration,
             new IndexBuilder(configuration, nodeprecated, true));
 
-        FrameOutputWriter.generate(configuration);
+        if (configuration.frames) {
+            FrameOutputWriter.generate(configuration);
+        }
 
         if (configuration.createoverview) {
             if (configuration.showModules) {
@@ -158,6 +160,11 @@
                 PackageIndexWriter.generate(configuration);
             }
         }
+
+        if (!configuration.frames && !configuration.createoverview) {
+            IndexRedirectWriter.generate(configuration);
+        }
+
         if (configuration.helpfile.length() == 0 &&
             !configuration.nohelp) {
             HelpWriter.generate(configuration);
@@ -270,13 +277,17 @@
     @Override // defined by AbstractDoclet
     protected void generateModuleFiles() throws Exception {
         if (configuration.showModules) {
-            ModuleIndexFrameWriter.generate(configuration);
+            if (configuration.frames) {
+                ModuleIndexFrameWriter.generate(configuration);
+            }
             ModuleElement prevModule = null, nextModule;
             List<ModuleElement> mdles = new ArrayList<>(configuration.modulePackages.keySet());
             int i = 0;
             for (ModuleElement mdle : mdles) {
-                ModulePackageIndexFrameWriter.generate(configuration, mdle);
-                ModuleFrameWriter.generate(configuration, mdle);
+                if (configuration.frames) {
+                    ModulePackageIndexFrameWriter.generate(configuration, mdle);
+                    ModuleFrameWriter.generate(configuration, mdle);
+                }
                 nextModule = (i + 1 < mdles.size()) ? mdles.get(i + 1) : null;
                 AbstractBuilder moduleSummaryBuilder =
                         configuration.getBuilderFactory().getModuleSummaryBuilder(
@@ -304,7 +315,7 @@
     @Override // defined by AbstractDoclet
     protected void generatePackageFiles(ClassTree classtree) throws Exception {
         Set<PackageElement> packages = configuration.packages;
-        if (packages.size() > 1) {
+        if (packages.size() > 1 && configuration.frames) {
             PackageIndexFrameWriter.generate(configuration);
         }
         List<PackageElement> pList = new ArrayList<>(packages);
@@ -315,7 +326,9 @@
             // and package-tree.html pages for that package.
             PackageElement pkg = pList.get(i);
             if (!(configuration.nodeprecated && utils.isDeprecated(pkg))) {
-                PackageFrameWriter.generate(configuration, pkg);
+                if (configuration.frames) {
+                    PackageFrameWriter.generate(configuration, pkg);
+                }
                 int nexti = i + 1;
                 PackageElement next = null;
                 if (nexti < pList.size()) {
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/HtmlDocletWriter.java	Mon Aug 15 18:00:36 2016 -0700
@@ -615,12 +615,13 @@
             } else {
                 tree.addContent(navDiv);
             }
-            Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious());
-            ulNav.addContent(getNavLinkNext());
+            Content ulNav = HtmlTree.UL(HtmlStyle.navList, getNavLinkPrevious(), getNavLinkNext());
             Content subDiv = HtmlTree.DIV(HtmlStyle.subNav, ulNav);
-            Content ulFrames = HtmlTree.UL(HtmlStyle.navList, getNavShowLists());
-            ulFrames.addContent(getNavHideLists(filename));
-            subDiv.addContent(ulFrames);
+            if (configuration.frames) {
+                Content ulFrames = HtmlTree.UL(HtmlStyle.navList,
+                    getNavShowLists(), getNavHideLists(filename));
+                subDiv.addContent(ulFrames);
+            }
             HtmlTree ulAllClasses = HtmlTree.UL(HtmlStyle.navList, getNavLinkClassIndex());
             ulAllClasses.addAttr(HtmlAttr.ID, allClassesId);
             subDiv.addContent(ulAllClasses);
@@ -688,7 +689,7 @@
      * @return a content tree for the link
      */
     protected Content getNavLinkContents() {
-        Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.OVERVIEW_SUMMARY),
+        Content linkContent = getHyperLink(pathToRoot.resolve(DocPaths.overviewSummary(configuration.frames)),
                 contents.overviewLabel, "", "");
         Content li = HtmlTree.LI(linkContent);
         return li;
@@ -875,7 +876,7 @@
      */
     protected Content getNavLinkClassIndex() {
         Content allClassesContent = getHyperLink(pathToRoot.resolve(
-                DocPaths.ALLCLASSES_NOFRAME),
+                DocPaths.AllClasses(configuration.frames)),
                 contents.allClassesLabel, "", "");
         Content li = HtmlTree.LI(allClassesContent);
         return li;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/IndexRedirectWriter.java	Mon Aug 15 18:00:36 2016 -0700
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.javadoc.internal.doclets.formats.html;
+
+import java.io.IOException;
+
+import jdk.javadoc.internal.doclets.formats.html.markup.Comment;
+import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
+import jdk.javadoc.internal.doclets.formats.html.markup.DocType;
+import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr;
+import jdk.javadoc.internal.doclets.formats.html.markup.HtmlConstants;
+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.StringContent;
+import jdk.javadoc.internal.doclets.toolkit.Content;
+import jdk.javadoc.internal.doclets.toolkit.Messages;
+import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
+import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
+import jdk.javadoc.internal.doclets.toolkit.util.DocletAbortException;
+
+import static jdk.javadoc.internal.doclets.formats.html.markup.HtmlDocWriter.CONTENT_TYPE;
+
+/**
+ * Writes an index.html file that tries to redirect to an alternate page.
+ * The redirect uses JavaSCript, if enabled, falling back on
+ * {@code <meta http-eqiv=refresh content="0,<uri>">}.
+ * If neither are supported/enabled in a browser, the page displays the
+ * standard "JavaScipt not enabled" message, and a link to the alternate page.
+ */
+public class IndexRedirectWriter extends HtmlDocletWriter {
+
+    public static void generate(ConfigurationImpl configuration) {
+        IndexRedirectWriter indexRedirect;
+        DocPath filename = DocPath.empty;
+        try {
+            filename = DocPaths.INDEX;
+            indexRedirect = new IndexRedirectWriter(configuration, filename);
+            indexRedirect.generateIndexFile();
+        } catch (IOException exc) {
+            Messages messages = configuration.getMessages();
+            messages.error(
+                    "doclet.exception_encountered",
+                    exc.toString(), filename);
+            throw new DocletAbortException(exc);
+        }
+    }
+
+    IndexRedirectWriter(ConfigurationImpl configuration, DocPath filename)
+            throws IOException {
+        super(configuration, filename);
+    }
+
+    void generateIndexFile() throws IOException {
+        Content htmlDocType = configuration.isOutputHtml5()
+                ? DocType.HTML5
+                : DocType.TRANSITIONAL;
+        Content htmlComment = new Comment(configuration.getText("doclet.New_Page"));
+        Content head = new HtmlTree(HtmlTag.HEAD);
+        head.addContent(getGeneratedBy(!configuration.notimestamp));
+
+        String title = (configuration.windowtitle.length() > 0)
+                ? configuration.windowtitle
+                : configuration.getText("doclet.Generated_Docs_Untitled");
+
+        Content windowTitle = HtmlTree.TITLE(new StringContent(title));
+        head.addContent(windowTitle);
+        Content metaContentType = HtmlTree.META("Content", CONTENT_TYPE,
+                (configuration.charset.length() > 0) ?
+                        configuration.charset : HtmlConstants.HTML_DEFAULT_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);
+        HtmlTree metaRefresh = new HtmlTree(HtmlTag.META);
+        metaRefresh.addAttr(HtmlAttr.HTTP_EQUIV, "Refresh");
+        metaRefresh.addAttr(HtmlAttr.CONTENT, "0;" + topFilePath);
+        if (configuration.isOutputHtml5()) {
+            head.addContent(HtmlTree.NOSCRIPT(metaRefresh));
+        } else {
+            head.addContent(metaRefresh);
+        }
+
+        head.addContent(getStyleSheetProperties(configuration));
+
+        ContentBuilder bodyContent = new ContentBuilder();
+        bodyContent.addContent(HtmlTree.NOSCRIPT(
+                HtmlTree.P(configuration.getContent("doclet.No_Script_Message"))));
+
+        bodyContent.addContent(HtmlTree.P(HtmlTree.A(topFilePath, new StringContent(topFilePath))));
+
+        Content body = new HtmlTree(HtmlTag.BODY);
+        if (configuration.allowTag(HtmlTag.MAIN)) {
+            HtmlTree main = HtmlTree.MAIN(bodyContent);
+            body.addContent(main);
+        } else {
+            body.addContent(bodyContent);
+        }
+
+        Content htmlTree = HtmlTree.HTML(configuration.getLocale().getLanguage(),
+                head, body);
+        Content htmlDocument = new HtmlDocument(htmlDocType,
+                htmlComment, htmlTree);
+        write(htmlDocument);
+
+    }
+}
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexWriter.java	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/ModuleIndexWriter.java	Mon Aug 15 18:00:36 2016 -0700
@@ -88,7 +88,7 @@
      */
     public static void generate(ConfigurationImpl configuration) {
         ModuleIndexWriter mdlgen;
-        DocPath filename = DocPaths.OVERVIEW_SUMMARY;
+        DocPath filename = DocPaths.overviewSummary(configuration.frames);
         try {
             mdlgen = new ModuleIndexWriter(configuration, filename);
             mdlgen.buildModuleIndexFile("doclet.Window_Overview_Summary", true);
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/PackageIndexWriter.java	Mon Aug 15 18:00:36 2016 -0700
@@ -101,7 +101,7 @@
      */
     public static void generate(ConfigurationImpl configuration) {
         PackageIndexWriter packgen;
-        DocPath filename = DocPaths.OVERVIEW_SUMMARY;
+        DocPath filename = DocPaths.overviewSummary(configuration.frames);
         try {
             packgen = new PackageIndexWriter(configuration, filename);
             packgen.buildPackageIndexFile("doclet.Window_Overview_Summary", true);
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/markup/HtmlTree.java	Mon Aug 15 18:00:36 2016 -0700
@@ -833,13 +833,18 @@
      * Generates a UL tag with the style class attribute and some content.
      *
      * @param styleClass style for the tag
-     * @param body content for the tag
+     * @param first initial content to be added
+     * @param more a series of additional content nodes to be added
      * @return an HtmlTree object for the UL tag
      */
-    public static HtmlTree UL(HtmlStyle styleClass, Content body) {
-        HtmlTree htmltree = new HtmlTree(HtmlTag.UL, nullCheck(body));
-        htmltree.addStyle(nullCheck(styleClass));
-        return htmltree;
+    public static HtmlTree UL(HtmlStyle styleClass, Content first, Content... more) {
+        HtmlTree htmlTree = new HtmlTree(HtmlTag.UL);
+        htmlTree.addContent(nullCheck(first));
+        for (Content c : more) {
+            htmlTree.addContent(nullCheck(c));
+        }
+        htmlTree.addStyle(nullCheck(styleClass));
+        return htmlTree;
     }
 
     /**
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/formats/html/resources/standard.properties	Mon Aug 15 18:00:36 2016 -0700
@@ -299,6 +299,10 @@
 doclet.usage.docencoding.parameters=<name>
 doclet.usage.docencoding.description=Specify the character encoding for the output
 
+doclet.usage.frames.description=Enable the use of frames in the generated output (default)
+
+doclet.usage.no-frames.description=Disable the use of frames in the generated output
+
 doclet.xusage.xdocrootparent.parameters=<url>
 doclet.xusage.xdocrootparent.description=Replaces all @docRoot followed by /..\n\
 \                                   in doc comments with <url>
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/util/DocPaths.java	Mon Aug 15 18:00:36 2016 -0700
@@ -37,6 +37,8 @@
  *
  */
 public class DocPaths {
+    /** The name of the file for all classes, without using frames, when --no-frames is specified. */
+    public static final DocPath ALLCLASSES = DocPath.create("allclasses.html");
 
     /** The name of the file for all classes, using frames. */
     public static final DocPath ALLCLASSES_FRAME = DocPath.create("allclasses-frame.html");
@@ -44,6 +46,10 @@
     /** The name of the file for all classes, without using frames. */
     public static final DocPath ALLCLASSES_NOFRAME = DocPath.create("allclasses-noframe.html");
 
+    public static DocPath AllClasses(boolean frames) {
+        return frames ? ALLCLASSES_NOFRAME : ALLCLASSES;
+    }
+
     /** The name of the sub-directory for storing class usage info. */
     public static final DocPath CLASS_USE = DocPath.create("class-use");
 
@@ -121,6 +127,10 @@
     /** The name of the file for the overview summary. */
     public static final DocPath OVERVIEW_SUMMARY = DocPath.create("overview-summary.html");
 
+    public static DocPath overviewSummary(boolean frames) {
+        return frames ? OVERVIEW_SUMMARY : INDEX;
+    }
+
     /** The name of the file for the overview tree. */
     public static final DocPath OVERVIEW_TREE = DocPath.create("overview-tree.html");
 
--- a/langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java	Mon Aug 15 18:00:36 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -40,6 +40,7 @@
 import java.nio.file.Files;
 import java.util.Arrays;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.EnumMap;
 import java.util.HashMap;
 import java.util.List;
@@ -157,7 +158,7 @@
     private final Map<File,SoftReference<String>> fileContentCache = new HashMap<>();
 
     /** Stream used for logging messages. */
-    private final PrintStream out = System.out;
+    protected final PrintStream out = System.out;
 
     /** The directory containing the source code for the test. */
     public static final String testSrc = System.getProperty("test.src");
@@ -416,6 +417,17 @@
      * @param paths the files to check, within the most recent output directory.
      * */
     public void checkFiles(boolean expectedFound, String... paths) {
+        checkFiles(expectedFound, Arrays.asList(paths));
+    }
+
+    /**
+     * Check for files in (or not in) the generated output.
+     * @param expectedFound true if all of the files are expected
+     *  to be found, or false if all of the files are expected to be
+     *  not found
+     * @param paths the files to check, within the most recent output directory.
+     * */
+    public void checkFiles(boolean expectedFound, Collection<String> paths) {
         for (String path: paths) {
 //            log.logCheckFile(path, expectedFound);
             checking("checkFile");
@@ -574,7 +586,7 @@
                 return content;
 
             content = new String(Files.readAllBytes(file.toPath()));
-            fileContentCache.put(file, new SoftReference(content));
+            fileContentCache.put(file, new SoftReference<>(content));
             return content;
         } catch (FileNotFoundException e) {
             System.err.println(e);
@@ -613,16 +625,19 @@
      * Print a summary of the test results.
      */
     protected void printSummary() {
-//        log.write();
+        String javadocRuns = (javadocRunNum <= 1) ? ""
+                : ", in " + javadocRunNum + " runs of javadoc";
+
         if (numTestsRun != 0 && numTestsPassed == numTestsRun) {
             // Test passed
             out.println();
-            out.println("All " + numTestsPassed + " subtests passed");
+            out.println("All " + numTestsPassed + " subtests passed" + javadocRuns);
         } else {
             // Test failed
             throw new Error((numTestsRun - numTestsPassed)
                     + " of " + (numTestsRun)
-                    + " subtests failed");
+                    + " subtests failed"
+                    + javadocRuns);
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/javadoc/doclet/testFramesNoFrames/TestFramesNoFrames.java	Mon Aug 15 18:00:36 2016 -0700
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8162353
+ * @summary javadoc should provide a way to disable use of frames
+ * @library /tools/lib ../lib
+ * @modules
+ *      jdk.compiler/com.sun.tools.javac.api
+ *      jdk.compiler/com.sun.tools.javac.main
+ *      jdk.javadoc/jdk.javadoc.internal.tool
+ * @build toolbox.ModuleBuilder toolbox.ToolBox
+ * @build JavadocTester
+ * @run main TestFramesNoFrames
+ */
+
+import java.io.*;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.nio.file.*;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import toolbox.ModuleBuilder;
+import toolbox.ToolBox;
+
+public class TestFramesNoFrames extends JavadocTester {
+
+    public static void main(String... args) throws Exception {
+        TestFramesNoFrames tester = new TestFramesNoFrames();
+        tester.generateSource();
+        tester.runTests();
+    }
+
+    ToolBox tb = new ToolBox();
+    Path gensrcModules = Paths.get("gensrc/modules");
+    Path gensrcPackages = Paths.get("gensrc/packages");
+
+    void generateSource() throws IOException {
+        String[] modules = { "", "m1", "m2", "m3" };
+        String[] packages = { "p1", "p2", "p3" };
+        String[] classes = { "C1", "C2", "C3" };
+        for (String m: modules) {
+            ModuleBuilder mb = m.equals("") ? null : new ModuleBuilder(tb, m);
+            for (String p: packages) {
+                Path pkgRoot;
+                if (m.equals("")) {
+                    pkgRoot = gensrcPackages;
+                } else {
+                    pkgRoot = gensrcModules.resolve(m);
+                    mb.exports(m + p);
+                }
+                for (String c: classes) {
+                    tb.writeJavaFiles(pkgRoot,
+                        "package " + (m + p) + ";\n"
+                        + "/** class " + (m + p + c).toUpperCase() + ". */\n"
+                        + "public class " + (m + p + c).toUpperCase() + " { }"
+                    );
+                }
+            }
+            if (!m.equals("")) {
+                mb.write(gensrcModules);
+            }
+        }
+        tb.writeFile("overview.html",
+                "<html><body>This is the overview file</body></html>");
+    }
+
+    enum FrameKind {
+        DEFAULT(),
+        FRAMES("--frames"),
+        NO_FRAMES("--no-frames");
+        FrameKind(String... opts) {
+            this.opts = Arrays.asList(opts);
+        }
+        final List<String> opts;
+    }
+
+    enum OverviewKind {
+        DEFAULT(),
+        OVERVIEW("-overview", "overview.html"),
+        NO_OVERVIEW("-nooverview");
+        OverviewKind(String... opts) {
+            this.opts = Arrays.asList(opts);
+        }
+        final List<String> opts;
+    }
+
+    enum HtmlKind {
+        HTML4("-html4"),
+        HTML5("-html5");
+        HtmlKind(String... opts) {
+            this.opts = Arrays.asList(opts);
+        }
+        final List<String> opts;
+    }
+
+    @Override
+    public void runTests() throws Exception {
+        for (Method m : getClass().getDeclaredMethods()) {
+            Annotation a = m.getAnnotation(Test.class);
+            if (a != null) {
+                for (FrameKind fk : FrameKind.values()) {
+                    for (OverviewKind ok : OverviewKind.values()) {
+                        for (HtmlKind hk : HtmlKind.values()) {
+                            try {
+                                out.println("Running test " + m.getName() + " " + fk + " " + ok + " " + hk);
+                                Path base = Paths.get(m.getName() + "_" + fk + "_" + ok + "_" + hk);
+                                Files.createDirectories(base);
+                                m.invoke(this, new Object[]{base, fk, ok, hk});
+                            } catch (InvocationTargetException e) {
+                                Throwable cause = e.getCause();
+                                throw (cause instanceof Exception) ? ((Exception) cause) : e;
+                            }
+                            out.println();
+                        }
+                    }
+                }
+            }
+        }
+        printSummary();
+    }
+
+    void javadoc(Path outDir, FrameKind fKind, OverviewKind oKind, HtmlKind hKind, String... rest) {
+        List<String> args = new ArrayList<>();
+        args.add("-d");
+        args.add(outDir.toString());
+        args.addAll(fKind.opts);
+        args.addAll(oKind.opts);
+        args.addAll(hKind.opts);
+        args.addAll(Arrays.asList(rest));
+        javadoc(args.toArray(new String[0]));
+        checkExit(Exit.OK);
+    }
+
+    @Test
+    void testClass(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws Exception {
+        javadoc(base, fKind, oKind, hKind,
+            gensrcPackages.resolve("p1/P1C1.java").toString());
+
+        new Checker(fKind, oKind, hKind)
+            .classes("p1.P1C1")
+            .check();
+    }
+
+    @Test
+    void testClasses(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
+        javadoc(base, fKind, oKind, hKind,
+            gensrcPackages.resolve("p1/P1C1.java").toString(),
+            gensrcPackages.resolve("p1/P1C2.java").toString(),
+            gensrcPackages.resolve("p1/P1C3.java").toString());
+
+        new Checker(fKind, oKind, hKind)
+            .classes("p1.P1C1", "p1.P1C2", "p1.P1C3")
+            .check();
+    }
+
+    @Test
+    void testPackage(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
+        javadoc(base, fKind, oKind, hKind,
+            "-sourcepath", gensrcPackages.toString(),
+            "p1");
+
+        new Checker(fKind, oKind, hKind)
+            .classes("p1.P1C1", "p1.P1C2", "p1.P1C3")
+            .check();
+    }
+
+    @Test
+    void testPackages(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
+        javadoc(base, fKind, oKind, hKind,
+            "-sourcepath", gensrcPackages.toString(),
+            "p1", "p2", "p3");
+
+        new Checker(fKind, oKind, hKind)
+            .classes("p1.P1C1", "p1.P1C2", "p1.P1C3",
+                    "p2.P2C1", "p2.P2C2", "p2.P2C3",
+                    "p3.P3C1", "p3.P3C2", "p3.P3C3")
+            .check();
+    }
+
+    @Test
+    void testModules(Path base, FrameKind fKind, OverviewKind oKind, HtmlKind hKind) throws IOException {
+        javadoc(base, fKind, oKind, hKind,
+            "-modulesourcepath", gensrcModules.toString(),
+            "-addmods", "m1,m2,m3", // TEMPORARY, SHOULD NOT BE NECESSARY
+            "m1p1", "m1p2", "m1p3",
+            "m2p1", "m2p2", "m2p3",
+            "m3p1", "m3p2", "m3p3");
+
+        new Checker(fKind, oKind, hKind)
+            .classes("m1/m1p1.M1P1C1", "m1/m1p1.M1P1C2", "m1/m1p1.M1P1C3",
+                    "m2/m2p1.M2P1C1", "m2/m2p1.M2P1C2", "m2/m2p1.M2P1C3",
+                    "m3/m3p1.M3P1C1", "m3/m3p1.M3P1C2", "m3/m3p1.M3P1C3")
+            .check();
+    }
+
+    /**
+     * Check the contents of the generated output, according to the
+     * specified options.
+     */
+    class Checker {
+        private final FrameKind fKind;
+        private final OverviewKind oKind;
+        private final HtmlKind hKind;
+        List<String> classes;
+
+        private boolean frames;
+        private boolean overview;
+
+        Checker(FrameKind fKind, OverviewKind oKind, HtmlKind hKind) {
+            this.fKind = fKind;
+            this.oKind = oKind;
+            this.hKind = hKind;
+        }
+
+        Checker classes(String... classes) {
+            this.classes = Arrays.asList(classes);
+            return this;
+        }
+
+        void check() throws IOException {
+            switch (fKind) {
+                case DEFAULT:
+                case FRAMES:
+                    frames = true;
+                    break;
+
+                case NO_FRAMES:
+                    frames = false;
+                    break;
+            }
+
+            switch (oKind) {
+                case DEFAULT:
+                    overview = (getPackageCount() > 1);
+                    break;
+
+                case OVERVIEW:
+                    overview = true;
+                    break;
+
+                case NO_OVERVIEW:
+                    overview = false;
+                    break;
+            }
+
+            checkAllClassesFiles();
+            checkFrameFiles();
+            checkOverviewSummary();
+
+            checkIndex();
+            checkNavBar();
+            checkHelpDoc();
+
+        }
+
+        private void checkAllClassesFiles() {
+            // these files are only generated in frames mode
+            checkFiles(frames,
+                    "allclasses-frame.html",
+                    "allclasses-noframe.html");
+
+            // this file is only generated when not in frames mode
+            checkFiles(!frames,
+                    "allclasses.html");
+        }
+
+        private void checkFrameFiles() {
+            // these files are all only generated in frames mode
+
+            // <module>-frame.html and <module>-type-frame.html files
+            checkFiles(frames, classes.stream()
+                .filter(c -> isInModule(c))
+                .map(c -> modulePart(c))
+                .flatMap(m -> Arrays.asList(
+                        m + "-frame.html",
+                        m + "-type-frame.html").stream())
+                .collect(Collectors.toSet()));
+
+            // <package>/package-frame.html files
+            checkFiles(frames, classes.stream()
+                    .map(c -> packagePart(c) + "/package-frame.html")
+                    .collect(Collectors.toSet()));
+        }
+
+        private void checkHelpDoc() {
+            // the Help page only describes Frame/NoFrames in frames mode
+            checkOutput("help-doc.html", frames,
+                        "<h2>Frames/No Frames</h2>");
+        }
+
+        private void checkIndex() {
+            // the index.html page only contains frames in frames mode
+            checkOutput("index.html", frames,
+                    "<iframe ",
+                    "</iframe>");
+
+            // the index.html contains the overview if one
+            // has been given, and not in frames mode
+            checkOutput("index.html", !frames && oKind == OverviewKind.OVERVIEW,
+                    "This is the overview file");
+
+            // the index.html file contains a summary table
+            // if an overview was generated and not in frames mode
+            checkOutput("index.html", !frames && overview,
+                    "<table class=\"overviewSummary\"");
+
+            // the index.html file contains a redirect if
+            // no frames and no overview
+            checkOutput("index.html", !frames && !overview,
+                    "<meta http-equiv=\"Refresh\" content=\"0;",
+                    "<script type=\"text/javascript\">window.location.replace(");
+
+            // the index.html file <meta> refresh should only use <noscript> in HTML 5
+            if (!frames && !overview) {
+                checkOutput("index.html", hKind == HtmlKind.HTML5,
+                        "<noscript>\n<meta http-equiv=\"Refresh\" content=\"0;");
+            }
+        }
+
+        private void checkNavBar() {
+            // the files containing a navigation bar should only
+            // contain FRAMES/NO-FRAMES links in frames mode
+            List<String> navbarFiles = new ArrayList<>();
+            navbarFiles.addAll(classes.stream()
+                    .map(c -> toHtml(packageClassPart(c)))
+                    .collect(Collectors.toSet()));
+            for (String f : navbarFiles) {
+                checkOutput(f, frames,
+                        "target=\"_top\">Frames</a>",
+                        "target=\"_top\">No&nbsp;Frames</a>");
+            }
+        }
+
+        private void checkOverviewSummary() {
+            // the overview-summary.html file only appears if
+            // in frames mode and (overview requested or multiple packages)
+            checkFiles(frames && overview,
+                    "overview-summary.html");
+        }
+
+        private long getPackageCount() {
+            return this.classes.stream()
+                .filter(name -> name.contains("."))
+                .map(name -> name.substring(0, name.lastIndexOf(".")))
+                .distinct()
+                .count();
+        }
+
+        private String packagePart(String className) {
+            int slash = className.indexOf("/");
+            int lastDot = className.lastIndexOf(".");
+            return className.substring(slash + 1, lastDot);
+        }
+
+        private String packageClassPart(String className) {
+            int slash = className.indexOf("/");
+            return className.substring(slash + 1);
+        }
+
+        private boolean isInModule(String className) {
+            return className.contains("/");
+        }
+
+        private String modulePart(String className) {
+            int slash = className.indexOf("/");
+            return className.substring(0, slash);
+        }
+
+        private String toHtml(String className) {
+            return className.replace(".", "/") + ".html";
+        }
+    }
+}
--- a/langtools/test/tools/lib/toolbox/ModuleBuilder.java	Mon Aug 15 13:42:34 2016 -0700
+++ b/langtools/test/tools/lib/toolbox/ModuleBuilder.java	Mon Aug 15 18:00:36 2016 -0700
@@ -165,7 +165,7 @@
         if (!comment.isEmpty()) {
             sb.append("/**\n").append(comment.replace("\n", " *")).append(" */\n");
         }
-        sb.append("module ").append(name).append(" {");
+        sb.append("module ").append(name).append(" {\n");
         requires.forEach(r -> sb.append("    " + r + "\n"));
         exports.forEach(e -> sb.append("    " + e + "\n"));
         uses.forEach(u -> sb.append("    " + u + "\n"));