layout/doc/adding-style-props.html

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 <!-- This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 - License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
michael@0 4
michael@0 5 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head>
michael@0 6 <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
michael@0 7 <meta name="author" content="Marc Attinasi"><title>Adding a new style property - layout cookbook</title></head>
michael@0 8
michael@0 9 <body>
michael@0 10 <h1>Adding a new style property</h1>
michael@0 11 <blockquote>
michael@0 12 <h4>Document history:</h4>
michael@0 13 <ul>
michael@0 14 <li>03/21/2002: Marc Attinasi (attinasi@netscape.com) created document
michael@0 15 / implementing -moz-force-broken-image-icon for bug <a href="http://bugzilla.mozilla.org/show_bug.cgi?id=58646">
michael@0 16 58646</a></li>
michael@0 17 <li>
michael@0 18 6/18/2002: David Baron (dbaron@dbaron.org): corrected support
michael@0 19 for 'inherit' and 'initial' (didn't test with code, though),
michael@0 20 and explained what the final boolean in CheckPropertyData is for.
michael@0 21 </li>
michael@0 22 <li>
michael@0 23 11/09/2002: Christopher Aillon (caillon@returnzero.com): updated
michael@0 24 nsCSSPropList.h macro description and added information about
michael@0 25 nsIDOMCSS2Properties.idl.
michael@0 26 </li>
michael@0 27 </ul>
michael@0 28 <p>
michael@0 29 <b>NOTE</b>: This document is still missing a few pieces. I need to
michael@0 30 add information on adding to <code>nsComputedDOMStyle</code>.
michael@0 31 </p>
michael@0 32 </blockquote>
michael@0 33 <h2>Overview</h2>
michael@0 34 When a new style property is needed there are many places in the code that
michael@0 35 need to be updated. This document outlines the procedure used to add a new
michael@0 36 property, in this case the property is a proprietary one called '-moz-force-broken-image-icons'
michael@0 37 and is used as a way for a stylesheet to force broken image icons to be displayed.
michael@0 38 This is all being done in the context of <a href="http://bugzilla.mozilla.org/show_bug.cgi?id=58646">
michael@0 39 bug 58646</a>.
michael@0 40
michael@0 41 <h2>Analysis</h2>
michael@0 42 <p>Up front you have to decide some things about the new property:</p>
michael@0 43
michael@0 44 <p><b>Questions:</b></p>
michael@0 45 <ol>
michael@0 46 <li>Is the property proprietary or specified by the CSS standard?</li>
michael@0 47 <li>Is the property inherited?</li>
michael@0 48 <li>What types of values can the property have?</li>
michael@0 49 <li>Does it logically fit with other existing properties?</li>
michael@0 50 <li>What is the impact to the layout of a page if that property changes?</li>
michael@0 51 <li>What do you want to name it?</li>
michael@0 52 </ol>
michael@0 53 <p><b>Answers:</b></p>
michael@0 54 <ol>
michael@0 55 <li>In our specific case, we want a property that is used internally,
michael@0 56 so it is a proprietary property.</li>
michael@0 57 <li>The property is to be used for images, which are leaf elements, so
michael@0 58 there is no need to inherit it.</li>
michael@0 59 <li>The property is used simply to force a broken image to be represented
michael@0 60 by an icon, so it only supports the values '0' and '1' as numerics. </li>
michael@0 61 <li>It is hard to see how this property fits logically in with other
michael@0 62 properties, but if we stretch our imaginations we could say that it is a
michael@0 63 sort of UI property.</li>
michael@0 64 <li>If this property changes, the image frame has to be recreated. This
michael@0 65 is because the decision about whether to display the icon or not will impact
michael@0 66 the decision to replace the image frame with an inline text frame for the
michael@0 67 ALT text, so if the ALT text inline is already made, there is no image frame
michael@0 68 left around to reflow or otherwise modify.</li>
michael@0 69 <li>Finally, the name will be '-moz-force-broken-image-icons' - that
michael@0 70 should be pretty self-describing (by convention we start proprietary property
michael@0 71 names with '-moz-').</li>
michael@0 72 </ol>
michael@0 73 <h2>Implementation</h2>
michael@0 74
michael@0 75 <p>There are several places that need to be educated about a new style property.
michael@0 76 They are:
michael@0 77 </p>
michael@0 78 <ul>
michael@0 79 <li><a href="#CSSPropList">CSS Property Names and Hint Tables</a>: the new
michael@0 80 property name must be made formally know to the system</li>
michael@0 81 <li><a href="#CSSDeclaration">CSS Declaration</a>: the declaration must be
michael@0 82 able to hold the property and its value(s)</li>
michael@0 83 <li><a href="#Parser">CSS Parser</a>: the parser must be able to parse the
michael@0 84 property name, validate the values, and provide a declaration for the property
michael@0 85 and value</li>
michael@0 86 <li><a href="#StyleContext">Style Context</a>: the StyleContext must be able
michael@0 87 to hold the resolved value of the property, and provide a means to retrieve the
michael@0 88 property value. Additionally, the StyleContext has to know what kind of impact a
michael@0 89 change to this property causes.</li>
michael@0 90 <li><a href="#RuleNode">Rule Nodes</a>: the RuleNodes need to know how the
michael@0 91 property is inherited and how it is shared by other elements.</li>
michael@0 92 <li><a href="#DOM">DOM</a>: Your style should be accessible from the DOM so
michael@0 93 users may dynamically set/get its property value.</li>
michael@0 94 <li><a href="#Layout">Layout</a>: layout has to know what to do with the
michael@0 95 property, in other words, the meaning of the property.</li>
michael@0 96 </ul>
michael@0 97 <h3><a name="CSSPropList">CSS Property Name / Constants / Hints</a></h3>
michael@0 98
michael@0 99 <p>
michael@0 100 First, add the new name to the property list in <a href="http://lxr.mozilla.org/seamonkey/source/content/shared/public/nsCSSPropList.h">
michael@0 101 nsCSSPropList.h</a>
michael@0 102  Insert the property in the list alphabetically, using the existing
michael@0 103 property names as a template. The format of the entry you will create is:
michael@0 104 </p>
michael@0 105 <pre>CSS_PROP(-moz-force-broken-image-icons, force_broken_image_icons, MozForceBrokenImageIcons, NS_STYLE_HINT_FRAMECHANGE) // bug 58646</pre>
michael@0 106
michael@0 107 <p>The first value is the formal property name, in other words the property
michael@0 108 name as it is seen by the CSS parser.<br>
michael@0 109 The second value is the name of the property as it will appear internally.<br>
michael@0 110 The third value is the name of the DOM property used to access your style.<br>
michael@0 111 The last value indicates what must change when the value of the property
michael@0 112 changes. It should be an
michael@0 113 <a href="http://lxr.mozilla.org/seamonkey/source/content/shared/public/nsChangeHint.h">nsChangeHint</a>.</p>
michael@0 114
michael@0 115 <p>If you need to introduce new constants for the values of the property, they
michael@0 116 must be added to <a href="http://lxr.mozilla.org/seamonkey/source/layout/base/public/nsStyleConsts.h">
michael@0 117 nsStyleConsts.h</a>
michael@0 118 and to the appropriate keyword tables in <a href="http://lxr.mozilla.org/seamonkey/source/content/shared/src/nsCSSProps.cpp">
michael@0 119 nsCSSProps.cpp</a>
michael@0 120 (note: this cookbook does not do this since the new property does not require
michael@0 121 any new keywords for the property values).
michael@0 122 <h3><a name="CSSDeclaration">CSS Declaration</a></h3>
michael@0 123 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">
michael@0 124 nsCSSDeclaration.h </a>
michael@0 125 and <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSDeclaration.cpp">
michael@0 126 nsCSSDeclaration.cpp</a>
michael@0 127 <br>
michael@0 128 <br>
michael@0 129 First, find the declaration of the struct that will hold the new property
michael@0 130 value (in the header file). For this example it is the struct <b>nsCSSUserInterface</b>
michael@0 131 . Modify the struct declaration to include a new data member for the new
michael@0 132 property, of the type CSSValue. Next, open the implementation file (the cpp)
michael@0 133 and modify the struct's constructors. <br>
michael@0 134 <br>
michael@0 135 Next, the <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSDeclaration.cpp#1410">
michael@0 136 AppendValue</a>
michael@0 137 method must be updated to support your new property. The CSSParser will
michael@0 138 call this to build up a declaration. Find the portion of that method that
michael@0 139 deals with the other properties in the struct that you are adding your property
michael@0 140 to (or create a new section if you are creating a new style struct). For
michael@0 141 this example we will find the 'UserInterface' section and add our new property
michael@0 142 <a href="#AppendValueCase">there</a>
michael@0 143 .<br>
michael@0 144 <pre> // nsCSSUserInterface
michael@0 145 case eCSSProperty_user_input:
michael@0 146 case eCSSProperty_user_modify:
michael@0 147 case eCSSProperty_user_select:
michael@0 148 case eCSSProperty_key_equivalent:
michael@0 149 case eCSSProperty_user_focus:
michael@0 150 case eCSSProperty_resizer:
michael@0 151 case eCSSProperty_cursor:
michael@0 152 case eCSSProperty_force_broken_image_icons: {
michael@0 153 CSS_ENSURE(UserInterface) {
michael@0 154 switch (aProperty) {
michael@0 155 case eCSSProperty_user_input: theUserInterface-&gt;mUserInput = aValue; break;
michael@0 156 case eCSSProperty_user_modify: theUserInterface-&gt;mUserModify = aValue; break;
michael@0 157 case eCSSProperty_user_select: theUserInterface-&gt;mUserSelect = aValue; break;
michael@0 158 case eCSSProperty_key_equivalent:
michael@0 159 CSS_ENSURE_DATA(theUserInterface-&gt;mKeyEquivalent, nsCSSValueList) {
michael@0 160 theUserInterface-&gt;mKeyEquivalent-&gt;mValue = aValue;
michael@0 161 CSS_IF_DELETE(theUserInterface-&gt;mKeyEquivalent-&gt;mNext);
michael@0 162 }
michael@0 163 break;
michael@0 164 case eCSSProperty_user_focus: theUserInterface-&gt;mUserFocus = aValue; break;
michael@0 165 case eCSSProperty_resizer: theUserInterface-&gt;mResizer = aValue; break;
michael@0 166 case eCSSProperty_cursor:
michael@0 167 CSS_ENSURE_DATA(theUserInterface-&gt;mCursor, nsCSSValueList) {
michael@0 168 theUserInterface-&gt;mCursor-&gt;mValue = aValue;
michael@0 169 CSS_IF_DELETE(theUserInterface-&gt;mCursor-&gt;mNext);
michael@0 170 }
michael@0 171 break;
michael@0 172 <b> <a name="AppendValueCase"></a>case eCSSProperty_force_broken_image_icons: theUserInterface-&gt;mForceBrokenImageIcon = aValue; break;
michael@0 173 </b>
michael@0 174 CSS_BOGUS_DEFAULT; // make compiler happy
michael@0 175 }
michael@0 176 }
michael@0 177 break;
michael@0 178 }
michael@0 179
michael@0 180 </pre>
michael@0 181 The GetValue method must be similarly <a href="#GetValueCase">modified</a>
michael@0 182 :<br>
michael@0 183 <pre> // nsCSSUserInterface
michael@0 184 case eCSSProperty_user_input:
michael@0 185 case eCSSProperty_user_modify:
michael@0 186 case eCSSProperty_user_select:
michael@0 187 case eCSSProperty_key_equivalent:
michael@0 188 case eCSSProperty_user_focus:
michael@0 189 case eCSSProperty_resizer:
michael@0 190 case eCSSProperty_cursor:
michael@0 191 case eCSSProperty_force_broken_image_icons: {
michael@0 192 CSS_VARONSTACK_GET(UserInterface);
michael@0 193 if (nullptr != theUserInterface) {
michael@0 194 switch (aProperty) {
michael@0 195 case eCSSProperty_user_input: aValue = theUserInterface-&gt;mUserInput; break;
michael@0 196 case eCSSProperty_user_modify: aValue = theUserInterface-&gt;mUserModify; break;
michael@0 197 case eCSSProperty_user_select: aValue = theUserInterface-&gt;mUserSelect; break;
michael@0 198 case eCSSProperty_key_equivalent:
michael@0 199 if (nullptr != theUserInterface-&gt;mKeyEquivalent) {
michael@0 200 aValue = theUserInterface-&gt;mKeyEquivalent-&gt;mValue;
michael@0 201 }
michael@0 202 break;
michael@0 203 case eCSSProperty_user_focus: aValue = theUserInterface-&gt;mUserFocus; break;
michael@0 204 case eCSSProperty_resizer: aValue = theUserInterface-&gt;mResizer; break;
michael@0 205 case eCSSProperty_cursor:
michael@0 206 if (nullptr != theUserInterface-&gt;mCursor) {
michael@0 207 aValue = theUserInterface-&gt;mCursor-&gt;mValue;
michael@0 208 }
michael@0 209 break;
michael@0 210 <b> <a name="GetValueCase"></a>case eCSSProperty_force_broken_image_icons: aValue = theUserInterface-&gt;mForceBrokenImageIcons; break;
michael@0 211 </b>
michael@0 212 CSS_BOGUS_DEFAULT; // make compiler happy
michael@0 213 }
michael@0 214 }
michael@0 215 else {
michael@0 216 aValue.Reset();
michael@0 217 }
michael@0 218 break;
michael@0 219 }
michael@0 220
michael@0 221 </pre>
michael@0 222 Finally <a href="#ListCase">modify </a>
michael@0 223 the 'List' method to output the property value.<br>
michael@0 224 <pre>void nsCSSUserInterface::List(FILE* out, int32_t aIndent) const
michael@0 225 {
michael@0 226 for (int32_t index = aIndent; --index &gt;= 0; ) fputs(" ", out);
michael@0 227
michael@0 228 nsAutoString buffer;
michael@0 229
michael@0 230 mUserInput.AppendToString(buffer, eCSSProperty_user_input);
michael@0 231 mUserModify.AppendToString(buffer, eCSSProperty_user_modify);
michael@0 232 mUserSelect.AppendToString(buffer, eCSSProperty_user_select);
michael@0 233 nsCSSValueList* keyEquiv = mKeyEquivalent;
michael@0 234 while (nullptr != keyEquiv) {
michael@0 235 keyEquiv-&gt;mValue.AppendToString(buffer, eCSSProperty_key_equivalent);
michael@0 236 keyEquiv= keyEquiv-&gt;mNext;
michael@0 237 }
michael@0 238 mUserFocus.AppendToString(buffer, eCSSProperty_user_focus);
michael@0 239 mResizer.AppendToString(buffer, eCSSProperty_resizer);
michael@0 240
michael@0 241 nsCSSValueList* cursor = mCursor;
michael@0 242 while (nullptr != cursor) {
michael@0 243 cursor-&gt;mValue.AppendToString(buffer, eCSSProperty_cursor);
michael@0 244 cursor = cursor-&gt;mNext;
michael@0 245 }
michael@0 246
michael@0 247 <b> <a name="ListCase"></a>mForceBrokenImageIcon.AppendToString(buffer,eCSSProperty_force_broken_image_icons);</b>
michael@0 248
michael@0 249 fputs(NS_LossyConvertUTF16toASCII(buffer).get(), out);
michael@0 250 }
michael@0 251
michael@0 252 </pre>
michael@0 253
michael@0 254 <h3><a name="Parser">CSS Parser</a></h3>
michael@0 255 Next, the CSSParser must be educated about this new property so that it can
michael@0 256 read in the formal declarations and build up the internal declarations that
michael@0 257 will be used to build the rules. If you are adding a simple property that
michael@0 258 takes a single value, you simply add your new property to the ParseSingleProperty
michael@0 259 method. If a more complex parsing is required you will have to write a new
michael@0 260 method to handle it, modeling it off of one of the existing parsing helper
michael@0 261 methods (see <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSParser.cpp#4151">
michael@0 262 ParseBackground</a>
michael@0 263 , for and example). We are just adding a simple single-value property here.<br>
michael@0 264 <br>
michael@0 265 Open nsCSSParser.cpp and look for the method <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSParser.cpp#3580">
michael@0 266 ParseSingleProperty</a>
michael@0 267 . This method is responsible for calling the relevant helper routine to parse
michael@0 268 the value(s). Find an existing property that is similar to the property you
michael@0 269 are adding. For our example we are adding a property that takes a numeric
michael@0 270 value so we will model it after the 'height' property and call ParsePositiveVariant.
michael@0 271 Add a new case for the new property and call the appropriate parser-helper
michael@0 272 and make a call to ParseVariant passing the <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSParser.cpp#2754">
michael@0 273 variant flag</a>
michael@0 274 that makes sense for your property. In our case<br>
michael@0 275 <br>
michael@0 276 <pre>  case eCSSProperty_force_broken_image_icons:</pre>
michael@0 277 <pre>    return ParsePositiveVariant(aErrorCode, aValue, VARIANT_INTEGER, nullptr);</pre>
michael@0 278 This will parse the value as a positive integer value, which is what we want.<br>
michael@0 279 <br>
michael@0 280 <h3><a name="StyleContext">Style Context</a></h3>
michael@0 281 Having implemented support for the new property in the CSS Parser and CSS
michael@0 282 Declaration classes in the content module, it is now time to provide support
michael@0 283 for the new property in layout. The Style Context must be given a new data
michael@0 284 member corresponding to the declaration's new data member, so the computed
michael@0 285 value can be held for the layout objects to use.<br>
michael@0 286 <br>
michael@0 287 First look into <a href="http://lxr.mozilla.org/seamonkey/source/content/shared/public/nsStyleStruct.h">
michael@0 288 nsStyleStruct.h</a>
michael@0 289 to see the existing style strucs. Find the one that you want to store the
michael@0 290 data on. In this example, we want to put it on the nsStyleUserInterface struct,
michael@0 291 however there is also a class nsStyleUIReset that holds the non-inherited
michael@0 292 values, so we will use that one (remember, our property is not inherited).
michael@0 293 Add a <a href="#StyleContextMember">data member</a>
michael@0 294 to hold the value:
michael@0 295 <pre>struct nsStyleUIReset {
michael@0 296 nsStyleUIReset(void);
michael@0 297 nsStyleUIReset(const nsStyleUIReset&amp; aOther);
michael@0 298 ~nsStyleUIReset(void);
michael@0 299
michael@0 300 NS_DEFINE_STATIC_STYLESTRUCTID_ACCESSOR(eStyleStruct_UIReset)
michael@0 301
michael@0 302 void* operator new(size_t sz, nsPresContext* aContext) {
michael@0 303 return aContext->AllocateFromShell(sz);
michael@0 304 }
michael@0 305 void Destroy(nsPresContext* aContext) {
michael@0 306 this-&gt;~nsStyleUIReset();
michael@0 307 aContext-&gt;FreeToShell(sizeof(nsStyleUIReset), this);
michael@0 308 };
michael@0 309
michael@0 310 int32_t CalcDifference(const nsStyleUIReset&amp; aOther) const;
michael@0 311
michael@0 312 uint8_t mUserSelect; // [reset] (selection-style)
michael@0 313 PRUnichar mKeyEquivalent; // [reset] XXX what type should this be?
michael@0 314 uint8_t mResizer; // [reset]
michael@0 315 <b><a name="StyleContextMember"></a>uint8_t mForceBrokenImageIcon; // [reset] (0 if not forcing, otherwise forcing)</b>
michael@0 316 };
michael@0 317 </pre>
michael@0 318 In the implementation file <a href="http://lxr.mozilla.org/seamonkey/source/content/shared/src/nsStyleStruct.cpp">
michael@0 319 nsStyleContext.cpp </a>
michael@0 320 add the new data member to the constructors of the style struct and the CalcDifference
michael@0 321 method, which must return the correct style-change hint when a change to
michael@0 322 your new property is detected. The constructor changes are obvious, but here
michael@0 323 is the CalcDifference change for our example:<br>
michael@0 324 <pre>int32_t nsStyleUIReset::CalcDifference(const nsStyleUIReset&amp; aOther) const
michael@0 325 {
michael@0 326 <b> if (mForceBrokenImageIcon == aOther.mForceBrokenImageIcon) {</b>
michael@0 327 if (mResizer == aOther.mResizer) {
michael@0 328 if (mUserSelect == aOther.mUserSelect) {
michael@0 329 if (mKeyEquivalent == aOther.mKeyEquivalent) {
michael@0 330 return NS_STYLE_HINT_NONE;
michael@0 331 }
michael@0 332 return NS_STYLE_HINT_CONTENT;
michael@0 333 }
michael@0 334 return NS_STYLE_HINT_VISUAL;
michael@0 335 }
michael@0 336 return NS_STYLE_HINT_VISUAL;
michael@0 337 }
michael@0 338 <b>return NS_STYLE_HINT_FRAMECHANGE;
michael@0 339 </b>}
michael@0 340 </pre>
michael@0 341 <h3>CSSStyleRule</h3>
michael@0 342 The nsCSSStyleRule must be updated to manage mapping the declaration to the
michael@0 343 style struct. In the file <a href="http://lxr.mozilla.org/seamonkey/source/content/html/style/src/nsCSSStyleRule.cpp">
michael@0 344 nsCSSStyleRule.cpp</a>
michael@0 345 , locate the Declaration mapping function corresponding to the style struct
michael@0 346 you have added your property to. For example, we <a href="http://bugzilla.mozilla.org/MapUIForDeclChange">
michael@0 347 update </a>
michael@0 348 MapUIForDeclaration:<br>
michael@0 349 <pre>static nsresult
michael@0 350 MapUIForDeclaration(nsCSSDeclaration* aDecl, const nsStyleStructID&amp; aID, nsCSSUserInterface&amp; aUI)
michael@0 351 {
michael@0 352 if (!aDecl)
michael@0 353 return NS_OK; // The rule must have a declaration.
michael@0 354
michael@0 355 nsCSSUserInterface* ourUI = (nsCSSUserInterface*)aDecl-&gt;GetData(kCSSUserInterfaceSID);
michael@0 356 if (!ourUI)
michael@0 357 return NS_OK; // We don't have any rules for UI.
michael@0 358
michael@0 359 if (aID == eStyleStruct_UserInterface) {
michael@0 360 if (aUI.mUserFocus.GetUnit() == eCSSUnit_Null &amp;&amp; ourUI-&gt;mUserFocus.GetUnit() != eCSSUnit_Null)
michael@0 361 aUI.mUserFocus = ourUI-&gt;mUserFocus;
michael@0 362
michael@0 363 if (aUI.mUserInput.GetUnit() == eCSSUnit_Null &amp;&amp; ourUI-&gt;mUserInput.GetUnit() != eCSSUnit_Null)
michael@0 364 aUI.mUserInput = ourUI-&gt;mUserInput;
michael@0 365
michael@0 366 if (aUI.mUserModify.GetUnit() == eCSSUnit_Null &amp;&amp; ourUI-&gt;mUserModify.GetUnit() != eCSSUnit_Null)
michael@0 367 aUI.mUserModify = ourUI-&gt;mUserModify;
michael@0 368
michael@0 369 if (!aUI.mCursor &amp;&amp; ourUI-&gt;mCursor)
michael@0 370 aUI.mCursor = ourUI-&gt;mCursor;
michael@0 371
michael@0 372
michael@0 373 }
michael@0 374 else if (aID == eStyleStruct_UIReset) {
michael@0 375 if (aUI.mUserSelect.GetUnit() == eCSSUnit_Null &amp;&amp; ourUI-&gt;mUserSelect.GetUnit() != eCSSUnit_Null)
michael@0 376 aUI.mUserSelect = ourUI-&gt;mUserSelect;
michael@0 377
michael@0 378 if (!aUI.mKeyEquivalent &amp;&amp; ourUI-&gt;mKeyEquivalent)
michael@0 379 aUI.mKeyEquivalent = ourUI-&gt;mKeyEquivalent;
michael@0 380
michael@0 381 if (aUI.mResizer.GetUnit() == eCSSUnit_Null &amp;&amp; ourUI-&gt;mResizer.GetUnit() != eCSSUnit_Null)
michael@0 382 aUI.mResizer = ourUI-&gt;mResizer;
michael@0 383 <b>
michael@0 384 <a name="MapUIForDeclChange"></a>if (aUI.mForceBrokenImageIcon.GetUnit() == eCSSUnit_Null &amp;&amp; ourUI-&gt;mForceBrokenImageIcon.GetUnit() == eCSSUnit_Integer)
michael@0 385 aUI.mForceBrokenImageIcon = ourUI-&gt;mForceBrokenImageIcon;</b>
michael@0 386 }
michael@0 387
michael@0 388 return NS_OK;
michael@0 389
michael@0 390 }
michael@0 391 </pre>
michael@0 392 <h3><a name="RuleNode">Rule Node</a></h3>
michael@0 393 Now we have to update the RuleNode code to know about the new property. First,
michael@0 394 locate the PropertyCheckData array for the data that you added the new property
michael@0 395 to. For this example, we add the following:<br>
michael@0 396 <pre>static const PropertyCheckData UIResetCheckProperties[] = {
michael@0 397 CHECKDATA_PROP(nsCSSUserInterface, mUserSelect, CHECKDATA_VALUE, PR_FALSE),
michael@0 398 CHECKDATA_PROP(nsCSSUserInterface, mResizer, CHECKDATA_VALUE, PR_FALSE),
michael@0 399 CHECKDATA_PROP(nsCSSUserInterface, mKeyEquivalent, CHECKDATA_VALUELIST, PR_FALSE)
michael@0 400 <b>CHECKDATA_PROP(nsCSSUserInterface, mForceBrokenImageIcon, CHECKDATA_VALUE, PR_FALSE)</b>
michael@0 401 };
michael@0 402 </pre>
michael@0 403 The first two arguments correspond to the structure and data member from
michael@0 404 the CSSDeclaration, the third is the data type, the fourth indicates
michael@0 405 whether it is a coord value that uses an explicit inherit value on the
michael@0 406 style data struct that must be computed by layout.<br>
michael@0 407 <br>
michael@0 408 Next, we have to make sure the ComputeXXX method for the structure the property
michael@0 409 was added to is updated to mange the new value. In this example we need to
michael@0 410 modify the nsRuleNode::ComputeUIResetData method to handle the CSS Declaration
michael@0 411 to the style struct:<br>
michael@0 412 <pre> ...
michael@0 413 // resizer: auto, none, enum, inherit
michael@0 414 if (eCSSUnit_Enumerated == uiData.mResizer.GetUnit()) {
michael@0 415 ui-&gt;mResizer = uiData.mResizer.GetIntValue();
michael@0 416 }
michael@0 417 else if (eCSSUnit_Auto == uiData.mResizer.GetUnit()) {
michael@0 418 ui-&gt;mResizer = NS_STYLE_RESIZER_AUTO;
michael@0 419 }
michael@0 420 else if (eCSSUnit_None == uiData.mResizer.GetUnit()) {
michael@0 421 ui-&gt;mResizer = NS_STYLE_RESIZER_NONE;
michael@0 422 }
michael@0 423 else if (eCSSUnit_Inherit == uiData.mResizer.GetUnit()) {
michael@0 424 inherited = PR_TRUE;
michael@0 425 ui-&gt;mResizer = parentUI-&gt;mResizer;
michael@0 426 }
michael@0 427
michael@0 428 <b>// force-broken-image-icons: integer, inherit, initial
michael@0 429 if (eCSSUnit_Integer == uiData.mForceBrokenImageIcons.GetUnit()) {
michael@0 430 ui-&gt;mForceBrokenImageIcons = uiData.mForceBrokenImageIcons.GetIntValue();
michael@0 431 } else if (eCSSUnit_Inherit == uiData.mForceBrokenImageIcons.GetUnit()) {
michael@0 432 inherited = PR_TRUE;
michael@0 433 ui-&gt;mForceBrokenImageIcons = parentUI-&gt;mForceBrokenImageIcons;
michael@0 434 } else if (eCSSUnit_Initial == uiData.mForceBrokenImageIcons.GetUnit()) {
michael@0 435 ui-&gt;mForceBrokenImageIcons = 0;
michael@0 436 }</b>
michael@0 437
michael@0 438 if (inherited)
michael@0 439 // We inherited, and therefore can't be cached in the rule node. We have to be put right on the
michael@0 440 // style context.
michael@0 441 aContext-&gt;SetStyle(eStyleStruct_UIReset, *ui);
michael@0 442 else {
michael@0 443 // We were fully specified and can therefore be cached right on the rule node.
michael@0 444 if (!aHighestNode-&gt;mStyleData.mResetData)
michael@0 445 aHighestNode-&gt;mStyleData.mResetData = new (mPresContext) nsResetStyleData;
michael@0 446 aHighestNode-&gt;mStyleData.mResetData-&gt;mUIData = ui;
michael@0 447 // Propagate the bit down.
michael@0 448 PropagateDependentBit(NS_STYLE_INHERIT_UI_RESET, aHighestNode);
michael@0 449 }
michael@0 450 ...
michael@0 451 </pre>
michael@0 452 <h3><a name="DOM">DOM</a></h3>
michael@0 453 Users in scripts, or anywhere outside of layout/ or content/ may need to access
michael@0 454 the new property. This is done using the CSS OM, specifically
michael@0 455 <code>nsIDOMCSSStyleDeclaration</code> and <code>CSS2Properties</code>.
michael@0 456 By the magic of C++ pre-processing, the
michael@0 457 CSS2Properties bits will be implemented automatically when you
michael@0 458 <a href="#CSSPropList">add your property</a> to <a href="http://lxr.mozilla.org/seamonkey/source/content/shared/public/nsCSSPropList.h">
michael@0 459 nsCSSPropList.h</a>.
michael@0 460 <h3><a name="Layout">Layout</a></h3>
michael@0 461 OK, finally the style system is supporting the new property. It is time to
michael@0 462 actually make use of it now.<br>
michael@0 463 <br>
michael@0 464 In layout, retrieve the styleStruct that has the new property from the frame's
michael@0 465 style context. Access the new property and get its value. It is that simple.
michael@0 466 For this example, it looks like this, in nsImageFrame:<br>
michael@0 467 <pre> PRBool forceIcon = PR_FALSE;
michael@0 468
michael@0 469 if (StyleUIReset()-&gt;mForceBrokenImageIcon) {
michael@0 470 forceIcon = PR_TRUE;
michael@0 471 }
michael@0 472
michael@0 473 </pre>
michael@0 474 Create some testcases with style rules that use the new property, make sure
michael@0 475 it is being parsed correctly. Test it in an external stylesheet and in inline
michael@0 476 style. Test that it is inherited correctly, or not inherited as appropriate
michael@0 477 to your property. Update this document with any further details, or correcting
michael@0 478 any errors.<br>
michael@0 479 </body></html>

mercurial