browser_rules_user-agent-styles.js (6435B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Check that user agent styles are inspectable via rule view if 7 // it is preffed on. 8 9 var PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles"; 10 const { PrefObserver } = require("resource://devtools/client/shared/prefs.js"); 11 12 const TEST_URI = URL_ROOT + "doc_author-sheet.html"; 13 14 const TEST_DATA = [ 15 { 16 selector: "blockquote", 17 numUserRules: 1, 18 numUARules: 0, 19 }, 20 { 21 selector: "pre", 22 numUserRules: 1, 23 numUARules: 0, 24 }, 25 { 26 selector: "input[type=range]", 27 numUserRules: 1, 28 numUARules: 0, 29 }, 30 { 31 selector: "input[type=number]", 32 numUserRules: 1, 33 numUARules: 0, 34 }, 35 { 36 selector: "input[type=color]", 37 numUserRules: 1, 38 numUARules: 0, 39 }, 40 { 41 selector: "input[type=text]", 42 numUserRules: 1, 43 numUARules: 0, 44 }, 45 { 46 selector: "progress", 47 numUserRules: 1, 48 numUARules: 0, 49 }, 50 // Note that some tests below assume that the "a" selector is the 51 // last test in TEST_DATA. 52 { 53 selector: "a", 54 numUserRules: 3, 55 numUARules: 0, 56 }, 57 ]; 58 59 add_task(async function () { 60 // Bug 1517210: GC heuristics are broken for this test, so that the test ends up 61 // running out of memory if we don't force to reduce the GC side before/after the test. 62 Cu.forceShrinkingGC(); 63 64 requestLongerTimeout(4); 65 66 info("Starting the test with the pref set to true before toolbox is opened"); 67 await setUserAgentStylesPref(true); 68 69 await addTab(TEST_URI); 70 const { inspector, view } = await openRuleView(); 71 72 info("Making sure that UA styles are visible on initial load"); 73 await userAgentStylesVisible(inspector, view); 74 75 info("Making sure that setting the pref to false hides UA styles"); 76 await setUserAgentStylesPref(false); 77 await userAgentStylesNotVisible(inspector, view); 78 79 info("Making sure that resetting the pref to true shows UA styles again"); 80 await setUserAgentStylesPref(true); 81 await userAgentStylesVisible(inspector, view); 82 83 info("Resetting " + PREF_UA_STYLES); 84 Services.prefs.clearUserPref(PREF_UA_STYLES); 85 86 // Bug 1517210: GC heuristics are broken for this test, so that the test ends up 87 // running out of memory if we don't force to reduce the GC side before/after the test. 88 Cu.forceShrinkingGC(); 89 }); 90 91 async function setUserAgentStylesPref(val) { 92 info("Setting the pref " + PREF_UA_STYLES + " to: " + val); 93 94 // Reset the pref and wait for PrefObserver to callback so UI 95 // has a chance to get updated. 96 const prefObserver = new PrefObserver("devtools."); 97 const oncePrefChanged = new Promise(resolve => { 98 prefObserver.on(PREF_UA_STYLES, onPrefChanged); 99 100 function onPrefChanged() { 101 prefObserver.off(PREF_UA_STYLES, onPrefChanged); 102 resolve(); 103 } 104 }); 105 Services.prefs.setBoolPref(PREF_UA_STYLES, val); 106 await oncePrefChanged; 107 } 108 109 async function userAgentStylesVisible(inspector, view) { 110 info("Making sure that user agent styles are currently visible"); 111 112 let userRules; 113 let uaRules; 114 115 for (const data of TEST_DATA) { 116 await selectNode(data.selector, inspector); 117 await compareAppliedStylesWithUI(inspector, view, "ua"); 118 119 userRules = view._elementStyle.rules.filter(rule => rule.editor.isEditable); 120 uaRules = view._elementStyle.rules.filter(rule => !rule.editor.isEditable); 121 is(userRules.length, data.numUserRules, "Correct number of user rules"); 122 Assert.greater(uaRules.length, data.numUARules, "Has UA rules"); 123 } 124 125 ok( 126 userRules.some(rule => rule.matchedSelectorIndexes.length === 1), 127 "There is an inline style for element in user styles" 128 ); 129 130 // These tests rely on the "a" selector being the last test in 131 // TEST_DATA. 132 ok( 133 uaRules.some(rule => { 134 const matchedSelectors = rule.matchedSelectorIndexes.map( 135 index => rule.selector.selectors[index] 136 ); 137 return matchedSelectors.includes(":any-link"); 138 }), 139 "There is a rule for :any-link" 140 ); 141 ok( 142 uaRules.some(rule => { 143 const matchedSelectors = rule.matchedSelectorIndexes.map( 144 index => rule.selector.selectors[index] 145 ); 146 return matchedSelectors.includes(":link"); 147 }), 148 "There is a rule for :link" 149 ); 150 ok( 151 uaRules.some(rule => { 152 return rule.matchedSelectorIndexes.length === 1; 153 }), 154 "Inline styles for ua styles" 155 ); 156 } 157 158 async function userAgentStylesNotVisible(inspector, view) { 159 info("Making sure that user agent styles are not currently visible"); 160 161 let userRules; 162 let uaRules; 163 164 for (const data of TEST_DATA) { 165 await selectNode(data.selector, inspector); 166 await compareAppliedStylesWithUI(inspector, view); 167 168 userRules = view._elementStyle.rules.filter(rule => rule.editor.isEditable); 169 uaRules = view._elementStyle.rules.filter(rule => !rule.editor.isEditable); 170 is(userRules.length, data.numUserRules, "Correct number of user rules"); 171 is(uaRules.length, data.numUARules, "No UA rules"); 172 } 173 } 174 175 async function compareAppliedStylesWithUI(inspector, view, filter) { 176 info("Making sure that UI is consistent with pageStyle.getApplied"); 177 178 const pageStyle = inspector.selection.nodeFront.inspectorFront.pageStyle; 179 let entries = await pageStyle.getApplied(inspector.selection.nodeFront, { 180 inherited: true, 181 matchedSelectors: true, 182 filter, 183 }); 184 185 // We may see multiple entries that map to a given rule; filter the 186 // duplicates here to match what the UI does. 187 const entryMap = new Map(); 188 for (const entry of entries) { 189 entryMap.set(entry.rule, entry); 190 } 191 entries = [...entryMap.values()]; 192 193 const elementStyle = view._elementStyle; 194 await waitFor(() => elementStyle.rules.length === entries.length); 195 is( 196 elementStyle.rules.length, 197 entries.length, 198 "Should have correct number of rules (" + entries.length + ")" 199 ); 200 201 entries = entries.sort((a, b) => { 202 return (a.pseudoElement || "z") > (b.pseudoElement || "z"); 203 }); 204 205 entries.forEach((entry, i) => { 206 const elementStyleRule = elementStyle.rules[i]; 207 is( 208 !!elementStyleRule.inherited, 209 !!entry.inherited, 210 "Same inherited (" + entry.inherited + ")" 211 ); 212 is( 213 elementStyleRule.isSystem, 214 entry.isSystem, 215 "Same isSystem (" + entry.isSystem + ")" 216 ); 217 is( 218 elementStyleRule.editor.isEditable, 219 !entry.isSystem, 220 "Editor isEditable opposite of UA (" + entry.isSystem + ")" 221 ); 222 }); 223 }