1 /*
   2  * Copyright (c) 2012, 2016, 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  * @key headful
  27  * @bug 7165725
  28  * @summary  Tests if HTML parser can handle successive script tags in a line
  29  *           and it does not call false text callback after script tags.
  30  * @run main bug7165725
  31  */
  32 
  33 import java.awt.BorderLayout;
  34 import java.awt.Robot;
  35 import java.io.File;
  36 import java.io.FileReader;
  37 import java.io.IOException;
  38 import java.net.URL;
  39 import java.util.ArrayList;
  40 import java.util.Arrays;
  41 import java.util.List;
  42 import javax.swing.*;
  43 import javax.swing.text.AbstractDocument.AbstractElement;
  44 import javax.swing.text.AbstractDocument;
  45 import javax.swing.text.Document;
  46 import javax.swing.text.MutableAttributeSet;
  47 import javax.swing.text.html.HTML;
  48 import javax.swing.text.html.HTMLDocument;
  49 import javax.swing.text.html.HTMLEditorKit;
  50 import javax.swing.text.html.parser.ParserDelegator;
  51 
  52 public class bug7165725 extends JFrame {
  53     private static class GoldenElement {
  54 
  55         private String goldenName;
  56         private List<GoldenElement> goldenChildren;
  57 
  58         GoldenElement(String goldenName, GoldenElement... goldenChildren){
  59             this.goldenName = goldenName;
  60             if (goldenChildren != null) {
  61                 this.goldenChildren = Arrays.asList(goldenChildren);
  62             } else {
  63                 this.goldenChildren = new ArrayList<>();
  64             }
  65         }
  66 
  67         // throws RuntimeException if not ok
  68         public void checkStructureEquivalence(AbstractDocument.AbstractElement elem) {
  69             String name = elem.getName();
  70             if (!goldenName.equals(name)) {
  71                 throw new RuntimeException("Bad structure: expected element name is '" + goldenName + "' but the actual name was '" + name + "'.");
  72             }
  73             int goldenChildCount = goldenChildren.size();
  74             int childCount = elem.getChildCount();
  75             if (childCount != goldenChildCount) {
  76                 System.out.print("D: children: ");
  77                 for (int i = 0; i < childCount; i++) {
  78                     System.out.print(" " + elem.getElement(i).getName());
  79                 }
  80                 System.out.println("");
  81                 System.out.print("D: goldenChildren: ");
  82                 for (GoldenElement ge : goldenChildren) {
  83                     System.out.print(" " + ge.goldenName);
  84                 }
  85                 System.out.println("");
  86 
  87                 throw new RuntimeException("Bad structure: expected child count of element '" + goldenName + "' is '" + goldenChildCount + "' but the actual count was '" + childCount + "'.");
  88             }
  89             for (int i = 0; i < childCount; i++) {
  90                 AbstractDocument.AbstractElement nextElem = (AbstractDocument.AbstractElement) elem.getElement(i);
  91                 GoldenElement goldenElement = goldenChildren.get(i);
  92                 goldenElement.checkStructureEquivalence(nextElem);
  93             }
  94         }
  95     }
  96 
  97     private JEditorPane editorPane;
  98     public void execute(final String urlStr, final GoldenElement goldenElement) throws Exception {
  99         System.out.println();
 100         System.out.println("***** TEST: " + urlStr + " *****");
 101         System.out.println();
 102 
 103         SwingUtilities.invokeAndWait(new Runnable() {
 104             public void run() {
 105                 try {
 106                     editorPane = new JEditorPane();
 107                     editorPane.setEditorKit(new HTMLEditorKit() {
 108                         public Document createDefaultDocument() {
 109                             AbstractDocument doc =
 110                                     (AbstractDocument) super.createDefaultDocument();
 111                             doc.setAsynchronousLoadPriority(-1);
 112                             return doc;
 113                         }
 114                     });
 115                     editorPane.setPage(new URL(urlStr));
 116                 } catch (IOException ex) {
 117                     throw new RuntimeException("Test failed", ex);
 118                 }
 119                 editorPane.setEditable(false);
 120                 JScrollPane scroller = new JScrollPane();
 121                 JViewport vp = scroller.getViewport();
 122                 vp.add(editorPane);
 123                 add(scroller, BorderLayout.CENTER);
 124                 setDefaultCloseOperation(EXIT_ON_CLOSE);
 125                 setSize(400, 400);
 126                 setLocationRelativeTo(null);
 127                 setVisible(true);
 128             }
 129         });
 130 
 131         Robot robot = new Robot();
 132         robot.waitForIdle();
 133 
 134         SwingUtilities.invokeAndWait(new Runnable() {
 135             public void run() {
 136                 HTMLDocument doc = (HTMLDocument) editorPane.getDocument();
 137                 doc.dump(System.out);
 138                 goldenElement.checkStructureEquivalence((AbstractElement) doc.getDefaultRootElement());
 139                 dispose();
 140             }
 141         });
 142 
 143         System.out.println();
 144         System.out.println("*********************************");
 145         System.out.println();
 146     }
 147 
 148     public static void main(String[] args) throws Exception {
 149 
 150         String dirURL = getDirURL();
 151 
 152         System.out.println("dirURL = " + dirURL);
 153 
 154         new bug7165725().execute(dirURL + "successive-script-tag.html", createSuccessiveScriptTags());
 155         new bug7165725().execute(dirURL + "false-text-after-script.html", createFalseTextAfterScript());
 156 
 157         checkByCallbackForSuccessiveScript();
 158         checkByCallbackForFalseTextAfterScript();
 159 
 160         System.out.println();
 161         System.out.println();
 162         System.out.println("Test passed.");
 163     }
 164 
 165     static String getDirURL() {
 166         return "file:///" +
 167                 new File(System.getProperty("test.src", ".")).getAbsolutePath() +
 168                 File.separator;
 169     }
 170 
 171     static String getParsedContentOneLine(String path) throws Exception {
 172         File f = new File(path);
 173         FileReader fr = new FileReader(f);
 174         ParserDelegator pd = new ParserDelegator();
 175         SBParserCallback sbcallback = new SBParserCallback();
 176         pd.parse(fr, sbcallback, true);
 177         fr.close();
 178         return sbcallback.getStringOneLine();
 179     }
 180 
 181     static String getParsedContentOneLine(URL url) throws Exception {
 182         return getParsedContentOneLine(url.getPath());
 183     }
 184 
 185     static void checkByCallbackForSuccessiveScript() throws Exception {
 186         String content = getParsedContentOneLine(new URL(getDirURL() + "successive-script-tag.html"));
 187         if (!content.matches(".*<script .*/js/js1\\.js.*<script .*/js/js2\\.js.*<script .*/js/js3\\.js.*"))
 188             throw new RuntimeException("Failed to lookup script tags/attributes.");
 189         if (!content.matches(".*<style .*stylesheets/base\\.css.*<style .*stylesheets/adv\\.css.*"))
 190             throw new RuntimeException("Failed to lookup style tags.");
 191     }
 192 
 193     static void checkByCallbackForFalseTextAfterScript() throws Exception {
 194         String content = getParsedContentOneLine(new URL(getDirURL() + "false-text-after-script.html"));
 195         final int bodyIdx = content.indexOf("<body ");
 196         if (bodyIdx > 0) {
 197             String sbody = content.substring(bodyIdx);
 198             // There should be no Text(...) in this html
 199             if (sbody.indexOf("Text(") >= 0)
 200                 throw new RuntimeException("Unexpected text found.");
 201         } else {
 202             throw new RuntimeException("Failed to find body tag.");
 203         }
 204     }
 205 
 206     private static GoldenElement createSuccessiveScriptTags() {
 207         return new GoldenElement("html",
 208                 new GoldenElement("head",
 209                         new GoldenElement("p-implied",
 210                                 new GoldenElement("title"),
 211                                 new GoldenElement("title"),
 212                                 new GoldenElement("script"),
 213                                 new GoldenElement("comment"),
 214                                 new GoldenElement("script"),
 215                                 new GoldenElement("script"),
 216                                 new GoldenElement("comment"),
 217                                 new GoldenElement("script"),
 218                                 new GoldenElement("script"),
 219                                 new GoldenElement("comment"),
 220                                 new GoldenElement("script"),
 221                                 new GoldenElement("content"))),
 222                 new GoldenElement("body",
 223                         new GoldenElement("p-implied",
 224                                 new GoldenElement("content"))));
 225     }
 226 
 227     private static GoldenElement createFalseTextAfterScript() {
 228         return new GoldenElement("html",
 229                 new GoldenElement("head",
 230                         new GoldenElement("p-implied",
 231                                 new GoldenElement("title"),
 232                                 new GoldenElement("title"),
 233                                 new GoldenElement("content"))),
 234                 new GoldenElement("body",
 235                         new GoldenElement("form",
 236                                 new GoldenElement("p-implied",
 237                                         new GoldenElement("input"),
 238                                         new GoldenElement("input"),
 239                                         new GoldenElement("content"))),
 240                         new GoldenElement("p-implied",
 241                                 new GoldenElement("script"),
 242                                 new GoldenElement("comment"),
 243                                 new GoldenElement("script"),
 244                                 new GoldenElement("script"),
 245                                 new GoldenElement("comment"),
 246                                 new GoldenElement("script"),
 247                                 new GoldenElement("content"))));
 248     }
 249 
 250     static class SBParserCallback extends HTMLEditorKit.ParserCallback
 251     {
 252         private int indentSize = 0;
 253         private ArrayList<String> elist = new ArrayList<>();
 254 
 255         public String getStringOneLine() {
 256             StringBuilder sb = new StringBuilder();
 257             for (String s : elist) sb.append(s);
 258             return sb.toString();
 259         }
 260 
 261         public String toString() {
 262             StringBuffer sb = new StringBuffer();
 263             for (String s : elist) sb.append(s + "\n");
 264             return sb.toString();
 265         }
 266 
 267         protected void indent() {
 268             indentSize += 3;
 269         }
 270         protected void unIndent() {
 271             indentSize -= 3; if (indentSize < 0) indentSize = 0;
 272         }
 273 
 274         protected String pIndent() {
 275             StringBuilder sb = new StringBuilder();
 276             for(int i = 0; i < indentSize; i++) sb.append(" ");
 277             return sb.toString();
 278         }
 279 
 280         public void handleText(char[] data, int pos) {
 281             elist.add(pIndent() + "Text(" + data.length + " chars) \"" + new String(data) + "\"");
 282         }
 283 
 284         public void handleComment(char[] data, int pos) {
 285             elist.add(pIndent() + "Comment(" + data.length + " chars)");
 286         }
 287 
 288         public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
 289             elist.add(pIndent() + "Tag start(<" + t.toString() + " " + a + ">, " +
 290                     a.getAttributeCount() + " attrs)");
 291             indent();
 292         }
 293 
 294         public void handleEndTag(HTML.Tag t, int pos) {
 295             unIndent();
 296             elist.add(pIndent() + "Tag end(</" + t.toString() + ">)");
 297         }
 298 
 299         public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) {
 300             elist.add(pIndent() + "Tag(<" + t.toString() + ">, " +
 301                     a.getAttributeCount() + " attrs)");
 302         }
 303 
 304         public void handleError(String errorMsg, int pos){
 305         }
 306     }
 307 }