1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2000 Simon Hausmann (hausmann (at) kde.org) 5 * (C) 2001 Dirk Mueller (mueller (at) 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 "bindings/v8/ScriptEventListener.h" 28 #include "core/CSSValueKeywords.h" 29 #include "core/HTMLNames.h" 30 #include "core/css/CSSImageValue.h" 31 #include "core/css/parser/BisonCSSParser.h" 32 #include "core/css/StylePropertySet.h" 33 #include "core/dom/Attribute.h" 34 #include "core/frame/FrameView.h" 35 #include "core/frame/LocalFrame.h" 36 #include "core/html/HTMLFrameElementBase.h" 37 #include "core/html/parser/HTMLParserIdioms.h" 38 #include "core/rendering/RenderBox.h" 39 40 namespace WebCore { 41 42 using namespace HTMLNames; 43 44 inline HTMLBodyElement::HTMLBodyElement(Document& document) 45 : HTMLElement(bodyTag, document) 46 { 47 ScriptWrappable::init(this); 48 } 49 50 DEFINE_NODE_FACTORY(HTMLBodyElement) 51 52 HTMLBodyElement::~HTMLBodyElement() 53 { 54 } 55 56 bool HTMLBodyElement::isPresentationAttribute(const QualifiedName& name) const 57 { 58 if (name == backgroundAttr || name == marginwidthAttr || name == leftmarginAttr || name == marginheightAttr || name == topmarginAttr || name == bgcolorAttr || name == textAttr || name == bgpropertiesAttr) 59 return true; 60 return HTMLElement::isPresentationAttribute(name); 61 } 62 63 void HTMLBodyElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 64 { 65 if (name == backgroundAttr) { 66 String url = stripLeadingAndTrailingHTMLSpaces(value); 67 if (!url.isEmpty()) { 68 RefPtrWillBeRawPtr<CSSImageValue> imageValue = CSSImageValue::create(url, document().completeURL(url)); 69 imageValue->setInitiator(localName()); 70 imageValue->setReferrer(Referrer(document().outgoingReferrer(), document().referrerPolicy())); 71 style->setProperty(CSSProperty(CSSPropertyBackgroundImage, imageValue.release())); 72 } 73 } else if (name == marginwidthAttr || name == leftmarginAttr) { 74 addHTMLLengthToStyle(style, CSSPropertyMarginRight, value); 75 addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value); 76 } else if (name == marginheightAttr || name == topmarginAttr) { 77 addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value); 78 addHTMLLengthToStyle(style, CSSPropertyMarginTop, value); 79 } else if (name == bgcolorAttr) { 80 addHTMLColorToStyle(style, CSSPropertyBackgroundColor, value); 81 } else if (name == textAttr) { 82 addHTMLColorToStyle(style, CSSPropertyColor, value); 83 } else if (name == bgpropertiesAttr) { 84 if (equalIgnoringCase(value, "fixed")) 85 addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundAttachment, CSSValueFixed); 86 } else 87 HTMLElement::collectStyleForPresentationAttribute(name, value, style); 88 } 89 90 void HTMLBodyElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 91 { 92 if (name == vlinkAttr || name == alinkAttr || name == linkAttr) { 93 if (value.isNull()) { 94 if (name == linkAttr) 95 document().textLinkColors().resetLinkColor(); 96 else if (name == vlinkAttr) 97 document().textLinkColors().resetVisitedLinkColor(); 98 else 99 document().textLinkColors().resetActiveLinkColor(); 100 } else { 101 RGBA32 color; 102 if (BisonCSSParser::parseColor(color, value, !document().inQuirksMode())) { 103 if (name == linkAttr) 104 document().textLinkColors().setLinkColor(color); 105 else if (name == vlinkAttr) 106 document().textLinkColors().setVisitedLinkColor(color); 107 else 108 document().textLinkColors().setActiveLinkColor(color); 109 } 110 } 111 112 setNeedsStyleRecalc(SubtreeStyleChange); 113 } else if (name == onloadAttr) 114 document().setWindowAttributeEventListener(EventTypeNames::load, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 115 else if (name == onbeforeunloadAttr) 116 document().setWindowAttributeEventListener(EventTypeNames::beforeunload, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 117 else if (name == onunloadAttr) 118 document().setWindowAttributeEventListener(EventTypeNames::unload, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 119 else if (name == onpagehideAttr) 120 document().setWindowAttributeEventListener(EventTypeNames::pagehide, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 121 else if (name == onpageshowAttr) 122 document().setWindowAttributeEventListener(EventTypeNames::pageshow, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 123 else if (name == onpopstateAttr) 124 document().setWindowAttributeEventListener(EventTypeNames::popstate, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 125 else if (name == onblurAttr) 126 document().setWindowAttributeEventListener(EventTypeNames::blur, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 127 else if (name == onerrorAttr) 128 document().setWindowAttributeEventListener(EventTypeNames::error, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 129 else if (name == onfocusAttr) 130 document().setWindowAttributeEventListener(EventTypeNames::focus, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 131 else if (RuntimeEnabledFeatures::orientationEventEnabled() && name == onorientationchangeAttr) 132 document().setWindowAttributeEventListener(EventTypeNames::orientationchange, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 133 else if (name == onhashchangeAttr) 134 document().setWindowAttributeEventListener(EventTypeNames::hashchange, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 135 else if (name == onmessageAttr) 136 document().setWindowAttributeEventListener(EventTypeNames::message, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 137 else if (name == onresizeAttr) 138 document().setWindowAttributeEventListener(EventTypeNames::resize, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 139 else if (name == onscrollAttr) 140 document().setWindowAttributeEventListener(EventTypeNames::scroll, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 141 else if (name == onselectionchangeAttr) 142 document().setAttributeEventListener(EventTypeNames::selectionchange, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 143 else if (name == onstorageAttr) 144 document().setWindowAttributeEventListener(EventTypeNames::storage, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 145 else if (name == ononlineAttr) 146 document().setWindowAttributeEventListener(EventTypeNames::online, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 147 else if (name == onofflineAttr) 148 document().setWindowAttributeEventListener(EventTypeNames::offline, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 149 else if (name == onlanguagechangeAttr) 150 document().setWindowAttributeEventListener(EventTypeNames::languagechange, createAttributeEventListener(document().frame(), name, value, eventParameterName())); 151 else 152 HTMLElement::parseAttribute(name, value); 153 } 154 155 Node::InsertionNotificationRequest HTMLBodyElement::insertedInto(ContainerNode* insertionPoint) 156 { 157 HTMLElement::insertedInto(insertionPoint); 158 return InsertionShouldCallDidNotifySubtreeInsertions; 159 } 160 161 void HTMLBodyElement::didNotifySubtreeInsertionsToDocument() 162 { 163 // FIXME: It's surprising this is web compatible since it means a 164 // marginwidth and marginheight attribute can magically appear on the <body> 165 // of all documents embedded through <iframe> or <frame>. 166 Element* ownerElement = document().ownerElement(); 167 if (!isHTMLFrameElementBase(ownerElement)) 168 return; 169 HTMLFrameElementBase& ownerFrameElement = toHTMLFrameElementBase(*ownerElement); 170 int marginWidth = ownerFrameElement.marginWidth(); 171 int marginHeight = ownerFrameElement.marginHeight(); 172 if (marginWidth != -1) 173 setIntegralAttribute(marginwidthAttr, marginWidth); 174 if (marginHeight != -1) 175 setIntegralAttribute(marginheightAttr, marginHeight); 176 } 177 178 bool HTMLBodyElement::isURLAttribute(const Attribute& attribute) const 179 { 180 return attribute.name() == backgroundAttr || HTMLElement::isURLAttribute(attribute); 181 } 182 183 bool HTMLBodyElement::hasLegalLinkAttribute(const QualifiedName& name) const 184 { 185 return name == backgroundAttr || HTMLElement::hasLegalLinkAttribute(name); 186 } 187 188 const QualifiedName& HTMLBodyElement::subResourceAttributeName() const 189 { 190 return backgroundAttr; 191 } 192 193 bool HTMLBodyElement::supportsFocus() const 194 { 195 // This override is needed because the inherited method bails if the parent is editable. 196 // The <body> should be focusable even if <html> is editable. 197 return rendererIsEditable() || HTMLElement::supportsFocus(); 198 } 199 200 static int adjustForZoom(int value, Document* document) 201 { 202 LocalFrame* frame = document->frame(); 203 float zoomFactor = frame->pageZoomFactor(); 204 if (zoomFactor == 1) 205 return value; 206 // Needed because of truncation (rather than rounding) when scaling up. 207 if (zoomFactor > 1) 208 value++; 209 return static_cast<int>(value / zoomFactor); 210 } 211 212 // Blink, Gecko and Presto's quirks mode implementations of overflow set to the 213 // body element differ from IE's: the formers can create a scrollable area for the 214 // body element that is not the same as the root elements's one. On IE's quirks mode 215 // though, as body is the root element, body's and the root element's scrollable areas, 216 // if any, are the same. 217 // In order words, a <body> will only have an overflow clip (that differs from 218 // documentElement's) if both html and body nodes have its overflow set to either hidden, 219 // auto or scroll. 220 // That said, Blink's {set}scroll{Top,Left} behaviors match Gecko's: even if there is a non-overflown 221 // scrollable area, scrolling should not get propagated to the viewport in neither strict 222 // or quirks modes. 223 int HTMLBodyElement::scrollLeft() 224 { 225 Document& document = this->document(); 226 document.updateLayoutIgnorePendingStylesheets(); 227 228 if (RuntimeEnabledFeatures::scrollTopLeftInteropEnabled()) { 229 RenderBox* render = renderBox(); 230 if (!render) 231 return 0; 232 if (render->hasOverflowClip()) 233 return adjustForAbsoluteZoom(render->scrollLeft(), render); 234 if (!document.inQuirksMode()) 235 return 0; 236 } 237 238 FrameView* view = document.view(); 239 return view ? adjustForZoom(view->scrollX(), &document) : 0; 240 } 241 242 void HTMLBodyElement::setScrollLeft(int scrollLeft) 243 { 244 Document& document = this->document(); 245 document.updateLayoutIgnorePendingStylesheets(); 246 247 if (RuntimeEnabledFeatures::scrollTopLeftInteropEnabled()) { 248 RenderBox* render = renderBox(); 249 if (!render) 250 return; 251 if (render->hasOverflowClip()) { 252 // FIXME: Investigate how are other browsers casting to int (rounding, ceiling, ...). 253 render->setScrollLeft(static_cast<int>(scrollLeft * render->style()->effectiveZoom())); 254 return; 255 } 256 if (!document.inQuirksMode()) 257 return; 258 } 259 260 LocalFrame* frame = document.frame(); 261 if (!frame) 262 return; 263 FrameView* view = frame->view(); 264 if (!view) 265 return; 266 view->setScrollPosition(IntPoint(static_cast<int>(scrollLeft * frame->pageZoomFactor()), view->scrollY())); 267 } 268 269 int HTMLBodyElement::scrollTop() 270 { 271 Document& document = this->document(); 272 document.updateLayoutIgnorePendingStylesheets(); 273 274 if (RuntimeEnabledFeatures::scrollTopLeftInteropEnabled()) { 275 RenderBox* render = renderBox(); 276 if (!render) 277 return 0; 278 if (render->hasOverflowClip()) 279 return adjustForAbsoluteZoom(render->scrollTop(), render); 280 if (!document.inQuirksMode()) 281 return 0; 282 } 283 284 FrameView* view = document.view(); 285 return view ? adjustForZoom(view->scrollY(), &document) : 0; 286 } 287 288 void HTMLBodyElement::setScrollTop(int scrollTop) 289 { 290 Document& document = this->document(); 291 document.updateLayoutIgnorePendingStylesheets(); 292 293 if (RuntimeEnabledFeatures::scrollTopLeftInteropEnabled()) { 294 RenderBox* render = renderBox(); 295 if (!render) 296 return; 297 if (render->hasOverflowClip()) { 298 // FIXME: Investigate how are other browsers casting to int (rounding, ceiling, ...). 299 render->setScrollTop(static_cast<int>(scrollTop * render->style()->effectiveZoom())); 300 return; 301 } 302 if (!document.inQuirksMode()) 303 return; 304 } 305 306 LocalFrame* frame = document.frame(); 307 if (!frame) 308 return; 309 FrameView* view = frame->view(); 310 if (!view) 311 return; 312 view->setScrollPosition(IntPoint(view->scrollX(), static_cast<int>(scrollTop * frame->pageZoomFactor()))); 313 } 314 315 int HTMLBodyElement::scrollHeight() 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->contentsHeight(), &document) : 0; 322 } 323 324 int HTMLBodyElement::scrollWidth() 325 { 326 // Update the document's layout. 327 Document& document = this->document(); 328 document.updateLayoutIgnorePendingStylesheets(); 329 FrameView* view = document.view(); 330 return view ? adjustForZoom(view->contentsWidth(), &document) : 0; 331 } 332 333 } // namespace WebCore 334