1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 2004-2005 Allan Sandfeld Jensen (kde (at) carewolf.com) 4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit (at) nickshanks.com) 5 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. 6 * Copyright (C) 2007 Alexey Proskuryakov <ap (at) webkit.org> 7 * Copyright (C) 2007, 2008 Eric Seidel <eric (at) webkit.org> 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. 10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. 11 * Copyright (C) 2013 Google Inc. All rights reserved. 12 * 13 * This library is free software; you can redistribute it and/or 14 * modify it under the terms of the GNU Library General Public 15 * License as published by the Free Software Foundation; either 16 * version 2 of the License, or (at your option) any later version. 17 * 18 * This library is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 21 * Library General Public License for more details. 22 * 23 * You should have received a copy of the GNU Library General Public License 24 * along with this library; see the file COPYING.LIB. If not, write to 25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 26 * Boston, MA 02110-1301, USA. 27 */ 28 29 #include "config.h" 30 #include "core/css/resolver/StyleAdjuster.h" 31 32 #include "core/HTMLNames.h" 33 #include "core/SVGNames.h" 34 #include "core/dom/ContainerNode.h" 35 #include "core/dom/Document.h" 36 #include "core/dom/Element.h" 37 #include "core/dom/NodeRenderStyle.h" 38 #include "core/html/HTMLIFrameElement.h" 39 #include "core/html/HTMLInputElement.h" 40 #include "core/html/HTMLPlugInElement.h" 41 #include "core/html/HTMLTableCellElement.h" 42 #include "core/html/HTMLTextAreaElement.h" 43 #include "core/frame/FrameView.h" 44 #include "core/frame/Settings.h" 45 #include "core/rendering/RenderReplaced.h" 46 #include "core/rendering/RenderTheme.h" 47 #include "core/rendering/style/GridPosition.h" 48 #include "core/rendering/style/RenderStyle.h" 49 #include "core/rendering/style/RenderStyleConstants.h" 50 #include "core/svg/SVGSVGElement.h" 51 #include "platform/Length.h" 52 #include "platform/transforms/TransformOperations.h" 53 #include "wtf/Assertions.h" 54 55 namespace blink { 56 57 using namespace HTMLNames; 58 59 static EDisplay equivalentBlockDisplay(EDisplay display, bool isFloating, bool strictParsing) 60 { 61 switch (display) { 62 case BLOCK: 63 case TABLE: 64 case BOX: 65 case FLEX: 66 case GRID: 67 return display; 68 69 case LIST_ITEM: 70 // It is a WinIE bug that floated list items lose their bullets, so we'll emulate the quirk, but only in quirks mode. 71 if (!strictParsing && isFloating) 72 return BLOCK; 73 return display; 74 case INLINE_TABLE: 75 return TABLE; 76 case INLINE_BOX: 77 return BOX; 78 case INLINE_FLEX: 79 return FLEX; 80 case INLINE_GRID: 81 return GRID; 82 83 case INLINE: 84 case INLINE_BLOCK: 85 case TABLE_ROW_GROUP: 86 case TABLE_HEADER_GROUP: 87 case TABLE_FOOTER_GROUP: 88 case TABLE_ROW: 89 case TABLE_COLUMN_GROUP: 90 case TABLE_COLUMN: 91 case TABLE_CELL: 92 case TABLE_CAPTION: 93 return BLOCK; 94 case NONE: 95 ASSERT_NOT_REACHED(); 96 return NONE; 97 } 98 ASSERT_NOT_REACHED(); 99 return BLOCK; 100 } 101 102 // CSS requires text-decoration to be reset at each DOM element for tables, 103 // inline blocks, inline tables, shadow DOM crossings, floating elements, 104 // and absolute or relatively positioned elements. 105 static bool doesNotInheritTextDecoration(const RenderStyle* style, const Element* e) 106 { 107 return style->display() == TABLE || style->display() == INLINE_TABLE 108 || style->display() == INLINE_BLOCK || style->display() == INLINE_BOX || isAtShadowBoundary(e) 109 || style->isFloating() || style->hasOutOfFlowPosition(); 110 } 111 112 // FIXME: This helper is only needed because pseudoStyleForElement passes a null 113 // element to adjustRenderStyle, so we can't just use element->isInTopLayer(). 114 static bool isInTopLayer(const Element* element, const RenderStyle* style) 115 { 116 return (element && element->isInTopLayer()) || (style && style->styleType() == BACKDROP); 117 } 118 119 static bool parentStyleForcesZIndexToCreateStackingContext(const RenderStyle* parentStyle) 120 { 121 return parentStyle->isDisplayFlexibleOrGridBox(); 122 } 123 124 static bool hasWillChangeThatCreatesStackingContext(const RenderStyle* style) 125 { 126 for (size_t i = 0; i < style->willChangeProperties().size(); ++i) { 127 switch (style->willChangeProperties()[i]) { 128 case CSSPropertyOpacity: 129 case CSSPropertyTransform: 130 case CSSPropertyWebkitTransform: 131 case CSSPropertyTransformStyle: 132 case CSSPropertyWebkitTransformStyle: 133 case CSSPropertyPerspective: 134 case CSSPropertyWebkitPerspective: 135 case CSSPropertyWebkitMask: 136 case CSSPropertyWebkitMaskBoxImage: 137 case CSSPropertyWebkitClipPath: 138 case CSSPropertyWebkitBoxReflect: 139 case CSSPropertyWebkitFilter: 140 case CSSPropertyZIndex: 141 case CSSPropertyPosition: 142 return true; 143 case CSSPropertyMixBlendMode: 144 case CSSPropertyIsolation: 145 if (RuntimeEnabledFeatures::cssCompositingEnabled()) 146 return true; 147 break; 148 default: 149 break; 150 } 151 } 152 return false; 153 } 154 155 void StyleAdjuster::adjustRenderStyle(RenderStyle* style, RenderStyle* parentStyle, Element *e, const CachedUAStyle* cachedUAStyle) 156 { 157 ASSERT(parentStyle); 158 159 if (style->display() != NONE) { 160 if (e) 161 adjustStyleForTagName(style, parentStyle, *e); 162 163 // Per the spec, position 'static' and 'relative' in the top layer compute to 'absolute'. 164 if (isInTopLayer(e, style) && (style->position() == StaticPosition || style->position() == RelativePosition)) 165 style->setPosition(AbsolutePosition); 166 167 // Absolute/fixed positioned elements, floating elements and the document element need block-like outside display. 168 if (style->hasOutOfFlowPosition() || style->isFloating() || (e && e->document().documentElement() == e)) 169 style->setDisplay(equivalentBlockDisplay(style->display(), style->isFloating(), !m_useQuirksModeStyles)); 170 171 adjustStyleForDisplay(style, parentStyle); 172 } 173 174 // Make sure our z-index value is only applied if the object is positioned. 175 if (style->position() == StaticPosition && !parentStyleForcesZIndexToCreateStackingContext(parentStyle)) 176 style->setHasAutoZIndex(); 177 178 // Auto z-index becomes 0 for the root element and transparent objects. This prevents 179 // cases where objects that should be blended as a single unit end up with a non-transparent 180 // object wedged in between them. Auto z-index also becomes 0 for objects that specify transforms/masks/reflections. 181 if (style->hasAutoZIndex() && ((e && e->document().documentElement() == e) 182 || style->hasOpacity() 183 || style->hasTransformRelatedProperty() 184 || style->hasMask() 185 || style->clipPath() 186 || style->boxReflect() 187 || style->hasFilter() 188 || style->hasBlendMode() 189 || style->hasIsolation() 190 || style->position() == FixedPosition 191 || isInTopLayer(e, style) 192 || hasWillChangeThatCreatesStackingContext(style))) 193 style->setZIndex(0); 194 195 // will-change:transform should result in the same rendering behavior as having a transform, 196 // including the creation of a containing block for fixed position descendants. 197 if (!style->hasTransform() && (style->willChangeProperties().contains(CSSPropertyWebkitTransform) || style->willChangeProperties().contains(CSSPropertyTransform))) { 198 bool makeIdentity = true; 199 style->setTransform(TransformOperations(makeIdentity)); 200 } 201 202 if (doesNotInheritTextDecoration(style, e)) 203 style->clearAppliedTextDecorations(); 204 205 style->applyTextDecorations(); 206 207 if (style->overflowX() != OVISIBLE || style->overflowY() != OVISIBLE) 208 adjustOverflow(style); 209 210 // Cull out any useless layers and also repeat patterns into additional layers. 211 style->adjustBackgroundLayers(); 212 style->adjustMaskLayers(); 213 214 // Let the theme also have a crack at adjusting the style. 215 if (style->hasAppearance()) 216 RenderTheme::theme().adjustStyle(style, e, cachedUAStyle); 217 218 // If we have first-letter pseudo style, transitions, or animations, do not share this style. 219 if (style->hasPseudoStyle(FIRST_LETTER) || style->transitions() || style->animations()) 220 style->setUnique(); 221 222 // FIXME: when dropping the -webkit prefix on transform-style, we should also have opacity < 1 cause flattening. 223 if (style->preserves3D() && (style->overflowX() != OVISIBLE 224 || style->overflowY() != OVISIBLE 225 || style->hasFilter())) 226 style->setTransformStyle3D(TransformStyle3DFlat); 227 228 if (e && e->isSVGElement()) { 229 // Only the root <svg> element in an SVG document fragment tree honors css position 230 if (!(isSVGSVGElement(*e) && e->parentNode() && !e->parentNode()->isSVGElement())) 231 style->setPosition(RenderStyle::initialPosition()); 232 233 // SVG text layout code expects us to be a block-level style element. 234 if ((isSVGForeignObjectElement(*e) || isSVGTextElement(*e)) && style->isDisplayInlineType()) 235 style->setDisplay(BLOCK); 236 } 237 238 if (e && e->renderStyle() && e->renderStyle()->textAutosizingMultiplier() != 1) { 239 // Preserve the text autosizing multiplier on style recalc. 240 // (The autosizer will update it during layout if it needs to be changed.) 241 style->setTextAutosizingMultiplier(e->renderStyle()->textAutosizingMultiplier()); 242 style->setUnique(); 243 } 244 245 adjustStyleForAlignment(*style, *parentStyle); 246 } 247 248 void StyleAdjuster::adjustStyleForAlignment(RenderStyle& style, const RenderStyle& parentStyle) 249 { 250 bool isFlexOrGrid = style.isDisplayFlexibleOrGridBox(); 251 bool absolutePositioned = style.position() == AbsolutePosition; 252 253 // If the inherited value of justify-items includes the legacy keyword, 'auto' 254 // computes to the the inherited value. 255 // Otherwise, auto computes to: 256 // - 'stretch' for flex containers and grid containers. 257 // - 'start' for everything else. 258 if (style.justifyItems() == ItemPositionAuto) { 259 if (parentStyle.justifyItemsPositionType() == LegacyPosition) { 260 style.setJustifyItems(parentStyle.justifyItems()); 261 style.setJustifyItemsPositionType(parentStyle.justifyItemsPositionType()); 262 } else if (isFlexOrGrid) { 263 style.setJustifyItems(ItemPositionStretch); 264 } 265 } 266 267 // The 'auto' keyword computes to 'stretch' on absolutely-positioned elements, 268 // and to the computed value of justify-items on the parent (minus 269 // any legacy keywords) on all other boxes. 270 if (style.justifySelf() == ItemPositionAuto) { 271 if (absolutePositioned) { 272 style.setJustifySelf(ItemPositionStretch); 273 } else { 274 style.setJustifySelf(parentStyle.justifyItems()); 275 style.setJustifySelfOverflowAlignment(parentStyle.justifyItemsOverflowAlignment()); 276 } 277 } 278 279 // The 'auto' keyword computes to: 280 // - 'stretch' for flex containers and grid containers, 281 // - 'start' for everything else. 282 if (style.alignItems() == ItemPositionAuto) { 283 if (isFlexOrGrid) 284 style.setAlignItems(ItemPositionStretch); 285 } 286 287 // The 'auto' keyword computes to 'stretch' on absolutely-positioned elements, 288 // and to the computed value of align-items on the parent (minus 289 // any 'legacy' keywords) on all other boxes. 290 if (style.alignSelf() == ItemPositionAuto) { 291 if (absolutePositioned) { 292 style.setAlignSelf(ItemPositionStretch); 293 } else { 294 style.setAlignSelf(parentStyle.alignItems()); 295 style.setAlignSelfOverflowAlignment(parentStyle.alignItemsOverflowAlignment()); 296 } 297 } 298 } 299 300 void StyleAdjuster::adjustStyleForTagName(RenderStyle* style, RenderStyle* parentStyle, Element& element) 301 { 302 // <div> and <span> are the most common elements on the web, we skip all the work for them. 303 if (isHTMLDivElement(element) || isHTMLSpanElement(element)) 304 return; 305 306 if (isHTMLTableCellElement(element)) { 307 // If we have a <td> that specifies a float property, in quirks mode we just drop the float property. 308 // FIXME: Why is this only <td> and not <th>? 309 if (element.hasTagName(tdTag) && m_useQuirksModeStyles) { 310 style->setDisplay(TABLE_CELL); 311 style->setFloating(NoFloat); 312 } 313 // FIXME: We shouldn't be overriding start/-webkit-auto like this. Do it in html.css instead. 314 // Table headers with a text-align of -webkit-auto will change the text-align to center. 315 if (element.hasTagName(thTag) && style->textAlign() == TASTART) 316 style->setTextAlign(CENTER); 317 if (style->whiteSpace() == KHTML_NOWRAP) { 318 // Figure out if we are really nowrapping or if we should just 319 // use normal instead. If the width of the cell is fixed, then 320 // we don't actually use NOWRAP. 321 if (style->width().isFixed()) 322 style->setWhiteSpace(NORMAL); 323 else 324 style->setWhiteSpace(NOWRAP); 325 } 326 return; 327 } 328 329 if (isHTMLTableElement(element)) { 330 // Sites commonly use display:inline/block on <td>s and <table>s. In quirks mode we force 331 // these tags to retain their display types. 332 if (m_useQuirksModeStyles) 333 style->setDisplay(style->isDisplayInlineType() ? INLINE_TABLE : TABLE); 334 // Tables never support the -webkit-* values for text-align and will reset back to the default. 335 if (style->textAlign() == WEBKIT_LEFT || style->textAlign() == WEBKIT_CENTER || style->textAlign() == WEBKIT_RIGHT) 336 style->setTextAlign(TASTART); 337 return; 338 } 339 340 if (isHTMLFrameElement(element) || isHTMLFrameSetElement(element)) { 341 // Frames and framesets never honor position:relative or position:absolute. This is necessary to 342 // fix a crash where a site tries to position these objects. They also never honor display. 343 style->setPosition(StaticPosition); 344 style->setDisplay(BLOCK); 345 return; 346 } 347 348 if (isHTMLRTElement(element)) { 349 // Ruby text does not support float or position. This might change with evolution of the specification. 350 style->setPosition(StaticPosition); 351 style->setFloating(NoFloat); 352 return; 353 } 354 355 if (isHTMLLegendElement(element)) { 356 style->setDisplay(BLOCK); 357 return; 358 } 359 360 if (isHTMLMarqueeElement(element)) { 361 // For now, <marquee> requires an overflow clip to work properly. 362 style->setOverflowX(OHIDDEN); 363 style->setOverflowY(OHIDDEN); 364 return; 365 } 366 367 if (isHTMLTextAreaElement(element)) { 368 // Textarea considers overflow visible as auto. 369 style->setOverflowX(style->overflowX() == OVISIBLE ? OAUTO : style->overflowX()); 370 style->setOverflowY(style->overflowY() == OVISIBLE ? OAUTO : style->overflowY()); 371 return; 372 } 373 374 if (isHTMLPlugInElement(element)) { 375 style->setRequiresAcceleratedCompositingForExternalReasons(toHTMLPlugInElement(element).shouldAccelerate()); 376 377 // Plugins should get the standard replaced width/height instead of 'auto'. 378 // Replaced renderers get this for free, and fallback content doesn't count. 379 if (toHTMLPlugInElement(element).usePlaceholderContent()) { 380 if (style->width().isAuto()) 381 style->setWidth(Length(RenderReplaced::defaultWidth, Fixed)); 382 if (style->height().isAuto()) 383 style->setHeight(Length(RenderReplaced::defaultHeight, Fixed)); 384 } 385 386 return; 387 } 388 } 389 390 void StyleAdjuster::adjustOverflow(RenderStyle* style) 391 { 392 ASSERT(style->overflowX() != OVISIBLE || style->overflowY() != OVISIBLE); 393 394 // If either overflow value is not visible, change to auto. 395 if (style->overflowX() == OVISIBLE && style->overflowY() != OVISIBLE) { 396 // FIXME: Once we implement pagination controls, overflow-x should default to hidden 397 // if overflow-y is set to -webkit-paged-x or -webkit-page-y. For now, we'll let it 398 // default to auto so we can at least scroll through the pages. 399 style->setOverflowX(OAUTO); 400 } else if (style->overflowY() == OVISIBLE && style->overflowX() != OVISIBLE) { 401 style->setOverflowY(OAUTO); 402 } 403 404 // Table rows, sections and the table itself will support overflow:hidden and will ignore scroll/auto. 405 // FIXME: Eventually table sections will support auto and scroll. 406 if (style->display() == TABLE || style->display() == INLINE_TABLE 407 || style->display() == TABLE_ROW_GROUP || style->display() == TABLE_ROW) { 408 if (style->overflowX() != OVISIBLE && style->overflowX() != OHIDDEN) 409 style->setOverflowX(OVISIBLE); 410 if (style->overflowY() != OVISIBLE && style->overflowY() != OHIDDEN) 411 style->setOverflowY(OVISIBLE); 412 } 413 414 // Menulists should have visible overflow 415 if (style->appearance() == MenulistPart) { 416 style->setOverflowX(OVISIBLE); 417 style->setOverflowY(OVISIBLE); 418 } 419 } 420 421 void StyleAdjuster::adjustStyleForDisplay(RenderStyle* style, RenderStyle* parentStyle) 422 { 423 if (style->display() == BLOCK && !style->isFloating()) 424 return; 425 426 // FIXME: Don't support this mutation for pseudo styles like first-letter or first-line, since it's not completely 427 // clear how that should work. 428 if (style->display() == INLINE && style->styleType() == NOPSEUDO && style->writingMode() != parentStyle->writingMode()) 429 style->setDisplay(INLINE_BLOCK); 430 431 // After performing the display mutation, check table rows. We do not honor position: relative table rows or cells. 432 // This has been established for position: relative in CSS2.1 (and caused a crash in containingBlock() 433 // on some sites). 434 if ((style->display() == TABLE_HEADER_GROUP || style->display() == TABLE_ROW_GROUP 435 || style->display() == TABLE_FOOTER_GROUP || style->display() == TABLE_ROW) 436 && style->position() == RelativePosition) 437 style->setPosition(StaticPosition); 438 439 // writing-mode does not apply to table row groups, table column groups, table rows, and table columns. 440 // FIXME: Table cells should be allowed to be perpendicular or flipped with respect to the table, though. 441 if (style->display() == TABLE_COLUMN || style->display() == TABLE_COLUMN_GROUP || style->display() == TABLE_FOOTER_GROUP 442 || style->display() == TABLE_HEADER_GROUP || style->display() == TABLE_ROW || style->display() == TABLE_ROW_GROUP 443 || style->display() == TABLE_CELL) 444 style->setWritingMode(parentStyle->writingMode()); 445 446 // FIXME: Since we don't support block-flow on flexible boxes yet, disallow setting 447 // of block-flow to anything other than TopToBottomWritingMode. 448 // https://bugs.webkit.org/show_bug.cgi?id=46418 - Flexible box support. 449 if (style->writingMode() != TopToBottomWritingMode && (style->display() == BOX || style->display() == INLINE_BOX)) 450 style->setWritingMode(TopToBottomWritingMode); 451 452 if (parentStyle->isDisplayFlexibleOrGridBox()) { 453 style->setFloating(NoFloat); 454 style->setDisplay(equivalentBlockDisplay(style->display(), style->isFloating(), !m_useQuirksModeStyles)); 455 } 456 } 457 458 } 459