Bug 1287951: stylo: Add support for computing style hints from Servo. r=heycam

MozReview-Commit-ID: ALuJxcfAMuL
This commit is contained in:
Emilio Cobos Álvarez 2016-07-18 18:02:55 -07:00
Родитель 0f56cc072c
Коммит 253dfd8aad
10 изменённых файлов: 561 добавлений и 122 удалений

Просмотреть файл

@ -12,6 +12,8 @@
namespace mozilla {
#define NS_EVENT_STATE_HIGHEST_SERVO_BIT 6
/**
* EventStates is the class used to represent the event states of nsIContent
* instances. These states are calculated by IntrinsicState() and
@ -23,6 +25,7 @@ class EventStates
{
public:
typedef uint64_t InternalType;
typedef uint8_t ServoType;
constexpr EventStates()
: mStates(0)
@ -155,6 +158,14 @@ public:
return mStates;
}
/**
* Method used to get the appropriate state representation for Servo.
*/
ServoType ServoValue() const
{
return mStates & ((1 << (NS_EVENT_STATE_HIGHEST_SERVO_BIT + 1)) - 1);
}
private:
InternalType mStates;
};
@ -200,10 +211,11 @@ private:
// Content is in the indeterminate state.
#define NS_EVENT_STATE_INDETERMINATE NS_DEFINE_EVENT_STATE_MACRO(6)
#define NS_EVENT_STATE_HIGHEST_SERVO_BIT 6
/*
* Bits below here do not have Servo-related ordering constraints.
*
* Remember to change NS_EVENT_STATE_HIGHEST_SERVO_BIT at the top of the file if
* this changes!
*/
// Drag is hovering over content.

Просмотреть файл

@ -17,20 +17,25 @@ ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
}
/* static */ void
ServoRestyleManager::DirtyTree(nsIContent* aContent)
ServoRestyleManager::DirtyTree(nsIContent* aContent, bool aIncludingRoot)
{
if (aContent->IsDirtyForServo()) {
return;
}
if (aIncludingRoot) {
// XXX: This can in theory leave nodes not dirty, but in practice this is not
// a problem, at least for now, since right now element dirty implies
// descendants dirty. Remove this early return if this ever changes.
if (aContent->IsDirtyForServo()) {
return;
}
aContent->SetIsDirtyForServo();
aContent->SetIsDirtyForServo();
}
FlattenedChildIterator it(aContent);
nsIContent* n = it.GetNextChild();
bool hadChildren = bool(n);
for ( ; n; n = it.GetNextChild()) {
DirtyTree(n);
for (; n; n = it.GetNextChild()) {
DirtyTree(n, true);
}
if (hadChildren) {
@ -48,26 +53,22 @@ ServoRestyleManager::PostRestyleEvent(Element* aElement,
return;
}
if (aRestyleHint == 0 && !aMinChangeHint) {
// Nothing to do here
return;
if (aRestyleHint == 0 && !aMinChangeHint && !HasPendingRestyles()) {
return; // Nothing to do.
}
// Note that unlike in Servo, we don't mark elements as dirty until we process
// the restyle hints in ProcessPendingRestyles.
if (aRestyleHint || aMinChangeHint) {
ServoElementSnapshot* snapshot = SnapshotForElement(aElement);
snapshot->AddExplicitRestyleHint(aRestyleHint);
snapshot->AddExplicitChangeHint(aMinChangeHint);
}
nsIPresShell* presShell = PresContext()->PresShell();
if (!ObservingRefreshDriver()) {
SetObservingRefreshDriver(PresContext()->RefreshDriver()->
AddStyleFlushObserver(presShell));
}
// Propagate the IS_DIRTY flag down the tree.
DirtyTree(aElement);
// Propagate the HAS_DIRTY_DESCENDANTS flag to the root.
nsINode* cur = aElement;
while ((cur = cur->GetParentNode())) {
if (cur->HasDirtyDescendantsForServo())
break;
cur->SetHasDirtyDescendantsForServo();
SetObservingRefreshDriver(
PresContext()->RefreshDriver()->AddStyleFlushObserver(presShell));
}
presShell->GetDocument()->SetNeedStyleFlush();
@ -137,6 +138,50 @@ ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent,
}
}
static void
MarkParentsAsHavingDirtyDescendants(Element* aElement)
{
nsINode* cur = aElement;
while ((cur = cur->GetParentNode())) {
if (cur->HasDirtyDescendantsForServo()) {
break;
}
cur->SetHasDirtyDescendantsForServo();
}
}
void
ServoRestyleManager::NoteRestyleHint(Element* aElement, nsRestyleHint aHint)
{
if (aHint & eRestyle_Self) {
aElement->SetIsDirtyForServo();
MarkParentsAsHavingDirtyDescendants(aElement);
// NB: Using Servo's style system marking the subtree as dirty is necessary
// so we inherit correctly the style structs.
aHint |= eRestyle_Subtree;
}
if (aHint & eRestyle_Subtree) {
DirtyTree(aElement, /* aIncludingRoot = */ false);
MarkParentsAsHavingDirtyDescendants(aElement);
}
if (aHint & eRestyle_LaterSiblings) {
for (nsINode* cur = aElement->GetNextSibling(); cur;
cur = cur->GetNextSibling()) {
if (cur->IsContent()) {
DirtyTree(cur->AsContent(), /* aIncludingRoot = */ true);
}
}
}
// TODO: Handle all other nsRestyleHint values.
if (aHint & ~(eRestyle_Self | eRestyle_Subtree | eRestyle_LaterSiblings)) {
NS_ERROR("stylo: Unhandled restyle hint");
}
}
void
ServoRestyleManager::ProcessPendingRestyles()
{
@ -149,10 +194,26 @@ ServoRestyleManager::ProcessPendingRestyles()
Element* root = doc->GetRootElement();
if (root) {
for (auto iter = mModifiedElements.Iter(); !iter.Done(); iter.Next()) {
ServoElementSnapshot* snapshot = iter.UserData();
Element* element = iter.Key();
// TODO: avoid the ComputeRestyleHint call if we already have the highest
// explicit restyle hint?
nsRestyleHint hint = styleSet->ComputeRestyleHint(element, snapshot);
hint |= snapshot->ExplicitRestyleHint();
if (hint) {
NoteRestyleHint(element, hint);
}
}
styleSet->RestyleSubtree(root, /* aForce = */ false);
RecreateStyleContexts(root, nullptr, styleSet);
}
mModifiedElements.Clear();
// NB: we restyle from the root element, but the document also gets the
// HAS_DIRTY_DESCENDANTS flag as part of the loop on PostRestyleEvent, and we
// use that to check we have pending restyles.
@ -188,7 +249,7 @@ ServoRestyleManager::RestyleForRemove(Element* aContainer,
nsresult
ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
EventStates aStateMask)
EventStates aChangedBits)
{
if (!aContent->IsElement()) {
return NS_OK;
@ -197,7 +258,32 @@ ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
Element* aElement = aContent->AsElement();
nsChangeHint changeHint;
nsRestyleHint restyleHint;
ContentStateChangedInternal(aElement, aStateMask, &changeHint, &restyleHint);
// NOTE: restyleHint here is effectively always 0, since that's what
// ServoStyleSet::HasStateDependentStyle returns. Servo computes on
// ProcessPendingRestyles using the ElementSnapshot, but in theory could
// compute it sequentially easily.
//
// Determine what's the best way to do it, and how much work do we save
// processing the restyle hint early (i.e., computing the style hint here
// sequentially, potentially saving the snapshot), vs lazily (snapshot
// approach).
//
// If we take the sequential approach we need to specialize Servo's restyle
// hints system a bit more, and mesure whether we save something storing the
// restyle hint in the table and deferring the dirtiness setting until
// ProcessPendingRestyles (that's a requirement if we store snapshots though),
// vs processing the restyle hint in-place, dirtying the nodes on
// PostRestyleEvent.
//
// If we definitely take the snapshot approach, we should take rid of
// HasStateDependentStyle, etc (though right now they're no-ops).
ContentStateChangedInternal(aElement, aChangedBits, &changeHint,
&restyleHint);
EventStates previousState = aElement->StyleState() ^ aChangedBits;
ServoElementSnapshot* snapshot = SnapshotForElement(aElement);
snapshot->AddState(previousState);
PostRestyleEvent(aElement, restyleHint, changeHint);
return NS_OK;
@ -206,18 +292,15 @@ ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
void
ServoRestyleManager::AttributeWillChange(Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
nsIAtom* aAttribute, int32_t aModType,
const nsAttrValue* aNewValue)
{
NS_ERROR("stylo: ServoRestyleManager::AttributeWillChange not implemented");
}
void
ServoRestyleManager::AttributeChanged(Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
nsIAtom* aAttribute, int32_t aModType,
const nsAttrValue* aOldValue)
{
NS_ERROR("stylo: ServoRestyleManager::AttributeChanged not implemented");
@ -229,4 +312,16 @@ ServoRestyleManager::ReparentStyleContext(nsIFrame* aFrame)
MOZ_CRASH("stylo: ServoRestyleManager::ReparentStyleContext not implemented");
}
ServoElementSnapshot*
ServoRestyleManager::SnapshotForElement(Element* aElement)
{
ServoElementSnapshot* snapshot = mModifiedElements.LookupOrAdd(aElement);
if (!snapshot->HasAny(
ServoElementSnapshot::Flags::HTMLElementInHTMLDocument)) {
snapshot->SetIsHTMLElementInHTMLDocument(
aElement->IsHTMLElement() && aElement->OwnerDoc()->IsHTMLDocument());
}
return snapshot;
}
} // namespace mozilla

Просмотреть файл

@ -9,10 +9,12 @@
#include "mozilla/EventStates.h"
#include "mozilla/RestyleManagerBase.h"
#include "mozilla/ServoElementSnapshot.h"
#include "nsChangeHint.h"
#include "nsHashKeys.h"
#include "nsINode.h"
#include "nsISupportsImpl.h"
#include "nsPresContext.h"
#include "nsINode.h"
namespace mozilla {
namespace dom {
@ -60,25 +62,25 @@ public:
nsIAtom* aAttribute,
int32_t aModType,
const nsAttrValue* aNewValue);
void AttributeChanged(dom::Element* aElement,
int32_t aNameSpaceID,
nsIAtom* aAttribute,
int32_t aModType,
void AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID,
nsIAtom* aAttribute, int32_t aModType,
const nsAttrValue* aOldValue);
nsresult ReparentStyleContext(nsIFrame* aFrame);
bool HasPendingRestyles() {
if (MOZ_UNLIKELY(IsDisconnected())) {
return false;
}
return PresContext()->PresShell()->GetDocument()->HasDirtyDescendantsForServo();
}
bool HasPendingRestyles() { return mModifiedElements.Count() != 0; }
protected:
~ServoRestyleManager() {}
private:
ServoElementSnapshot* SnapshotForElement(Element* aElement);
/**
* The element-to-element snapshot table to compute restyle hints.
*/
nsClassHashtable<nsRefPtrHashKey<Element>, ServoElementSnapshot>
mModifiedElements;
/**
* Traverses a tree of content that Servo has just restyled, recreating style
* contexts for their frames with the new style data.
@ -95,9 +97,15 @@ private:
* Propagates the IS_DIRTY flag down to the tree, setting
* HAS_DIRTY_DESCENDANTS appropriately.
*/
static void DirtyTree(nsIContent* aContent);
static void DirtyTree(nsIContent* aContent, bool aIncludingRoot = true);
inline ServoStyleSet* StyleSet() const {
/**
* Marks the tree with the appropriate dirty flags for the given restyle hint.
*/
static void NoteRestyleHint(Element* aElement, nsRestyleHint aRestyleHint);
inline ServoStyleSet* StyleSet() const
{
MOZ_ASSERT(PresContext()->StyleSet()->IsServo(),
"ServoRestyleManager should only be used with a Servo-flavored "
"style backend");

Просмотреть файл

@ -6,6 +6,7 @@
#include "mozilla/ServoBindings.h"
#include "StyleStructContext.h"
#include "gfxFontFamilyList.h"
#include "nsAttrValueInlines.h"
#include "nsCSSRuleProcessor.h"
@ -18,11 +19,11 @@
#include "nsNameSpaceManager.h"
#include "nsString.h"
#include "nsStyleStruct.h"
#include "nsTArray.h"
#include "nsStyleUtil.h"
#include "StyleStructContext.h"
#include "nsTArray.h"
#include "mozilla/EventStates.h"
#include "mozilla/ServoElementSnapshot.h"
#include "mozilla/dom/Element.h"
uint32_t
@ -102,11 +103,10 @@ Gecko_GetDocumentElement(RawGeckoDocument* aDoc)
return aDoc->GetDocumentElement();
}
uint8_t
EventStates::ServoType
Gecko_ElementState(RawGeckoElement* aElement)
{
return aElement->StyleState().GetInternalValue() &
((1 << (NS_EVENT_STATE_HIGHEST_SERVO_BIT + 1)) - 1);
return aElement->StyleState().ServoValue();
}
bool
@ -184,9 +184,27 @@ Gecko_UnsetNodeFlags(RawGeckoNode* aNode, uint32_t aFlags)
aNode->UnsetFlags(aFlags);
}
template<class MatchFn>
bool
DoMatch(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName, MatchFn aMatch)
ServoDeclarationBlock*
Gecko_GetServoDeclarationBlock(RawGeckoElement* aElement)
{
const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::style);
if (!attr || attr->Type() != nsAttrValue::eServoCSSDeclaration) {
return nullptr;
}
return attr->GetServoCSSDeclarationValue();
}
template <typename Implementor>
static nsIAtom*
AtomAttrValue(Implementor* aElement, nsIAtom* aName)
{
const nsAttrValue* attr = aElement->GetParsedAttr(aName);
return attr ? attr->GetAtomValue() : nullptr;
}
template <typename Implementor, typename MatchFn>
static bool
DoMatch(Implementor* aElement, nsIAtom* aNS, nsIAtom* aName, MatchFn aMatch)
{
if (aNS) {
int32_t ns = nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNS);
@ -218,16 +236,18 @@ struct FakeRef {
T* mPtr;
};
bool
Gecko_HasAttr(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName)
template <typename Implementor>
static bool
HasAttr(Implementor* aElement, nsIAtom* aNS, nsIAtom* aName)
{
auto match = [](const nsAttrValue* aValue) { return true; };
return DoMatch(aElement, aNS, aName, match);
}
bool
Gecko_AttrEquals(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
nsIAtom* aStr_, bool aIgnoreCase)
template <typename Implementor>
static bool
AttrEquals(Implementor* aElement, nsIAtom* aNS, nsIAtom* aName, nsIAtom* aStr_,
bool aIgnoreCase)
{
FakeRef<nsIAtom> aStr(aStr_);
auto match = [aStr, aIgnoreCase](const nsAttrValue* aValue) {
@ -236,9 +256,10 @@ Gecko_AttrEquals(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
return DoMatch(aElement, aNS, aName, match);
}
bool
Gecko_AttrDashEquals(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
nsIAtom* aStr_)
template <typename Implementor>
static bool
AttrDashEquals(Implementor* aElement, nsIAtom* aNS, nsIAtom* aName,
nsIAtom* aStr_)
{
FakeRef<nsIAtom> aStr(aStr_);
auto match = [aStr](const nsAttrValue* aValue) {
@ -250,9 +271,10 @@ Gecko_AttrDashEquals(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
return DoMatch(aElement, aNS, aName, match);
}
bool
Gecko_AttrIncludes(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
nsIAtom* aStr_)
template <typename Implementor>
static bool
AttrIncludes(Implementor* aElement, nsIAtom* aNS, nsIAtom* aName,
nsIAtom* aStr_)
{
FakeRef<nsIAtom> aStr(aStr_);
auto match = [aStr](const nsAttrValue* aValue) {
@ -264,9 +286,10 @@ Gecko_AttrIncludes(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
return DoMatch(aElement, aNS, aName, match);
}
bool
Gecko_AttrHasSubstring(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
nsIAtom* aStr_)
template <typename Implementor>
static bool
AttrHasSubstring(Implementor* aElement, nsIAtom* aNS, nsIAtom* aName,
nsIAtom* aStr_)
{
FakeRef<nsIAtom> aStr(aStr_);
auto match = [aStr](const nsAttrValue* aValue) {
@ -277,9 +300,10 @@ Gecko_AttrHasSubstring(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
return DoMatch(aElement, aNS, aName, match);
}
bool
Gecko_AttrHasPrefix(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
nsIAtom* aStr_)
template <typename Implementor>
static bool
AttrHasPrefix(Implementor* aElement, nsIAtom* aNS, nsIAtom* aName,
nsIAtom* aStr_)
{
FakeRef<nsIAtom> aStr(aStr_);
auto match = [aStr](const nsAttrValue* aValue) {
@ -290,9 +314,10 @@ Gecko_AttrHasPrefix(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
return DoMatch(aElement, aNS, aName, match);
}
bool
Gecko_AttrHasSuffix(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
nsIAtom* aStr_)
template <typename Implementor>
static bool
AttrHasSuffix(Implementor* aElement, nsIAtom* aNS, nsIAtom* aName,
nsIAtom* aStr_)
{
FakeRef<nsIAtom> aStr(aStr_);
auto match = [aStr](const nsAttrValue* aValue) {
@ -303,9 +328,22 @@ Gecko_AttrHasSuffix(RawGeckoElement* aElement, nsIAtom* aNS, nsIAtom* aName,
return DoMatch(aElement, aNS, aName, match);
}
uint32_t
Gecko_ClassOrClassList(RawGeckoElement* aElement,
nsIAtom** aClass, nsIAtom*** aClassList)
/**
* Gets the class or class list (if any) of the implementor. The calling
* convention here is rather hairy, and is optimized for getting Servo the
* information it needs for hot calls.
*
* The return value indicates the number of classes. If zero, neither outparam
* is valid. If one, the class_ outparam is filled with the atom of the class.
* If two or more, the classList outparam is set to point to an array of atoms
* representing the class list.
*
* The array is borrowed and the atoms are not addrefed. These values can be
* invalidated by any DOM mutation. Use them in a tight scope.
*/
template <typename Implementor>
static uint32_t
ClassOrClassList(Implementor* aElement, nsIAtom** aClass, nsIAtom*** aClassList)
{
const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::_class);
if (!attr) {
@ -357,15 +395,55 @@ Gecko_ClassOrClassList(RawGeckoElement* aElement,
return atomArray->Length();
}
ServoDeclarationBlock*
Gecko_GetServoDeclarationBlock(RawGeckoElement* aElement)
{
const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::style);
if (!attr || attr->Type() != nsAttrValue::eServoCSSDeclaration) {
return nullptr;
#define SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \
nsIAtom* prefix_##AtomAttrValue(implementor_* aElement, nsIAtom* aName) \
{ \
return AtomAttrValue(aElement, aName); \
} \
bool prefix_##HasAttr(implementor_* aElement, nsIAtom* aNS, nsIAtom* aName) \
{ \
return HasAttr(aElement, aNS, aName); \
} \
bool prefix_##AttrEquals(implementor_* aElement, nsIAtom* aNS, \
nsIAtom* aName, nsIAtom* aStr, bool aIgnoreCase) \
{ \
return AttrEquals(aElement, aNS, aName, aStr, aIgnoreCase); \
} \
bool prefix_##AttrDashEquals(implementor_* aElement, nsIAtom* aNS, \
nsIAtom* aName, nsIAtom* aStr) \
{ \
return AttrDashEquals(aElement, aNS, aName, aStr); \
} \
bool prefix_##AttrIncludes(implementor_* aElement, nsIAtom* aNS, \
nsIAtom* aName, nsIAtom* aStr) \
{ \
return AttrIncludes(aElement, aNS, aName, aStr); \
} \
bool prefix_##AttrHasSubstring(implementor_* aElement, nsIAtom* aNS, \
nsIAtom* aName, nsIAtom* aStr) \
{ \
return AttrHasSubstring(aElement, aNS, aName, aStr); \
} \
bool prefix_##AttrHasPrefix(implementor_* aElement, nsIAtom* aNS, \
nsIAtom* aName, nsIAtom* aStr) \
{ \
return AttrHasPrefix(aElement, aNS, aName, aStr); \
} \
bool prefix_##AttrHasSuffix(implementor_* aElement, nsIAtom* aNS, \
nsIAtom* aName, nsIAtom* aStr) \
{ \
return AttrHasSuffix(aElement, aNS, aName, aStr); \
} \
uint32_t prefix_##ClassOrClassList(implementor_* aElement, nsIAtom** aClass, \
nsIAtom*** aClassList) \
{ \
return ClassOrClassList(aElement, aClass, aClassList); \
}
return attr->GetServoCSSDeclarationValue();
}
SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElement)
SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, ServoElementSnapshot)
#undef SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS
ServoNodeData*
Gecko_GetNodeData(RawGeckoNode* aNode)
@ -809,6 +887,15 @@ Servo_Initialize()
"non-MOZ_STYLO build");
}
// Restyle hints.
nsRestyleHint
Servo_ComputeRestyleHint(RawGeckoElement* element,
ServoElementSnapshot* snapshot, RawServoStyleSet* set)
{
MOZ_CRASH("stylo: shouldn't be calling Servo_ComputeRestyleHint in a "
"non-MOZ_STYLO build");
}
void
Servo_RestyleDocument(RawGeckoDocument* doc, RawServoStyleSet* set)
{

Просмотреть файл

@ -7,12 +7,14 @@
#ifndef mozilla_ServoBindings_h
#define mozilla_ServoBindings_h
#include "stdint.h"
#include "nsColor.h"
#include "nsStyleStruct.h"
#include "nsStyleCoord.h"
#include "mozilla/ServoElementSnapshot.h"
#include "mozilla/css/SheetParsingMode.h"
#include "nsChangeHint.h"
#include "nsColor.h"
#include "nsProxyRelease.h"
#include "nsStyleCoord.h"
#include "nsStyleStruct.h"
#include "stdint.h"
/*
* API for Servo to access Gecko data structures. This file must compile as valid
@ -36,6 +38,7 @@ namespace mozilla {
using mozilla::FontFamilyList;
using mozilla::FontFamilyType;
using mozilla::dom::Element;
using mozilla::ServoElementSnapshot;
typedef mozilla::dom::Element RawGeckoElement;
class nsIDocument;
typedef nsIDocument RawGeckoDocument;
@ -104,29 +107,29 @@ nsIAtom* Gecko_Namespace(RawGeckoElement* element);
nsIAtom* Gecko_GetElementId(RawGeckoElement* element);
// Attributes.
bool Gecko_HasAttr(RawGeckoElement* element, nsIAtom* ns, nsIAtom* name);
bool Gecko_AttrEquals(RawGeckoElement* element, nsIAtom* ns, nsIAtom* name, nsIAtom* str, bool ignoreCase);
bool Gecko_AttrDashEquals(RawGeckoElement* element, nsIAtom* ns, nsIAtom* name, nsIAtom* str);
bool Gecko_AttrIncludes(RawGeckoElement* element, nsIAtom* ns, nsIAtom* name, nsIAtom* str);
bool Gecko_AttrHasSubstring(RawGeckoElement* element, nsIAtom* ns, nsIAtom* name, nsIAtom* str);
bool Gecko_AttrHasPrefix(RawGeckoElement* element, nsIAtom* ns, nsIAtom* name, nsIAtom* str);
bool Gecko_AttrHasSuffix(RawGeckoElement* element, nsIAtom* ns, nsIAtom* name, nsIAtom* str);
#define SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(prefix_, implementor_) \
nsIAtom* prefix_##AtomAttrValue(implementor_* element, nsIAtom* attribute); \
bool prefix_##HasAttr(implementor_* element, nsIAtom* ns, nsIAtom* name); \
bool prefix_##AttrEquals(implementor_* element, nsIAtom* ns, nsIAtom* name, \
nsIAtom* str, bool ignoreCase); \
bool prefix_##AttrDashEquals(implementor_* element, nsIAtom* ns, \
nsIAtom* name, nsIAtom* str); \
bool prefix_##AttrIncludes(implementor_* element, nsIAtom* ns, \
nsIAtom* name, nsIAtom* str); \
bool prefix_##AttrHasSubstring(implementor_* element, nsIAtom* ns, \
nsIAtom* name, nsIAtom* str); \
bool prefix_##AttrHasPrefix(implementor_* element, nsIAtom* ns, \
nsIAtom* name, nsIAtom* str); \
bool prefix_##AttrHasSuffix(implementor_* element, nsIAtom* ns, \
nsIAtom* name, nsIAtom* str); \
uint32_t prefix_##ClassOrClassList(implementor_* element, nsIAtom** class_, \
nsIAtom*** classList);
SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElement)
SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot,
ServoElementSnapshot)
// Gets the class or class list (if any) of the Element.
//
// The calling convention here is rather hairy, and is optimized for getting
// Servo the information it needs for hot calls.
//
// The return value indicates the number of classes. If zero, neither outparam
// is valid. If one, the class_ outparam is filled with the atom of the class.
// If two or more, the classList outparam is set to point to an array of atoms
// representing the class list.
//
// The array is borrowed and the atoms are not addrefed. These values can be
// invalidated by any DOM mutation. Use them in a tight scope.
uint32_t Gecko_ClassOrClassList(RawGeckoElement* element,
nsIAtom** class_, nsIAtom*** classList);
#undef SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS
// Style attributes.
ServoDeclarationBlock* Gecko_GetServoDeclarationBlock(RawGeckoElement* element);
@ -259,13 +262,20 @@ void Servo_Initialize();
void Servo_RestyleDocument(RawGeckoDocument* doc, RawServoStyleSet* set);
void Servo_RestyleSubtree(RawGeckoNode* node, RawServoStyleSet* set);
// Restyle hints.
nsRestyleHint Servo_ComputeRestyleHint(RawGeckoElement* element,
ServoElementSnapshot* snapshot,
RawServoStyleSet* set);
// Style-struct management.
#define STYLE_STRUCT(name, checkdata_cb) \
struct nsStyle##name; \
void Gecko_Construct_nsStyle##name(nsStyle##name* ptr); \
void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr, const nsStyle##name* other); \
void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr); \
const nsStyle##name* Servo_GetStyle##name(ServoComputedValues* computedValues);
#define STYLE_STRUCT(name, checkdata_cb) \
struct nsStyle##name; \
void Gecko_Construct_nsStyle##name(nsStyle##name* ptr); \
void Gecko_CopyConstruct_nsStyle##name(nsStyle##name* ptr, \
const nsStyle##name* other); \
void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr); \
const nsStyle##name* Servo_GetStyle##name( \
ServoComputedValues* computedValues);
#include "nsStyleStructList.h"
#undef STYLE_STRUCT

Просмотреть файл

@ -0,0 +1,32 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ServoElementSnapshot.h"
#include "mozilla/dom/Element.h"
namespace mozilla {
void
ServoElementSnapshot::AddAttrs(Element* aElement)
{
MOZ_ASSERT(aElement);
if (!HasAny(Flags::Attributes)) {
return;
}
uint32_t attrCount = aElement->GetAttrCount();
const nsAttrName* attrName;
for (uint32_t i = 0; i < attrCount; ++i) {
attrName = aElement->GetAttrNameAt(i);
const nsAttrValue* attrValue =
aElement->GetParsedAttr(attrName->LocalName(), attrName->NamespaceID());
mAttrs.AppendElement(ServoAttrSnapshot(*attrName, *attrValue));
}
mContains |= Flags::Attributes;
}
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,180 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_ServoElementSnapshot_h
#define mozilla_ServoElementSnapshot_h
#include "mozilla/EventStates.h"
#include "mozilla/TypedEnumBits.h"
#include "nsAttrName.h"
#include "nsAttrValue.h"
#include "nsChangeHint.h"
#include "nsIAtom.h"
namespace mozilla {
namespace dom {
class Element;
} // namespace dom
/**
* A structure representing a single attribute name and value.
*
* This is pretty similar to the private nsAttrAndChildArray::InternalAttr.
*/
struct ServoAttrSnapshot
{
nsAttrName mName;
nsAttrValue mValue;
ServoAttrSnapshot(const nsAttrName& aName, const nsAttrValue& aValue)
: mName(aName)
, mValue(aValue)
{
}
};
/**
* A bitflags enum class used to determine what data does a ServoElementSnapshot
* contains.
*/
enum class ServoElementSnapshotFlags : uint8_t
{
State = 1 << 0,
Attributes = 1 << 1,
HTMLElementInHTMLDocument = 1 << 2,
All = State | Attributes | HTMLElementInHTMLDocument
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(ServoElementSnapshotFlags)
/**
* This class holds all non-tree-structural state of an element that might be
* used for selector matching eventually.
*
* This means the attributes, and the element state, such as :hover, :active,
* etc...
*/
class ServoElementSnapshot
{
typedef dom::Element Element;
typedef EventStates::ServoType ServoStateType;
public:
typedef ServoElementSnapshotFlags Flags;
/**
* Empty snapshot, with no data at all.
*/
ServoElementSnapshot()
: mContains(Flags(0))
, mState(0)
, mExplicitRestyleHint(nsRestyleHint(0))
, mExplicitChangeHint(nsChangeHint(0))
, mIsHTMLElementInHTMLDocument(false)
{
}
bool HasAttrs() { return HasAny(Flags::Attributes); }
bool HasState() { return HasAny(Flags::State); }
/**
* Captures the given state (if not previously captured).
*/
void AddState(EventStates aState)
{
if (!HasAny(Flags::State)) {
mState = aState.ServoValue();
mContains |= Flags::State;
}
}
/**
* Captures the given element attributes (if not previously captured).
*/
void AddAttrs(Element* aElement);
void AddExplicitChangeHint(nsChangeHint aMinChangeHint)
{
mExplicitChangeHint |= aMinChangeHint;
}
void AddExplicitRestyleHint(nsRestyleHint aRestyleHint)
{
mExplicitRestyleHint |= aRestyleHint;
}
nsRestyleHint ExplicitRestyleHint() { return mExplicitRestyleHint; }
nsChangeHint ExplicitChangeHint() { return mExplicitChangeHint; }
/**
* Needed methods for attribute matching.
*/
const nsAttrName* GetAttrNameAt(uint32_t aIndex) const
{
if (aIndex >= mAttrs.Length()) {
return nullptr;
}
return &mAttrs[aIndex].mName;
}
const nsAttrValue* GetParsedAttr(nsIAtom* aLocalName) const
{
return GetParsedAttr(aLocalName, kNameSpaceID_None);
}
const nsAttrValue* GetParsedAttr(nsIAtom* aLocalName,
int32_t aNamespaceID) const
{
uint32_t i, len = mAttrs.Length();
if (aNamespaceID == kNameSpaceID_None) {
// This should be the common case so lets make an optimized loop
for (i = 0; i < len; ++i) {
if (mAttrs[i].mName.Equals(aLocalName)) {
return &mAttrs[i].mValue;
}
}
return nullptr;
}
for (i = 0; i < len; ++i) {
if (mAttrs[i].mName.Equals(aLocalName, aNamespaceID)) {
return &mAttrs[i].mValue;
}
}
return nullptr;
}
void SetIsHTMLElementInHTMLDocument(bool aIs)
{
MOZ_ASSERT(!HasAny(Flags::HTMLElementInHTMLDocument),
"Only expected to be set once!");
mContains |= Flags::HTMLElementInHTMLDocument;
mIsHTMLElementInHTMLDocument = aIs;
}
bool HasAny(Flags aFlags) { return bool(mContains & aFlags); }
private:
// TODO: Profile, a 1 or 2 element AutoTArray could be worth it, given we know
// we're dealing with attribute changes when we take snapshots of attributes,
// though it can be wasted space if we deal with a lot of state-only
// snapshots.
Flags mContains;
nsTArray<ServoAttrSnapshot> mAttrs;
ServoStateType mState;
nsRestyleHint mExplicitRestyleHint;
nsChangeHint mExplicitChangeHint;
bool mIsHTMLElementInHTMLDocument;
};
} // namespace mozilla
#endif

Просмотреть файл

@ -381,6 +381,13 @@ ServoStyleSet::HasStateDependentStyle(dom::Element* aElement,
return nsRestyleHint(0);
}
nsRestyleHint
ServoStyleSet::ComputeRestyleHint(dom::Element* aElement,
ServoElementSnapshot* aSnapshot)
{
return Servo_ComputeRestyleHint(aElement, aSnapshot, mRawSet.get());
}
void
ServoStyleSet::RestyleSubtree(nsINode* aNode, bool aForce)
{

Просмотреть файл

@ -11,11 +11,12 @@
#include "mozilla/EventStates.h"
#include "mozilla/RefPtr.h"
#include "mozilla/ServoBindingHelpers.h"
#include "mozilla/ServoElementSnapshot.h"
#include "mozilla/ServoStyleSheet.h"
#include "mozilla/SheetType.h"
#include "mozilla/UniquePtr.h"
#include "nsChangeHint.h"
#include "nsCSSPseudoElements.h"
#include "nsChangeHint.h"
#include "nsIAtom.h"
#include "nsTArray.h"
@ -113,10 +114,15 @@ public:
// Test if style is dependent on content state
nsRestyleHint HasStateDependentStyle(dom::Element* aElement,
EventStates aStateMask);
nsRestyleHint HasStateDependentStyle(dom::Element* aElement,
mozilla::CSSPseudoElementType aPseudoType,
dom::Element* aPseudoElement,
EventStates aStateMask);
nsRestyleHint HasStateDependentStyle(
dom::Element* aElement, mozilla::CSSPseudoElementType aPseudoType,
dom::Element* aPseudoElement, EventStates aStateMask);
/**
* Computes a restyle hint given a element and a previous element snapshot.
*/
nsRestyleHint ComputeRestyleHint(dom::Element* aElement,
ServoElementSnapshot* aSnapshot);
/**
* Restyles a whole subtree of nodes.

Просмотреть файл

@ -93,6 +93,7 @@ EXPORTS.mozilla += [
'RuleProcessorCache.h',
'ServoBindingHelpers.h',
'ServoBindings.h',
'ServoElementSnapshot.h',
'ServoStyleSet.h',
'ServoStyleSheet.h',
'SheetType.h',
@ -190,6 +191,7 @@ UNIFIED_SOURCES += [
'RuleNodeCacheConditions.cpp',
'RuleProcessorCache.cpp',
'ServoBindings.cpp',
'ServoElementSnapshot.cpp',
'ServoStyleSet.cpp',
'ServoStyleSheet.cpp',
'StyleAnimationValue.cpp',