Document history:
- 03/21/2002: Marc Attinasi (attinasi@netscape.com) created document / implementing -moz-force-broken-image-icon for bug 58646
- 6/18/2002: David Baron (dbaron@fas.harvard.edu): corrected support for 'inherit' and '-moz-initial' (didn't test with code, though), and explained what the final boolean in CheckPropertyData is for.
- 11/09/2002: Christopher Aillon (caillon@returnzero.com): updated nsCSSPropList.h macro description and added information about nsIDOMCSS2Properties.idl.
NOTE: This document is still missing a few pieces. I need to add information on adding to
nsComputedDOMStyle
.
Up front you have to decide some things about the new property:
Questions:
Answers:
There are several places that need to be educated about a new style property. They are:
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-icons, force_broken_image_icons, MozForceBrokenImageIcons, NS_STYLE_HINT_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 third value is the name of the DOM property used to access your style.
The last value indicates what must change when the value of the property
changes. It should be an
nsChangeHint.
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).
// 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); }
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.
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; }
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; }
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 the CSSDeclaration, the third is the data type, the fourth indicates whether it is a coord value that uses an explicit inherit value on the style data struct that must be computed by layout.
... // 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, inherit, -moz-initial if (eCSSUnit_Integer == uiData.mForceBrokenImageIcons.GetUnit()) { ui->mForceBrokenImageIcons = uiData.mForceBrokenImageIcons.GetIntValue(); } else if (eCSSUnit_Inherit == uiData.mForceBrokenImageIcons.GetUnit()) { inherited = PR_TRUE; ui->mForceBrokenImageIcons = parentUI->mForceBrokenImageIcons; } else if (eCSSUnit_Initial == uiData.mForceBrokenImageIcons.GetUnit()) { ui->mForceBrokenImageIcons = 0; } 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); } ...
nsIDOMCSSStyleDeclaration
, nsIDOMCSS2Properties
, and
nsIDOMNSCSS2Properties
. By the magic of C++ pre-processing, the
CSS2Properties interfaces will be implemented automatically when you
add your property to
nsCSSPropList.h. Because of this, if you fail to add your property to the
DOM interface, you will be rewarded with compiler errors. All you have to do
is modify
nsIDOMCSS2Properties.idl and add the appropriate attribute to the
nsIDOMNSCSS2Properties
interface (lower in the file).
For example:attribute DOMString MozForceBrokenImageIcon; // raises(DOMException) on setting
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.