A large part of the current CSS
Object Model sucks. More specifically, the CSSValue,
CSSPrimitiveValue, CSSValueList, RGBColor, Rect and Counter interfaces are
so poorly designed they're not implemented. I just tried to implement them
for a project of mine and I must say the model is so weak and incoherent
it cannot be implemented as is.
I have then tried to refine what's in the 2000-nov-13 spec of DOM Level 2
Style to reach something workable. I am NOT saying this has to be done or
implemented. Call it a mental exercise I did just for fun, w/o caring
about performance.
Let's first look at what's wrong:
Interface CSSValue
interface CSSValue {
// UnitTypes
const unsigned short CSS_INHERIT = 0;
const unsigned short CSS_PRIMITIVE_VALUE = 1;
const unsigned short CSS_VALUE_LIST = 2;
const unsigned short CSS_CUSTOM = 3;
attribute DOMString cssText;
// raises(DOMException) on setting
readonly attribute unsigned short cssValueType;
};
"inherit" is here considered as a special identifier despite of the fact
a CSSPrimitiveValue can be a CSS_IDENT. There is no UnitType for
"initial". A CSS_CUSTOM is, according to the spec, a "custom value"; but a
custom value still has to be valid per CSS syntax so it should be
representable with CSS_VALUE_LISTs and CSS_VALUEs.
Interface CSSValueList
interface CSSValueList : CSSValue {
readonly attribute unsigned long length;
CSSValue item(in unsigned long index);
};
All in all, this one is simple and should be quite ok. But one thing is
missing: a property can accept a comma-separated list of
whitespace-separated values. The current CSSValueList cannot express if
the serialization of a CSSValueList should be whitespace- or
comma-separated.
Interface CSSPrimitiveValue
interface CSSPrimitiveValue : CSSValue {
// UnitTypes
const unsigned short CSS_UNKNOWN = 0;
const unsigned short CSS_NUMBER = 1;
const unsigned short CSS_PERCENTAGE = 2;
const unsigned short CSS_EMS = 3;
const unsigned short CSS_EXS = 4;
const unsigned short CSS_PX = 5;
const unsigned short CSS_CM = 6;
const unsigned short CSS_MM = 7;
const unsigned short CSS_IN = 8;
const unsigned short CSS_PT = 9;
const unsigned short CSS_PC = 10;
const unsigned short CSS_DEG = 11;
const unsigned short CSS_RAD = 12;
const unsigned short CSS_GRAD = 13;
const unsigned short CSS_MS = 14;
const unsigned short CSS_S = 15;
const unsigned short CSS_HZ = 16;
const unsigned short CSS_KHZ = 17;
const unsigned short CSS_DIMENSION = 18;
const unsigned short CSS_STRING = 19;
const unsigned short CSS_URI = 20;
const unsigned short CSS_IDENT = 21;
const unsigned short CSS_ATTR = 22;
const unsigned short CSS_COUNTER = 23;
const unsigned short CSS_RECT = 24;
const unsigned short CSS_RGBCOLOR = 25;
readonly attribute unsigned short primitiveType;
void setFloatValue(in unsigned short unitType,
in float floatValue)
raises(DOMException);
float getFloatValue(in unsigned short unitType)
raises(DOMException);
void setStringValue(in unsigned short stringType,
in DOMString stringValue)
raises(DOMException);
DOMString getStringValue()
raises(DOMException);
Counter getCounterValue()
raises(DOMException);
Rect getRectValue()
raises(DOMException);
RGBColor getRGBColorValue()
raises(DOMException);
};
This is so completely crazy I don't know where to start...
- CSS_UNKNOWN is supposed to represent a "value that is not a recognized
CSS2 value". Then it should be thrown away by the parser as invalid and
never reach the OM, right?
- the list of units is long and not easily extensible
- attr(), counter(), counters(), rect() and the more recent gradients or
var() calls are all functions; adding a new setter and a new getter for
each new type is overkill
- attr() was extended by recent specs and can now take more than one
argument. The above does not allow to individually modify those
arguments.
- "initial" and "inherit" are, as I already said above, covered by both
CSSValue and CSS_IDENT here
- let's suppose we have a CSSValue that is a CSSPrimitiveValue. Setting
its cssText to "10px 10px" will then trigger an exception since a
CSSPrimitiveValue cannot transmute magically into a CSSValueList...
- I love how the spec prose says setStringValue() has "No Parameters"...
Interface Rect
interface Rect {
readonly attribute CSSPrimitiveValue top;
readonly attribute CSSPrimitiveValue right;
readonly attribute CSSPrimitiveValue bottom;
readonly attribute CSSPrimitiveValue left;
};
This looks and smells like a CSSValueList far too much.
Interface RGBColor
interface RGBColor {
readonly attribute CSSPrimitiveValue red;
readonly attribute CSSPrimitiveValue green;
readonly attribute CSSPrimitiveValue blue;
};
This cannot represent rgba(), hsl() and hsla() colors. We also have to
use three CSSPrimitiveValue for the three color components because they
can be a percentage or an integer...
Interface Counter
interface Counter {
readonly attribute DOMString identifier;
readonly attribute DOMString listStyle;
readonly attribute DOMString separator;
};
Again, something is missing here: nothing says if it's supposed to be a
counter() or a counters() value. And no, the separator could not do the
trick since it can be the empty string.
Requirements
To have a better OM for Values, i.e. an extensible OM that allows an
application to deal with parsed values of all kinds, we need to change of
perspective. First, the list of reserved idents, the list of units and the
list of functions are not extensible. Secondly, we have cast issues
between PrimitiveValues and ValueLists and we need a single interface. We
can deal with all the issues with a single CSSValue interface:
New interface CSSValue
interface CSSValue {
// ValueTypes
const unsigned short CSS_SYMBOL = 0;
const unsigned short CSS_NUMBER = 1;
const unsigned short CSS_UNIT = 2;
const unsigned short CSS_STRING = 3;
const unsigned short CSS_URI = 4;
const unsigned short CSS_IDENT = 5;
const unsigned short CSS_VALUE_LIST = 6;
readonly attribute unsigned short type;
attribute boolean commaSeparated;
readonly attribute unsigned long length;
CSSValue item(in unsigned long index);
raises(DOMException);
void setFloatValue(in float floatValue)
raises(DOMException);
float getFloatValue()
raises(DOMException);
void setStringValue(in DOMString stringValue)
raises(DOMException);
DOMString getStringValue()
raises(DOMException);
};
Definition group ValueTypes
An integer indicating the type of the Value
CSS_SYMBOL
- The value is a single character than cannot be interpreted otherwise.
For instance the
/
character in the font shorthand
property. The value can be obtained by the getStringValue()
and set by the setStringValue()
method.
CSS_NUMBER
- The value is a simple number. The value can be obtained by using the
getFloatValue()
method and set through by setFloatValue()
method.
CSS_UNIT
- The value is a number followed by a unit. The number part of the value
can be obtained by using the
getFloatValue()
method and
set through by setFloatValue()
method. The unit part of
the value can be obtained by using the getUnit()
method
and set through by setUnit()
method
CSS_STRING
- The value is a string. The value can be obtained by the
getStringValue()
and set by the setStringValue()
method.
CSS_URI
- The value is a URI. The parameter of the
url()
function
can be obtained by the getStringValue()
and set by the setStringValue()
method.
CSS_IDENT
- The value is a CSS identifier. The value can be obtained by the
getStringValue()
and set by the setStringValue()
method.
CSS_VALUE_LIST
- The value is a list of values or a function. It is a function if the
getStringValue()
method does not reply the empty string. The list of values is
whitespace-separated if the commaSeparated
attribute is
false and comma-separated otherwise.
Attributes
type
of type unsigned short
, readonly
- The type of the value as defined by the constants specified above.
commaSeparated
of type boolean
- The separation type of the list of values. Meaningful only if the
type
attribute is CSS_VALUE_LIST
. The list is
whitespace-separated if the attribute is false and comma-separated
otherwise.
length
of type unsigned long
, readonly
- The number of
CSSValue
in the list. The range of valid
values of the indices is 0
to length-1
inclusive.
-
- Exceptions
- INVALID_ACCESS_ERR: Raised if the CSS value is a not a
CSS_VALUE_LIST
.
Methods
getFloatValue
- Retrieves the value of a
CSS_NUMBER
or the number part
of the value of a CSS_UNIT
. If this CSS value is not a CSS_NUMBER
or a CSS_UNIT
, a DOMException
is raised.
-
- Return Value
float
The float value of this CSS_NUMBER
or CSS_UNIT
- Exceptions
- INVALID_ACCESS_ERR: Raised if the CSS value isn't a
CSS_NUMBER
nor a CSS_UNIT
.
getStringValue
- For a
CSS_SYMBOL
, retrieves the single character used as
a symbol.
- For a
CSS_STRING
, retrieves the string. Enclosing quotes
or double-quotes are NOT included.
- For a
CSS_UNIT
, retrieves the unit of the value.
- For a
CSS_URI
, retrieves the argument of the url(...)
notation. Enclosing quotes or double-quotes are NOT includedt.
- For a
CSS_IDENT
, retrieves the identifier.
- For a
CSS_VALUE_LIST
and if that list of values is
passed as the parameters of a function, retrieves the function name.
Retrieves the empty string otherwise.
- For a
CSS_NUMBER
and CSS_UNIT
, a DOMException
is raised.
-
- No Parameters
- Return Value
DOMString
The float value of this CSS_NUMBER
or CSS_UNIT
- Exceptions
- INVALID_ACCESS_ERR: Raised if the CSS value is a
CSS_NUMBER
or a CSS_UNIT
.
item
- For a
CSS_VALUE_LIST,
Used to retrieve a CSSValue
by ordinal index. The order in this collection represents the order of
thevalues in the CSS style property. If index is greater than or equal
to the number of values in the list, this returnsnull
.
- For all other value types, a
DOMException
is raised.
-
- Parameter
index
of type unsigned long
: index into
the collection.
- Return value
CSSValue
The CSSValue at the index position in the CSSValueList
,
or null
if that is not a valid index.
- Exceptions
- INVALID_ACCESS_ERR: Raised if the CSS value is a not a
CSS_VALUE_LIST
.
setFloatValue
- Sets the value of a
CSS_NUMBER
or the number part of the
value of a CSS_UNIT
. If this CSS value is not a CSS_NUMBER
or a CSS_UNIT
, a DOMException
is raised.
-
- Parameter
floatValue
of type float
;
- No Return Value
- Exceptions
- INVALID_ACCESS_ERR: Raised if the CSS value isn't a
CSS_NUMBER
nor a CSS_UNIT
or if the attached property doesn't
support the float value or the unit type.
- NO_MODIFICATION_ALLOWED_ERR: Raised if this property is readonly.
setStringValue
- For a
CSS_SYMBOL
, sets the single character used as a
symbol.
- For a
CSS_STRING
, sets the string.
- For a
CSS_UNIT
, sets the unit of the value.
- For a
CSS_URI
, sets the argument of the url(...)
notation.
- For a
CSS_IDENT
, sets the identifier.
- For a
CSS_VALUE_LIST
and if the parameter is not the
empty string, make the list of values become a function and sets the
function name. Make the list become a plain list of values if the
parameter is the empty string.
- For a
CSS_NUMBER
and CSS_UNIT
, a DOMException
is raised.
-
- Parameter
stringValue
of type DOMString
- No Return Value
- Exceptions
- INVALID_ACCESS_ERR: Raised if the CSS value is a
CSS_NUMBER
or a CSS_UNIT
, if the type of the value is CSS_SYMBOL
and the string can be parsed as an other type of value, if the type
of the value is CSS_UNIT
and the string is not a valid
CSS unit, if the type of the value is CSS_URI
and the
string is not a valid URI, if the type of the value is CSS_IDENT
and the string is not a valid CSS identifier, if the type of the
value is CSS_VALUE_LIST
and the string is not a valid
CSS identifier or the empty string.
- NO_MODIFICATION_ALLOWED_ERR: Raised if this property is readonly.
Conclusion
The above should be enough to describe any CSS value, specified or
computed. The model will become a bit complex for complex values but it
ensures any web application can have access to parsed values, deal with
their types and modify them. Let's take an example:
background-image: linear-gradient(to bottom, yellow 0%, blue 100%), url(foo.png);
This will result in the following OM (click on the image to enlarge it):
Again, I'm not saying the above is the thing to do or implement. It can
certainly be improved, for instance for colors. A totally different
perspective is also perfectly possible. I am only saying that making a
better CSS OM allowing a full representation of parsed values in
stylesheets and computed values is feasible. I hope the CSS OM will offer
such power in the future.
UPDATE: the new CSSValue interface above lacks one thing, the ubiquitous
cssText for parsing and serialization. Sorry for that.