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