2016-07-08 10:08:46 +03:00
|
|
|
/* -*- 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/. */
|
|
|
|
|
2017-02-13 06:21:31 +03:00
|
|
|
#include "mozilla/RestyleManager.h"
|
2017-02-13 06:21:33 +03:00
|
|
|
#include "mozilla/RestyleManagerInlines.h"
|
2017-05-02 09:03:16 +03:00
|
|
|
|
|
|
|
#include "Layers.h"
|
|
|
|
#include "LayerAnimationInfo.h" // For LayerAnimationInfo::sRecords
|
2016-11-19 03:37:13 +03:00
|
|
|
#include "mozilla/StyleSetHandleInlines.h"
|
2016-07-08 10:08:46 +03:00
|
|
|
#include "nsIFrame.h"
|
2017-02-10 05:42:27 +03:00
|
|
|
#include "nsIPresShellInlines.h"
|
2016-07-08 10:08:46 +03:00
|
|
|
|
2017-05-02 09:03:16 +03:00
|
|
|
|
2016-07-08 10:08:46 +03:00
|
|
|
namespace mozilla {
|
|
|
|
|
2017-02-13 06:21:32 +03:00
|
|
|
RestyleManager::RestyleManager(StyleBackendType aType,
|
|
|
|
nsPresContext* aPresContext)
|
2016-07-08 10:08:46 +03:00
|
|
|
: mPresContext(aPresContext)
|
|
|
|
, mRestyleGeneration(1)
|
2017-05-23 09:13:47 +03:00
|
|
|
, mUndisplayedRestyleGeneration(1)
|
2016-07-08 10:08:46 +03:00
|
|
|
, mHoverGeneration(0)
|
2017-02-13 06:21:32 +03:00
|
|
|
, mType(aType)
|
2016-08-04 04:58:06 +03:00
|
|
|
, mInStyleRefresh(false)
|
2017-03-09 12:15:08 +03:00
|
|
|
, mAnimationGeneration(0)
|
2016-07-08 10:08:46 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(mPresContext);
|
|
|
|
}
|
|
|
|
|
2017-02-13 06:21:33 +03:00
|
|
|
void
|
|
|
|
RestyleManager::ContentInserted(nsINode* aContainer, nsIContent* aChild)
|
|
|
|
{
|
|
|
|
RestyleForInsertOrChange(aContainer, aChild);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RestyleManager::ContentAppended(nsIContent* aContainer, nsIContent* aFirstNewContent)
|
|
|
|
{
|
|
|
|
RestyleForAppend(aContainer, aFirstNewContent);
|
|
|
|
}
|
|
|
|
|
2017-02-13 06:21:33 +03:00
|
|
|
void
|
|
|
|
RestyleManager::RestyleForEmptyChange(Element* aContainer)
|
|
|
|
{
|
|
|
|
// In some cases (:empty + E, :empty ~ E), a change in the content of
|
|
|
|
// an element requires restyling its parent's siblings.
|
|
|
|
nsRestyleHint hint = eRestyle_Subtree;
|
|
|
|
nsIContent* grandparent = aContainer->GetParent();
|
|
|
|
if (grandparent &&
|
|
|
|
(grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
|
|
|
|
hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
|
|
|
|
}
|
|
|
|
PostRestyleEvent(aContainer, hint, nsChangeHint(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RestyleManager::RestyleForAppend(nsIContent* aContainer,
|
|
|
|
nsIContent* aFirstNewContent)
|
|
|
|
{
|
|
|
|
// The container cannot be a document, but might be a ShadowRoot.
|
|
|
|
if (!aContainer->IsElement()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Element* container = aContainer->AsElement();
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
{
|
|
|
|
for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
|
|
|
|
NS_ASSERTION(!cur->IsRootOfAnonymousSubtree(),
|
|
|
|
"anonymous nodes should not be in child lists");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
uint32_t selectorFlags =
|
|
|
|
container->GetFlags() & (NODE_ALL_SELECTOR_FLAGS &
|
|
|
|
~NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS);
|
|
|
|
if (selectorFlags == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
|
|
|
|
// see whether we need to restyle the container
|
|
|
|
bool wasEmpty = true; // :empty or :-moz-only-whitespace
|
|
|
|
for (nsIContent* cur = container->GetFirstChild();
|
|
|
|
cur != aFirstNewContent;
|
|
|
|
cur = cur->GetNextSibling()) {
|
|
|
|
// We don't know whether we're testing :empty or :-moz-only-whitespace,
|
|
|
|
// so be conservative and assume :-moz-only-whitespace (i.e., make
|
|
|
|
// IsSignificantChild less likely to be true, and thus make us more
|
|
|
|
// likely to restyle).
|
|
|
|
if (nsStyleUtil::IsSignificantChild(cur, true, false)) {
|
|
|
|
wasEmpty = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (wasEmpty) {
|
|
|
|
RestyleForEmptyChange(container);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
|
|
|
|
PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
|
|
|
|
// Restyling the container is the most we can do here, so we're done.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
|
|
|
|
// restyle the last element child before this node
|
|
|
|
for (nsIContent* cur = aFirstNewContent->GetPreviousSibling();
|
|
|
|
cur;
|
|
|
|
cur = cur->GetPreviousSibling()) {
|
|
|
|
if (cur->IsElement()) {
|
|
|
|
PostRestyleEvent(cur->AsElement(), eRestyle_Subtree, nsChangeHint(0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Needed since we can't use PostRestyleEvent on non-elements (with
|
|
|
|
// eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree |
|
|
|
|
// eRestyle_LaterSiblings) as appropriate).
|
|
|
|
static void
|
|
|
|
RestyleSiblingsStartingWith(RestyleManager* aRestyleManager,
|
|
|
|
nsIContent* aStartingSibling /* may be null */)
|
|
|
|
{
|
|
|
|
for (nsIContent* sibling = aStartingSibling; sibling;
|
|
|
|
sibling = sibling->GetNextSibling()) {
|
|
|
|
if (sibling->IsElement()) {
|
|
|
|
aRestyleManager->
|
|
|
|
PostRestyleEvent(sibling->AsElement(),
|
|
|
|
nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
|
|
|
|
nsChangeHint(0));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restyling for a ContentInserted or CharacterDataChanged notification.
|
|
|
|
// This could be used for ContentRemoved as well if we got the
|
|
|
|
// notification before the removal happened (and sometimes
|
|
|
|
// CharacterDataChanged is more like a removal than an addition).
|
|
|
|
// The comments are written and variables are named in terms of it being
|
|
|
|
// a ContentInserted notification.
|
|
|
|
void
|
|
|
|
RestyleManager::RestyleForInsertOrChange(nsINode* aContainer,
|
|
|
|
nsIContent* aChild)
|
|
|
|
{
|
|
|
|
// The container might be a document or a ShadowRoot.
|
|
|
|
if (!aContainer->IsElement()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Element* container = aContainer->AsElement();
|
|
|
|
|
|
|
|
NS_ASSERTION(!aChild->IsRootOfAnonymousSubtree(),
|
|
|
|
"anonymous nodes should not be in child lists");
|
2017-07-09 01:02:47 +03:00
|
|
|
uint32_t selectorFlags = container->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
|
2017-02-13 06:21:33 +03:00
|
|
|
if (selectorFlags == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
|
|
|
|
// see whether we need to restyle the container
|
|
|
|
bool wasEmpty = true; // :empty or :-moz-only-whitespace
|
|
|
|
for (nsIContent* child = container->GetFirstChild();
|
|
|
|
child;
|
|
|
|
child = child->GetNextSibling()) {
|
|
|
|
if (child == aChild)
|
|
|
|
continue;
|
|
|
|
// We don't know whether we're testing :empty or :-moz-only-whitespace,
|
|
|
|
// so be conservative and assume :-moz-only-whitespace (i.e., make
|
|
|
|
// IsSignificantChild less likely to be true, and thus make us more
|
|
|
|
// likely to restyle).
|
|
|
|
if (nsStyleUtil::IsSignificantChild(child, true, false)) {
|
|
|
|
wasEmpty = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (wasEmpty) {
|
|
|
|
RestyleForEmptyChange(container);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
|
|
|
|
PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
|
|
|
|
// Restyling the container is the most we can do here, so we're done.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
|
|
|
|
// Restyle all later siblings.
|
|
|
|
RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
|
|
|
|
// restyle the previously-first element child if it is after this node
|
|
|
|
bool passedChild = false;
|
|
|
|
for (nsIContent* content = container->GetFirstChild();
|
|
|
|
content;
|
|
|
|
content = content->GetNextSibling()) {
|
|
|
|
if (content == aChild) {
|
|
|
|
passedChild = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (content->IsElement()) {
|
|
|
|
if (passedChild) {
|
|
|
|
PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
|
|
|
|
nsChangeHint(0));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// restyle the previously-last element child if it is before this node
|
|
|
|
passedChild = false;
|
|
|
|
for (nsIContent* content = container->GetLastChild();
|
|
|
|
content;
|
|
|
|
content = content->GetPreviousSibling()) {
|
|
|
|
if (content == aChild) {
|
|
|
|
passedChild = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (content->IsElement()) {
|
|
|
|
if (passedChild) {
|
|
|
|
PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
|
|
|
|
nsChangeHint(0));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RestyleManager::ContentRemoved(nsINode* aContainer,
|
|
|
|
nsIContent* aOldChild,
|
|
|
|
nsIContent* aFollowingSibling)
|
|
|
|
{
|
|
|
|
// The container might be a document or a ShadowRoot.
|
|
|
|
if (!aContainer->IsElement()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Element* container = aContainer->AsElement();
|
|
|
|
|
|
|
|
if (aOldChild->IsRootOfAnonymousSubtree()) {
|
|
|
|
// This should be an assert, but this is called incorrectly in
|
|
|
|
// HTMLEditor::DeleteRefToAnonymousNode and the assertions were clogging
|
|
|
|
// up the logs. Make it an assert again when that's fixed.
|
|
|
|
MOZ_ASSERT(aOldChild->GetProperty(nsGkAtoms::restylableAnonymousNode),
|
|
|
|
"anonymous nodes should not be in child lists (bug 439258)");
|
|
|
|
}
|
2017-07-09 01:02:47 +03:00
|
|
|
uint32_t selectorFlags = container->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
|
2017-02-13 06:21:33 +03:00
|
|
|
if (selectorFlags == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
|
|
|
|
// see whether we need to restyle the container
|
|
|
|
bool isEmpty = true; // :empty or :-moz-only-whitespace
|
|
|
|
for (nsIContent* child = container->GetFirstChild();
|
|
|
|
child;
|
|
|
|
child = child->GetNextSibling()) {
|
|
|
|
// We don't know whether we're testing :empty or :-moz-only-whitespace,
|
|
|
|
// so be conservative and assume :-moz-only-whitespace (i.e., make
|
|
|
|
// IsSignificantChild less likely to be true, and thus make us more
|
|
|
|
// likely to restyle).
|
|
|
|
if (nsStyleUtil::IsSignificantChild(child, true, false)) {
|
|
|
|
isEmpty = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (isEmpty) {
|
|
|
|
RestyleForEmptyChange(container);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
|
|
|
|
PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
|
|
|
|
// Restyling the container is the most we can do here, so we're done.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
|
|
|
|
// Restyle all later siblings.
|
|
|
|
RestyleSiblingsStartingWith(this, aFollowingSibling);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
|
|
|
|
// restyle the now-first element child if it was after aOldChild
|
|
|
|
bool reachedFollowingSibling = false;
|
|
|
|
for (nsIContent* content = container->GetFirstChild();
|
|
|
|
content;
|
|
|
|
content = content->GetNextSibling()) {
|
|
|
|
if (content == aFollowingSibling) {
|
|
|
|
reachedFollowingSibling = true;
|
|
|
|
// do NOT continue here; we might want to restyle this node
|
|
|
|
}
|
|
|
|
if (content->IsElement()) {
|
|
|
|
if (reachedFollowingSibling) {
|
|
|
|
PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
|
|
|
|
nsChangeHint(0));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// restyle the now-last element child if it was before aOldChild
|
|
|
|
reachedFollowingSibling = (aFollowingSibling == nullptr);
|
|
|
|
for (nsIContent* content = container->GetLastChild();
|
|
|
|
content;
|
|
|
|
content = content->GetPreviousSibling()) {
|
|
|
|
if (content->IsElement()) {
|
|
|
|
if (reachedFollowingSibling) {
|
|
|
|
PostRestyleEvent(content->AsElement(), eRestyle_Subtree, nsChangeHint(0));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (content == aFollowingSibling) {
|
|
|
|
reachedFollowingSibling = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-08 10:08:46 +03:00
|
|
|
/**
|
|
|
|
* Calculates the change hint and the restyle hint for a given content state
|
|
|
|
* change.
|
|
|
|
*
|
|
|
|
* This is called from both Restyle managers.
|
|
|
|
*/
|
|
|
|
void
|
2017-02-13 06:21:32 +03:00
|
|
|
RestyleManager::ContentStateChangedInternal(Element* aElement,
|
|
|
|
EventStates aStateMask,
|
2017-08-30 01:50:50 +03:00
|
|
|
nsChangeHint* aOutChangeHint)
|
2016-07-08 10:08:46 +03:00
|
|
|
{
|
2017-05-02 23:42:26 +03:00
|
|
|
MOZ_ASSERT(!mInStyleRefresh);
|
2016-07-08 10:08:46 +03:00
|
|
|
MOZ_ASSERT(aOutChangeHint);
|
|
|
|
|
2016-07-17 17:20:21 +03:00
|
|
|
*aOutChangeHint = nsChangeHint(0);
|
2016-07-08 10:08:46 +03:00
|
|
|
// Any change to a content state that affects which frames we construct
|
|
|
|
// must lead to a frame reconstruct here if we already have a frame.
|
|
|
|
// Note that we never decide through non-CSS means to not create frames
|
|
|
|
// based on content states, so if we already don't have a frame we don't
|
|
|
|
// need to force a reframe -- if it's needed, the HasStateDependentStyle
|
|
|
|
// call will handle things.
|
|
|
|
nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
|
|
|
|
if (primaryFrame) {
|
|
|
|
// If it's generated content, ignore LOADING/etc state changes on it.
|
|
|
|
if (!primaryFrame->IsGeneratedContentFrame() &&
|
|
|
|
aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
|
|
|
|
NS_EVENT_STATE_USERDISABLED |
|
|
|
|
NS_EVENT_STATE_SUPPRESSED |
|
|
|
|
NS_EVENT_STATE_LOADING)) {
|
|
|
|
*aOutChangeHint = nsChangeHint_ReconstructFrame;
|
|
|
|
} else {
|
2017-05-21 12:15:00 +03:00
|
|
|
uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
|
2016-07-08 10:08:46 +03:00
|
|
|
if (app) {
|
2016-08-01 23:40:27 +03:00
|
|
|
nsITheme* theme = PresContext()->GetTheme();
|
|
|
|
if (theme &&
|
|
|
|
theme->ThemeSupportsWidget(PresContext(), primaryFrame, app)) {
|
2016-07-08 10:08:46 +03:00
|
|
|
bool repaint = false;
|
2016-08-01 23:40:27 +03:00
|
|
|
theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint,
|
|
|
|
nullptr);
|
2016-07-08 10:08:46 +03:00
|
|
|
if (repaint) {
|
|
|
|
*aOutChangeHint |= nsChangeHint_RepaintFrame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
primaryFrame->ContentStatesChanged(aStateMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
|
|
|
|
// Exposing information to the page about whether the link is
|
|
|
|
// visited or not isn't really something we can worry about here.
|
|
|
|
// FIXME: We could probably do this a bit better.
|
|
|
|
*aOutChangeHint |= nsChangeHint_RepaintFrame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-23 01:27:05 +03:00
|
|
|
/* static */ nsCString
|
2017-02-13 06:21:32 +03:00
|
|
|
RestyleManager::RestyleHintToString(nsRestyleHint aHint)
|
2016-07-23 01:27:05 +03:00
|
|
|
{
|
|
|
|
nsCString result;
|
|
|
|
bool any = false;
|
|
|
|
const char* names[] = {
|
|
|
|
"Self", "SomeDescendants", "Subtree", "LaterSiblings", "CSSTransitions",
|
2017-03-24 05:59:54 +03:00
|
|
|
"CSSAnimations", "StyleAttribute", "StyleAttribute_Animations",
|
|
|
|
"Force", "ForceDescendants"
|
2016-07-23 01:27:05 +03:00
|
|
|
};
|
|
|
|
uint32_t hint = aHint & ((1 << ArrayLength(names)) - 1);
|
|
|
|
uint32_t rest = aHint & ~((1 << ArrayLength(names)) - 1);
|
|
|
|
for (uint32_t i = 0; i < ArrayLength(names); i++) {
|
|
|
|
if (hint & (1 << i)) {
|
|
|
|
if (any) {
|
|
|
|
result.AppendLiteral(" | ");
|
|
|
|
}
|
|
|
|
result.AppendPrintf("eRestyle_%s", names[i]);
|
|
|
|
any = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rest) {
|
|
|
|
if (any) {
|
|
|
|
result.AppendLiteral(" | ");
|
|
|
|
}
|
|
|
|
result.AppendPrintf("0x%0x", rest);
|
|
|
|
} else {
|
|
|
|
if (!any) {
|
|
|
|
result.AppendLiteral("0");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-08-05 21:54:22 +03:00
|
|
|
#ifdef DEBUG
|
|
|
|
/* static */ nsCString
|
2017-02-13 06:21:32 +03:00
|
|
|
RestyleManager::ChangeHintToString(nsChangeHint aHint)
|
2016-08-05 21:54:22 +03:00
|
|
|
{
|
|
|
|
nsCString result;
|
|
|
|
bool any = false;
|
|
|
|
const char* names[] = {
|
|
|
|
"RepaintFrame", "NeedReflow", "ClearAncestorIntrinsics",
|
|
|
|
"ClearDescendantIntrinsics", "NeedDirtyReflow", "SyncFrameView",
|
|
|
|
"UpdateCursor", "UpdateEffects", "UpdateOpacityLayer",
|
|
|
|
"UpdateTransformLayer", "ReconstructFrame", "UpdateOverflow",
|
|
|
|
"UpdateSubtreeOverflow", "UpdatePostTransformOverflow",
|
|
|
|
"UpdateParentOverflow",
|
2017-03-07 03:10:38 +03:00
|
|
|
"ChildrenOnlyTransform", "RecomputePosition", "UpdateContainingBlock",
|
2016-08-05 21:54:22 +03:00
|
|
|
"BorderStyleNoneChange", "UpdateTextPath", "SchedulePaint",
|
|
|
|
"NeutralChange", "InvalidateRenderingObservers",
|
|
|
|
"ReflowChangesSizeOrPosition", "UpdateComputedBSize",
|
2016-09-30 00:15:32 +03:00
|
|
|
"UpdateUsesOpacity", "UpdateBackgroundPosition",
|
2017-05-10 23:53:27 +03:00
|
|
|
"AddOrRemoveTransform", "CSSOverflowChange",
|
2017-09-05 23:30:40 +03:00
|
|
|
"UpdateWidgetProperties", "UpdateTableCellSpans"
|
2016-08-05 21:54:22 +03:00
|
|
|
};
|
2017-09-05 23:30:40 +03:00
|
|
|
static_assert(nsChangeHint_AllHints == (1u << ArrayLength(names)) - 1,
|
2016-08-15 13:52:05 +03:00
|
|
|
"Name list doesn't match change hints.");
|
2017-09-05 23:30:40 +03:00
|
|
|
uint32_t hint = aHint & ((1u << ArrayLength(names)) - 1);
|
|
|
|
uint32_t rest = aHint & ~((1u << ArrayLength(names)) - 1);
|
2017-03-21 11:33:05 +03:00
|
|
|
if ((hint & NS_STYLE_HINT_REFLOW) == NS_STYLE_HINT_REFLOW) {
|
|
|
|
result.AppendLiteral("NS_STYLE_HINT_REFLOW");
|
|
|
|
hint = hint & ~NS_STYLE_HINT_REFLOW;
|
|
|
|
any = true;
|
|
|
|
} else if ((hint & nsChangeHint_AllReflowHints) == nsChangeHint_AllReflowHints) {
|
|
|
|
result.AppendLiteral("nsChangeHint_AllReflowHints");
|
|
|
|
hint = hint & ~nsChangeHint_AllReflowHints;
|
|
|
|
any = true;
|
|
|
|
} else if ((hint & NS_STYLE_HINT_VISUAL) == NS_STYLE_HINT_VISUAL) {
|
|
|
|
result.AppendLiteral("NS_STYLE_HINT_VISUAL");
|
|
|
|
hint = hint & ~NS_STYLE_HINT_VISUAL;
|
2016-08-05 21:54:22 +03:00
|
|
|
any = true;
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < ArrayLength(names); i++) {
|
|
|
|
if (hint & (1 << i)) {
|
|
|
|
if (any) {
|
|
|
|
result.AppendLiteral(" | ");
|
|
|
|
}
|
|
|
|
result.AppendPrintf("nsChangeHint_%s", names[i]);
|
|
|
|
any = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rest) {
|
|
|
|
if (any) {
|
|
|
|
result.AppendLiteral(" | ");
|
|
|
|
}
|
|
|
|
result.AppendPrintf("0x%0x", rest);
|
|
|
|
} else {
|
|
|
|
if (!any) {
|
|
|
|
result.AppendLiteral("nsChangeHint(0)");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-08-01 23:31:57 +03:00
|
|
|
/**
|
|
|
|
* Frame construction helpers follow.
|
|
|
|
*/
|
|
|
|
#ifdef DEBUG
|
|
|
|
static bool gInApplyRenderingChangeToTree = false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
static void
|
|
|
|
DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
|
|
|
|
{
|
|
|
|
if (aFrame) {
|
|
|
|
fputs("frame: ", stdout);
|
2016-08-01 23:40:27 +03:00
|
|
|
nsAutoString name;
|
2016-08-01 23:31:57 +03:00
|
|
|
aFrame->GetFrameName(name);
|
|
|
|
fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
|
|
|
|
fprintf(stdout, " (%p)", static_cast<void*>(aFrame));
|
|
|
|
}
|
|
|
|
if (aContext) {
|
|
|
|
fprintf(stdout, " style: %p ", static_cast<void*>(aContext));
|
|
|
|
|
|
|
|
nsIAtom* pseudoTag = aContext->GetPseudo();
|
|
|
|
if (pseudoTag) {
|
2016-08-01 23:40:27 +03:00
|
|
|
nsAutoString buffer;
|
2016-08-01 23:31:57 +03:00
|
|
|
pseudoTag->ToString(buffer);
|
|
|
|
fputs(NS_LossyConvertUTF16toASCII(buffer).get(), stdout);
|
|
|
|
fputs(" ", stdout);
|
|
|
|
}
|
|
|
|
fputs("{}\n", stdout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-07-26 19:21:35 +03:00
|
|
|
VerifySameTree(GeckoStyleContext* aContext1, GeckoStyleContext* aContext2)
|
2016-08-01 23:31:57 +03:00
|
|
|
{
|
2017-07-26 19:21:35 +03:00
|
|
|
GeckoStyleContext* top1 = aContext1;
|
|
|
|
GeckoStyleContext* top2 = aContext2;
|
|
|
|
GeckoStyleContext* parent;
|
2016-08-01 23:31:57 +03:00
|
|
|
for (;;) {
|
|
|
|
parent = top1->GetParent();
|
|
|
|
if (!parent)
|
|
|
|
break;
|
|
|
|
top1 = parent;
|
|
|
|
}
|
|
|
|
for (;;) {
|
|
|
|
parent = top2->GetParent();
|
|
|
|
if (!parent)
|
|
|
|
break;
|
|
|
|
top2 = parent;
|
|
|
|
}
|
|
|
|
NS_ASSERTION(top1 == top2,
|
|
|
|
"Style contexts are not in the same style context tree");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2017-07-26 19:21:35 +03:00
|
|
|
VerifyContextParent(nsIFrame* aFrame, GeckoStyleContext* aContext,
|
|
|
|
GeckoStyleContext* aParentContext)
|
2016-08-01 23:31:57 +03:00
|
|
|
{
|
|
|
|
// get the contexts not provided
|
|
|
|
if (!aContext) {
|
2017-07-26 19:21:35 +03:00
|
|
|
aContext = aFrame->StyleContext()->AsGecko();
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!aParentContext) {
|
|
|
|
nsIFrame* providerFrame;
|
2017-07-26 19:21:35 +03:00
|
|
|
nsStyleContext* parent = aFrame->GetParentStyleContext(&providerFrame);
|
|
|
|
aParentContext = parent ? parent->AsGecko() : nullptr;
|
2016-08-01 23:31:57 +03:00
|
|
|
// aParentContext could still be null
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(aContext, "Failure to get required contexts");
|
2017-07-26 19:21:35 +03:00
|
|
|
GeckoStyleContext* actualParentContext = aContext->GetParent();
|
2016-08-01 23:31:57 +03:00
|
|
|
|
|
|
|
if (aParentContext) {
|
|
|
|
if (aParentContext != actualParentContext) {
|
|
|
|
DumpContext(aFrame, aContext);
|
|
|
|
if (aContext == aParentContext) {
|
|
|
|
NS_ERROR("Using parent's style context");
|
2016-08-01 23:40:27 +03:00
|
|
|
} else {
|
2016-08-01 23:31:57 +03:00
|
|
|
NS_ERROR("Wrong parent style context");
|
|
|
|
fputs("Wrong parent style context: ", stdout);
|
|
|
|
DumpContext(nullptr, actualParentContext);
|
|
|
|
fputs("should be using: ", stdout);
|
|
|
|
DumpContext(nullptr, aParentContext);
|
|
|
|
VerifySameTree(actualParentContext, aParentContext);
|
|
|
|
fputs("\n", stdout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-01 23:40:27 +03:00
|
|
|
} else {
|
2016-08-01 23:31:57 +03:00
|
|
|
if (actualParentContext) {
|
|
|
|
NS_ERROR("Have parent context and shouldn't");
|
|
|
|
DumpContext(aFrame, aContext);
|
|
|
|
fputs("Has parent context: ", stdout);
|
|
|
|
DumpContext(nullptr, actualParentContext);
|
|
|
|
fputs("Should be null\n\n", stdout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-26 19:21:35 +03:00
|
|
|
GeckoStyleContext* childStyleIfVisited = aContext->GetStyleIfVisited();
|
2016-08-01 23:31:57 +03:00
|
|
|
// Either childStyleIfVisited has aContext->GetParent()->GetStyleIfVisited()
|
|
|
|
// as the parent or it has a different rulenode from aContext _and_ has
|
|
|
|
// aContext->GetParent() as the parent.
|
|
|
|
if (childStyleIfVisited &&
|
|
|
|
!((childStyleIfVisited->RuleNode() != aContext->RuleNode() &&
|
|
|
|
childStyleIfVisited->GetParent() == aContext->GetParent()) ||
|
|
|
|
childStyleIfVisited->GetParent() ==
|
|
|
|
aContext->GetParent()->GetStyleIfVisited())) {
|
|
|
|
NS_ERROR("Visited style has wrong parent");
|
|
|
|
DumpContext(aFrame, aContext);
|
|
|
|
fputs("\n", stdout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
VerifyStyleTree(nsIFrame* aFrame)
|
|
|
|
{
|
2017-07-26 19:21:35 +03:00
|
|
|
GeckoStyleContext* context = aFrame->StyleContext()->AsGecko();
|
2016-08-01 23:31:57 +03:00
|
|
|
VerifyContextParent(aFrame, context, nullptr);
|
|
|
|
|
|
|
|
nsIFrame::ChildListIterator lists(aFrame);
|
|
|
|
for (; !lists.IsDone(); lists.Next()) {
|
|
|
|
for (nsIFrame* child : lists.CurrentList()) {
|
|
|
|
if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
|
|
|
|
// only do frames that are in flow
|
2017-04-30 18:30:08 +03:00
|
|
|
if (child->IsPlaceholderFrame()) {
|
2016-08-01 23:31:57 +03:00
|
|
|
// placeholder: first recurse and verify the out of flow frame,
|
|
|
|
// then verify the placeholder's context
|
|
|
|
nsIFrame* outOfFlowFrame =
|
|
|
|
nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
|
|
|
|
|
|
|
|
// recurse to out of flow frame, letting the parent context get resolved
|
|
|
|
do {
|
|
|
|
VerifyStyleTree(outOfFlowFrame);
|
|
|
|
} while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
|
|
|
|
|
|
|
|
// verify placeholder using the parent frame's context as
|
|
|
|
// parent context
|
|
|
|
VerifyContextParent(child, nullptr, nullptr);
|
|
|
|
} else { // regular frame
|
|
|
|
VerifyStyleTree(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// do additional contexts
|
|
|
|
int32_t contextIndex = 0;
|
|
|
|
for (nsStyleContext* extraContext;
|
|
|
|
(extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
|
|
|
|
++contextIndex) {
|
2017-07-26 19:21:35 +03:00
|
|
|
VerifyContextParent(aFrame, extraContext->AsGecko(), context);
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-02-13 06:21:32 +03:00
|
|
|
RestyleManager::DebugVerifyStyleTree(nsIFrame* aFrame)
|
2016-08-01 23:31:57 +03:00
|
|
|
{
|
2017-01-11 07:20:24 +03:00
|
|
|
if (IsServo()) {
|
|
|
|
// XXXheycam For now, we know that we don't use the same inheritance
|
|
|
|
// hierarchy for certain cases, so just skip these assertions until
|
|
|
|
// we work out what we want to assert (bug 1322570).
|
|
|
|
return;
|
|
|
|
}
|
2016-08-01 23:31:57 +03:00
|
|
|
if (aFrame) {
|
|
|
|
VerifyStyleTree(aFrame);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // DEBUG
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sync views on aFrame and all of aFrame's descendants (following placeholders),
|
|
|
|
* if aChange has nsChangeHint_SyncFrameView.
|
|
|
|
* Calls DoApplyRenderingChangeToTree on all aFrame's out-of-flow descendants
|
|
|
|
* (following placeholders), if aChange has nsChangeHint_RepaintFrame.
|
|
|
|
* aFrame should be some combination of nsChangeHint_SyncFrameView,
|
|
|
|
* nsChangeHint_RepaintFrame, nsChangeHint_UpdateOpacityLayer and
|
|
|
|
* nsChangeHint_SchedulePaint, nothing else.
|
|
|
|
*/
|
2016-08-01 23:40:27 +03:00
|
|
|
static void SyncViewsAndInvalidateDescendants(nsIFrame* aFrame,
|
|
|
|
nsChangeHint aChange);
|
2016-08-01 23:31:57 +03:00
|
|
|
|
2016-08-01 23:40:27 +03:00
|
|
|
static void StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint);
|
2016-08-01 23:31:57 +03:00
|
|
|
|
|
|
|
/**
|
|
|
|
* To handle nsChangeHint_ChildrenOnlyTransform we must iterate over the child
|
|
|
|
* frames of the SVG frame concerned. This helper function is used to find that
|
|
|
|
* SVG frame when we encounter nsChangeHint_ChildrenOnlyTransform to ensure
|
|
|
|
* that we iterate over the intended children, since sometimes we end up
|
|
|
|
* handling that hint while processing hints for one of the SVG frame's
|
|
|
|
* ancestor frames.
|
|
|
|
*
|
|
|
|
* The reason that we sometimes end up trying to process the hint for an
|
|
|
|
* ancestor of the SVG frame that the hint is intended for is due to the way we
|
|
|
|
* process restyle events. ApplyRenderingChangeToTree adjusts the frame from
|
|
|
|
* the restyled element's principle frame to one of its ancestor frames based
|
|
|
|
* on what nsCSSRendering::FindBackground returns, since the background style
|
|
|
|
* may have been propagated up to an ancestor frame. Processing hints using an
|
|
|
|
* ancestor frame is fine in general, but nsChangeHint_ChildrenOnlyTransform is
|
|
|
|
* a special case since it is intended to update the children of a specific
|
|
|
|
* frame.
|
|
|
|
*/
|
|
|
|
static nsIFrame*
|
|
|
|
GetFrameForChildrenOnlyTransformHint(nsIFrame* aFrame)
|
|
|
|
{
|
2017-04-30 18:30:08 +03:00
|
|
|
if (aFrame->IsViewportFrame()) {
|
2016-08-01 23:31:57 +03:00
|
|
|
// This happens if the root-<svg> is fixed positioned, in which case we
|
|
|
|
// can't use aFrame->GetContent() to find the primary frame, since
|
|
|
|
// GetContent() returns nullptr for ViewportFrame.
|
|
|
|
aFrame = aFrame->PrincipalChildList().FirstChild();
|
|
|
|
}
|
|
|
|
// For an nsHTMLScrollFrame, this will get the SVG frame that has the
|
|
|
|
// children-only transforms:
|
|
|
|
aFrame = aFrame->GetContent()->GetPrimaryFrame();
|
2017-04-30 18:30:08 +03:00
|
|
|
if (aFrame->IsSVGOuterSVGFrame()) {
|
2016-08-01 23:31:57 +03:00
|
|
|
aFrame = aFrame->PrincipalChildList().FirstChild();
|
2017-04-30 18:30:08 +03:00
|
|
|
MOZ_ASSERT(aFrame->IsSVGOuterSVGAnonChildFrame(),
|
2016-08-01 23:31:57 +03:00
|
|
|
"Where is the nsSVGOuterSVGFrame's anon child??");
|
|
|
|
}
|
2016-08-01 23:40:27 +03:00
|
|
|
MOZ_ASSERT(aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer),
|
2016-08-01 23:31:57 +03:00
|
|
|
"Children-only transforms only expected on SVG frames");
|
|
|
|
return aFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if this function managed to successfully move a frame, and
|
|
|
|
// false if it could not process the position change, and a reflow should
|
|
|
|
// be performed instead.
|
|
|
|
bool
|
|
|
|
RecomputePosition(nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
// Don't process position changes on table frames, since we already handle
|
|
|
|
// the dynamic position change on the table wrapper frame, and the
|
|
|
|
// reflow-based fallback code path also ignores positions on inner table
|
|
|
|
// frames.
|
2017-04-30 18:30:08 +03:00
|
|
|
if (aFrame->IsTableFrame()) {
|
2016-08-01 23:31:57 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const nsStyleDisplay* display = aFrame->StyleDisplay();
|
|
|
|
// Changes to the offsets of a non-positioned element can safely be ignored.
|
|
|
|
if (display->mPosition == NS_STYLE_POSITION_STATIC) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't process position changes on frames which have views or the ones which
|
|
|
|
// have a view somewhere in their descendants, because the corresponding view
|
|
|
|
// needs to be repositioned properly as well.
|
|
|
|
if (aFrame->HasView() ||
|
|
|
|
(aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW)) {
|
|
|
|
StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
aFrame->SchedulePaint();
|
|
|
|
|
|
|
|
// For relative positioning, we can simply update the frame rect
|
|
|
|
if (display->IsRelativelyPositionedStyle()) {
|
|
|
|
// Move the frame
|
|
|
|
if (display->mPosition == NS_STYLE_POSITION_STICKY) {
|
|
|
|
if (display->IsInnerTableStyle()) {
|
|
|
|
// We don't currently support sticky positioning of inner table
|
|
|
|
// elements (bug 975644). Bail.
|
|
|
|
//
|
|
|
|
// When this is fixed, remove the null-check for the computed
|
|
|
|
// offsets in nsTableRowFrame::ReflowChildren.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update sticky positioning for an entire element at once, starting with
|
|
|
|
// the first continuation or ib-split sibling.
|
|
|
|
// It's rare that the frame we already have isn't already the first
|
|
|
|
// continuation or ib-split sibling, but it can happen when styles differ
|
|
|
|
// across continuations such as ::first-line or ::first-letter, and in
|
|
|
|
// those cases we will generally (but maybe not always) do the work twice.
|
|
|
|
nsIFrame* firstContinuation =
|
|
|
|
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
|
|
|
|
|
|
|
|
StickyScrollContainer::ComputeStickyOffsets(firstContinuation);
|
|
|
|
StickyScrollContainer* ssc =
|
2016-08-01 23:40:27 +03:00
|
|
|
StickyScrollContainer::GetStickyScrollContainerForFrame(
|
|
|
|
firstContinuation);
|
2016-08-01 23:31:57 +03:00
|
|
|
if (ssc) {
|
|
|
|
ssc->PositionContinuations(firstContinuation);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition,
|
|
|
|
"Unexpected type of positioning");
|
|
|
|
for (nsIFrame* cont = aFrame; cont;
|
|
|
|
cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
|
|
|
|
nsIFrame* cb = cont->GetContainingBlock();
|
|
|
|
nsMargin newOffsets;
|
|
|
|
WritingMode wm = cb->GetWritingMode();
|
|
|
|
const LogicalSize size(wm, cb->GetContentRectRelativeToSelf().Size());
|
|
|
|
|
|
|
|
ReflowInput::ComputeRelativeOffsets(wm, cont, size, newOffsets);
|
|
|
|
NS_ASSERTION(newOffsets.left == -newOffsets.right &&
|
|
|
|
newOffsets.top == -newOffsets.bottom,
|
|
|
|
"ComputeRelativeOffsets should return valid results");
|
|
|
|
|
|
|
|
// ReflowInput::ApplyRelativePositioning would work here, but
|
|
|
|
// since we've already checked mPosition and aren't changing the frame's
|
|
|
|
// normal position, go ahead and add the offsets directly.
|
2016-08-19 20:56:20 +03:00
|
|
|
// First, we need to ensure that the normal position is stored though.
|
2017-05-28 15:16:55 +03:00
|
|
|
bool hasProperty;
|
|
|
|
nsPoint normalPosition = cont->GetNormalPosition(&hasProperty);
|
|
|
|
if (!hasProperty) {
|
|
|
|
cont->AddProperty(nsIFrame::NormalPositionProperty(),
|
2017-05-27 14:36:00 +03:00
|
|
|
new nsPoint(normalPosition));
|
2016-08-19 20:56:20 +03:00
|
|
|
}
|
|
|
|
cont->SetPosition(normalPosition +
|
2016-08-01 23:31:57 +03:00
|
|
|
nsPoint(newOffsets.left, newOffsets.top));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For the absolute positioning case, set up a fake HTML reflow state for
|
|
|
|
// the frame, and then get the offsets and size from it. If the frame's size
|
|
|
|
// doesn't need to change, we can simply update the frame position. Otherwise
|
|
|
|
// we fall back to a reflow.
|
2017-06-09 22:14:53 +03:00
|
|
|
RefPtr<gfxContext> rc =
|
|
|
|
aFrame->PresContext()->PresShell()->CreateReferenceRenderingContext();
|
2016-08-01 23:31:57 +03:00
|
|
|
|
|
|
|
// Construct a bogus parent reflow state so that there's a usable
|
|
|
|
// containing block reflow state.
|
|
|
|
nsIFrame* parentFrame = aFrame->GetParent();
|
|
|
|
WritingMode parentWM = parentFrame->GetWritingMode();
|
|
|
|
WritingMode frameWM = aFrame->GetWritingMode();
|
|
|
|
LogicalSize parentSize = parentFrame->GetLogicalSize();
|
|
|
|
|
|
|
|
nsFrameState savedState = parentFrame->GetStateBits();
|
2017-06-09 22:14:53 +03:00
|
|
|
ReflowInput parentReflowInput(aFrame->PresContext(), parentFrame, rc,
|
2016-08-01 23:40:27 +03:00
|
|
|
parentSize);
|
2016-08-01 23:31:57 +03:00
|
|
|
parentFrame->RemoveStateBits(~nsFrameState(0));
|
|
|
|
parentFrame->AddStateBits(savedState);
|
|
|
|
|
|
|
|
// The bogus parent state here was created with no parent state of its own,
|
|
|
|
// and therefore it won't have an mCBReflowInput set up.
|
|
|
|
// But we may need one (for InitCBReflowInput in a child state), so let's
|
|
|
|
// try to create one here for the cases where it will be needed.
|
|
|
|
Maybe<ReflowInput> cbReflowInput;
|
|
|
|
nsIFrame* cbFrame = parentFrame->GetContainingBlock();
|
|
|
|
if (cbFrame && (aFrame->GetContainingBlock() != parentFrame ||
|
2017-04-30 18:30:08 +03:00
|
|
|
parentFrame->IsTableFrame())) {
|
2016-08-01 23:31:57 +03:00
|
|
|
LogicalSize cbSize = cbFrame->GetLogicalSize();
|
2017-06-09 22:14:53 +03:00
|
|
|
cbReflowInput.emplace(cbFrame->PresContext(), cbFrame, rc, cbSize);
|
2016-08-01 23:31:57 +03:00
|
|
|
cbReflowInput->ComputedPhysicalMargin() = cbFrame->GetUsedMargin();
|
|
|
|
cbReflowInput->ComputedPhysicalPadding() = cbFrame->GetUsedPadding();
|
|
|
|
cbReflowInput->ComputedPhysicalBorderPadding() =
|
|
|
|
cbFrame->GetUsedBorderAndPadding();
|
|
|
|
parentReflowInput.mCBReflowInput = cbReflowInput.ptr();
|
|
|
|
}
|
|
|
|
|
2016-09-01 08:01:16 +03:00
|
|
|
NS_WARNING_ASSERTION(parentSize.ISize(parentWM) != NS_INTRINSICSIZE &&
|
|
|
|
parentSize.BSize(parentWM) != NS_INTRINSICSIZE,
|
|
|
|
"parentSize should be valid");
|
2016-08-01 23:31:57 +03:00
|
|
|
parentReflowInput.SetComputedISize(std::max(parentSize.ISize(parentWM), 0));
|
|
|
|
parentReflowInput.SetComputedBSize(std::max(parentSize.BSize(parentWM), 0));
|
|
|
|
parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
|
|
|
|
|
|
|
|
parentReflowInput.ComputedPhysicalPadding() = parentFrame->GetUsedPadding();
|
|
|
|
parentReflowInput.ComputedPhysicalBorderPadding() =
|
|
|
|
parentFrame->GetUsedBorderAndPadding();
|
|
|
|
LogicalSize availSize = parentSize.ConvertTo(frameWM, parentWM);
|
|
|
|
availSize.BSize(frameWM) = NS_INTRINSICSIZE;
|
|
|
|
|
|
|
|
ViewportFrame* viewport = do_QueryFrame(parentFrame);
|
|
|
|
nsSize cbSize = viewport ?
|
|
|
|
viewport->AdjustReflowInputAsContainingBlock(&parentReflowInput).Size()
|
|
|
|
: aFrame->GetContainingBlock()->GetSize();
|
|
|
|
const nsMargin& parentBorder =
|
|
|
|
parentReflowInput.mStyleBorder->GetComputedBorder();
|
|
|
|
cbSize -= nsSize(parentBorder.LeftRight(), parentBorder.TopBottom());
|
|
|
|
LogicalSize lcbSize(frameWM, cbSize);
|
2016-08-01 23:40:27 +03:00
|
|
|
ReflowInput reflowInput(aFrame->PresContext(), parentReflowInput, aFrame,
|
|
|
|
availSize, &lcbSize);
|
|
|
|
nsSize computedSize(reflowInput.ComputedWidth(),
|
|
|
|
reflowInput.ComputedHeight());
|
2016-08-01 23:31:57 +03:00
|
|
|
computedSize.width += reflowInput.ComputedPhysicalBorderPadding().LeftRight();
|
|
|
|
if (computedSize.height != NS_INTRINSICSIZE) {
|
2016-08-01 23:40:27 +03:00
|
|
|
computedSize.height +=
|
|
|
|
reflowInput.ComputedPhysicalBorderPadding().TopBottom();
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
nsSize size = aFrame->GetSize();
|
|
|
|
// The RecomputePosition hint is not used if any offset changed between auto
|
|
|
|
// and non-auto. If computedSize.height == NS_INTRINSICSIZE then the new
|
|
|
|
// element height will be its intrinsic height, and since 'top' and 'bottom''s
|
|
|
|
// auto-ness hasn't changed, the old height must also be its intrinsic
|
|
|
|
// height, which we can assume hasn't changed (or reflow would have
|
|
|
|
// been triggered).
|
|
|
|
if (computedSize.width == size.width &&
|
|
|
|
(computedSize.height == NS_INTRINSICSIZE || computedSize.height == size.height)) {
|
|
|
|
// If we're solving for 'left' or 'top', then compute it here, in order to
|
|
|
|
// match the reflow code path.
|
|
|
|
if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().left) {
|
|
|
|
reflowInput.ComputedPhysicalOffsets().left = cbSize.width -
|
|
|
|
reflowInput.ComputedPhysicalOffsets().right -
|
|
|
|
reflowInput.ComputedPhysicalMargin().right -
|
|
|
|
size.width -
|
|
|
|
reflowInput.ComputedPhysicalMargin().left;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NS_AUTOOFFSET == reflowInput.ComputedPhysicalOffsets().top) {
|
|
|
|
reflowInput.ComputedPhysicalOffsets().top = cbSize.height -
|
|
|
|
reflowInput.ComputedPhysicalOffsets().bottom -
|
|
|
|
reflowInput.ComputedPhysicalMargin().bottom -
|
|
|
|
size.height -
|
|
|
|
reflowInput.ComputedPhysicalMargin().top;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move the frame
|
|
|
|
nsPoint pos(parentBorder.left + reflowInput.ComputedPhysicalOffsets().left +
|
|
|
|
reflowInput.ComputedPhysicalMargin().left,
|
|
|
|
parentBorder.top + reflowInput.ComputedPhysicalOffsets().top +
|
|
|
|
reflowInput.ComputedPhysicalMargin().top);
|
|
|
|
aFrame->SetPosition(pos);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fall back to a reflow
|
|
|
|
StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
HasBoxAncestor(nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
for (nsIFrame* f = aFrame; f; f = f->GetParent()) {
|
|
|
|
if (f->IsXULBoxFrame()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return true if aFrame's subtree has placeholders for out-of-flow content
|
|
|
|
* whose 'position' style's bit in aPositionMask is set.
|
|
|
|
*/
|
|
|
|
static bool
|
2016-08-01 23:40:27 +03:00
|
|
|
FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame,
|
|
|
|
uint32_t aPositionMask)
|
2016-08-01 23:31:57 +03:00
|
|
|
{
|
2016-08-26 22:38:16 +03:00
|
|
|
MOZ_ASSERT(aPositionMask & (1 << NS_STYLE_POSITION_FIXED));
|
|
|
|
|
2016-08-01 23:31:57 +03:00
|
|
|
for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
|
2016-08-26 22:38:16 +03:00
|
|
|
for (nsIFrame* f : lists.CurrentList()) {
|
2017-04-30 18:30:08 +03:00
|
|
|
if (f->IsPlaceholderFrame()) {
|
2016-08-26 22:38:16 +03:00
|
|
|
nsIFrame* outOfFlow =
|
|
|
|
nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
|
|
|
|
// If SVG text frames could appear here, they could confuse us since
|
|
|
|
// they ignore their position style ... but they can't.
|
2017-03-23 10:29:11 +03:00
|
|
|
NS_ASSERTION(!nsSVGUtils::IsInSVGTextSubtree(outOfFlow),
|
2016-08-26 22:38:16 +03:00
|
|
|
"SVG text frames can't be out of flow");
|
|
|
|
if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
|
2016-08-01 23:31:57 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2016-08-26 22:38:16 +03:00
|
|
|
uint32_t positionMask = aPositionMask;
|
2016-10-04 21:39:36 +03:00
|
|
|
// NOTE: It's tempting to check f->IsAbsPosContainingBlock() or
|
|
|
|
// f->IsFixedPosContainingBlock() here. However, that would only
|
|
|
|
// be testing the *new* style of the frame, which might exclude
|
|
|
|
// descendants that currently have this frame as an abs-pos
|
|
|
|
// containing block. Taking the codepath where we don't reframe
|
|
|
|
// could lead to an unsafe call to
|
|
|
|
// cont->MarkAsNotAbsoluteContainingBlock() before we've reframed
|
|
|
|
// the descendant and taken it off the absolute list.
|
2016-08-26 22:38:16 +03:00
|
|
|
if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
|
|
|
|
return true;
|
|
|
|
}
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
NeedToReframeForAddingOrRemovingTransform(nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
static_assert(0 <= NS_STYLE_POSITION_ABSOLUTE &&
|
|
|
|
NS_STYLE_POSITION_ABSOLUTE < 32, "Style constant out of range");
|
|
|
|
static_assert(0 <= NS_STYLE_POSITION_FIXED &&
|
|
|
|
NS_STYLE_POSITION_FIXED < 32, "Style constant out of range");
|
|
|
|
|
|
|
|
uint32_t positionMask;
|
|
|
|
// Don't call aFrame->IsPositioned here, since that returns true if
|
|
|
|
// the frame already has a transform, and we want to ignore that here
|
2016-08-01 23:40:27 +03:00
|
|
|
if (aFrame->IsAbsolutelyPositioned() || aFrame->IsRelativelyPositioned()) {
|
2016-08-01 23:31:57 +03:00
|
|
|
// This frame is a container for abs-pos descendants whether or not it
|
|
|
|
// has a transform.
|
|
|
|
// So abs-pos descendants are no problem; we only need to reframe if
|
|
|
|
// we have fixed-pos descendants.
|
|
|
|
positionMask = 1 << NS_STYLE_POSITION_FIXED;
|
|
|
|
} else {
|
|
|
|
// This frame may not be a container for abs-pos descendants already.
|
|
|
|
// So reframe if we have abs-pos or fixed-pos descendants.
|
2016-08-01 23:40:27 +03:00
|
|
|
positionMask =
|
|
|
|
(1 << NS_STYLE_POSITION_FIXED) | (1 << NS_STYLE_POSITION_ABSOLUTE);
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
for (nsIFrame* f = aFrame; f;
|
|
|
|
f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
|
|
|
|
if (FrameHasPositionedPlaceholderDescendants(f, positionMask)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
DoApplyRenderingChangeToTree(nsIFrame* aFrame,
|
|
|
|
nsChangeHint aChange)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(gInApplyRenderingChangeToTree,
|
|
|
|
"should only be called within ApplyRenderingChangeToTree");
|
|
|
|
|
|
|
|
for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
|
|
|
|
// Invalidate and sync views on all descendant frames, following placeholders.
|
|
|
|
// We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
|
|
|
|
// there can't be any out-of-flows or popups that need to be transformed;
|
|
|
|
// all out-of-flow descendants of the transformed element must also be
|
|
|
|
// descendants of the transformed frame.
|
|
|
|
SyncViewsAndInvalidateDescendants(aFrame,
|
|
|
|
nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
|
|
|
|
nsChangeHint_SyncFrameView |
|
|
|
|
nsChangeHint_UpdateOpacityLayer |
|
|
|
|
nsChangeHint_SchedulePaint)));
|
|
|
|
// This must be set to true if the rendering change needs to
|
|
|
|
// invalidate content. If it's false, a composite-only paint
|
|
|
|
// (empty transaction) will be scheduled.
|
|
|
|
bool needInvalidatingPaint = false;
|
|
|
|
|
|
|
|
// if frame has view, will already be invalidated
|
|
|
|
if (aChange & nsChangeHint_RepaintFrame) {
|
|
|
|
// Note that this whole block will be skipped when painting is suppressed
|
|
|
|
// (due to our caller ApplyRendingChangeToTree() discarding the
|
|
|
|
// nsChangeHint_RepaintFrame hint). If you add handling for any other
|
|
|
|
// hints within this block, be sure that they too should be ignored when
|
|
|
|
// painting is suppressed.
|
|
|
|
needInvalidatingPaint = true;
|
|
|
|
aFrame->InvalidateFrameSubtree();
|
|
|
|
if ((aChange & nsChangeHint_UpdateEffects) &&
|
|
|
|
aFrame->IsFrameOfType(nsIFrame::eSVG) &&
|
|
|
|
!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
|
|
|
|
// Need to update our overflow rects:
|
|
|
|
nsSVGUtils::ScheduleReflowSVG(aFrame);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (aChange & nsChangeHint_UpdateTextPath) {
|
2017-03-23 10:29:11 +03:00
|
|
|
if (nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
|
2016-08-01 23:31:57 +03:00
|
|
|
// Invalidate and reflow the entire SVGTextFrame:
|
|
|
|
NS_ASSERTION(aFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
|
|
|
|
"expected frame for a <textPath> element");
|
2017-05-01 20:32:52 +03:00
|
|
|
nsIFrame* text = nsLayoutUtils::GetClosestFrameOfType(
|
|
|
|
aFrame, LayoutFrameType::SVGText);
|
2016-08-01 23:31:57 +03:00
|
|
|
NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
|
|
|
|
static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
|
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(false, "unexpected frame got nsChangeHint_UpdateTextPath");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (aChange & nsChangeHint_UpdateOpacityLayer) {
|
|
|
|
// FIXME/bug 796697: we can get away with empty transactions for
|
|
|
|
// opacity updates in many cases.
|
|
|
|
needInvalidatingPaint = true;
|
|
|
|
|
2017-08-13 12:26:13 +03:00
|
|
|
ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
|
2016-08-01 23:31:57 +03:00
|
|
|
if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
|
|
|
|
// SVG effects paints the opacity without using
|
|
|
|
// nsDisplayOpacity. We need to invalidate manually.
|
|
|
|
aFrame->InvalidateFrameSubtree();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((aChange & nsChangeHint_UpdateTransformLayer) &&
|
|
|
|
aFrame->IsTransformed()) {
|
|
|
|
ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
|
|
|
|
// If we're not already going to do an invalidating paint, see
|
|
|
|
// if we can get away with only updating the transform on a
|
|
|
|
// layer for this frame, and not scheduling an invalidating
|
|
|
|
// paint.
|
|
|
|
if (!needInvalidatingPaint) {
|
|
|
|
Layer* layer;
|
|
|
|
needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);
|
|
|
|
|
|
|
|
if (!needInvalidatingPaint) {
|
|
|
|
// Since we're not going to paint, we need to resend animation
|
|
|
|
// data to the layer.
|
|
|
|
MOZ_ASSERT(layer, "this can't happen if there's no layer");
|
2016-08-01 23:40:27 +03:00
|
|
|
nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
|
|
|
|
layer, nullptr, nullptr, aFrame, eCSSProperty_transform);
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (aChange & nsChangeHint_ChildrenOnlyTransform) {
|
|
|
|
needInvalidatingPaint = true;
|
|
|
|
nsIFrame* childFrame =
|
|
|
|
GetFrameForChildrenOnlyTransformHint(aFrame)->PrincipalChildList().FirstChild();
|
|
|
|
for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
|
|
|
|
ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (aChange & nsChangeHint_SchedulePaint) {
|
|
|
|
needInvalidatingPaint = true;
|
|
|
|
}
|
2016-08-01 23:40:27 +03:00
|
|
|
aFrame->SchedulePaint(needInvalidatingPaint
|
|
|
|
? nsIFrame::PAINT_DEFAULT
|
|
|
|
: nsIFrame::PAINT_COMPOSITE_ONLY);
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-03 01:47:19 +03:00
|
|
|
static void
|
2016-08-01 23:40:27 +03:00
|
|
|
SyncViewsAndInvalidateDescendants(nsIFrame* aFrame, nsChangeHint aChange)
|
2016-08-01 23:31:57 +03:00
|
|
|
{
|
2016-08-01 23:40:27 +03:00
|
|
|
NS_PRECONDITION(gInApplyRenderingChangeToTree,
|
|
|
|
"should only be called within ApplyRenderingChangeToTree");
|
2016-08-01 23:31:57 +03:00
|
|
|
NS_ASSERTION(nsChangeHint_size_t(aChange) ==
|
|
|
|
(aChange & (nsChangeHint_RepaintFrame |
|
|
|
|
nsChangeHint_SyncFrameView |
|
|
|
|
nsChangeHint_UpdateOpacityLayer |
|
|
|
|
nsChangeHint_SchedulePaint)),
|
|
|
|
"Invalid change flag");
|
|
|
|
|
2017-03-21 03:22:13 +03:00
|
|
|
if (aChange & nsChangeHint_SyncFrameView) {
|
|
|
|
aFrame->SyncFrameViewProperties();
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame::ChildListIterator lists(aFrame);
|
|
|
|
for (; !lists.IsDone(); lists.Next()) {
|
|
|
|
for (nsIFrame* child : lists.CurrentList()) {
|
|
|
|
if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
|
|
|
|
// only do frames that don't have placeholders
|
2017-04-30 18:30:08 +03:00
|
|
|
if (child->IsPlaceholderFrame()) {
|
2016-08-01 23:31:57 +03:00
|
|
|
// do the out-of-flow frame and its continuations
|
|
|
|
nsIFrame* outOfFlowFrame =
|
|
|
|
nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
|
|
|
|
DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
|
|
|
|
} else if (lists.CurrentID() == nsIFrame::kPopupList) {
|
|
|
|
DoApplyRenderingChangeToTree(child, aChange);
|
2016-08-01 23:40:27 +03:00
|
|
|
} else { // regular frame
|
2016-08-01 23:31:57 +03:00
|
|
|
SyncViewsAndInvalidateDescendants(child, aChange);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-03 01:47:19 +03:00
|
|
|
static void
|
2016-08-01 23:31:57 +03:00
|
|
|
ApplyRenderingChangeToTree(nsIPresShell* aPresShell,
|
|
|
|
nsIFrame* aFrame,
|
|
|
|
nsChangeHint aChange)
|
|
|
|
{
|
|
|
|
// We check StyleDisplay()->HasTransformStyle() in addition to checking
|
|
|
|
// IsTransformed() since we can get here for some frames that don't support
|
|
|
|
// CSS transforms.
|
|
|
|
NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
|
|
|
|
aFrame->IsTransformed() ||
|
|
|
|
aFrame->StyleDisplay()->HasTransformStyle(),
|
|
|
|
"Unexpected UpdateTransformLayer hint");
|
|
|
|
|
|
|
|
if (aPresShell->IsPaintingSuppressed()) {
|
|
|
|
// Don't allow synchronous rendering changes when painting is turned off.
|
|
|
|
aChange &= ~nsChangeHint_RepaintFrame;
|
|
|
|
if (!aChange) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-01 23:40:27 +03:00
|
|
|
// Trigger rendering updates by damaging this frame and any
|
|
|
|
// continuations of this frame.
|
2016-08-01 23:31:57 +03:00
|
|
|
#ifdef DEBUG
|
|
|
|
gInApplyRenderingChangeToTree = true;
|
|
|
|
#endif
|
|
|
|
if (aChange & nsChangeHint_RepaintFrame) {
|
|
|
|
// If the frame's background is propagated to an ancestor, walk up to
|
|
|
|
// that ancestor and apply the RepaintFrame change hint to it.
|
|
|
|
nsStyleContext* bgSC;
|
|
|
|
nsIFrame* propagatedFrame = aFrame;
|
|
|
|
while (!nsCSSRendering::FindBackground(propagatedFrame, &bgSC)) {
|
|
|
|
propagatedFrame = propagatedFrame->GetParent();
|
|
|
|
NS_ASSERTION(aFrame, "root frame must paint");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (propagatedFrame != aFrame) {
|
|
|
|
DoApplyRenderingChangeToTree(propagatedFrame, nsChangeHint_RepaintFrame);
|
|
|
|
aChange &= ~nsChangeHint_RepaintFrame;
|
|
|
|
if (!aChange) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DoApplyRenderingChangeToTree(aFrame, aChange);
|
|
|
|
#ifdef DEBUG
|
|
|
|
gInApplyRenderingChangeToTree = false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
AddSubtreeToOverflowTracker(nsIFrame* aFrame,
|
|
|
|
OverflowChangedTracker& aOverflowChangedTracker)
|
|
|
|
{
|
|
|
|
if (aFrame->FrameMaintainsOverflow()) {
|
|
|
|
aOverflowChangedTracker.AddFrame(aFrame,
|
|
|
|
OverflowChangedTracker::CHILDREN_CHANGED);
|
|
|
|
}
|
|
|
|
nsIFrame::ChildListIterator lists(aFrame);
|
|
|
|
for (; !lists.IsDone(); lists.Next()) {
|
|
|
|
for (nsIFrame* child : lists.CurrentList()) {
|
|
|
|
AddSubtreeToOverflowTracker(child, aOverflowChangedTracker);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint)
|
|
|
|
{
|
|
|
|
nsIPresShell::IntrinsicDirty dirtyType;
|
|
|
|
if (aHint & nsChangeHint_ClearDescendantIntrinsics) {
|
|
|
|
NS_ASSERTION(aHint & nsChangeHint_ClearAncestorIntrinsics,
|
|
|
|
"Please read the comments in nsChangeHint.h");
|
|
|
|
NS_ASSERTION(aHint & nsChangeHint_NeedDirtyReflow,
|
|
|
|
"ClearDescendantIntrinsics requires NeedDirtyReflow");
|
|
|
|
dirtyType = nsIPresShell::eStyleChange;
|
|
|
|
} else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
|
2016-08-01 23:40:27 +03:00
|
|
|
aFrame->HasAnyStateBits(
|
|
|
|
NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
|
2016-08-01 23:31:57 +03:00
|
|
|
dirtyType = nsIPresShell::eStyleChange;
|
|
|
|
} else if (aHint & nsChangeHint_ClearAncestorIntrinsics) {
|
|
|
|
dirtyType = nsIPresShell::eTreeChange;
|
|
|
|
} else if ((aHint & nsChangeHint_UpdateComputedBSize) &&
|
|
|
|
HasBoxAncestor(aFrame)) {
|
|
|
|
// The frame's computed BSize is changing, and we have a box ancestor
|
|
|
|
// whose cached intrinsic height may need to be updated.
|
|
|
|
dirtyType = nsIPresShell::eTreeChange;
|
|
|
|
} else {
|
|
|
|
dirtyType = nsIPresShell::eResize;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsFrameState dirtyBits;
|
|
|
|
if (aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
|
|
|
|
dirtyBits = nsFrameState(0);
|
|
|
|
} else if ((aHint & nsChangeHint_NeedDirtyReflow) ||
|
|
|
|
dirtyType == nsIPresShell::eStyleChange) {
|
|
|
|
dirtyBits = NS_FRAME_IS_DIRTY;
|
|
|
|
} else {
|
|
|
|
dirtyBits = NS_FRAME_HAS_DIRTY_CHILDREN;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we're not going to clear any intrinsic sizes on the frames, and
|
|
|
|
// there are no dirty bits to set, then there's nothing to do.
|
|
|
|
if (dirtyType == nsIPresShell::eResize && !dirtyBits)
|
|
|
|
return;
|
|
|
|
|
|
|
|
nsIPresShell::ReflowRootHandling rootHandling;
|
|
|
|
if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) {
|
|
|
|
rootHandling = nsIPresShell::ePositionOrSizeChange;
|
|
|
|
} else {
|
|
|
|
rootHandling = nsIPresShell::eNoPositionOrSizeChange;
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
2016-08-01 23:40:27 +03:00
|
|
|
aFrame->PresContext()->PresShell()->FrameNeedsReflow(
|
|
|
|
aFrame, dirtyType, dirtyBits, rootHandling);
|
2016-08-01 23:31:57 +03:00
|
|
|
aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame);
|
|
|
|
} while (aFrame);
|
|
|
|
}
|
|
|
|
|
2017-08-11 07:17:07 +03:00
|
|
|
// Get the next sibling which might have a frame. This only considers siblings
|
|
|
|
// that stylo post-traversal looks at, so only elements and text. In
|
|
|
|
// particular, it ignores comments.
|
|
|
|
static nsIContent*
|
|
|
|
NextSiblingWhichMayHaveFrame(nsIContent* aContent)
|
|
|
|
{
|
|
|
|
for (nsIContent* next = aContent->GetNextSibling();
|
|
|
|
next;
|
|
|
|
next = next->GetNextSibling()) {
|
|
|
|
if (next->IsElement() || next->IsNodeOfType(nsINode::eTEXT)) {
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-02-17 22:25:56 +03:00
|
|
|
void
|
2017-02-13 06:21:32 +03:00
|
|
|
RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
|
2016-08-01 23:31:57 +03:00
|
|
|
{
|
|
|
|
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
|
|
|
|
"Someone forgot a script blocker");
|
2017-07-19 11:13:43 +03:00
|
|
|
|
|
|
|
// See bug 1378219 comment 9:
|
|
|
|
// Recursive calls here are a bit worrying, but apparently do happen in the
|
|
|
|
// wild (although not currently in any of our automated tests). Try to get a
|
|
|
|
// stack from Nightly/Dev channel to figure out what's going on and whether
|
|
|
|
// it's OK.
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!mDestroyedFrames, "ProcessRestyledFrames recursion");
|
2017-05-27 14:36:00 +03:00
|
|
|
|
|
|
|
if (aChangeList.IsEmpty()) {
|
2017-02-17 22:25:56 +03:00
|
|
|
return;
|
2017-05-27 14:36:00 +03:00
|
|
|
}
|
|
|
|
|
2017-07-19 11:13:43 +03:00
|
|
|
// If mDestroyedFrames is null, we want to create a new hashtable here
|
|
|
|
// and destroy it on exit; but if it is already non-null (because we're in
|
|
|
|
// a recursive call), we will continue to use the existing table to
|
|
|
|
// accumulate destroyed frames, and NOT clear mDestroyedFrames on exit.
|
|
|
|
// We use a MaybeClearDestroyedFrames helper to conditionally reset the
|
|
|
|
// mDestroyedFrames pointer when this method returns.
|
|
|
|
typedef decltype(mDestroyedFrames) DestroyedFramesT;
|
|
|
|
class MOZ_RAII MaybeClearDestroyedFrames
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
DestroyedFramesT& mDestroyedFramesRef; // ref to caller's mDestroyedFrames
|
|
|
|
const bool mResetOnDestruction;
|
|
|
|
public:
|
|
|
|
explicit MaybeClearDestroyedFrames(DestroyedFramesT& aTarget)
|
|
|
|
: mDestroyedFramesRef(aTarget)
|
|
|
|
, mResetOnDestruction(!aTarget) // reset only if target starts out null
|
|
|
|
{
|
|
|
|
}
|
|
|
|
~MaybeClearDestroyedFrames()
|
|
|
|
{
|
|
|
|
if (mResetOnDestruction) {
|
|
|
|
mDestroyedFramesRef.reset(nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
MaybeClearDestroyedFrames maybeClear(mDestroyedFrames);
|
|
|
|
if (!mDestroyedFrames) {
|
|
|
|
mDestroyedFrames = MakeUnique<nsTHashtable<nsPtrHashKey<const nsIFrame>>>();
|
|
|
|
}
|
2016-08-01 23:31:57 +03:00
|
|
|
|
Bug 1375392 - Tweak the PROFILER_LABEL* macros. r=mstange.
This patch makes the following changes to the macros.
- Removes PROFILER_LABEL_FUNC. It's only suitable for use in functions outside
classes, due to PROFILER_FUNCTION_NAME not getting class names, and it was
mostly misused.
- Removes PROFILER_FUNCTION_NAME. It's no longer used, and __func__ is
universally available now anyway.
- Combines the first two string literal arguments of PROFILER_LABEL and
PROFILER_LABEL_DYNAMIC into a single argument. There was no good reason for
them to be separate, and it forced a '::' in the label, which isn't always
appropriate. Also, the meaning of the "name_space" argument was interpreted
in an interesting variety of ways.
- Adds an "AUTO_" prefix to PROFILER_LABEL and PROFILER_LABEL_DYNAMIC, to make
it clearer they construct RAII objects rather than just being function calls.
(I myself have screwed up the scoping because of this in the past.)
- Fills in the 'js::ProfileEntry::Category::' qualifier within the macro, so
the caller doesn't need to. This makes a *lot* more of the uses fit onto a
single line.
The patch also makes the following changes to the macro uses (beyond those
required by the changes described above).
- Fixes a bunch of labels that had gotten out of sync with the name of the
class and/or function that encloses them.
- Removes a useless PROFILER_LABEL use within a trivial scope in
EventStateManager::DispatchMouseOrPointerEvent(). It clearly wasn't serving
any useful purpose. It also serves as extra evidence that the AUTO_ prefix is
a good idea.
- Tweaks DecodePool::SyncRunIf{Preferred,Possible} so that the labelling is
done within them, instead of at their callsites, because that's a more
standard way of doing things.
--HG--
extra : rebase_source : 318d1bc6fc1425a94aacbf489dd46e4f83211de4
2017-06-22 10:08:53 +03:00
|
|
|
AUTO_PROFILER_LABEL("RestyleManager::ProcessRestyledFrames", CSS);
|
2016-08-01 23:31:57 +03:00
|
|
|
|
2016-08-03 01:47:19 +03:00
|
|
|
nsPresContext* presContext = PresContext();
|
|
|
|
nsCSSFrameConstructor* frameConstructor = presContext->FrameConstructor();
|
2016-08-01 23:31:57 +03:00
|
|
|
|
2017-05-10 23:53:27 +03:00
|
|
|
// Handle nsChangeHint_CSSOverflowChange, by either updating the
|
|
|
|
// scrollbars on the viewport, or upgrading the change hint to frame-reconstruct.
|
|
|
|
for (nsStyleChangeData& data : aChangeList) {
|
|
|
|
if (data.mHint & nsChangeHint_CSSOverflowChange) {
|
|
|
|
data.mHint &= ~nsChangeHint_CSSOverflowChange;
|
|
|
|
bool doReconstruct = true; // assume the worst
|
|
|
|
|
|
|
|
// Only bother with this if we're html/body, since:
|
|
|
|
// (a) It'd be *expensive* to reframe these particular nodes. They're
|
|
|
|
// at the root, so reframing would mean rebuilding the world.
|
|
|
|
// (b) It's often *unnecessary* to reframe for "overflow" changes on
|
|
|
|
// these particular nodes. In general, the only reason we reframe
|
|
|
|
// for "overflow" changes is so we can construct (or destroy) a
|
|
|
|
// scrollframe & scrollbars -- and the html/body nodes often don't
|
|
|
|
// need their own scrollframe/scrollbars because they coopt the ones
|
|
|
|
// on the viewport (which always exist). So depending on whether
|
|
|
|
// that's happening, we can skip the reframe for these nodes.
|
|
|
|
if (data.mContent->IsAnyOfHTMLElements(nsGkAtoms::body,
|
|
|
|
nsGkAtoms::html)) {
|
|
|
|
// If the restyled element provided/provides the scrollbar styles for
|
|
|
|
// the viewport before and/or after this restyle, AND it's not coopting
|
|
|
|
// that responsibility from some other element (which would need
|
|
|
|
// reconstruction to make its own scrollframe now), THEN: we don't need
|
|
|
|
// to reconstruct - we can just reflow, because no scrollframe is being
|
|
|
|
// added/removed.
|
|
|
|
nsIContent* prevOverrideNode =
|
|
|
|
presContext->GetViewportScrollbarStylesOverrideNode();
|
|
|
|
nsIContent* newOverrideNode =
|
|
|
|
presContext->UpdateViewportScrollbarStylesOverride();
|
|
|
|
|
|
|
|
if (data.mContent == prevOverrideNode ||
|
|
|
|
data.mContent == newOverrideNode) {
|
|
|
|
// If we get here, the restyled element provided the scrollbar styles
|
|
|
|
// for viewport before this restyle, OR it will provide them after.
|
|
|
|
if (!prevOverrideNode || !newOverrideNode ||
|
|
|
|
prevOverrideNode == newOverrideNode) {
|
|
|
|
// If we get here, the restyled element is NOT replacing (or being
|
|
|
|
// replaced by) some other element as the viewport's
|
|
|
|
// scrollbar-styles provider. (If it were, we'd potentially need to
|
|
|
|
// reframe to create a dedicated scrollframe for whichever element
|
|
|
|
// is being booted from providing viewport scrollbar styles.)
|
|
|
|
//
|
|
|
|
// Under these conditions, we're OK to assume that this "overflow"
|
|
|
|
// change only impacts the root viewport's scrollframe, which
|
|
|
|
// already exists, so we can simply reflow instead of reframing.
|
2017-05-26 00:53:27 +03:00
|
|
|
// When requesting this reflow, we send the exact same change hints
|
|
|
|
// that "width" and "height" would send (since conceptually,
|
|
|
|
// adding/removing scrollbars is like changing the available
|
|
|
|
// space).
|
|
|
|
data.mHint |= (nsChangeHint_ReflowHintsForISizeChange |
|
|
|
|
nsChangeHint_ReflowHintsForBSizeChange);
|
2017-05-10 23:53:27 +03:00
|
|
|
doReconstruct = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (doReconstruct) {
|
|
|
|
data.mHint |= nsChangeHint_ReconstructFrame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-01 23:31:57 +03:00
|
|
|
// Make sure to not rebuild quote or counter lists while we're
|
|
|
|
// processing restyles
|
|
|
|
frameConstructor->BeginUpdate();
|
|
|
|
|
|
|
|
bool didUpdateCursor = false;
|
|
|
|
|
2017-03-03 09:51:39 +03:00
|
|
|
for (size_t i = 0; i < aChangeList.Length(); ++i) {
|
|
|
|
|
|
|
|
// Collect and coalesce adjacent siblings for lazy frame construction.
|
|
|
|
// Eventually it would be even better to make RecreateFramesForContent
|
|
|
|
// accept a range and coalesce all adjacent reconstructs (bug 1344139).
|
|
|
|
size_t lazyRangeStart = i;
|
|
|
|
while (i < aChangeList.Length() &&
|
|
|
|
aChangeList[i].mContent &&
|
|
|
|
aChangeList[i].mContent->HasFlag(NODE_NEEDS_FRAME) &&
|
|
|
|
(i == lazyRangeStart ||
|
2017-08-11 07:17:07 +03:00
|
|
|
NextSiblingWhichMayHaveFrame(aChangeList[i - 1].mContent) ==
|
|
|
|
aChangeList[i].mContent))
|
2017-03-03 09:51:39 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(aChangeList[i].mHint & nsChangeHint_ReconstructFrame);
|
|
|
|
MOZ_ASSERT(!aChangeList[i].mFrame);
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
if (i != lazyRangeStart) {
|
|
|
|
nsIContent* start = aChangeList[lazyRangeStart].mContent;
|
2017-08-11 07:17:07 +03:00
|
|
|
nsIContent* end = NextSiblingWhichMayHaveFrame(aChangeList[i-1].mContent);
|
2017-02-28 06:06:07 +03:00
|
|
|
nsIContent* container = start->GetParent();
|
2017-03-03 09:51:39 +03:00
|
|
|
MOZ_ASSERT(container);
|
|
|
|
if (!end) {
|
2017-09-07 14:39:20 +03:00
|
|
|
frameConstructor->ContentAppended(
|
|
|
|
container,
|
|
|
|
start,
|
|
|
|
nsCSSFrameConstructor::LazyConstructionAllowed::No);
|
2017-03-03 09:51:39 +03:00
|
|
|
} else {
|
2017-09-07 14:39:20 +03:00
|
|
|
frameConstructor->ContentRangeInserted(
|
|
|
|
container,
|
|
|
|
start,
|
|
|
|
end,
|
|
|
|
nullptr,
|
|
|
|
nsCSSFrameConstructor::LazyConstructionAllowed::No);
|
2017-03-03 09:51:39 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for (size_t j = lazyRangeStart; j < i; ++j) {
|
2017-06-07 08:27:17 +03:00
|
|
|
MOZ_ASSERT(!aChangeList[j].mContent->GetPrimaryFrame() ||
|
|
|
|
!aChangeList[j].mContent->HasFlag(NODE_NEEDS_FRAME));
|
2017-03-03 09:51:39 +03:00
|
|
|
}
|
|
|
|
if (i == aChangeList.Length()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-04-05 06:59:21 +03:00
|
|
|
nsStyleChangeData& mutable_data = aChangeList[i];
|
|
|
|
const nsStyleChangeData& data = mutable_data;
|
2016-08-17 03:15:29 +03:00
|
|
|
nsIFrame* frame = data.mFrame;
|
|
|
|
nsIContent* content = data.mContent;
|
|
|
|
nsChangeHint hint = data.mHint;
|
2016-08-01 23:31:57 +03:00
|
|
|
bool didReflowThisFrame = false;
|
|
|
|
|
|
|
|
NS_ASSERTION(!(hint & nsChangeHint_AllReflowHints) ||
|
|
|
|
(hint & nsChangeHint_NeedReflow),
|
|
|
|
"Reflow hint bits set without actually asking for a reflow");
|
|
|
|
|
|
|
|
// skip any frame that has been destroyed due to a ripple effect
|
2017-05-27 14:36:00 +03:00
|
|
|
if (frame && mDestroyedFrames->Contains(frame)) {
|
2016-08-01 23:31:57 +03:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame && frame->GetContent() != content) {
|
|
|
|
// XXXbz this is due to image maps messing with the primary frame of
|
|
|
|
// <area>s. See bug 135040. Remove this block once that's fixed.
|
|
|
|
frame = nullptr;
|
|
|
|
if (!(hint & nsChangeHint_ReconstructFrame)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((hint & nsChangeHint_UpdateContainingBlock) && frame &&
|
|
|
|
!(hint & nsChangeHint_ReconstructFrame)) {
|
|
|
|
if (NeedToReframeForAddingOrRemovingTransform(frame) ||
|
2017-04-30 18:30:08 +03:00
|
|
|
frame->IsFieldSetFrame() ||
|
2016-08-01 23:31:57 +03:00
|
|
|
frame->GetContentInsertionFrame() != frame) {
|
|
|
|
// The frame has positioned children that need to be reparented, or
|
|
|
|
// it can't easily be converted to/from being an abs-pos container correctly.
|
|
|
|
hint |= nsChangeHint_ReconstructFrame;
|
|
|
|
} else {
|
|
|
|
for (nsIFrame* cont = frame; cont;
|
|
|
|
cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
|
|
|
|
// Normally frame construction would set state bits as needed,
|
|
|
|
// but we're not going to reconstruct the frame so we need to set them.
|
|
|
|
// It's because we need to set this state on each affected frame
|
|
|
|
// that we can't coalesce nsChangeHint_UpdateContainingBlock hints up
|
2016-09-30 00:15:32 +03:00
|
|
|
// to ancestors (i.e. it can't be an change hint that is handled for
|
|
|
|
// descendants).
|
2016-08-13 04:39:45 +03:00
|
|
|
if (cont->IsAbsPosContainingBlock()) {
|
2016-08-01 23:31:57 +03:00
|
|
|
if (!cont->IsAbsoluteContainer() &&
|
|
|
|
(cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
|
|
|
|
cont->MarkAsAbsoluteContainingBlock();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (cont->IsAbsoluteContainer()) {
|
2016-10-04 21:39:36 +03:00
|
|
|
if (cont->HasAbsolutelyPositionedChildren()) {
|
|
|
|
// If |cont| still has absolutely positioned children,
|
|
|
|
// we can't call MarkAsNotAbsoluteContainingBlock. This
|
|
|
|
// will remove a frame list that still has children in
|
|
|
|
// it that we need to keep track of.
|
|
|
|
// The optimization of removing it isn't particularly
|
|
|
|
// important, although it does mean we skip some tests.
|
|
|
|
NS_WARNING("skipping removal of absolute containing block");
|
|
|
|
} else {
|
|
|
|
cont->MarkAsNotAbsoluteContainingBlock();
|
|
|
|
}
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-09-30 00:15:32 +03:00
|
|
|
|
|
|
|
if ((hint & nsChangeHint_AddOrRemoveTransform) && frame &&
|
|
|
|
!(hint & nsChangeHint_ReconstructFrame)) {
|
|
|
|
for (nsIFrame* cont = frame; cont;
|
|
|
|
cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
|
|
|
|
if (cont->StyleDisplay()->HasTransform(cont)) {
|
|
|
|
cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
|
|
|
|
}
|
|
|
|
// Don't remove NS_FRAME_MAY_BE_TRANSFORMED since it may still be
|
|
|
|
// transformed by other means. It's OK to have the bit even if it's
|
|
|
|
// not needed.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-01 23:31:57 +03:00
|
|
|
if (hint & nsChangeHint_ReconstructFrame) {
|
|
|
|
// If we ever start passing true here, be careful of restyles
|
|
|
|
// that involve a reframe and animations. In particular, if the
|
|
|
|
// restyle we're processing here is an animation restyle, but
|
|
|
|
// the style resolution we will do for the frame construction
|
|
|
|
// happens async when we're not in an animation restyle already,
|
|
|
|
// problems could arise.
|
|
|
|
// We could also have problems with triggering of CSS transitions
|
|
|
|
// on elements whose frames are reconstructed, since we depend on
|
|
|
|
// the reconstruction happening synchronously.
|
Bug 1389743: Only reconstruct frames synchronously from ContentRemoved when called from frame construction. r=mats
There's only one case of sync frame construction from ContentRemoved now, and
it's not on the element being removed, but on the whitespace siblings if needed,
and _only_ when they don't support lazy frame construction.
Basically, this switches all the RecreateFramesForContent calls to use
`aAsyncInsert` (which I changed to an enum class for readability), except when
we're already reframing.
Also, it switches ReframeTextIfNeeded to opt-in into lazy frame construction,
since it's used only when aFlags == CONTENT_REMOVED.
This allows to simplify the DestroyFramesFor API (which I'm happy to rename to
something more meaningful, since now it's something like
DestroyFramesForAndRecreateThemAsync), and do some other consistency cleanups.
A bunch of the ContentRemoved callsites were pretty random at passing
aAsyncInsert, and that was some kind of a mess. This patch ensures consistency,
and makes it impossible to do O(n^2) work when removing DOM nodes, which is
nice.
The underlying reason for this is explained in the description of bug 1377848,
and basically allows us to remove a bunch of Servo hacks on the longer term (a
few of them are going away already, yay!).
MozReview-Commit-ID: 2DrUTxGV8RX
--HG--
extra : rebase_source : f428d839a5482477dea22c0fea600d54f3e8799c
2017-08-23 10:58:57 +03:00
|
|
|
frameConstructor->RecreateFramesForContent(
|
Bug 1396018: Don't pass aFlags all the way through RecreateFramesForContent. r=bz
We currently use the aFlags argument of ContentRemoved for two purposes:
(1) To determine when a frame is being removed due to its element being removed
from the DOM, so we reframe its now-possibly-no-longer-suppressed
whitespace siblings as needed.
In other cases, our ContentRemoved call will be followed by a
ContentInserted call, which will end up doing AddTextItemIfNeeded() to
generate the relevant textframes if they're necessary.
Since we only need to tell apart the "DOM removal" and "anything else"
cases, we don't need to thread the aFlags argument through all the ways
ContentRemoved can call itself (on an ancestor).
All those cases should just be treated as "not DOM removal". In particular,
even if the original call _was_ for a DOM removal, if we convert it to an
ancestor reframe, which will call ContentInserted on the ancestor as well,
we don't need to do anything with text siblings of the ancestor.
(2) To save the frame tree state from DestroyFramesFor, but the frame tree
state is unconditionally captured on RecreateFramesForContent, so we only
need to care about it in the original ContentRemoved call.
Because of that, we can move that to the callsite, patch incoming for that.
MozReview-Commit-ID: Gy5IhUysBlz
Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
2017-09-01 17:10:49 +03:00
|
|
|
content, nsCSSFrameConstructor::InsertionKind::Sync);
|
2016-08-01 23:31:57 +03:00
|
|
|
} else {
|
|
|
|
NS_ASSERTION(frame, "This shouldn't happen");
|
|
|
|
|
|
|
|
if (!frame->FrameMaintainsOverflow()) {
|
|
|
|
// frame does not maintain overflow rects, so avoid calling
|
|
|
|
// FinishAndStoreOverflow on it:
|
|
|
|
hint &= ~(nsChangeHint_UpdateOverflow |
|
|
|
|
nsChangeHint_ChildrenOnlyTransform |
|
|
|
|
nsChangeHint_UpdatePostTransformOverflow |
|
|
|
|
nsChangeHint_UpdateParentOverflow);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(frame->GetStateBits() & NS_FRAME_MAY_BE_TRANSFORMED)) {
|
|
|
|
// Frame can not be transformed, and thus a change in transform will
|
|
|
|
// have no effect and we should not use the
|
|
|
|
// nsChangeHint_UpdatePostTransformOverflow hint.
|
|
|
|
hint &= ~nsChangeHint_UpdatePostTransformOverflow;
|
|
|
|
}
|
|
|
|
|
2017-07-27 06:05:47 +03:00
|
|
|
if (hint & nsChangeHint_AddOrRemoveTransform) {
|
|
|
|
// When dropping a running transform animation we will first add an
|
|
|
|
// nsChangeHint_UpdateTransformLayer hint as part of the animation-only
|
|
|
|
// restyle. During the subsequent regular restyle, if the animation was
|
|
|
|
// the only reason the element had any transform applied, we will add
|
|
|
|
// nsChangeHint_AddOrRemoveTransform as part of the regular restyle.
|
|
|
|
//
|
|
|
|
// With the Gecko backend, these two change hints are processed
|
|
|
|
// after each restyle but when using the Servo backend they accumulate
|
|
|
|
// and are processed together after we have already removed the
|
|
|
|
// transform as part of the regular restyle. Since we don't actually
|
|
|
|
// need the nsChangeHint_UpdateTransformLayer hint if we already have
|
|
|
|
// a nsChangeHint_AddOrRemoveTransform hint, and since we
|
|
|
|
// will fail an assertion in ApplyRenderingChangeToTree if we try
|
|
|
|
// specify nsChangeHint_UpdateTransformLayer but don't have any
|
|
|
|
// transform style, we just drop the unneeded hint here.
|
|
|
|
hint &= ~nsChangeHint_UpdateTransformLayer;
|
|
|
|
}
|
|
|
|
|
2016-08-01 23:31:57 +03:00
|
|
|
if (hint & nsChangeHint_UpdateEffects) {
|
|
|
|
for (nsIFrame* cont = frame; cont;
|
|
|
|
cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
|
|
|
|
nsSVGEffects::UpdateEffects(cont);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
|
|
|
|
((hint & nsChangeHint_UpdateOpacityLayer) &&
|
|
|
|
frame->IsFrameOfType(nsIFrame::eSVG) &&
|
|
|
|
!(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) {
|
|
|
|
nsSVGEffects::InvalidateRenderingObservers(frame);
|
|
|
|
}
|
|
|
|
if (hint & nsChangeHint_NeedReflow) {
|
|
|
|
StyleChangeReflow(frame, hint);
|
|
|
|
didReflowThisFrame = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((hint & nsChangeHint_UpdateUsesOpacity) &&
|
|
|
|
frame->IsFrameOfType(nsIFrame::eTablePart)) {
|
|
|
|
NS_ASSERTION(hint & nsChangeHint_UpdateOpacityLayer,
|
|
|
|
"should only return UpdateUsesOpacity hint "
|
|
|
|
"when also returning UpdateOpacityLayer hint");
|
|
|
|
// When an internal table part (including cells) changes between
|
|
|
|
// having opacity 1 and non-1, it changes whether its
|
|
|
|
// backgrounds (and those of table parts inside of it) are
|
|
|
|
// painted as part of the table's nsDisplayTableBorderBackground
|
|
|
|
// display item, or part of its own display item. That requires
|
|
|
|
// invalidation, so change UpdateOpacityLayer to RepaintFrame.
|
|
|
|
hint &= ~nsChangeHint_UpdateOpacityLayer;
|
|
|
|
hint |= nsChangeHint_RepaintFrame;
|
|
|
|
}
|
|
|
|
|
2017-02-13 03:09:17 +03:00
|
|
|
// Opacity disables preserve-3d, so if we toggle it, then we also need
|
|
|
|
// to update the overflow areas of all potentially affected frames.
|
|
|
|
if ((hint & nsChangeHint_UpdateUsesOpacity) &&
|
|
|
|
frame->StyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D) {
|
|
|
|
hint |= nsChangeHint_UpdateSubtreeOverflow;
|
|
|
|
}
|
|
|
|
|
2016-08-01 23:31:57 +03:00
|
|
|
if (hint & nsChangeHint_UpdateBackgroundPosition) {
|
|
|
|
// For most frame types, DLBI can detect background position changes,
|
|
|
|
// so we only need to schedule a paint.
|
|
|
|
hint |= nsChangeHint_SchedulePaint;
|
|
|
|
if (frame->IsFrameOfType(nsIFrame::eTablePart) ||
|
|
|
|
frame->IsFrameOfType(nsIFrame::eMathML)) {
|
|
|
|
// Table parts and MathML frames don't build display items for their
|
|
|
|
// backgrounds, so DLBI can't detect background-position changes for
|
|
|
|
// these frames. Repaint the whole frame.
|
|
|
|
hint |= nsChangeHint_RepaintFrame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hint & (nsChangeHint_RepaintFrame | nsChangeHint_SyncFrameView |
|
|
|
|
nsChangeHint_UpdateOpacityLayer | nsChangeHint_UpdateTransformLayer |
|
|
|
|
nsChangeHint_ChildrenOnlyTransform | nsChangeHint_SchedulePaint)) {
|
2016-08-03 01:47:19 +03:00
|
|
|
ApplyRenderingChangeToTree(presContext->PresShell(), frame, hint);
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
if ((hint & nsChangeHint_RecomputePosition) && !didReflowThisFrame) {
|
|
|
|
ActiveLayerTracker::NotifyOffsetRestyle(frame);
|
|
|
|
// It is possible for this to fall back to a reflow
|
|
|
|
if (!RecomputePosition(frame)) {
|
|
|
|
didReflowThisFrame = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
|
|
|
|
(hint & nsChangeHint_UpdateOverflow),
|
|
|
|
"nsChangeHint_UpdateOverflow should be passed too");
|
|
|
|
if (!didReflowThisFrame &&
|
|
|
|
(hint & (nsChangeHint_UpdateOverflow |
|
|
|
|
nsChangeHint_UpdatePostTransformOverflow |
|
|
|
|
nsChangeHint_UpdateParentOverflow |
|
|
|
|
nsChangeHint_UpdateSubtreeOverflow))) {
|
|
|
|
if (hint & nsChangeHint_UpdateSubtreeOverflow) {
|
|
|
|
for (nsIFrame* cont = frame; cont; cont =
|
|
|
|
nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
|
2016-08-03 01:47:19 +03:00
|
|
|
AddSubtreeToOverflowTracker(cont, mOverflowChangedTracker);
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
// The work we just did in AddSubtreeToOverflowTracker
|
|
|
|
// subsumes some of the other hints:
|
|
|
|
hint &= ~(nsChangeHint_UpdateOverflow |
|
|
|
|
nsChangeHint_UpdatePostTransformOverflow);
|
|
|
|
}
|
|
|
|
if (hint & nsChangeHint_ChildrenOnlyTransform) {
|
|
|
|
// The overflow areas of the child frames need to be updated:
|
|
|
|
nsIFrame* hintFrame = GetFrameForChildrenOnlyTransformHint(frame);
|
|
|
|
nsIFrame* childFrame = hintFrame->PrincipalChildList().FirstChild();
|
|
|
|
NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(frame),
|
|
|
|
"SVG frames should not have continuations "
|
|
|
|
"or ib-split siblings");
|
|
|
|
NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(hintFrame),
|
|
|
|
"SVG frames should not have continuations "
|
|
|
|
"or ib-split siblings");
|
|
|
|
for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
|
|
|
|
MOZ_ASSERT(childFrame->IsFrameOfType(nsIFrame::eSVG),
|
|
|
|
"Not expecting non-SVG children");
|
|
|
|
// If |childFrame| is dirty or has dirty children, we don't bother
|
|
|
|
// updating overflows since that will happen when it's reflowed.
|
|
|
|
if (!(childFrame->GetStateBits() &
|
|
|
|
(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
|
2016-08-03 01:47:19 +03:00
|
|
|
mOverflowChangedTracker.AddFrame(childFrame,
|
2016-08-01 23:31:57 +03:00
|
|
|
OverflowChangedTracker::CHILDREN_CHANGED);
|
|
|
|
}
|
|
|
|
NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrIBSplitSibling(childFrame),
|
|
|
|
"SVG frames should not have continuations "
|
|
|
|
"or ib-split siblings");
|
|
|
|
NS_ASSERTION(childFrame->GetParent() == hintFrame,
|
|
|
|
"SVG child frame not expected to have different parent");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If |frame| is dirty or has dirty children, we don't bother updating
|
|
|
|
// overflows since that will happen when it's reflowed.
|
|
|
|
if (!(frame->GetStateBits() &
|
|
|
|
(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN))) {
|
|
|
|
if (hint & (nsChangeHint_UpdateOverflow |
|
|
|
|
nsChangeHint_UpdatePostTransformOverflow)) {
|
|
|
|
OverflowChangedTracker::ChangeKind changeKind;
|
|
|
|
// If we have both nsChangeHint_UpdateOverflow and
|
|
|
|
// nsChangeHint_UpdatePostTransformOverflow,
|
|
|
|
// CHILDREN_CHANGED is selected as it is
|
|
|
|
// strictly stronger.
|
|
|
|
if (hint & nsChangeHint_UpdateOverflow) {
|
|
|
|
changeKind = OverflowChangedTracker::CHILDREN_CHANGED;
|
|
|
|
} else {
|
|
|
|
changeKind = OverflowChangedTracker::TRANSFORM_CHANGED;
|
|
|
|
}
|
|
|
|
for (nsIFrame* cont = frame; cont; cont =
|
|
|
|
nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
|
2016-08-03 01:47:19 +03:00
|
|
|
mOverflowChangedTracker.AddFrame(cont, changeKind);
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// UpdateParentOverflow hints need to be processed in addition
|
|
|
|
// to the above, since if the processing of the above hints
|
|
|
|
// yields no change, the update will not propagate to the
|
|
|
|
// parent.
|
|
|
|
if (hint & nsChangeHint_UpdateParentOverflow) {
|
|
|
|
MOZ_ASSERT(frame->GetParent(),
|
|
|
|
"shouldn't get style hints for the root frame");
|
|
|
|
for (nsIFrame* cont = frame; cont; cont =
|
|
|
|
nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
|
2016-08-03 01:47:19 +03:00
|
|
|
mOverflowChangedTracker.AddFrame(cont->GetParent(),
|
2016-08-01 23:31:57 +03:00
|
|
|
OverflowChangedTracker::CHILDREN_CHANGED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((hint & nsChangeHint_UpdateCursor) && !didUpdateCursor) {
|
2016-08-03 01:47:19 +03:00
|
|
|
presContext->PresShell()->SynthesizeMouseMove(false);
|
2016-08-01 23:31:57 +03:00
|
|
|
didUpdateCursor = true;
|
|
|
|
}
|
2017-06-19 04:04:40 +03:00
|
|
|
if (hint & nsChangeHint_UpdateWidgetProperties) {
|
|
|
|
frame->UpdateWidgetProperties();
|
|
|
|
}
|
2017-09-05 23:30:40 +03:00
|
|
|
if (hint & nsChangeHint_UpdateTableCellSpans) {
|
|
|
|
frameConstructor->UpdateTableCellSpans(content);
|
|
|
|
}
|
2016-08-01 23:31:57 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
frameConstructor->EndUpdate();
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
2017-05-27 14:36:00 +03:00
|
|
|
// Verify the style tree. Note that this needs to happen once we've
|
|
|
|
// processed the whole list, since until then the tree is not in fact in a
|
|
|
|
// consistent state.
|
|
|
|
for (const nsStyleChangeData& data : aChangeList) {
|
2016-08-01 23:31:57 +03:00
|
|
|
// reget frame from content since it may have been regenerated...
|
2016-08-17 03:15:29 +03:00
|
|
|
if (data.mContent) {
|
|
|
|
nsIFrame* frame = data.mContent->GetPrimaryFrame();
|
2016-08-01 23:31:57 +03:00
|
|
|
if (frame) {
|
|
|
|
DebugVerifyStyleTree(frame);
|
|
|
|
}
|
2017-04-30 18:30:08 +03:00
|
|
|
} else if (!data.mFrame || !data.mFrame->IsViewportFrame()) {
|
2016-08-01 23:31:57 +03:00
|
|
|
NS_WARNING("Unable to test style tree integrity -- no content node "
|
|
|
|
"(and not a viewport frame)");
|
|
|
|
}
|
|
|
|
}
|
2017-05-27 14:36:00 +03:00
|
|
|
#endif
|
2016-08-01 23:31:57 +03:00
|
|
|
|
|
|
|
aChangeList.Clear();
|
|
|
|
}
|
2016-07-23 01:27:05 +03:00
|
|
|
|
2017-03-09 12:15:08 +03:00
|
|
|
/* static */ uint64_t
|
|
|
|
RestyleManager::GetAnimationGenerationForFrame(nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
EffectSet* effectSet = EffectSet::GetEffectSet(aFrame);
|
|
|
|
return effectSet ? effectSet->GetAnimationGeneration() : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RestyleManager::IncrementAnimationGeneration()
|
|
|
|
{
|
|
|
|
// We update the animation generation at start of each call to
|
|
|
|
// ProcessPendingRestyles so we should ignore any subsequent (redundant)
|
|
|
|
// calls that occur while we are still processing restyles.
|
|
|
|
if ((IsGecko() && !AsGecko()->IsProcessingRestyles()) ||
|
|
|
|
(IsServo() && !mInStyleRefresh)) {
|
|
|
|
++mAnimationGeneration;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-02 09:03:16 +03:00
|
|
|
/* static */ void
|
|
|
|
RestyleManager::AddLayerChangesForAnimation(nsIFrame* aFrame,
|
|
|
|
nsIContent* aContent,
|
|
|
|
nsStyleChangeList&
|
|
|
|
aChangeListToProcess)
|
|
|
|
{
|
|
|
|
if (!aFrame || !aContent) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t frameGeneration =
|
|
|
|
RestyleManager::GetAnimationGenerationForFrame(aFrame);
|
|
|
|
|
|
|
|
nsChangeHint hint = nsChangeHint(0);
|
|
|
|
for (const LayerAnimationInfo::Record& layerInfo :
|
|
|
|
LayerAnimationInfo::sRecords) {
|
|
|
|
layers::Layer* layer =
|
|
|
|
FrameLayerBuilder::GetDedicatedLayer(aFrame, layerInfo.mLayerType);
|
|
|
|
if (layer && frameGeneration != layer->GetAnimationGeneration()) {
|
|
|
|
// If we have a transform layer but don't have any transform style, we
|
|
|
|
// probably just removed the transform but haven't destroyed the layer
|
|
|
|
// yet. In this case we will add the appropriate change hint
|
|
|
|
// (nsChangeHint_UpdateContainingBlock) when we compare style contexts
|
|
|
|
// so we can skip adding any change hint here. (If we *were* to add
|
|
|
|
// nsChangeHint_UpdateTransformLayer, ApplyRenderingChangeToTree would
|
|
|
|
// complain that we're updating a transform layer without a transform).
|
2017-08-07 07:07:43 +03:00
|
|
|
if (layerInfo.mLayerType == DisplayItemType::TYPE_TRANSFORM &&
|
2017-05-02 09:03:16 +03:00
|
|
|
!aFrame->StyleDisplay()->HasTransformStyle()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
hint |= layerInfo.mChangeHint;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We consider it's the first paint for the frame if we have an animation
|
|
|
|
// for the property but have no layer.
|
|
|
|
// Note that in case of animations which has properties preventing running
|
|
|
|
// on the compositor, e.g., width or height, corresponding layer is not
|
|
|
|
// created at all, but even in such cases, we normally set valid change
|
|
|
|
// hint for such animations in each tick, i.e. restyles in each tick. As
|
|
|
|
// a result, we usually do restyles for such animations in every tick on
|
|
|
|
// the main-thread. The only animations which will be affected by this
|
|
|
|
// explicit change hint are animations that have opacity/transform but did
|
|
|
|
// not have those properies just before. e.g, setting transform by
|
|
|
|
// setKeyframes or changing target element from other target which prevents
|
|
|
|
// running on the compositor, etc.
|
|
|
|
if (!layer &&
|
|
|
|
nsLayoutUtils::HasEffectiveAnimation(aFrame, layerInfo.mProperty)) {
|
|
|
|
hint |= layerInfo.mChangeHint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hint) {
|
|
|
|
aChangeListToProcess.AppendChange(aFrame, aContent, hint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-13 06:21:32 +03:00
|
|
|
RestyleManager::AnimationsWithDestroyedFrame::AnimationsWithDestroyedFrame(
|
|
|
|
RestyleManager* aRestyleManager)
|
2017-01-26 12:05:53 +03:00
|
|
|
: mRestyleManager(aRestyleManager)
|
|
|
|
, mRestorePointer(mRestyleManager->mAnimationsWithDestroyedFrame)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!mRestyleManager->mAnimationsWithDestroyedFrame,
|
|
|
|
"shouldn't construct recursively");
|
|
|
|
mRestyleManager->mAnimationsWithDestroyedFrame = this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-02-13 06:21:32 +03:00
|
|
|
RestyleManager::AnimationsWithDestroyedFrame
|
|
|
|
::StopAnimationsForElementsWithoutFrames()
|
2017-01-26 12:05:53 +03:00
|
|
|
{
|
|
|
|
StopAnimationsWithoutFrame(mContents, CSSPseudoElementType::NotPseudo);
|
|
|
|
StopAnimationsWithoutFrame(mBeforeContents, CSSPseudoElementType::before);
|
|
|
|
StopAnimationsWithoutFrame(mAfterContents, CSSPseudoElementType::after);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-02-13 06:21:32 +03:00
|
|
|
RestyleManager::AnimationsWithDestroyedFrame
|
|
|
|
::StopAnimationsWithoutFrame(
|
|
|
|
nsTArray<RefPtr<nsIContent>>& aArray,
|
|
|
|
CSSPseudoElementType aPseudoType)
|
2017-01-26 12:05:53 +03:00
|
|
|
{
|
|
|
|
nsAnimationManager* animationManager =
|
|
|
|
mRestyleManager->PresContext()->AnimationManager();
|
|
|
|
nsTransitionManager* transitionManager =
|
|
|
|
mRestyleManager->PresContext()->TransitionManager();
|
|
|
|
for (nsIContent* content : aArray) {
|
2017-07-24 03:20:22 +03:00
|
|
|
if (aPseudoType == CSSPseudoElementType::NotPseudo) {
|
|
|
|
if (content->GetPrimaryFrame()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else if (aPseudoType == CSSPseudoElementType::before) {
|
|
|
|
if (nsLayoutUtils::GetBeforeFrame(content)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else if (aPseudoType == CSSPseudoElementType::after) {
|
|
|
|
if (nsLayoutUtils::GetAfterFrame(content)) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-01-26 12:05:53 +03:00
|
|
|
}
|
|
|
|
dom::Element* element = content->AsElement();
|
|
|
|
|
|
|
|
animationManager->StopAnimationsForElement(element, aPseudoType);
|
2017-03-06 07:19:09 +03:00
|
|
|
transitionManager->StopAnimationsForElement(element, aPseudoType);
|
2017-01-26 12:05:53 +03:00
|
|
|
|
|
|
|
// All other animations should keep running but not running on the
|
|
|
|
// *compositor* at this point.
|
|
|
|
EffectSet* effectSet = EffectSet::GetEffectSet(element, aPseudoType);
|
|
|
|
if (effectSet) {
|
|
|
|
for (KeyframeEffectReadOnly* effect : *effectSet) {
|
|
|
|
effect->ResetIsRunningOnCompositor();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-08 10:08:46 +03:00
|
|
|
} // namespace mozilla
|