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 "CSSStyleSelector.h"
29#include "CSSStyleSheet.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 "ScriptEventListener.h"
38
39namespace WebCore {
40
41using namespace HTMLNames;
42
43HTMLBodyElement::HTMLBodyElement(const QualifiedName& tagName, Document* document)
44    : HTMLElement(tagName, document)
45{
46    ASSERT(hasTagName(bodyTag));
47}
48
49PassRefPtr<HTMLBodyElement> HTMLBodyElement::create(Document* document)
50{
51    return adoptRef(new HTMLBodyElement(bodyTag, document));
52}
53
54PassRefPtr<HTMLBodyElement> HTMLBodyElement::create(const QualifiedName& tagName, Document* document)
55{
56    return adoptRef(new HTMLBodyElement(tagName, document));
57}
58
59HTMLBodyElement::~HTMLBodyElement()
60{
61    if (m_linkDecl) {
62        m_linkDecl->setNode(0);
63        m_linkDecl->setParent(0);
64    }
65}
66
67void HTMLBodyElement::createLinkDecl()
68{
69    m_linkDecl = CSSMutableStyleDeclaration::create();
70    m_linkDecl->setParent(document()->elementSheet());
71    m_linkDecl->setNode(this);
72    m_linkDecl->setStrictParsing(!document()->inQuirksMode());
73}
74
75bool HTMLBodyElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
76{
77    if (attrName == backgroundAttr) {
78        result = (MappedAttributeEntry)(eLastEntry + document()->docID());
79        return false;
80    }
81
82    if (attrName == bgcolorAttr ||
83        attrName == textAttr ||
84        attrName == marginwidthAttr ||
85        attrName == leftmarginAttr ||
86        attrName == marginheightAttr ||
87        attrName == topmarginAttr ||
88        attrName == bgpropertiesAttr) {
89        result = eUniversal;
90        return false;
91    }
92
93    return HTMLElement::mapToEntry(attrName, result);
94}
95
96void HTMLBodyElement::parseMappedAttribute(Attribute* attr)
97{
98    if (attr->name() == backgroundAttr) {
99        String url = stripLeadingAndTrailingHTMLSpaces(attr->value());
100        if (!url.isEmpty())
101            addCSSImageProperty(attr, CSSPropertyBackgroundImage, document()->completeURL(url).string());
102    } else if (attr->name() == marginwidthAttr || attr->name() == leftmarginAttr) {
103        addCSSLength(attr, CSSPropertyMarginRight, attr->value());
104        addCSSLength(attr, CSSPropertyMarginLeft, attr->value());
105    } else if (attr->name() == marginheightAttr || attr->name() == topmarginAttr) {
106        addCSSLength(attr, CSSPropertyMarginBottom, attr->value());
107        addCSSLength(attr, CSSPropertyMarginTop, attr->value());
108    } else if (attr->name() == bgcolorAttr) {
109        addCSSColor(attr, CSSPropertyBackgroundColor, attr->value());
110    } else if (attr->name() == textAttr) {
111        addCSSColor(attr, CSSPropertyColor, attr->value());
112    } else if (attr->name() == bgpropertiesAttr) {
113        if (equalIgnoringCase(attr->value(), "fixed"))
114            addCSSProperty(attr, CSSPropertyBackgroundAttachment, CSSValueFixed);
115    } else if (attr->name() == vlinkAttr ||
116               attr->name() == alinkAttr ||
117               attr->name() == linkAttr) {
118        if (attr->isNull()) {
119            if (attr->name() == linkAttr)
120                document()->resetLinkColor();
121            else if (attr->name() == vlinkAttr)
122                document()->resetVisitedLinkColor();
123            else
124                document()->resetActiveLinkColor();
125        } else {
126            if (!m_linkDecl)
127                createLinkDecl();
128            m_linkDecl->setProperty(CSSPropertyColor, attr->value(), false, false);
129            RefPtr<CSSValue> val = m_linkDecl->getPropertyCSSValue(CSSPropertyColor);
130            if (val && val->isPrimitiveValue()) {
131                Color col = document()->styleSelector()->getColorFromPrimitiveValue(static_cast<CSSPrimitiveValue*>(val.get()));
132                if (attr->name() == linkAttr)
133                    document()->setLinkColor(col);
134                else if (attr->name() == vlinkAttr)
135                    document()->setVisitedLinkColor(col);
136                else
137                    document()->setActiveLinkColor(col);
138            }
139        }
140
141        if (attached())
142            document()->recalcStyle(Force);
143    } else if (attr->name() == onloadAttr)
144        document()->setWindowAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(document()->frame(), attr));
145    else if (attr->name() == onbeforeunloadAttr)
146        document()->setWindowAttributeEventListener(eventNames().beforeunloadEvent, createAttributeEventListener(document()->frame(), attr));
147    else if (attr->name() == onunloadAttr)
148        document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), attr));
149    else if (attr->name() == onpagehideAttr)
150        document()->setWindowAttributeEventListener(eventNames().pagehideEvent, createAttributeEventListener(document()->frame(), attr));
151    else if (attr->name() == onpageshowAttr)
152        document()->setWindowAttributeEventListener(eventNames().pageshowEvent, createAttributeEventListener(document()->frame(), attr));
153    else if (attr->name() == onpopstateAttr)
154        document()->setWindowAttributeEventListener(eventNames().popstateEvent, createAttributeEventListener(document()->frame(), attr));
155    else if (attr->name() == onblurAttr)
156        document()->setWindowAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(document()->frame(), attr));
157    else if (attr->name() == onfocusAttr)
158        document()->setWindowAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(document()->frame(), attr));
159#if ENABLE(ORIENTATION_EVENTS)
160    else if (attr->name() == onorientationchangeAttr)
161        document()->setWindowAttributeEventListener(eventNames().orientationchangeEvent, createAttributeEventListener(document()->frame(), attr));
162#endif
163    else if (attr->name() == onhashchangeAttr)
164        document()->setWindowAttributeEventListener(eventNames().hashchangeEvent, createAttributeEventListener(document()->frame(), attr));
165    else if (attr->name() == onresizeAttr)
166        document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), attr));
167    else if (attr->name() == onscrollAttr)
168        document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), attr));
169    else if (attr->name() == onselectionchangeAttr)
170        document()->setAttributeEventListener(eventNames().selectionchangeEvent, createAttributeEventListener(document()->frame(), attr));
171    else if (attr->name() == onstorageAttr)
172        document()->setWindowAttributeEventListener(eventNames().storageEvent, createAttributeEventListener(document()->frame(), attr));
173    else if (attr->name() == ononlineAttr)
174        document()->setWindowAttributeEventListener(eventNames().onlineEvent, createAttributeEventListener(document()->frame(), attr));
175    else if (attr->name() == onofflineAttr)
176        document()->setWindowAttributeEventListener(eventNames().offlineEvent, createAttributeEventListener(document()->frame(), attr));
177    else
178        HTMLElement::parseMappedAttribute(attr);
179}
180
181void HTMLBodyElement::insertedIntoDocument()
182{
183    HTMLElement::insertedIntoDocument();
184
185    // FIXME: Perhaps this code should be in attach() instead of here.
186    Element* ownerElement = document()->ownerElement();
187    if (ownerElement && (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag))) {
188        HTMLFrameElementBase* ownerFrameElement = static_cast<HTMLFrameElementBase*>(ownerElement);
189        int marginWidth = ownerFrameElement->marginWidth();
190        if (marginWidth != -1)
191            setAttribute(marginwidthAttr, String::number(marginWidth));
192        int marginHeight = ownerFrameElement->marginHeight();
193        if (marginHeight != -1)
194            setAttribute(marginheightAttr, String::number(marginHeight));
195    }
196
197    // FIXME: This call to scheduleRelayout should not be needed here.
198    // But without it we hang during WebKit tests; need to fix that and remove this.
199    if (FrameView* view = document()->view())
200        view->scheduleRelayout();
201
202    if (document() && document()->page())
203        document()->page()->updateViewportArguments();
204}
205
206bool HTMLBodyElement::isURLAttribute(Attribute *attr) const
207{
208    return attr->name() == backgroundAttr;
209}
210
211bool HTMLBodyElement::supportsFocus() const
212{
213    return rendererIsEditable() || HTMLElement::supportsFocus();
214}
215
216String HTMLBodyElement::aLink() const
217{
218    return getAttribute(alinkAttr);
219}
220
221void HTMLBodyElement::setALink(const String& value)
222{
223    setAttribute(alinkAttr, value);
224}
225
226String HTMLBodyElement::bgColor() const
227{
228    return getAttribute(bgcolorAttr);
229}
230
231void HTMLBodyElement::setBgColor(const String& value)
232{
233    setAttribute(bgcolorAttr, value);
234}
235
236String HTMLBodyElement::link() const
237{
238    return getAttribute(linkAttr);
239}
240
241void HTMLBodyElement::setLink(const String& value)
242{
243    setAttribute(linkAttr, value);
244}
245
246String HTMLBodyElement::text() const
247{
248    return getAttribute(textAttr);
249}
250
251void HTMLBodyElement::setText(const String& value)
252{
253    setAttribute(textAttr, value);
254}
255
256String HTMLBodyElement::vLink() const
257{
258    return getAttribute(vlinkAttr);
259}
260
261void HTMLBodyElement::setVLink(const String& value)
262{
263    setAttribute(vlinkAttr, value);
264}
265
266static int adjustForZoom(int value, Document* document)
267{
268    Frame* frame = document->frame();
269    float zoomFactor = frame->pageZoomFactor() * frame->pageScaleFactor();
270    if (zoomFactor == 1)
271        return value;
272    // Needed because of truncation (rather than rounding) when scaling up.
273    if (zoomFactor > 1)
274        value++;
275    return static_cast<int>(value / zoomFactor);
276}
277
278int HTMLBodyElement::scrollLeft() const
279{
280    // Update the document's layout.
281    Document* document = this->document();
282    document->updateLayoutIgnorePendingStylesheets();
283    FrameView* view = document->view();
284    return view ? adjustForZoom(view->scrollX(), document) : 0;
285}
286
287void HTMLBodyElement::setScrollLeft(int scrollLeft)
288{
289    Document* document = this->document();
290    document->updateLayoutIgnorePendingStylesheets();
291    Frame* frame = document->frame();
292    if (!frame)
293        return;
294    FrameView* view = frame->view();
295    if (!view)
296        return;
297    view->setScrollPosition(IntPoint(static_cast<int>(scrollLeft * frame->pageZoomFactor() * frame->pageScaleFactor()), view->scrollY()));
298}
299
300int HTMLBodyElement::scrollTop() const
301{
302    // Update the document's layout.
303    Document* document = this->document();
304    document->updateLayoutIgnorePendingStylesheets();
305    FrameView* view = document->view();
306    return view ? adjustForZoom(view->scrollY(), document) : 0;
307}
308
309void HTMLBodyElement::setScrollTop(int scrollTop)
310{
311    Document* document = this->document();
312    document->updateLayoutIgnorePendingStylesheets();
313    Frame* frame = document->frame();
314    if (!frame)
315        return;
316    FrameView* view = frame->view();
317    if (!view)
318        return;
319    view->setScrollPosition(IntPoint(view->scrollX(), static_cast<int>(scrollTop * frame->pageZoomFactor() * frame->pageScaleFactor())));
320}
321
322int HTMLBodyElement::scrollHeight() const
323{
324    // Update the document's layout.
325    Document* document = this->document();
326    document->updateLayoutIgnorePendingStylesheets();
327    FrameView* view = document->view();
328    return view ? adjustForZoom(view->contentsHeight(), document) : 0;
329}
330
331int HTMLBodyElement::scrollWidth() const
332{
333    // Update the document's layout.
334    Document* document = this->document();
335    document->updateLayoutIgnorePendingStylesheets();
336    FrameView* view = document->view();
337    return view ? adjustForZoom(view->contentsWidth(), document) : 0;
338}
339
340void HTMLBodyElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
341{
342    HTMLElement::addSubresourceAttributeURLs(urls);
343
344    addSubresourceURL(urls, document()->completeURL(getAttribute(backgroundAttr)));
345}
346
347void HTMLBodyElement::didMoveToNewOwnerDocument()
348{
349    // When moving body elements between documents, we should have to reset the parent sheet for any
350    // link style declarations.  If we don't we might crash later.
351    // In practice I can't reproduce this theoretical problem.
352    // webarchive/adopt-attribute-styled-body-webarchive.html tries to make sure this crash won't surface.
353    if (m_linkDecl)
354        m_linkDecl->setParent(document()->elementSheet());
355
356    HTMLElement::didMoveToNewOwnerDocument();
357}
358
359} // namespace WebCore
360