Bug 1463600 - Implement CSS 'contain: style'. draft
authorYusuf Sermet <ysermet@mozilla.com>
Tue, 26 Jun 2018 00:01:43 -0700
changeset 827104 0308fbf576865f09986e6a3ae740a908ab31829c
parent 813385 455fe743bc831ef1a2e3306175f4682ce771e997
push id118466
push userbmo:ysermet@mozilla.com
push dateTue, 07 Aug 2018 02:05:09 +0000
bugs1463600
milestone62.0a1
Bug 1463600 - Implement CSS 'contain: style'. MozReview-Commit-ID: 5w6WvUSf1oO
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/base/nsCounterManager.cpp
layout/base/nsCounterManager.h
layout/base/nsGenConList.h
layout/base/nsQuoteList.cpp
layout/base/nsQuoteList.h
layout/style/ComputedStyle.h
layout/style/nsStyleStruct.h
servo/components/style/properties/computed_value_flags.rs
servo/components/style/style_adjuster.rs
servo/ports/geckolib/glue.rs
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -120,16 +120,19 @@
 #include "mozilla/dom/SVGTests.h"
 #include "nsSVGUtils.h"
 
 #include "nsRefreshDriver.h"
 #include "nsTextNode.h"
 #include "ActiveLayerTracker.h"
 #include "nsIPresShellInlines.h"
 
+// XXXyusuf: delete this, we're no longer using it here. 
+#include "nsComputedDOMStyle.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // An alias for convenience.
 static const nsIFrame::ChildListID kPrincipalList = nsIFrame::kPrincipalList;
 
 nsIFrame*
 NS_NewHTMLCanvasFrame (nsIPresShell* aPresShell, ComputedStyle* aStyle);
@@ -1652,18 +1655,19 @@ nsCSSFrameConstructor::nsCSSFrameConstru
   }
 #endif
 }
 
 void
 nsCSSFrameConstructor::NotifyDestroyingFrame(nsIFrame* aFrame)
 {
   if (aFrame->GetStateBits() & NS_FRAME_GENERATED_CONTENT) {
-    if (mQuoteList.DestroyNodesFor(aFrame))
+    if (mQuoteList.DestroyNodesFor(aFrame)) {
       QuotesDirty();
+    }
   }
 
   if (aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE) &&
       mCounterManager.DestroyNodesFor(aFrame)) {
     // Technically we don't need to update anything if we destroyed only
     // USE nodes.  However, this is unlikely to happen in the real world
     // since USE nodes generally go along with INCREMENT nodes.
     CountersDirty();
@@ -1749,40 +1753,61 @@ nsCSSFrameConstructor::CreateGeneratedCo
       nsCOMPtr<nsIContent> content;
       NS_NewAttributeContent(mDocument->NodeInfoManager(),
                              attrNameSpace, attrName, getter_AddRefs(content));
       return content.forget();
     }
 
     case eStyleContentType_Counter:
     case eStyleContentType_Counters: {
+      uint32_t currentStyleLevel = 0,
+               resetFrom = 1; // 0 means do not reset
+
+      if(mActiveElementForContainStyleRoots != aParentContent) {
+        SetContainStyleParameters(aParentContent, resetFrom, mContainStyleRootsForCounters);
+        mActiveElementForContainStyleRoots = aParentContent;
+        mResetFrom = resetFrom;
+      }
+      else {
+        resetFrom = 0;
+      }
+      currentStyleLevel = mContainStyleRootsForCounters.size();
+
       nsStyleContentData::CounterFunction* counters = data.GetCounters();
       nsCounterList* counterList =
         mCounterManager.CounterListFor(counters->mIdent);
 
       nsCounterUseNode* node =
         new nsCounterUseNode(counters, aContentIndex,
-                             type == eStyleContentType_Counters);
+                             type == eStyleContentType_Counters,
+                             currentStyleLevel, resetFrom);
 
       nsGenConInitializer* initializer =
         new nsGenConInitializer(node, counterList,
                                 &nsCSSFrameConstructor::CountersDirty);
       return CreateGenConTextNode(aState, EmptyString(), &node->mText,
                                   initializer);
     }
 
     case eStyleContentType_OpenQuote:
     case eStyleContentType_CloseQuote:
     case eStyleContentType_NoOpenQuote:
     case eStyleContentType_NoCloseQuote: {
+      uint32_t currentStyleLevel = 0,
+               resetFrom = 1; // 0 means do not reset
+
+      SetContainStyleParameters(aParentContent, resetFrom, mContainStyleRootsForQuotes);
+      currentStyleLevel = mContainStyleRootsForQuotes.size();
+
       nsQuoteNode* node =
-        new nsQuoteNode(type, aContentIndex);
+        new nsQuoteNode(type, aContentIndex, currentStyleLevel, resetFrom);
 
       nsGenConInitializer* initializer =
-        new nsGenConInitializer(node, &mQuoteList,
+        new nsGenConInitializer(node,
+                                &mQuoteList,
                                 &nsCSSFrameConstructor::QuotesDirty);
       return CreateGenConTextNode(aState, EmptyString(), &node->mText,
                                   initializer);
     }
 
     case eStyleContentType_AltContent: {
       // Use the "alt" attribute; if that fails and the node is an HTML
       // <input>, try the value attribute and then fall back to some default
@@ -3478,16 +3503,23 @@ nsCSSFrameConstructor::ConstructTextFram
   // We never need to create a view for a text frame.
 
   if (newFrame->IsGeneratedContentFrame()) {
     nsAutoPtr<nsGenConInitializer> initializer;
     initializer =
       static_cast<nsGenConInitializer*>(
         aContent->UnsetProperty(nsGkAtoms::genConInitializerProperty));
     if (initializer) {
+      // XXXyusuf: This is a partial fix for having two consecutive contain:style'd groups at the same
+      // style level, but in different scopes. They should not affect each other (yet, it does)
+      // See bug xxx comment x
+      if (mLastCounterNodesNeedSwap) {
+        initializer->mNode->mResetFrom = 0;
+        mLastCounterNodesNeedSwap = false;
+      }
       if (initializer->mNode->InitTextFrame(initializer->mList,
               FindAncestorWithGeneratedContentPseudo(newFrame), newFrame)) {
         (this->*(initializer->mDirtyAll))();
       }
       initializer->mNode.forget();
     }
   }
 
@@ -4947,19 +4979,39 @@ nsCSSFrameConstructor::InitAndRestoreFra
   aNewFrame->Init(aContent, aParentFrame, nullptr);
   aNewFrame->AddStateBits(aState.mAdditionalStateBits);
 
   if (aState.mFrameState) {
     // Restore frame state for just the newly created frame.
     RestoreFrameStateFor(aNewFrame, aState.mFrameState);
   }
 
-  if (aAllowCounters &&
-      mCounterManager.AddCounterResetsAndIncrements(aNewFrame)) {
-    CountersDirty();
+  if (aAllowCounters && mCounterManager.HasCounterResetsAndIncrements(aNewFrame)) {
+      // Here in frameConstructor, have two variable of that class; one for quotes, one for counters
+      uint32_t currentStyleLevel = 0,
+               resetFrom = 1; // 0 means do not reset
+
+      Element* element = Element::FromNodeOrNull(aParentFrame->GetContent());
+      // This means that the previous node was belong to the same element
+      // No need to find contain style roots again
+      if(mActiveElementForContainStyleRoots != element) {
+        SetContainStyleParameters(element, resetFrom, mContainStyleRootsForCounters);
+        mActiveElementForContainStyleRoots = element;
+        mResetFrom = resetFrom;
+      }
+      else {
+        resetFrom = mResetFrom;
+        // resetFrom = 0;
+        mLastCounterNodesNeedSwap = true;
+      }
+      currentStyleLevel = mContainStyleRootsForCounters.size();
+
+      if (mCounterManager.AddCounterResetsAndIncrements(aNewFrame, currentStyleLevel, resetFrom)) {
+        CountersDirty();
+      }
   }
 }
 
 already_AddRefed<ComputedStyle>
 nsCSSFrameConstructor::ResolveComputedStyle(nsIContent* aContent)
 {
   ServoStyleSet* styleSet = mPresShell->StyleSet();
 
@@ -8343,16 +8395,22 @@ void
 nsCSSFrameConstructor::WillDestroyFrameTree()
 {
 #if defined(DEBUG_dbaron_off)
   mCounterManager.Dump();
 #endif
 
   mIsDestroyingFrameTree = true;
 
+  // XXXyusuf: Is using clear() enough here? We don't want
+  // to delete actual DOM element that pointer references anyway,
+  // we just need to empty the vector.
+  // Clear contain:style roots
+  mContainStyleRootsForCounters.clear();
+  mContainStyleRootsForQuotes.clear();
   // Prevent frame tree destruction from being O(N^2)
   mQuoteList.Clear();
   mCounterManager.Clear();
   nsFrameManager::Destroy();
 }
 
 //STATIC
 
@@ -11976,16 +12034,84 @@ nsCSSFrameConstructor::GenerateChildFram
     }
   }
 #endif
 
   // call XBL constructors after the frames are created
   mPresShell->GetDocument()->BindingManager()->ProcessAttachedQueue();
 }
 
+// XXXyusuf: create a class instead to hold all the relevant values for tracking contain:style
+void
+nsCSSFrameConstructor::SetContainStyleParameters(Element* aParentContent,
+                                                 uint32_t& aResetFrom,
+                                                 std::vector<Element*>& aOldContainStyleRoots)
+{
+  std::vector<Element*> newContainStyleRoots;
+  FindContainStyleRootsAndLevel(aParentContent, aResetFrom, aOldContainStyleRoots, newContainStyleRoots);
+  if (aResetFrom -1 == aOldContainStyleRoots.size()) {
+    aResetFrom = 0;
+  }
+
+  // Remove contain:style ancestors that are no longer in our scope
+  if (aResetFrom != 0 && aResetFrom -1 < aOldContainStyleRoots.size()) {
+    aOldContainStyleRoots.erase(aOldContainStyleRoots.begin(), aOldContainStyleRoots.begin() + (aOldContainStyleRoots.size() - aResetFrom + 1));
+  }
+  // Add contain:style ancestors that are now in our scope
+  aOldContainStyleRoots.insert(aOldContainStyleRoots.begin(), newContainStyleRoots.begin(), newContainStyleRoots.end());
+}
+
+void
+nsCSSFrameConstructor::FindContainStyleRootsAndLevel(Element* aParent,
+                                                     uint32_t& aResetFrom,
+                                                     std::vector<Element*>& aOldContainStyleRoots,
+                                                     std::vector<Element*>& aNewContainStyleRoots)
+{
+  Element* ancestor = aParent;
+  if (!ancestor) {
+    return;
+  }
+
+  ServoStyleSet* styleSet = mPresShell->StyleSet();
+  RefPtr<ComputedStyle> computedStyle = styleSet->
+    ResolveStyleFor(ancestor, nullptr, LazyComputeBehavior::Allow);
+
+  if (!computedStyle ||
+      !computedStyle->SelfOrAncestorHasContainStyle()) {
+    // We're not contain style.
+    return;
+  }
+
+  while (computedStyle &&
+         !computedStyle->StyleDisplay()->IsContainStyle()) {
+    ancestor = ancestor->GetFlattenedTreeParentElement();
+    MOZ_ASSERT(ancestor, "Weird, we should've found element with contain:style");
+    computedStyle =
+      styleSet->ResolveStyleFor(ancestor, nullptr, LazyComputeBehavior::Allow);
+  }
+
+  // XXXyusuf: Do we have a generic function for searching in vector and returning the index?
+  // XXXyusuf: using iterators and std funtions is not shorter. Just use a for-loop for searching
+  // Might be cleaner to read
+  std::vector<Element*>::iterator locationInVector;
+  // first element in vector is the deepest contain:style, if nested
+  locationInVector = find(aOldContainStyleRoots.begin(), aOldContainStyleRoots.end(), ancestor);
+  if (locationInVector != aOldContainStyleRoots.end()) {
+    // We found a match from previous contain:style ancestors. So, we don't
+    // need to continue searching anymore, we know what we'll find.
+    // Just 'return' here, we have the correct aResetFrom and aNewContainStyleRoots
+    aResetFrom = std::distance(locationInVector, aOldContainStyleRoots.end()) + 1;
+    return;
+  }
+
+  aNewContainStyleRoots.push_back(ancestor);
+  FindContainStyleRootsAndLevel(ancestor->GetFlattenedTreeParentElement(), aResetFrom,
+                                aOldContainStyleRoots, aNewContainStyleRoots);
+}
+
 //////////////////////////////////////////////////////////
 // nsCSSFrameConstructor::FrameConstructionItem methods //
 //////////////////////////////////////////////////////////
 bool
 nsCSSFrameConstructor::
 FrameConstructionItem::IsWhitespace(nsFrameConstructorState& aState) const
 {
   MOZ_ASSERT(aState.mCreatingExtraFrames ||
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -461,24 +461,24 @@ private:
    * Create a content node for the given generated content style.
    * The caller takes care of making it SetIsNativeAnonymousRoot, binding it
    * to the document, and creating frames for it.
    * @param aParentContent is the node that has the before/after style
    * @param aComputedStyle is the 'before' or 'after' pseudo-element style.
    * @param aContentIndex is the index of the content item to create
    */
   already_AddRefed<nsIContent> CreateGeneratedContent(nsFrameConstructorState& aState,
-                                                      mozilla::dom::Element* aParentContent,
-                                                      ComputedStyle* aComputedStyle,
+                                                      Element*        aParentContent,
+                                                      ComputedStyle*  aComputedStyle,
                                                       uint32_t        aContentIndex);
 
   // aFrame may be null; this method doesn't use it directly in any case.
   void CreateGeneratedContentItem(nsFrameConstructorState&   aState,
                                   nsContainerFrame*          aFrame,
-                                  mozilla::dom::Element*     aContent,
+                                  Element*                   aContent,
                                   ComputedStyle*             aComputedStyle,
                                   CSSPseudoElementType       aPseudoElement,
                                   FrameConstructionItemList& aItems);
 
   // This method can change aFrameList: it can chop off the beginning and put
   // it in aParentFrame while putting the remainder into a ib-split sibling of
   // aParentFrame.  aPrevSibling must be the frame after which aFrameList is to
   // be placed on aParentFrame's principal child list.  It may be null if
@@ -2131,16 +2131,28 @@ private:
   void ConstructAnonymousContentForCanvas(nsFrameConstructorState& aState,
                                           nsIFrame* aFrame,
                                           nsIContent* aDocElement);
 
 public:
 
   friend class nsFrameConstructorState;
 
+  // Sets the parameters used in tracking style containment
+  void  SetContainStyleParameters(Element* aParentContent,
+                                  uint32_t& aResetFrom,
+                                  std::vector<Element*>& aOldContainStyleRoots);
+
+  // Helper Function for SetContainStyleParameters
+  // Finds all style-contained ancestor elements
+  void  FindContainStyleRootsAndLevel(Element* aParent,
+                                      uint32_t& aResetFrom,
+                                      std::vector<Element*>& aOldContainStyleRoots,
+                                      std::vector<Element*>& aNewContainStyleRoots);
+
 private:
   // For allocating FrameConstructionItems from the mFCItemPool arena.
   friend struct FrameConstructionItem;
   void* AllocateFCItem();
   void FreeFCItem(FrameConstructionItem*);
 
   nsIDocument*        mDocument;  // Weak ref
 
@@ -2157,16 +2169,24 @@ private:
   nsIFrame*           mPageSequenceFrame;
 
   // FrameConstructionItem arena + list of freed items available for re-use.
   mozilla::ArenaAllocator<4096, 8> mFCItemPool;
   struct FreeFCItemLink { FreeFCItemLink* mNext; };
   FreeFCItemLink* mFirstFreeFCItem;
   size_t mFCItemsInUse;
 
+  // Track contain:style
+  std::vector<Element*> mContainStyleRootsForQuotes;
+  std::vector<Element*> mContainStyleRootsForCounters;
+  // XXXyusuf: below 3 won't be needed if we create a class
+  Element*              mActiveElementForContainStyleRoots;
+  uint32_t              mResetFrom;
+  bool                  mLastCounterNodesNeedSwap : 1;
+
   nsQuoteList         mQuoteList;
   nsCounterManager    mCounterManager;
   // Current ProcessChildren depth.
   uint16_t            mCurrentDepth;
   bool                mQuotesDirty : 1;
   bool                mCountersDirty : 1;
   bool                mIsDestroyingFrameTree : 1;
   // This is true if mDocElementContainingBlock supports absolute positioning
--- a/layout/base/nsCounterManager.cpp
+++ b/layout/base/nsCounterManager.cpp
@@ -13,16 +13,36 @@
 #include "nsBulletFrame.h" // legacy location for list style type to text code
 #include "nsContentUtils.h"
 #include "nsIContent.h"
 #include "nsTArray.h"
 #include "mozilla/dom/Text.h"
 
 using namespace mozilla;
 
+void
+nsCounterNode::AssignOrResetStyleLevel(uint32_t aLevel, int32_t aAssignedValue) {
+  if (mResetFrom != 0 && aLevel >= mResetFrom){
+    // Reset these levels; they are not in current counter's style containing scope.
+    mNodeValues[aLevel] = nsCounterList::ValueBeforeForCustomLevelWithReset(this, mResetFrom-1);
+  }
+  else {
+    // Main path, either inherit or update (i.e. counter-reset, counter-increment) the current counter value.
+    mNodeValues[aLevel] = aAssignedValue;
+  }
+}
+
+void
+nsCounterNode::InheritStyleLevels() {
+  for (uint32_t level = 0; level <= mStyleLevel; level++) {
+    AssignOrResetStyleLevel(level,
+                            nsCounterList::ValueBeforeForCustomLevel(this, level));
+  }
+}
+
 bool
 nsCounterUseNode::InitTextFrame(nsGenConList* aList,
                                 nsIFrame* aPseudoFrame,
                                 nsIFrame* aTextFrame)
 {
   nsCounterNode::InitTextFrame(aList, aPseudoFrame, aTextFrame);
 
   nsCounterList* counterList = static_cast<nsCounterList*>(aList);
@@ -49,31 +69,33 @@ nsCounterUseNode::InitTextFrame(nsGenCon
 
 // assign the correct |mValueAfter| value to a node that has been inserted
 // Should be called immediately after calling |Insert|.
 void
 nsCounterUseNode::Calc(nsCounterList* aList)
 {
   NS_ASSERTION(!aList->IsDirty(),
                "Why are we calculating with a dirty list?");
-  mValueAfter = nsCounterList::ValueBefore(this);
+  InheritStyleLevels();
 }
 
 // assign the correct |mValueAfter| value to a node that has been inserted
 // Should be called immediately after calling |Insert|.
 void
 nsCounterChangeNode::Calc(nsCounterList* aList)
 {
   NS_ASSERTION(!aList->IsDirty(), "Why are we calculating with a dirty list?");
   if (mType == RESET) {
-    mValueAfter = mChangeValue;
+    InheritStyleLevels();
+    mNodeValues[mStyleLevel] = mChangeValue;
   } else {
     NS_ASSERTION(mType == INCREMENT, "invalid type");
-    mValueAfter = nsCounterManager::IncrementCounter(nsCounterList::ValueBefore(this),
-                                                     mChangeValue);
+    InheritStyleLevels();
+    mNodeValues[mStyleLevel] = nsCounterManager::IncrementCounter(mNodeValues[mStyleLevel],
+                                                                  mChangeValue);
   }
 }
 
 // The text that should be displayed for this counter.
 void
 nsCounterUseNode::GetText(nsString& aResult)
 {
   aResult.Truncate();
@@ -88,17 +110,17 @@ nsCounterUseNode::GetText(nsString& aRes
   }
 
   WritingMode wm = mPseudoFrame ?
     mPseudoFrame->GetWritingMode() : WritingMode();
   for (uint32_t i = stack.Length() - 1;; --i) {
     nsCounterNode* n = stack[i];
     nsAutoString text;
     bool isTextRTL;
-    mCounterStyle->GetCounterText(n->mValueAfter, wm, text, isTextRTL);
+    mCounterStyle->GetCounterText(n->mNodeValues[n->mStyleLevel], wm, text, isTextRTL);
     aResult.Append(text);
     if (i == 0) {
       break;
     }
     aResult.Append(mSeparator);
   }
 }
 
@@ -181,49 +203,68 @@ nsCounterList::RecalcAll()
         useNode->GetText(text);
         useNode->mText->SetData(text, IgnoreErrors());
       }
     }
   }
 }
 
 bool
-nsCounterManager::AddCounterResetsAndIncrements(nsIFrame* aFrame)
+nsCounterManager::HasCounterResetsAndIncrements(nsIFrame* aFrame,
+                                                const nsStyleContent* aStyleContent)
+{
+  if (!aStyleContent) {
+    aStyleContent = aFrame->StyleContent();
+  }
+
+  if (!aStyleContent->CounterIncrementCount() &&
+      !aStyleContent->CounterResetCount()) {
+    MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE));
+    return false;
+  }
+  return true;
+}
+
+bool
+nsCounterManager::AddCounterResetsAndIncrements(nsIFrame* aFrame,
+                                                uint32_t aStyleLevel,
+                                                uint32_t aResetFrom)
 {
   const nsStyleContent* styleContent = aFrame->StyleContent();
-  if (!styleContent->CounterIncrementCount() &&
-      !styleContent->CounterResetCount()) {
-    MOZ_ASSERT(!aFrame->HasAnyStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE));
+  if (!HasCounterResetsAndIncrements(aFrame, styleContent)) {
     return false;
   }
 
   aFrame->AddStateBits(NS_FRAME_HAS_CSS_COUNTER_STYLE);
 
   // Add in order, resets first, so all the comparisons will be optimized
   // for addition at the end of the list.
   int32_t i, i_end;
   bool dirty = false;
   for (i = 0, i_end = styleContent->CounterResetCount(); i != i_end; ++i) {
     dirty |= AddResetOrIncrement(aFrame, i, styleContent->CounterResetAt(i),
-                                 nsCounterChangeNode::RESET);
+                                 nsCounterChangeNode::RESET, aStyleLevel, aResetFrom);
   }
   for (i = 0, i_end = styleContent->CounterIncrementCount(); i != i_end; ++i) {
     dirty |= AddResetOrIncrement(aFrame, i, styleContent->CounterIncrementAt(i),
-                                 nsCounterChangeNode::INCREMENT);
+                                 nsCounterChangeNode::INCREMENT, aStyleLevel, aResetFrom);
   }
   return dirty;
 }
 
 bool
-nsCounterManager::AddResetOrIncrement(nsIFrame* aFrame, int32_t aIndex,
+nsCounterManager::AddResetOrIncrement(nsIFrame* aFrame,
+                                      int32_t aIndex,
                                       const nsStyleCounterData& aCounterData,
-                                      nsCounterNode::Type aType)
+                                      nsCounterNode::Type aType,
+                                      uint32_t aStyleLevel,
+                                      uint32_t aResetFrom)
 {
   nsCounterChangeNode* node =
-    new nsCounterChangeNode(aFrame, aType, aCounterData.mValue, aIndex);
+    new nsCounterChangeNode(aFrame, aType, aCounterData.mValue, aIndex, aStyleLevel, aResetFrom);
 
   nsCounterList* counterList = CounterListFor(aCounterData.mCounter);
   counterList->Insert(node);
   if (!counterList->IsLast(node)) {
     // Tell the caller it's responsible for recalculating the entire
     // list.
     counterList->SetDirty();
     return true;
@@ -245,16 +286,17 @@ nsCounterManager::CounterListFor(const n
   });
 }
 
 void
 nsCounterManager::RecalcAll()
 {
   for (auto iter = mNames.Iter(); !iter.Done(); iter.Next()) {
     nsCounterList* list = iter.UserData();
+
     if (list->IsDirty()) {
       list->RecalcAll();
     }
   }
 }
 
 void
 nsCounterManager::SetAllDirty()
@@ -292,17 +334,18 @@ nsCounterManager::Dump()
     nsCounterList* list = iter.UserData();
     int32_t i = 0;
     for (nsCounterNode* node = list->First(); node; node = list->Next(node)) {
       const char* types[] = { "RESET", "INCREMENT", "USE" };
       printf("  Node #%d @%p frame=%p index=%d type=%s valAfter=%d\n"
              "       scope-start=%p scope-prev=%p",
              i++, (void*)node, (void*)node->mPseudoFrame,
              node->mContentIndex, types[node->mType],
-             node->mValueAfter, (void*)node->mScopeStart,
+             node->mNodeValues[node->mResetFrom],
+             (void*)node->mScopeStart,
              (void*)node->mScopePrev);
       if (node->mType == nsCounterNode::USE) {
         nsAutoString text;
         node->UseNode()->GetText(text);
         printf(" text=%s", NS_ConvertUTF16toUTF8(text).get());
       }
       printf("\n");
     }
--- a/layout/base/nsCounterManager.h
+++ b/layout/base/nsCounterManager.h
@@ -23,19 +23,16 @@ struct nsCounterNode : public nsGenConNo
     enum Type {
         RESET,     // a "counter number" pair in 'counter-reset'
         INCREMENT, // a "counter number" pair in 'counter-increment'
         USE        // counter() or counters() in 'content'
     };
 
     Type mType;
 
-    // Counter value after this node
-    int32_t mValueAfter;
-
     // mScopeStart points to the node (usually a RESET, but not in the
     // case of an implied 'counter-reset') that created the scope for
     // this element (for a RESET, its outer scope, i.e., the one it is
     // inside rather than the one it creates).
 
     // May be null for all types, but only when mScopePrev is also null.
     // Being null for a non-RESET means that it is an implied
     // 'counter-reset'.  Being null for a RESET means it has no outer
@@ -57,40 +54,56 @@ struct nsCounterNode : public nsGenConNo
     inline nsCounterChangeNode* ChangeNode();
 
     // For RESET and INCREMENT nodes, aPseudoFrame need not be a
     // pseudo-element, and aContentIndex represents the index within the
     // 'counter-reset' or 'counter-increment' property instead of within
     // the 'content' property but offset to ensure that (reset,
     // increment, use) sort in that order.  (This slight weirdness
     // allows sharing a lot of code with 'quotes'.)
-    nsCounterNode(int32_t aContentIndex, Type aType)
-        : nsGenConNode(aContentIndex)
+    nsCounterNode(int32_t aContentIndex, Type aType, uint32_t aStyleLevel, uint32_t aResetFrom)
+        : nsGenConNode(aContentIndex, aStyleLevel, aResetFrom)
         , mType(aType)
-        , mValueAfter(0)
         , mScopeStart(nullptr)
         , mScopePrev(nullptr)
     {
     }
 
     // to avoid virtual function calls in the common case
     inline void Calc(nsCounterList* aList);
+
+    // Inherit the valueAfter values for inactive contain:style levels
+    // For example, assume current node has mStyleLevel as 3, meaning it
+    // has 3 nested parent elements that has contain:style. If we previously
+    // had any counter nodes that weren't under contain:style at all, we'd
+    // like to keep track of where we were at that level, so that when we leave
+    // the contain:style'd group, we can correctly continue our counters.
+    void InheritStyleLevels();
+
+    // Assign this node's valueAfter value for the its current contain:style
+    // level (if there's no contain:style, than the level is 0).
+    // This function complements the above InheritStyleLevels function, and
+    // will most likely be used together. The reason they are seperate functions
+    // is because unlike quotes, in counters there are more than one way to update
+    // the value of a node (e.g. counter-reset, counter-increment, counter use)
+    void AssignOrResetStyleLevel(uint32_t aLevel, int32_t aAssignedValue);
+
 };
 
 struct nsCounterUseNode : public nsCounterNode {
     mozilla::CounterStylePtr mCounterStyle;
     nsString mSeparator;
 
     // false for counter(), true for counters()
     bool mAllCounters;
 
     // args go directly to member variables here and of nsGenConNode
     nsCounterUseNode(nsStyleContentData::CounterFunction* aCounterFunction,
-                     uint32_t aContentIndex, bool aAllCounters)
-        : nsCounterNode(aContentIndex, USE)
+                     uint32_t aContentIndex, bool aAllCounters, uint32_t aStyleLevel, uint32_t aResetFrom)
+        : nsCounterNode(aContentIndex, USE, aStyleLevel, aResetFrom)
         , mCounterStyle(aCounterFunction->mCounterStyle)
         , mSeparator(aCounterFunction->mSeparator)
         , mAllCounters(aAllCounters)
     {
         NS_ASSERTION(aContentIndex <= INT32_MAX, "out of range");
     }
 
     virtual bool InitTextFrame(nsGenConList* aList,
@@ -110,24 +123,26 @@ struct nsCounterChangeNode : public nsCo
     // |aPseudoFrame| is not necessarily a pseudo-element's frame, but
     // since it is for every other subclass of nsGenConNode, we follow
     // the naming convention here.
     // |aPropIndex| is the index of the value within the list in the
     // 'counter-increment' or 'counter-reset' property.
     nsCounterChangeNode(nsIFrame* aPseudoFrame,
                         nsCounterNode::Type aChangeType,
                         int32_t aChangeValue,
-                        int32_t aPropIndex)
+                        int32_t aPropIndex,
+                        uint32_t aStyleLevel,
+                        uint32_t aResetFrom)
         : nsCounterNode(// Fake a content index for resets and increments
                         // that comes before all the real content, with
                         // the resets first, in order, and then the increments.
                         aPropIndex + (aChangeType == RESET
                                         ? (INT32_MIN)
                                         : (INT32_MIN / 2)),
-                        aChangeType)
+                        aChangeType, aStyleLevel, aResetFrom)
         , mChangeValue(aChangeValue)
     {
         NS_ASSERTION(aPropIndex >= 0, "out of range");
         NS_ASSERTION(aChangeType == INCREMENT || aChangeType == RESET,
                      "bad type");
         mPseudoFrame = aPseudoFrame;
         CheckFrameAssertions();
     }
@@ -179,17 +194,25 @@ public:
     static nsCounterNode* Next(nsCounterNode* aNode) {
         return static_cast<nsCounterNode*>(nsGenConList::Next(aNode));
     }
     static nsCounterNode* Prev(nsCounterNode* aNode) {
         return static_cast<nsCounterNode*>(nsGenConList::Prev(aNode));
     }
 
     static int32_t ValueBefore(nsCounterNode* aNode) {
-        return aNode->mScopePrev ? aNode->mScopePrev->mValueAfter : 0;
+        return ValueBeforeForCustomLevel(aNode, aNode->mStyleLevel);
+    }
+
+    static int32_t ValueBeforeForCustomLevel(nsCounterNode* aNode, uint32_t aCustomLevel) {
+        return aNode->mScopePrev ? aNode->mScopePrev->GetNodeValue(aCustomLevel) : 0;
+    }
+
+    static int32_t ValueBeforeForCustomLevelWithReset(nsCounterNode* aNode, uint32_t aCustomLevel) {
+        return aNode->mScopePrev ? aNode->mScopePrev->mNodeValues[aCustomLevel] : 0;
     }
 
     // Correctly set |aNode->mScopeStart| and |aNode->mScopePrev|
     void SetScope(nsCounterNode *aNode);
 
     // Recalculate |mScopeStart|, |mScopePrev|, and |mValueAfter| for
     // all nodes and update text in text content nodes.
     void RecalcAll();
@@ -202,18 +225,21 @@ private:
 };
 
 /**
  * The counter manager maintains an |nsCounterList| for each named
  * counter to keep track of all scopes with that name.
  */
 class nsCounterManager {
 public:
+    // Returns true if frame has counter-reset or counter-increment
+    bool HasCounterResetsAndIncrements(nsIFrame* aFrame, const nsStyleContent* aStyleContent = NULL);
+
     // Returns true if dirty
-    bool AddCounterResetsAndIncrements(nsIFrame *aFrame);
+    bool AddCounterResetsAndIncrements(nsIFrame *aFrame, uint32_t aStyleLevel, uint32_t aResetFrom);
 
     // Gets the appropriate counter list, creating it if necessary.
     // Guaranteed to return non-null. (Uses an infallible hashtable API.)
     nsCounterList* CounterListFor(const nsAString& aCounterName);
 
     // Clean up data in any dirty counter lists.
     void RecalcAll();
 
@@ -251,17 +277,20 @@ public:
         // the maximum 32-bit integer.)
         if ((aIncrement > 0) != (newValue > aOldValue)) {
           newValue = aOldValue;
         }
         return newValue;
     }
 
 private:
-    // for |AddCounterResetsAndIncrements| only
-  bool AddResetOrIncrement(nsIFrame* aFrame, int32_t aIndex,
+  // for |AddCounterResetsAndIncrements| only
+  bool AddResetOrIncrement(nsIFrame* aFrame,
+                           int32_t aIndex,
                            const nsStyleCounterData& aCounterData,
-                           nsCounterNode::Type aType);
+                           nsCounterNode::Type aType,
+                           uint32_t aStyleLevel,
+                           uint32_t aResetFrom);
 
   nsClassHashtable<nsStringHashKey, nsCounterList> mNames;
 };
 
 #endif /* nsCounterManager_h_ */
--- a/layout/base/nsGenConList.h
+++ b/layout/base/nsGenConList.h
@@ -24,23 +24,39 @@ struct nsGenConNode : public mozilla::Li
   // but does not necessarily for |nsCounterChangeNode|s.
   nsIFrame* mPseudoFrame;
 
   // Index within the list of things specified by the 'content' property,
   // which is needed to do 'content: open-quote open-quote' correctly,
   // and needed for similar cases for counters.
   const int32_t mContentIndex;
 
+  // For quotes: List of quote depth before this quote, which is always non-negative.
+  // For counters: List of counter values after this node
+  // List represents the different levels of contain:style, if any.
+  // Index represents the number of contain:style's found on parents.
+  std::vector<int32_t> mNodeValues;
+
+  // Contain:style level of the node; 0 means no contain:style
+  uint32_t mStyleLevel;
+
+  // Reset all contain:style depths after this index (including)
+  // 0 means don't reset
+  uint32_t mResetFrom;
+
   // null for 'content:no-open-quote', 'content:no-close-quote' and for
   // counter nodes for increments and resets (rather than uses)
   RefPtr<nsTextNode> mText;
 
-  explicit nsGenConNode(int32_t aContentIndex)
+  explicit nsGenConNode(int32_t aContentIndex, uint32_t aStyleLevel, uint32_t aResetFrom)
     : mPseudoFrame(nullptr)
     , mContentIndex(aContentIndex)
+    , mNodeValues(aStyleLevel + 1, 0)
+    , mStyleLevel(aStyleLevel)
+    , mResetFrom(aResetFrom)
   {
   }
 
   /**
    * Finish initializing the generated content node once we know the
    * relevant text frame. This must be called just after
    * the textframe has been initialized. This need not be called at all
    * for nodes that don't generate text. This will generally set the
@@ -56,16 +72,36 @@ struct nsGenConNode : public mozilla::Li
   {
     mPseudoFrame = aPseudoFrame;
     CheckFrameAssertions();
     return false;
   }
 
   virtual ~nsGenConNode() {} // XXX Avoid, perhaps?
 
+  // Get node value for given contain:style level
+  //
+  // Note: Here lies both a runtime and a memory optimization, and a fix
+  // so we can have unlimited amounts of nested contain:style'd elements.
+  // This method allows us to hold only the relevant style levels for each
+  // node, rather than having to keep track of all possible levels, meaning
+  // that if a page have a million quotes/counters, and only one of them is
+  // under a 1000 nested contain:styled elements, than all one million
+  // node would need to keep a track of that 1000 style levels, just
+  // because of that one node. With this, only that one node will keep track
+  // of that 1000 levels, and thus we won't unnecessarily use memory.
+  //
+  // Note: It also reduces runtime for resetting logic.
+  int32_t GetNodeValue(uint32_t aStyleLevel) {
+    if (aStyleLevel >= mNodeValues.size()) {
+      return mNodeValues.back();
+    }
+    return mNodeValues[aStyleLevel];
+  }
+
 protected:
   void CheckFrameAssertions() {
     NS_ASSERTION(mContentIndex <
                    int32_t(mPseudoFrame->StyleContent()->ContentCount()),
                  "index out of range");
       // We allow negative values of mContentIndex for 'counter-reset' and
       // 'counter-increment'.
 
--- a/layout/base/nsQuoteList.cpp
+++ b/layout/base/nsQuoteList.cpp
@@ -64,58 +64,74 @@ nsQuoteNode::Text()
   }
   return result;
 }
 
 void
 nsQuoteList::Calc(nsQuoteNode* aNode)
 {
   if (aNode == FirstNode()) {
-    aNode->mDepthBefore = 0;
+    aNode->mNodeValues[aNode->mStyleLevel] = 0;
   } else {
-    aNode->mDepthBefore = Prev(aNode)->DepthAfter();
+    // Inherit the depth values for all style levels, only update the ones that
+    // have the same or higher style levels, i.e. contain:style'd element will
+    // read outside of its scope, but won't be able to affect it.
+    nsQuoteNode* prevNode = Prev(aNode);
+    for (uint32_t level = 0; level < aNode->mNodeValues.size(); level++) {
+      if (aNode->mResetFrom != 0 && level >= aNode->mResetFrom) {
+        // Reset the levels that reach here, because they are out of our contain:style scope right now.
+        aNode->mNodeValues[level] = prevNode->mNodeValues[aNode->mResetFrom-1];
+      } else {
+        // Main path to calculate the current quote depth
+        aNode->mNodeValues[level] =
+          level < prevNode->mStyleLevel ? prevNode->GetNodeValue(level)
+                                        : prevNode->DepthAfter(level);
+      }
+    }
   }
 }
 
 void
 nsQuoteList::RecalcAll()
 {
   for (nsQuoteNode* node = FirstNode(); node; node = Next(node)) {
-    int32_t oldDepth = node->mDepthBefore;
+    int32_t oldDepth = node->mNodeValues[node->mStyleLevel];
     Calc(node);
 
-    if (node->mDepthBefore != oldDepth && node->mText && node->IsRealQuote())
+    if (node->mNodeValues[node->mStyleLevel] != oldDepth &&
+        node->mText && node->IsRealQuote())
       node->mText->SetData(*node->Text(), IgnoreErrors());
   }
 }
 
 #ifdef DEBUG
 void
 nsQuoteList::PrintChain()
 {
   printf("Chain: \n");
   for (nsQuoteNode* node = FirstNode(); node; node = Next(node)) {
-    printf("  %p %d - ", static_cast<void*>(node), node->mDepthBefore);
+    printf("  %p %d - ", static_cast<void*>(node),
+                         node->mNodeValues[node->mStyleLevel]);
     switch(node->mType) {
         case (eStyleContentType_OpenQuote):
           printf("open");
           break;
         case (eStyleContentType_NoOpenQuote):
           printf("noOpen");
           break;
         case (eStyleContentType_CloseQuote):
           printf("close");
           break;
         case (eStyleContentType_NoCloseQuote):
           printf("noClose");
           break;
         default:
           printf("unknown!!!");
     }
-    printf(" %d - %d,", node->Depth(), node->DepthAfter());
+    printf(" %d - %d,", node->Depth(), node->DepthAfter(node->mStyleLevel));
     if (node->mText) {
       nsAutoString data;
       node->mText->GetData(data);
       printf(" \"%s\",", NS_ConvertUTF16toUTF8(data).get());
     }
     printf("\n");
   }
 }
--- a/layout/base/nsQuoteList.h
+++ b/layout/base/nsQuoteList.h
@@ -11,23 +11,19 @@
 
 #include "mozilla/Attributes.h"
 #include "nsGenConList.h"
 
 struct nsQuoteNode : public nsGenConNode {
   // open-quote, close-quote, no-open-quote, or no-close-quote
   const nsStyleContentType mType;
 
-  // Quote depth before this quote, which is always non-negative.
-  int32_t mDepthBefore;
-
-  nsQuoteNode(nsStyleContentType& aType, uint32_t aContentIndex)
-    : nsGenConNode(aContentIndex)
+  nsQuoteNode(nsStyleContentType& aType, uint32_t aContentIndex, uint32_t aStyleLevel, uint32_t aResetFrom)
+    : nsGenConNode(aContentIndex, aStyleLevel, aResetFrom)
     , mType(aType)
-    , mDepthBefore(0)
   {
     NS_ASSERTION(aType == eStyleContentType_OpenQuote ||
                  aType == eStyleContentType_CloseQuote ||
                  aType == eStyleContentType_NoOpenQuote ||
                  aType == eStyleContentType_NoCloseQuote,
                  "incorrect type");
     NS_ASSERTION(aContentIndex <= INT32_MAX, "out of range");
   }
@@ -51,34 +47,39 @@ struct nsQuoteNode : public nsGenConNode
     return mType == eStyleContentType_OpenQuote ||
            mType == eStyleContentType_CloseQuote;
   }
 
   // Depth of the quote for *this* node.  Either non-negative or -1.
   // -1 means this is a closing quote that tried to decrement the
   // counter below zero (which means no quote should be rendered).
   int32_t Depth() {
-    return IsOpenQuote() ? mDepthBefore : mDepthBefore - 1;
+    int32_t depthBefore = mNodeValues[mStyleLevel];
+    return IsOpenQuote() ? depthBefore : depthBefore - 1;
   }
 
   // always non-negative
-  int32_t DepthAfter() {
-    return IsOpenQuote() ? mDepthBefore + 1
-                         : (mDepthBefore == 0 ? 0 : mDepthBefore - 1);
+  // Returns the depth of the quote for *this* node for given
+  // contain:style level
+  int32_t DepthAfter(uint32_t aStyleLevel) {
+    int32_t depthBefore = GetNodeValue(aStyleLevel);
+    return IsOpenQuote() ? depthBefore + 1
+                         : (depthBefore == 0 ? 0 : depthBefore - 1);
   }
 
   // The text that should be displayed for this quote.
   const nsString* Text();
 };
 
 class nsQuoteList : public nsGenConList {
 private:
   nsQuoteNode* FirstNode() { return static_cast<nsQuoteNode*>(mList.getFirst()); }
 public:
-  // assign the correct |mDepthBefore| value to a node that has been inserted
+
+  // assign the correct |mNodeValues| values to the inserted node
   // Should be called immediately after calling |Insert|.
   void Calc(nsQuoteNode* aNode);
 
   nsQuoteNode* Next(nsQuoteNode* aNode) {
     return static_cast<nsQuoteNode*>(nsGenConList::Next(aNode));
   }
   nsQuoteNode* Prev(nsQuoteNode* aNode) {
     return static_cast<nsQuoteNode*>(nsGenConList::Prev(aNode));
--- a/layout/style/ComputedStyle.h
+++ b/layout/style/ComputedStyle.h
@@ -65,16 +65,17 @@ class ComputedStyle;
 
 enum class ComputedStyleBit : uint8_t
 {
   HasTextDecorationLines = 1 << 0,
   HasPseudoElementData = 1 << 1,
   SuppressLineBreak = 1 << 2,
   IsTextCombined = 1 << 3,
   RelevantLinkVisited = 1 << 4,
+  SelfOrAncestorHasContainStyle = 1 << 5
 };
 
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ComputedStyleBit)
 
 class ComputedStyle
 {
   using Bit = ComputedStyleBit;
 public:
@@ -157,16 +158,22 @@ public:
   // decoration lines?
   // Differs from nsStyleTextReset::HasTextDecorationLines, which tests
   // only the data for a single context.
   bool HasTextDecorationLines() const
   {
     return bool(mBits & Bit::HasTextDecorationLines);
   }
 
+  // Does this style context or any of its ancestors have 'contain: style' set?
+  bool SelfOrAncestorHasContainStyle() const
+  {
+    return bool(mBits & Bit::SelfOrAncestorHasContainStyle);
+  }
+
   // Whether any line break inside should be suppressed? If this returns
   // true, the line should not be broken inside, which means inlines act
   // as if nowrap is set, <br> is suppressed, and blocks are inlinized.
   // This bit is propogated to all children of line partitipants. It is
   // currently used by ruby to make its content frames unbreakable.
   // NOTE: for nsTextFrame, use nsTextFrame::ShouldSuppressLineBreak()
   // instead of this method.
   bool ShouldSuppressLineBreak() const
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2432,16 +2432,20 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 
   bool IsScrollableOverflow() const {
     // mOverflowX and mOverflowY always match when one of them is
     // NS_STYLE_OVERFLOW_VISIBLE or NS_STYLE_OVERFLOW_CLIP.
     return mOverflowX != NS_STYLE_OVERFLOW_VISIBLE &&
            mOverflowX != NS_STYLE_OVERFLOW_CLIP;
   }
 
+  bool IsContainStyle() const {
+    return (NS_STYLE_CONTAIN_STYLE & mContain);
+  }
+
   bool IsContainPaint() const {
     return (NS_STYLE_CONTAIN_PAINT & mContain) &&
            !IsInternalRubyDisplayType() &&
            !IsInternalTableStyleExceptCell();
   }
 
   /* Returns whether the element has the -moz-transform property
    * or a related property. */
--- a/servo/components/style/properties/computed_value_flags.rs
+++ b/servo/components/style/properties/computed_value_flags.rs
@@ -36,16 +36,19 @@ bitflags! {
 
         /// A flag used to mark styles under a relevant link that is also
         /// visited.
         const IS_RELEVANT_LINK_VISITED = 1 << 3;
 
         /// A flag used to mark styles which are a pseudo-element or under one.
         const IS_IN_PSEUDO_ELEMENT_SUBTREE = 1 << 4;
 
+        /// A flag used to mark styles which have contain:style or under one.
+        const SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE = 1 << 5;
+
         /// Whether this style inherits the `display` property.
         ///
         /// This is important because it may affect our optimizations to avoid
         /// computing the style of pseudo-elements, given whether the
         /// pseudo-element is generated depends on the `display` value.
         const INHERITS_DISPLAY = 1 << 6;
 
         /// Whether this style inherits the `content` property.
@@ -69,17 +72,18 @@ bitflags! {
 impl ComputedValueFlags {
     /// Flags that are unconditionally propagated to descendants.
     #[inline]
     fn inherited_flags() -> Self {
         ComputedValueFlags::IS_STYLE_IF_VISITED |
         ComputedValueFlags::IS_RELEVANT_LINK_VISITED |
         ComputedValueFlags::CAN_BE_FRAGMENTED |
         ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE |
-        ComputedValueFlags::HAS_TEXT_DECORATION_LINES
+        ComputedValueFlags::HAS_TEXT_DECORATION_LINES |
+        ComputedValueFlags::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE
     }
 
     /// Flags that may be propagated to descendants.
     #[inline]
     fn maybe_inherited_flags() -> Self {
         Self::inherited_flags() | ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK
     }
 
--- a/servo/components/style/style_adjuster.rs
+++ b/servo/components/style/style_adjuster.rs
@@ -187,16 +187,17 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
                 .mutate_box()
                 .set_adjusted_display(blockified_display, is_item_or_root);
         }
     }
 
     /// Compute a few common flags for both text and element's style.
     pub fn set_bits(&mut self) {
         let display = self.style.get_box().clone_display();
+        use properties::longhands::contain::SpecifiedValue;
 
         if !display.is_contents() &&
             !self.style
                 .get_text()
                 .clone_text_decoration_line()
                 .is_empty()
         {
             self.style
@@ -205,16 +206,26 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> {
         }
 
         if self.style.is_pseudo_element() {
             self.style
                 .flags
                 .insert(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE);
         }
 
+        if self.style
+            .get_box()
+            .clone_contain()
+            .contains(SpecifiedValue::STYLE)
+        {
+            self.style
+                .flags
+                .insert(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE);
+        }
+
         #[cfg(feature = "servo")]
         {
             if self.style.get_parent_column().is_multicol() {
                 self.style
                     .flags
                     .insert(ComputedValueFlags::CAN_BE_FRAGMENTED);
             }
         }
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -3117,16 +3117,19 @@ pub extern "C" fn Servo_ComputedValues_G
         result |= structs::ComputedStyleBit_SuppressLineBreak;
     }
     if flags.contains(ComputedValueFlags::IS_TEXT_COMBINED) {
         result |= structs::ComputedStyleBit_IsTextCombined;
     }
     if flags.contains(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE) {
         result |= structs::ComputedStyleBit_HasPseudoElementData;
     }
+    if flags.contains(ComputedValueFlags::SELF_OR_ANCESTOR_HAS_CONTAIN_STYLE) {
+        result |= structs::ComputedStyleBit_SelfOrAncestorHasContainStyle;
+    }
     result
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_ComputedValues_SpecifiesAnimationsOrTransitions(
     values: ComputedStyleBorrowed,
 ) -> bool {
     let b = values.get_box();