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/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 41 namespace blink { 42 43 using namespace HTMLNames; 44 45 inline HTMLBodyElement::HTMLBodyElement(Document& document) 46 : HTMLElement(bodyTag, document) 47 { 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 UseCounter::count(document(), UseCounter::BgPropertiesFixed); 86 addPropertyToPresentationAttributeStyle(style, CSSPropertyBackgroundAttachment, CSSValueFixed); 87 } 88 } else 89 HTMLElement::collectStyleForPresentationAttribute(name, value, style); 90 } 91 92 void 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 157 Node::InsertionNotificationRequest HTMLBodyElement::insertedInto(ContainerNode* insertionPoint) 158 { 159 HTMLElement::insertedInto(insertionPoint); 160 return InsertionShouldCallDidNotifySubtreeInsertions; 161 } 162 163 void 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 180 bool HTMLBodyElement::isURLAttribute(const Attribute& attribute) const 181 { 182 return attribute.name() == backgroundAttr || HTMLElement::isURLAttribute(attribute); 183 } 184 185 bool HTMLBodyElement::hasLegalLinkAttribute(const QualifiedName& name) const 186 { 187 return name == backgroundAttr || HTMLElement::hasLegalLinkAttribute(name); 188 } 189 190 const QualifiedName& HTMLBodyElement::subResourceAttributeName() const 191 { 192 return backgroundAttr; 193 } 194 195 bool 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 202 static 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. 225 double 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 245 void 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 272 double 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 292 void 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 319 int 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 328 int 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