| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "config.h" | 5 #include "config.h" |
| 6 #include "HTMLNames.h" | 6 #include "HTMLNames.h" |
| 7 #include "core/dom/Element.h" | 7 #include "core/dom/Element.h" |
| 8 #include "core/dom/ElementTraversal.h" | 8 #include "core/dom/ElementTraversal.h" |
| 9 #include "core/dom/NodeRenderStyle.h" | 9 #include "core/dom/NodeRenderStyle.h" |
| 10 #include "core/frame/FrameView.h" | 10 #include "core/frame/FrameView.h" |
| 11 #include "core/html/HTMLDocument.h" | 11 #include "core/html/HTMLDocument.h" |
| 12 #include "core/html/HTMLElement.h" | 12 #include "core/html/HTMLElement.h" |
| 13 #include "core/testing/DummyPageHolder.h" | 13 #include "core/testing/DummyPageHolder.h" |
| 14 #include <gtest/gtest.h> | 14 #include <gtest/gtest.h> |
| 15 | 15 |
| 16 using namespace WebCore; | 16 using namespace WebCore; |
| 17 using namespace HTMLNames; | 17 using namespace HTMLNames; |
| 18 | 18 |
| 19 namespace { | 19 namespace { |
| 20 | 20 |
| 21 class AffectedByFocusTest : public ::testing::Test { | 21 class AffectedByFocusTest : public ::testing::Test { |
| 22 | 22 |
| 23 protected: | 23 protected: |
| 24 | 24 |
| 25 struct ElementResult { | 25 struct ElementResult { |
| 26 const WebCore::QualifiedName tag; | 26 const WebCore::QualifiedName tag; |
| 27 bool affectedBy; | 27 bool affectedBy; |
| 28 bool childrenAffectedBy; | 28 bool childrenOrSiblingsAffectedBy; |
| 29 }; | 29 }; |
| 30 | 30 |
| 31 virtual void SetUp() OVERRIDE; | 31 virtual void SetUp() OVERRIDE; |
| 32 | 32 |
| 33 HTMLDocument& document() const { return *m_document; } | 33 HTMLDocument& document() const { return *m_document; } |
| 34 | 34 |
| 35 void setHtmlInnerHTML(const char* htmlContent); | 35 void setHtmlInnerHTML(const char* htmlContent); |
| 36 | 36 |
| 37 void checkElements(ElementResult expected[], unsigned expectedCount) const; | 37 void checkElements(ElementResult expected[], unsigned expectedCount) const; |
| 38 | 38 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 57 | 57 |
| 58 void AffectedByFocusTest::checkElements(ElementResult expected[], unsigned expec
tedCount) const | 58 void AffectedByFocusTest::checkElements(ElementResult expected[], unsigned expec
tedCount) const |
| 59 { | 59 { |
| 60 unsigned i = 0; | 60 unsigned i = 0; |
| 61 Element* elm = document().body(); | 61 Element* elm = document().body(); |
| 62 | 62 |
| 63 for (; elm && i < expectedCount; elm = ElementTraversal::next(*elm), ++i) { | 63 for (; elm && i < expectedCount; elm = ElementTraversal::next(*elm), ++i) { |
| 64 ASSERT_TRUE(elm->hasTagName(expected[i].tag)); | 64 ASSERT_TRUE(elm->hasTagName(expected[i].tag)); |
| 65 ASSERT(elm->renderStyle()); | 65 ASSERT(elm->renderStyle()); |
| 66 ASSERT_EQ(expected[i].affectedBy, elm->renderStyle()->affectedByFocus())
; | 66 ASSERT_EQ(expected[i].affectedBy, elm->renderStyle()->affectedByFocus())
; |
| 67 ASSERT_EQ(expected[i].childrenAffectedBy, elm->childrenAffectedByFocus()
); | 67 ASSERT_EQ(expected[i].childrenOrSiblingsAffectedBy, elm->childrenOrSibli
ngsAffectedByFocus()); |
| 68 } | 68 } |
| 69 | 69 |
| 70 ASSERT(!elm && i == expectedCount); | 70 ASSERT(!elm && i == expectedCount); |
| 71 } | 71 } |
| 72 | 72 |
| 73 // A global :focus rule in html.css currently causes every single element to be | 73 // A global :focus rule in html.css currently causes every single element to be |
| 74 // affectedByFocus. Check that all elements in a document with no :focus rules | 74 // affectedByFocus. Check that all elements in a document with no :focus rules |
| 75 // gets the affectedByFocus set on RenderStyle and not childrenAffectedByFocus. | 75 // gets the affectedByFocus set on RenderStyle and not childrenOrSiblingsAffecte
dByFocus. |
| 76 TEST_F(AffectedByFocusTest, UAUniversalFocusRule) | 76 TEST_F(AffectedByFocusTest, UAUniversalFocusRule) |
| 77 { | 77 { |
| 78 ElementResult expected[] = { | 78 ElementResult expected[] = { |
| 79 { bodyTag, true, false }, | 79 { bodyTag, true, false }, |
| 80 { divTag, true, false }, | 80 { divTag, true, false }, |
| 81 { divTag, true, false }, | 81 { divTag, true, false }, |
| 82 { divTag, true, false }, | 82 { divTag, true, false }, |
| 83 { spanTag, true, false } | 83 { spanTag, true, false } |
| 84 }; | 84 }; |
| 85 | 85 |
| 86 setHtmlInnerHTML("<body>" | 86 setHtmlInnerHTML("<body>" |
| 87 "<div><div></div></div>" | 87 "<div><div></div></div>" |
| 88 "<div><span></span></div>" | 88 "<div><span></span></div>" |
| 89 "</body>"); | 89 "</body>"); |
| 90 | 90 |
| 91 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); | 91 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); |
| 92 } | 92 } |
| 93 | 93 |
| 94 // ":focus div" will mark ascendants of all divs with childrenAffectedByFocus. | 94 // ":focus div" will mark ascendants of all divs with childrenOrSiblingsAffected
ByFocus. |
| 95 TEST_F(AffectedByFocusTest, FocusedAscendant) | 95 TEST_F(AffectedByFocusTest, FocusedAscendant) |
| 96 { | 96 { |
| 97 ElementResult expected[] = { | 97 ElementResult expected[] = { |
| 98 { bodyTag, true, true }, | 98 { bodyTag, true, true }, |
| 99 { divTag, true, true }, | 99 { divTag, true, true }, |
| 100 { divTag, true, false }, | 100 { divTag, true, false }, |
| 101 { divTag, true, false }, | 101 { divTag, true, false }, |
| 102 { spanTag, true, false } | 102 { spanTag, true, false } |
| 103 }; | 103 }; |
| 104 | 104 |
| 105 setHtmlInnerHTML("<head>" | 105 setHtmlInnerHTML("<head>" |
| 106 "<style>:focus div { background-color: pink }</style>" | 106 "<style>:focus div { background-color: pink }</style>" |
| 107 "</head>" | 107 "</head>" |
| 108 "<body>" | 108 "<body>" |
| 109 "<div><div></div></div>" | 109 "<div><div></div></div>" |
| 110 "<div><span></span></div>" | 110 "<div><span></span></div>" |
| 111 "</body>"); | 111 "</body>"); |
| 112 | 112 |
| 113 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); | 113 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); |
| 114 } | 114 } |
| 115 | 115 |
| 116 // "body:focus div" will mark the body element with childrenAffectedByFocus. | 116 // "body:focus div" will mark the body element with childrenOrSiblingsAffectedBy
Focus. |
| 117 TEST_F(AffectedByFocusTest, FocusedAscendantWithType) | 117 TEST_F(AffectedByFocusTest, FocusedAscendantWithType) |
| 118 { | 118 { |
| 119 ElementResult expected[] = { | 119 ElementResult expected[] = { |
| 120 { bodyTag, true, true }, | 120 { bodyTag, true, true }, |
| 121 { divTag, true, false }, | 121 { divTag, true, false }, |
| 122 { divTag, true, false }, | 122 { divTag, true, false }, |
| 123 { divTag, true, false }, | 123 { divTag, true, false }, |
| 124 { spanTag, true, false } | 124 { spanTag, true, false } |
| 125 }; | 125 }; |
| 126 | 126 |
| 127 setHtmlInnerHTML("<head>" | 127 setHtmlInnerHTML("<head>" |
| 128 "<style>body:focus div { background-color: pink }</style>" | 128 "<style>body:focus div { background-color: pink }</style>" |
| 129 "</head>" | 129 "</head>" |
| 130 "<body>" | 130 "<body>" |
| 131 "<div><div></div></div>" | 131 "<div><div></div></div>" |
| 132 "<div><span></span></div>" | 132 "<div><span></span></div>" |
| 133 "</body>"); | 133 "</body>"); |
| 134 | 134 |
| 135 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); | 135 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); |
| 136 } | 136 } |
| 137 | 137 |
| 138 // ":not(body):focus div" should not mark the body element with childrenAffected
ByFocus. | 138 // ":not(body):focus div" should not mark the body element with childrenOrSiblin
gsAffectedByFocus. |
| 139 // Note that currently ":focus:not(body)" does not do the same. Then the :focus
is | 139 // Note that currently ":focus:not(body)" does not do the same. Then the :focus
is |
| 140 // checked and the childrenAffectedByFocus flag set before the negated type sele
ctor | 140 // checked and the childrenOrSiblingsAffectedByFocus flag set before the negated
type selector |
| 141 // is found. | 141 // is found. |
| 142 TEST_F(AffectedByFocusTest, FocusedAscendantWithNegatedType) | 142 TEST_F(AffectedByFocusTest, FocusedAscendantWithNegatedType) |
| 143 { | 143 { |
| 144 ElementResult expected[] = { | 144 ElementResult expected[] = { |
| 145 { bodyTag, true, false }, | 145 { bodyTag, true, false }, |
| 146 { divTag, true, true }, | 146 { divTag, true, true }, |
| 147 { divTag, true, false }, | 147 { divTag, true, false }, |
| 148 { divTag, true, false }, | 148 { divTag, true, false }, |
| 149 { spanTag, true, false } | 149 { spanTag, true, false } |
| 150 }; | 150 }; |
| 151 | 151 |
| 152 setHtmlInnerHTML("<head>" | 152 setHtmlInnerHTML("<head>" |
| 153 "<style>:not(body):focus div { background-color: pink }</style>" | 153 "<style>:not(body):focus div { background-color: pink }</style>" |
| 154 "</head>" | 154 "</head>" |
| 155 "<body>" | 155 "<body>" |
| 156 "<div><div></div></div>" | 156 "<div><div></div></div>" |
| 157 "<div><span></span></div>" | 157 "<div><span></span></div>" |
| 158 "</body>"); | 158 "</body>"); |
| 159 | 159 |
| 160 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); | 160 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); |
| 161 } | 161 } |
| 162 | 162 |
| 163 // Checking current behavior for ":focus + div", but this is a BUG or at best | 163 // Checking current behavior for ":focus + div", but this is a BUG or at best |
| 164 // sub-optimal. The focused element will also in this case get childrenAffectedB
yFocus | 164 // sub-optimal. The focused element will also in this case get childrenOrSibling
sAffectedByFocus |
| 165 // even if it's really a sibling. Effectively, the whole sub-tree of the focused | 165 // even if it's really a sibling. Effectively, the whole sub-tree of the focused |
| 166 // element will have styles recalculated even though none of the children are | 166 // element will have styles recalculated even though none of the children are |
| 167 // affected. There are other mechanisms that makes sure the sibling also gets it
s | 167 // affected. There are other mechanisms that makes sure the sibling also gets it
s |
| 168 // styles recalculated. | 168 // styles recalculated. |
| 169 TEST_F(AffectedByFocusTest, FocusedSibling) | 169 TEST_F(AffectedByFocusTest, FocusedSibling) |
| 170 { | 170 { |
| 171 ElementResult expected[] = { | 171 ElementResult expected[] = { |
| 172 { bodyTag, true, false }, | 172 { bodyTag, true, false }, |
| 173 { divTag, true, true }, | 173 { divTag, true, true }, |
| 174 { spanTag, true, false }, | 174 { spanTag, true, false }, |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 212 unsigned startCount = document().styleEngine()->resolverAccessCount(); | 212 unsigned startCount = document().styleEngine()->resolverAccessCount(); |
| 213 | 213 |
| 214 document().getElementById("d")->focus(); | 214 document().getElementById("d")->focus(); |
| 215 document().view()->updateLayoutAndStyleIfNeededRecursive(); | 215 document().view()->updateLayoutAndStyleIfNeededRecursive(); |
| 216 | 216 |
| 217 unsigned accessCount = document().styleEngine()->resolverAccessCount() - sta
rtCount; | 217 unsigned accessCount = document().styleEngine()->resolverAccessCount() - sta
rtCount; |
| 218 | 218 |
| 219 ASSERT_EQ(1U, accessCount); | 219 ASSERT_EQ(1U, accessCount); |
| 220 } | 220 } |
| 221 | 221 |
| 222 TEST_F(AffectedByFocusTest, ChildrenAffectedByFocusUpdate) | 222 TEST_F(AffectedByFocusTest, ChildrenOrSiblingsAffectedByFocusUpdate) |
| 223 { | 223 { |
| 224 // Check that when focussing the outer div in the document below, you get a | 224 // Check that when focussing the outer div in the document below, you get a |
| 225 // style recalc for the whole subtree. | 225 // style recalc for the whole subtree. |
| 226 | 226 |
| 227 setHtmlInnerHTML("<style>:focus div { border: 1px solid lime; }</style>" | 227 setHtmlInnerHTML("<style>:focus div { border: 1px solid lime; }</style>" |
| 228 "<div id=d tabIndex=1>" | 228 "<div id=d tabIndex=1>" |
| 229 "<div></div>" | 229 "<div></div>" |
| 230 "<div></div>" | 230 "<div></div>" |
| 231 "<div></div>" | 231 "<div></div>" |
| 232 "<div></div>" | 232 "<div></div>" |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 278 | 278 |
| 279 unsigned accessCount = document().styleEngine()->resolverAccessCount() - sta
rtCount; | 279 unsigned accessCount = document().styleEngine()->resolverAccessCount() - sta
rtCount; |
| 280 | 280 |
| 281 ASSERT_EQ(2U, accessCount); | 281 ASSERT_EQ(2U, accessCount); |
| 282 } | 282 } |
| 283 | 283 |
| 284 TEST_F(AffectedByFocusTest, NoInvalidationSetFocusUpdate) | 284 TEST_F(AffectedByFocusTest, NoInvalidationSetFocusUpdate) |
| 285 { | 285 { |
| 286 // Check that when focussing the outer div in the document below, you get a | 286 // Check that when focussing the outer div in the document below, you get a |
| 287 // style recalc for the outer div only. The invalidation set for :focus will | 287 // style recalc for the outer div only. The invalidation set for :focus will |
| 288 // include 'a', but the id=d div should be affectedByFocus, not childrenAffe
ctedByFocus. | 288 // include 'a', but the id=d div should be affectedByFocus, not childrenOrSi
blingsAffectedByFocus. |
| 289 | 289 |
| 290 setHtmlInnerHTML("<style>#nomatch:focus .a { border: 1px solid lime; }</styl
e>" | 290 setHtmlInnerHTML("<style>#nomatch:focus .a { border: 1px solid lime; }</styl
e>" |
| 291 "<div id=d tabIndex=1>" | 291 "<div id=d tabIndex=1>" |
| 292 "<div></div>" | 292 "<div></div>" |
| 293 "<div></div>" | 293 "<div></div>" |
| 294 "<div></div>" | 294 "<div></div>" |
| 295 "<div></div>" | 295 "<div></div>" |
| 296 "<div></div>" | 296 "<div></div>" |
| 297 "<div></div>" | 297 "<div></div>" |
| 298 "<div></div>" | 298 "<div></div>" |
| 299 "<div></div>" | 299 "<div></div>" |
| 300 "<div></div>" | 300 "<div></div>" |
| 301 "<div class='a'></div>" | 301 "<div class='a'></div>" |
| 302 "</div>"); | 302 "</div>"); |
| 303 | 303 |
| 304 document().view()->updateLayoutAndStyleIfNeededRecursive(); | 304 document().view()->updateLayoutAndStyleIfNeededRecursive(); |
| 305 | 305 |
| 306 unsigned startCount = document().styleEngine()->resolverAccessCount(); | 306 unsigned startCount = document().styleEngine()->resolverAccessCount(); |
| 307 | 307 |
| 308 document().getElementById("d")->focus(); | 308 document().getElementById("d")->focus(); |
| 309 document().view()->updateLayoutAndStyleIfNeededRecursive(); | 309 document().view()->updateLayoutAndStyleIfNeededRecursive(); |
| 310 | 310 |
| 311 unsigned accessCount = document().styleEngine()->resolverAccessCount() - sta
rtCount; | 311 unsigned accessCount = document().styleEngine()->resolverAccessCount() - sta
rtCount; |
| 312 | 312 |
| 313 ASSERT_EQ(1U, accessCount); | 313 ASSERT_EQ(1U, accessCount); |
| 314 } | 314 } |
| 315 | 315 |
| 316 } // namespace | 316 } // namespace |
| OLD | NEW |