From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- layout/doc/adding-style-props.html | 479 +++++++++++++++++++++++++++++++++++++ 1 file changed, 479 insertions(+) create mode 100644 layout/doc/adding-style-props.html (limited to 'layout/doc/adding-style-props.html') 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 @@ + + + + + Adding a new style property - layout cookbook + + +

Adding a new style property

+
+

Document history:

+ +

+ NOTE: This document is still missing a few pieces. I need to + add information on adding to nsComputedDOMStyle. +

+
+

Overview

+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 +bug 58646. + +

Analysis

+

Up front you have to decide some things about the new property:

+ +

Questions:

+
    +
  1. Is the property proprietary or specified by the CSS standard?
  2. +
  3. Is the property inherited?
  4. +
  5. What types of values can the property have?
  6. +
  7. Does it logically fit with other existing properties?
  8. +
  9. What is the impact to the layout of a page if that property changes?
  10. +
  11. What do you want to name it?
  12. +
+

Answers:

+
    +
  1. In our specific case, we want a property that is used internally, +so it is a proprietary property.
  2. +
  3. The property is to be used for images, which are leaf elements, so +there is no need to inherit it.
  4. +
  5. 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. 
  6. +
  7. 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.
  8. +
  9. 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.
  10. +
  11. 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-').
  12. +
+

Implementation

+ +

There are several places that need to be educated about a new style property. +They are: +

+ +

CSS Property Name / Constants / Hints

+ +

+First, add the new name to the property list in +nsCSSPropList.h +  Insert the property in the list alphabetically, using the existing +property names as a template. The format of the entry you will create is: +

+
CSS_PROP(-moz-force-broken-image-icons, force_broken_image_icons, MozForceBrokenImageIcons, nsChangeHint_ReconstructFrame) // bug 58646
+ +

The first value is the formal property name, in other words the property +name as it is seen by the CSS parser.
+The second value is the name of the property as it will appear internally.
+The third value is the name of the DOM property used to access your style.
+The last value indicates what must change when the value of the property +changes. It should be an +nsChangeHint.

+ +

If you need to introduce new constants for the values of the property, they +must be added to +nsStyleConsts.h + and to the appropriate keyword tables in +nsCSSProps.cpp + (note: this cookbook does not do this since the new property does not require +any new keywords for the property values). +

CSS Declaration

+Changes will need to be made to the structs and classes defined in +nsCSSDeclaration.h +and +nsCSSDeclaration.cpp +
+
+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 nsCSSUserInterface +. 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.
+
+Next, the +AppendValue + 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 + there +.
+
    // nsCSSUserInterface
+    case eCSSProperty_user_input:
+    case eCSSProperty_user_modify:
+    case eCSSProperty_user_select:
+    case eCSSProperty_key_equivalent:
+    case eCSSProperty_user_focus:
+    case eCSSProperty_resizer:
+    case eCSSProperty_cursor:
+    case eCSSProperty_force_broken_image_icons: {
+      CSS_ENSURE(UserInterface) {
+        switch (aProperty) {
+          case eCSSProperty_user_input:       theUserInterface->mUserInput = aValue;      break;
+          case eCSSProperty_user_modify:      theUserInterface->mUserModify = aValue;     break;
+          case eCSSProperty_user_select:      theUserInterface->mUserSelect = aValue;     break;
+          case eCSSProperty_key_equivalent: 
+            CSS_ENSURE_DATA(theUserInterface->mKeyEquivalent, nsCSSValueList) {
+              theUserInterface->mKeyEquivalent->mValue = aValue;
+              CSS_IF_DELETE(theUserInterface->mKeyEquivalent->mNext);
+            }
+            break;
+          case eCSSProperty_user_focus:       theUserInterface->mUserFocus = aValue;      break;
+          case eCSSProperty_resizer:          theUserInterface->mResizer = aValue;        break;
+          case eCSSProperty_cursor:
+            CSS_ENSURE_DATA(theUserInterface->mCursor, nsCSSValueList) {
+              theUserInterface->mCursor->mValue = aValue;
+              CSS_IF_DELETE(theUserInterface->mCursor->mNext);
+            }
+            break;
+          case eCSSProperty_force_broken_image_icons: theUserInterface->mForceBrokenImageIcon = aValue; break;
+
+          CSS_BOGUS_DEFAULT; // make compiler happy
+        }
+      }
+      break;
+    }
+
+
+The GetValue method must be similarly modified +:
+
    // nsCSSUserInterface
+    case eCSSProperty_user_input:
+    case eCSSProperty_user_modify:
+    case eCSSProperty_user_select:
+    case eCSSProperty_key_equivalent:
+    case eCSSProperty_user_focus:
+    case eCSSProperty_resizer:
+    case eCSSProperty_cursor:
+    case eCSSProperty_force_broken_image_icons: {
+      CSS_VARONSTACK_GET(UserInterface);
+      if (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;
+          case eCSSProperty_force_broken_image_icons: aValue = theUserInterface->mForceBrokenImageIcons; break;
+
+          CSS_BOGUS_DEFAULT; // make compiler happy
+        }
+      }
+      else {
+        aValue.Reset();
+      }
+      break;
+    }
+
+
+Finally modify +the 'List' method to output the property value.
+
void nsCSSUserInterface::List(FILE* out, 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;
+  }
+
+  mForceBrokenImageIcon.AppendToString(buffer,eCSSProperty_force_broken_image_icons);
+
+  fputs(NS_LossyConvertUTF16toASCII(buffer).get(), out);
+}
+
+
+ +

CSS Parser

+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 +ParseBackground +, for and example). We are just adding a simple single-value property here.
+
+Open nsCSSParser.cpp and look for the method +ParseSingleProperty +. 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 +variant flag + that makes sense for your property. In our case
+
+
  case eCSSProperty_force_broken_image_icons:
+
    return ParsePositiveVariant(aErrorCode, aValue, VARIANT_INTEGER, nullptr);
+This will parse the value as a positive integer value, which is what we want.
+
+

Style Context

+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.
+
+First look into +nsStyleStruct.h + 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 data member + to hold the value: +
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]
+  uint8_t   mForceBrokenImageIcon; // [reset]  (0 if not forcing, otherwise forcing)
+};
+
+In the implementation file +nsStyleContext.cpp +add the new data member to the constructors of the style struct and the CalcDifference +method, which must return the correct style-change hint when a change to +your new property is detected. The constructor changes are obvious, but here +is the CalcDifference change for our example:
+
int32_t nsStyleUIReset::CalcDifference(const nsStyleUIReset& aOther) const
+{
+  if (mForceBrokenImageIcon == aOther.mForceBrokenImageIcon) {
+    if (mResizer == aOther.mResizer) {
+      if (mUserSelect == aOther.mUserSelect) {
+        if (mKeyEquivalent == aOther.mKeyEquivalent) {
+          return NS_STYLE_HINT_NONE;
+        }
+        return NS_STYLE_HINT_CONTENT;
+      }
+      return NS_STYLE_HINT_VISUAL;
+    }
+    return NS_STYLE_HINT_VISUAL;
+  }
+  return nsChangeHint_ReconstructFrame;
+}
+
+

CSSStyleRule

+The nsCSSStyleRule must be updated to manage mapping the declaration to the +style struct. In the file +nsCSSStyleRule.cpp +, locate the Declaration mapping function corresponding to the style struct +you have added your property to. For example, we +update +MapUIForDeclaration:
+
static nsresult
+MapUIForDeclaration(nsCSSDeclaration* aDecl, const nsStyleStructID& aID, nsCSSUserInterface& aUI)
+{
+  if (!aDecl)
+    return NS_OK; // The rule must have a declaration.
+
+  nsCSSUserInterface* ourUI = (nsCSSUserInterface*)aDecl->GetData(kCSSUserInterfaceSID);
+  if (!ourUI)
+    return NS_OK; // We don't have any rules for UI.
+
+  if (aID == eStyleStruct_UserInterface) {
+    if (aUI.mUserFocus.GetUnit() == eCSSUnit_Null && ourUI->mUserFocus.GetUnit() != eCSSUnit_Null)
+      aUI.mUserFocus = ourUI->mUserFocus;
+    
+    if (aUI.mUserInput.GetUnit() == eCSSUnit_Null && ourUI->mUserInput.GetUnit() != eCSSUnit_Null)
+      aUI.mUserInput = ourUI->mUserInput;
+
+    if (aUI.mUserModify.GetUnit() == eCSSUnit_Null && ourUI->mUserModify.GetUnit() != eCSSUnit_Null)
+      aUI.mUserModify = ourUI->mUserModify;
+
+    if (!aUI.mCursor && ourUI->mCursor)
+      aUI.mCursor = ourUI->mCursor;
+
+
+  }
+  else if (aID == eStyleStruct_UIReset) {
+    if (aUI.mUserSelect.GetUnit() == eCSSUnit_Null && ourUI->mUserSelect.GetUnit() != eCSSUnit_Null)
+      aUI.mUserSelect = ourUI->mUserSelect;
+   
+    if (!aUI.mKeyEquivalent && ourUI->mKeyEquivalent)
+      aUI.mKeyEquivalent = ourUI->mKeyEquivalent;
+
+    if (aUI.mResizer.GetUnit() == eCSSUnit_Null && ourUI->mResizer.GetUnit() != eCSSUnit_Null)
+      aUI.mResizer = ourUI->mResizer;
+    
+    if (aUI.mForceBrokenImageIcon.GetUnit() == eCSSUnit_Null && ourUI->mForceBrokenImageIcon.GetUnit() == eCSSUnit_Integer)
+      aUI.mForceBrokenImageIcon = ourUI->mForceBrokenImageIcon;
+  }
+
+  return NS_OK;
+
+}
+
+

Rule Node

+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:
+
static const PropertyCheckData UIResetCheckProperties[] = {
+  CHECKDATA_PROP(nsCSSUserInterface, mUserSelect, CHECKDATA_VALUE, PR_FALSE),
+  CHECKDATA_PROP(nsCSSUserInterface, mResizer, CHECKDATA_VALUE, PR_FALSE),
+  CHECKDATA_PROP(nsCSSUserInterface, mKeyEquivalent, CHECKDATA_VALUELIST, PR_FALSE)
+  CHECKDATA_PROP(nsCSSUserInterface, mForceBrokenImageIcon, CHECKDATA_VALUE, PR_FALSE)
+};
+
+The first two arguments correspond to the structure and data member from +the CSSDeclaration, the third is the data type, the fourth indicates +whether it is a coord value that uses an explicit inherit value on the +style data struct that must be computed by layout.
+
+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:
+
  ...
+  // resizer: auto, none, enum, inherit
+  if (eCSSUnit_Enumerated == uiData.mResizer.GetUnit()) {
+    ui->mResizer = uiData.mResizer.GetIntValue();
+  }
+  else if (eCSSUnit_Auto == uiData.mResizer.GetUnit()) {
+    ui->mResizer = NS_STYLE_RESIZER_AUTO;
+  }
+  else if (eCSSUnit_None == uiData.mResizer.GetUnit()) {
+    ui->mResizer = NS_STYLE_RESIZER_NONE;
+  }
+  else if (eCSSUnit_Inherit == uiData.mResizer.GetUnit()) {
+    inherited = PR_TRUE;
+    ui->mResizer = parentUI->mResizer;
+  }
+
+  // force-broken-image-icons: integer, inherit, initial
+  if (eCSSUnit_Integer == uiData.mForceBrokenImageIcons.GetUnit()) {
+    ui->mForceBrokenImageIcons = uiData.mForceBrokenImageIcons.GetIntValue();
+  } else if (eCSSUnit_Inherit == uiData.mForceBrokenImageIcons.GetUnit()) {
+    inherited = PR_TRUE;
+    ui->mForceBrokenImageIcons = parentUI->mForceBrokenImageIcons;
+  } else if (eCSSUnit_Initial == uiData.mForceBrokenImageIcons.GetUnit()) {
+    ui->mForceBrokenImageIcons = 0;
+  }
+  
+  if (inherited)
+    // We inherited, and therefore can't be cached in the rule node.  We have to be put right on the
+    // style context.
+    aContext->SetStyle(eStyleStruct_UIReset, *ui);
+  else {
+    // We were fully specified and can therefore be cached right on the rule node.
+    if (!aHighestNode->mStyleData.mResetData)
+      aHighestNode->mStyleData.mResetData = new (mPresContext) nsResetStyleData;
+    aHighestNode->mStyleData.mResetData->mUIData = ui;
+    // Propagate the bit down.
+    PropagateDependentBit(NS_STYLE_INHERIT_UI_RESET, aHighestNode);
+  }
+  ...
+
+

DOM

+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 +nsIDOMCSSStyleDeclaration and CSS2Properties. +By the magic of C++ pre-processing, the +CSS2Properties bits will be implemented automatically when you +add your property to +nsCSSPropList.h. +

Layout

+OK, finally the style system is supporting the new property. It is time to +actually make use of it now.
+
+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:
+
        PRBool forceIcon = PR_FALSE;
+
+        if (StyleUIReset()->mForceBrokenImageIcon) {
+          forceIcon = PR_TRUE;
+        }
+
+
+Create some testcases with style rules that use the new property, make sure +it is being parsed correctly. Test it in an external stylesheet and in inline +style. Test that it is inherited correctly, or not inherited as appropriate +to your property. Update this document with any further details, or correcting +any errors.
+ -- cgit v1.2.3