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/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?
isAtShadowBoundary(const Element * 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
addIntrinsicMargins(RenderStyle * style)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
equivalentBlockDisplay(EDisplay display,bool isFloating,bool strictParsing)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.
doesNotInheritTextDecoration(const RenderStyle * style,const Element * e)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().
isInTopLayer(const Element * element,const RenderStyle * style)144 static bool isInTopLayer(const Element* element, const RenderStyle* style)
145 {
146 return (element && element->isInTopLayer()) || (style && style->styleType() == BACKDROP);
147 }
148
isDisplayFlexibleBox(EDisplay display)149 static bool isDisplayFlexibleBox(EDisplay display)
150 {
151 return display == FLEX || display == INLINE_FLEX;
152 }
153
isDisplayGridBox(EDisplay display)154 static bool isDisplayGridBox(EDisplay display)
155 {
156 return display == GRID || display == INLINE_GRID;
157 }
158
parentStyleForcesZIndexToCreateStackingContext(const RenderStyle * parentStyle)159 static bool parentStyleForcesZIndexToCreateStackingContext(const RenderStyle* parentStyle)
160 {
161 return isDisplayFlexibleBox(parentStyle->display()) || isDisplayGridBox(parentStyle->display());
162 }
163
adjustRenderStyle(RenderStyle * style,RenderStyle * parentStyle,Element * e)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
adjustGridItemPosition(RenderStyle * style,RenderStyle * parentStyle) const394 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