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 "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
44namespace WebCore {
45
46using namespace HTMLNames;
47
48HTMLFrameElementBase::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
60bool 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
93void 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
112void 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
153void 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
165void HTMLFrameElementBase::setNameAndOpenURLCallback(Node* n)
166{
167    static_cast<HTMLFrameElementBase*>(n)->setNameAndOpenURL();
168}
169
170void 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
180void HTMLFrameElementBase::removedFromDocument()
181{
182    m_shouldOpenURLAfterAttach = false;
183
184    HTMLFrameOwnerElement::removedFromDocument();
185}
186
187void 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
205KURL HTMLFrameElementBase::location() const
206{
207    return document()->completeURL(getAttribute(srcAttr));
208}
209
210void 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
222bool HTMLFrameElementBase::supportsFocus() const
223{
224    return true;
225}
226
227void HTMLFrameElementBase::setFocus(bool received)
228{
229    HTMLFrameOwnerElement::setFocus(received);
230    if (Page* page = document()->page())
231        page->focusController()->setFocusedFrame(received ? contentFrame() : 0);
232}
233
234bool HTMLFrameElementBase::isURLAttribute(Attribute *attr) const
235{
236    return attr->name() == srcAttr;
237}
238
239int HTMLFrameElementBase::width() const
240{
241    if (!renderer())
242        return 0;
243
244    document()->updateLayoutIgnorePendingStylesheets();
245    return toRenderBox(renderer())->width();
246}
247
248int HTMLFrameElementBase::height() const
249{
250    if (!renderer())
251        return 0;
252
253    document()->updateLayoutIgnorePendingStylesheets();
254    return toRenderBox(renderer())->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_checkAttachedTimer.startOneShot(0);
265    else
266        m_checkAttachedTimer.stop();
267}
268
269void HTMLFrameElementBase::checkAttachedTimerFired(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} // namespace WebCore
287