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 }