Skip to content

Commit

Permalink
Use [ReflectStyle] for basic properties
Browse files Browse the repository at this point in the history
  • Loading branch information
ExE-Boss committed Mar 27, 2021
1 parent 66283a6 commit d8f8f98
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 147 deletions.
213 changes: 84 additions & 129 deletions lib/CSSStyleDeclaration-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@
* https://github.com/NV/CSSOM
********************************************************************/
'use strict';
var CSSOM = require('cssom');
var allProperties = require('./allProperties');
var allExtraProperties = require('./allExtraProperties');
var implementedProperties = require('./implementedProperties');
var { cssPropertyToIDLAttribute } = require('./parsers');
var getBasicPropertyDescriptor = require('./utils/getBasicPropertyDescriptor');
const CSSOM = require('cssom');
const allProperties = require('./allProperties');
const allExtraProperties = require('./allExtraProperties');
const implementedProperties = require('./implementedProperties');
const idlUtils = require('./utils.js');

class CSSStyleDeclarationImpl {
/**
* @constructor
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration
* @see https://drafts.csswg.org/cssom/#cssstyledeclaration
*
* @param {object} globalObject
* @param {*[]} args
Expand All @@ -25,15 +23,67 @@ class CSSStyleDeclarationImpl {
this._globalObject = globalObject;
this._values = Object.create(null);
this._importants = Object.create(null);
this._length = 0;
this._list = [];
this._onChange = onChangeCallback || (() => {});
this.parentRule = null;
}

/**
* @see https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext
*/
get cssText() {
const { _list } = this;
const properties = [];
for (let i = 0; i < _list.length; i++) {
const name = _list[i];
const value = this.getPropertyValue(name);
let priority = this.getPropertyPriority(name);
if (priority !== '') {
priority = ` !${priority}`;
}
properties.push(`${name}: ${value}${priority};`);
}
return properties.join(' ');
}

set cssText(value) {
this._values = Object.create(null);
this._importants = Object.create(null);
this._list = [];
let dummyRule;
try {
dummyRule = CSSOM.parse('#bogus{' + value + '}').cssRules[0].style;
} catch (err) {
// malformed css, just return
return;
}
const rule_length = dummyRule.length;
for (let i = 0; i < rule_length; ++i) {
const name = dummyRule[i];
this.setProperty(
dummyRule[i],
dummyRule.getPropertyValue(name),
dummyRule.getPropertyPriority(name)
);
}
this._onChange(this.cssText);
}

/**
* @see https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-length
*/
get length() {
return this._list.length;
}

set length(value) {
this._list.length = value;
}

/**
*
* @param {string} name
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue
* @see https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertyvalue
* @return {string} the value of the property if it has been explicitly set for this declaration block.
* Returns the empty string if the property has not been set.
*/
Expand All @@ -46,24 +96,29 @@ class CSSStyleDeclarationImpl {
* @param {string} name
* @param {string} value
* @param {string} [priority=""] "important" or ""
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty
* @see https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-setproperty
*/
setProperty(name, value, priority = '') {
if (value === '') {
this.removeProperty(name);
return;
}
var isCustomProperty = name.indexOf('--') === 0;
if (isCustomProperty) {

if (name.startsWith('--')) {
this._setProperty(name, value, priority);
return;
}
var lowercaseName = name.toLowerCase();

const lowercaseName = name.toLowerCase();
if (!allProperties.has(lowercaseName) && !allExtraProperties.has(lowercaseName)) {
return;
}

this[lowercaseName] = value;
if (implementedProperties.has(lowercaseName)) {
this[lowercaseName] = value;
} else {
this._setProperty(lowercaseName, value, priority);
}
this._importants[lowercaseName] = priority;
}

Expand All @@ -84,15 +139,12 @@ class CSSStyleDeclarationImpl {
}
if (this._values[name]) {
// Property already exist. Overwrite it.
var index = Array.prototype.indexOf.call(this, name);
if (index < 0) {
this[this._length] = name;
this._length++;
if (!this._list.includes(name)) {
this._list.push(name);
}
} else {
// New property.
this[this._length] = name;
this._length++;
this._list.push(name);
}
this._values[name] = value;
this._importants[name] = priority;
Expand All @@ -102,7 +154,7 @@ class CSSStyleDeclarationImpl {
/**
*
* @param {string} name
* @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty
* @see https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-removeproperty
* @return {string} the value of the property if it has been explicitly set for this declaration block.
* Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property.
*/
Expand All @@ -111,20 +163,20 @@ class CSSStyleDeclarationImpl {
return '';
}

var prevValue = this._values[name];
const prevValue = this._values[name];
delete this._values[name];
delete this._importants[name];

var index = Array.prototype.indexOf.call(this, name);
const index = this._list.indexOf(name);
if (index < 0) {
return prevValue;
}

// That's what WebKit and Opera do
Array.prototype.splice.call(this, index, 1);
this._list.splice(index, 1);

// That's what Firefox does
//this[index] = ""
//this._list[index] = ''

this._onChange(this.cssText);
return prevValue;
Expand All @@ -133,129 +185,32 @@ class CSSStyleDeclarationImpl {
/**
*
* @param {String} name
* @see https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-getpropertypriority
*/
getPropertyPriority(name) {
return this._importants[name] || '';
}

/**
* http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-item
* @see https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-item
*/
item(index) {
if (index < 0 || index >= this._length) {
const { _list } = this;
if (index < 0 || index >= _list.length) {
return '';
}
return this[index];
return _list[index];
}

[idlUtils.supportsPropertyIndex](index) {
return index >= 0 && index < this._length;
return index >= 0 && index < this._list.length;
}

[idlUtils.supportedPropertyIndices]() {
return Array.prototype.keys.call(this);
return this._list.keys();
}
}

Object.defineProperties(CSSStyleDeclarationImpl.prototype, {
cssText: {
get: function() {
var properties = [];
var i;
var name;
var value;
var priority;
for (i = 0; i < this._length; i++) {
name = this[i];
value = this.getPropertyValue(name);
priority = this.getPropertyPriority(name);
if (priority !== '') {
priority = ` !${priority}`;
}
properties.push(`${name}: ${value}${priority};`);
}
return properties.join(' ');
},
set: function(value) {
var i;
this._values = {};
Array.prototype.splice.call(this, 0, this._length);
this._importants = {};
var dummyRule;
try {
dummyRule = CSSOM.parse('#bogus{' + value + '}').cssRules[0].style;
} catch (err) {
// malformed css, just return
return;
}
var rule_length = dummyRule.length;
var name;
for (i = 0; i < rule_length; ++i) {
name = dummyRule[i];
this.setProperty(
dummyRule[i],
dummyRule.getPropertyValue(name),
dummyRule.getPropertyPriority(name)
);
}
this._onChange(this.cssText);
},
enumerable: true,
configurable: true,
},
length: {
get: function() {
return this._length;
},
/**
* This deletes indices if the new length is less then the current
* length. If the new length is more, it does nothing, the new indices
* will be undefined until set.
**/
set: function(value) {
var i;
for (i = value; i < this._length; i++) {
delete this[i];
}
this._length = value;
},
enumerable: true,
configurable: true,
},
});

require('./properties')(CSSStyleDeclarationImpl.prototype);

// TODO: Consider using `[Reflect]` for basic properties
allProperties.forEach(function(property) {
if (!implementedProperties.has(property)) {
var declaration = getBasicPropertyDescriptor(property);
Object.defineProperty(CSSStyleDeclarationImpl.prototype, property, declaration);
Object.defineProperty(
CSSStyleDeclarationImpl.prototype,
cssPropertyToIDLAttribute(property),
declaration
);
}
});

allExtraProperties.forEach(function(property) {
if (!implementedProperties.has(property)) {
var declaration = getBasicPropertyDescriptor(property);
Object.defineProperty(CSSStyleDeclarationImpl.prototype, property, declaration);
Object.defineProperty(
CSSStyleDeclarationImpl.prototype,
cssPropertyToIDLAttribute(property),
declaration
);
if (property.startsWith('-webkit-')) {
Object.defineProperty(
CSSStyleDeclarationImpl.prototype,
cssPropertyToIDLAttribute(property, /* lowercaseFirst = */ true),
declaration
);
}
}
});

exports.implementation = CSSStyleDeclarationImpl;
14 changes: 0 additions & 14 deletions lib/utils/getBasicPropertyDescriptor.js

This file was deleted.

30 changes: 27 additions & 3 deletions scripts/convert-idl.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const Transformer = require('webidl2js');

const allProperties = require('../lib/allProperties.js');
const allExtraProperties = require('../lib/allExtraProperties.js');
const implementedProperties = require('../lib/implementedProperties.js');
const { cssPropertyToIDLAttribute } = require('../lib/parsers.js');

const srcDir = path.resolve(__dirname, '../src');
Expand Down Expand Up @@ -39,7 +40,11 @@ partial interface CSSStyleDeclaration {

for (const property of propertyNames) {
const camelCasedAttribute = cssPropertyToIDLAttribute(property);
genIDL.write(` [CEReactions] attribute [LegacyNullToEmptyString] CSSOMString _${camelCasedAttribute};
let extAttrs = 'CEReactions';
if (!implementedProperties.has(property)) {
extAttrs += `,ReflectStyle="${property}"`;
}
genIDL.write(` [${extAttrs}] attribute [LegacyNullToEmptyString] CSSOMString _${camelCasedAttribute};
`);
}

Expand All @@ -50,7 +55,11 @@ partial interface CSSStyleDeclaration {
for (const property of propertyNames) {
if (!property.startsWith('-webkit-')) continue;
const webkitCasedAttribute = cssPropertyToIDLAttribute(property, /* lowercaseFirst = */ true);
genIDL.write(` [CEReactions] attribute [LegacyNullToEmptyString] CSSOMString _${webkitCasedAttribute};
let extAttrs = 'CEReactions';
if (!implementedProperties.has(property)) {
extAttrs += `,ReflectStyle="${property}"`;
}
genIDL.write(` [${extAttrs}] attribute [LegacyNullToEmptyString] CSSOMString _${webkitCasedAttribute};
`);
}

Expand All @@ -60,7 +69,11 @@ partial interface CSSStyleDeclaration {

for (const property of propertyNames) {
if (!property.includes('-')) continue;
genIDL.write(` [CEReactions] attribute [LegacyNullToEmptyString] CSSOMString ${property};
let extAttrs = 'CEReactions';
if (!implementedProperties.has(property)) {
extAttrs += `,ReflectStyle="${property}"`;
}
genIDL.write(` [${extAttrs}] attribute [LegacyNullToEmptyString] CSSOMString ${property};
`);
}

Expand All @@ -77,6 +90,17 @@ partial interface CSSStyleDeclaration {
const transformer = new Transformer({
implSuffix: '-impl',
// TODO: Add support for `[CEReactions]`
processReflect(idl, implName) {
const reflectStyle = idl.extAttrs.find(extAttr => extAttr.name === 'ReflectStyle');
if (!reflectStyle || !reflectStyle.rhs || reflectStyle.rhs.type !== 'string') {
throw new Error(`Internal error: Invalid [ReflectStyle] for attribute ${idl.name}`);
}

return {
get: `return ${implName}.getPropertyValue(${reflectStyle.rhs.value});`,
set: `${implName}._setProperty(${reflectStyle.rhs.value}, V);`,
};
},
});

transformer.addSource(srcDir, implDir);
Expand Down
Loading

0 comments on commit d8f8f98

Please sign in to comment.