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