From 5e53cefedf0aefee6d2c612f35fda13e1009c407 Mon Sep 17 00:00:00 2001 From: "attinasi%netscape.com" Date: Tue, 7 May 2002 22:46:14 +0000 Subject: [PATCH] Documentation only: not part of any build. Tech Note for how to add a new style property. git-svn-id: svn://10.0.0.236/trunk@120999 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/layout/doc/adding-style-props.html | 218 +++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 mozilla/layout/doc/adding-style-props.html diff --git a/mozilla/layout/doc/adding-style-props.html b/mozilla/layout/doc/adding-style-props.html new file mode 100644 index 00000000000..6488685dcdf --- /dev/null +++ b/mozilla/layout/doc/adding-style-props.html @@ -0,0 +1,218 @@ + + + Adding a new style property - layout cookbook + + +

Adding a new style property

+
+
+

Document history:

+ +
+
+

+

Overview

+When a new style property is needed there are many places in the code that +need to be updated. This document outlines the proceedure used to add a new +property, in this case the property is a proprietary one called '-moz-force-broken-image-icons' +and is used as a way for a stylesheet to force broken image icons to be displayed. +This is all being done in the context of +bug 58646 +.
+

Analysis

+Up front you have to decide some things about the new property:
+
+ Questions:
+
    +
  1. Is the property proprietary or specified by the CSS standard?
    +
  2. +
  3. Is the property inherited?
  4. +
  5. What types of values can the property have?
    +
  6. +
  7. Does it logically fit with other existing properties?
  8. +
  9. What is the impact to the layout of a page if that property changes?
  10. +
  11. What do you want to name it?
  12. +
+ Answers:
+
    +
  1. In our specific case, we want a property that is used internally, +so it is a proprietary property.  
  2. +
  3. The property is to be used for images, which are leaf elements, so +there is no need to inherit it. 
  4. +
  5. The property is used simply to force a broken image to be represented +by an icon, so it only supports the values '0' and '1' as numerics. 
  6. +
  7. It is hard to see how this property fits logically in with other +properties, but if we stretch our imaginations we could say that it is a +sort of UI property. 
  8. +
  9. If this property changes, the image frame has to be recreated. This +is because the decision about whether to display the icon or not will impact +the decision to replace the image frame with an inline text frame for the +ALT text, so if the ALT text inline is already made, there is no image frame +left around to reflow or otherwise modify. 
  10. +
  11. Finally, the name will be '-moz-force-broken-image-icons' - that +should be pretty self-describing (by convention we start proprietary property +names with '-moz-').
  12. +
+

Implementation

+There are several places that need to be educated about a new style property. +The are:
+ +

CSS Property Name / Constants / Hints

+First, add the new name to the property list in +nsCSSPropList.h +  Insert the property in the list alphabetically, using the existing +property names as a template. The format of the entry you will create is: +
CSS_PROP(-moz-force-broken-image-iconst, force_broken_image_icons, FRAMECHANGE) // bug 58646
+The first value is the formal property name, in other words the property +name as it is seen by the CSS parser.
+The second value is the name of the property as it will appear internally.
+The last value indicates what must change when the value of the property +changes. It should be one of ( +VISUAL, AURAL, REFLOW, CONTENT, FRAMECHANGE +).
+
+If you need to introduce new constants for the values of the property, they +must be added to +nsStyleConsts.h + and to the appropriate keyword tables in +nsCSSProps.cpp + (note: this cookbook does not do this since the new property does not require +any new keywords for the property values). +

CSS Declaration

+Changes will need to be made to the structs and classes defined in +nsCSSDeclaration.h +and +nsCSSDeclaration.cpp +
+
+First, find the declaration of the struct that will hold the new property +value (in the header file). For this example it is the struct nsCSSUserInterface +. Modify the struct declaration to include a new data member for the new +property, of the type CSSValue. Next, open the implementation file (the cpp) +and modify the struct's constructors.
+
+Next, the +AppendValue + method must be updated to support your new property. The CSSParser will +call this to build up a declaration. Find the portion of that method that +deals with the other properties in the struct that you are adding your property +to (or create a new section if you are creating a new style struct). For +this example we will find the 'UserInterface' section and add our new property + there +.
+
    // nsCSSUserInterface
case eCSSProperty_user_input:
case eCSSProperty_user_modify:
case eCSSProperty_user_select:
case eCSSProperty_key_equivalent:
case eCSSProperty_user_focus:
case eCSSProperty_resizer:
case eCSSProperty_cursor:
case eCSSProperty_force_broken_image_icons: {
CSS_ENSURE(UserInterface) {
switch (aProperty) {
case eCSSProperty_user_input: theUserInterface->mUserInput = aValue; break;
case eCSSProperty_user_modify: theUserInterface->mUserModify = aValue; break;
case eCSSProperty_user_select: theUserInterface->mUserSelect = aValue; break;
case eCSSProperty_key_equivalent:
CSS_ENSURE_DATA(theUserInterface->mKeyEquivalent, nsCSSValueList) {
theUserInterface->mKeyEquivalent->mValue = aValue;
CSS_IF_DELETE(theUserInterface->mKeyEquivalent->mNext);
}
break;
case eCSSProperty_user_focus: theUserInterface->mUserFocus = aValue; break;
case eCSSProperty_resizer: theUserInterface->mResizer = aValue; break;
case eCSSProperty_cursor:
CSS_ENSURE_DATA(theUserInterface->mCursor, nsCSSValueList) {
theUserInterface->mCursor->mValue = aValue;
CSS_IF_DELETE(theUserInterface->mCursor->mNext);
}
break;
case eCSSProperty_force_broken_image_icons: theUserInterface->mForceBrokenImageIcon = aValue; break;

CSS_BOGUS_DEFAULT; // make compiler happy
}
}
break;
}

+The GetValue method must be similarly modified +:
+
    // nsCSSUserInterface
case eCSSProperty_user_input:
case eCSSProperty_user_modify:
case eCSSProperty_user_select:
case eCSSProperty_key_equivalent:
case eCSSProperty_user_focus:
case eCSSProperty_resizer:
case eCSSProperty_cursor:
case eCSSProperty_force_broken_image_icons: {
CSS_VARONSTACK_GET(UserInterface);
if (nsnull != theUserInterface) {
switch (aProperty) {
case eCSSProperty_user_input: aValue = theUserInterface->mUserInput; break;
case eCSSProperty_user_modify: aValue = theUserInterface->mUserModify; break;
case eCSSProperty_user_select: aValue = theUserInterface->mUserSelect; break;
case eCSSProperty_key_equivalent:
if (nsnull != theUserInterface->mKeyEquivalent) {
aValue = theUserInterface->mKeyEquivalent->mValue;
}
break;
case eCSSProperty_user_focus: aValue = theUserInterface->mUserFocus; break;
case eCSSProperty_resizer: aValue = theUserInterface->mResizer; break;
case eCSSProperty_cursor:
if (nsnull != theUserInterface->mCursor) {
aValue = theUserInterface->mCursor->mValue;
}
break;
case eCSSProperty_force_broken_image_icons: aValue = theUserInterface->mForceBrokenImageIcons; break;

CSS_BOGUS_DEFAULT; // make compiler happy
}
}
else {
aValue.Reset();
}
break;
}

+Finally modify +the 'List' method to output the property value.
+
void nsCSSUserInterface::List(FILE* out, PRInt32 aIndent) const
{
for (PRInt32 index = aIndent; --index >= 0; ) fputs(" ", out);

nsAutoString buffer;

mUserInput.AppendToString(buffer, eCSSProperty_user_input);
mUserModify.AppendToString(buffer, eCSSProperty_user_modify);
mUserSelect.AppendToString(buffer, eCSSProperty_user_select);
nsCSSValueList* keyEquiv = mKeyEquivalent;
while (nsnull != keyEquiv) {
keyEquiv->mValue.AppendToString(buffer, eCSSProperty_key_equivalent);
keyEquiv= keyEquiv->mNext;
}
mUserFocus.AppendToString(buffer, eCSSProperty_user_focus);
mResizer.AppendToString(buffer, eCSSProperty_resizer);

nsCSSValueList* cursor = mCursor;
while (nsnull != cursor) {
cursor->mValue.AppendToString(buffer, eCSSProperty_cursor);
cursor = cursor->mNext;
}

mForceBrokenImageIcon.AppendToString(buffer,eCSSProperty_force_broken_image_icons);

fputs(NS_LossyConvertUCS2toASCII(buffer).get(), out);
}

+ +

CSS Parser

+Next, the CSSParser must be educated about this new property so that it can +read in the formal declarations and build up the internal declarations that +will be used to build the rules. If you are adding a simple property that +takes a single value, you simply add your new property to the ParseSingleProperty +method. If a more complex parsing is required you will have to write a new +method to handle it, modeling it off of one of the existing parsing helper +methods (see +ParseBackground +, for and example). We are just adding a simple single-value property here.
+
+Open nsCSSParser.cpp and look for the method +ParseSingleProperty +. This method is responsible for calling the relevant helper routine to parse +the value(s). Find an existing property that is similar to the property you +are adding. For our example we are adding a property that takes a numeric +value so we will model it after the 'height' property and call ParsePositiveVariant. +Add a new case for the new property and call the appropriate parser-helper +and make a call to ParseVariant passing the +variant flag + that makes sense for your property. In our case
+
+
  case eCSSProperty_force_broken_image_icons:
+
    return ParsePositiveVariant(aErrorCode, aValue, VARIANT_INTEGER, nsnull);
+This will parse the value as a positive integer value, which is what we want.
+
+

Style Context

+Having implemented support for the new property in the CSS Parser and CSS +Declaration classes in the content module, it is now time to provide support +for the new property in layout. The Style Context must be given a new data +member corresponding to the declaration's new data member, so the computed +value can be held for the layout objects to use.
+
+First look into +nsStyleStruct.h + to see the existing style strucs. Find the one that you want to store the +data on. In this example, we want to put it on the nsStyleUserInterface struct, +however there is also a class nsStyleUIReset that holds the non-inherited +values, so we will use that one (remember, our property is not inherited). +Add a data member + to hold the value: +
struct nsStyleUIReset: public nsStyleStruct {
nsStyleUIReset(void);
nsStyleUIReset(const nsStyleUIReset& aOther);
~nsStyleUIReset(void);

NS_DEFINE_STATIC_STYLESTRUCTID_ACCESSOR(eStyleStruct_UIReset)

void* operator new(size_t sz, nsIPresContext* aContext) {
void* result = nsnull;
aContext->AllocateFromShell(sz, &result);
return result;
}
void Destroy(nsIPresContext* aContext) {
this->~nsStyleUIReset();
aContext->FreeToShell(sizeof(nsStyleUIReset), this);
};

PRInt32 CalcDifference(const nsStyleUIReset& aOther) const;

PRUint8 mUserSelect; // [reset] (selection-style)
PRUnichar mKeyEquivalent; // [reset] XXX what type should this be?
PRUint8 mResizer; // [reset]
PRUint8 mForceBrokenImageIcon; // [reset] (0 if not forcing, otherwise forcing)
};
+In the implementation file +nsStyleContext.cpp +add the new data member to the constructors of the style struct and the CalcDifference +method, which must return the correct style-change hint when a change to +your new property is detected. The constructor changes are obvious, but here +is the CalcDifference change for our example:
+
PRInt32 nsStyleUIReset::CalcDifference(const nsStyleUIReset& aOther) const
{
if (mForceBrokenImageIcon == aOther.mForceBrokenImageIcon) {
if (mResizer == aOther.mResizer) {
if (mUserSelect == aOther.mUserSelect) {
if (mKeyEquivalent == aOther.mKeyEquivalent) {
return NS_STYLE_HINT_NONE;
}
return NS_STYLE_HINT_CONTENT;
}
return NS_STYLE_HINT_VISUAL;
}
return NS_STYLE_HINT_VISUAL;
}
return NS_STYLE_HINT_FRAMECHANGE;
}
+

CSSStyleRule

+The nsCSSStyleRule must be updated to manage mapping the declaration to the +style struct. In the file +nsCSSStyleRule.cpp +, locate the Declaration mapping function corresponding to the style struct +you have added your property to. For example, we +update +MapUIForDeclaration:
+
static nsresult
MapUIForDeclaration(nsCSSDeclaration* aDecl, const nsStyleStructID& aID, nsCSSUserInterface& aUI)
{
if (!aDecl)
return NS_OK; // The rule must have a declaration.

nsCSSUserInterface* ourUI = (nsCSSUserInterface*)aDecl->GetData(kCSSUserInterfaceSID);
if (!ourUI)
return NS_OK; // We don't have any rules for UI.

if (aID == eStyleStruct_UserInterface) {
if (aUI.mUserFocus.GetUnit() == eCSSUnit_Null && ourUI->mUserFocus.GetUnit() != eCSSUnit_Null)
aUI.mUserFocus = ourUI->mUserFocus;

if (aUI.mUserInput.GetUnit() == eCSSUnit_Null && ourUI->mUserInput.GetUnit() != eCSSUnit_Null)
aUI.mUserInput = ourUI->mUserInput;

if (aUI.mUserModify.GetUnit() == eCSSUnit_Null && ourUI->mUserModify.GetUnit() != eCSSUnit_Null)
aUI.mUserModify = ourUI->mUserModify;

if (!aUI.mCursor && ourUI->mCursor)
aUI.mCursor = ourUI->mCursor;


}
else if (aID == eStyleStruct_UIReset) {
if (aUI.mUserSelect.GetUnit() == eCSSUnit_Null && ourUI->mUserSelect.GetUnit() != eCSSUnit_Null)
aUI.mUserSelect = ourUI->mUserSelect;

if (!aUI.mKeyEquivalent && ourUI->mKeyEquivalent)
aUI.mKeyEquivalent = ourUI->mKeyEquivalent;

if (aUI.mResizer.GetUnit() == eCSSUnit_Null && ourUI->mResizer.GetUnit() != eCSSUnit_Null)
aUI.mResizer = ourUI->mResizer;

if (aUI.mForceBrokenImageIcon.GetUnit() == eCSSUnit_Null && ourUI->mForceBrokenImageIcon.GetUnit() == eCSSUnit_Integer)
aUI.mForceBrokenImageIcon = ourUI->mForceBrokenImageIcon;

}

return NS_OK;

}
+

Rule Node

+Now we have to update the RuleNode code to know about the new property. First, +locate the PropertyCheckData array for the data that you added the new property +to. For this example, we add the following:
+
static const PropertyCheckData UIResetCheckProperties[] = {
CHECKDATA_PROP(nsCSSUserInterface, mUserSelect, CHECKDATA_VALUE, PR_FALSE),
CHECKDATA_PROP(nsCSSUserInterface, mResizer, CHECKDATA_VALUE, PR_FALSE),
CHECKDATA_PROP(nsCSSUserInterface, mKeyEquivalent, CHECKDATA_VALUELIST, PR_FALSE)
CHECKDATA_PROP(nsCSSUserInterface, mForceBrokenImageIcon, CHECKDATA_VALUE, PR_FALSE)
};
+The first two arguments correspond to the structure and data member from +teh CSSDeclaration, the third is the data type, the fourth indicates if it +is a coord value or not.
+
+Next, we have to make sure the ComputeXXX method for the structure the property +was added to is updated to mange the new value. In this example we need to +modify the nsRuleNode::ComputeUIResetData method to handle the CSS Declaration +to the style struct:
+
  ...
// resizer: auto, none, enum, inherit
if (eCSSUnit_Enumerated == uiData.mResizer.GetUnit()) {
ui->mResizer = uiData.mResizer.GetIntValue();
}
else if (eCSSUnit_Auto == uiData.mResizer.GetUnit()) {
ui->mResizer = NS_STYLE_RESIZER_AUTO;
}
else if (eCSSUnit_None == uiData.mResizer.GetUnit()) {
ui->mResizer = NS_STYLE_RESIZER_NONE;
}
else if (eCSSUnit_Inherit == uiData.mResizer.GetUnit()) {
inherited = PR_TRUE;
ui->mResizer = parentUI->mResizer;
}

// force-broken-image-icons: integer
if (eCSSUnit_Integer == uiData.mForceBrokenImageIcons.GetUnit()) {
ui->mForceBrokenImageIcons = uiData.mForceBrokenImageIcons.GetIntValue();
}


if (inherited)
// We inherited, and therefore can't be cached in the rule node. We have to be put right on the
// style context.
aContext->SetStyle(eStyleStruct_UIReset, *ui);
else {
// We were fully specified and can therefore be cached right on the rule node.
if (!aHighestNode->mStyleData.mResetData)
aHighestNode->mStyleData.mResetData = new (mPresContext) nsResetStyleData;
aHighestNode->mStyleData.mResetData->mUIData = ui;
// Propagate the bit down.
PropagateDependentBit(NS_STYLE_INHERIT_UI_RESET, aHighestNode);
}
...
+

Layout

+OK, finally the style system is supporting the new property. It is time to +actually make use of it now.
+
+In layout, retrieve the styleStruct that has the new property from the frame's +style context. Access the new property and get its value. It is that simple. +For this example, it looks like this, in nsImageFrame:
+
        PRBool forceIcon = PR_FALSE;

const nsStyleUIReset* styleData;
GetStyleData(eStyleStruct_UIReset, (const nsStyleStruct*&) styleData);
if (styleData->mForceBrokenImageIcon) {
forceIcon = PR_TRUE;
}

+Create some testcases with style rules that use the new property, make sure +it is being parsed correctly. Test it in an external stylesheet and in inline +style. Test that it is inherited correctly, or not inherited as appropriate +to your property. Update this document with any further details, or correcting +any errors.
+ \ No newline at end of file