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