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