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-2017 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 "CSSImageValue.h"
28#include "CSSParser.h"
29#include "CSSValueKeywords.h"
30#include "DOMWindow.h"
31#include "DOMWrapperWorld.h"
32#include "EventNames.h"
33#include "HTMLFrameElement.h"
34#include "HTMLIFrameElement.h"
35#include "HTMLNames.h"
36#include "HTMLParserIdioms.h"
37#include "StyleProperties.h"
38#include <wtf/IsoMallocInlines.h>
39#include <wtf/NeverDestroyed.h>
40
41namespace WebCore {
42
43WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLBodyElement);
44
45using namespace HTMLNames;
46
47HTMLBodyElement::HTMLBodyElement(const QualifiedName& tagName, Document& document)
48 : HTMLElement(tagName, document)
49{
50 ASSERT(hasTagName(bodyTag));
51}
52
53Ref<HTMLBodyElement> HTMLBodyElement::create(Document& document)
54{
55 return adoptRef(*new HTMLBodyElement(bodyTag, document));
56}
57
58Ref<HTMLBodyElement> HTMLBodyElement::create(const QualifiedName& tagName, Document& document)
59{
60 return adoptRef(*new HTMLBodyElement(tagName, document));
61}
62
63HTMLBodyElement::~HTMLBodyElement() = default;
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, MutableStyleProperties& style)
73{
74 if (name == backgroundAttr) {
75 String url = stripLeadingAndTrailingHTMLSpaces(value);
76 if (!url.isEmpty()) {
77 auto imageValue = CSSImageValue::create(document().completeURL(url), LoadedFromOpaqueSource::No);
78 imageValue.get().setInitiator(localName());
79 style.setProperty(CSSProperty(CSSPropertyBackgroundImage, WTFMove(imageValue)));
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 (equalLettersIgnoringASCIICase(value, "fixed"))
93 addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundAttachment, CSSValueFixed);
94 } else
95 HTMLElement::collectStyleForPresentationAttribute(name, value, style);
96}
97
98HTMLElement::EventHandlerNameMap HTMLBodyElement::createWindowEventHandlerNameMap()
99{
100 static const QualifiedName* const table[] = {
101 &onafterprintAttr.get(),
102 &onbeforeprintAttr.get(),
103 &onbeforeunloadAttr.get(),
104 &onblurAttr.get(),
105 &onerrorAttr.get(),
106 &onfocusAttr.get(),
107 &onfocusinAttr.get(),
108 &onfocusoutAttr.get(),
109 &onhashchangeAttr.get(),
110 &onlanguagechangeAttr.get(),
111 &onloadAttr.get(),
112 &onmessageAttr.get(),
113 &onofflineAttr.get(),
114 &ononlineAttr.get(),
115 &onorientationchangeAttr.get(),
116 &onpagehideAttr.get(),
117 &onpageshowAttr.get(),
118 &onpopstateAttr.get(),
119 &onresizeAttr.get(),
120 &onscrollAttr.get(),
121 &onstorageAttr.get(),
122 &onunloadAttr.get(),
123 &onwebkitmouseforcechangedAttr.get(),
124 &onwebkitmouseforcedownAttr.get(),
125 &onwebkitmouseforceupAttr.get(),
126 &onwebkitmouseforcewillbeginAttr.get(),
127 &onwebkitwillrevealbottomAttr.get(),
128 &onwebkitwillrevealleftAttr.get(),
129 &onwebkitwillrevealrightAttr.get(),
130 &onwebkitwillrevealtopAttr.get(),
131 };
132
133 EventHandlerNameMap map;
134 populateEventHandlerNameMap(map, table);
135 return map;
136}
137
138const AtomicString& HTMLBodyElement::eventNameForWindowEventHandlerAttribute(const QualifiedName& attributeName)
139{
140 static NeverDestroyed<EventHandlerNameMap> map = createWindowEventHandlerNameMap();
141 return eventNameForEventHandlerAttribute(attributeName, map.get());
142}
143
144void HTMLBodyElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
145{
146 if (name == vlinkAttr || name == alinkAttr || name == linkAttr) {
147 if (value.isNull()) {
148 if (name == linkAttr)
149 document().resetLinkColor();
150 else if (name == vlinkAttr)
151 document().resetVisitedLinkColor();
152 else
153 document().resetActiveLinkColor();
154 } else {
155 Color color = CSSParser::parseColor(value, !document().inQuirksMode());
156 if (color.isValid()) {
157 if (name == linkAttr)
158 document().setLinkColor(color);
159 else if (name == vlinkAttr)
160 document().setVisitedLinkColor(color);
161 else
162 document().setActiveLinkColor(color);
163 }
164 }
165
166 invalidateStyleForSubtree();
167 return;
168 }
169
170 if (name == onselectionchangeAttr) {
171 document().setAttributeEventListener(eventNames().selectionchangeEvent, name, value, mainThreadNormalWorld());
172 return;
173 }
174
175 auto& eventName = eventNameForWindowEventHandlerAttribute(name);
176 if (!eventName.isNull()) {
177 document().setWindowAttributeEventListener(eventName, name, value, mainThreadNormalWorld());
178 return;
179 }
180
181 HTMLElement::parseAttribute(name, value);
182}
183
184Node::InsertedIntoAncestorResult HTMLBodyElement::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
185{
186 HTMLElement::insertedIntoAncestor(insertionType, parentOfInsertedTree);
187 if (!insertionType.connectedToDocument)
188 return InsertedIntoAncestorResult::Done;
189
190 // FIXME: It's surprising this is web compatible since it means a marginwidth and marginheight attribute can
191 // magically appear on the <body> of all documents embedded through <iframe> or <frame>.
192 // FIXME: Perhaps this code should be in attach() instead of here.
193 auto ownerElement = makeRefPtr(document().ownerElement());
194 if (!is<HTMLFrameElementBase>(ownerElement))
195 return InsertedIntoAncestorResult::Done;
196
197 return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
198}
199
200void HTMLBodyElement::didFinishInsertingNode()
201{
202 auto ownerElement = makeRefPtr(document().ownerElement());
203 RELEASE_ASSERT(is<HTMLFrameElementBase>(ownerElement));
204 auto& ownerFrameElement = downcast<HTMLFrameElementBase>(*ownerElement);
205
206 // Read values from the owner before setting any attributes, since setting an attribute can run arbitrary
207 // JavaScript, which might delete the owner element.
208 int marginWidth = ownerFrameElement.marginWidth();
209 int marginHeight = ownerFrameElement.marginHeight();
210
211 if (marginWidth != -1)
212 setIntegralAttribute(marginwidthAttr, marginWidth);
213 if (marginHeight != -1)
214 setIntegralAttribute(marginheightAttr, marginHeight);
215}
216
217bool HTMLBodyElement::isURLAttribute(const Attribute& attribute) const
218{
219 return attribute.name() == backgroundAttr || HTMLElement::isURLAttribute(attribute);
220}
221
222bool HTMLBodyElement::supportsFocus() const
223{
224 return hasEditableStyle() || HTMLElement::supportsFocus();
225}
226
227void HTMLBodyElement::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
228{
229 HTMLElement::addSubresourceAttributeURLs(urls);
230
231 addSubresourceURL(urls, document().completeURL(attributeWithoutSynchronization(backgroundAttr)));
232}
233
234} // namespace WebCore
235