mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-06 09:05:45 +00:00
9efb158ac7
MozReview-Commit-ID: eN0j8vnesa
315 lines
10 KiB
C++
315 lines
10 KiB
C++
/* -*- 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/ServoRestyleManager.h"
|
|
#include "mozilla/ServoStyleSet.h"
|
|
|
|
using namespace mozilla::dom;
|
|
|
|
namespace mozilla {
|
|
|
|
ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
|
|
: RestyleManagerBase(aPresContext)
|
|
{
|
|
}
|
|
|
|
void
|
|
ServoRestyleManager::PostRestyleEvent(Element* aElement,
|
|
nsRestyleHint aRestyleHint,
|
|
nsChangeHint aMinChangeHint)
|
|
{
|
|
if (MOZ_UNLIKELY(IsDisconnected()) ||
|
|
MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
|
|
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));
|
|
}
|
|
|
|
presShell->GetDocument()->SetNeedStyleFlush();
|
|
}
|
|
|
|
void
|
|
ServoRestyleManager::PostRestyleEventForLazyConstruction()
|
|
{
|
|
NS_WARNING("stylo: ServoRestyleManager::PostRestyleEventForLazyConstruction not implemented");
|
|
}
|
|
|
|
void
|
|
ServoRestyleManager::RebuildAllStyleData(nsChangeHint aExtraHint,
|
|
nsRestyleHint aRestyleHint)
|
|
{
|
|
NS_WARNING("stylo: ServoRestyleManager::RebuildAllStyleData not implemented");
|
|
}
|
|
|
|
void
|
|
ServoRestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
|
|
nsRestyleHint aRestyleHint)
|
|
{
|
|
MOZ_CRASH("stylo: ServoRestyleManager::PostRebuildAllStyleDataEvent not implemented");
|
|
}
|
|
|
|
/* static */ void
|
|
ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent,
|
|
nsStyleContext* aParentContext,
|
|
ServoStyleSet* aStyleSet)
|
|
{
|
|
nsIFrame* primaryFrame = aContent->GetPrimaryFrame();
|
|
|
|
// TODO: AFAIK this can happen when we have, let's say, display: none. Here we
|
|
// should trigger frame construction if the element is actually dirty (I
|
|
// guess), but we'd better do that once we have all the restyle hints thing
|
|
// figured out.
|
|
if (!primaryFrame) {
|
|
aContent->UnsetFlags(NODE_IS_DIRTY_FOR_SERVO | NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
|
|
return;
|
|
}
|
|
|
|
if (aContent->IsDirtyForServo()) {
|
|
RefPtr<ServoComputedValues> computedValues =
|
|
dont_AddRef(Servo_GetComputedValues(aContent));
|
|
|
|
// TODO: Figure out what pseudos does this content have, and do the proper
|
|
// thing with them.
|
|
RefPtr<nsStyleContext> context =
|
|
aStyleSet->GetContext(computedValues.forget(),
|
|
aParentContext,
|
|
nullptr,
|
|
CSSPseudoElementType::NotPseudo);
|
|
|
|
// TODO: Compare old and new styles to generate restyle change hints, and
|
|
// process them.
|
|
primaryFrame->SetStyleContext(context.get());
|
|
|
|
aContent->UnsetFlags(NODE_IS_DIRTY_FOR_SERVO);
|
|
}
|
|
|
|
if (aContent->HasDirtyDescendantsForServo()) {
|
|
FlattenedChildIterator it(aContent);
|
|
for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
|
|
RecreateStyleContexts(n, primaryFrame->StyleContext(), aStyleSet);
|
|
}
|
|
aContent->UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
|
|
}
|
|
}
|
|
|
|
static void
|
|
MarkParentsAsHavingDirtyDescendants(Element* aElement)
|
|
{
|
|
nsINode* cur = aElement;
|
|
while ((cur = cur->GetParentNode())) {
|
|
if (cur->HasDirtyDescendantsForServo()) {
|
|
break;
|
|
}
|
|
|
|
cur->SetHasDirtyDescendantsForServo();
|
|
}
|
|
}
|
|
|
|
static void
|
|
MarkChildrenAsDirtyForServo(nsIContent* aContent)
|
|
{
|
|
FlattenedChildIterator it(aContent);
|
|
|
|
nsIContent* n = it.GetNextChild();
|
|
bool hadChildren = bool(n);
|
|
for (; n; n = it.GetNextChild()) {
|
|
n->SetIsDirtyForServo();
|
|
}
|
|
|
|
if (hadChildren) {
|
|
aContent->SetHasDirtyDescendantsForServo();
|
|
}
|
|
}
|
|
|
|
void
|
|
ServoRestyleManager::NoteRestyleHint(Element* aElement, nsRestyleHint aHint)
|
|
{
|
|
if (aHint & eRestyle_Self) {
|
|
aElement->SetIsDirtyForServo();
|
|
MarkParentsAsHavingDirtyDescendants(aElement);
|
|
// NB: For Servo, at least for now, restyling and running selector-matching
|
|
// against the subtree is necessary as part of restyling the element, so
|
|
// processing eRestyle_Self will perform at least as much work as
|
|
// eRestyle_Subtree.
|
|
} else if (aHint & eRestyle_Subtree) {
|
|
MarkChildrenAsDirtyForServo(aElement);
|
|
MarkParentsAsHavingDirtyDescendants(aElement);
|
|
}
|
|
|
|
if (aHint & eRestyle_LaterSiblings) {
|
|
for (nsINode* cur = aElement->GetNextSibling(); cur;
|
|
cur = cur->GetNextSibling()) {
|
|
if (cur->IsContent()) {
|
|
cur->SetIsDirtyForServo();
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Handle all other nsRestyleHint values.
|
|
if (aHint & ~(eRestyle_Self | eRestyle_Subtree | eRestyle_LaterSiblings)) {
|
|
NS_WARNING(nsPrintfCString("stylo: Unhandled restyle hint %s",
|
|
RestyleManagerBase::RestyleHintToString(aHint).get()).get());
|
|
}
|
|
}
|
|
|
|
void
|
|
ServoRestyleManager::ProcessPendingRestyles()
|
|
{
|
|
if (!HasPendingRestyles()) {
|
|
return;
|
|
}
|
|
ServoStyleSet* styleSet = StyleSet();
|
|
|
|
if (!styleSet->StylingStarted()) {
|
|
// If something caused us to restyle, and we haven't started styling yet,
|
|
// do nothing. Everything is dirty, and we'll style it all later.
|
|
return;
|
|
}
|
|
|
|
nsIDocument* doc = PresContext()->Document();
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (root->IsDirtyForServo() || root->HasDirtyDescendantsForServo()) {
|
|
styleSet->RestyleSubtree(root);
|
|
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.
|
|
//
|
|
// Thus, they need to get cleared here.
|
|
MOZ_ASSERT(!doc->IsDirtyForServo());
|
|
doc->UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
|
|
|
|
IncrementRestyleGeneration();
|
|
}
|
|
|
|
void
|
|
ServoRestyleManager::RestyleForInsertOrChange(Element* aContainer,
|
|
nsIContent* aChild)
|
|
{
|
|
NS_WARNING("stylo: ServoRestyleManager::RestyleForInsertOrChange not implemented");
|
|
}
|
|
|
|
void
|
|
ServoRestyleManager::RestyleForAppend(Element* aContainer,
|
|
nsIContent* aFirstNewContent)
|
|
{
|
|
NS_WARNING("stylo: ServoRestyleManager::RestyleForAppend not implemented");
|
|
}
|
|
|
|
void
|
|
ServoRestyleManager::RestyleForRemove(Element* aContainer,
|
|
nsIContent* aOldChild,
|
|
nsIContent* aFollowingSibling)
|
|
{
|
|
NS_WARNING("stylo: ServoRestyleManager::RestyleForRemove not implemented");
|
|
}
|
|
|
|
nsresult
|
|
ServoRestyleManager::ContentStateChanged(nsIContent* aContent,
|
|
EventStates aChangedBits)
|
|
{
|
|
if (!aContent->IsElement()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
Element* aElement = aContent->AsElement();
|
|
nsChangeHint changeHint;
|
|
nsRestyleHint 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;
|
|
}
|
|
|
|
void
|
|
ServoRestyleManager::AttributeWillChange(Element* aElement,
|
|
int32_t aNameSpaceID,
|
|
nsIAtom* aAttribute, int32_t aModType,
|
|
const nsAttrValue* aNewValue)
|
|
{
|
|
ServoElementSnapshot* snapshot = SnapshotForElement(aElement);
|
|
snapshot->AddAttrs(aElement);
|
|
}
|
|
|
|
nsresult
|
|
ServoRestyleManager::ReparentStyleContext(nsIFrame* aFrame)
|
|
{
|
|
NS_WARNING("stylo: ServoRestyleManager::ReparentStyleContext not implemented");
|
|
return NS_OK;
|
|
}
|
|
|
|
ServoElementSnapshot*
|
|
ServoRestyleManager::SnapshotForElement(Element* aElement)
|
|
{
|
|
// NB: aElement is the argument for the construction of the snapshot in the
|
|
// not found case.
|
|
return mModifiedElements.LookupOrAdd(aElement, aElement);
|
|
}
|
|
|
|
} // namespace mozilla
|