1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * (C) 2000 Simon Hausmann (hausmann@kde.org) 5 * (C) 2001 Dirk Mueller (mueller@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 "Attribute.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 "HTMLParserIdioms.h" 38#include "KURL.h" 39#include "Page.h" 40#include "RenderEmbeddedObject.h" 41#include "RenderFrame.h" 42#include "ScriptController.h" 43#include "ScriptEventListener.h" 44#include "Settings.h" 45 46namespace WebCore { 47 48using namespace HTMLNames; 49 50HTMLFrameElementBase::HTMLFrameElementBase(const QualifiedName& tagName, Document* document) 51 : HTMLFrameOwnerElement(tagName, document) 52 , m_scrolling(ScrollbarAuto) 53 , m_marginWidth(-1) 54 , m_marginHeight(-1) 55 , m_checkInDocumentTimer(this, &HTMLFrameElementBase::checkInDocumentTimerFired) 56 , m_viewSource(false) 57 , m_remainsAliveOnRemovalFromTree(false) 58{ 59} 60 61bool HTMLFrameElementBase::isURLAllowed() const 62{ 63 if (m_URL.isEmpty()) 64 return true; 65 66 const KURL& completeURL = document()->completeURL(m_URL); 67 68 if (protocolIsJavaScript(completeURL)) { 69 Document* contentDoc = this->contentDocument(); 70 if (contentDoc && !ScriptController::canAccessFromCurrentOrigin(contentDoc->frame())) 71 return false; 72 } 73 74 if (Frame* parentFrame = document()->frame()) { 75 if (parentFrame->page()->frameCount() >= Page::maxNumberOfFrames) 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->document()->url(), completeURL)) { 84 if (foundSelfReference) 85 return false; 86 foundSelfReference = true; 87 } 88 } 89 90 return true; 91} 92 93void HTMLFrameElementBase::openURL(bool lockHistory, bool lockBackForwardList) 94{ 95 if (!isURLAllowed()) 96 return; 97 98 if (m_URL.isEmpty()) 99 m_URL = blankURL().string(); 100 101 Frame* parentFrame = document()->frame(); 102 if (!parentFrame) 103 return; 104 105 parentFrame->loader()->subframeLoader()->requestFrame(this, m_URL, m_frameName, lockHistory, lockBackForwardList); 106 if (contentFrame()) 107 contentFrame()->setInViewSourceMode(viewSourceMode()); 108} 109 110void HTMLFrameElementBase::parseMappedAttribute(Attribute* attr) 111{ 112 if (attr->name() == srcAttr) 113 setLocation(stripLeadingAndTrailingHTMLSpaces(attr->value())); 114 else if (isIdAttributeName(attr->name())) { 115 // Important to call through to base for the id attribute so the hasID bit gets set. 116 HTMLFrameOwnerElement::parseMappedAttribute(attr); 117 m_frameName = attr->value(); 118 } else if (attr->name() == nameAttr) { 119 m_frameName = attr->value(); 120 // FIXME: If we are already attached, this doesn't actually change the frame's name. 121 // FIXME: If we are already attached, this doesn't check for frame name 122 // conflicts and generate a unique frame name. 123 } else if (attr->name() == marginwidthAttr) { 124 m_marginWidth = attr->value().toInt(); 125 // FIXME: If we are already attached, this has no effect. 126 } else if (attr->name() == marginheightAttr) { 127 m_marginHeight = attr->value().toInt(); 128 // FIXME: If we are already attached, this has no effect. 129 } else if (attr->name() == scrollingAttr) { 130 // Auto and yes both simply mean "allow scrolling." No means "don't allow scrolling." 131 if (equalIgnoringCase(attr->value(), "auto") || equalIgnoringCase(attr->value(), "yes")) 132 m_scrolling = document()->frameElementsShouldIgnoreScrolling() ? ScrollbarAlwaysOff : ScrollbarAuto; 133 else if (equalIgnoringCase(attr->value(), "no")) 134 m_scrolling = ScrollbarAlwaysOff; 135 // FIXME: If we are already attached, this has no effect. 136 } else if (attr->name() == viewsourceAttr) { 137 m_viewSource = !attr->isNull(); 138 if (contentFrame()) 139 contentFrame()->setInViewSourceMode(viewSourceMode()); 140 } else if (attr->name() == onloadAttr) 141 setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); 142 else if (attr->name() == onbeforeloadAttr) 143 setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); 144 else if (attr->name() == onbeforeunloadAttr) { 145 // FIXME: should <frame> elements have beforeunload handlers? 146 setAttributeEventListener(eventNames().beforeunloadEvent, createAttributeEventListener(this, attr)); 147 } else 148 HTMLFrameOwnerElement::parseMappedAttribute(attr); 149} 150 151void HTMLFrameElementBase::setNameAndOpenURL() 152{ 153 m_frameName = getAttribute(nameAttr); 154 if (m_frameName.isNull()) 155 m_frameName = getIdAttribute(); 156 openURL(); 157} 158 159void HTMLFrameElementBase::updateOnReparenting() 160{ 161 ASSERT(m_remainsAliveOnRemovalFromTree); 162 163 if (Frame* frame = contentFrame()) 164 frame->transferChildFrameToNewDocument(); 165} 166 167void HTMLFrameElementBase::insertedIntoDocument() 168{ 169 HTMLFrameOwnerElement::insertedIntoDocument(); 170 171 if (m_remainsAliveOnRemovalFromTree) { 172 updateOnReparenting(); 173 setRemainsAliveOnRemovalFromTree(false); 174 return; 175 } 176 // DocumentFragments don't kick of any loads. 177 if (!document()->frame()) 178 return; 179 180 // Loads may cause synchronous javascript execution (e.g. beforeload or 181 // src=javascript), which could try to access the renderer before the normal 182 // parser machinery would call lazyAttach() and set us as needing style 183 // resolve. Any code which expects this to be attached will resolve style 184 // before using renderer(), so this will make sure we attach in time. 185 // FIXME: Normally lazyAttach marks the renderer as attached(), but we don't 186 // want to do that here, as as callers expect to call attach() right after 187 // this and attach() will ASSERT(!attached()) 188 ASSERT(!renderer()); // This recalc is unecessary if we already have a renderer. 189 lazyAttach(DoNotSetAttached); 190 setNameAndOpenURL(); 191} 192 193void HTMLFrameElementBase::attach() 194{ 195 HTMLFrameOwnerElement::attach(); 196 197 if (RenderPart* part = renderPart()) { 198 if (Frame* frame = contentFrame()) 199 part->setWidget(frame->view()); 200 } 201} 202 203KURL HTMLFrameElementBase::location() const 204{ 205 return document()->completeURL(getAttribute(srcAttr)); 206} 207 208void HTMLFrameElementBase::setLocation(const String& str) 209{ 210 Settings* settings = document()->settings(); 211 if (settings && settings->needsAcrobatFrameReloadingQuirk() && m_URL == str) 212 return; 213 214 m_URL = AtomicString(str); 215 216 if (inDocument()) 217 openURL(false, false); 218} 219 220bool HTMLFrameElementBase::supportsFocus() const 221{ 222 return true; 223} 224 225void HTMLFrameElementBase::setFocus(bool received) 226{ 227 HTMLFrameOwnerElement::setFocus(received); 228 if (Page* page = document()->page()) { 229 if (received) 230 page->focusController()->setFocusedFrame(contentFrame()); 231 else if (page->focusController()->focusedFrame() == contentFrame()) // Focus may have already been given to another frame, don't take it away. 232 page->focusController()->setFocusedFrame(0); 233 } 234} 235 236bool HTMLFrameElementBase::isURLAttribute(Attribute *attr) const 237{ 238 return attr->name() == srcAttr; 239} 240 241int HTMLFrameElementBase::width() const 242{ 243 document()->updateLayoutIgnorePendingStylesheets(); 244 if (!renderBox()) 245 return 0; 246 return renderBox()->width(); 247} 248 249int HTMLFrameElementBase::height() const 250{ 251 document()->updateLayoutIgnorePendingStylesheets(); 252 if (!renderBox()) 253 return 0; 254 return renderBox()->height(); 255} 256 257void 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_checkInDocumentTimer.startOneShot(0); 265 else 266 m_checkInDocumentTimer.stop(); 267} 268 269void HTMLFrameElementBase::checkInDocumentTimerFired(Timer<HTMLFrameElementBase>*) 270{ 271 ASSERT(!attached()); 272 ASSERT(m_remainsAliveOnRemovalFromTree); 273 274 m_remainsAliveOnRemovalFromTree = false; 275 willRemove(); 276} 277 278void HTMLFrameElementBase::willRemove() 279{ 280 if (m_remainsAliveOnRemovalFromTree) 281 return; 282 283 HTMLFrameOwnerElement::willRemove(); 284} 285 286#if ENABLE(FULLSCREEN_API) 287bool HTMLFrameElementBase::allowFullScreen() const 288{ 289 return hasAttribute(webkitallowfullscreenAttr); 290} 291#endif 292 293} // namespace WebCore 294