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