<!DOCTYPE html>
<meta charset="utf-8">
<title>Common serialization checks for all properties</title>
<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com" />
<link rel="help" href="https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue">

<div id="element"></div>

<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
const element = document.getElementById("element");
const { style } = element;
const computedStyle = getComputedStyle(element);
const cssProperties = new Set();
const cssShorthands = new Map();
const cssShorthandsForLonghand = new Map();
const cssLonghands = new Set();
const cssAliases = new Map();
const initialValues = new Map();

setup(function() {
  for (let obj = style; obj; obj = Reflect.getPrototypeOf(obj)) {
    for (let name of Object.getOwnPropertyNames(obj)) {
      const property = name.replace(/[A-Z]/g, c => "-" + c.toLowerCase());
      if (CSS.supports(property, "initial")) {
        cssProperties.add(property);
      }
    }
  }
  for (let property of cssProperties) {
    style.cssText = "";
    style.setProperty(property, "initial");
    if (style.length > 1) {
      cssShorthands.set(property, [...style]);
      for (let longhand of style) {
        if (cssShorthandsForLonghand.has(longhand)) {
          cssShorthandsForLonghand.get(longhand).add(property);
        } else {
          cssShorthandsForLonghand.set(longhand, new Set([property]));
        }
      }
    } else if (style.length === 1) {
      if (property === style[0]) {
        cssLonghands.add(property);
      } else {
        cssAliases.set(property, style[0]);
      }
    }
  }
});

test(function() {
  const bad = [];
  for (let property of cssProperties) {
    style.cssText = "";
    style.setProperty(property, "initial");
    const result = style.getPropertyValue(property);
    if (result !== "initial") {
      bad.push([property, result]);
    }
  }
  assert_array_equals(bad, []);
}, "All properties can serialize 'initial'");

test(function() {
  for (let longhand of cssLonghands) {
    element.style.setProperty(longhand, "initial");
  }
  const bad = [];
  for (let property of cssProperties) {
    const result = computedStyle.getPropertyValue(property);
    if (CSS.supports(property, result)) {
      initialValues.set(property, result);
    } else if (property !== "all") {
      bad.push([property, result]);
    }
  }
  assert_array_equals(bad, []);
}, "All properties (except 'all') can serialize their initial value (computed)");

test(function() {
  const bad = [];
  for (let [property, value] of initialValues) {
    style.cssText = "";
    style.setProperty(property, value);
    const result = style.getPropertyValue(property);
    if (!CSS.supports(property, result) && property !== "all") {
      bad.push([property, value, result]);
    }
  }
  assert_array_equals(bad, []);
}, "All properties (except 'all') can serialize their initial value (specified)");

test(function() {
  const bad = [];
  for (let [shorthand, longhands] of cssShorthands) {
    style.cssText = "";
    for (let longhand of longhands) {
      style.setProperty(longhand, "initial");
    }
    const result = style.getPropertyValue(shorthand);
    if (result !== "initial") {
      bad.push([shorthand, result]);
    }
  }
  assert_array_equals(bad, []);
}, "All shorthands can serialize their longhands set to 'initial'");

test(function() {
  const bad = [];
  outerloop:
  for (let [shorthand, longhands] of cssShorthands) {
    style.cssText = "";
    for (let longhand of longhands) {
      if (!initialValues.has(longhand)) {
        continue outerloop;
      }
      style.setProperty(longhand, initialValues.get(longhand));
    }
    const result = style.getPropertyValue(shorthand);
    if (!CSS.supports(shorthand, result) && shorthand !== "all") {
      bad.push([shorthand, result]);
    }
  }
  assert_array_equals(bad, []);
}, "All shorthands (except 'all') can serialize their longhands set to their initial value");

test(function() {
  const bad = [];
  for (let [alias, target] of cssAliases) {
    style.cssText = "";
    style.setProperty(target, "initial");
    const result = style.getPropertyValue(alias);
    if (result !== "initial") {
      bad.push([alias, result]);
    }
  }
  assert_array_equals(bad, []);
}, "All aliases can serialize target property set to 'initial'");

test(function() {
  const bad = [];
  for (let [alias, target] of cssAliases) {
    if (!initialValues.has(target)) {
      continue;
    }
    style.cssText = "";
    style.setProperty(target, initialValues.get(target));
    const result = style.getPropertyValue(alias);
    if (!CSS.supports(alias, result)) {
      bad.push([alias, result]);
    }
  }
  assert_array_equals(bad, []);
}, "All aliases can serialize target property set to its initial value");

test(function() {
  const bad = [];
  for (let [shorthand, longhands] of cssShorthands) {
    for (let longhand of longhands) {
      style.cssText = "";
      style.setProperty(shorthand, "initial");
      style.setProperty(longhand, "inherit");
      const result = style.getPropertyValue(shorthand);
      if (result !== "") {
        bad.push([shorthand, longhand, result]);
      }
    }
  }
  assert_array_equals(bad, []);
}, "Can't serialize shorthand when longhands are set to different css-wide keywords");

test(function() {
  const bad = [];
  for (let [shorthand, longhands] of cssShorthands) {
    for (let longhand of longhands) {
      style.cssText = "";
      style.setProperty(shorthand, "initial");
      style.setProperty(longhand, "initial", "important");
      const result = style.getPropertyValue(shorthand);
      if (result !== "") {
        bad.push([shorthand, longhand, result]);
      }
    }
  }
  assert_array_equals(bad, []);
}, "Can't serialize shorthand when longhands have different priority");

test(function() {
  const bad = [];
  for (let [shorthand, longhands] of cssShorthands) {
    for (let longhand of longhands) {
      style.cssText = "";
      style.setProperty(shorthand, "initial");
      style.removeProperty(longhand);
      const result = style.getPropertyValue(shorthand);
      if (result !== "") {
        bad.push([shorthand, longhand, result]);
      }
    }
  }
  assert_array_equals(bad, []);
}, "Can't serialize shorthand set to 'initial' when some longhand is missing");

test(function() {
  const bad = [];
  for (let [shorthand, longhands] of cssShorthands) {
    if (initialValues.has(shorthand)) {
      for (let longhand of longhands) {
        style.cssText = "";
        style.setProperty(shorthand, initialValues.get(shorthand));
        style.removeProperty(longhand);
        const result = style.getPropertyValue(shorthand);
        if (result !== "") {
          bad.push([shorthand, longhand, result]);
        }
      }
    }
  }
  assert_array_equals(bad, []);
}, "Can't serialize shorthand set to initial value when some longhand is missing");
</script>
