diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /layout/doc/adding-style-props.html | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/doc/adding-style-props.html')
-rw-r--r-- | layout/doc/adding-style-props.html | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/layout/doc/adding-style-props.html b/layout/doc/adding-style-props.html new file mode 100644 index 000000000..c654a55d5 --- /dev/null +++ b/layout/doc/adding-style-props.html @@ -0,0 +1,479 @@ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head> + <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1"> + <meta name="author" content="Marc Attinasi"><title>Adding a new style property - layout cookbook</title></head> + +<body> +<h1>Adding a new style property</h1> +<blockquote> + <h4>Document history:</h4> + <ul> + <li>03/21/2002: Marc Attinasi (attinasi@netscape.com) created document +/ implementing -moz-force-broken-image-icon for bug <a href="http://bugzilla.mozilla.org/show_bug.cgi?id=58646"> +58646</a></li> + <li> + 6/18/2002: David Baron (dbaron@dbaron.org): corrected support + for 'inherit' and 'initial' (didn't test with code, though), + and explained what the final boolean in CheckPropertyData is for. + </li> + <li> + 11/09/2002: Christopher Aillon (caillon@returnzero.com): updated + nsCSSPropList.h macro description and added information about + nsIDOMCSS2Properties.idl. + </li> + </ul> + <p> + <b>NOTE</b>: This document is still missing a few pieces. I need to + add information on adding to <code>nsComputedDOMStyle</code>. + </p> + </blockquote> + <h2>Overview</h2> +When a new style property is needed there are many places in the code that +need to be updated. This document outlines the procedure 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 <a href="http://bugzilla.mozilla.org/show_bug.cgi?id=58646"> +bug 58646</a>. + + <h2>Analysis</h2> +<p>Up front you have to decide some things about the new property:</p> + +<p><b>Questions:</b></p> + <ol> + <li>Is the property proprietary or specified by the CSS standard?</li> + <li>Is the property inherited?</li> + <li>What types of values can the property have?</li> + <li>Does it logically fit with other existing properties?</li> + <li>What is the impact to the layout of a page if that property changes?</li> + <li>What do you want to name it?</li> + </ol> +<p><b>Answers:</b></p> + <ol> + <li>In our specific case, we want a property that is used internally, +so it is a proprietary property.</li> + <li>The property is to be used for images, which are leaf elements, so +there is no need to inherit it.</li> + <li>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. </li> + <li>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.</li> + <li>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.</li> + <li>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-').</li> + </ol> + <h2>Implementation</h2> + +<p>There are several places that need to be educated about a new style property. +They are: +</p> + <ul> + <li><a href="#CSSPropList">CSS Property Names and Hint Tables</a>: the new +property name must be made formally know to the system</li> + <li><a href="#CSSDeclaration">CSS Declaration</a>: the declaration must be +able to hold the property and its value(s)</li> + <li><a href="#Parser">CSS Parser</a>: the parser must be able to parse the +property name, validate the values, and provide a declaration for the property +and value</li> + <li><a href="#StyleContext">Style Context</a>: the StyleContext must be able +to hold the resolved value of the property, and provide a means to retrieve the +property value. Additionally, the StyleContext has to know what kind of impact a +change to this property causes.</li> + <li><a href="#RuleNode">Rule Nodes</a>: the RuleNodes need to know how the +property is inherited and how it is shared by other elements.</li> + <li><a href="#DOM">DOM</a>: Your style should be accessible from the DOM so +users may dynamically set/get its property value.</li> + <li><a href="#Layout">Layout</a>: layout has to know what to do with the +property, in other words, the meaning of the property.</li> + </ul> + <h3><a name="CSSPropList">CSS Property Name / Constants / Hints</a></h3> + +<p> +First, add the new name to the property list in <a href="http://lxr.mozilla.org/seamonkey/source/content/shared/public/nsCSSPropList.h"> +nsCSSPropList.h</a> + Insert the property in the list alphabetically, using the existing +property names as a template. The format of the entry you will create is: +</p> + <pre>CSS_PROP(-moz-force-broken-image-icons, force_broken_image_icons, MozForceBrokenImageIcons, nsChangeHint_ReconstructFrame) // bug 58646</pre> + +<p>The first value is the formal property name, in other words the property +name as it is seen by the CSS parser.<br> +The second value is the name of the property as it will appear internally.<br> +The third value is the name of the DOM property used to access your style.<br> +The last value indicates what must change when the value of the property +changes. It should be an +<a href="http://lxr.mozilla.org/seamonkey/source/content/shared/public/nsChangeHint.h">nsChangeHint</a>.</p> + +<p>If you need to introduce new constants for the values of the property, they +must be added to <a href="http://lxr.mozilla.org/seamonkey/source/layout/base/public/nsStyleConsts.h"> +nsStyleConsts.h</a> + and to the appropriate keyword tables in <a href="http://lxr.mozilla.org/seamonkey/source/content/shared/src/nsCSSProps.cpp"> +nsCSSProps.cpp</a> + (note: this cookbook does not do this since the new property does not require +any new keywords for the property values). + <h3><a name="CSSDeclaration">CSS Declaration</a></h3> +Changes will need to be made to the structs and classes defined in <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSDeclaration.h"> +nsCSSDeclaration.h </a> +and <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSDeclaration.cpp"> +nsCSSDeclaration.cpp</a> + <br> + <br> +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 <b>nsCSSUserInterface</b> +. 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. <br> + <br> +Next, the <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSDeclaration.cpp#1410"> +AppendValue</a> + 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 + <a href="#AppendValueCase">there</a> +.<br> + <pre> // 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; +<b> <a name="AppendValueCase"></a>case eCSSProperty_force_broken_image_icons: theUserInterface->mForceBrokenImageIcon = aValue; break; +</b> + CSS_BOGUS_DEFAULT; // make compiler happy + } + } + break; + } + +</pre> +The GetValue method must be similarly <a href="#GetValueCase">modified</a> +:<br> + <pre> // 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 (nullptr != 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 (nullptr != 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 (nullptr != theUserInterface->mCursor) { + aValue = theUserInterface->mCursor->mValue; + } + break; +<b> <a name="GetValueCase"></a>case eCSSProperty_force_broken_image_icons: aValue = theUserInterface->mForceBrokenImageIcons; break; +</b> + CSS_BOGUS_DEFAULT; // make compiler happy + } + } + else { + aValue.Reset(); + } + break; + } + +</pre> +Finally <a href="#ListCase">modify </a> +the 'List' method to output the property value.<br> + <pre>void nsCSSUserInterface::List(FILE* out, int32_t aIndent) const +{ + for (int32_t 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 (nullptr != 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 (nullptr != cursor) { + cursor->mValue.AppendToString(buffer, eCSSProperty_cursor); + cursor = cursor->mNext; + } + + <b> <a name="ListCase"></a>mForceBrokenImageIcon.AppendToString(buffer,eCSSProperty_force_broken_image_icons);</b> + + fputs(NS_LossyConvertUTF16toASCII(buffer).get(), out); +} + +</pre> + + <h3><a name="Parser">CSS Parser</a></h3> +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 <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSParser.cpp#4151"> +ParseBackground</a> +, for and example). We are just adding a simple single-value property here.<br> + <br> +Open nsCSSParser.cpp and look for the method <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSParser.cpp#3580"> +ParseSingleProperty</a> +. 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 <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSParser.cpp#2754"> +variant flag</a> + that makes sense for your property. In our case<br> + <br> + <pre> case eCSSProperty_force_broken_image_icons:</pre> + <pre> return ParsePositiveVariant(aErrorCode, aValue, VARIANT_INTEGER, nullptr);</pre> +This will parse the value as a positive integer value, which is what we want.<br> + <br> + <h3><a name="StyleContext">Style Context</a></h3> +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.<br> + <br> +First look into <a href="http://lxr.mozilla.org/seamonkey/source/content/shared/public/nsStyleStruct.h"> +nsStyleStruct.h</a> + 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 <a href="#StyleContextMember">data member</a> + to hold the value: + <pre>struct nsStyleUIReset { + nsStyleUIReset(void); + nsStyleUIReset(const nsStyleUIReset& aOther); + ~nsStyleUIReset(void); + + NS_DEFINE_STATIC_STYLESTRUCTID_ACCESSOR(eStyleStruct_UIReset) + + void* operator new(size_t sz, nsPresContext* aContext) { + return aContext->AllocateFromShell(sz); + } + void Destroy(nsPresContext* aContext) { + this->~nsStyleUIReset(); + aContext->FreeToShell(sizeof(nsStyleUIReset), this); + }; + + int32_t CalcDifference(const nsStyleUIReset& aOther) const; + + uint8_t mUserSelect; // [reset] (selection-style) + PRUnichar mKeyEquivalent; // [reset] XXX what type should this be? + uint8_t mResizer; // [reset] + <b><a name="StyleContextMember"></a>uint8_t mForceBrokenImageIcon; // [reset] (0 if not forcing, otherwise forcing)</b> +}; +</pre> +In the implementation file <a href="http://lxr.mozilla.org/seamonkey/source/content/shared/src/nsStyleStruct.cpp"> +nsStyleContext.cpp </a> +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:<br> + <pre>int32_t nsStyleUIReset::CalcDifference(const nsStyleUIReset& aOther) const +{ + <b> if (mForceBrokenImageIcon == aOther.mForceBrokenImageIcon) {</b> + 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; + } + <b>return nsChangeHint_ReconstructFrame; +</b>} +</pre> + <h3>CSSStyleRule</h3> +The nsCSSStyleRule must be updated to manage mapping the declaration to the +style struct. In the file <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSStyleRule.cpp"> +nsCSSStyleRule.cpp</a> +, locate the Declaration mapping function corresponding to the style struct +you have added your property to. For example, we <a href="http://bugzilla.mozilla.org/MapUIForDeclChange"> +update </a> +MapUIForDeclaration:<br> + <pre>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; + <b> + <a name="MapUIForDeclChange"></a>if (aUI.mForceBrokenImageIcon.GetUnit() == eCSSUnit_Null && ourUI->mForceBrokenImageIcon.GetUnit() == eCSSUnit_Integer) + aUI.mForceBrokenImageIcon = ourUI->mForceBrokenImageIcon;</b> + } + + return NS_OK; + +} +</pre> + <h3><a name="RuleNode">Rule Node</a></h3> +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:<br> + <pre>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) + <b>CHECKDATA_PROP(nsCSSUserInterface, mForceBrokenImageIcon, CHECKDATA_VALUE, PR_FALSE)</b> +}; +</pre> +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.<br> + <br> +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:<br> + <pre> ... + // 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; + } + + <b>// force-broken-image-icons: integer, inherit, 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; + }</b> + + 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); + } + ... +</pre> + <h3><a name="DOM">DOM</a></h3> +Users in scripts, or anywhere outside of layout/ or content/ may need to access +the new property. This is done using the CSS OM, specifically +<code>nsIDOMCSSStyleDeclaration</code> and <code>CSS2Properties</code>. +By the magic of C++ pre-processing, the +CSS2Properties bits will be implemented automatically when you +<a href="#CSSPropList">add your property</a> to <a href="http://lxr.mozilla.org/seamonkey/source/content/shared/public/nsCSSPropList.h"> +nsCSSPropList.h</a>. + <h3><a name="Layout">Layout</a></h3> +OK, finally the style system is supporting the new property. It is time to +actually make use of it now.<br> + <br> +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:<br> + <pre> PRBool forceIcon = PR_FALSE; + + if (StyleUIReset()->mForceBrokenImageIcon) { + forceIcon = PR_TRUE; + } + +</pre> +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.<br> + </body></html> |