OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) | 3 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) |
4 * Copyright (C) 2006, 2007 Nicholas Shanks (webkit@nickshanks.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. | 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> | 6 * Copyright (C) 2007 Alexey Proskuryakov <ap@webkit.org> |
7 * Copyright (C) 2007, 2008 Eric Seidel <eric@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.t
orchmobile.com/) | 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.t
orchmobile.com/) |
9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. | 9 * Copyright (c) 2011, Code Aurora Forum. All rights reserved. |
10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. | 10 * Copyright (C) Research In Motion Limited 2011. All rights reserved. |
(...skipping 18 matching lines...) Expand all Loading... |
29 #include "config.h" | 29 #include "config.h" |
30 #include "core/css/resolver/SharedStyleFinder.h" | 30 #include "core/css/resolver/SharedStyleFinder.h" |
31 | 31 |
32 #include "HTMLNames.h" | 32 #include "HTMLNames.h" |
33 #include "XMLNames.h" | 33 #include "XMLNames.h" |
34 #include "core/css/resolver/StyleResolver.h" | 34 #include "core/css/resolver/StyleResolver.h" |
35 #include "core/css/resolver/StyleResolverState.h" | 35 #include "core/css/resolver/StyleResolverState.h" |
36 #include "core/dom/ContainerNode.h" | 36 #include "core/dom/ContainerNode.h" |
37 #include "core/dom/Document.h" | 37 #include "core/dom/Document.h" |
38 #include "core/dom/Element.h" | 38 #include "core/dom/Element.h" |
| 39 #include "core/dom/ElementTraversal.h" |
39 #include "core/dom/FullscreenElementStack.h" | 40 #include "core/dom/FullscreenElementStack.h" |
40 #include "core/dom/Node.h" | 41 #include "core/dom/Node.h" |
41 #include "core/dom/NodeRenderStyle.h" | 42 #include "core/dom/NodeRenderStyle.h" |
42 #include "core/dom/QualifiedName.h" | 43 #include "core/dom/QualifiedName.h" |
43 #include "core/dom/SpaceSplitString.h" | 44 #include "core/dom/SpaceSplitString.h" |
44 #include "core/dom/shadow/ElementShadow.h" | 45 #include "core/dom/shadow/ElementShadow.h" |
45 #include "core/html/HTMLElement.h" | 46 #include "core/html/HTMLElement.h" |
46 #include "core/html/HTMLInputElement.h" | 47 #include "core/html/HTMLInputElement.h" |
47 #include "core/html/HTMLOptGroupElement.h" | 48 #include "core/html/HTMLOptGroupElement.h" |
48 #include "core/html/track/WebVTTElement.h" | 49 #include "core/html/track/WebVTTElement.h" |
49 #include "core/rendering/style/RenderStyle.h" | 50 #include "core/rendering/style/RenderStyle.h" |
50 #include "core/svg/SVGElement.h" | 51 #include "core/svg/SVGElement.h" |
51 #include "wtf/HashSet.h" | 52 #include "wtf/HashSet.h" |
52 #include "wtf/text/AtomicString.h" | 53 #include "wtf/text/AtomicString.h" |
53 | 54 |
54 namespace WebCore { | 55 namespace WebCore { |
55 | 56 |
56 using namespace HTMLNames; | 57 using namespace HTMLNames; |
57 | 58 |
58 static const unsigned cStyleSearchThreshold = 10; | |
59 static const unsigned cStyleSearchLevelThreshold = 10; | |
60 | |
61 static inline bool parentElementPreventsSharing(const Element* parentElement) | 59 static inline bool parentElementPreventsSharing(const Element* parentElement) |
62 { | 60 { |
63 if (!parentElement) | |
64 return false; | |
65 return parentElement->hasFlagsSetDuringStylingOfChildren(); | 61 return parentElement->hasFlagsSetDuringStylingOfChildren(); |
66 } | 62 } |
67 | 63 |
68 Node* SharedStyleFinder::locateCousinList(Element* parent, unsigned& visitedNode
Count) const | |
69 { | |
70 if (visitedNodeCount >= cStyleSearchThreshold * cStyleSearchLevelThreshold) | |
71 return 0; | |
72 if (!parent || !parent->isStyledElement()) | |
73 return 0; | |
74 if (parent->hasScopedHTMLStyleChild()) | |
75 return 0; | |
76 if (parent->inlineStyle()) | |
77 return 0; | |
78 if (parent->isSVGElement() && toSVGElement(parent)->animatedSMILStylePropert
ies()) | |
79 return 0; | |
80 if (parent->hasID() && m_features.idsInRules.contains(parent->idForStyleReso
lution().impl())) | |
81 return 0; | |
82 if (isShadowHost(parent) && parent->shadow()->containsActiveStyles()) | |
83 return 0; | |
84 | |
85 RenderStyle* parentStyle = parent->renderStyle(); | |
86 unsigned subcount = 0; | |
87 Node* thisCousin = parent; | |
88 Node* currentNode = parent->previousSibling(); | |
89 | |
90 // Reserve the tries for this level. This effectively makes sure that the al
gorithm | |
91 // will never go deeper than cStyleSearchLevelThreshold levels into recursio
n. | |
92 visitedNodeCount += cStyleSearchThreshold; | |
93 while (thisCousin) { | |
94 while (currentNode) { | |
95 ++subcount; | |
96 if (!currentNode->hasScopedHTMLStyleChild() && currentNode->renderSt
yle() == parentStyle && currentNode->lastChild() | |
97 && currentNode->isElementNode() && !parentElementPreventsSharing
(toElement(currentNode)) | |
98 && !toElement(currentNode)->shadow() | |
99 ) { | |
100 // Adjust for unused reserved tries. | |
101 visitedNodeCount -= cStyleSearchThreshold - subcount; | |
102 return currentNode->lastChild(); | |
103 } | |
104 if (subcount >= cStyleSearchThreshold) | |
105 return 0; | |
106 currentNode = currentNode->previousSibling(); | |
107 } | |
108 currentNode = locateCousinList(thisCousin->parentElement(), visitedNodeC
ount); | |
109 thisCousin = currentNode; | |
110 } | |
111 | |
112 return 0; | |
113 } | |
114 | |
115 | |
116 bool SharedStyleFinder::canShareStyleWithControl(const ElementResolveContext& co
ntext, Element* element) const | 64 bool SharedStyleFinder::canShareStyleWithControl(const ElementResolveContext& co
ntext, Element* element) const |
117 { | 65 { |
118 if (!element->hasTagName(inputTag) || !context.element()->hasTagName(inputTa
g)) | 66 if (!element->hasTagName(inputTag) || !context.element()->hasTagName(inputTa
g)) |
119 return false; | 67 return false; |
120 | 68 |
121 HTMLInputElement* thisInputElement = toHTMLInputElement(element); | 69 HTMLInputElement* thisInputElement = toHTMLInputElement(element); |
122 HTMLInputElement* otherInputElement = toHTMLInputElement(context.element()); | 70 HTMLInputElement* otherInputElement = toHTMLInputElement(context.element()); |
123 if (thisInputElement->elementData() != otherInputElement->elementData()) { | 71 if (thisInputElement->elementData() != otherInputElement->elementData()) { |
124 if (thisInputElement->fastGetAttribute(typeAttr) != otherInputElement->f
astGetAttribute(typeAttr)) | 72 if (thisInputElement->fastGetAttribute(typeAttr) != otherInputElement->f
astGetAttribute(typeAttr)) |
125 return false; | 73 return false; |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
206 if (context.element()->hasTagName(progressTag)) { | 154 if (context.element()->hasTagName(progressTag)) { |
207 if (context.element()->shouldAppearIndeterminate() != sharingCandidate->
shouldAppearIndeterminate()) | 155 if (context.element()->shouldAppearIndeterminate() != sharingCandidate->
shouldAppearIndeterminate()) |
208 return false; | 156 return false; |
209 } | 157 } |
210 | 158 |
211 return true; | 159 return true; |
212 } | 160 } |
213 | 161 |
214 bool SharedStyleFinder::canShareStyleWithElement(const ElementResolveContext& co
ntext, Element* element) const | 162 bool SharedStyleFinder::canShareStyleWithElement(const ElementResolveContext& co
ntext, Element* element) const |
215 { | 163 { |
| 164 if (context.element() == element) |
| 165 return false; |
| 166 Element* parent = element->parentElement(); |
216 RenderStyle* style = element->renderStyle(); | 167 RenderStyle* style = element->renderStyle(); |
217 if (!style) | 168 if (!style) |
218 return false; | 169 return false; |
| 170 if (!parent) |
| 171 return false; |
| 172 if (context.element()->parentElement()->renderStyle() != parent->renderStyle
()) |
| 173 return false; |
219 if (style->unique()) | 174 if (style->unique()) |
220 return false; | 175 return false; |
221 if (style->hasUniquePseudoStyle()) | 176 if (style->hasUniquePseudoStyle()) |
222 return false; | 177 return false; |
223 if (element->tagQName() != context.element()->tagQName()) | 178 if (element->tagQName() != context.element()->tagQName()) |
224 return false; | 179 return false; |
225 if (element->inlineStyle()) | 180 if (element->inlineStyle()) |
226 return false; | 181 return false; |
227 if (element->needsStyleRecalc()) | 182 if (element->needsStyleRecalc()) |
228 return false; | 183 return false; |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
288 return false; | 243 return false; |
289 | 244 |
290 if (element->isWebVTTElement() && context.element()->isWebVTTElement() && to
WebVTTElement(element)->isPastNode() != toWebVTTElement(context.element())->isPa
stNode()) | 245 if (element->isWebVTTElement() && context.element()->isWebVTTElement() && to
WebVTTElement(element)->isPastNode() != toWebVTTElement(context.element())->isPa
stNode()) |
291 return false; | 246 return false; |
292 | 247 |
293 if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExist
s(&context.document())) { | 248 if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExist
s(&context.document())) { |
294 if (element == fullscreen->webkitCurrentFullScreenElement() || context.e
lement() == fullscreen->webkitCurrentFullScreenElement()) | 249 if (element == fullscreen->webkitCurrentFullScreenElement() || context.e
lement() == fullscreen->webkitCurrentFullScreenElement()) |
295 return false; | 250 return false; |
296 } | 251 } |
297 | 252 |
| 253 if (context.element()->parentElement() != parent) { |
| 254 if (!parent->isStyledElement()) |
| 255 return false; |
| 256 if (parent->hasScopedHTMLStyleChild()) |
| 257 return false; |
| 258 if (parent->inlineStyle()) |
| 259 return false; |
| 260 if (parent->isSVGElement() && toSVGElement(parent)->animatedSMILStylePro
perties()) |
| 261 return false; |
| 262 if (parent->hasID() && m_features.idsInRules.contains(parent->idForStyle
Resolution().impl())) |
| 263 return false; |
| 264 if (parentElementPreventsSharing(parent)) |
| 265 return false; |
| 266 } |
| 267 |
298 return true; | 268 return true; |
299 } | 269 } |
300 | 270 |
301 inline Element* SharedStyleFinder::findSiblingForStyleSharing(const ElementResol
veContext& context, Node* node, unsigned& count) const | |
302 { | |
303 for (; node; node = node->previousSibling()) { | |
304 if (!node->isStyledElement()) | |
305 continue; | |
306 if (canShareStyleWithElement(context, toElement(node))) | |
307 break; | |
308 if (count++ == cStyleSearchThreshold) | |
309 return 0; | |
310 } | |
311 return toElement(node); | |
312 } | |
313 | |
314 #ifdef STYLE_STATS | 271 #ifdef STYLE_STATS |
315 Element* SharedStyleFinder::searchDocumentForSharedStyle(const ElementResolveCon
text& context) const | 272 Element* SharedStyleFinder::searchDocumentForSharedStyle(const ElementResolveCon
text& context) const |
316 { | 273 { |
317 for (Element* element = context.element()->document().documentElement(); ele
ment; element = ElementTraversal::next(element)) { | 274 for (Element* element = context.element()->document().documentElement(); ele
ment; element = ElementTraversal::next(element)) { |
318 if (canShareStyleWithElement(context, element)) | 275 if (canShareStyleWithElement(context, element)) |
319 return element; | 276 return element; |
320 } | 277 } |
321 return 0; | 278 return 0; |
322 } | 279 } |
323 #endif | 280 #endif |
324 | 281 |
| 282 inline Element* SharedStyleFinder::findElementForStyleSharing(const ElementResol
veContext& context) const |
| 283 { |
| 284 StyleSharingList& styleSharingList = m_styleResolver->styleSharingList(); |
| 285 for (StyleSharingList::iterator it = styleSharingList.begin(); it != styleSh
aringList.end(); ++it) { |
| 286 if (!canShareStyleWithElement(context, it->get())) |
| 287 continue; |
| 288 Element* element = it->get(); |
| 289 if (it != styleSharingList.begin()) { |
| 290 // Move the element to the front of the LRU |
| 291 styleSharingList.remove(it); |
| 292 styleSharingList.prepend(element); |
| 293 } |
| 294 return element; |
| 295 } |
| 296 m_styleResolver->addToStyleSharingList(context.element()); |
| 297 return 0; |
| 298 } |
| 299 |
325 RenderStyle* SharedStyleFinder::locateSharedStyle(const ElementResolveContext& c
ontext, RenderStyle* newStyle) | 300 RenderStyle* SharedStyleFinder::locateSharedStyle(const ElementResolveContext& c
ontext, RenderStyle* newStyle) |
326 { | 301 { |
327 STYLE_STATS_ADD_SEARCH(); | 302 STYLE_STATS_ADD_SEARCH(); |
328 if (!context.element() || !context.element()->isStyledElement()) | |
329 return 0; | |
330 | 303 |
331 // If the element has inline style it is probably unique. | 304 if (!m_styleResolver->supportsStyleSharing(context.element())) |
332 if (context.element()->inlineStyle()) | |
333 return 0; | |
334 if (context.element()->isSVGElement() && toSVGElement(context.element())->an
imatedSMILStyleProperties()) | |
335 return 0; | |
336 // Ids stop style sharing if they show up in the stylesheets. | |
337 if (context.element()->hasID() && m_features.idsInRules.contains(context.ele
ment()->idForStyleResolution().impl())) | |
338 return 0; | |
339 // Active and hovered elements always make a chain towards the document node | |
340 // and no siblings or cousins will have the same state. | |
341 if (context.element()->hovered()) | |
342 return 0; | |
343 if (context.element()->active()) | |
344 return 0; | |
345 // There is always only one focused element. | |
346 if (context.element()->focused()) | |
347 return 0; | |
348 if (parentElementPreventsSharing(context.element()->parentElement())) | |
349 return 0; | |
350 if (context.element()->hasScopedHTMLStyleChild()) | |
351 return 0; | |
352 if (context.element() == context.document().cssTarget()) | |
353 return 0; | |
354 if (elementHasDirectionAuto(context.element())) | |
355 return 0; | |
356 if (context.element()->hasActiveAnimations()) | |
357 return 0; | |
358 // When a dialog is first shown, its style is mutated to center it in the | |
359 // viewport. So the styles can't be shared since the viewport position and | |
360 // size may be different each time a dialog is opened. | |
361 if (context.element()->hasTagName(dialogTag)) | |
362 return 0; | |
363 if (isShadowHost(context.element()) && context.element()->shadow()->contains
ActiveStyles()) | |
364 return 0; | 305 return 0; |
365 | 306 |
366 STYLE_STATS_ADD_ELEMENT_ELIGIBLE_FOR_SHARING(); | 307 STYLE_STATS_ADD_ELEMENT_ELIGIBLE_FOR_SHARING(); |
367 | 308 |
368 // Cache whether context.element() is affected by any known class selectors. | 309 // Cache whether context.element() is affected by any known class selectors. |
369 // FIXME: This should be an explicit out parameter, instead of a member vari
able. | 310 // FIXME: This should be an explicit out parameter, instead of a member vari
able. |
370 m_elementAffectedByClassRules = context.element() && context.element()->hasC
lass() && classNamesAffectedByRules(context.element()->classNames()); | 311 m_elementAffectedByClassRules = context.element() && context.element()->hasC
lass() && classNamesAffectedByRules(context.element()->classNames()); |
371 | 312 |
372 // Check previous siblings and their cousins. | 313 Element* shareElement = findElementForStyleSharing(context); |
373 unsigned count = 0; | |
374 unsigned visitedNodeCount = 0; | |
375 Element* shareElement = 0; | |
376 Node* cousinList = context.element()->previousSibling(); | |
377 while (cousinList) { | |
378 shareElement = findSiblingForStyleSharing(context, cousinList, count); | |
379 if (shareElement) | |
380 break; | |
381 cousinList = locateCousinList(cousinList->parentElement(), visitedNodeCo
unt); | |
382 } | |
383 | 314 |
384 #ifdef STYLE_STATS | 315 #ifdef STYLE_STATS |
385 // FIXME: these stats don't to into account whether or not sibling/attribute | 316 // FIXME: these stats don't to into account whether or not sibling/attribute |
386 // rules prevent these nodes from actually sharing | 317 // rules prevent these nodes from actually sharing |
387 if (shareElement) { | 318 if (shareElement) { |
388 STYLE_STATS_ADD_SEARCH_FOUND_SIBLING_FOR_SHARING(); | 319 STYLE_STATS_ADD_SEARCH_FOUND_SIBLING_FOR_SHARING(); |
389 } else { | 320 } else { |
390 shareElement = searchDocumentForSharedStyle(context); | 321 shareElement = searchDocumentForSharedStyle(context); |
391 if (shareElement) | 322 if (shareElement) |
392 STYLE_STATS_ADD_SEARCH_MISSED_SHARING(); | 323 STYLE_STATS_ADD_SEARCH_MISSED_SHARING(); |
(...skipping 12 matching lines...) Expand all Loading... |
405 if (m_styleResolver->styleSharingCandidateMatchesRuleSet(context, newStyle,
m_uncommonAttributeRuleSet)) | 336 if (m_styleResolver->styleSharingCandidateMatchesRuleSet(context, newStyle,
m_uncommonAttributeRuleSet)) |
406 return 0; | 337 return 0; |
407 // Tracking child index requires unique style for each node. This may get se
t by the sibling rule match above. | 338 // Tracking child index requires unique style for each node. This may get se
t by the sibling rule match above. |
408 if (parentElementPreventsSharing(context.element()->parentElement())) | 339 if (parentElementPreventsSharing(context.element()->parentElement())) |
409 return 0; | 340 return 0; |
410 STYLE_STATS_ADD_STYLE_SHARED(); | 341 STYLE_STATS_ADD_STYLE_SHARED(); |
411 return shareElement->renderStyle(); | 342 return shareElement->renderStyle(); |
412 } | 343 } |
413 | 344 |
414 } | 345 } |
OLD | NEW |