HybridOS Specification 01-B Topic: How to print log in WebCore Author: David Geng Category: App Development Date: July 2019 Status: Proposal
Copyright Notice
Copyright (C) 2018, 2019 [FMSoft Technologies] All Rights Reserved.
Introduction
There are three types of CSS properties:
- The CSS property value is user defined, with type unit, such as the length, angle;
- The CSS property value is pre-defined value, such as direction, display;
- Shorthands, such as boder-block.
This article will describe how to add new CSS properties, according to their types respectively.
Essential of work flow
In css directory, you will find some special files as blow:
html.css: defines the default CSS value of every HTML tag;
svg.css: defines the default value of svg properties;
CSSProperties.json: describes the css properies, define initialization and setter, getter function;
CSSValueKeywords.in: all pre-defined value of CSS type 2;
makeprop.pl: according to CSSProperties.json, creates StyleBuilder.cpp;
makevalues.pl: according to CSSValueKeywords.in, creates CSSValueKeywords.cpp.
So the work flow of add a new CSS property is about:
1. modify html.css if necessary;
2. modify CSSProperties.json and CSSValueKeywords.in, and the new properties and pre-defined value;
3. execute makeprop.pl and makevalues.pl, to create StyleBuilder.cpp, CSSPropertyNames.cpp and CSSValueKeywords.cpp;
4. modify css/CSSPropertyParser.cpp, add the interpretation code;
5. modify rendering/style/RenderStyle.h, add initialization, setter, getter functions;
Add the property with user defined value
As an example, now try to add hi-value property.
1. It's not necessary to modify html.css;
2. Add the code to CSSProperties.json:
"-hi-value": {
"inherited": false,
"codegen-properties": {
"initial": "initialFloatValue",
"converter": "PercentOrNumber"
},
"specification": {
"category": "css-22",
"url": "https://www.w3.org/TR/CSS22/visudet.html#the-width-property"
}
},
In above code:
inherited: the value is not inheried from parent node;
initial: the name of initialization function; Without this item, the compiler will use default function.
setter: the name of setter function. Without this item, the compiler will use default function.
getter: the name of getter function. Without this item, the compiler will use default function.
converter: the name of convertion function. Withtout this item, convertion function does not exist.
There are lot of description, read the instructions in CSSProperties.json carefully.
The default function name is blow, if the compiler can not find corresponding item:
initial: initialHiValue();
setter: hiValue();
getter: setHiVAlue();
converter: convertPercentOrNumber();
In this case, the initialization function is named initialFloatValue, so lots of properties with same
value type, can share one initializaiton function, such as width, hight, and so on.
3. Execute makeprop.pl:
After the exection, two files are created, StyleBuilder.cpp and CSSPropertyNames.cpp.
First, let's glance at StyleBuilder.cpp. In this file, it provides initializaiton, setter, getter
functions, you can invoke these functions in parsing processing. The final implementation is in
render layer, rendering/style/RenderStyle.h.
Second, CSSPropertyNames.cpp, provides hash algorithm for identifying every properties by name, and
gives an ID number to the properties.
4. Modify rendering/style/RenderStyle.h manually:
static float initialFloatValue() { return 0.0f; }
float hiValue() const { return m_boxData->handValue(); }
void setHiValue(float && value) { SET_VAR(m_boxData, m_handValue, WTFMove(value)); }
Of course, the property value is stored in another structure or objects, you should modified those
files, such as StyleBoxData.cpp, in which m_handValue is stored.
5. Modify css/parser/CSSPropertyParser.cpp, add the code for dealing with the property values.
With the initialization, setter, getter, hash algorithm, the framework identifies the property name
for us automatically. In this step, the css parser is just like a text processing software. But
next, the parser can do nothing for us, because of semantic analysis. So we have to do it by ourselves.
The framework will invoke CSSPropertyParser::parseSingleValue() function to get property value, and
return a CSSPrimitiveValue object. In CSSPrimitiveValue object, the value, type, unit are all inclusive.
6. Modify css/StyleBuilderConverter.h to implement convertion function. What is convertion function?
Some CSS properties can have value, which is not numeric, but text. For example, width:fill-available.
When in render step, the browser needs only numeric value. So convert function can do it for you.
7. It is OK, clean and re-compile the whole projects.
8. Get the property value in RenderObject, you could use: style().getter().
Add the property with pre-define value
In this case, try to add -hi-hand-type property.
1. It's not necessary to modify html.css;
2. Add the code to CSSProperties.json:
"-hi-hand-type": {
"inherited": false,
"values": [
"pointer",
"stripe"
],
"specification": {
"category": "css-22",
"url": "https://www.w3.org/TR/CSS22/text.html#propdef-white-space"
}
},
In above code, there is no initialization, setter, getter, converter functions, but the key is
"values". You should enumerate all possible values of the property.
3. Execute makeprop.pl:
After the exection, two files are created, StyleBuilder.cpp and CSSPropertyNames.cpp. It is the
same as the user defined value. In this step, framework gets property ID.
4. Modify rendering/style/RenderStyle.h manually:
static HiHandType initialHiHandType() { return HiHandType::Needle; }
HiHandType hiHandType() const { return static_cast<HiHandType>(m_inheritedFlags.hiHandType); }
void setHiHandType(HiHandType v) { m_inheritedFlags.hiHandType = static_cast<unsigned>(v); }
5. Modify rendering/style/RenderStyleConstants.h, add a new enum type for possible value. The
initialization, setter, getter functions will use this type.
enum class HiHandType : uint8_t {
Needle,
Stripe
};
6. Execute makevalues.pl:
The result is the creation of CSSValueKeyWords.cpp. In this file, it provides hash algorithm
for identifying every values by name, and gives an ID number to the value. In this step,
framework gets value ID.
7. Modify isKeywordPropertyID() and isValidKeywordPropertyAndValue() in CSSParserFastPaths.cpp,
the framework will use these functions when parsing:
In isValidKeywordPropertyAndValue():
case CSSPropertyHiHandType:
return valueID == CSSValueNeedle || valueID == CSSValueStripe;
In isKeywordPropertyID()
case CSSPropertyHiHandType:
return true;
8. Modify valueForPropertyInStyle() and computedProperties[] arrary in CSSComputedStyleDeclaration.cpp.
After parsing, the framework will return a CSSPrimitiveValue object:
In valueForPropertyInStyle()
case CSSPropertyHiHandType:
return cssValuePool.createValue(style.hiHandType());
In computedProperties[] array, add property name ID at the tail:
CSSPropertyHiHandType,
9. With propery name ID and value ID, as a text processing software now, the framework can parse the
css code correctly. The next step, is representation the value for render layer.
10. Modify css/CSSPrimitiveValueMappings.h, define operator HiHandType():
template<> inline CSSPrimitiveValue::CSSPrimitiveValue(HiHandType e)
: CSSValue(PrimitiveClass)
{
m_primitiveUnitType = CSS_VALUE_ID;
switch (e) {
case HiHandType::Needle:
m_value.valueID = CSSValueNeedle;
break;
case HiHandType::Stripe:
m_value.valueID = CSSValueStripe;
break;
}
}
template<> inline CSSPrimitiveValue::operator HiHandType() const
{
ASSERT(isValueID());
switch (m_value.valueID) {
case CSSValueNeedle:
return HiHandType::Needle;
case CSSValueStripe:
return HiHandType::Stripe;
default:
break;
}
ASSERT_NOT_REACHED();
return HiHandType::Needle;
}
11. It is OK, clean and re-compile the whole projects.
12. Get the property value in RenderObject, you could use: style().getter().
ShortHand
As an example, now try to add hi-hand-center property.
1. At first, as described abvoe, you should add new properties in shorthand respectively.
2. It's not necessary to modify html.css;
3. Add the code to CSSProperties.json:
"-hi-hand-center": {
"codegen-properties": {
"longhands": [
"-hi-hand-center-x",
"-hi-hand-center-y"
]
},
"specification": {
"category": "css-transitions",
"url": "https://www.w3.org/TR/css3-transitions/#transition-shorthand-property"
}
},
In above code, there is no initialization, setter, getter, converter functions, but you
have to enumerate all properties in item "longhands". Now you know, -hi-hjand-center can
include -hi-hand-center-x and -hi-hand-center-y properties.
4. Execute makeprop.pl:
For shothand, after the exection, two files are created, StylePropertyShorthandFunctions.h
and StylePropertyShorthandFunctions.cpp. In these two files, provides the methods to create
StylePropertyShorthand object. All possible propertyies are in hiHandCenterProperties array.
5. Modify css/parser/CSSPropertyParser.cpp, add parsing code for the shorthand in parseShorthand()
function.
case CSSPropertyHiHandCenter:
return consumeCoordinate(hiHandCenterShorthand(), important);
You should program your own parsing code for the shorthand.
6. It is OK, clean and re-compile the whole projects.
Transition with new properties
After you add new properties, the framework can parse new properites correctly, and transition
property with new properties as well. But when executes transition, one thing is left to be done.
The framework does not know how to change the value gradually, because the framework can not
understand what is the new properties. It can be area, length, color, angle, or something else.
So you should tell the framework, how to calculate the property value when transition is executed.
The code is in page/animation/CSSPropertyAnimation.cpp. Now we use the new property hi-value
as an example, and modify the file.
1. Create a new class derived from PropertyWrapperGetter:
class AnglePropertyWrapper : public PropertyWrapperGetter<float>
{
WTF_MAKE_FAST_ALLOCATED;
public:
AnglePropertyWrapper(CSSPropertyID prop, float (RenderStyle::*getter)() const, void (RenderStyle::*setter)(float &&))
: PropertyWrapperGetter<float >(prop, getter)
, m_setter(setter)
{
}
void blend(const CSSPropertyBlendingClient* anim, RenderStyle* dst, const RenderStyle* a, const RenderStyle* b, double progress) const override
{
(dst->*m_setter)(blendFunc(anim, (a->*PropertyWrapperGetter<float>::m_getter)(), (b->*PropertyWrapperGetter<float>::m_getter)(), progress));
}
protected:
void (RenderStyle::*m_setter)(float&&);
};
The framework provides blendFunc for common variable types, such as float, Length, color and
so on. If your property is a new type, you have to write blendFunc by yourselves. In most
cases, blendFunc is enough. This function can invoke different calculation according to your
property value type.
2. Add the new class to animatableLonghandPropertyWrappers array, the framework will look
up this array, and get the corresponding calculation, according to the property name ID:
AnimationPropertyWrapperBase* animatableLonghandPropertyWrappers[] = {
new AnglePropertyWrapper(CSSPropertyHiValue, &RenderStyle::hiValue, &RenderStyle::setHiValue),
3. It is OK, clean and re-compile the whole projects.