1 /* 2 * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 4494033 7028815 7052425 8007338 8023608 8008164 8016549 8072461 8154261 8162363 8160196 8151743 8177417 27 * 8175218 8176452 8181215 8182263 8183511 8169819 8183037 8185369 8182765 8196201 8184205 8223378 8241544 28 * 8253117 8263528 29 * @summary Run tests on doclet stylesheet. 30 * @library /tools/lib ../../lib 31 * @modules jdk.javadoc/jdk.javadoc.internal.tool 32 * @build toolbox.ToolBox javadoc.tester.* 33 * @run main TestStylesheet 34 */ 35 36 import java.io.IOException; 37 import java.io.PrintStream; 38 import java.nio.file.Path; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Set; 42 import java.util.TreeSet; 43 import java.util.function.Function; 44 45 import javadoc.tester.HtmlChecker; 46 import javadoc.tester.JavadocTester; 47 import toolbox.ToolBox; 48 49 public class TestStylesheet extends JavadocTester { 50 main(String... args)51 public static void main(String... args) throws Exception { 52 TestStylesheet tester = new TestStylesheet(); 53 tester.runTests(m -> new Object[] { Path.of(m.getName())}); 54 } 55 56 @Test test(Path base)57 public void test(Path base) { 58 javadoc("-d", base.resolve("out").toString(), 59 "-sourcepath", testSrc, 60 "pkg"); 61 checkExit(Exit.ERROR); 62 63 checkOutput(Output.OUT, true, 64 "attribute not supported in HTML5: name"); 65 66 // TODO: most of this test seems a bit silly, since javadoc is simply 67 // copying in the stylesheet from the source directory 68 checkOutput("stylesheet.css", true, 69 """ 70 body { 71 background-color:#ffffff; 72 color:#353833; 73 font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; 74 font-size:14px; 75 margin:0; 76 padding:0; 77 height:100%; 78 width:100%; 79 }""", 80 """ 81 iframe { 82 margin:0; 83 padding:0; 84 height:100%; 85 width:100%; 86 overflow-y:scroll; 87 border:none; 88 }""", 89 "ul {\n" 90 + " list-style-type:disc;\n" 91 + "}", 92 """ 93 .caption { 94 position:relative; 95 text-align:left; 96 background-repeat:no-repeat; 97 color:#253441; 98 font-weight:bold; 99 clear:none; 100 overflow:hidden; 101 padding:0; 102 padding-top:10px; 103 padding-left:1px; 104 margin:0; 105 white-space:pre; 106 }""", 107 """ 108 .caption span { 109 white-space:nowrap; 110 padding-top:5px; 111 padding-left:12px; 112 padding-right:12px; 113 padding-bottom:7px; 114 display:inline-block; 115 float:left; 116 background-color:#F8981D; 117 border: none; 118 height:16px; 119 }""", 120 """ 121 div.table-tabs > button { 122 border: none; 123 cursor: pointer; 124 padding: 5px 12px 7px 12px; 125 font-weight: bold; 126 margin-right: 3px; 127 } 128 div.table-tabs > button.active-table-tab { 129 background: #F8981D; 130 color: #253441; 131 } 132 div.table-tabs > button.table-tab { 133 background: #4D7A97; 134 color: #FFFFFF; 135 }""", 136 // Test the formatting styles for proper content display in use and constant values pages. 137 """ 138 .col-first, .col-second, .col-constructor-name { 139 vertical-align:top; 140 overflow: auto; 141 }""", 142 """ 143 .summary-table > div, .details-table > div { 144 text-align:left; 145 padding: 8px 3px 3px 7px; 146 }""", 147 "@import url('resources/fonts/dejavu.css');", 148 """ 149 .search-tag-result:target { 150 background-color:yellow; 151 }""", 152 """ 153 a[href]:hover, a[href]:focus { 154 text-decoration:none; 155 color:#bb7a2a; 156 }""", 157 """ 158 .col-first a:link, .col-first a:visited, 159 .col-second a:link, .col-second a:visited, 160 .col-first a:link, .col-first a:visited, 161 .col-second a:link, .col-second a:visited, 162 .col-constructor-name a:link, .col-constructor-name a:visited, 163 .col-summary-item-name a:link, .col-summary-item-name a:visited, 164 .constant-values-container a:link, .constant-values-container a:visited, 165 .all-classes-container a:link, .all-classes-container a:visited, 166 .all-packages-container a:link, .all-packages-container a:visited { 167 font-weight:bold; 168 }""", 169 """ 170 .deprecation-block { 171 font-size:14px; 172 font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; 173 border-style:solid; 174 border-width:thin; 175 border-radius:10px; 176 padding:10px; 177 margin-bottom:10px; 178 margin-right:10px; 179 display:inline-block; 180 }""", 181 """ 182 #reset-button { 183 background-color: rgb(255,255,255); 184 background-image:url('resources/x.png'); 185 background-position:center; 186 background-repeat:no-repeat; 187 background-size:12px; 188 border:0 none; 189 width:16px; 190 height:16px; 191 position:relative; 192 left:-4px; 193 top:-4px; 194 font-size:0px; 195 }""", 196 """ 197 .watermark { 198 color:#545454; 199 }"""); 200 201 checkOutput("pkg/A.html", true, 202 // Test whether a link to the stylesheet file is inserted properly 203 // in the class documentation. 204 """ 205 <link rel="stylesheet" type="text/css" href="../stylesheet.css" title="Style">""", 206 """ 207 <div class="block">Test comment for a class which has an <a name="named_anchor">anchor_with_name</a> and 208 an <a id="named_anchor1">anchor_with_id</a>.</div>"""); 209 210 checkOutput("pkg/package-summary.html", true, 211 """ 212 <div class="col-last even-row-color class-summary class-summary-tab2"> 213 <div class="block">Test comment for a class which has an <a name="named_anchor">anchor_with_name</a> and 214 an <a id="named_anchor1">anchor_with_id</a>.</div> 215 </div>"""); 216 217 checkOutput("index.html", true, 218 """ 219 <link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">"""); 220 221 checkOutput("stylesheet.css", false, 222 """ 223 * { 224 margin:0; 225 padding:0; 226 }""", 227 """ 228 a:active { 229 text-decoration:none; 230 color:#4A6782; 231 }""", 232 """ 233 a[name]:hover { 234 text-decoration:none; 235 color:#353833; 236 }""", 237 """ 238 td.col-first a:link, td.col-first a:visited, 239 td.col-second a:link, td.col-second a:visited, 240 th.col-first a:link, th.col-first a:visited, 241 th.col-second a:link, th.col-second a:visited, 242 th.col-constructor-name a:link, th.col-constructor-name a:visited, 243 td.col-last a:link, td.col-last a:visited, 244 .constant-values-container td a:link, .constant-values-container td a:visited { 245 font-weight:bold; 246 }"""); 247 } 248 249 ToolBox tb = new ToolBox(); 250 251 @Test testStyles(Path base)252 public void testStyles(Path base) throws Exception { 253 Path src = base.resolve("src"); 254 tb.writeJavaFiles(src, 255 "module mA { exports p; }", 256 """ 257 package p; public class C { 258 public C() { } 259 public C(int i) { } 260 public int f1; 261 public int f2; 262 public int m1() { } 263 public int m2(int i) { } 264 } 265 """, 266 """ 267 package p; public @interface Anno { 268 public int value(); 269 } 270 """ 271 ); 272 273 javadoc("-d", base.resolve("out").toString(), 274 "-sourcepath", src.toString(), 275 "--module", "mA"); 276 checkExit(Exit.OK); 277 checkStyles(addExtraCSSClassNamesTo(readStylesheet())); 278 } 279 readStylesheet()280 Set<String> readStylesheet() { 281 // scan for class selectors, skipping '{' ... '}' 282 Set<String> styles = new TreeSet<>(); 283 String stylesheet = readFile("stylesheet.css"); 284 for (int i = 0; i < stylesheet.length(); i++) { 285 char ch = stylesheet.charAt(i); 286 switch (ch) { 287 case '.': 288 i++; 289 int start = i; 290 while (i < stylesheet.length()) { 291 ch = stylesheet.charAt(i); 292 if (!(Character.isLetterOrDigit(ch) || ch == '-')) { 293 break; 294 } 295 i++; 296 } 297 styles.add(stylesheet.substring(start, i)); 298 break; 299 300 case '{': 301 i++; 302 while (i < stylesheet.length()) { 303 ch = stylesheet.charAt(i); 304 if (ch == '}') { 305 break; 306 } 307 i++; 308 } 309 break; 310 311 case '@': 312 i++; 313 while (i < stylesheet.length()) { 314 ch = stylesheet.charAt(i); 315 if (ch == '{') { 316 break; 317 } 318 i++; 319 } 320 break; 321 } 322 } 323 out.println("found styles: " + styles); 324 return styles; 325 } 326 addExtraCSSClassNamesTo(Set<String> styles)327 Set<String> addExtraCSSClassNamesTo(Set<String> styles) throws Exception { 328 // The following names are used in the generated HTML, 329 // but have no corresponding definitions in the stylesheet file. 330 // They are mostly optional, in the "use if you want to" category. 331 // They are included here so that we do not get errors when these 332 // names are used in the generated HTML. 333 List<String> extra = List.of( 334 // entries for <body> elements 335 "all-classes-index-page", 336 "all-packages-index-page", 337 "constants-summary-page", 338 "deprecated-list-page", 339 "help-page", 340 "index-redirect-page", 341 "package-declaration-page", 342 "package-tree-page", 343 "single-index-page", 344 "tree-page", 345 // the following names are matched by [class$='...'] in the stylesheet 346 "constructor-details", 347 "constructor-summary", 348 "field-details", 349 "field-summary", 350 "member-details", 351 "method-details", 352 "method-summary", 353 // the following provide the ability to optionally override components of the 354 // memberSignature structure 355 "name", 356 "modifiers", 357 "packages", 358 "return-type", 359 // and others... 360 "help-section", // part of the help page 361 "hierarchy", // for the hierarchy on a tree page 362 "index" // on the index page 363 ); 364 Set<String> all = new TreeSet<>(styles); 365 for (String e : extra) { 366 if (styles.contains(e)) { 367 throw new Exception("extra CSS class name found in style sheet: " + e); 368 } 369 all.add(e); 370 } 371 return all; 372 } 373 374 /** 375 * Checks that all the CSS names found in {@code class} attributes in HTML files in the 376 * output directory are present in a given set of styles. 377 * 378 * @param styles the styles 379 */ checkStyles(Set<String> styles)380 void checkStyles(Set<String> styles) { 381 checking("Check CSS class names"); 382 CSSClassChecker c = new CSSClassChecker(out, this::readFile, styles); 383 try { 384 c.checkDirectory(outputDir.toPath()); 385 c.report(); 386 int errors = c.getErrorCount(); 387 if (errors == 0) { 388 passed("No CSS class name errors found"); 389 } else { 390 failed(errors + " errors found when checking CSS class names"); 391 } 392 } catch (IOException e) { 393 failed("exception thrown when reading files: " + e); 394 } 395 396 } 397 398 class CSSClassChecker extends HtmlChecker { 399 Set<String> styles; 400 int errors; 401 CSSClassChecker(PrintStream out, Function<Path, String> fileReader, Set<String> styles)402 protected CSSClassChecker(PrintStream out, 403 Function<Path, String> fileReader, 404 Set<String> styles) { 405 super(out, fileReader); 406 this.styles = styles; 407 } 408 getErrorCount()409 protected int getErrorCount() { 410 return errors; 411 } 412 413 @Override report()414 protected void report() { 415 if (getErrorCount() == 0) { 416 out.println("All CSS class names found"); 417 } else { 418 out.println(getErrorCount() + " CSS class names not found"); 419 } 420 421 } 422 423 @Override startElement(String name, Map<String,String> attrs, boolean selfClosing)424 public void startElement(String name, Map<String,String> attrs, boolean selfClosing) { 425 String style = attrs.get("class"); 426 if (style != null && !styles.contains(style)) { 427 error(currFile, getLineNumber(), "CSS class name not found: " + style); 428 } 429 } 430 } 431 } 432