font-computed.html (6545B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>CSS Fonts Module Level 4: getComputedStyle().font</title> 6 <link rel="help" href="https://drafts.csswg.org/css-fonts-4/#font-prop"> 7 <meta name="assert" content="font computed value round-trips."> 8 <script src="/resources/testharness.js"></script> 9 <script src="/resources/testharnessreport.js"></script> 10 <script src="/css/support/computed-testcommon.js"></script> 11 <style> 12 #container { 13 font-weight: 800; 14 font-size: 40px; 15 } 16 </style> 17 </head> 18 <body> 19 <div id="container"> 20 <div id="target"></div> 21 </div> 22 <script> 23 'use strict'; 24 25 // Firefox and Edge 18 serialize these as supplied. 26 // Blink and Safari have implementation-dependent or platform-dependent serializations. 27 function test_system_font(keyword) { 28 test(() => { 29 const target = document.getElementById('target'); 30 const previousValue = 'italic xx-large/0px fantasy'; 31 target.style.font = previousValue; 32 target.style.font = keyword; 33 const readValue = getComputedStyle(target).font; 34 assert_not_equals(readValue, '', 'font should be set'); 35 assert_not_equals(readValue, previousValue, 'font should be updated'); 36 target.style.font = previousValue; 37 target.style.font = readValue; 38 assert_equals(getComputedStyle(target).font, readValue, "serialization should round-trip"); 39 }, keyword + ' should be a supported system font.'); 40 } 41 42 test_system_font('caption'); 43 test_system_font('icon'); 44 test_system_font('menu'); 45 test_system_font('message-box'); 46 test_system_font('small-caption'); 47 test_system_font('status-bar'); 48 49 // a value other than normal 50 const generate_style = () => 'italic'; 51 52 // value other than normal 53 const generate_variant = () => 'small-caps'; 54 55 // values other than normal 56 const generate_weight = (() => { 57 const alternatives = [ 58 'bold', 59 'bolder', 60 'lighter', 61 '100', 62 '900' 63 ]; 64 let counter = 0; 65 return () => alternatives[counter++ % alternatives.length]; 66 })(); 67 68 const compute_weight = (() => { 69 const cache = {} 70 return (weight) => { 71 if (!(weight in cache)) { 72 const weight_reference = document.createElement('div'); 73 document.getElementById('container').appendChild(weight_reference); 74 weight_reference.style.fontWeight = weight; 75 cache[weight] = getComputedStyle(weight_reference).fontWeight; 76 weight_reference.remove(); 77 } 78 return cache[weight]; 79 } 80 })(); 81 82 // values other than normal 83 const generate_stretch = (() => { 84 const alternatives = [ 85 'ultra-condensed', 86 'extra-condensed', 87 'condensed', 88 'semi-condensed', 89 'semi-expanded', 90 'expanded', 91 'extra-expanded', 92 'ultra-expanded' 93 ]; 94 let counter = 0; 95 return () => alternatives[counter++ % alternatives.length]; 96 })(); 97 98 const generate_size = (() => { 99 const alternatives = [ 100 // <absolute-size> 101 'xx-small', 102 'medium', 103 'xx-large', 104 105 // <relative-size> 106 'larger', 107 'smaller', 108 109 // <length-percentage> 110 '10px', 111 '20%', 112 'calc(30% - 40px)', 113 ]; 114 let counter = 0; 115 return () => alternatives[counter++ % alternatives.length]; 116 })(); 117 118 const generate_line_height = (() => { 119 const alternatives = [ 120 null, 121 'normal', 122 '1.2', 123 'calc(120% + 1.2em)' 124 ]; 125 let counter = 0; 126 return () => alternatives[counter++ % alternatives.length]; 127 })(); 128 129 const generate_family = (() => { 130 const alternatives = [ 131 'serif', 132 'sans-serif', 133 'cursive', 134 'fantasy', 135 'monospace', 136 'Menu', 137 '"Non-Generic Example Family Name"' 138 ]; 139 let counter = 0; 140 return () => alternatives[counter++ % alternatives.length]; 141 })(); 142 143 function test_specific(prefix) { 144 const reference = document.createElement('div'); 145 document.getElementById('container').appendChild(reference); 146 147 let parts = []; 148 let canonical = []; 149 let style = null; 150 let variant = null; 151 let weight = null; 152 let stretch = null; 153 for (let entry of prefix) { 154 if (entry === 'style') { 155 style = generate_style(); 156 parts.push(style); 157 } else if (entry === 'variant') { 158 variant = generate_variant(); 159 parts.push(variant); 160 } else if (entry === 'weight') { 161 weight = generate_weight(); 162 parts.push(weight); 163 } else if (entry === 'stretch') { 164 stretch = generate_stretch(); 165 parts.push(stretch); 166 } else { 167 // normal 168 parts.push('normal'); 169 } 170 } 171 172 if (style) { 173 canonical.push(style); 174 reference.style.fontStyle = style; 175 } 176 177 if (variant) { 178 canonical.push(variant); 179 reference.style.fontVariant = style; 180 } 181 if (weight) { 182 canonical.push(compute_weight(weight)); 183 reference.style.fontWeight = style; 184 } 185 if (stretch) { 186 canonical.push(stretch); 187 reference.style.fontStretch = style; 188 } 189 190 const size = generate_size(); 191 reference.style.fontSize = size; 192 const line_height = generate_line_height(); 193 if (line_height) { 194 parts.push(size + '/' + line_height); 195 reference.style.lineHeight = line_height; 196 } else { 197 parts.push(size); 198 } 199 200 const family = generate_family(); 201 parts.push(family); 202 reference.style.fontFamily = family; 203 204 if (!line_height || line_height === 'normal') { 205 canonical.push(getComputedStyle(reference).fontSize); 206 } else { 207 // Implementations differ on adjacent space when serializing '/' 208 // https://github.com/w3c/csswg-drafts/issues/4282 209 canonical.push(getComputedStyle(reference).fontSize + ' / ' + getComputedStyle(reference).lineHeight); 210 } 211 212 canonical.push(family); 213 214 reference.remove(); 215 216 test_computed_value('font', parts.join(' '), canonical.join(' ')); 217 } 218 219 // Font style, variant, weight and stretch may appear in any order. 220 // Any or all may be omitted. Each accepts the keyword 'normal'. 221 // We generate every permutation of these four properties, treating 222 // the cases of a property value being omitted or being explicitly 223 // 'normal' as being distinct permutations from when the property 224 // has a value other than 'normal'. 225 function test_various(prefix) { 226 test_specific(prefix); 227 if (prefix.length === 4) { 228 // Font style, variant, weight and stretch may not appear 229 // more than once. 230 return; 231 } 232 233 const alternatives = [ 234 'normal', 235 'style', 236 'variant', 237 'weight', 238 'stretch' 239 ]; 240 for (let alternative of alternatives) { 241 // Since this is called recursively, check prefix for existing 242 // alternatives, otherwise we may have two styles or two variants, etc. 243 if (alternative === 'normal' || !prefix.includes(alternative)) 244 test_various(prefix.concat(alternative)); 245 } 246 } 247 248 test_various([]); 249 </script> 250 </body> 251 </html>