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 @SuppressWarnings("deprecation") 222 private void createScrollPane() { 223 AttributeSet attributes = getElement().getAttributes(); 224 String scrolling = (String)attributes.getAttribute(HTML.Attribute.SCROLLING); 225 if (scrolling == null) { 226 scrolling = "auto"; 227 } 228 229 if (!scrolling.equals("no")) { 230 if (scrolling.equals("yes")) { 231 scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 232 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); 233 } else { 234 // scrollbars will be displayed if needed 235 // 236 scroller = new JScrollPane(); 237 } 238 } else { 239 scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_NEVER, 240 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 241 } 242 243 JViewport vp = scroller.getViewport(); 244 vp.add(htmlPane); 245 vp.setBackingStoreEnabled(true); 246 scroller.setMinimumSize(new Dimension(5,5)); 247 scroller.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); 248 } 249 250 251 /** 252 * Finds the outermost FrameSetView. It then 253 * returns that FrameSetView's container. 254 */ 255 JEditorPane getOutermostJEditorPane() { 256 257 View parent = getParent(); 258 FrameSetView frameSetView = null; 259 while (parent != null) { 260 if (parent instanceof FrameSetView) { 261 frameSetView = (FrameSetView)parent; 262 } 263 parent = parent.getParent(); 264 } 265 if (frameSetView != null) { 266 return (JEditorPane)frameSetView.getContainer(); 267 } 268 return null; 269 } 270 271 272 /** 273 * Returns true if this frame is contained within 274 * a nested frameset. 275 */ 276 private boolean inNestedFrameSet() { 277 FrameSetView parent = (FrameSetView)getParent(); 278 return (parent.getParent() instanceof FrameSetView); 279 } 280 281 282 /** 283 * Notification of a change relative to a 284 * hyperlink. This method searches for the outermost 285 * JEditorPane, and then fires an HTMLFrameHyperlinkEvent 286 * to that frame. In addition, if the target is _parent, 287 * and there is not nested framesets then the target is 288 * reset to _top. If the target is _top, in addition to 289 * firing the event to the outermost JEditorPane, this 290 * method also invokes the setPage() method and explicitly 291 * replaces the current document with the destination url. 292 * 293 * @param HyperlinkEvent 294 */ 295 public void hyperlinkUpdate(HyperlinkEvent evt) { 296 297 JEditorPane c = getOutermostJEditorPane(); 298 if (c == null) { 299 return; 300 } 301 302 if (!(evt instanceof HTMLFrameHyperlinkEvent)) { 303 c.fireHyperlinkUpdate(evt); 304 return; 305 } 306 307 HTMLFrameHyperlinkEvent e = (HTMLFrameHyperlinkEvent)evt; 308 309 if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { 310 String target = e.getTarget(); 311 String postTarget = target; 312 313 if (target.equals("_parent") && !inNestedFrameSet()){ 314 target = "_top"; 315 } 316 317 if (evt instanceof FormSubmitEvent) { 318 HTMLEditorKit kit = (HTMLEditorKit)c.getEditorKit(); 319 if (kit != null && kit.isAutoFormSubmission()) { 320 if (target.equals("_top")) { 321 try { 322 movePostData(c, postTarget); 323 c.setPage(e.getURL()); 324 } catch (IOException ex) { 325 // Need a way to handle exceptions 326 } 327 } else { 328 HTMLDocument doc = (HTMLDocument)c.getDocument(); 329 doc.processHTMLFrameHyperlinkEvent(e); 330 } 331 } else { 332 c.fireHyperlinkUpdate(evt); 333 } 334 return; 335 } 336 337 if (target.equals("_top")) { 338 try { 339 c.setPage(e.getURL()); 340 } catch (IOException ex) { 341 // Need a way to handle exceptions 342 // ex.printStackTrace(); 343 } 344 } 345 if (!c.isEditable()) { 346 c.fireHyperlinkUpdate(new HTMLFrameHyperlinkEvent(c, 347 e.getEventType(), 348 e.getURL(), 349 e.getDescription(), 350 getElement(), 351 e.getInputEvent(), 352 target)); 353 } 354 } 355 } 356 357 /** 358 * Gives notification from the document that attributes were changed 359 * in a location that this view is responsible for. Currently this view 360 * handles changes to its SRC attribute. 361 * 362 * @param e the change information from the associated document 363 * @param a the current allocation of the view 364 * @param f the factory to use to rebuild if the view has children 365 * 366 */ 367 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) { 368 369 Element elem = getElement(); 370 AttributeSet attributes = elem.getAttributes(); 371 372 URL oldPage = src; 373 374 String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC); 375 URL base = ((HTMLDocument)elem.getDocument()).getBase(); 376 try { 377 if (!createdComponent) { 378 return; 379 } 380 381 Object postData = movePostData(htmlPane, null); 382 src = new URL(base, srcAtt); 383 if (oldPage.equals(src) && (src.getRef() == null) && (postData == null)) { 384 return; 385 } 386 387 htmlPane.setPage(src); 388 Document newDoc = htmlPane.getDocument(); 389 if (newDoc instanceof HTMLDocument) { 390 ((HTMLDocument)newDoc).setFrameDocumentState(true); 391 } 392 } catch (MalformedURLException e1) { 393 // Need a way to handle exceptions 394 //e1.printStackTrace(); 395 } catch (IOException e2) { 396 // Need a way to handle exceptions 397 //e2.printStackTrace(); 398 } 399 } 400 401 /** 402 * Move POST data from temporary storage into the target document property. 403 * 404 * @return the POST data or null if no data found 405 */ 406 private Object movePostData(JEditorPane targetPane, String frameName) { 407 Object postData = null; 408 JEditorPane p = getOutermostJEditorPane(); 409 if (p != null) { 410 if (frameName == null) { 411 frameName = (String) getElement().getAttributes().getAttribute( 412 HTML.Attribute.NAME); 413 } 414 if (frameName != null) { 415 String propName = FormView.PostDataProperty + "." + frameName; 416 Document d = p.getDocument(); 417 postData = d.getProperty(propName); 418 if (postData != null) { 419 targetPane.getDocument().putProperty( 420 FormView.PostDataProperty, postData); 421 d.putProperty(propName, null); 422 } 423 } 424 } 425 426 return postData; 427 } 428 429 /** 430 * Determines the minimum span for this view along an 431 * axis. 432 * 433 * @param axis may be either <code>View.X_AXIS</code> or 434 * <code>View.Y_AXIS</code> 435 * @return the preferred span; given that we do not 436 * support resizing of frames, the minimum span returned 437 * is the same as the preferred span 438 * 439 */ 440 public float getMinimumSpan(int axis) { 441 return 5; 442 } 443 444 /** 445 * Determines the maximum span for this view along an 446 * axis. 447 * 448 * @param axis may be either <code>View.X_AXIS</code> or 449 * <code>View.Y_AXIS</code> 450 * @return the preferred span; given that we do not 451 * support resizing of frames, the maximum span returned 452 * is the same as the preferred span 453 * 454 */ 455 public float getMaximumSpan(int axis) { 456 return Integer.MAX_VALUE; 457 } 458 459 /** Editor pane rendering frame of HTML document 460 * It uses the same editor kits classes as outermost JEditorPane 461 */ 462 @SuppressWarnings("serial") // Superclass is not serializable across versions 463 class FrameEditorPane extends JEditorPane implements FrameEditorPaneTag { 464 public EditorKit getEditorKitForContentType(String type) { 465 EditorKit editorKit = super.getEditorKitForContentType(type); 466 JEditorPane outerMostJEditorPane = null; 467 if ((outerMostJEditorPane = getOutermostJEditorPane()) != null) { 468 EditorKit inheritedEditorKit = outerMostJEditorPane.getEditorKitForContentType(type); 469 if (! editorKit.getClass().equals(inheritedEditorKit.getClass())) { 470 editorKit = (EditorKit) inheritedEditorKit.clone(); 471 setEditorKitForContentType(type, editorKit); 472 } 473 } 474 return editorKit; 475 } 476 477 FrameView getFrameView() { 478 return FrameView.this; 479 } 480 } 481 }