forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			479 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			479 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!-- 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.<2E></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="#ComputedStyle">Style Context</a>: the ComputedStyle must be able
 | ||
| to hold the resolved value of the property, and provide a means to retrieve the
 | ||
| property value. Additionally, the ComputedStyle 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>
 | ||
|  <20>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><EFBFBD> case eCSSProperty_force_broken_image_icons:</pre>
 | ||
|   <pre><EFBFBD><EFBFBD><EFBFBD> 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="ComputedStyle">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="#ComputedStyleMember">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="ComputedStyleMember"></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">
 | ||
| ComputedStyle.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>
 | 
