Home | History | Annotate | Download | only in resolver
      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