| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <title>CSS Fonts Module Level 4: getComputedStyle().font</title> |
| <link rel="help" href="https://drafts.csswg.org/css-fonts-4/#font-prop"> |
| <meta name="assert" content="font computed value round-trips."> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="/css/support/computed-testcommon.js"></script> |
| <style> |
| #container { |
| font-weight: 800; |
| font-size: 40px; |
| } |
| </style> |
| </head> |
| <body> |
| <div id="container"> |
| <div id="target"></div> |
| </div> |
| <script> |
| 'use strict'; |
| |
| // Firefox and Edge 18 serialize these as supplied. |
| // Blink and Safari have implementation-dependent or platform-dependent serializations. |
| function test_system_font(keyword) { |
| test(() => { |
| const target = document.getElementById('target'); |
| const previousValue = 'italic xx-large/0px fantasy'; |
| target.style.font = previousValue; |
| target.style.font = keyword; |
| const readValue = getComputedStyle(target).font; |
| assert_not_equals(readValue, '', 'font should be set'); |
| assert_not_equals(readValue, previousValue, 'font should be updated'); |
| target.style.font = previousValue; |
| target.style.font = readValue; |
| assert_equals(getComputedStyle(target).font, readValue, "serialization should round-trip"); |
| }, keyword + ' should be a supported system font.'); |
| } |
| |
| test_system_font('caption'); |
| test_system_font('icon'); |
| test_system_font('menu'); |
| test_system_font('message-box'); |
| test_system_font('small-caption'); |
| test_system_font('status-bar'); |
| |
| // a value other than normal |
| const generate_style = () => 'italic'; |
| |
| // value other than normal |
| const generate_variant = () => 'small-caps'; |
| |
| // values other than normal |
| const generate_weight = (() => { |
| const alternatives = [ |
| 'bold', |
| 'bolder', |
| 'lighter', |
| '100', |
| '900' |
| ]; |
| let counter = 0; |
| return () => alternatives[counter++ % alternatives.length]; |
| })(); |
| |
| const compute_weight = (() => { |
| const cache = {} |
| return (weight) => { |
| if (!(weight in cache)) { |
| const weight_reference = document.createElement('div'); |
| document.getElementById('container').appendChild(weight_reference); |
| weight_reference.style.fontWeight = weight; |
| cache[weight] = getComputedStyle(weight_reference).fontWeight; |
| weight_reference.remove(); |
| } |
| return cache[weight]; |
| } |
| })(); |
| |
| // values other than normal |
| const generate_stretch = (() => { |
| const alternatives = [ |
| 'ultra-condensed', |
| 'extra-condensed', |
| 'condensed', |
| 'semi-condensed', |
| 'semi-expanded', |
| 'expanded', |
| 'extra-expanded', |
| 'ultra-expanded' |
| ]; |
| let counter = 0; |
| return () => alternatives[counter++ % alternatives.length]; |
| })(); |
| |
| const generate_size = (() => { |
| const alternatives = [ |
| // <absolute-size> |
| 'xx-small', |
| 'medium', |
| 'xx-large', |
| |
| // <relative-size> |
| 'larger', |
| 'smaller', |
| |
| // <length-percentage> |
| '10px', |
| '20%', |
| 'calc(30% - 40px)', |
| ]; |
| let counter = 0; |
| return () => alternatives[counter++ % alternatives.length]; |
| })(); |
| |
| const generate_line_height = (() => { |
| const alternatives = [ |
| null, |
| 'normal', |
| '1.2', |
| 'calc(120% + 1.2em)' |
| ]; |
| let counter = 0; |
| return () => alternatives[counter++ % alternatives.length]; |
| })(); |
| |
| const generate_family = (() => { |
| const alternatives = [ |
| 'serif', |
| 'sans-serif', |
| 'cursive', |
| 'fantasy', |
| 'monospace', |
| 'Menu', |
| '"Non-Generic Example Family Name"' |
| ]; |
| let counter = 0; |
| return () => alternatives[counter++ % alternatives.length]; |
| })(); |
| |
| function test_specific(prefix) { |
| const reference = document.createElement('div'); |
| document.getElementById('container').appendChild(reference); |
| |
| let parts = []; |
| let canonical = []; |
| let style = null; |
| let variant = null; |
| let weight = null; |
| let stretch = null; |
| for (let entry of prefix) { |
| if (entry === 'style') { |
| style = generate_style(); |
| parts.push(style); |
| } else if (entry === 'variant') { |
| variant = generate_variant(); |
| parts.push(variant); |
| } else if (entry === 'weight') { |
| weight = generate_weight(); |
| parts.push(weight); |
| } else if (entry === 'stretch') { |
| stretch = generate_stretch(); |
| parts.push(stretch); |
| } else { |
| // normal |
| parts.push('normal'); |
| } |
| } |
| |
| if (style) { |
| canonical.push(style); |
| reference.style.fontStyle = style; |
| } |
| |
| if (variant) { |
| canonical.push(variant); |
| reference.style.fontVariant = style; |
| } |
| if (weight) { |
| canonical.push(compute_weight(weight)); |
| reference.style.fontWeight = style; |
| } |
| if (stretch) { |
| canonical.push(stretch); |
| reference.style.fontStretch = style; |
| } |
| |
| const size = generate_size(); |
| reference.style.fontSize = size; |
| const line_height = generate_line_height(); |
| if (line_height) { |
| parts.push(size + '/' + line_height); |
| reference.style.lineHeight = line_height; |
| } else { |
| parts.push(size); |
| } |
| |
| const family = generate_family(); |
| parts.push(family); |
| reference.style.fontFamily = family; |
| |
| if (!line_height || line_height === 'normal') { |
| canonical.push(getComputedStyle(reference).fontSize); |
| } else { |
| // Implementations differ on adjacent space when serializing '/' |
| // https://github.com/w3c/csswg-drafts/issues/4282 |
| canonical.push(getComputedStyle(reference).fontSize + ' / ' + getComputedStyle(reference).lineHeight); |
| } |
| |
| canonical.push(family); |
| |
| reference.remove(); |
| |
| test_computed_value('font', parts.join(' '), canonical.join(' ')); |
| } |
| |
| // Font style, variant, weight and stretch may appear in any order. |
| // Any or all may be omitted. Each accepts the keyword 'normal'. |
| // We generate every permutation of these four properties, treating |
| // the cases of a property value being omitted or being explicitly |
| // 'normal' as being distinct permutations from when the property |
| // has a value other than 'normal'. |
| function test_various(prefix) { |
| test_specific(prefix); |
| if (prefix.length === 4) { |
| // Font style, variant, weight and stretch may not appear |
| // more than once. |
| return; |
| } |
| |
| const alternatives = [ |
| 'normal', |
| 'style', |
| 'variant', |
| 'weight', |
| 'stretch' |
| ]; |
| for (let alternative of alternatives) { |
| // Since this is called recursively, check prefix for existing |
| // alternatives, otherwise we may have two styles or two variants, etc. |
| if (alternative === 'normal' || !prefix.includes(alternative)) |
| test_various(prefix.concat(alternative)); |
| } |
| } |
| |
| test_various([]); |
| </script> |
| </body> |
| </html> |