1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2000 Simon Hausmann (hausmann (at) kde.org) 5 * (C) 2001 Dirk Mueller (mueller (at) kde.org) 6 * Copyright (C) 2004, 2006, 2008, 2009 Apple Inc. All rights reserved. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 */ 23 24 #include "config.h" 25 #include "HTMLFrameElementBase.h" 26 27 #include "CSSHelper.h" 28 #include "Document.h" 29 #include "EventNames.h" 30 #include "FocusController.h" 31 #include "Frame.h" 32 #include "FrameLoader.h" 33 #include "FrameTree.h" 34 #include "FrameView.h" 35 #include "HTMLFrameSetElement.h" 36 #include "HTMLNames.h" 37 #include "ScriptEventListener.h" 38 #include "KURL.h" 39 #include "MappedAttribute.h" 40 #include "Page.h" 41 #include "RenderFrame.h" 42 #include "Settings.h" 43 44 namespace WebCore { 45 46 using namespace HTMLNames; 47 48 HTMLFrameElementBase::HTMLFrameElementBase(const QualifiedName& tagName, Document* document) 49 : HTMLFrameOwnerElement(tagName, document) 50 , m_scrolling(ScrollbarAuto) 51 , m_marginWidth(-1) 52 , m_marginHeight(-1) 53 , m_checkAttachedTimer(this, &HTMLFrameElementBase::checkAttachedTimerFired) 54 , m_viewSource(false) 55 , m_shouldOpenURLAfterAttach(false) 56 , m_remainsAliveOnRemovalFromTree(false) 57 { 58 } 59 60 bool HTMLFrameElementBase::isURLAllowed() const 61 { 62 if (m_URL.isEmpty()) 63 return true; 64 65 const KURL& completeURL = document()->completeURL(m_URL); 66 67 // Don't allow more than 200 total frames in a set. This seems 68 // like a reasonable upper bound, and otherwise mutually recursive 69 // frameset pages can quickly bring the program to its knees with 70 // exponential growth in the number of frames. 71 // FIXME: This limit could be higher, but because WebKit has some 72 // algorithms that happen while loading which appear to be N^2 or 73 // worse in the number of frames, we'll keep it at 200 for now. 74 if (Frame* parentFrame = document()->frame()) { 75 if (parentFrame->page()->frameCount() > 200) 76 return false; 77 } 78 79 // We allow one level of self-reference because some sites depend on that. 80 // But we don't allow more than one. 81 bool foundSelfReference = false; 82 for (Frame* frame = document()->frame(); frame; frame = frame->tree()->parent()) { 83 if (equalIgnoringFragmentIdentifier(frame->loader()->url(), completeURL)) { 84 if (foundSelfReference) 85 return false; 86 foundSelfReference = true; 87 } 88 } 89 90 return true; 91 } 92 93 void HTMLFrameElementBase::openURL() 94 { 95 ASSERT(!m_frameName.isEmpty()); 96 97 if (!isURLAllowed()) 98 return; 99 100 if (m_URL.isEmpty()) 101 m_URL = blankURL().string(); 102 103 Frame* parentFrame = document()->frame(); 104 if (!parentFrame) 105 return; 106 107 parentFrame->loader()->requestFrame(this, m_URL, m_frameName); 108 if (contentFrame()) 109 contentFrame()->setInViewSourceMode(viewSourceMode()); 110 } 111 112 void HTMLFrameElementBase::parseMappedAttribute(MappedAttribute *attr) 113 { 114 if (attr->name() == srcAttr) 115 setLocation(deprecatedParseURL(attr->value())); 116 else if (attr->name() == idAttributeName()) { 117 // Important to call through to base for the id attribute so the hasID bit gets set. 118 HTMLFrameOwnerElement::parseMappedAttribute(attr); 119 m_frameName = attr->value(); 120 } else if (attr->name() == nameAttr) { 121 m_frameName = attr->value(); 122 // FIXME: If we are already attached, this doesn't actually change the frame's name. 123 // FIXME: If we are already attached, this doesn't check for frame name 124 // conflicts and generate a unique frame name. 125 } else if (attr->name() == marginwidthAttr) { 126 m_marginWidth = attr->value().toInt(); 127 // FIXME: If we are already attached, this has no effect. 128 } else if (attr->name() == marginheightAttr) { 129 m_marginHeight = attr->value().toInt(); 130 // FIXME: If we are already attached, this has no effect. 131 } else if (attr->name() == scrollingAttr) { 132 // Auto and yes both simply mean "allow scrolling." No means "don't allow scrolling." 133 if (equalIgnoringCase(attr->value(), "auto") || equalIgnoringCase(attr->value(), "yes")) 134 m_scrolling = document()->frameElementsShouldIgnoreScrolling() ? ScrollbarAlwaysOff : ScrollbarAuto; 135 else if (equalIgnoringCase(attr->value(), "no")) 136 m_scrolling = ScrollbarAlwaysOff; 137 // FIXME: If we are already attached, this has no effect. 138 } else if (attr->name() == viewsourceAttr) { 139 m_viewSource = !attr->isNull(); 140 if (contentFrame()) 141 contentFrame()->setInViewSourceMode(viewSourceMode()); 142 } else if (attr->name() == onloadAttr) 143 setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); 144 else if (attr->name() == onbeforeloadAttr) 145 setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); 146 else if (attr->name() == onbeforeunloadAttr) { 147 // FIXME: should <frame> elements have beforeunload handlers? 148 setAttributeEventListener(eventNames().beforeunloadEvent, createAttributeEventListener(this, attr)); 149 } else 150 HTMLFrameOwnerElement::parseMappedAttribute(attr); 151 } 152 153 void HTMLFrameElementBase::setNameAndOpenURL() 154 { 155 m_frameName = getAttribute(nameAttr); 156 if (m_frameName.isNull()) 157 m_frameName = getAttribute(idAttributeName()); 158 159 if (Frame* parentFrame = document()->frame()) 160 m_frameName = parentFrame->tree()->uniqueChildName(m_frameName); 161 162 openURL(); 163 } 164 165 void HTMLFrameElementBase::setNameAndOpenURLCallback(Node* n) 166 { 167 static_cast<HTMLFrameElementBase*>(n)->setNameAndOpenURL(); 168 } 169 170 void HTMLFrameElementBase::insertedIntoDocument() 171 { 172 HTMLFrameOwnerElement::insertedIntoDocument(); 173 174 // We delay frame loading until after the render tree is fully constructed. 175 // Othewise, a synchronous load that executed JavaScript would see incorrect 176 // (0) values for the frame's renderer-dependent properties, like width. 177 m_shouldOpenURLAfterAttach = true; 178 } 179 180 void HTMLFrameElementBase::removedFromDocument() 181 { 182 m_shouldOpenURLAfterAttach = false; 183 184 HTMLFrameOwnerElement::removedFromDocument(); 185 } 186 187 void HTMLFrameElementBase::attach() 188 { 189 if (m_shouldOpenURLAfterAttach) { 190 m_shouldOpenURLAfterAttach = false; 191 if (!m_remainsAliveOnRemovalFromTree) 192 queuePostAttachCallback(&HTMLFrameElementBase::setNameAndOpenURLCallback, this); 193 } 194 195 setRemainsAliveOnRemovalFromTree(false); 196 197 HTMLFrameOwnerElement::attach(); 198 199 if (RenderPart* renderPart = toRenderPart(renderer())) { 200 if (Frame* frame = contentFrame()) 201 renderPart->setWidget(frame->view()); 202 } 203 } 204 205 KURL HTMLFrameElementBase::location() const 206 { 207 return document()->completeURL(getAttribute(srcAttr)); 208 } 209 210 void HTMLFrameElementBase::setLocation(const String& str) 211 { 212 Settings* settings = document()->settings(); 213 if (settings && settings->needsAcrobatFrameReloadingQuirk() && m_URL == str) 214 return; 215 216 m_URL = AtomicString(str); 217 218 if (inDocument()) 219 openURL(); 220 } 221 222 bool HTMLFrameElementBase::supportsFocus() const 223 { 224 return true; 225 } 226 227 void HTMLFrameElementBase::setFocus(bool received) 228 { 229 HTMLFrameOwnerElement::setFocus(received); 230 if (Page* page = document()->page()) 231 page->focusController()->setFocusedFrame(received ? contentFrame() : 0); 232 } 233 234 bool HTMLFrameElementBase::isURLAttribute(Attribute *attr) const 235 { 236 return attr->name() == srcAttr; 237 } 238 239 int HTMLFrameElementBase::width() const 240 { 241 if (!renderer()) 242 return 0; 243 244 document()->updateLayoutIgnorePendingStylesheets(); 245 return toRenderBox(renderer())->width(); 246 } 247 248 int HTMLFrameElementBase::height() const 249 { 250 if (!renderer()) 251 return 0; 252 253 document()->updateLayoutIgnorePendingStylesheets(); 254 return toRenderBox(renderer())->height(); 255 } 256 257 void HTMLFrameElementBase::setRemainsAliveOnRemovalFromTree(bool value) 258 { 259 m_remainsAliveOnRemovalFromTree = value; 260 261 // There is a possibility that JS will do document.adoptNode() on this element but will not insert it into the tree. 262 // Start the async timer that is normally stopped by attach(). If it's not stopped and fires, it'll unload the frame. 263 if (value) 264 m_checkAttachedTimer.startOneShot(0); 265 else 266 m_checkAttachedTimer.stop(); 267 } 268 269 void HTMLFrameElementBase::checkAttachedTimerFired(Timer<HTMLFrameElementBase>*) 270 { 271 ASSERT(!attached()); 272 ASSERT(m_remainsAliveOnRemovalFromTree); 273 274 m_remainsAliveOnRemovalFromTree = false; 275 willRemove(); 276 } 277 278 void HTMLFrameElementBase::willRemove() 279 { 280 if (m_remainsAliveOnRemovalFromTree) 281 return; 282 283 HTMLFrameOwnerElement::willRemove(); 284 } 285 286 } // namespace WebCore 287