1 /*
   2  * Copyright (c) 1998, 2017, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package javax.swing.text.html;
  26 
  27 import java.awt.*;
  28 import java.util.*;
  29 import java.net.*;
  30 import java.io.*;
  31 import javax.swing.*;
  32 import javax.swing.text.*;
  33 import javax.swing.event.*;
  34 
  35 import sun.swing.text.html.FrameEditorPaneTag;
  36 
  37 /**
  38  * Implements a FrameView, intended to support the HTML
  39  * <FRAME> tag.  Supports the frameborder, scrolling,
  40  * marginwidth and marginheight attributes.
  41  *
  42  * @author    Sunita Mani
  43  */
  44 
  45 class FrameView extends ComponentView implements HyperlinkListener {
  46 
  47 
  48     JEditorPane htmlPane;
  49     JScrollPane scroller;
  50     boolean editable;
  51     float width;
  52     float height;
  53     URL src;
  54     /** Set to true when the component has been created. */
  55     private boolean createdComponent;
  56 
  57     /**
  58      * Creates a new Frame.
  59      *
  60      * @param elem the element to represent.
  61      */
  62     public FrameView(Element elem) {
  63         super(elem);
  64     }
  65 
  66     protected Component createComponent() {
  67 
  68         Element elem = getElement();
  69         AttributeSet attributes = elem.getAttributes();
  70         String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC);
  71 
  72         if ((srcAtt != null) && (!srcAtt.equals(""))) {
  73             try {
  74                 URL base = ((HTMLDocument)elem.getDocument()).getBase();
  75                 src = new URL(base, srcAtt);
  76                 htmlPane = new FrameEditorPane();
  77                 htmlPane.addHyperlinkListener(this);
  78                 JEditorPane host = getHostPane();
  79                 boolean isAutoFormSubmission = true;
  80                 if (host != null) {
  81                     htmlPane.setEditable(host.isEditable());
  82                     String charset = (String) host.getClientProperty("charset");
  83                     if (charset != null) {
  84                         htmlPane.putClientProperty("charset", charset);
  85                     }
  86                     HTMLEditorKit hostKit = (HTMLEditorKit)host.getEditorKit();
  87                     if (hostKit != null) {
  88                         isAutoFormSubmission = hostKit.isAutoFormSubmission();
  89                     }
  90                 }
  91                 htmlPane.setPage(src);
  92                 HTMLEditorKit kit = (HTMLEditorKit)htmlPane.getEditorKit();
  93                 if (kit != null) {
  94                     kit.setAutoFormSubmission(isAutoFormSubmission);
  95                 }
  96 
  97                 Document doc = htmlPane.getDocument();
  98                 if (doc instanceof HTMLDocument) {
  99                     ((HTMLDocument)doc).setFrameDocumentState(true);
 100                 }
 101                 setMargin();
 102                 createScrollPane();
 103                 setBorder();
 104             } catch (MalformedURLException e) {
 105                 e.printStackTrace();
 106             } catch (IOException e1) {
 107                 e1.printStackTrace();
 108             }
 109         }
 110         createdComponent = true;
 111         return scroller;
 112     }
 113 
 114     JEditorPane getHostPane() {
 115         Container c = getContainer();
 116         while ((c != null) && ! (c instanceof JEditorPane)) {
 117             c = c.getParent();
 118         }
 119         return (JEditorPane) c;
 120     }
 121 
 122 
 123     /**
 124      * Sets the parent view for the FrameView.
 125      * Also determines if the FrameView should be editable
 126      * or not based on whether the JTextComponent that
 127      * contains it is editable.
 128      *
 129      * @param parent View
 130      */
 131     public void setParent(View parent) {
 132         if (parent != null) {
 133             JTextComponent t = (JTextComponent)parent.getContainer();
 134             editable = t.isEditable();
 135         }
 136         super.setParent(parent);
 137     }
 138 
 139 
 140     /**
 141      * Also determines if the FrameView should be editable
 142      * or not based on whether the JTextComponent that
 143      * contains it is editable. And then proceeds to call
 144      * the superclass to do the paint().
 145      *
 146      * @see javax.swing.text.ComponentView#paint
 147      */
 148     public void paint(Graphics g, Shape allocation) {
 149 
 150         Container host = getContainer();
 151         if (host != null && htmlPane != null &&
 152             htmlPane.isEditable() != ((JTextComponent)host).isEditable()) {
 153             editable = ((JTextComponent)host).isEditable();
 154             htmlPane.setEditable(editable);
 155         }
 156         super.paint(g, allocation);
 157     }
 158 
 159 
 160     /**
 161      * If the marginwidth or marginheight attributes have been specified,
 162      * then the JEditorPane's margin's are set to the new values.
 163      */
 164     private void setMargin() {
 165         int margin = 0;
 166         Insets in = htmlPane.getMargin();
 167         Insets newInsets;
 168         boolean modified = false;
 169         AttributeSet attributes = getElement().getAttributes();
 170         String marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINWIDTH);
 171         if ( in != null) {
 172             newInsets = new Insets(in.top, in.left, in.right, in.bottom);
 173         } else {
 174             newInsets = new Insets(0,0,0,0);
 175         }
 176         if (marginStr != null) {
 177             margin = Integer.parseInt(marginStr);
 178             if (margin > 0) {
 179                 newInsets.left = margin;
 180                 newInsets.right = margin;
 181                 modified = true;
 182             }
 183         }
 184         marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINHEIGHT);
 185         if (marginStr != null) {
 186             margin = Integer.parseInt(marginStr);
 187             if (margin > 0) {
 188                 newInsets.top = margin;
 189                 newInsets.bottom = margin;
 190                 modified = true;
 191             }
 192         }
 193         if (modified) {
 194             htmlPane.setMargin(newInsets);
 195         }
 196     }
 197 
 198     /**
 199      * If the frameborder attribute has been specified, either in the frame,
 200      * or by the frames enclosing frameset, the JScrollPane's setBorder()
 201      * method is invoked to achieve the desired look.
 202      */
 203     private void setBorder() {
 204 
 205         AttributeSet attributes = getElement().getAttributes();
 206         String frameBorder = (String)attributes.getAttribute(HTML.Attribute.FRAMEBORDER);
 207         if ((frameBorder != null) &&
 208             (frameBorder.equals("no") || frameBorder.equals("0"))) {
 209             // make invisible borders.
 210             scroller.setBorder(null);
 211         }
 212     }
 213 
 214 
 215     /**
 216      * This method creates the JScrollPane.  The scrollbar policy is determined by
 217      * the scrolling attribute.  If not defined, the default is "auto" which
 218      * maps to the scrollbar's being displayed as needed.
 219      */
 220     @SuppressWarnings("deprecation")
 221     private void createScrollPane() {
 222         AttributeSet attributes = getElement().getAttributes();
 223         String scrolling = (String)attributes.getAttribute(HTML.Attribute.SCROLLING);
 224         if (scrolling == null) {
 225             scrolling = "auto";
 226         }
 227 
 228         if (!scrolling.equals("no")) {
 229             if (scrolling.equals("yes")) {
 230                 scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
 231                                            JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
 232             } else {
 233                 // scrollbars will be displayed if needed
 234                 //
 235                 scroller = new JScrollPane();
 236             }
 237         } else {
 238             scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_NEVER,
 239                                        JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
 240         }
 241 
 242         JViewport vp = scroller.getViewport();
 243         vp.add(htmlPane);
 244         vp.setBackingStoreEnabled(true);
 245         scroller.setMinimumSize(new Dimension(5,5));
 246         scroller.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
 247     }
 248 
 249 
 250     /**
 251      * Finds the outermost FrameSetView.  It then
 252      * returns that FrameSetView's container.
 253      */
 254     JEditorPane getOutermostJEditorPane() {
 255 
 256         View parent = getParent();
 257         FrameSetView frameSetView = null;
 258         while (parent != null) {
 259             if (parent instanceof FrameSetView) {
 260                 frameSetView = (FrameSetView)parent;
 261             }
 262             parent = parent.getParent();
 263         }
 264         if (frameSetView != null) {
 265             return (JEditorPane)frameSetView.getContainer();
 266         }
 267         return null;
 268     }
 269 
 270 
 271     /**
 272      * Returns true if this frame is contained within
 273      * a nested frameset.
 274      */
 275     private boolean inNestedFrameSet() {
 276         FrameSetView parent = (FrameSetView)getParent();
 277         return (parent.getParent() instanceof FrameSetView);
 278     }
 279 
 280 
 281     /**
 282      * Notification of a change relative to a
 283      * hyperlink. This method searches for the outermost
 284      * JEditorPane, and then fires an HTMLFrameHyperlinkEvent
 285      * to that frame.  In addition, if the target is _parent,
 286      * and there is not nested framesets then the target is
 287      * reset to _top.  If the target is _top, in addition to
 288      * firing the event to the outermost JEditorPane, this
 289      * method also invokes the setPage() method and explicitly
 290      * replaces the current document with the destination url.
 291      */
 292     public void hyperlinkUpdate(HyperlinkEvent evt) {
 293 
 294         JEditorPane c = getOutermostJEditorPane();
 295         if (c == null) {
 296             return;
 297         }
 298 
 299         if (!(evt instanceof HTMLFrameHyperlinkEvent)) {
 300             c.fireHyperlinkUpdate(evt);
 301             return;
 302         }
 303 
 304         HTMLFrameHyperlinkEvent e = (HTMLFrameHyperlinkEvent)evt;
 305 
 306         if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
 307             String target = e.getTarget();
 308             String postTarget = target;
 309 
 310             if (target.equals("_parent") && !inNestedFrameSet()){
 311                 target = "_top";
 312             }
 313 
 314             if (evt instanceof FormSubmitEvent) {
 315                 HTMLEditorKit kit = (HTMLEditorKit)c.getEditorKit();
 316                 if (kit != null && kit.isAutoFormSubmission()) {
 317                     if (target.equals("_top")) {
 318                         try {
 319                             movePostData(c, postTarget);
 320                             c.setPage(e.getURL());
 321                         } catch (IOException ex) {
 322                             // Need a way to handle exceptions
 323                         }
 324                     } else {
 325                         HTMLDocument doc = (HTMLDocument)c.getDocument();
 326                         doc.processHTMLFrameHyperlinkEvent(e);
 327                     }
 328                 } else {
 329                     c.fireHyperlinkUpdate(evt);
 330                 }
 331                 return;
 332             }
 333 
 334             if (target.equals("_top")) {
 335                 try {
 336                     c.setPage(e.getURL());
 337                 } catch (IOException ex) {
 338                     // Need a way to handle exceptions
 339                     // ex.printStackTrace();
 340                 }
 341             }
 342             if (!c.isEditable()) {
 343                 c.fireHyperlinkUpdate(new HTMLFrameHyperlinkEvent(c,
 344                                                                   e.getEventType(),
 345                                                                   e.getURL(),
 346                                                                   e.getDescription(),
 347                                                                   getElement(),
 348                                                                   e.getInputEvent(),
 349                                                                   target));
 350             }
 351         }
 352     }
 353 
 354     /**
 355      * Gives notification from the document that attributes were changed
 356      * in a location that this view is responsible for.  Currently this view
 357      * handles changes to its SRC attribute.
 358      *
 359      * @param e the change information from the associated document
 360      * @param a the current allocation of the view
 361      * @param f the factory to use to rebuild if the view has children
 362      *
 363      */
 364     public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
 365 
 366         Element elem = getElement();
 367         AttributeSet attributes = elem.getAttributes();
 368 
 369         URL oldPage = src;
 370 
 371         String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC);
 372         URL base = ((HTMLDocument)elem.getDocument()).getBase();
 373         try {
 374             if (!createdComponent) {
 375                 return;
 376             }
 377 
 378             Object postData = movePostData(htmlPane, null);
 379             src = new URL(base, srcAtt);
 380             if (oldPage.equals(src) && (src.getRef() == null) && (postData == null)) {
 381                 return;
 382             }
 383 
 384             htmlPane.setPage(src);
 385             Document newDoc = htmlPane.getDocument();
 386             if (newDoc instanceof HTMLDocument) {
 387                 ((HTMLDocument)newDoc).setFrameDocumentState(true);
 388             }
 389         } catch (MalformedURLException e1) {
 390             // Need a way to handle exceptions
 391             //e1.printStackTrace();
 392         } catch (IOException e2) {
 393             // Need a way to handle exceptions
 394             //e2.printStackTrace();
 395         }
 396     }
 397 
 398     /**
 399      * Move POST data from temporary storage into the target document property.
 400      *
 401      * @return the POST data or null if no data found
 402      */
 403     private Object movePostData(JEditorPane targetPane, String frameName) {
 404         Object postData = null;
 405         JEditorPane p = getOutermostJEditorPane();
 406         if (p != null) {
 407             if (frameName == null) {
 408                 frameName = (String) getElement().getAttributes().getAttribute(
 409                         HTML.Attribute.NAME);
 410             }
 411             if (frameName != null) {
 412                 String propName = FormView.PostDataProperty + "." + frameName;
 413                 Document d = p.getDocument();
 414                 postData = d.getProperty(propName);
 415                 if (postData != null) {
 416                     targetPane.getDocument().putProperty(
 417                             FormView.PostDataProperty, postData);
 418                     d.putProperty(propName, null);
 419                 }
 420             }
 421         }
 422 
 423         return postData;
 424     }
 425 
 426     /**
 427      * Determines the minimum span for this view along an
 428      * axis.
 429      *
 430      * @param axis may be either <code>View.X_AXIS</code> or
 431      *  <code>View.Y_AXIS</code>
 432      * @return the preferred span; given that we do not
 433      * support resizing of frames, the minimum span returned
 434      * is the same as the preferred span
 435      *
 436      */
 437     public float getMinimumSpan(int axis) {
 438       return 5;
 439     }
 440 
 441     /**
 442      * Determines the maximum span for this view along an
 443      * axis.
 444      *
 445      * @param axis may be either <code>View.X_AXIS</code> or
 446      *  <code>View.Y_AXIS</code>
 447      * @return the preferred span; given that we do not
 448      * support resizing of frames, the maximum span returned
 449      * is the same as the preferred span
 450      *
 451      */
 452     public float getMaximumSpan(int axis) {
 453         return Integer.MAX_VALUE;
 454     }
 455 
 456     /** Editor pane rendering frame of HTML document
 457      *  It uses the same editor kits classes as outermost JEditorPane
 458      */
 459     @SuppressWarnings("serial") // Superclass is not serializable across versions
 460     class FrameEditorPane extends JEditorPane implements FrameEditorPaneTag {
 461         public EditorKit getEditorKitForContentType(String type) {
 462             EditorKit editorKit = super.getEditorKitForContentType(type);
 463             JEditorPane outerMostJEditorPane = null;
 464             if ((outerMostJEditorPane = getOutermostJEditorPane()) != null) {
 465                 EditorKit inheritedEditorKit = outerMostJEditorPane.getEditorKitForContentType(type);
 466                 if (! editorKit.getClass().equals(inheritedEditorKit.getClass())) {
 467                     editorKit = (EditorKit) inheritedEditorKit.clone();
 468                     setEditorKitForContentType(type, editorKit);
 469                 }
 470             }
 471             return editorKit;
 472         }
 473 
 474         FrameView getFrameView() {
 475             return FrameView.this;
 476         }
 477     }
 478 }