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, 2007, 2008, 2009, 2010 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 "core/html/HTMLBodyElement.h"
26
27#include "CSSValueKeywords.h"
28#include "HTMLNames.h"
29#include "bindings/v8/ScriptEventListener.h"
30#include "core/css/CSSImageValue.h"
31#include "core/css/CSSParser.h"
32#include "core/css/StylePropertySet.h"
33#include "core/dom/Attribute.h"
34#include "core/dom/EventNames.h"
35#include "core/html/HTMLFrameElementBase.h"
36#include "core/html/parser/HTMLParserIdioms.h"
37#include "core/page/Frame.h"
38#include "core/page/FrameView.h"
39
40namespace WebCore {
41
42using namespace HTMLNames;
43
44HTMLBodyElement::HTMLBodyElement(const QualifiedName& tagName, Document* document)
45    : HTMLElement(tagName, document)
46{
47    ASSERT(hasTagName(bodyTag));
48    ScriptWrappable::init(this);
49}
50
51PassRefPtr<HTMLBodyElement> HTMLBodyElement::create(Document* document)
52{
53    return adoptRef(new HTMLBodyElement(bodyTag, document));
54}
55
56PassRefPtr<HTMLBodyElement> HTMLBodyElement::create(const QualifiedName& tagName, Document* document)
57{
58    return adoptRef(new HTMLBodyElement(tagName, document));
59}
60
61HTMLBodyElement::~HTMLBodyElement()
62{
63}
64
65bool HTMLBodyElement::isPresentationAttribute(const QualifiedName& name) const
66{
67    if (name == backgroundAttr || name == marginwidthAttr || name == leftmarginAttr || name == marginheightAttr || name == topmarginAttr || name == bgcolorAttr || name == textAttr || name == bgpropertiesAttr)
68        return true;
69    return HTMLElement::isPresentationAttribute(name);
70}
71
72void HTMLBodyElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
73{
74    if (name == backgroundAttr) {
75        String url = stripLeadingAndTrailingHTMLSpaces(value);
76        if (!url.isEmpty()) {
77            RefPtr<CSSImageValue> imageValue = CSSImageValue::create(document()->completeURL(url).string());
78            imageValue->setInitiator(localName());
79            style->setProperty(CSSProperty(CSSPropertyBackgroundImage, imageValue.release()));
80        }
81    } else if (name == marginwidthAttr || name == leftmarginAttr) {
82        addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
83        addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
84    } else if (name == marginheightAttr || name == topmarginAttr) {
85        addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
86        addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
87    } else if (name == bgcolorAttr) {
88        addHTMLColorToStyle(style, CSSPropertyBackgroundColor, value);
89    } else if (name == textAttr) {
90        addHTMLColorToStyle(style, CSSPropertyColor, value);
91    } else if (name == bgpropertiesAttr) {
92        if (equalIgnoringCase(value, "fixed"))
93           addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundAttachment, CSSValueFixed);
94    } else
95        HTMLElement::collectStyleForPresentationAttribute(name, value, style);
96}
97
98void HTMLBodyElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
99{
100    if (name == vlinkAttr || name == alinkAttr || name == linkAttr) {
101        if (value.isNull()) {
102            if (name == linkAttr)
103                document()->textLinkColors().resetLinkColor();
104            else if (name == vlinkAttr)
105                document()->textLinkColors().resetVisitedLinkColor();
106            else
107                document()->textLinkColors().resetActiveLinkColor();
108        } else {
109            RGBA32 color;
110            if (CSSParser::parseColor(color, value, !document()->inQuirksMode())) {
111                if (name == linkAttr)
112                    document()->textLinkColors().setLinkColor(color);
113                else if (name == vlinkAttr)
114                    document()->textLinkColors().setVisitedLinkColor(color);
115                else
116                    document()->textLinkColors().setActiveLinkColor(color);
117            }
118        }
119
120        setNeedsStyleRecalc();
121    } else if (name == onloadAttr)
122        document()->setWindowAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(document()->frame(), name, value));
123    else if (name == onbeforeunloadAttr)
124        document()->setWindowAttributeEventListener(eventNames().beforeunloadEvent, createAttributeEventListener(document()->frame(), name, value));
125    else if (name == onunloadAttr)
126        document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), name, value));
127    else if (name == onpagehideAttr)
128        document()->setWindowAttributeEventListener(eventNames().pagehideEvent, createAttributeEventListener(document()->frame(), name, value));
129    else if (name == onpageshowAttr)
130        document()->setWindowAttributeEventListener(eventNames().pageshowEvent, createAttributeEventListener(document()->frame(), name, value));
131    else if (name == onpopstateAttr)
132        document()->setWindowAttributeEventListener(eventNames().popstateEvent, createAttributeEventListener(document()->frame(), name, value));
133    else if (name == onblurAttr)
134        document()->setWindowAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(document()->frame(), name, value));
135    else if (name == onfocusAttr)
136        document()->setWindowAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(document()->frame(), name, value));
137#if ENABLE(ORIENTATION_EVENTS)
138    else if (name == onorientationchangeAttr)
139        document()->setWindowAttributeEventListener(eventNames().orientationchangeEvent, createAttributeEventListener(document()->frame(), name, value));
140#endif
141    else if (name == onhashchangeAttr)
142        document()->setWindowAttributeEventListener(eventNames().hashchangeEvent, createAttributeEventListener(document()->frame(), name, value));
143    else if (name == onresizeAttr)
144        document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), name, value));
145    else if (name == onscrollAttr)
146        document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), name, value));
147    else if (name == onselectionchangeAttr)
148        document()->setAttributeEventListener(eventNames().selectionchangeEvent, createAttributeEventListener(document()->frame(), name, value));
149    else if (name == onstorageAttr)
150        document()->setWindowAttributeEventListener(eventNames().storageEvent, createAttributeEventListener(document()->frame(), name, value));
151    else if (name == ononlineAttr)
152        document()->setWindowAttributeEventListener(eventNames().onlineEvent, createAttributeEventListener(document()->frame(), name, value));
153    else if (name == onofflineAttr)
154        document()->setWindowAttributeEventListener(eventNames().offlineEvent, createAttributeEventListener(document()->frame(), name, value));
155    else
156        HTMLElement::parseAttribute(name, value);
157}
158
159Node::InsertionNotificationRequest HTMLBodyElement::insertedInto(ContainerNode* insertionPoint)
160{
161    HTMLElement::insertedInto(insertionPoint);
162    if (insertionPoint->inDocument())
163        return InsertionShouldCallDidNotifySubtreeInsertions;
164    return InsertionDone;
165}
166
167void HTMLBodyElement::didNotifySubtreeInsertions(ContainerNode* insertionPoint)
168{
169    ASSERT_UNUSED(insertionPoint, insertionPoint->inDocument());
170    ASSERT(document());
171
172    // FIXME: Perhaps this code should be in attach() instead of here.
173    Element* ownerElement = document()->ownerElement();
174    if (ownerElement && (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag))) {
175        HTMLFrameElementBase* ownerFrameElement = static_cast<HTMLFrameElementBase*>(ownerElement);
176        int marginWidth = ownerFrameElement->marginWidth();
177        if (marginWidth != -1)
178            setAttribute(marginwidthAttr, String::number(marginWidth));
179        int marginHeight = ownerFrameElement->marginHeight();
180        if (marginHeight != -1)
181            setAttribute(marginheightAttr, String::number(marginHeight));
182    }
183
184    // FIXME: This call to scheduleRelayout should not be needed here.
185    // But without it we hang during WebKit tests; need to fix that and remove this.
186    if (FrameView* view = document()->view())
187        view->scheduleRelayout();
188}
189
190bool HTMLBodyElement::isURLAttribute(const Attribute& attribute) const
191{
192    return attribute.name() == backgroundAttr || HTMLElement::isURLAttribute(attribute);
193}
194
195bool HTMLBodyElement::supportsFocus() const
196{
197    // This override is needed because the inherited method bails if the parent is editable.
198    // The <body> should be focusable even if <html> is editable.
199    return rendererIsEditable() || HTMLElement::supportsFocus();
200}
201
202String HTMLBodyElement::aLink() const
203{
204    return getAttribute(alinkAttr);
205}
206
207void HTMLBodyElement::setALink(const String& value)
208{
209    setAttribute(alinkAttr, value);
210}
211
212String HTMLBodyElement::bgColor() const
213{
214    return getAttribute(bgcolorAttr);
215}
216
217void HTMLBodyElement::setBgColor(const String& value)
218{
219    setAttribute(bgcolorAttr, value);
220}
221
222String HTMLBodyElement::link() const
223{
224    return getAttribute(linkAttr);
225}
226
227void HTMLBodyElement::setLink(const String& value)
228{
229    setAttribute(linkAttr, value);
230}
231
232String HTMLBodyElement::text() const
233{
234    return getAttribute(textAttr);
235}
236
237void HTMLBodyElement::setText(const String& value)
238{
239    setAttribute(textAttr, value);
240}
241
242String HTMLBodyElement::vLink() const
243{
244    return getAttribute(vlinkAttr);
245}
246
247void HTMLBodyElement::setVLink(const String& value)
248{
249    setAttribute(vlinkAttr, value);
250}
251
252static int adjustForZoom(int value, Document* document)
253{
254    Frame* frame = document->frame();
255    float zoomFactor = frame->pageZoomFactor();
256    if (zoomFactor == 1)
257        return value;
258    // Needed because of truncation (rather than rounding) when scaling up.
259    if (zoomFactor > 1)
260        value++;
261    return static_cast<int>(value / zoomFactor);
262}
263
264int HTMLBodyElement::scrollLeft()
265{
266    // Update the document's layout.
267    Document* document = this->document();
268    document->updateLayoutIgnorePendingStylesheets();
269    FrameView* view = document->view();
270    return view ? adjustForZoom(view->scrollX(), document) : 0;
271}
272
273void HTMLBodyElement::setScrollLeft(int scrollLeft)
274{
275    Document* document = this->document();
276    document->updateLayoutIgnorePendingStylesheets();
277    Frame* frame = document->frame();
278    if (!frame)
279        return;
280    FrameView* view = frame->view();
281    if (!view)
282        return;
283    view->setScrollPosition(IntPoint(static_cast<int>(scrollLeft * frame->pageZoomFactor()), view->scrollY()));
284}
285
286int HTMLBodyElement::scrollTop()
287{
288    // Update the document's layout.
289    Document* document = this->document();
290    document->updateLayoutIgnorePendingStylesheets();
291    FrameView* view = document->view();
292    return view ? adjustForZoom(view->scrollY(), document) : 0;
293}
294
295void HTMLBodyElement::setScrollTop(int scrollTop)
296{
297    Document* document = this->document();
298    document->updateLayoutIgnorePendingStylesheets();
299    Frame* frame = document->frame();
300    if (!frame)
301        return;
302    FrameView* view = frame->view();
303    if (!view)
304        return;
305    view->setScrollPosition(IntPoint(view->scrollX(), static_cast<int>(scrollTop * frame->pageZoomFactor())));
306}
307
308int HTMLBodyElement::scrollHeight()
309{
310    // Update the document's layout.
311    Document* document = this->document();
312    document->updateLayoutIgnorePendingStylesheets();
313    FrameView* view = document->view();
314    return view ? adjustForZoom(view->contentsHeight(), document) : 0;
315}
316
317int HTMLBodyElement::scrollWidth()
318{
319    // Update the document's layout.
320    Document* document = this->document();
321    document->updateLayoutIgnorePendingStylesheets();
322    FrameView* view = document->view();
323    return view ? adjustForZoom(view->contentsWidth(), document) : 0;
324}
325
326void HTMLBodyElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
327{
328    HTMLElement::addSubresourceAttributeURLs(urls);
329
330    addSubresourceURL(urls, document()->completeURL(getAttribute(backgroundAttr)));
331}
332
333} // namespace WebCore
334