d90638251b3b3cc0672c32262b578487480e6f14
[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 "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/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 "core/svg/SVGSVGElement.h"
50 #include "platform/Length.h"
51 #include "platform/transforms/TransformOperations.h"
52 #include "wtf/Assertions.h"
53
54 namespace WebCore {
55
56 using namespace HTMLNames;
57
58 // FIXME: This is duplicated with StyleResolver.cpp
59 // Perhaps this should move onto ElementResolveContext or even Element?
60 static inline bool isAtShadowBoundary(const Element* element)
61 {
62     if (!element)
63         return false;
64     ContainerNode* parentNode = element->parentNode();
65     return parentNode && parentNode->isShadowRoot();
66 }
67
68
69 static void addIntrinsicMargins(RenderStyle* style)
70 {
71     // Intrinsic margin value.
72     const int intrinsicMargin = 2 * style->effectiveZoom();
73
74     // FIXME: Using width/height alone and not also dealing with min-width/max-width is flawed.
75     // FIXME: Using "quirk" to decide the margin wasn't set is kind of lame.
76     if (style->width().isIntrinsicOrAuto()) {
77         if (style->marginLeft().quirk())
78             style->setMarginLeft(Length(intrinsicMargin, Fixed));
79         if (style->marginRight().quirk())
80             style->setMarginRight(Length(intrinsicMargin, Fixed));
81     }
82
83     if (style->height().isAuto()) {
84         if (style->marginTop().quirk())
85             style->setMarginTop(Length(intrinsicMargin, Fixed));
86         if (style->marginBottom().quirk())
87             style->setMarginBottom(Length(intrinsicMargin, Fixed));
88     }
89 }
90
91 static EDisplay equivalentBlockDisplay(EDisplay display, bool isFloating, bool strictParsing)
92 {
93     switch (display) {
94     case BLOCK:
95     case TABLE:
96     case BOX:
97     case FLEX:
98     case GRID:
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 INLINE_BLOCK:
117     case TABLE_ROW_GROUP:
118     case TABLE_HEADER_GROUP:
119     case TABLE_FOOTER_GROUP:
120     case TABLE_ROW:
121     case TABLE_COLUMN_GROUP:
122     case TABLE_COLUMN:
123     case TABLE_CELL:
124     case TABLE_CAPTION:
125         return BLOCK;
126     case NONE:
127         ASSERT_NOT_REACHED();
128         return NONE;
129     }
130     ASSERT_NOT_REACHED();
131     return BLOCK;
132 }
133
134 // CSS requires text-decoration to be reset at each DOM element for tables,
135 // inline blocks, inline tables, shadow DOM crossings, floating elements,
136 // and absolute or relatively positioned elements.
137 static bool doesNotInheritTextDecoration(const RenderStyle* style, const Element* e)
138 {
139     return style->display() == TABLE || style->display() == INLINE_TABLE
140         || style->display() == INLINE_BLOCK || style->display() == INLINE_BOX || isAtShadowBoundary(e)
141         || style->isFloating() || style->hasOutOfFlowPosition();
142 }
143
144 // FIXME: This helper is only needed because pseudoStyleForElement passes a null
145 // element to adjustRenderStyle, so we can't just use element->isInTopLayer().
146 static bool isInTopLayer(const Element* element, const RenderStyle* style)
147 {
148     return (element && element->isInTopLayer()) || (style && style->styleType() == BACKDROP);
149 }
150
151 static bool isDisplayFlexibleBox(EDisplay display)
152 {
153     return display == FLEX || display == INLINE_FLEX;
154 }
155
156 static bool isDisplayGridBox(EDisplay display)
157 {
158     return display == GRID || display == INLINE_GRID;
159 }
160
161 static bool parentStyleForcesZIndexToCreateStackingContext(const RenderStyle* parentStyle)
162 {
163     return isDisplayFlexibleBox(parentStyle->display()) || isDisplayGridBox(parentStyle->display());
164 }
165
166 static bool hasWillChangeThatCreatesStackingContext(const RenderStyle* style)
167 {
168     for (size_t i = 0; i < style->willChangeProperties().size(); ++i) {
169         switch (style->willChangeProperties()[i]) {
170         case CSSPropertyOpacity:
171         case CSSPropertyTransform:
172         case CSSPropertyWebkitTransform:
173         case CSSPropertyTransformStyle:
174         case CSSPropertyWebkitTransformStyle:
175         case CSSPropertyPerspective:
176         case CSSPropertyWebkitPerspective:
177         case CSSPropertyWebkitMask:
178         case CSSPropertyWebkitMaskBoxImage:
179         case CSSPropertyWebkitClipPath:
180         case CSSPropertyWebkitBoxReflect:
181         case CSSPropertyWebkitFilter:
182         case CSSPropertyZIndex:
183         case CSSPropertyPosition:
184             return true;
185         case CSSPropertyMixBlendMode:
186         case CSSPropertyIsolation:
187             if (RuntimeEnabledFeatures::cssCompositingEnabled())
188                 return true;
189             break;
190         default:
191             break;
192         }
193     }
194     return false;
195 }
196
197 void StyleAdjuster::adjustRenderStyle(RenderStyle* style, RenderStyle* parentStyle, Element *e, const CachedUAStyle* cachedUAStyle)
198 {
199     ASSERT(parentStyle);
200
201     // Cache our original display.
202     style->setOriginalDisplay(style->display());
203
204     if (style->display() != NONE) {
205         if (e)
206             adjustStyleForTagName(style, parentStyle, *e);
207
208         // Per the spec, position 'static' and 'relative' in the top layer compute to 'absolute'.
209         if (isInTopLayer(e, style) && (style->position() == StaticPosition || style->position() == RelativePosition))
210             style->setPosition(AbsolutePosition);
211
212         // Absolute/fixed positioned elements, floating elements and the document element need block-like outside display.
213         if (style->hasOutOfFlowPosition() || style->isFloating() || (e && e->document().documentElement() == e))
214             style->setDisplay(equivalentBlockDisplay(style->display(), style->isFloating(), !m_useQuirksModeStyles));
215
216         adjustStyleForDisplay(style, parentStyle);
217     }
218
219     // Make sure our z-index value is only applied if the object is positioned.
220     if (style->position() == StaticPosition && !parentStyleForcesZIndexToCreateStackingContext(parentStyle))
221         style->setHasAutoZIndex();
222
223     // Auto z-index becomes 0 for the root element and transparent objects. This prevents
224     // cases where objects that should be blended as a single unit end up with a non-transparent
225     // object wedged in between them. Auto z-index also becomes 0 for objects that specify transforms/masks/reflections.
226     if (style->hasAutoZIndex() && ((e && e->document().documentElement() == e)
227         || style->opacity() < 1.0f
228         || style->hasTransformRelatedProperty()
229         || style->hasMask()
230         || style->clipPath()
231         || style->boxReflect()
232         || style->hasFilter()
233         || style->hasBlendMode()
234         || style->hasIsolation()
235         || style->position() == StickyPosition
236         || style->position() == FixedPosition
237         || isInTopLayer(e, style)
238         || hasWillChangeThatCreatesStackingContext(style)))
239         style->setZIndex(0);
240
241     // will-change:transform should result in the same rendering behavior as having a transform,
242     // including the creation of a containing block for fixed position descendants.
243     if (!style->hasTransform() && (style->willChangeProperties().contains(CSSPropertyWebkitTransform) || style->willChangeProperties().contains(CSSPropertyTransform))) {
244         bool makeIdentity = true;
245         style->setTransform(TransformOperations(makeIdentity));
246     }
247
248     if (doesNotInheritTextDecoration(style, e))
249         style->clearAppliedTextDecorations();
250
251     style->applyTextDecorations();
252
253     if (style->overflowX() != OVISIBLE || style->overflowY() != OVISIBLE)
254         adjustOverflow(style);
255
256     // Cull out any useless layers and also repeat patterns into additional layers.
257     style->adjustBackgroundLayers();
258     style->adjustMaskLayers();
259
260     // Let the theme also have a crack at adjusting the style.
261     if (style->hasAppearance())
262         RenderTheme::theme().adjustStyle(style, e, cachedUAStyle);
263
264     // If we have first-letter pseudo style, transitions, or animations, do not share this style.
265     if (style->hasPseudoStyle(FIRST_LETTER) || style->transitions() || style->animations())
266         style->setUnique();
267
268     // FIXME: when dropping the -webkit prefix on transform-style, we should also have opacity < 1 cause flattening.
269     if (style->preserves3D() && (style->overflowX() != OVISIBLE
270         || style->overflowY() != OVISIBLE
271         || style->hasFilter()))
272         style->setTransformStyle3D(TransformStyle3DFlat);
273
274     if (e && e->isSVGElement()) {
275         // Only the root <svg> element in an SVG document fragment tree honors css position
276         if (!(isSVGSVGElement(*e) && e->parentNode() && !e->parentNode()->isSVGElement()))
277             style->setPosition(RenderStyle::initialPosition());
278
279         // SVG text layout code expects us to be a block-level style element.
280         if ((isSVGForeignObjectElement(*e) || isSVGTextElement(*e)) && style->isDisplayInlineType())
281             style->setDisplay(BLOCK);
282     }
283
284     if (e && e->renderStyle() && e->renderStyle()->textAutosizingMultiplier() != 1) {
285         // Preserve the text autosizing multiplier on style recalc.
286         // (The autosizer will update it during layout if it needs to be changed.)
287         style->setTextAutosizingMultiplier(e->renderStyle()->textAutosizingMultiplier());
288         style->setUnique();
289     }
290 }
291
292 void StyleAdjuster::adjustStyleForTagName(RenderStyle* style, RenderStyle* parentStyle, Element& element)
293 {
294     // <div> and <span> are the most common elements on the web, we skip all the work for them.
295     if (isHTMLDivElement(element) || isHTMLSpanElement(element))
296         return;
297
298     if (isHTMLTableCellElement(element)) {
299         // If we have a <td> that specifies a float property, in quirks mode we just drop the float property.
300         // FIXME: Why is this only <td> and not <th>?
301         if (element.hasTagName(tdTag) && m_useQuirksModeStyles) {
302             style->setDisplay(TABLE_CELL);
303             style->setFloating(NoFloat);
304         }
305         // FIXME: We shouldn't be overriding start/-webkit-auto like this. Do it in html.css instead.
306         // Table headers with a text-align of -webkit-auto will change the text-align to center.
307         if (element.hasTagName(thTag) && style->textAlign() == TASTART)
308             style->setTextAlign(CENTER);
309         if (style->whiteSpace() == KHTML_NOWRAP) {
310             // Figure out if we are really nowrapping or if we should just
311             // use normal instead. If the width of the cell is fixed, then
312             // we don't actually use NOWRAP.
313             if (style->width().isFixed())
314                 style->setWhiteSpace(NORMAL);
315             else
316                 style->setWhiteSpace(NOWRAP);
317         }
318         return;
319     }
320
321     if (isHTMLTableElement(element)) {
322         // Sites commonly use display:inline/block on <td>s and <table>s. In quirks mode we force
323         // these tags to retain their display types.
324         if (m_useQuirksModeStyles)
325             style->setDisplay(style->isDisplayInlineType() ? INLINE_TABLE : TABLE);
326         // Tables never support the -webkit-* values for text-align and will reset back to the default.
327         if (style->textAlign() == WEBKIT_LEFT || style->textAlign() == WEBKIT_CENTER || style->textAlign() == WEBKIT_RIGHT)
328             style->setTextAlign(TASTART);
329         return;
330     }
331
332     if (isHTMLFrameElement(element) || isHTMLFrameSetElement(element)) {
333         // Frames and framesets never honor position:relative or position:absolute. This is necessary to
334         // fix a crash where a site tries to position these objects. They also never honor display.
335         style->setPosition(StaticPosition);
336         style->setDisplay(BLOCK);
337         return;
338     }
339
340     if (isHTMLRTElement(element)) {
341         // Ruby text does not support float or position. This might change with evolution of the specification.
342         style->setPosition(StaticPosition);
343         style->setFloating(NoFloat);
344         return;
345     }
346
347     if (isHTMLLegendElement(element)) {
348         style->setDisplay(BLOCK);
349         return;
350     }
351
352     if (isHTMLMarqueeElement(element)) {
353         // For now, <marquee> requires an overflow clip to work properly.
354         style->setOverflowX(OHIDDEN);
355         style->setOverflowY(OHIDDEN);
356         return;
357     }
358
359     if (element.isFormControlElement()) {
360         if (isHTMLTextAreaElement(element)) {
361             // Textarea considers overflow visible as auto.
362             style->setOverflowX(style->overflowX() == OVISIBLE ? OAUTO : style->overflowX());
363             style->setOverflowY(style->overflowY() == OVISIBLE ? OAUTO : style->overflowY());
364         }
365
366         // Important: Intrinsic margins get added to controls before the theme has adjusted the style,
367         // since the theme will alter fonts and heights/widths.
368         //
369         // Don't apply intrinsic margins to image buttons. The designer knows how big the images are,
370         // so we have to treat all image buttons as though they were explicitly sized.
371         if (style->fontSize() >= 11 && (!isHTMLInputElement(element) || !toHTMLInputElement(element).isImageButton()))
372             addIntrinsicMargins(style);
373         return;
374     }
375
376     if (isHTMLPlugInElement(element)) {
377         style->setRequiresAcceleratedCompositingForExternalReasons(toHTMLPlugInElement(element).shouldAccelerate());
378         return;
379     }
380 }
381
382 void StyleAdjuster::adjustOverflow(RenderStyle* style)
383 {
384     ASSERT(style->overflowX() != OVISIBLE || style->overflowY() != OVISIBLE);
385
386     // If either overflow value is not visible, change to auto.
387     if (style->overflowX() == OVISIBLE && style->overflowY() != OVISIBLE) {
388         // FIXME: Once we implement pagination controls, overflow-x should default to hidden
389         // if overflow-y is set to -webkit-paged-x or -webkit-page-y. For now, we'll let it
390         // default to auto so we can at least scroll through the pages.
391         style->setOverflowX(OAUTO);
392     } else if (style->overflowY() == OVISIBLE && style->overflowX() != OVISIBLE) {
393         style->setOverflowY(OAUTO);
394     }
395
396     // Table rows, sections and the table itself will support overflow:hidden and will ignore scroll/auto.
397     // FIXME: Eventually table sections will support auto and scroll.
398     if (style->display() == TABLE || style->display() == INLINE_TABLE
399         || style->display() == TABLE_ROW_GROUP || style->display() == TABLE_ROW) {
400         if (style->overflowX() != OVISIBLE && style->overflowX() != OHIDDEN)
401             style->setOverflowX(OVISIBLE);
402         if (style->overflowY() != OVISIBLE && style->overflowY() != OHIDDEN)
403             style->setOverflowY(OVISIBLE);
404     }
405
406     // Menulists should have visible overflow
407     if (style->appearance() == MenulistPart) {
408         style->setOverflowX(OVISIBLE);
409         style->setOverflowY(OVISIBLE);
410     }
411 }
412
413 void StyleAdjuster::adjustStyleForDisplay(RenderStyle* style, RenderStyle* parentStyle)
414 {
415     if (style->display() == BLOCK && !style->isFloating())
416         return;
417
418     // FIXME: Don't support this mutation for pseudo styles like first-letter or first-line, since it's not completely
419     // clear how that should work.
420     if (style->display() == INLINE && style->styleType() == NOPSEUDO && style->writingMode() != parentStyle->writingMode())
421         style->setDisplay(INLINE_BLOCK);
422
423     // After performing the display mutation, check table rows. We do not honor position: relative table rows or cells.
424     // This has been established for position: relative in CSS2.1 (and caused a crash in containingBlock()
425     // on some sites).
426     if ((style->display() == TABLE_HEADER_GROUP || style->display() == TABLE_ROW_GROUP
427         || style->display() == TABLE_FOOTER_GROUP || style->display() == TABLE_ROW)
428         && style->position() == RelativePosition)
429         style->setPosition(StaticPosition);
430
431     // Cannot support position: sticky for table columns and column groups because current code is only doing
432     // background painting through columns / column groups
433     if ((style->display() == TABLE_COLUMN_GROUP || style->display() == TABLE_COLUMN)
434         && style->position() == StickyPosition)
435         style->setPosition(StaticPosition);
436
437     // writing-mode does not apply to table row groups, table column groups, table rows, and table columns.
438     // FIXME: Table cells should be allowed to be perpendicular or flipped with respect to the table, though.
439     if (style->display() == TABLE_COLUMN || style->display() == TABLE_COLUMN_GROUP || style->display() == TABLE_FOOTER_GROUP
440         || style->display() == TABLE_HEADER_GROUP || style->display() == TABLE_ROW || style->display() == TABLE_ROW_GROUP
441         || style->display() == TABLE_CELL)
442         style->setWritingMode(parentStyle->writingMode());
443
444     // FIXME: Since we don't support block-flow on flexible boxes yet, disallow setting
445     // of block-flow to anything other than TopToBottomWritingMode.
446     // https://bugs.webkit.org/show_bug.cgi?id=46418 - Flexible box support.
447     if (style->writingMode() != TopToBottomWritingMode && (style->display() == BOX || style->display() == INLINE_BOX))
448         style->setWritingMode(TopToBottomWritingMode);
449
450     if (isDisplayFlexibleBox(parentStyle->display()) || isDisplayGridBox(parentStyle->display())) {
451         style->setFloating(NoFloat);
452         style->setDisplay(equivalentBlockDisplay(style->display(), style->isFloating(), !m_useQuirksModeStyles));
453     }
454 }
455
456 }