Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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->mUserInput = aValue; break; |
michael@0 | 156 | case eCSSProperty_user_modify: theUserInterface->mUserModify = aValue; break; |
michael@0 | 157 | case eCSSProperty_user_select: theUserInterface->mUserSelect = aValue; break; |
michael@0 | 158 | case eCSSProperty_key_equivalent: |
michael@0 | 159 | CSS_ENSURE_DATA(theUserInterface->mKeyEquivalent, nsCSSValueList) { |
michael@0 | 160 | theUserInterface->mKeyEquivalent->mValue = aValue; |
michael@0 | 161 | CSS_IF_DELETE(theUserInterface->mKeyEquivalent->mNext); |
michael@0 | 162 | } |
michael@0 | 163 | break; |
michael@0 | 164 | case eCSSProperty_user_focus: theUserInterface->mUserFocus = aValue; break; |
michael@0 | 165 | case eCSSProperty_resizer: theUserInterface->mResizer = aValue; break; |
michael@0 | 166 | case eCSSProperty_cursor: |
michael@0 | 167 | CSS_ENSURE_DATA(theUserInterface->mCursor, nsCSSValueList) { |
michael@0 | 168 | theUserInterface->mCursor->mValue = aValue; |
michael@0 | 169 | CSS_IF_DELETE(theUserInterface->mCursor->mNext); |
michael@0 | 170 | } |
michael@0 | 171 | break; |
michael@0 | 172 | <b> <a name="AppendValueCase"></a>case eCSSProperty_force_broken_image_icons: theUserInterface->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->mUserInput; break; |
michael@0 | 196 | case eCSSProperty_user_modify: aValue = theUserInterface->mUserModify; break; |
michael@0 | 197 | case eCSSProperty_user_select: aValue = theUserInterface->mUserSelect; break; |
michael@0 | 198 | case eCSSProperty_key_equivalent: |
michael@0 | 199 | if (nullptr != theUserInterface->mKeyEquivalent) { |
michael@0 | 200 | aValue = theUserInterface->mKeyEquivalent->mValue; |
michael@0 | 201 | } |
michael@0 | 202 | break; |
michael@0 | 203 | case eCSSProperty_user_focus: aValue = theUserInterface->mUserFocus; break; |
michael@0 | 204 | case eCSSProperty_resizer: aValue = theUserInterface->mResizer; break; |
michael@0 | 205 | case eCSSProperty_cursor: |
michael@0 | 206 | if (nullptr != theUserInterface->mCursor) { |
michael@0 | 207 | aValue = theUserInterface->mCursor->mValue; |
michael@0 | 208 | } |
michael@0 | 209 | break; |
michael@0 | 210 | <b> <a name="GetValueCase"></a>case eCSSProperty_force_broken_image_icons: aValue = theUserInterface->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 >= 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->mValue.AppendToString(buffer, eCSSProperty_key_equivalent); |
michael@0 | 236 | keyEquiv= keyEquiv->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->mValue.AppendToString(buffer, eCSSProperty_cursor); |
michael@0 | 244 | cursor = cursor->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& 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->~nsStyleUIReset(); |
michael@0 | 307 | aContext->FreeToShell(sizeof(nsStyleUIReset), this); |
michael@0 | 308 | }; |
michael@0 | 309 | |
michael@0 | 310 | int32_t CalcDifference(const nsStyleUIReset& 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& 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& aID, nsCSSUserInterface& 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->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 && ourUI->mUserFocus.GetUnit() != eCSSUnit_Null) |
michael@0 | 361 | aUI.mUserFocus = ourUI->mUserFocus; |
michael@0 | 362 | |
michael@0 | 363 | if (aUI.mUserInput.GetUnit() == eCSSUnit_Null && ourUI->mUserInput.GetUnit() != eCSSUnit_Null) |
michael@0 | 364 | aUI.mUserInput = ourUI->mUserInput; |
michael@0 | 365 | |
michael@0 | 366 | if (aUI.mUserModify.GetUnit() == eCSSUnit_Null && ourUI->mUserModify.GetUnit() != eCSSUnit_Null) |
michael@0 | 367 | aUI.mUserModify = ourUI->mUserModify; |
michael@0 | 368 | |
michael@0 | 369 | if (!aUI.mCursor && ourUI->mCursor) |
michael@0 | 370 | aUI.mCursor = ourUI->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 && ourUI->mUserSelect.GetUnit() != eCSSUnit_Null) |
michael@0 | 376 | aUI.mUserSelect = ourUI->mUserSelect; |
michael@0 | 377 | |
michael@0 | 378 | if (!aUI.mKeyEquivalent && ourUI->mKeyEquivalent) |
michael@0 | 379 | aUI.mKeyEquivalent = ourUI->mKeyEquivalent; |
michael@0 | 380 | |
michael@0 | 381 | if (aUI.mResizer.GetUnit() == eCSSUnit_Null && ourUI->mResizer.GetUnit() != eCSSUnit_Null) |
michael@0 | 382 | aUI.mResizer = ourUI->mResizer; |
michael@0 | 383 | <b> |
michael@0 | 384 | <a name="MapUIForDeclChange"></a>if (aUI.mForceBrokenImageIcon.GetUnit() == eCSSUnit_Null && ourUI->mForceBrokenImageIcon.GetUnit() == eCSSUnit_Integer) |
michael@0 | 385 | aUI.mForceBrokenImageIcon = ourUI->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->mResizer = uiData.mResizer.GetIntValue(); |
michael@0 | 416 | } |
michael@0 | 417 | else if (eCSSUnit_Auto == uiData.mResizer.GetUnit()) { |
michael@0 | 418 | ui->mResizer = NS_STYLE_RESIZER_AUTO; |
michael@0 | 419 | } |
michael@0 | 420 | else if (eCSSUnit_None == uiData.mResizer.GetUnit()) { |
michael@0 | 421 | ui->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->mResizer = parentUI->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->mForceBrokenImageIcons = uiData.mForceBrokenImageIcons.GetIntValue(); |
michael@0 | 431 | } else if (eCSSUnit_Inherit == uiData.mForceBrokenImageIcons.GetUnit()) { |
michael@0 | 432 | inherited = PR_TRUE; |
michael@0 | 433 | ui->mForceBrokenImageIcons = parentUI->mForceBrokenImageIcons; |
michael@0 | 434 | } else if (eCSSUnit_Initial == uiData.mForceBrokenImageIcons.GetUnit()) { |
michael@0 | 435 | ui->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->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->mStyleData.mResetData) |
michael@0 | 445 | aHighestNode->mStyleData.mResetData = new (mPresContext) nsResetStyleData; |
michael@0 | 446 | aHighestNode->mStyleData.mResetData->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()->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> |