OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "config.h" |
| 6 #include "HTMLNames.h" |
| 7 #include "core/dom/Element.h" |
| 8 #include "core/dom/ElementTraversal.h" |
| 9 #include "core/dom/NodeRenderStyle.h" |
| 10 #include "core/frame/FrameView.h" |
| 11 #include "core/html/HTMLDocument.h" |
| 12 #include "core/html/HTMLElement.h" |
| 13 #include "core/testing/DummyPageHolder.h" |
| 14 #include <gtest/gtest.h> |
| 15 |
| 16 using namespace WebCore; |
| 17 using namespace HTMLNames; |
| 18 |
| 19 namespace { |
| 20 |
| 21 class AffectedByFocusTest : public ::testing::Test { |
| 22 |
| 23 protected: |
| 24 |
| 25 struct ElementResult { |
| 26 const WebCore::QualifiedName tag; |
| 27 bool affectedBy; |
| 28 bool childrenAffectedBy; |
| 29 }; |
| 30 |
| 31 virtual void SetUp() OVERRIDE; |
| 32 |
| 33 HTMLDocument& document() const { return *m_document; } |
| 34 |
| 35 void setHtmlInnerHTML(const char* htmlContent); |
| 36 |
| 37 void checkElements(ElementResult expected[], unsigned expectedCount) const; |
| 38 |
| 39 private: |
| 40 OwnPtr<DummyPageHolder> m_dummyPageHolder; |
| 41 |
| 42 HTMLDocument* m_document; |
| 43 }; |
| 44 |
| 45 void AffectedByFocusTest::SetUp() |
| 46 { |
| 47 m_dummyPageHolder = DummyPageHolder::create(IntSize(800, 600)); |
| 48 m_document = toHTMLDocument(&m_dummyPageHolder->document()); |
| 49 ASSERT(m_document); |
| 50 } |
| 51 |
| 52 void AffectedByFocusTest::setHtmlInnerHTML(const char* htmlContent) |
| 53 { |
| 54 document().documentElement()->setInnerHTML(String::fromUTF8(htmlContent), AS
SERT_NO_EXCEPTION); |
| 55 document().view()->updateLayoutAndStyleIfNeededRecursive(); |
| 56 } |
| 57 |
| 58 void AffectedByFocusTest::checkElements(ElementResult expected[], unsigned expec
tedCount) const |
| 59 { |
| 60 unsigned i = 0; |
| 61 Element* elm = document().body(); |
| 62 |
| 63 for (; elm && i < expectedCount; elm = ElementTraversal::next(*elm), ++i) { |
| 64 ASSERT_TRUE(elm->hasTagName(expected[i].tag)); |
| 65 ASSERT(elm->renderStyle()); |
| 66 ASSERT_EQ(expected[i].affectedBy, elm->renderStyle()->affectedByFocus())
; |
| 67 ASSERT_EQ(expected[i].childrenAffectedBy, elm->childrenAffectedByFocus()
); |
| 68 } |
| 69 |
| 70 ASSERT(!elm && i == expectedCount); |
| 71 } |
| 72 |
| 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 |
| 75 // gets the affectedByFocus set on RenderStyle and not childrenAffectedByFocus. |
| 76 TEST_F(AffectedByFocusTest, UAUniversalFocusRule) |
| 77 { |
| 78 ElementResult expected[] = { |
| 79 { bodyTag, true, false }, |
| 80 { divTag, true, false }, |
| 81 { divTag, true, false }, |
| 82 { divTag, true, false }, |
| 83 { spanTag, true, false } |
| 84 }; |
| 85 |
| 86 setHtmlInnerHTML("<body>" |
| 87 "<div><div></div></div>" |
| 88 "<div><span></span></div>" |
| 89 "</body>"); |
| 90 |
| 91 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); |
| 92 } |
| 93 |
| 94 // ":focus div" will mark ascendants of all divs with childrenAffectedByFocus. |
| 95 TEST_F(AffectedByFocusTest, FocusedAscendant) |
| 96 { |
| 97 ElementResult expected[] = { |
| 98 { bodyTag, true, true }, |
| 99 { divTag, true, true }, |
| 100 { divTag, true, false }, |
| 101 { divTag, true, false }, |
| 102 { spanTag, true, false } |
| 103 }; |
| 104 |
| 105 setHtmlInnerHTML("<head>" |
| 106 "<style>:focus div { background-color: pink }</style>" |
| 107 "</head>" |
| 108 "<body>" |
| 109 "<div><div></div></div>" |
| 110 "<div><span></span></div>" |
| 111 "</body>"); |
| 112 |
| 113 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); |
| 114 } |
| 115 |
| 116 // "body:focus div" will mark the body element with childrenAffectedByFocus. |
| 117 TEST_F(AffectedByFocusTest, FocusedAscendantWithType) |
| 118 { |
| 119 ElementResult expected[] = { |
| 120 { bodyTag, true, true }, |
| 121 { divTag, true, false }, |
| 122 { divTag, true, false }, |
| 123 { divTag, true, false }, |
| 124 { spanTag, true, false } |
| 125 }; |
| 126 |
| 127 setHtmlInnerHTML("<head>" |
| 128 "<style>body:focus div { background-color: pink }</style>" |
| 129 "</head>" |
| 130 "<body>" |
| 131 "<div><div></div></div>" |
| 132 "<div><span></span></div>" |
| 133 "</body>"); |
| 134 |
| 135 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); |
| 136 } |
| 137 |
| 138 // ":not(body):focus div" should not mark the body element with childrenAffected
ByFocus. |
| 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 |
| 141 // is found. |
| 142 TEST_F(AffectedByFocusTest, FocusedAscendantWithNegatedType) |
| 143 { |
| 144 ElementResult expected[] = { |
| 145 { bodyTag, true, false }, |
| 146 { divTag, true, true }, |
| 147 { divTag, true, false }, |
| 148 { divTag, true, false }, |
| 149 { spanTag, true, false } |
| 150 }; |
| 151 |
| 152 setHtmlInnerHTML("<head>" |
| 153 "<style>:not(body):focus div { background-color: pink }</style>" |
| 154 "</head>" |
| 155 "<body>" |
| 156 "<div><div></div></div>" |
| 157 "<div><span></span></div>" |
| 158 "</body>"); |
| 159 |
| 160 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); |
| 161 } |
| 162 |
| 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 |
| 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 |
| 167 // affected. There are other mechanisms that makes sure the sibling also gets it
s |
| 168 // styles recalculated. |
| 169 TEST_F(AffectedByFocusTest, FocusedSibling) |
| 170 { |
| 171 ElementResult expected[] = { |
| 172 { bodyTag, true, false }, |
| 173 { divTag, true, true }, |
| 174 { spanTag, true, false }, |
| 175 { divTag, true, false } |
| 176 }; |
| 177 |
| 178 setHtmlInnerHTML("<head>" |
| 179 "<style>:focus + div { background-color: pink }</style>" |
| 180 "</head>" |
| 181 "<body>" |
| 182 "<div>" |
| 183 " <span></span>" |
| 184 "</div>" |
| 185 "<div></div>" |
| 186 "</body>"); |
| 187 |
| 188 checkElements(expected, sizeof(expected) / sizeof(ElementResult)); |
| 189 } |
| 190 |
| 191 TEST_F(AffectedByFocusTest, AffectedByFocusUpdate) |
| 192 { |
| 193 // Check that when focussing the outer div in the document below, you only |
| 194 // get a single element style recalc. |
| 195 |
| 196 setHtmlInnerHTML("<style>:focus { border: 1px solid lime; }</style>" |
| 197 "<div id=d tabIndex=1>" |
| 198 "<div></div>" |
| 199 "<div></div>" |
| 200 "<div></div>" |
| 201 "<div></div>" |
| 202 "<div></div>" |
| 203 "<div></div>" |
| 204 "<div></div>" |
| 205 "<div></div>" |
| 206 "<div></div>" |
| 207 "<div></div>" |
| 208 "</div>"); |
| 209 |
| 210 document().view()->updateLayoutAndStyleIfNeededRecursive(); |
| 211 |
| 212 unsigned startCount = document().styleEngine()->resolverAccessCount(); |
| 213 |
| 214 document().getElementById("d")->focus(); |
| 215 document().view()->updateLayoutAndStyleIfNeededRecursive(); |
| 216 |
| 217 unsigned accessCount = document().styleEngine()->resolverAccessCount() - sta
rtCount; |
| 218 |
| 219 ASSERT_EQ(1U, accessCount); |
| 220 } |
| 221 |
| 222 TEST_F(AffectedByFocusTest, ChildrenAffectedByFocusUpdate) |
| 223 { |
| 224 // Check that when focussing the outer div in the document below, you get a |
| 225 // style recalc for the whole subtree. |
| 226 |
| 227 setHtmlInnerHTML("<style>:focus div { border: 1px solid lime; }</style>" |
| 228 "<div id=d tabIndex=1>" |
| 229 "<div></div>" |
| 230 "<div></div>" |
| 231 "<div></div>" |
| 232 "<div></div>" |
| 233 "<div></div>" |
| 234 "<div></div>" |
| 235 "<div></div>" |
| 236 "<div></div>" |
| 237 "<div></div>" |
| 238 "<div></div>" |
| 239 "</div>"); |
| 240 |
| 241 document().view()->updateLayoutAndStyleIfNeededRecursive(); |
| 242 |
| 243 unsigned startCount = document().styleEngine()->resolverAccessCount(); |
| 244 |
| 245 document().getElementById("d")->focus(); |
| 246 document().view()->updateLayoutAndStyleIfNeededRecursive(); |
| 247 |
| 248 unsigned accessCount = document().styleEngine()->resolverAccessCount() - sta
rtCount; |
| 249 |
| 250 ASSERT_EQ(11U, accessCount); |
| 251 } |
| 252 |
| 253 } // namespace |
OLD | NEW |