TEST_F(StyleEngineTest, AnalyzedInject) { document().body()->setInnerHTML( "<style>div { color: red }</style><div id='t1'>Green</div><div></div>"); document().view()->updateAllLifecyclePhases(); Element* t1 = document().getElementById("t1"); ASSERT_TRUE(t1); ASSERT_TRUE(t1->computedStyle()); EXPECT_EQ(makeRGB(255, 0, 0), t1->computedStyle()->visitedDependentColor(CSSPropertyColor)); unsigned beforeCount = styleEngine().styleForElementCount(); StyleSheetContents* parsedSheet = StyleSheetContents::create(CSSParserContext(document(), nullptr)); parsedSheet->parseString("#t1 { color: green }"); styleEngine().injectAuthorSheet(parsedSheet); document().view()->updateAllLifecyclePhases(); unsigned afterCount = styleEngine().styleForElementCount(); EXPECT_EQ(1u, afterCount - beforeCount); ASSERT_TRUE(t1->computedStyle()); EXPECT_EQ(makeRGB(0, 128, 0), t1->computedStyle()->visitedDependentColor(CSSPropertyColor)); }
TEST_F(StyleEngineTest, RuleSetInvalidationHostContext) { document().body()->setInnerHTML("<div id=host></div>"); Element* host = document().getElementById("host"); ASSERT_TRUE(host); ShadowRootInit init; init.setMode("open"); ShadowRoot* shadowRoot = host->attachShadow( ScriptState::forMainWorld(document().frame()), init, ASSERT_NO_EXCEPTION); ASSERT_TRUE(shadowRoot); shadowRoot->setInnerHTML("<div></div><div class=a></div><div></div>"); document().view()->updateAllLifecyclePhases(); unsigned beforeCount = styleEngine().styleForElementCount(); EXPECT_EQ(scheduleInvalidationsForRules( *shadowRoot, ":host-context(.nomatch) .a { background: green}"), RuleSetInvalidationsScheduled); document().view()->updateAllLifecyclePhases(); unsigned afterCount = styleEngine().styleForElementCount(); EXPECT_EQ(1u, afterCount - beforeCount); EXPECT_EQ(scheduleInvalidationsForRules( *shadowRoot, ":host-context(:hover) { background: green}"), RuleSetInvalidationFullRecalc); EXPECT_EQ(scheduleInvalidationsForRules( *shadowRoot, ":host-context(#host) { background: green}"), RuleSetInvalidationFullRecalc); }
TEST_F(StyleEngineTest, StyleMediaAttributeNoStyleChange) { document().body()->setInnerHTML( "<style id='s1' media='(max-width: 1000px)'>#t1 { color: green }</style>" "<div id='t1'>Green</div><div></div>"); document().view()->updateAllLifecyclePhases(); Element* t1 = document().getElementById("t1"); ASSERT_TRUE(t1); ASSERT_TRUE(t1->computedStyle()); EXPECT_EQ(makeRGB(0, 128, 0), t1->computedStyle()->visitedDependentColor(CSSPropertyColor)); unsigned beforeCount = styleEngine().styleForElementCount(); Element* s1 = document().getElementById("s1"); s1->setAttribute(blink::HTMLNames::mediaAttr, "(max-width: 2000px)"); document().view()->updateAllLifecyclePhases(); unsigned afterCount = styleEngine().styleForElementCount(); // TODO([email protected]): Should be 0 for ruleset based invalidations. EXPECT_EQ(8u, afterCount - beforeCount); ASSERT_TRUE(t1->computedStyle()); EXPECT_EQ(makeRGB(0, 128, 0), t1->computedStyle()->visitedDependentColor(CSSPropertyColor)); }
TEST_F(StyleEngineTest, RuleSetInvalidationTypeSelectors) { document().body()->setInnerHTML( "<div>" " <span></span>" " <div></div>" "</div>"); document().view()->updateAllLifecyclePhases(); unsigned beforeCount = styleEngine().styleForElementCount(); EXPECT_EQ( scheduleInvalidationsForRules(document(), "span { background: green}"), RuleSetInvalidationsScheduled); document().view()->updateAllLifecyclePhases(); unsigned afterCount = styleEngine().styleForElementCount(); EXPECT_EQ(1u, afterCount - beforeCount); beforeCount = afterCount; EXPECT_EQ(scheduleInvalidationsForRules(document(), "body div { background: green}"), RuleSetInvalidationsScheduled); document().view()->updateAllLifecyclePhases(); afterCount = styleEngine().styleForElementCount(); EXPECT_EQ(2u, afterCount - beforeCount); EXPECT_EQ( scheduleInvalidationsForRules(document(), "div * { background: green}"), RuleSetInvalidationFullRecalc); }
TEST_F(CSSSelectorWatchTest, RecalcOnDocumentChange) { document().body()->setInnerHTML( "<div>" " <span id='x' class='a'></span>" " <span id='y' class='b'><span></span></span>" " <span id='z'><span></span></span>" "</div>", ASSERT_NO_EXCEPTION); CSSSelectorWatch& watch = CSSSelectorWatch::from(document()); Vector<String> selectors; selectors.append(".a"); watch.watchCSSSelectors(selectors); document().view()->updateAllLifecyclePhases(); selectors.clear(); selectors.append(".b"); selectors.append(".c"); selectors.append("#nomatch"); watch.watchCSSSelectors(selectors); document().view()->updateAllLifecyclePhases(); Element* x = document().getElementById("x"); Element* y = document().getElementById("y"); Element* z = document().getElementById("z"); ASSERT_TRUE(x); ASSERT_TRUE(y); ASSERT_TRUE(z); x->removeAttribute(HTMLNames::classAttr); y->removeAttribute(HTMLNames::classAttr); z->setAttribute(HTMLNames::classAttr, "c"); clearAddedRemoved(watch); unsigned beforeCount = styleEngine().styleForElementCount(); document().view()->updateAllLifecyclePhases(); unsigned afterCount = styleEngine().styleForElementCount(); EXPECT_EQ(2u, afterCount - beforeCount); EXPECT_EQ(1u, addedSelectors(watch).size()); EXPECT_TRUE(addedSelectors(watch).contains(".c")); EXPECT_EQ(1u, removedSelectors(watch).size()); EXPECT_TRUE(removedSelectors(watch).contains(".b")); }
TEST_F(StyleEngineTest, DocumentDirtyAfterInject) { StyleSheetContents* parsedSheet = StyleSheetContents::create(CSSParserContext(document(), nullptr)); parsedSheet->parseString("div {}"); styleEngine().injectAuthorSheet(parsedSheet); document().view()->updateAllLifecyclePhases(); EXPECT_TRUE(isDocumentStyleSheetCollectionClean()); }
TEST_F(ApplyRulesetsTest, AddShadowV0BoundaryCrossingRuleToDocument) { document().view()->updateAllLifecyclePhases(); CSSStyleSheet* sheet = createSheet(".a /deep/ .b { color:red }"); ActiveStyleSheetVector newStyleSheets; newStyleSheets.append(std::make_pair(sheet, &sheet->contents()->ruleSet())); applyRuleSetChanges(styleEngine(), document(), ActiveStyleSheetVector(), newStyleSheets); EXPECT_EQ(SubtreeStyleChange, document().getStyleChangeType()); }
TEST_F(ApplyRulesetsTest, AddFontFaceRuleToDocument) { document().view()->updateAllLifecyclePhases(); CSSStyleSheet* sheet = createSheet("@font-face { font-family: ahum; src: url(ahum.ttf) }"); ActiveStyleSheetVector newStyleSheets; newStyleSheets.append(std::make_pair(sheet, &sheet->contents()->ruleSet())); applyRuleSetChanges(styleEngine(), document(), ActiveStyleSheetVector(), newStyleSheets); EXPECT_EQ(SubtreeStyleChange, document().getStyleChangeType()); }
TEST_F(StyleEngineTest, RuleSetInvalidationSlotted) { document().body()->setInnerHTML( "<div id=host>" " <span slot=other class=s1></span>" " <span class=s2></span>" " <span class=s1></span>" " <span></span>" "</div>"); Element* host = document().getElementById("host"); ASSERT_TRUE(host); ShadowRootInit init; init.setMode("open"); ShadowRoot* shadowRoot = host->attachShadow( ScriptState::forMainWorld(document().frame()), init, ASSERT_NO_EXCEPTION); ASSERT_TRUE(shadowRoot); shadowRoot->setInnerHTML("<slot name=other></slot><slot></slot>"); document().view()->updateAllLifecyclePhases(); unsigned beforeCount = styleEngine().styleForElementCount(); EXPECT_EQ(scheduleInvalidationsForRules( *shadowRoot, "::slotted(.s1) { background: green}"), RuleSetInvalidationsScheduled); document().view()->updateAllLifecyclePhases(); unsigned afterCount = styleEngine().styleForElementCount(); EXPECT_EQ(4u, afterCount - beforeCount); beforeCount = afterCount; EXPECT_EQ(scheduleInvalidationsForRules(*shadowRoot, "::slotted(*) { background: green}"), RuleSetInvalidationsScheduled); document().view()->updateAllLifecyclePhases(); afterCount = styleEngine().styleForElementCount(); EXPECT_EQ(4u, afterCount - beforeCount); }
TEST_F(StyleEngineTest, TextToSheetCache) { HTMLStyleElement* element = HTMLStyleElement::create(document(), false); String sheetText("div {}"); TextPosition minPos = TextPosition::minimumPosition(); StyleEngineContext context; CSSStyleSheet* sheet1 = styleEngine().createSheet(*element, sheetText, minPos, context); // Check that the first sheet is not using a cached StyleSheetContents. EXPECT_FALSE(sheet1->contents()->isUsedFromTextCache()); CSSStyleSheet* sheet2 = styleEngine().createSheet(*element, sheetText, minPos, context); // Check that the second sheet uses the cached StyleSheetContents for the // first. EXPECT_EQ(sheet1->contents(), sheet2->contents()); EXPECT_TRUE(sheet2->contents()->isUsedFromTextCache()); sheet1 = nullptr; sheet2 = nullptr; element = nullptr; // Garbage collection should clear the weak reference in the // StyleSheetContents cache. ThreadState::current()->collectAllGarbage(); element = HTMLStyleElement::create(document(), false); sheet1 = styleEngine().createSheet(*element, sheetText, minPos, context); // Check that we did not use a cached StyleSheetContents after the garbage // collection. EXPECT_FALSE(sheet1->contents()->isUsedFromTextCache()); }
StyleEngineTest::RuleSetInvalidation StyleEngineTest::scheduleInvalidationsForRules(TreeScope& treeScope, const String& cssText) { StyleSheetContents* sheet = StyleSheetContents::create(CSSParserContext(HTMLStandardMode, nullptr)); sheet->parseString(cssText); HeapVector<Member<RuleSet>> ruleSets; RuleSet& ruleSet = sheet->ensureRuleSet(MediaQueryEvaluator(), RuleHasDocumentSecurityOrigin); ruleSet.compactRulesIfNeeded(); if (ruleSet.needsFullRecalcForRuleSetInvalidation()) return RuleSetInvalidationFullRecalc; ruleSets.append(&ruleSet); styleEngine().scheduleInvalidationsForRuleSets(treeScope, ruleSets); return RuleSetInvalidationsScheduled; }
TEST_F(ApplyRulesetsTest, AddFontFaceRuleToShadowTree) { document().body()->setInnerHTML("<div id=host></div>", ASSERT_NO_EXCEPTION); Element* host = document().getElementById("host"); ASSERT_TRUE(host); ShadowRoot& shadowRoot = attachShadow(*host); document().view()->updateAllLifecyclePhases(); CSSStyleSheet* sheet = createSheet("@font-face { font-family: ahum; src: url(ahum.ttf) }"); ActiveStyleSheetVector newStyleSheets; newStyleSheets.append(std::make_pair(sheet, &sheet->contents()->ruleSet())); applyRuleSetChanges(styleEngine(), shadowRoot, ActiveStyleSheetVector(), newStyleSheets); EXPECT_FALSE(document().needsLayoutTreeUpdate()); }
TEST_F(ApplyRulesetsTest, AddShadowV0BoundaryCrossingRuleToShadowTree) { document().body()->setInnerHTML("<div id=host></div>", ASSERT_NO_EXCEPTION); Element* host = document().getElementById("host"); ASSERT_TRUE(host); ShadowRoot& shadowRoot = attachShadow(*host); document().view()->updateAllLifecyclePhases(); CSSStyleSheet* sheet = createSheet(".a /deep/ .b { color:red }"); ActiveStyleSheetVector newStyleSheets; newStyleSheets.append(std::make_pair(sheet, &sheet->contents()->ruleSet())); applyRuleSetChanges(styleEngine(), shadowRoot, ActiveStyleSheetVector(), newStyleSheets); EXPECT_FALSE(document().needsStyleRecalc()); EXPECT_EQ(SubtreeStyleChange, host->getStyleChangeType()); }
bool isDocumentStyleSheetCollectionClean() { return !styleEngine().shouldUpdateDocumentStyleSheetCollection(); }