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 "HTMLNames.h" 33 #include "SVGNames.h" 34 #include "core/dom/ContainerNode.h" 35 #include "core/dom/Document.h" 36 #include "core/dom/Element.h" 37 #include "core/html/HTMLHtmlElement.h" 38 #include "core/html/HTMLIFrameElement.h" 39 #include "core/html/HTMLInputElement.h" 40 #include "core/html/HTMLTableElement.h" 41 #include "core/html/HTMLTextAreaElement.h" 42 #include "core/frame/FrameView.h" 43 #include "core/frame/Settings.h" 44 #include "core/rendering/Pagination.h" 45 #include "core/rendering/RenderTheme.h" 46 #include "core/rendering/style/GridPosition.h" 47 #include "core/rendering/style/RenderStyle.h" 48 #include "core/rendering/style/RenderStyleConstants.h" 49 #include "platform/Length.h" 50 #include "wtf/Assertions.h" 51 52 namespace WebCore { 53 54 using namespace HTMLNames; 55 56 // FIXME: This is duplicated with StyleResolver.cpp 57 // Perhaps this should move onto ElementResolveContext or even Element? 58 static inline bool isAtShadowBoundary(const Element* element) 59 { 60 if (!element) 61 return false; 62 ContainerNode* parentNode = element->parentNode(); 63 return parentNode && parentNode->isShadowRoot(); 64 } 65 66 67 static void addIntrinsicMargins(RenderStyle* style) 68 { 69 // Intrinsic margin value. 70 const int intrinsicMargin = 2 * style->effectiveZoom(); 71 72 // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed. 73 // FIXME: Using "quirk" to decide the margin wasn't set is kind of lame. 74 if (style->width().isIntrinsicOrAuto()) { 75 if (style->marginLeft().quirk()) 76 style->setMarginLeft(Length(intrinsicMargin, Fixed)); 77 if (style->marginRight().quirk()) 78 style->setMarginRight(Length(intrinsicMargin, Fixed)); 79 } 80 81 if (style->height().isAuto()) { 82 if (style->marginTop().quirk()) 83 style->setMarginTop(Length(intrinsicMargin, Fixed)); 84 if (style->marginBottom().quirk()) 85 style->setMarginBottom(Length(intrinsicMargin, Fixed)); 86 } 87 } 88 89 static EDisplay equivalentBlockDisplay(EDisplay display, bool isFloating, bool strictParsing) 90 { 91 switch (display) { 92 case BLOCK: 93 case TABLE: 94 case BOX: 95 case FLEX: 96 case GRID: 97 return display; 98 99 case LIST_ITEM: 100 // It is a WinIE bug that floated list items lose their bullets, so we'll emulate the quirk, but only in quirks mode. 101 if (!strictParsing && isFloating) 102 return BLOCK; 103 return display; 104 case INLINE_TABLE: 105 return TABLE; 106 case INLINE_BOX: 107 return BOX; 108 case INLINE_FLEX: 109 return FLEX; 110 case INLINE_GRID: 111 return GRID; 112 113 case INLINE: 114 case INLINE_BLOCK: 115 case TABLE_ROW_GROUP: 116 case TABLE_HEADER_GROUP: 117 case TABLE_FOOTER_GROUP: 118 case TABLE_ROW: 119 case TABLE_COLUMN_GROUP: 120 case TABLE_COLUMN: 121 case TABLE_CELL: 122 case TABLE_CAPTION: 123 return BLOCK; 124 case NONE: 125 ASSERT_NOT_REACHED(); 126 return NONE; 127 } 128 ASSERT_NOT_REACHED(); 129 return BLOCK; 130 } 131 132 // CSS requires text-decoration to be reset at each DOM element for tables, 133 // inline blocks, inline tables, shadow DOM crossings, floating elements, 134 // and absolute or relatively positioned elements. 135 static bool doesNotInheritTextDecoration(const RenderStyle* style, const Element* e) 136 { 137 return style->display() == TABLE || style->display() == INLINE_TABLE 138 || style->display() == INLINE_BLOCK || style->display() == INLINE_BOX || isAtShadowBoundary(e) 139 || style->isFloating() || style->hasOutOfFlowPosition(); 140 } 141 142 // FIXME: This helper is only needed because pseudoStyleForElement passes a null 143 // element to adjustRenderStyle, so we can't just use element->isInTopLayer(). 144 static bool isInTopLayer(const Element* element, const RenderStyle* style) 145 { 146 return (element && element->isInTopLayer()) || (style && style->styleType() == BACKDROP); 147 } 148 149 static bool isDisplayFlexibleBox(EDisplay display) 150 { 151 return display == FLEX || display == INLINE_FLEX; 152 } 153 154 static bool isDisplayGridBox(EDisplay display) 155 { 156 return display == GRID || display == INLINE_GRID; 157 } 158 159 static bool parentStyleForcesZIndexToCreateStackingContext(const RenderStyle* parentStyle) 160 { 161 return isDisplayFlexibleBox(parentStyle->display()) || isDisplayGridBox(parentStyle->display()); 162 } 163 164 void StyleAdjuster::adjustRenderStyle(RenderStyle* style, RenderStyle* parentStyle, Element *e) 165 { 166 ASSERT(parentStyle); 167 168 // Cache our original display. 169 style->setOriginalDisplay(style->display()); 170 171 if (style->display() != NONE) { 172 // If we have a <td> that specifies a float property, in quirks mode we just drop the float 173 // property. 174 // Sites also commonly use display:inline/block on <td>s and <table>s. In quirks mode we force 175 // these tags to retain their display types. 176 if (m_useQuirksModeStyles && e) { 177 if (e->hasTagName(tdTag)) { 178 style->setDisplay(TABLE_CELL); 179 style->setFloating(NoFloat); 180 } else if (isHTMLTableElement(e)) { 181 style->setDisplay(style->isDisplayInlineType() ? INLINE_TABLE : TABLE); 182 } 183 } 184 185 if (e && (e->hasTagName(tdTag) || e->hasTagName(thTag))) { 186 if (style->whiteSpace() == KHTML_NOWRAP) { 187 // Figure out if we are really nowrapping or if we should just 188 // use normal instead. If the width of the cell is fixed, then 189 // we don't actually use NOWRAP. 190 if (style->width().isFixed()) 191 style->setWhiteSpace(NORMAL); 192 else 193 style->setWhiteSpace(NOWRAP); 194 } 195 } 196 197 // Tables never support the -webkit-* values for text-align and will reset back to the default. 198 if (e && isHTMLTableElement(e) && (style->textAlign() == WEBKIT_LEFT || style->textAlign() == WEBKIT_CENTER || style->textAlign() == WEBKIT_RIGHT)) 199 style->setTextAlign(TASTART); 200 201 // Frames and framesets never honor position:relative or position:absolute. This is necessary to 202 // fix a crash where a site tries to position these objects. They also never honor display. 203 if (e && (e->hasTagName(frameTag) || e->hasTagName(framesetTag))) { 204 style->setPosition(StaticPosition); 205 style->setDisplay(BLOCK); 206 } 207 208 // Ruby text does not support float or position. This might change with evolution of the specification. 209 if (e && e->hasTagName(rtTag)) { 210 style->setPosition(StaticPosition); 211 style->setFloating(NoFloat); 212 } 213 214 // FIXME: We shouldn't be overriding start/-webkit-auto like this. Do it in html.css instead. 215 // Table headers with a text-align of -webkit-auto will change the text-align to center. 216 if (e && e->hasTagName(thTag) && style->textAlign() == TASTART) 217 style->setTextAlign(CENTER); 218 219 if (e && e->hasTagName(legendTag)) 220 style->setDisplay(BLOCK); 221 222 // Per the spec, position 'static' and 'relative' in the top layer compute to 'absolute'. 223 if (isInTopLayer(e, style) && (style->position() == StaticPosition || style->position() == RelativePosition)) 224 style->setPosition(AbsolutePosition); 225 226 // Absolute/fixed positioned elements, floating elements and the document element need block-like outside display. 227 if (style->hasOutOfFlowPosition() || style->isFloating() || (e && e->document().documentElement() == e)) 228 style->setDisplay(equivalentBlockDisplay(style->display(), style->isFloating(), !m_useQuirksModeStyles)); 229 230 // FIXME: Don't support this mutation for pseudo styles like first-letter or first-line, since it's not completely 231 // clear how that should work. 232 if (style->display() == INLINE && style->styleType() == NOPSEUDO && style->writingMode() != parentStyle->writingMode()) 233 style->setDisplay(INLINE_BLOCK); 234 235 // After performing the display mutation, check table rows. We do not honor position:relative or position:sticky on 236 // table rows or cells. This has been established for position:relative in CSS2.1 (and caused a crash in containingBlock() 237 // on some sites). 238 if ((style->display() == TABLE_HEADER_GROUP || style->display() == TABLE_ROW_GROUP 239 || style->display() == TABLE_FOOTER_GROUP || style->display() == TABLE_ROW) 240 && style->hasInFlowPosition()) 241 style->setPosition(StaticPosition); 242 243 // writing-mode does not apply to table row groups, table column groups, table rows, and table columns. 244 // FIXME: Table cells should be allowed to be perpendicular or flipped with respect to the table, though. 245 if (style->display() == TABLE_COLUMN || style->display() == TABLE_COLUMN_GROUP || style->display() == TABLE_FOOTER_GROUP 246 || style->display() == TABLE_HEADER_GROUP || style->display() == TABLE_ROW || style->display() == TABLE_ROW_GROUP 247 || style->display() == TABLE_CELL) 248 style->setWritingMode(parentStyle->writingMode()); 249 250 // FIXME: Since we don't support block-flow on flexible boxes yet, disallow setting 251 // of block-flow to anything other than TopToBottomWritingMode. 252 // https://bugs.webkit.org/show_bug.cgi?id=46418 - Flexible box support. 253 if (style->writingMode() != TopToBottomWritingMode && (style->display() == BOX || style->display() == INLINE_BOX)) 254 style->setWritingMode(TopToBottomWritingMode); 255 256 if (isDisplayFlexibleBox(parentStyle->display()) || isDisplayGridBox(parentStyle->display())) { 257 style->setFloating(NoFloat); 258 style->setDisplay(equivalentBlockDisplay(style->display(), style->isFloating(), !m_useQuirksModeStyles)); 259 } 260 } 261 262 // Make sure our z-index value is only applied if the object is positioned. 263 if (style->position() == StaticPosition && !parentStyleForcesZIndexToCreateStackingContext(parentStyle)) 264 style->setHasAutoZIndex(); 265 266 // Auto z-index becomes 0 for the root element and transparent objects. This prevents 267 // cases where objects that should be blended as a single unit end up with a non-transparent 268 // object wedged in between them. Auto z-index also becomes 0 for objects that specify transforms/masks/reflections. 269 if (style->hasAutoZIndex() && ((e && e->document().documentElement() == e) 270 || style->opacity() < 1.0f 271 || style->hasTransformRelatedProperty() 272 || style->hasMask() 273 || style->clipPath() 274 || style->boxReflect() 275 || style->hasFilter() 276 || style->hasBlendMode() 277 || style->hasIsolation() 278 || style->position() == StickyPosition 279 || (style->position() == FixedPosition && e && e->document().settings() && e->document().settings()->fixedPositionCreatesStackingContext()) 280 || isInTopLayer(e, style) 281 || style->hasFlowFrom() 282 )) 283 style->setZIndex(0); 284 285 // Textarea considers overflow visible as auto. 286 if (e && isHTMLTextAreaElement(e)) { 287 style->setOverflowX(style->overflowX() == OVISIBLE ? OAUTO : style->overflowX()); 288 style->setOverflowY(style->overflowY() == OVISIBLE ? OAUTO : style->overflowY()); 289 } 290 291 // For now, <marquee> requires an overflow clip to work properly. 292 if (e && e->hasTagName(marqueeTag)) { 293 style->setOverflowX(OHIDDEN); 294 style->setOverflowY(OHIDDEN); 295 } 296 297 if (doesNotInheritTextDecoration(style, e)) 298 style->setTextDecorationsInEffect(style->textDecoration()); 299 else 300 style->addToTextDecorationsInEffect(style->textDecoration()); 301 302 // If either overflow value is not visible, change to auto. 303 if (style->overflowX() == OVISIBLE && style->overflowY() != OVISIBLE) { 304 // FIXME: Once we implement pagination controls, overflow-x should default to hidden 305 // if overflow-y is set to -webkit-paged-x or -webkit-page-y. For now, we'll let it 306 // default to auto so we can at least scroll through the pages. 307 style->setOverflowX(OAUTO); 308 } else if (style->overflowY() == OVISIBLE && style->overflowX() != OVISIBLE) { 309 style->setOverflowY(OAUTO); 310 } 311 312 // Call setStylesForPaginationMode() if a pagination mode is set for any non-root elements. If these 313 // styles are specified on a root element, then they will be incorporated in 314 // StyleAdjuster::styleForDocument(). 315 if ((style->overflowY() == OPAGEDX || style->overflowY() == OPAGEDY) && !(e && (isHTMLHtmlElement(e) || e->hasTagName(bodyTag)))) 316 Pagination::setStylesForPaginationMode(WebCore::paginationModeForRenderStyle(style), style); 317 318 // Table rows, sections and the table itself will support overflow:hidden and will ignore scroll/auto. 319 // FIXME: Eventually table sections will support auto and scroll. 320 if (style->display() == TABLE || style->display() == INLINE_TABLE 321 || style->display() == TABLE_ROW_GROUP || style->display() == TABLE_ROW) { 322 if (style->overflowX() != OVISIBLE && style->overflowX() != OHIDDEN) 323 style->setOverflowX(OVISIBLE); 324 if (style->overflowY() != OVISIBLE && style->overflowY() != OHIDDEN) 325 style->setOverflowY(OVISIBLE); 326 } 327 328 // Menulists should have visible overflow 329 if (style->appearance() == MenulistPart) { 330 style->setOverflowX(OVISIBLE); 331 style->setOverflowY(OVISIBLE); 332 } 333 334 // Cull out any useless layers and also repeat patterns into additional layers. 335 style->adjustBackgroundLayers(); 336 style->adjustMaskLayers(); 337 338 // Important: Intrinsic margins get added to controls before the theme has adjusted the style, since the theme will 339 // alter fonts and heights/widths. 340 if (e && e->isFormControlElement() && style->fontSize() >= 11) { 341 // Don't apply intrinsic margins to image buttons. The designer knows how big the images are, 342 // so we have to treat all image buttons as though they were explicitly sized. 343 if (!e->hasTagName(inputTag) || !toHTMLInputElement(e)->isImageButton()) 344 addIntrinsicMargins(style); 345 } 346 347 // Let the theme also have a crack at adjusting the style. 348 if (style->hasAppearance()) 349 RenderTheme::theme().adjustStyle(style, e, m_cachedUAStyle); 350 351 // If we have first-letter pseudo style, do not share this style. 352 if (style->hasPseudoStyle(FIRST_LETTER)) 353 style->setUnique(); 354 355 // FIXME: when dropping the -webkit prefix on transform-style, we should also have opacity < 1 cause flattening. 356 if (style->preserves3D() && (style->overflowX() != OVISIBLE 357 || style->overflowY() != OVISIBLE 358 || style->hasFilter())) 359 style->setTransformStyle3D(TransformStyle3DFlat); 360 361 // Seamless iframes behave like blocks. Map their display to inline-block when marked inline. 362 if (e && e->hasTagName(iframeTag) && style->display() == INLINE && toHTMLIFrameElement(e)->shouldDisplaySeamlessly()) 363 style->setDisplay(INLINE_BLOCK); 364 365 adjustGridItemPosition(style, parentStyle); 366 367 if (e && e->isSVGElement()) { 368 // Spec: http://www.w3.org/TR/SVG/masking.html#OverflowProperty 369 if (style->overflowY() == OSCROLL) 370 style->setOverflowY(OHIDDEN); 371 else if (style->overflowY() == OAUTO) 372 style->setOverflowY(OVISIBLE); 373 374 if (style->overflowX() == OSCROLL) 375 style->setOverflowX(OHIDDEN); 376 else if (style->overflowX() == OAUTO) 377 style->setOverflowX(OVISIBLE); 378 379 // Only the root <svg> element in an SVG document fragment tree honors css position 380 if (!(e->hasTagName(SVGNames::svgTag) && e->parentNode() && !e->parentNode()->isSVGElement())) 381 style->setPosition(RenderStyle::initialPosition()); 382 383 // RenderSVGRoot handles zooming for the whole SVG subtree, so foreignObject content should 384 // not be scaled again. 385 if (e->hasTagName(SVGNames::foreignObjectTag)) 386 style->setEffectiveZoom(RenderStyle::initialZoom()); 387 388 // SVG text layout code expects us to be a block-level style element. 389 if ((e->hasTagName(SVGNames::foreignObjectTag) || e->hasTagName(SVGNames::textTag)) && style->isDisplayInlineType()) 390 style->setDisplay(BLOCK); 391 } 392 } 393 394 void StyleAdjuster::adjustGridItemPosition(RenderStyle* style, RenderStyle* parentStyle) const 395 { 396 const GridPosition& columnStartPosition = style->gridColumnStart(); 397 const GridPosition& columnEndPosition = style->gridColumnEnd(); 398 const GridPosition& rowStartPosition = style->gridRowStart(); 399 const GridPosition& rowEndPosition = style->gridRowEnd(); 400 401 // If opposing grid-placement properties both specify a grid span, they both compute to auto. 402 if (columnStartPosition.isSpan() && columnEndPosition.isSpan()) { 403 style->setGridColumnStart(GridPosition()); 404 style->setGridColumnEnd(GridPosition()); 405 } 406 407 if (rowStartPosition.isSpan() && rowEndPosition.isSpan()) { 408 style->setGridRowStart(GridPosition()); 409 style->setGridRowEnd(GridPosition()); 410 } 411 412 // Unknown named grid area compute to 'auto'. 413 const NamedGridAreaMap& map = parentStyle->namedGridArea(); 414 415 #define CLEAR_UNKNOWN_NAMED_AREA(prop, Prop) \ 416 if (prop.isNamedGridArea() && !map.contains(prop.namedGridLine())) \ 417 style->setGrid##Prop(GridPosition()); 418 419 CLEAR_UNKNOWN_NAMED_AREA(columnStartPosition, ColumnStart); 420 CLEAR_UNKNOWN_NAMED_AREA(columnEndPosition, ColumnEnd); 421 CLEAR_UNKNOWN_NAMED_AREA(rowStartPosition, RowStart); 422 CLEAR_UNKNOWN_NAMED_AREA(rowEndPosition, RowEnd); 423 } 424 425 } 426