Bug 1285474: stylo: Add dirtiness-tracking hooks for Servo and convenient methods.
Also, guard with asserts the access to the new shared flags.
MozReview-Commit-ID: H9UFFHRPmiu
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1521,17 +1521,17 @@ Element::BindToTree(nsIDocument* aDocume
// aDocument->BindingManager()->ChangeDocumentFor(this, nullptr,
// aDocument);
// We no longer need to track the subtree pointer (and in fact we'll assert
// if we do this any later).
ClearSubtreeRootPointer();
// Being added to a document.
- SetInDocument();
+ SetIsInDocument();
// Unset this flag since we now really are in a document.
UnsetFlags(NODE_FORCE_XBL_BINDINGS |
// And clear the lazy frame construction bits.
NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
// And the restyle bits
ELEMENT_ALL_RESTYLE_FLAGS);
} else if (IsInShadowTree()) {
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1445,16 +1445,23 @@ inline mozilla::dom::Element* nsINode::A
}
inline const mozilla::dom::Element* nsINode::AsElement() const
{
MOZ_ASSERT(IsElement());
return static_cast<const mozilla::dom::Element*>(this);
}
+inline void nsINode::UnsetRestyleFlagsIfGecko()
+{
+ if (IsElement() && !IsStyledByServo()) {
+ UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
+ }
+}
+
/**
* Macros to implement Clone(). _elementName is the class for which to implement
* Clone.
*/
#define NS_IMPL_ELEMENT_CLONE(_elementName) \
nsresult \
_elementName::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const \
{ \
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1455,17 +1455,20 @@ nsIDocument::nsIDocument()
mFontFaceSetDirty(true),
mGetUserFontSetCalled(false),
mPostedFlushUserFontSet(false),
mPartID(0),
mDidFireDOMContentLoaded(true),
mHasScrollLinkedEffect(false),
mUserHasInteracted(false)
{
- SetInDocument();
+ SetIsDocument();
+ if (IsStyledByServo()) {
+ SetFlags(NODE_IS_DIRTY_FOR_SERVO | NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
+ }
PR_INIT_CLIST(&mDOMMediaQueryLists);
}
// NOTE! nsDocument::operator new() zeroes out all members, so don't
// bother initializing members to 0.
nsDocument::nsDocument(const char* aContentType)
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -306,17 +306,17 @@ nsGenericDOMDataNode::SetTextInternal(ui
nsContentUtils::HasMutationListeners(this,
NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED,
this);
nsCOMPtr<nsIAtom> oldValue;
if (haveMutationListeners) {
oldValue = GetCurrentValueAtom();
}
-
+
if (aNotify) {
CharacterDataChangeInfo info = {
aOffset == textLength,
aOffset,
endOffset,
aLength,
aDetails
};
@@ -479,17 +479,17 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
"Already have a parent. Unbind first!");
NS_PRECONDITION(!GetBindingParent() ||
aBindingParent == GetBindingParent() ||
(!aBindingParent && aParent &&
aParent->GetBindingParent() == GetBindingParent()),
"Already have a binding parent. Unbind first!");
NS_PRECONDITION(aBindingParent != this,
"Content must not be its own binding parent");
- NS_PRECONDITION(!IsRootOfNativeAnonymousSubtree() ||
+ NS_PRECONDITION(!IsRootOfNativeAnonymousSubtree() ||
aBindingParent == aParent,
"Native anonymous content must have its parent as its "
"own binding parent");
if (!aBindingParent && aParent) {
aBindingParent = aParent->GetBindingParent();
}
@@ -535,17 +535,17 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
// Set document
if (aDocument) {
// We no longer need to track the subtree pointer (and in fact we'll assert
// if we do this any later).
ClearSubtreeRootPointer();
// XXX See the comment in Element::BindToTree
- SetInDocument();
+ SetIsInDocument();
if (mText.IsBidi()) {
aDocument->SetBidiEnabled();
}
// Clear the lazy frame construction bits.
UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
} else if (!IsInShadowTree()) {
// If we're not in the doc and not in a shadow tree,
// update our subtree pointer.
@@ -800,17 +800,17 @@ nsGenericDOMDataNode::SaveSubtreeState()
#ifdef DEBUG
void
nsGenericDOMDataNode::List(FILE* out, int32_t aIndent) const
{
}
void
nsGenericDOMDataNode::DumpContent(FILE* out, int32_t aIndent,
- bool aDumpAll) const
+ bool aDumpAll) const
{
}
#endif
bool
nsGenericDOMDataNode::IsLink(nsIURI** aURI) const
{
*aURI = nullptr;
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -998,16 +998,17 @@ public:
SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
}
inline void SetIsDirtyAndHasDirtyDescendantsForServo() {
MOZ_ASSERT(IsStyledByServo());
SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO | NODE_IS_DIRTY_FOR_SERVO);
}
+ inline void UnsetRestyleFlagsIfGecko();
/**
* Adds a mutation observer to be notified when this node, or any of its
* descendants, are modified. The node will hold a weak reference to the
* observer, which means that it is the responsibility of the observer to
* remove itself in case it dies before the node. If an observer is added
* while observers are being notified, it may also be notified. In general,
* adding observers while inside a notification is not a good idea. An
@@ -1721,17 +1722,27 @@ public:
void SetMayBeApzAware() { SetBoolFlag(MayBeApzAware); }
bool NodeMayBeApzAware() const
{
return GetBoolFlag(MayBeApzAware);
}
protected:
void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
- void SetInDocument() { SetBoolFlag(IsInDocument); }
+ /**
+ * This is a special case of SetIsInDocument used to special-case it for the
+ * document constructor (which can't do the IsStyledByServo() check).
+ */
+ void SetIsDocument() { SetBoolFlag(IsInDocument); }
+ void SetIsInDocument() {
+ if (IsStyledByServo()) {
+ SetIsDirtyAndHasDirtyDescendantsForServo();
+ }
+ SetBoolFlag(IsInDocument);
+ }
void SetNodeIsContent() { SetBoolFlag(NodeIsContent); }
void ClearInDocument() { ClearBoolFlag(IsInDocument); }
void SetIsElement() { SetBoolFlag(NodeIsElement); }
void SetHasID() { SetBoolFlag(ElementHasID); }
void ClearHasID() { ClearBoolFlag(ElementHasID); }
void SetMayHaveStyle() { SetBoolFlag(ElementMayHaveStyle); }
void SetHasName() { SetBoolFlag(ElementHasName); }
void ClearHasName() { ClearBoolFlag(ElementHasName); }
@@ -2053,18 +2064,18 @@ public:
return mServoNodeData;
#else
MOZ_CRASH("Accessing servo node data in non-stylo build");
#endif
}
void SetServoNodeData(ServoNodeData* aData) {
#ifdef MOZ_STYLO
- MOZ_ASSERT(!mServoNodeData);
- mServoNodeData = aData;
+ MOZ_ASSERT(!mServoNodeData);
+ mServoNodeData = aData;
#else
MOZ_CRASH("Setting servo node data in non-stylo build");
#endif
}
protected:
static bool Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb);
static void Unlink(nsINode *tmp);
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1975,17 +1975,17 @@ nsCSSFrameConstructor::GetParentType(nsI
if (aFrameType == nsGkAtoms::tableColGroupFrame) {
return eTypeColGroup;
}
if (aFrameType == nsGkAtoms::rubyBaseContainerFrame) {
return eTypeRubyBaseContainer;
}
if (aFrameType == nsGkAtoms::rubyTextContainerFrame) {
return eTypeRubyTextContainer;
- }
+ }
if (aFrameType == nsGkAtoms::rubyFrame) {
return eTypeRuby;
}
return eTypeBlock;
}
static nsContainerFrame*
@@ -2425,17 +2425,17 @@ nsCSSFrameConstructor::ConstructDocEleme
state.mPresShell->CaptureHistoryState(getter_AddRefs(mTempFrameTreeState));
// Make sure that we'll handle restyles for this document element in
// the future. We need this, because the document element might
// have stale restyle bits from a previous frame constructor for
// this document. Unlike in AddFrameConstructionItems, it's safe to
// unset all element restyle flags, since we don't have any
// siblings.
- aDocElement->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
+ aDocElement->UnsetRestyleFlagsIfGecko();
// --------- CREATE AREA OR BOX FRAME -------
// FIXME: Should this use ResolveStyleContext? (The calls in this
// function are the only case in nsCSSFrameConstructor where we don't
// do so for the construction of a style context for an element.)
RefPtr<nsStyleContext> styleContext;
styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
nullptr);
@@ -4135,17 +4135,17 @@ nsCSSFrameConstructor::CreateAnonymousFr
if (newFrame) {
NS_ASSERTION(content->GetPrimaryFrame(),
"Content must have a primary frame now");
newFrame->AddStateBits(NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT);
aChildItems.AddChild(newFrame);
} else {
FrameConstructionItemList items;
{
- // Skip parent display based style-fixup during our
+ // Skip parent display based style-fixup during our
// AddFrameConstructionItems() call:
TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper
parentDisplayBasedStyleFixupSkipper(aState.mTreeMatchContext);
AddFrameConstructionItems(aState, content, true, insertion, items);
}
ConstructFramesFromItemList(aState, items, aParentFrame, aChildItems);
}
@@ -5508,17 +5508,17 @@ nsCSSFrameConstructor::AddPageBreakItem(
}
bool
nsCSSFrameConstructor::ShouldCreateItemsForChild(nsFrameConstructorState& aState,
nsIContent* aContent,
nsContainerFrame* aParentFrame)
{
aContent->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
- if (aContent->IsElement()) {
+ if (aContent->IsElement() && !aContent->IsStyledByServo()) {
// We can't just remove our pending restyle flags, since we may
// have restyle-later-siblings set on us. But we _can_ remove the
// "is possible restyle root" flags, and need to. Otherwise we can
// end up with stale such flags (e.g. if we used to have a
// display:none parent when our last restyle was posted and
// processed and now no longer do).
aContent->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS &
~ELEMENT_PENDING_RESTYLE_FLAGS);
@@ -9751,17 +9751,17 @@ nsCSSFrameConstructor::sPseudoParentData
{ // Ruby
FCDATA_DECL(FCDATA_IS_LINE_PARTICIPANT |
FCDATA_USE_CHILD_ITEMS |
FCDATA_SKIP_FRAMESET,
NS_NewRubyFrame),
&nsCSSAnonBoxes::ruby
},
{ // Ruby Base
- FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
+ FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
FCDATA_IS_LINE_PARTICIPANT |
FCDATA_DESIRED_PARENT_TYPE_TO_BITS(eTypeRubyBaseContainer) |
FCDATA_SKIP_FRAMESET,
NS_NewRubyBaseFrame),
&nsCSSAnonBoxes::rubyBase
},
{ // Ruby Base Container
FCDATA_DECL(FCDATA_USE_CHILD_ITEMS |
@@ -10321,17 +10321,17 @@ nsCSSFrameConstructor::CreateNeededPseud
break;
case eTypeTable:
// Either colgroup or rowgroup, depending on what we're grouping.
wrapperType = groupingParentType == eTypeColGroup ?
eTypeColGroup : eTypeRowGroup;
break;
case eTypeColGroup:
MOZ_CRASH("Colgroups should be suppresing non-col child items");
- default:
+ default:
NS_ASSERTION(ourParentType == eTypeBlock, "Unrecognized parent type");
if (IsRubyParentType(groupingParentType)) {
wrapperType = eTypeRuby;
} else {
NS_ASSERTION(IsTableParentType(groupingParentType),
"groupingParentType should be either Ruby or table");
wrapperType = eTypeTable;
}
@@ -10537,23 +10537,26 @@ nsCSSFrameConstructor::AddFCItemsForAnon
nsIContent* content = aAnonymousItems[i].mContent;
#ifdef DEBUG
nsIAnonymousContentCreator* creator = do_QueryFrame(aFrame);
NS_ASSERTION(!creator || !creator->CreateFrameFor(content),
"If you need to use CreateFrameFor, you need to call "
"CreateAnonymousFrames manually and not follow the standard "
"ProcessChildren() codepath for this frame");
#endif
+ // Anything restyled by servo should already have the style data.
+ MOZ_ASSERT_IF(content->IsStyledByServo(), !!content->GetServoNodeData());
+ // Gecko-styled nodes should have no pending restyle flags.
+ MOZ_ASSERT_IF(!content->IsStyledByServo(),
+ !content->IsElement() ||
+ !(content->GetFlags() & ELEMENT_ALL_RESTYLE_FLAGS));
// Assert some things about this content
MOZ_ASSERT(!(content->GetFlags() &
(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME)),
"Should not be marked as needing frames");
- MOZ_ASSERT(!content->IsElement() ||
- !(content->GetFlags() & ELEMENT_ALL_RESTYLE_FLAGS),
- "Should have no pending restyle flags");
MOZ_ASSERT(!content->GetPrimaryFrame(),
"Should have no existing frame");
MOZ_ASSERT(!content->IsNodeOfType(nsINode::eCOMMENT) &&
!content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION),
"Why is someone creating garbage anonymous content");
RefPtr<nsStyleContext> styleContext;
TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper
@@ -10691,19 +10694,17 @@ nsCSSFrameConstructor::ProcessChildren(n
ancestorPusher.PushAncestorAndStyleScope(parent->AsElement());
} else {
ancestorPusher.PushStyleScope(parent->AsElement());
}
}
// Frame construction item construction should not post
// restyles, so removing restyle flags here is safe.
- if (child->IsElement()) {
- child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
- }
+ child->UnsetRestyleFlagsIfGecko();
if (addChildItems) {
AddFrameConstructionItems(aState, child, iter.XBLInvolved(), insertion,
itemsToConstruct);
} else {
ClearLazyBits(child, child->GetNextSibling());
}
}
itemsToConstruct.SetParentHasNoXBLChildren(!iter.XBLInvolved());
@@ -12003,23 +12004,22 @@ nsCSSFrameConstructor::BuildInlineChildI
// Manually check for comments/PIs, since we don't have a frame to pass to
// AddFrameConstructionItems. We know our parent is a non-replaced inline,
// so there is no need to do the NeedFrameFor check.
content->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
if (content->IsNodeOfType(nsINode::eCOMMENT) ||
content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
continue;
}
- if (content->IsElement()) {
- // See comment explaining why we need to remove the "is possible
- // restyle root" flags in AddFrameConstructionItems. But note
- // that we can remove all restyle flags, just like in
- // ProcessChildren and for the same reason.
- content->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
- }
+
+ // See comment explaining why we need to remove the "is possible
+ // restyle root" flags in AddFrameConstructionItems. But note
+ // that we can remove all restyle flags, just like in
+ // ProcessChildren and for the same reason.
+ content->UnsetRestyleFlagsIfGecko();
RefPtr<nsStyleContext> childContext =
ResolveStyleContext(parentStyleContext, content, &aState);
AddFrameConstructionItemsInternal(aState, content, nullptr,
content->NodeInfo()->NameAtom(),
content->GetNameSpaceID(),
iter.XBLInvolved(), childContext,
--- a/layout/generic/nsFrameSetFrame.cpp
+++ b/layout/generic/nsFrameSetFrame.cpp
@@ -274,29 +274,25 @@ nsHTMLFramesetFrame::Init(nsIContent*
for (uint32_t childX = 0; childX < numChildren; childX++) {
if (mChildCount == numCells) { // we have more <frame> or <frameset> than cells
// Clear the lazy bits in the remaining children. Also clear
// the restyle flags, like nsCSSFrameConstructor::ProcessChildren does.
for (uint32_t i = childX; i < numChildren; i++) {
nsIContent *child = mContent->GetChildAt(i);
child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
- if (child->IsElement()) {
- child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
- }
+ child->UnsetRestyleFlagsIfGecko();
}
break;
}
nsIContent *child = mContent->GetChildAt(childX);
child->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES | NODE_NEEDS_FRAME);
// Also clear the restyle flags in the child like
// nsCSSFrameConstructor::ProcessChildren does.
- if (child->IsElement()) {
- child->UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
- }
+ child->UnsetRestyleFlagsIfGecko();
// IMPORTANT: This must match the conditions in
// nsCSSFrameConstructor::ContentAppended/Inserted/Removed
if (!child->IsHTMLElement())
continue;
if (child->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame)) {
RefPtr<nsStyleContext> kidSC;
@@ -637,22 +633,22 @@ nsresult nsHTMLFramesetFrame::HandleEven
nsEventStatus* aEventStatus)
{
NS_ENSURE_ARG_POINTER(aEventStatus);
if (mDragger) {
// the nsFramesetBorderFrame has captured NS_MOUSE_DOWN
switch (aEvent->mMessage) {
case eMouseMove:
MouseDrag(aPresContext, aEvent);
- break;
+ break;
case eMouseUp:
if (aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) {
EndMouseDrag(aPresContext);
}
- break;
+ break;
default:
break;
}
*aEventStatus = nsEventStatus_eConsumeNoDefault;
} else {
*aEventStatus = nsEventStatus_eIgnore;
}
return NS_OK;
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -159,16 +159,35 @@ Gecko_Namespace(RawGeckoElement* aElemen
nsIAtom*
Gecko_GetElementId(RawGeckoElement* aElement)
{
const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::id);
return attr ? attr->GetAtomValue() : nullptr;
}
+// Dirtiness tracking.
+uint32_t
+Gecko_GetNodeFlags(RawGeckoNode* aNode)
+{
+ return aNode->GetFlags();
+}
+
+void
+Gecko_SetNodeFlags(RawGeckoNode* aNode, uint32_t aFlags)
+{
+ aNode->SetFlags(aFlags);
+}
+
+void
+Gecko_UnsetNodeFlags(RawGeckoNode* aNode, uint32_t aFlags)
+{
+ aNode->UnsetFlags(aFlags);
+}
+
template<class MatchFn>
bool
DoMatch(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName, MatchFn aMatch)
{
if (aNS) {
int32_t ns = nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNS);
NS_ENSURE_TRUE(ns != kNameSpaceID_Unknown, false);
const nsAttrValue* value = aElement->GetParsedAttr(aName, ns);
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -171,16 +171,21 @@ NS_DECL_HOLDER_FFI_REFCOUNTING(nsIURI, U
// Display style.
void Gecko_SetMozBinding(nsStyleDisplay* style_struct,
const uint8_t* string_bytes, uint32_t string_length,
ThreadSafeURIHolder* base_uri,
ThreadSafeURIHolder* referrer,
ThreadSafePrincipalHolder* principal);
void Gecko_CopyMozBindingFrom(nsStyleDisplay* des, const nsStyleDisplay* src);
+// Dirtiness tracking.
+uint32_t Gecko_GetNodeFlags(RawGeckoNode* node);
+void Gecko_SetNodeFlags(RawGeckoNode* node, uint32_t flags);
+void Gecko_UnsetNodeFlags(RawGeckoNode* node, uint32_t flags);
+
// Styleset and Stylesheet management.
//
// TODO: Make these return already_AddRefed and UniquePtr when the binding
// generator is smart enough to handle them.
RawServoStyleSheet* Servo_StylesheetFromUTF8Bytes(
const uint8_t* bytes, uint32_t length,
mozilla::css::SheetParsingMode parsing_mode,
ThreadSafeURIHolder* base,