/**
* This file is part of the DOM implementation for KDE.
*
* 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 Apple Computer, Inc.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include "HTMLFrameElement.h"
#include "csshelper.h"
#include "Document.h"
#include "EventNames.h"
#include "Frame.h"
#include "FrameTree.h"
#include "FrameView.h"
#include "HTMLNames.h"
#include "HTMLFrameSetElement.h"
#include "Page.h"
#include "RenderFrame.h"
namespace WebCore {
using namespace EventNames;
using namespace HTMLNames;
HTMLFrameElement::HTMLFrameElement(Document *doc)
: HTMLElement(frameTag, doc)
{
init();
}
HTMLFrameElement::HTMLFrameElement(const QualifiedName& tagName, Document *doc)
: HTMLElement(tagName, doc)
{
init();
}
void HTMLFrameElement::init()
{
m_frameBorder = true;
m_frameBorderSet = false;
m_marginWidth = -1;
m_marginHeight = -1;
m_scrolling = ScrollBarAuto;
m_noResize = false;
m_viewSource = false;
}
HTMLFrameElement::~HTMLFrameElement()
{
}
bool HTMLFrameElement::isURLAllowed(const AtomicString &URLString) const
{
if (URLString.isEmpty())
return true;
FrameView* w = document()->view();
if (!w)
return false;
KURL newURL(document()->completeURL(URLString.deprecatedString()));
newURL.setRef(DeprecatedString::null);
// Don't allow more than 1000 total frames in a set. This seems
// like a reasonable upper bound, and otherwise mutually recursive
// frameset pages can quickly bring the program to its knees with
// exponential growth in the number of frames.
// FIXME: This limit could be higher, but WebKit has some
// algorithms that happen while loading which appear to be N^2 or
// worse in the number of frames
if (w->frame()->page()->frameCount() >= 200)
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 = w->frame(); frame; frame = frame->tree()->parent()) {
KURL frameURL = frame->url();
frameURL.setRef(DeprecatedString::null);
if (frameURL == newURL) {
if (foundSelfReference)
return false;
foundSelfReference = true;
}
}
return true;
}
void HTMLFrameElement::openURL()
{
FrameView *w = document()->view();
if (!w)
return;
AtomicString relativeURL = m_URL;
if (relativeURL.isEmpty())
relativeURL = "about:blank";
// Load the frame contents.
Frame* parentFrame = w->frame();
if (Frame* childFrame = parentFrame->tree()->child(m_name))
childFrame->openURL(document()->completeURL(relativeURL.deprecatedString()));
else
parentFrame->requestFrame(this, relativeURL, m_name);
}
void HTMLFrameElement::parseMappedAttribute(MappedAttribute *attr)
{
if (attr->name() == srcAttr)
setLocation(parseURL(attr->value()));
else if (attr->name() == idAttr) {
// Important to call through to base for the id attribute so the hasID bit gets set.
HTMLElement::parseMappedAttribute(attr);
m_name = attr->value();
} else if (attr->name() == nameAttr) {
m_name = 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() == frameborderAttr) {
m_frameBorder = attr->value().toInt();
m_frameBorderSet = !attr->isNull();
// FIXME: If we are already attached, this has no effect.
} 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() == noresizeAttr) {
m_noResize = true;
// 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 = 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) {
setHTMLEventListener(loadEvent, attr);
} else if (attr->name() == onbeforeunloadAttr) {
// FIXME: should elements have beforeunload handlers?
setHTMLEventListener(beforeunloadEvent, attr);
} else if (attr->name() == onunloadAttr) {
setHTMLEventListener(unloadEvent, attr);
} else
HTMLElement::parseMappedAttribute(attr);
}
bool HTMLFrameElement::rendererIsNeeded(RenderStyle *style)
{
// Ignore display: none.
return isURLAllowed(m_URL);
}
RenderObject *HTMLFrameElement::createRenderer(RenderArena *arena, RenderStyle *style)
{
return new (arena) RenderFrame(this);
}
void HTMLFrameElement::attach()
{
m_name = getAttribute(nameAttr);
if (m_name.isNull())
m_name = getAttribute(idAttr);
// inherit default settings from parent frameset
for (Node *node = parentNode(); node; node = node->parentNode())
if (node->hasTagName(framesetTag)) {
HTMLFrameSetElement* frameset = static_cast(node);
if (!m_frameBorderSet)
m_frameBorder = frameset->frameBorder();
if (!m_noResize)
m_noResize = frameset->noResize();
break;
}
HTMLElement::attach();
if (!renderer())
return;
Frame* frame = document()->frame();
if (!frame)
return;
AtomicString relativeURL = m_URL;
if (relativeURL.isEmpty())
relativeURL = "about:blank";
m_name = frame->tree()->uniqueChildName(m_name);
// load the frame contents
frame->requestFrame(this, relativeURL, m_name);
if (contentFrame())
contentFrame()->setInViewSourceMode(viewSourceMode());
}
void HTMLFrameElement::close()
{
Frame* frame = document()->frame();
if (renderer() && frame) {
if (Frame* childFrame = frame->tree()->child(m_name)) {
childFrame->frameDetached();
childFrame->disconnectOwnerElement();
}
}
}
void HTMLFrameElement::willRemove()
{
// close the frame and dissociate the renderer, but leave the
// node attached so that frame does not get re-attached before
// actually leaving the document. see
close();
if (renderer()) {
renderer()->destroy();
setRenderer(0);
}
HTMLElement::willRemove();
}
void HTMLFrameElement::detach()
{
close();
HTMLElement::detach();
}
void HTMLFrameElement::setLocation(const String& str)
{
m_URL = AtomicString(str);
if (!attached()) {
return;
}
// Handle the common case where we decided not to make a frame the first time.
// Detach and the let attach() decide again whether to make the frame for this URL.
if (!renderer() && !hasTagName(iframeTag)) {
detach();
attach();
return;
}
if (!isURLAllowed(m_URL))
return;
openURL();
}
bool HTMLFrameElement::isFocusable() const
{
return false;
}
void HTMLFrameElement::setFocus(bool received)
{
HTMLElement::setFocus(received);
RenderFrame *renderFrame = static_cast(renderer());
if (!renderFrame || !renderFrame->widget())
return;
if (received)
renderFrame->widget()->setFocus();
else
renderFrame->widget()->clearFocus();
}
Frame* HTMLFrameElement::contentFrame() const
{
// Start with the part that contains this element, our ownerDocument.
Frame* parentFrame = document()->frame();
if (!parentFrame)
return 0;
// Find the part for the subframe that this element represents.
return parentFrame->tree()->child(m_name);
}
Document* HTMLFrameElement::contentDocument() const
{
Frame* frame = contentFrame();
if (!frame)
return 0;
return frame->document();
}
bool HTMLFrameElement::isURLAttribute(Attribute *attr) const
{
return attr->name() == srcAttr;
}
String HTMLFrameElement::frameBorder() const
{
return getAttribute(frameborderAttr);
}
void HTMLFrameElement::setFrameBorder(const String &value)
{
setAttribute(frameborderAttr, value);
}
String HTMLFrameElement::longDesc() const
{
return getAttribute(longdescAttr);
}
void HTMLFrameElement::setLongDesc(const String &value)
{
setAttribute(longdescAttr, value);
}
String HTMLFrameElement::marginHeight() const
{
return getAttribute(marginheightAttr);
}
void HTMLFrameElement::setMarginHeight(const String &value)
{
setAttribute(marginheightAttr, value);
}
String HTMLFrameElement::marginWidth() const
{
return getAttribute(marginwidthAttr);
}
void HTMLFrameElement::setMarginWidth(const String &value)
{
setAttribute(marginwidthAttr, value);
}
String HTMLFrameElement::name() const
{
return getAttribute(nameAttr);
}
void HTMLFrameElement::setName(const String &value)
{
setAttribute(nameAttr, value);
}
void HTMLFrameElement::setNoResize(bool noResize)
{
setAttribute(noresizeAttr, noResize ? "" : 0);
}
String HTMLFrameElement::scrolling() const
{
return getAttribute(scrollingAttr);
}
void HTMLFrameElement::setScrolling(const String &value)
{
setAttribute(scrollingAttr, value);
}
String HTMLFrameElement::src() const
{
return getAttribute(srcAttr);
}
void HTMLFrameElement::setSrc(const String &value)
{
setAttribute(srcAttr, value);
}
int HTMLFrameElement::frameWidth() const
{
if (!renderer())
return 0;
document()->updateLayoutIgnorePendingStylesheets();
return renderer()->width();
}
int HTMLFrameElement::frameHeight() const
{
if (!renderer())
return 0;
document()->updateLayoutIgnorePendingStylesheets();
return renderer()->height();
}
}