blob: 08b6a64d0637db292b7872cf868794b274024deb [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2000 Simon Hausmann (hausmann@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
* Copyright (C) 2004, 2006, 2008, 2009 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "HTMLFrameElementBase.h"
#include "Attribute.h"
#include "Document.h"
#include "EventNames.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "FrameTree.h"
#include "FrameView.h"
#include "HTMLFrameSetElement.h"
#include "HTMLNames.h"
#include "HTMLParserIdioms.h"
#include "KURL.h"
#include "Page.h"
#include "RenderEmbeddedObject.h"
#include "RenderFrame.h"
#include "ScriptController.h"
#include "ScriptEventListener.h"
#include "Settings.h"
namespace WebCore {
using namespace HTMLNames;
HTMLFrameElementBase::HTMLFrameElementBase(const QualifiedName& tagName, Document* document)
: HTMLFrameOwnerElement(tagName, document)
, m_scrolling(ScrollbarAuto)
, m_marginWidth(-1)
, m_marginHeight(-1)
, m_checkInDocumentTimer(this, &HTMLFrameElementBase::checkInDocumentTimerFired)
, m_viewSource(false)
, m_remainsAliveOnRemovalFromTree(false)
{
}
bool HTMLFrameElementBase::isURLAllowed() const
{
if (m_URL.isEmpty())
return true;
const KURL& completeURL = document()->completeURL(m_URL);
if (protocolIsJavaScript(completeURL)) {
Document* contentDoc = this->contentDocument();
if (contentDoc && !ScriptController::canAccessFromCurrentOrigin(contentDoc->frame()))
return false;
}
if (Frame* parentFrame = document()->frame()) {
if (parentFrame->page()->frameCount() >= Page::maxNumberOfFrames)
return false;
}
// We allow one level of self-reference because some sites depend on that.
// But we don't allow more than one.
bool foundSelfReference = false;
for (Frame* frame = document()->frame(); frame; frame = frame->tree()->parent()) {
if (equalIgnoringFragmentIdentifier(frame->document()->url(), completeURL)) {
if (foundSelfReference)
return false;
foundSelfReference = true;
}
}
return true;
}
void HTMLFrameElementBase::openURL(bool lockHistory, bool lockBackForwardList)
{
if (!isURLAllowed())
return;
if (m_URL.isEmpty())
m_URL = blankURL().string();
Frame* parentFrame = document()->frame();
if (!parentFrame)
return;
parentFrame->loader()->subframeLoader()->requestFrame(this, m_URL, m_frameName, lockHistory, lockBackForwardList);
if (contentFrame())
contentFrame()->setInViewSourceMode(viewSourceMode());
}
void HTMLFrameElementBase::parseMappedAttribute(Attribute* attr)
{
if (attr->name() == srcAttr)
setLocation(stripLeadingAndTrailingHTMLSpaces(attr->value()));
else if (isIdAttributeName(attr->name())) {
// Important to call through to base for the id attribute so the hasID bit gets set.
HTMLFrameOwnerElement::parseMappedAttribute(attr);
m_frameName = attr->value();
} else if (attr->name() == nameAttr) {
m_frameName = attr->value();
// FIXME: If we are already attached, this doesn't actually change the frame's name.
// FIXME: If we are already attached, this doesn't check for frame name
// conflicts and generate a unique frame name.
} else if (attr->name() == marginwidthAttr) {
m_marginWidth = attr->value().toInt();
// FIXME: If we are already attached, this has no effect.
} else if (attr->name() == marginheightAttr) {
m_marginHeight = attr->value().toInt();
// FIXME: If we are already attached, this has no effect.
} else if (attr->name() == scrollingAttr) {
// Auto and yes both simply mean "allow scrolling." No means "don't allow scrolling."
if (equalIgnoringCase(attr->value(), "auto") || equalIgnoringCase(attr->value(), "yes"))
m_scrolling = document()->frameElementsShouldIgnoreScrolling() ? ScrollbarAlwaysOff : ScrollbarAuto;
else if (equalIgnoringCase(attr->value(), "no"))
m_scrolling = ScrollbarAlwaysOff;
// FIXME: If we are already attached, this has no effect.
} else if (attr->name() == viewsourceAttr) {
m_viewSource = !attr->isNull();
if (contentFrame())
contentFrame()->setInViewSourceMode(viewSourceMode());
} else if (attr->name() == onloadAttr)
setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr));
else if (attr->name() == onbeforeloadAttr)
setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr));
else if (attr->name() == onbeforeunloadAttr) {
// FIXME: should <frame> elements have beforeunload handlers?
setAttributeEventListener(eventNames().beforeunloadEvent, createAttributeEventListener(this, attr));
} else
HTMLFrameOwnerElement::parseMappedAttribute(attr);
}
void HTMLFrameElementBase::setNameAndOpenURL()
{
m_frameName = getAttribute(nameAttr);
if (m_frameName.isNull())
m_frameName = getIdAttribute();
openURL();
}
void HTMLFrameElementBase::updateOnReparenting()
{
ASSERT(m_remainsAliveOnRemovalFromTree);
if (Frame* frame = contentFrame())
frame->transferChildFrameToNewDocument();
}
void HTMLFrameElementBase::insertedIntoDocument()
{
HTMLFrameOwnerElement::insertedIntoDocument();
if (m_remainsAliveOnRemovalFromTree) {
updateOnReparenting();
setRemainsAliveOnRemovalFromTree(false);
return;
}
// DocumentFragments don't kick of any loads.
if (!document()->frame())
return;
// Loads may cause synchronous javascript execution (e.g. beforeload or
// src=javascript), which could try to access the renderer before the normal
// parser machinery would call lazyAttach() and set us as needing style
// resolve. Any code which expects this to be attached will resolve style
// before using renderer(), so this will make sure we attach in time.
// FIXME: Normally lazyAttach marks the renderer as attached(), but we don't
// want to do that here, as as callers expect to call attach() right after
// this and attach() will ASSERT(!attached())
ASSERT(!renderer()); // This recalc is unecessary if we already have a renderer.
lazyAttach(DoNotSetAttached);
setNameAndOpenURL();
}
void HTMLFrameElementBase::attach()
{
HTMLFrameOwnerElement::attach();
if (RenderPart* part = renderPart()) {
if (Frame* frame = contentFrame())
part->setWidget(frame->view());
}
}
KURL HTMLFrameElementBase::location() const
{
return document()->completeURL(getAttribute(srcAttr));
}
void HTMLFrameElementBase::setLocation(const String& str)
{
Settings* settings = document()->settings();
if (settings && settings->needsAcrobatFrameReloadingQuirk() && m_URL == str)
return;
m_URL = AtomicString(str);
if (inDocument())
openURL(false, false);
}
bool HTMLFrameElementBase::supportsFocus() const
{
return true;
}
void HTMLFrameElementBase::setFocus(bool received)
{
HTMLFrameOwnerElement::setFocus(received);
if (Page* page = document()->page()) {
if (received)
page->focusController()->setFocusedFrame(contentFrame());
else if (page->focusController()->focusedFrame() == contentFrame()) // Focus may have already been given to another frame, don't take it away.
page->focusController()->setFocusedFrame(0);
}
}
bool HTMLFrameElementBase::isURLAttribute(Attribute *attr) const
{
return attr->name() == srcAttr;
}
int HTMLFrameElementBase::width() const
{
document()->updateLayoutIgnorePendingStylesheets();
if (!renderBox())
return 0;
return renderBox()->width();
}
int HTMLFrameElementBase::height() const
{
document()->updateLayoutIgnorePendingStylesheets();
if (!renderBox())
return 0;
return renderBox()->height();
}
void HTMLFrameElementBase::setRemainsAliveOnRemovalFromTree(bool value)
{
m_remainsAliveOnRemovalFromTree = value;
// There is a possibility that JS will do document.adoptNode() on this element but will not insert it into the tree.
// Start the async timer that is normally stopped by attach(). If it's not stopped and fires, it'll unload the frame.
if (value)
m_checkInDocumentTimer.startOneShot(0);
else
m_checkInDocumentTimer.stop();
}
void HTMLFrameElementBase::checkInDocumentTimerFired(Timer<HTMLFrameElementBase>*)
{
ASSERT(!attached());
ASSERT(m_remainsAliveOnRemovalFromTree);
m_remainsAliveOnRemovalFromTree = false;
willRemove();
}
void HTMLFrameElementBase::willRemove()
{
if (m_remainsAliveOnRemovalFromTree)
return;
HTMLFrameOwnerElement::willRemove();
}
#if ENABLE(FULLSCREEN_API)
bool HTMLFrameElementBase::allowFullScreen() const
{
return hasAttribute(webkitallowfullscreenAttr);
}
#endif
} // namespace WebCore