1 /* 2 * Copyright (c) 1998, 2014, 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 * @param parent View 147 * @see text.ComponentView#paint 148 */ 149 public void paint(Graphics g, Shape allocation) { 150 151 Container host = getContainer(); 152 if (host != null && htmlPane != null && 153 htmlPane.isEditable() != ((JTextComponent)host).isEditable()) { 154 editable = ((JTextComponent)host).isEditable(); 155 htmlPane.setEditable(editable); 156 } 157 super.paint(g, allocation); 158 } 159 160 161 /** 162 * If the marginwidth or marginheight attributes have been specified, 163 * then the JEditorPane's margin's are set to the new values. 164 */ 165 private void setMargin() { 166 int margin = 0; 167 Insets in = htmlPane.getMargin(); 168 Insets newInsets; 169 boolean modified = false; 170 AttributeSet attributes = getElement().getAttributes(); 171 String marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINWIDTH); 172 if ( in != null) { 173 newInsets = new Insets(in.top, in.left, in.right, in.bottom); 174 } else { 175 newInsets = new Insets(0,0,0,0); 176 } 177 if (marginStr != null) { 178 margin = Integer.parseInt(marginStr); 179 if (margin > 0) { 180 newInsets.left = margin; 181 newInsets.right = margin; 182 modified = true; 183 } 184 } 185 marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINHEIGHT); 186 if (marginStr != null) { 187 margin = Integer.parseInt(marginStr); 188 if (margin > 0) { 189 newInsets.top = margin; 190 newInsets.bottom = margin; 191 modified = true; 192 } 193 } 194 if (modified) { 195 htmlPane.setMargin(newInsets); 196 } 197 } 198 199 /** 200 * If the frameborder attribute has been specified, either in the frame, 201 * or by the frames enclosing frameset, the JScrollPane's setBorder() 202 * method is invoked to achieve the desired look. 203 */ 204 private void setBorder() { 205 206 AttributeSet attributes = getElement().getAttributes(); 207 String frameBorder = (String)attributes.getAttribute(HTML.Attribute.FRAMEBORDER); 208 if ((frameBorder != null) && 209 (frameBorder.equals("no") || frameBorder.equals("0"))) { 210 // make invisible borders. 211 scroller.setBorder(null); 212 } 213 } 214 215 216 /** 217 * This method creates the JScrollPane. The scrollbar policy is determined by 218 * the scrolling attribute. If not defined, the default is "auto" which 219 * maps to the scrollbar's being displayed as needed. 220 */ 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 * @param HyperlinkEvent 293 */ 294 public void hyperlinkUpdate(HyperlinkEvent evt) { 295 296 JEditorPane c = getOutermostJEditorPane(); 297 if (c == null) { 298 return; 299 } 300 301 if (!(evt instanceof HTMLFrameHyperlinkEvent)) { 302 c.fireHyperlinkUpdate(evt); 303 return; 304 } 305 306 HTMLFrameHyperlinkEvent e = (HTMLFrameHyperlinkEvent)evt; 307 308 if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { 309 String target = e.getTarget(); 310 String postTarget = target; 311 312 if (target.equals("_parent") && !inNestedFrameSet()){ 313 target = "_top"; 314 } 315 316 if (evt instanceof FormSubmitEvent) { 317 HTMLEditorKit kit = (HTMLEditorKit)c.getEditorKit(); 318 if (kit != null && kit.isAutoFormSubmission()) { 319 if (target.equals("_top")) { 320 try { 321 movePostData(c, postTarget); 322 c.setPage(e.getURL()); 323 } catch (IOException ex) { 324 // Need a way to handle exceptions 325 } 326 } else { 327 HTMLDocument doc = (HTMLDocument)c.getDocument(); 328 doc.processHTMLFrameHyperlinkEvent(e); 329 } 330 } else { 331 c.fireHyperlinkUpdate(evt); 332 } 333 return; 334 } 335 336 if (target.equals("_top")) { 337 try { 338 c.setPage(e.getURL()); 339 } catch (IOException ex) { 340 // Need a way to handle exceptions 341 // ex.printStackTrace(); 342 } 343 } 344 if (!c.isEditable()) { 345 c.fireHyperlinkUpdate(new HTMLFrameHyperlinkEvent(c, 346 e.getEventType(), 347 e.getURL(), 348 e.getDescription(), 349 getElement(), 350 e.getInputEvent(), 351 target)); 352 } 353 } 354 } 355 356 /** 357 * Gives notification from the document that attributes were changed 358 * in a location that this view is responsible for. Currently this view 359 * handles changes to its SRC attribute. 360 * 361 * @param e the change information from the associated document 362 * @param a the current allocation of the view 363 * @param f the factory to use to rebuild if the view has children 364 * 365 */ 366 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) { 367 368 Element elem = getElement(); 369 AttributeSet attributes = elem.getAttributes(); 370 371 URL oldPage = src; 372 373 String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC); 374 URL base = ((HTMLDocument)elem.getDocument()).getBase(); 375 try { 376 if (!createdComponent) { 377 return; 378 } 379 380 Object postData = movePostData(htmlPane, null); 381 src = new URL(base, srcAtt); 382 if (oldPage.equals(src) && (src.getRef() == null) && (postData == null)) { 383 return; 384 } 385 386 htmlPane.setPage(src); 387 Document newDoc = htmlPane.getDocument(); 388 if (newDoc instanceof HTMLDocument) { 389 ((HTMLDocument)newDoc).setFrameDocumentState(true); 390 } 391 } catch (MalformedURLException e1) { 392 // Need a way to handle exceptions 393 //e1.printStackTrace(); 394 } catch (IOException e2) { 395 // Need a way to handle exceptions 396 //e2.printStackTrace(); 397 } 398 } 399 400 /** 401 * Move POST data from temporary storage into the target document property. 402 * 403 * @return the POST data or null if no data found 404 */ 405 private Object movePostData(JEditorPane targetPane, String frameName) { 406 Object postData = null; 407 JEditorPane p = getOutermostJEditorPane(); 408 if (p != null) { 409 if (frameName == null) { 410 frameName = (String) getElement().getAttributes().getAttribute( 411 HTML.Attribute.NAME); 412 } 413 if (frameName != null) { 414 String propName = FormView.PostDataProperty + "." + frameName; 415 Document d = p.getDocument(); 416 postData = d.getProperty(propName); 417 if (postData != null) { 418 targetPane.getDocument().putProperty( 419 FormView.PostDataProperty, postData); 420 d.putProperty(propName, null); 421 } 422 } 423 } 424 425 return postData; 426 } 427 428 /** 429 * Determines the minimum span for this view along an 430 * axis. 431 * 432 * @param axis may be either <code>View.X_AXIS</code> or 433 * <code>View.Y_AXIS</code> 434 * @return the preferred span; given that we do not 435 * support resizing of frames, the minimum span returned 436 * is the same as the preferred span 437 * 438 */ 439 public float getMinimumSpan(int axis) { 440 return 5; 441 } 442 443 /** 444 * Determines the maximum span for this view along an 445 * axis. 446 * 447 * @param axis may be either <code>View.X_AXIS</code> or 448 * <code>View.Y_AXIS</code> 449 * @return the preferred span; given that we do not 450 * support resizing of frames, the maximum span returned 451 * is the same as the preferred span 452 * 453 */ 454 public float getMaximumSpan(int axis) { 455 return Integer.MAX_VALUE; 456 } 457 458 /** Editor pane rendering frame of HTML document 459 * It uses the same editor kits classes as outermost JEditorPane 460 */ 461 @SuppressWarnings("serial") // Superclass is not serializable across versions 462 class FrameEditorPane extends JEditorPane implements FrameEditorPaneTag { 463 public EditorKit getEditorKitForContentType(String type) { 464 EditorKit editorKit = super.getEditorKitForContentType(type); 465 JEditorPane outerMostJEditorPane = null; 466 if ((outerMostJEditorPane = getOutermostJEditorPane()) != null) { 467 EditorKit inheritedEditorKit = outerMostJEditorPane.getEditorKitForContentType(type); 468 if (! editorKit.getClass().equals(inheritedEditorKit.getClass())) { 469 editorKit = (EditorKit) inheritedEditorKit.clone(); 470 setEditorKitForContentType(type, editorKit); 471 } 472 } 473 return editorKit; 474 } 475 476 FrameView getFrameView() { 477 return FrameView.this; 478 } 479 } 480 }