| 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 |