| <!DOCTYPE html> |
| <html> |
| <head> |
| <link rel="stylesheet" href="resources/modify-css-property.css"> |
| <script src="../../http/tests/inspector/resources/inspector-test.js"></script> |
| <script> |
| function makeNarrow() { |
| document.getElementById("x").style.width = "50px"; |
| } |
| |
| function makeWide() { |
| document.getElementById("x").style.width = "200px"; |
| } |
| |
| function test() { |
| let nodeStyles = null; |
| let suite = InspectorTest.createAsyncSuite("ModifyCSSProperty"); |
| |
| suite.addTestCase({ |
| name: "Update value when CSSStyleDeclaration is not locked", |
| test(resolve, reject) { |
| let getMatchedStyleDeclaration = () => { |
| for (let rule of nodeStyles.matchedRules) { |
| if (rule.selectorText === ".rule-b") |
| return rule.style; |
| } |
| InspectorTest.fail("No declaration found."); |
| resolve(); |
| }; |
| |
| let getProperty = (propertyName) => { |
| let styleDeclaration = getMatchedStyleDeclaration(); |
| for (let property of styleDeclaration.enabledProperties) { |
| if (property.name === propertyName) |
| return property; |
| } |
| InspectorTest.fail("No property found."); |
| resolve(); |
| }; |
| |
| let styleDeclaration = getMatchedStyleDeclaration(); |
| styleDeclaration.locked = false; |
| |
| styleDeclaration.singleFireEventListener(WI.CSSStyleDeclaration.Event.PropertiesChanged, function(event) { |
| let fontProperty = getProperty("font-size"); |
| let relativeRange = fontProperty.styleSheetTextRange.relativeTo(fontProperty.ownerStyle.styleSheetTextRange.startLine, fontProperty.ownerStyle.styleSheetTextRange.startOffset); |
| relativeRange.resolveOffsets(fontProperty.ownerStyle.text); |
| let propertyText = fontProperty.ownerStyle.text.slice(relativeRange.startOffset, relativeRange.endOffset); |
| InspectorTest.expectEqual(propertyText, "font-size: 10px;", `styleSheetTextRange should correspond to "font-size: 10px;"`); |
| resolve(); |
| }); |
| |
| getProperty("font-size").rawValue = "11px"; |
| getProperty("font-size").rawValue = "10px"; |
| |
| InspectorTest.expectEqual(getProperty("font-size").rawValue, "10px", `"font-size" property value should update immediately.`); |
| |
| InspectorTest.expectEqual(styleDeclaration.text, `\n font-size: 10px;\n color: antiquewhite;\n`, `Style declaration text should update immediately.`); |
| } |
| }); |
| |
| suite.addTestCase({ |
| name: "Update value when CSSStyleDeclaration is locked", |
| test(resolve, reject) { |
| let getMatchedStyleDeclaration = () => { |
| for (let rule of nodeStyles.matchedRules) { |
| if (rule.selectorText === ".rule-a") |
| return rule.style; |
| } |
| InspectorTest.fail("No declaration found."); |
| resolve(); |
| }; |
| |
| let getProperty = (propertyName) => { |
| let styleDeclaration = getMatchedStyleDeclaration(); |
| for (let property of styleDeclaration.enabledProperties) { |
| if (property.name === propertyName) |
| return property; |
| } |
| InspectorTest.fail("No property found."); |
| resolve(); |
| }; |
| |
| let styleDeclaration = getMatchedStyleDeclaration(); |
| styleDeclaration.locked = true; |
| getProperty("font-size").rawValue = "15px"; |
| getProperty("font-size").rawValue = "16px"; |
| |
| InspectorTest.expectEqual(getProperty("font-size").rawValue, "16px", `"font-size" property value should update immediately.`); |
| |
| InspectorTest.expectEqual(styleDeclaration.text, ` |
| font-size: 16px; |
| color: #333; |
| margin-left: 0; |
| margin-top: 1em; |
| `, `Style declaration text should update immediately.`); |
| |
| styleDeclaration.locked = false; |
| |
| resolve(); |
| } |
| }); |
| |
| suite.addTestCase({ |
| name: "ModifyCSSProperty.PropertiesChangedEvent", |
| test(resolve, reject) { |
| let getInlineStyleDeclaration = () => { |
| for (let styleDeclaration of nodeStyles.orderedStyles) { |
| if (styleDeclaration.type === styleDeclaration.constructor.Type.Inline) |
| return styleDeclaration; |
| } |
| InspectorTest.fail("No declaration found."); |
| resolve(); |
| }; |
| |
| let getProperty = (propertyName) => { |
| let styleDeclaration = getInlineStyleDeclaration(); |
| for (let property of styleDeclaration.properties) { |
| if (property.name === propertyName) |
| return property; |
| } |
| InspectorTest.fail("No property found."); |
| resolve(); |
| }; |
| |
| let styleDeclaration = getInlineStyleDeclaration(); |
| |
| styleDeclaration.awaitEvent(WI.CSSStyleDeclaration.Event.PropertiesChanged).then((event) => { |
| InspectorTest.expectThat(!styleDeclaration.locked, `Style declaration is unlocked.`); |
| InspectorTest.expectEqual(getProperty("width").rawValue, "200px", `"width" property value should update to "200px".`); |
| InspectorTest.expectEqual(styleDeclaration.text, `width: 200px;`, `Inline style declaration text should update when not locked.`); |
| resolve(); |
| }); |
| |
| styleDeclaration.locked = true; |
| // WI.CSSStyleDeclaration.Event.PropertiesChanged event should not fire when the style declaration is locked. |
| InspectorTest.evaluateInPage(`makeNarrow()`); |
| |
| styleDeclaration.locked = false; |
| InspectorTest.evaluateInPage(`makeWide()`); |
| } |
| }); |
| |
| suite.addTestCase({ |
| name: "Update inline style value when CSSStyleDeclaration locked and not locked", |
| test(resolve, reject) { |
| let getInlineStyleDeclaration = () => { |
| for (let styleDeclaration of nodeStyles.orderedStyles) { |
| if (styleDeclaration.type === styleDeclaration.constructor.Type.Inline) |
| return styleDeclaration; |
| } |
| InspectorTest.fail("No declaration found."); |
| resolve(); |
| }; |
| |
| let getProperty = (propertyName) => { |
| let styleDeclaration = getInlineStyleDeclaration(); |
| for (let property of styleDeclaration.enabledProperties) { |
| if (property.name === propertyName) |
| return property; |
| } |
| InspectorTest.fail("No property found."); |
| resolve(); |
| }; |
| |
| let styleDeclaration = getInlineStyleDeclaration(); |
| |
| WI.CSSStyleDeclaration.awaitEvent(WI.CSSStyleDeclaration.Event.PropertiesChanged).then((event) => { |
| InspectorTest.expectThat(!styleDeclaration.locked, `Style declaration is unlocked.`); |
| InspectorTest.expectEqual(getProperty("width").rawValue, "128px", `"width" property value should update to "128px".`); |
| InspectorTest.expectEqual(styleDeclaration.text, `width: 128px;`, `Inline style declaration text should update when not locked.`); |
| resolve(); |
| }); |
| |
| styleDeclaration.locked = true; |
| getProperty("width").rawValue = "64px"; |
| InspectorTest.expectEqual(styleDeclaration.text, `width: 64px;`, `Style declaration text should update immediately.`); |
| |
| // WI.CSSStyleDeclaration.Event.PropertiesChanged event should not fire when the style declaration is locked. |
| InspectorTest.evaluateInPage(`makeNarrow()`); |
| |
| styleDeclaration.locked = false; |
| getProperty("width").rawValue = "128px"; |
| InspectorTest.expectEqual(styleDeclaration.text, `width: 128px;`, `Style declaration text should update immediately.`); |
| |
| InspectorTest.evaluateInPage(`makeWide()`); |
| } |
| }); |
| |
| suite.addTestCase({ |
| name: "ModifyCSSProperty.ConsequentValueChanges", |
| test(resolve, reject) { |
| let getInlineStyleDeclaration = () => { |
| for (let styleDeclaration of nodeStyles.orderedStyles) { |
| if (styleDeclaration.type === styleDeclaration.constructor.Type.Inline) |
| return styleDeclaration; |
| } |
| InspectorTest.fail("No declaration found."); |
| resolve(); |
| }; |
| |
| let getProperty = (propertyName) => { |
| let styleDeclaration = getInlineStyleDeclaration(); |
| for (let property of styleDeclaration.properties) { |
| if (property.name === propertyName) |
| return property; |
| } |
| InspectorTest.fail("No property found."); |
| resolve(); |
| }; |
| |
| let styleDeclaration = getInlineStyleDeclaration(); |
| styleDeclaration.locked = false; |
| |
| for (let i = 0; i <= 20; ++i) |
| getProperty("width").rawValue = i + "px"; |
| |
| InspectorTest.expectEqual(styleDeclaration.text, `width: 20px;`, `Style declaration text should contain most recent value.`); |
| |
| resolve(); |
| } |
| }); |
| |
| suite.addTestCase({ |
| name: "ModifyCSSProperty.CommentOutAndUncommentPropertyWithNewlines", |
| test(resolve, reject) { |
| let getMatchedStyleDeclaration = () => { |
| for (let rule of nodeStyles.matchedRules) { |
| if (rule.selectorText === ".rule-c") |
| return rule.style; |
| } |
| InspectorTest.fail("No declaration found."); |
| resolve(); |
| }; |
| |
| let getProperty = (propertyName) => { |
| let styleDeclaration = getMatchedStyleDeclaration(); |
| for (let property of styleDeclaration.properties) { |
| if (property.name === propertyName) |
| return property; |
| } |
| InspectorTest.fail("No property found."); |
| resolve(); |
| }; |
| |
| let styleDeclaration = getMatchedStyleDeclaration(); |
| styleDeclaration.locked = true; |
| |
| InspectorTest.expectThat(!getProperty("padding-right").enabled, `Commented out property should be disabled.`); |
| |
| let disabled = false; |
| getProperty("padding-right").commentOut(disabled); |
| |
| let expectedStyleText = `\n /* padding-left: 2em; */\n padding-right: 0px;\n`; |
| InspectorTest.expectEqual(styleDeclaration.text, expectedStyleText, `Style declaration text should update immediately with uncommented property.`); |
| |
| InspectorTest.expectThat(getProperty("padding-right").enabled, `Uncommented property should be enabled.`); |
| |
| disabled = true; |
| getProperty("padding-right").commentOut(disabled); |
| |
| expectedStyleText = `\n /* padding-left: 2em; */\n /* padding-right: 0px; */\n`; |
| InspectorTest.expectEqual(styleDeclaration.text, expectedStyleText, `Style declaration text should update immediately with commented out property.`); |
| |
| InspectorTest.expectThat(!getProperty("padding-right").enabled, `Commented out property should be disabled.`); |
| |
| resolve(); |
| } |
| }); |
| |
| suite.addTestCase({ |
| name: "ModifyCSSProperty.CommentOutAndUncommentPropertyWithoutNewlines", |
| test(resolve, reject) { |
| let getMatchedStyleDeclaration = () => { |
| for (let rule of nodeStyles.matchedRules) { |
| if (rule.selectorText === ".rule-d") |
| return rule.style; |
| } |
| InspectorTest.fail("No declaration found."); |
| resolve(); |
| }; |
| |
| let getProperty = (propertyName) => { |
| let styleDeclaration = getMatchedStyleDeclaration(); |
| for (let property of styleDeclaration.properties) { |
| if (property.name === propertyName) |
| return property; |
| } |
| InspectorTest.fail("No property found."); |
| resolve(); |
| }; |
| |
| let styleDeclaration = getMatchedStyleDeclaration(); |
| styleDeclaration.locked = true; |
| |
| InspectorTest.expectThat(!getProperty("font-size").enabled, `Commented out property should be disabled.`); |
| |
| let disabled = false; |
| getProperty("font-size").commentOut(disabled); |
| |
| let expectedStyleText = `\n font-size: 13px;\n /* border: 2px solid brown; */\n`; |
| InspectorTest.expectEqual(styleDeclaration.text, expectedStyleText, `Style declaration text should update immediately with uncommented property.`); |
| |
| InspectorTest.expectThat(getProperty("font-size").enabled, `Uncommented property should be enabled.`); |
| InspectorTest.expectThat(!getProperty("border").enabled, `Commented out property should be disabled.`); |
| |
| disabled = false; |
| getProperty("border").commentOut(disabled); |
| |
| expectedStyleText = `\n font-size: 13px;\n border: 2px solid brown\n`; |
| InspectorTest.expectEqual(styleDeclaration.text, expectedStyleText, `Style declaration text should update immediately with commented out property.`); |
| |
| InspectorTest.expectThat(getProperty("border").enabled, `Uncommented property should be enabled.`); |
| |
| resolve(); |
| } |
| }); |
| |
| suite.addTestCase({ |
| name: "ModifyCSSProperty.ReplacePropertyName", |
| async test() { |
| let getMatchedStyleDeclaration = () => { |
| for (let rule of nodeStyles.matchedRules) { |
| if (rule.selectorText === ".rule-e") |
| return rule.style; |
| } |
| throw "No declaration found."; |
| }; |
| let getProperty = (propertyName) => { |
| let styleDeclaration = getMatchedStyleDeclaration(); |
| for (let property of styleDeclaration.properties) { |
| if (property.name === propertyName) |
| return property; |
| } |
| throw "No property found."; |
| }; |
| let styleDeclaration = getMatchedStyleDeclaration(); |
| |
| let cssProperty = getProperty("color"); |
| cssProperty.name = ""; |
| InspectorTest.expectEqual(styleDeclaration.text, "", "Style declaration text should be empty."); |
| |
| cssProperty.name = "border-color"; |
| InspectorTest.expectEqual(styleDeclaration.text, `\n border-color: darkseagreen;\n`, "Style declaration text should have new property name."); |
| } |
| }); |
| |
| function addTestCase({name, description, selector, authoredRulesHandler}) |
| { |
| suite.addTestCase({ |
| name, |
| description, |
| async test() { |
| let documentNode = await WI.domManager.requestDocument(); |
| let nodeId = await documentNode.querySelector(selector); |
| let domNode = WI.domManager.nodeForId(nodeId); |
| InspectorTest.assert(domNode, `Should find DOM Node for selector '#item'.`); |
| |
| let domNodeStyles = WI.cssManager.stylesForNode(domNode); |
| InspectorTest.assert(domNodeStyles, `Should find CSS Styles for DOM Node.`); |
| await domNodeStyles.refresh(); |
| |
| let flatInheritedRules = domNodeStyles.inheritedRules.flatMap((inheritedRuleInfo) => inheritedRuleInfo.matchedRules); |
| |
| let authoredRules = [...domNodeStyles.matchedRules, ...flatInheritedRules].filter((rule) => rule.type === WI.CSSStyleSheet.Type.Author); |
| await authoredRulesHandler(authoredRules, domNodeStyles); |
| }, |
| }); |
| } |
| |
| function findRuleStyleInRules(rules, {selector, nestingRootSelector}) { |
| for (let rule of rules) { |
| if (rule.selectorText !== selector) |
| continue; |
| |
| if (!nestingRootSelector) |
| return rule.style; |
| |
| for (let grouping of rule.groupings) { |
| if (grouping.type !== WI.CSSGrouping.Type.StyleRule) |
| continue; |
| |
| if (grouping.text !== nestingRootSelector) |
| continue; |
| |
| return rule.style; |
| } |
| } |
| InspectorTest.fail(`No rule found with selector "${selector}" nested under selector "${nestingRootSelector}".`); |
| } |
| |
| function getColorProperty(styleDeclaration) { |
| for (let property of styleDeclaration.properties) { |
| if (property.name === "color") |
| return property; |
| } |
| InspectorTest.fail("No color property found in style declaration."); |
| } |
| |
| addTestCase({ |
| name: "ModifyCSSProperty.EnsureNestedRulesEditingDoesNotTrampleInnerRules", |
| description: "", |
| selector: ".rule-f", |
| async authoredRulesHandler(authoredRules, domNodeStyles) { |
| let outerRuleStyle = findRuleStyleInRules(authoredRules, { selector: ".rule-f" }); |
| InspectorTest.expectEqual(outerRuleStyle.text, "\n color: red;\n\n @media(min-width: 0px) {\n &.explicit-nested-rule { \n color: blue;\n }\n\n color: green;\n }\n", "Outer style declaration text should contain the expected `color` property."); |
| let outerRuleStyleChangedPromise = domNodeStyles.awaitEvent(WI.DOMNodeStyles.Event.Refreshed); |
| getColorProperty(outerRuleStyle).rawValue = "magenta"; |
| await outerRuleStyleChangedPromise; |
| InspectorTest.expectEqual(outerRuleStyle.text, "\n color: magenta;\n\n @media(min-width: 0px) {\n &.explicit-nested-rule { \n color: blue;\n }\n\n color: green;\n }\n\n", "Outer style declaration text should contain the new `color` value."); |
| |
| let innerImplicitRuleStyle = findRuleStyleInRules(authoredRules, { selector: "&", nestingRootSelector: ".rule-f" }); |
| InspectorTest.expectEqual(innerImplicitRuleStyle.text, "\n &.explicit-nested-rule { \n color: blue;\n }\n\n color: green;\n ", "Inner implicit style declaration text should contain the expected `color` property."); |
| let innerImplicitRuleStyleChangedPromise = domNodeStyles.awaitEvent(WI.DOMNodeStyles.Event.Refreshed); |
| getColorProperty(innerImplicitRuleStyle).rawValue = "yellow"; |
| await innerImplicitRuleStyleChangedPromise; |
| InspectorTest.expectEqual(innerImplicitRuleStyle.text, "\n color: yellow;\n \n &.explicit-nested-rule { \n color: blue;\n }\n\n ", "Inner implicit style declaration text should contain the new `color` value."); |
| |
| let innerExplicitRuleStyle = findRuleStyleInRules(authoredRules, { selector: "&.explicit-nested-rule", nestingRootSelector: ".rule-f" }); |
| InspectorTest.expectEqual(innerExplicitRuleStyle.text, " \n color: blue;\n ", "Inner explicit style declaration text should contain the expected `color` property."); |
| let innerExplicitRuleStyleChangedPromise = domNodeStyles.awaitEvent(WI.DOMNodeStyles.Event.Refreshed); |
| getColorProperty(innerExplicitRuleStyle).rawValue = "cyan"; |
| await innerExplicitRuleStyleChangedPromise; |
| InspectorTest.expectEqual(innerExplicitRuleStyle.text, "\n color: cyan;\n \n ", "Inner explicit style declaration text should contain the new `color` value."); |
| }, |
| }) |
| |
| addTestCase({ |
| name: "ModifyCSSProperty.EnsureNestedRulesEditingDoesNotTrampleInnerRulesOriginalExcludesNewlines", |
| description: "", |
| selector: ".rule-g", |
| async authoredRulesHandler(authoredRules, domNodeStyles) { |
| let outerRuleStyle = findRuleStyleInRules(authoredRules, { selector: ".rule-g" }); |
| InspectorTest.expectEqual(outerRuleStyle.text, " color: red; @media(min-width: 0px) { &.explicit-nested-rule { color: blue; } color: green; } ", "Outer style declaration text should contain the expected `color` property."); |
| let outerRuleStyleChangedPromise = domNodeStyles.awaitEvent(WI.DOMNodeStyles.Event.Refreshed); |
| getColorProperty(outerRuleStyle).rawValue = "magenta"; |
| await outerRuleStyleChangedPromise; |
| InspectorTest.expectEqual(outerRuleStyle.text, "\n color: magenta;\n\n @media(min-width: 0px) { &.explicit-nested-rule { color: blue; } color: green; }\n\n", "Outer style declaration text should contain the new `color` value."); |
| |
| let innerImplicitRuleStyle = findRuleStyleInRules(authoredRules, { selector: "&", nestingRootSelector: ".rule-g" }); |
| InspectorTest.expectEqual(innerImplicitRuleStyle.text, " &.explicit-nested-rule { color: blue; } color: green; ", "Inner implicit style declaration text should contain the expected `color` property."); |
| let innerImplicitRuleStyleChangedPromise = domNodeStyles.awaitEvent(WI.DOMNodeStyles.Event.Refreshed); |
| getColorProperty(innerImplicitRuleStyle).rawValue = "yellow"; |
| await innerImplicitRuleStyleChangedPromise; |
| InspectorTest.expectEqual(innerImplicitRuleStyle.text, "\n color: yellow;\n \n &.explicit-nested-rule { color: blue; }\n\n ", "Inner implicit style declaration text should contain the new `color` value."); |
| |
| let innerExplicitRuleStyle = findRuleStyleInRules(authoredRules, { selector: "&.explicit-nested-rule", nestingRootSelector: ".rule-g" }); |
| InspectorTest.expectEqual(innerExplicitRuleStyle.text, " color: blue; ", "Inner explicit style declaration text should contain the expected `color` property."); |
| let innerExplicitRuleStyleChangedPromise = domNodeStyles.awaitEvent(WI.DOMNodeStyles.Event.Refreshed); |
| getColorProperty(innerExplicitRuleStyle).rawValue = "cyan"; |
| await innerExplicitRuleStyleChangedPromise; |
| InspectorTest.expectEqual(innerExplicitRuleStyle.text, "\n color: cyan;\n \n ", "Inner explicit style declaration text should contain the new `color` value."); |
| }, |
| }) |
| |
| WI.domManager.requestDocument((documentNode) => { |
| documentNode.querySelector("#x", (contentNodeId) => { |
| if (contentNodeId) { |
| let domNode = WI.domManager.nodeForId(contentNodeId); |
| nodeStyles = WI.cssManager.stylesForNode(domNode); |
| |
| let flatInheritedRules = nodeStyles.inheritedRules.flatMap((inheritedRuleInfo) => inheritedRuleInfo.matchedRules); |
| authoredRules = [...nodeStyles.matchedRules, ...flatInheritedRules].filter((rule) => rule.type === WI.CSSStyleSheet.Type.Author); |
| |
| if (nodeStyles.needsRefresh) { |
| nodeStyles.singleFireEventListener(WI.DOMNodeStyles.Event.Refreshed, (event) => { |
| suite.runTestCasesAndFinish() |
| }); |
| } else |
| suite.runTestCasesAndFinish(); |
| } else { |
| InspectorTest.fail("DOM node not found."); |
| InspectorTest.completeTest(); |
| } |
| }); |
| }); |
| } |
| </script> |
| </head> |
| <body onload="runTest()"> |
| <p>Testing that CSSStyleDeclaration update immediately after modifying its properties when it is not locked.</p> |
| <div id="x" class="test-node rule-a rule-b rule-c rule-d rule-e rule-f rule-g explicit-nested-rule" style="width: 100px"></div> |
| </body> |
| </html> |