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 "HTMLBodyElement.h"
26
27#include "Attribute.h"
28#include "CSSImageValue.h"
29#include "CSSParser.h"
30#include "CSSValueKeywords.h"
31#include "EventNames.h"
32#include "Frame.h"
33#include "FrameView.h"
34#include "HTMLFrameElementBase.h"
35#include "HTMLNames.h"
36#include "HTMLParserIdioms.h"
37#include "Page.h"
38#include "ScriptEventListener.h"
39#include "StylePropertySet.h"
40
41namespace WebCore {
42
43using namespace HTMLNames;
44
45HTMLBodyElement::HTMLBodyElement(const QualifiedName& tagName, Document* document)
46    : HTMLElement(tagName, document)
47{
48    ASSERT(hasTagName(bodyTag));
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()->resetLinkColor();
104            else if (name == vlinkAttr)
105                document()->resetVisitedLinkColor();
106            else
107                document()->resetActiveLinkColor();
108        } else {
109            RGBA32 color;
110            if (CSSParser::parseColor(color, value, !document()->inQuirksMode())) {
111                if (name == linkAttr)
112                    document()->setLinkColor(color);
113                else if (name == vlinkAttr)
114                    document()->setVisitedLinkColor(color);
115                else
116                    document()->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    return rendererIsEditable() || HTMLElement::supportsFocus();
198}
199
200String HTMLBodyElement::aLink() const
201{
202    return getAttribute(alinkAttr);
203}
204
205void HTMLBodyElement::setALink(const String& value)
206{
207    setAttribute(alinkAttr, value);
208}
209
210String HTMLBodyElement::bgColor() const
211{
212    return getAttribute(bgcolorAttr);
213}
214
215void HTMLBodyElement::setBgColor(const String& value)
216{
217    setAttribute(bgcolorAttr, value);
218}
219
220String HTMLBodyElement::link() const
221{
222    return getAttribute(linkAttr);
223}
224
225void HTMLBodyElement::setLink(const String& value)
226{
227    setAttribute(linkAttr, value);
228}
229
230String HTMLBodyElement::text() const
231{
232    return getAttribute(textAttr);
233}
234
235void HTMLBodyElement::setText(const String& value)
236{
237    setAttribute(textAttr, value);
238}
239
240String HTMLBodyElement::vLink() const
241{
242    return getAttribute(vlinkAttr);
243}
244
245void HTMLBodyElement::setVLink(const String& value)
246{
247    setAttribute(vlinkAttr, value);
248}
249
250static int adjustForZoom(int value, Document* document)
251{
252    Frame* frame = document->frame();
253    float zoomFactor = frame->pageZoomFactor() * frame->frameScaleFactor();
254    if (zoomFactor == 1)
255        return value;
256    // Needed because of truncation (rather than rounding) when scaling up.
257    if (zoomFactor > 1)
258        value++;
259    return static_cast<int>(value / zoomFactor);
260}
261
262int HTMLBodyElement::scrollLeft()
263{
264    // Update the document's layout.
265    Document* document = this->document();
266    document->updateLayoutIgnorePendingStylesheets();
267    FrameView* view = document->view();
268    return view ? adjustForZoom(view->scrollX(), document) : 0;
269}
270
271void HTMLBodyElement::setScrollLeft(int scrollLeft)
272{
273    Document* document = this->document();
274    document->updateLayoutIgnorePendingStylesheets();
275    Frame* frame = document->frame();
276    if (!frame)
277        return;
278    FrameView* view = frame->view();
279    if (!view)
280        return;
281    view->setScrollPosition(IntPoint(static_cast<int>(scrollLeft * frame->pageZoomFactor() * frame->frameScaleFactor()), view->scrollY()));
282}
283
284int HTMLBodyElement::scrollTop()
285{
286    // Update the document's layout.
287    Document* document = this->document();
288    document->updateLayoutIgnorePendingStylesheets();
289    FrameView* view = document->view();
290    return view ? adjustForZoom(view->scrollY(), document) : 0;
291}
292
293void HTMLBodyElement::setScrollTop(int scrollTop)
294{
295    Document* document = this->document();
296    document->updateLayoutIgnorePendingStylesheets();
297    Frame* frame = document->frame();
298    if (!frame)
299        return;
300    FrameView* view = frame->view();
301    if (!view)
302        return;
303    view->setScrollPosition(IntPoint(view->scrollX(), static_cast<int>(scrollTop * frame->pageZoomFactor() * frame->frameScaleFactor())));
304}
305
306int HTMLBodyElement::scrollHeight()
307{
308    // Update the document's layout.
309    Document* document = this->document();
310    document->updateLayoutIgnorePendingStylesheets();
311    FrameView* view = document->view();
312    return view ? adjustForZoom(view->contentsHeight(), document) : 0;
313}
314
315int HTMLBodyElement::scrollWidth()
316{
317    // Update the document's layout.
318    Document* document = this->document();
319    document->updateLayoutIgnorePendingStylesheets();
320    FrameView* view = document->view();
321    return view ? adjustForZoom(view->contentsWidth(), document) : 0;
322}
323
324void HTMLBodyElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
325{
326    HTMLElement::addSubresourceAttributeURLs(urls);
327
328    addSubresourceURL(urls, document()->completeURL(getAttribute(backgroundAttr)));
329}
330
331} // namespace WebCore
332