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 "FrameView.h"
34#include "HTMLNames.h"
35#include "HTMLParserIdioms.h"
36#include "KURL.h"
37#include "Page.h"
38#include "RenderPart.h"
39#include "ScriptController.h"
40#include "ScriptEventListener.h"
41#include "Settings.h"
42
43namespace WebCore {
44
45using namespace HTMLNames;
46
47HTMLFrameElementBase::HTMLFrameElementBase(const QualifiedName& tagName, Document* document)
48    : HTMLFrameOwnerElement(tagName, document)
49    , m_scrolling(ScrollbarAuto)
50    , m_marginWidth(-1)
51    , m_marginHeight(-1)
52    , m_viewSource(false)
53{
54}
55
56bool HTMLFrameElementBase::isURLAllowed() const
57{
58    if (m_URL.isEmpty())
59        return true;
60
61    const KURL& completeURL = document()->completeURL(m_URL);
62
63    if (protocolIsJavaScript(completeURL)) {
64        Document* contentDoc = this->contentDocument();
65        if (contentDoc && !ScriptController::canAccessFromCurrentOrigin(contentDoc->frame()))
66            return false;
67    }
68
69    Frame* parentFrame = document()->frame();
70    if (parentFrame)
71        return parentFrame->isURLAllowed(completeURL);
72
73    return true;
74}
75
76void HTMLFrameElementBase::openURL(bool lockHistory, bool lockBackForwardList)
77{
78    if (!isURLAllowed())
79        return;
80
81    if (m_URL.isEmpty())
82        m_URL = blankURL().string();
83
84    Frame* parentFrame = document()->frame();
85    if (!parentFrame)
86        return;
87
88    parentFrame->loader()->subframeLoader()->requestFrame(this, m_URL, m_frameName, lockHistory, lockBackForwardList);
89    if (contentFrame())
90        contentFrame()->setInViewSourceMode(viewSourceMode());
91}
92
93void HTMLFrameElementBase::parseAttribute(const QualifiedName& name, const AtomicString& value)
94{
95    if (name == srcdocAttr)
96        setLocation("about:srcdoc");
97    else if (name == srcAttr && !fastHasAttribute(srcdocAttr))
98        setLocation(stripLeadingAndTrailingHTMLSpaces(value));
99    else if (isIdAttributeName(name)) {
100        // Important to call through to base for the id attribute so the hasID bit gets set.
101        HTMLFrameOwnerElement::parseAttribute(name, value);
102        m_frameName = value;
103    } else if (name == nameAttr) {
104        m_frameName = value;
105        // FIXME: If we are already attached, this doesn't actually change the frame's name.
106        // FIXME: If we are already attached, this doesn't check for frame name
107        // conflicts and generate a unique frame name.
108    } else if (name == marginwidthAttr) {
109        m_marginWidth = value.toInt();
110        // FIXME: If we are already attached, this has no effect.
111    } else if (name == marginheightAttr) {
112        m_marginHeight = value.toInt();
113        // FIXME: If we are already attached, this has no effect.
114    } else if (name == scrollingAttr) {
115        // Auto and yes both simply mean "allow scrolling." No means "don't allow scrolling."
116        if (equalIgnoringCase(value, "auto") || equalIgnoringCase(value, "yes"))
117            m_scrolling = document()->frameElementsShouldIgnoreScrolling() ? ScrollbarAlwaysOff : ScrollbarAuto;
118        else if (equalIgnoringCase(value, "no"))
119            m_scrolling = ScrollbarAlwaysOff;
120        // FIXME: If we are already attached, this has no effect.
121#if ENABLE(VIEWSOURCE_ATTRIBUTE)
122    } else if (name == viewsourceAttr) {
123        m_viewSource = !value.isNull();
124        if (contentFrame())
125            contentFrame()->setInViewSourceMode(viewSourceMode());
126#endif
127    } else if (name == onbeforeloadAttr)
128        setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, name, value));
129    else if (name == onbeforeunloadAttr) {
130        // FIXME: should <frame> elements have beforeunload handlers?
131        setAttributeEventListener(eventNames().beforeunloadEvent, createAttributeEventListener(this, name, value));
132    } else
133        HTMLFrameOwnerElement::parseAttribute(name, value);
134}
135
136void HTMLFrameElementBase::setNameAndOpenURL()
137{
138    m_frameName = getNameAttribute();
139    if (m_frameName.isNull())
140        m_frameName = getIdAttribute();
141    openURL();
142}
143
144Node::InsertionNotificationRequest HTMLFrameElementBase::insertedInto(ContainerNode* insertionPoint)
145{
146    HTMLFrameOwnerElement::insertedInto(insertionPoint);
147    if (insertionPoint->inDocument())
148        return InsertionShouldCallDidNotifySubtreeInsertions;
149    return InsertionDone;
150}
151
152void HTMLFrameElementBase::didNotifySubtreeInsertions(ContainerNode*)
153{
154    if (!inDocument())
155        return;
156
157    // DocumentFragments don't kick of any loads.
158    if (!document()->frame())
159        return;
160
161    if (!SubframeLoadingDisabler::canLoadFrame(this))
162        return;
163
164    // JavaScript in src=javascript: and beforeonload can access the renderer
165    // during attribute parsing *before* the normal parser machinery would
166    // attach the element. To support this, we lazyAttach here, but only
167    // if we don't already have a renderer (if we're inserted
168    // as part of a DocumentFragment, insertedInto from an earlier element
169    // could have forced a style resolve and already attached us).
170    if (!renderer())
171        lazyAttach(DoNotSetAttached);
172    setNameAndOpenURL();
173}
174
175void HTMLFrameElementBase::attach(const AttachContext& context)
176{
177    HTMLFrameOwnerElement::attach(context);
178
179    if (RenderPart* part = renderPart()) {
180        if (Frame* frame = contentFrame())
181            part->setWidget(frame->view());
182    }
183}
184
185KURL HTMLFrameElementBase::location() const
186{
187    if (fastHasAttribute(srcdocAttr))
188        return KURL(ParsedURLString, "about:srcdoc");
189    return document()->completeURL(getAttribute(srcAttr));
190}
191
192void HTMLFrameElementBase::setLocation(const String& str)
193{
194    Settings* settings = document()->settings();
195    if (settings && settings->needsAcrobatFrameReloadingQuirk() && m_URL == str)
196        return;
197
198    m_URL = AtomicString(str);
199
200    if (inDocument())
201        openURL(false, false);
202}
203
204bool HTMLFrameElementBase::supportsFocus() const
205{
206    return true;
207}
208
209void HTMLFrameElementBase::setFocus(bool received)
210{
211    HTMLFrameOwnerElement::setFocus(received);
212    if (Page* page = document()->page()) {
213        if (received)
214            page->focusController()->setFocusedFrame(contentFrame());
215        else if (page->focusController()->focusedFrame() == contentFrame()) // Focus may have already been given to another frame, don't take it away.
216            page->focusController()->setFocusedFrame(0);
217    }
218}
219
220bool HTMLFrameElementBase::isURLAttribute(const Attribute& attribute) const
221{
222    return attribute.name() == srcAttr || HTMLFrameOwnerElement::isURLAttribute(attribute);
223}
224
225bool HTMLFrameElementBase::isHTMLContentAttribute(const Attribute& attribute) const
226{
227    return attribute.name() == srcdocAttr || HTMLFrameOwnerElement::isHTMLContentAttribute(attribute);
228}
229
230int HTMLFrameElementBase::width()
231{
232    document()->updateLayoutIgnorePendingStylesheets();
233    if (!renderBox())
234        return 0;
235    return renderBox()->width();
236}
237
238int HTMLFrameElementBase::height()
239{
240    document()->updateLayoutIgnorePendingStylesheets();
241    if (!renderBox())
242        return 0;
243    return renderBox()->height();
244}
245
246} // namespace WebCore
247