--- 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();