mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-25 11:15:34 +00:00
680815cd6e
This patch was generated automatically by the "modeline.py" script, available here: https://github.com/amccreight/moz-source-tools/blob/master/modeline.py For every file that is modified in this patch, the changes are as follows: (1) The patch changes the file to use the exact C++ mode lines from the Mozilla coding style guide, available here: https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Coding_Style#Mode_Line (2) The patch deletes any blank lines between the mode line & the MPL boilerplate comment. (3) If the file previously had the mode lines and MPL boilerplate in a single contiguous C++ comment, then the patch splits them into separate C++ comments, to match the boilerplate in the coding style. MozReview-Commit-ID: EuRsDue63tK --HG-- extra : rebase_source : 3356d4b80ff6213935192e87cdbc9103fec6084c
1187 lines
42 KiB
C++
1187 lines
42 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/GeckoStyleContext.h"
|
||
|
||
#include "CSSVariableImageTable.h"
|
||
#include "nsStyleConsts.h"
|
||
#include "nsStyleStruct.h"
|
||
#include "nsPresContext.h"
|
||
#include "nsRuleNode.h"
|
||
#include "nsStyleContextInlines.h"
|
||
#include "nsIFrame.h"
|
||
#include "nsLayoutUtils.h"
|
||
#include "mozilla/ReflowInput.h"
|
||
#include "RubyUtils.h"
|
||
|
||
using namespace mozilla;
|
||
|
||
#ifdef DEBUG
|
||
// Whether to perform expensive assertions in the nsStyleContext destructor.
|
||
static bool sExpensiveStyleStructAssertionsEnabled;
|
||
|
||
/* static */ void
|
||
GeckoStyleContext::Initialize()
|
||
{
|
||
Preferences::AddBoolVarCache(
|
||
&sExpensiveStyleStructAssertionsEnabled,
|
||
"layout.css.expensive-style-struct-assertions.enabled");
|
||
}
|
||
#endif
|
||
|
||
GeckoStyleContext::GeckoStyleContext(GeckoStyleContext* aParent,
|
||
nsAtom* aPseudoTag,
|
||
CSSPseudoElementType aPseudoType,
|
||
already_AddRefed<nsRuleNode> aRuleNode,
|
||
bool aSkipParentDisplayBasedStyleFixup)
|
||
: nsStyleContext(aPseudoTag, aPseudoType)
|
||
, mCachedResetData(nullptr)
|
||
, mRefCnt(0)
|
||
, mChild(nullptr)
|
||
, mEmptyChild(nullptr)
|
||
, mRuleNode(Move(aRuleNode))
|
||
, mParent(aParent)
|
||
#ifdef DEBUG
|
||
, mComputingStruct(nsStyleStructID_None)
|
||
, mFrameRefCnt(0)
|
||
#endif
|
||
{
|
||
mBits |= NS_STYLE_CONTEXT_IS_GECKO;
|
||
|
||
if (aParent) {
|
||
#ifdef DEBUG
|
||
nsRuleNode *r1 = mParent->RuleNode(), *r2 = mRuleNode;
|
||
while (r1->GetParent())
|
||
r1 = r1->GetParent();
|
||
while (r2->GetParent())
|
||
r2 = r2->GetParent();
|
||
NS_ASSERTION(r1 == r2, "must be in the same rule tree as parent");
|
||
#endif
|
||
} else {
|
||
PresContext()->PresShell()->StyleSet()->RootStyleContextAdded();
|
||
}
|
||
|
||
mRuleNode->SetUsedDirectly(); // before ApplyStyleFixups()!
|
||
// FinishConstruction() calls AddChild which needs these
|
||
// to be initialized!
|
||
mNextSibling = this;
|
||
mPrevSibling = this;
|
||
|
||
FinishConstruction();
|
||
ApplyStyleFixups(aSkipParentDisplayBasedStyleFixup);
|
||
}
|
||
|
||
// Overloaded new operator. Initializes the memory to 0 and relies on an arena
|
||
// (which comes from the presShell) to perform the allocation.
|
||
void*
|
||
GeckoStyleContext::operator new(size_t sz, nsPresContext* aPresContext)
|
||
{
|
||
MOZ_ASSERT(sz == sizeof(GeckoStyleContext));
|
||
// Check the recycle list first.
|
||
return aPresContext->PresShell()->
|
||
AllocateByObjectID(eArenaObjectID_GeckoStyleContext, sz);
|
||
}
|
||
|
||
// Overridden to prevent the global delete from being called, since the memory
|
||
// came out of an nsIArena instead of the global delete operator's heap.
|
||
void
|
||
GeckoStyleContext::Destroy()
|
||
{
|
||
// Get the pres context.
|
||
RefPtr<nsPresContext> presContext = PresContext();
|
||
// Call our destructor.
|
||
this->~GeckoStyleContext();
|
||
// Don't let the memory be freed, since it will be recycled
|
||
// instead. Don't call the global operator delete.
|
||
presContext->PresShell()->
|
||
FreeByObjectID(eArenaObjectID_GeckoStyleContext, this);
|
||
}
|
||
|
||
GeckoStyleContext::~GeckoStyleContext()
|
||
{
|
||
nsPresContext *presContext = PresContext();
|
||
#ifdef DEBUG
|
||
NS_ASSERTION(HasNoChildren(), "destructing context with children");
|
||
if (sExpensiveStyleStructAssertionsEnabled) {
|
||
// Assert that the style structs we are about to destroy are not referenced
|
||
// anywhere else in the style context tree. These checks are expensive,
|
||
// which is why they are not enabled by default.
|
||
GeckoStyleContext* root = this;
|
||
while (root->GetParent()) {
|
||
root = root->GetParent();
|
||
}
|
||
root->AssertStructsNotUsedElsewhere(this,
|
||
std::numeric_limits<int32_t>::max());
|
||
} else {
|
||
// In DEBUG builds when the pref is not enabled, we perform a more limited
|
||
// check just of the children of this style context.
|
||
this->AssertStructsNotUsedElsewhere(this, 2);
|
||
}
|
||
|
||
nsStyleSet* geckoStyleSet = presContext->PresShell()->StyleSet()->GetAsGecko();
|
||
NS_ASSERTION(!geckoStyleSet ||
|
||
geckoStyleSet->GetRuleTree() == AsGecko()->RuleNode()->RuleTree() ||
|
||
geckoStyleSet->IsInRuleTreeReconstruct(),
|
||
"destroying style context from old rule tree too late");
|
||
#endif
|
||
|
||
if (mParent) {
|
||
mParent->AsGecko()->RemoveChild(this);
|
||
} else {
|
||
presContext->StyleSet()->RootStyleContextRemoved();
|
||
}
|
||
|
||
// Free up our data structs.
|
||
DestroyCachedStructs(presContext);
|
||
CSSVariableImageTable::RemoveAll(this);
|
||
}
|
||
|
||
void
|
||
GeckoStyleContext::AddChild(GeckoStyleContext* aChild)
|
||
{
|
||
NS_ASSERTION(aChild->mPrevSibling == aChild &&
|
||
aChild->mNextSibling == aChild,
|
||
"child already in a child list");
|
||
|
||
GeckoStyleContext **listPtr = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
|
||
if (const nsRuleNode* source = aChild->mRuleNode) {
|
||
if (source->IsRoot()) {
|
||
listPtr = &mEmptyChild;
|
||
}
|
||
}
|
||
|
||
// Explicitly dereference listPtr so that compiler doesn't have to know that mNextSibling
|
||
// etc. don't alias with what ever listPtr points at.
|
||
GeckoStyleContext *list = *listPtr;
|
||
|
||
// Insert at the beginning of the list. See also FindChildWithRules.
|
||
if (list) {
|
||
// Link into existing elements, if there are any.
|
||
aChild->mNextSibling = list;
|
||
aChild->mPrevSibling = list->mPrevSibling;
|
||
list->mPrevSibling->mNextSibling = aChild;
|
||
list->mPrevSibling = aChild;
|
||
}
|
||
(*listPtr) = aChild;
|
||
}
|
||
|
||
void
|
||
GeckoStyleContext::MoveTo(GeckoStyleContext* aNewParent)
|
||
{
|
||
MOZ_ASSERT(aNewParent != mParent);
|
||
|
||
// This function shouldn't be getting called if the parents have different
|
||
// values for some flags in mBits (unless the flag is also set on this style
|
||
// context) because if that were the case we would need to recompute those
|
||
// bits for |this|.
|
||
|
||
#define CHECK_FLAG(bit_) \
|
||
MOZ_ASSERT((mParent->AsGecko()->mBits & (bit_)) == \
|
||
(aNewParent->mBits & (bit_)) || (mBits & (bit_)), \
|
||
"MoveTo cannot be called if " #bit_ " value on old and new " \
|
||
"style context parents do not match, unless the flag is set " \
|
||
"on this style context");
|
||
|
||
CHECK_FLAG(NS_STYLE_HAS_PSEUDO_ELEMENT_DATA)
|
||
CHECK_FLAG(NS_STYLE_IN_DISPLAY_NONE_SUBTREE)
|
||
CHECK_FLAG(NS_STYLE_HAS_TEXT_DECORATION_LINES)
|
||
CHECK_FLAG(NS_STYLE_RELEVANT_LINK_VISITED)
|
||
|
||
#undef CHECK_FLAG
|
||
|
||
// Assertions checking for visited style are just to avoid some tricky
|
||
// cases we can't be bothered handling at the moment.
|
||
MOZ_ASSERT(!IsStyleIfVisited());
|
||
MOZ_ASSERT(!mParent->IsStyleIfVisited());
|
||
MOZ_ASSERT(!aNewParent->IsStyleIfVisited());
|
||
auto* styleIfVisited = GetStyleIfVisited();
|
||
MOZ_ASSERT(!styleIfVisited || styleIfVisited->mParent == mParent);
|
||
|
||
if (mParent->HasChildThatUsesResetStyle()) {
|
||
aNewParent->AddStyleBit(NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE);
|
||
}
|
||
|
||
mParent->RemoveChild(this);
|
||
mParent = aNewParent;
|
||
mParent->AddChild(this);
|
||
|
||
if (styleIfVisited) {
|
||
styleIfVisited->mParent->RemoveChild(styleIfVisited);
|
||
styleIfVisited->mParent = aNewParent;
|
||
styleIfVisited->mParent->AddChild(styleIfVisited);
|
||
}
|
||
}
|
||
|
||
void
|
||
GeckoStyleContext::RemoveChild(GeckoStyleContext* aChild)
|
||
{
|
||
NS_PRECONDITION(nullptr != aChild && this == aChild->mParent, "bad argument");
|
||
|
||
MOZ_ASSERT(aChild->mRuleNode, "child context should have rule node");
|
||
GeckoStyleContext **list = aChild->mRuleNode->IsRoot() ? &mEmptyChild : &mChild;
|
||
|
||
if (aChild->mPrevSibling != aChild) { // has siblings
|
||
if ((*list) == aChild) {
|
||
(*list) = (*list)->mNextSibling;
|
||
}
|
||
}
|
||
else {
|
||
NS_ASSERTION((*list) == aChild, "bad sibling pointers");
|
||
(*list) = nullptr;
|
||
}
|
||
|
||
aChild->mPrevSibling->mNextSibling = aChild->mNextSibling;
|
||
aChild->mNextSibling->mPrevSibling = aChild->mPrevSibling;
|
||
aChild->mNextSibling = aChild;
|
||
aChild->mPrevSibling = aChild;
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
void
|
||
GeckoStyleContext::ListDescendants(FILE* out, int32_t aIndent)
|
||
{
|
||
if (nullptr != mChild) {
|
||
GeckoStyleContext* child = mChild;
|
||
do {
|
||
child->List(out, aIndent + 1, true);
|
||
child = child->mNextSibling;
|
||
} while (mChild != child);
|
||
}
|
||
if (nullptr != mEmptyChild) {
|
||
GeckoStyleContext* child = mEmptyChild;
|
||
do {
|
||
child->List(out, aIndent + 1, true);
|
||
child = child->mNextSibling;
|
||
} while (mEmptyChild != child);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
void
|
||
GeckoStyleContext::ClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs)
|
||
{
|
||
if (mChild) {
|
||
GeckoStyleContext* child = mChild;
|
||
do {
|
||
child->DoClearCachedInheritedStyleDataOnDescendants(aStructs);
|
||
child = child->mNextSibling;
|
||
} while (mChild != child);
|
||
}
|
||
if (mEmptyChild) {
|
||
GeckoStyleContext* child = mEmptyChild;
|
||
do {
|
||
child->DoClearCachedInheritedStyleDataOnDescendants(aStructs);
|
||
child = child->mNextSibling;
|
||
} while (mEmptyChild != child);
|
||
}
|
||
}
|
||
|
||
void
|
||
GeckoStyleContext::DoClearCachedInheritedStyleDataOnDescendants(uint32_t aStructs)
|
||
{
|
||
NS_ASSERTION(mFrameRefCnt == 0, "frame still referencing style context");
|
||
for (nsStyleStructID i = nsStyleStructID_Inherited_Start;
|
||
i < nsStyleStructID_Inherited_Start + nsStyleStructID_Inherited_Count;
|
||
i = nsStyleStructID(i + 1)) {
|
||
uint32_t bit = nsCachedStyleData::GetBitForSID(i);
|
||
if (aStructs & bit) {
|
||
if (!(mBits & bit) && mCachedInheritedData.mStyleStructs[i]) {
|
||
aStructs &= ~bit;
|
||
} else {
|
||
mCachedInheritedData.mStyleStructs[i] = nullptr;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (mCachedResetData) {
|
||
for (nsStyleStructID i = nsStyleStructID_Reset_Start;
|
||
i < nsStyleStructID_Reset_Start + nsStyleStructID_Reset_Count;
|
||
i = nsStyleStructID(i + 1)) {
|
||
uint32_t bit = nsCachedStyleData::GetBitForSID(i);
|
||
if (aStructs & bit) {
|
||
if (!(mBits & bit) && mCachedResetData->mStyleStructs[i]) {
|
||
aStructs &= ~bit;
|
||
} else {
|
||
mCachedResetData->mStyleStructs[i] = nullptr;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (aStructs == 0) {
|
||
return;
|
||
}
|
||
|
||
ClearCachedInheritedStyleDataOnDescendants(aStructs);
|
||
}
|
||
|
||
already_AddRefed<GeckoStyleContext>
|
||
GeckoStyleContext::FindChildWithRules(const nsAtom* aPseudoTag,
|
||
nsRuleNode* aSource,
|
||
nsRuleNode* aSourceIfVisited,
|
||
bool aRelevantLinkVisited)
|
||
{
|
||
uint32_t threshold = 10; // The # of siblings we're willing to examine
|
||
// before just giving this whole thing up.
|
||
|
||
RefPtr<GeckoStyleContext> result;
|
||
MOZ_ASSERT(aSource);
|
||
GeckoStyleContext *list = aSource->IsRoot() ? mEmptyChild : mChild;
|
||
|
||
if (list) {
|
||
GeckoStyleContext *child = list;
|
||
do {
|
||
if (child->RuleNode() == aSource &&
|
||
child->mPseudoTag == aPseudoTag &&
|
||
!child->IsStyleIfVisited() &&
|
||
child->RelevantLinkVisited() == aRelevantLinkVisited) {
|
||
bool match = false;
|
||
if (aSourceIfVisited) {
|
||
match = child->GetStyleIfVisited() &&
|
||
child->GetStyleIfVisited()->RuleNode() == aSourceIfVisited;
|
||
} else {
|
||
match = !child->GetStyleIfVisited();
|
||
}
|
||
if (match && !(child->mBits & NS_STYLE_INELIGIBLE_FOR_SHARING)) {
|
||
result = child;
|
||
break;
|
||
}
|
||
}
|
||
child = child->mNextSibling;
|
||
threshold--;
|
||
if (threshold == 0)
|
||
break;
|
||
} while (child != list);
|
||
}
|
||
|
||
if (result) {
|
||
if (result != list) {
|
||
// Move result to the front of the list.
|
||
RemoveChild(result);
|
||
AddChild(result);
|
||
}
|
||
result->mBits |= NS_STYLE_IS_SHARED;
|
||
}
|
||
|
||
return result.forget();
|
||
}
|
||
|
||
|
||
|
||
// This is an evil evil function, since it forces you to alloc your own separate copy of
|
||
// style data! Do not use this function unless you absolutely have to! You should avoid
|
||
// this at all costs! -dwh
|
||
void*
|
||
GeckoStyleContext::GetUniqueStyleData(const nsStyleStructID& aSID)
|
||
{
|
||
// If we already own the struct and no kids could depend on it, then
|
||
// just return it. (We leak in this case if there are kids -- and this
|
||
// function really shouldn't be called for style contexts that could
|
||
// have kids depending on the data. ClearStyleData would be OK, but
|
||
// this test for no mChild or mEmptyChild doesn't catch that case.)
|
||
const void *current = StyleData(aSID);
|
||
if (!mChild && !mEmptyChild &&
|
||
!(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
|
||
GetCachedStyleData(aSID))
|
||
return const_cast<void*>(current);
|
||
|
||
void* result;
|
||
nsPresContext *presContext = PresContext();
|
||
switch (aSID) {
|
||
|
||
#define UNIQUE_CASE(c_) \
|
||
case eStyleStruct_##c_: \
|
||
result = new (presContext) nsStyle##c_( \
|
||
* static_cast<const nsStyle##c_ *>(current)); \
|
||
break;
|
||
|
||
UNIQUE_CASE(Font)
|
||
UNIQUE_CASE(Display)
|
||
UNIQUE_CASE(Position)
|
||
UNIQUE_CASE(Text)
|
||
UNIQUE_CASE(TextReset)
|
||
UNIQUE_CASE(Visibility)
|
||
|
||
#undef UNIQUE_CASE
|
||
|
||
default:
|
||
NS_ERROR("Struct type not supported. Please find another way to do this if you can!");
|
||
return nullptr;
|
||
}
|
||
|
||
SetStyle(aSID, result);
|
||
mBits &= ~static_cast<uint64_t>(nsCachedStyleData::GetBitForSID(aSID));
|
||
|
||
return result;
|
||
}
|
||
|
||
|
||
// This is an evil function, but less evil than GetUniqueStyleData. It
|
||
// creates an empty style struct for this nsStyleContext.
|
||
void*
|
||
GeckoStyleContext::CreateEmptyStyleData(const nsStyleStructID& aSID)
|
||
{
|
||
MOZ_ASSERT(!mChild && !mEmptyChild &&
|
||
!(mBits & nsCachedStyleData::GetBitForSID(aSID)) &&
|
||
!GetCachedStyleData(aSID),
|
||
"This style should not have been computed");
|
||
|
||
void* result;
|
||
nsPresContext* presContext = PresContext();
|
||
switch (aSID) {
|
||
#define UNIQUE_CASE(c_) \
|
||
case eStyleStruct_##c_: \
|
||
result = new (presContext) nsStyle##c_(presContext); \
|
||
break;
|
||
|
||
UNIQUE_CASE(Border)
|
||
UNIQUE_CASE(Padding)
|
||
|
||
#undef UNIQUE_CASE
|
||
|
||
default:
|
||
NS_ERROR("Struct type not supported.");
|
||
return nullptr;
|
||
}
|
||
|
||
// The new struct is owned by this style context, but that we don't
|
||
// need to clear the bit in mBits because we've asserted that at the
|
||
// top of this function.
|
||
SetStyle(aSID, result);
|
||
return result;
|
||
}
|
||
|
||
|
||
void
|
||
GeckoStyleContext::SetIneligibleForSharing()
|
||
{
|
||
if (mBits & NS_STYLE_INELIGIBLE_FOR_SHARING) {
|
||
return;
|
||
}
|
||
mBits |= NS_STYLE_INELIGIBLE_FOR_SHARING;
|
||
if (mChild) {
|
||
GeckoStyleContext* child = mChild;
|
||
do {
|
||
child->SetIneligibleForSharing();
|
||
child = child->mNextSibling;
|
||
} while (mChild != child);
|
||
}
|
||
if (mEmptyChild) {
|
||
GeckoStyleContext* child = mEmptyChild;
|
||
do {
|
||
child->SetIneligibleForSharing();
|
||
child = child->mNextSibling;
|
||
} while (mEmptyChild != child);
|
||
}
|
||
}
|
||
|
||
#ifdef RESTYLE_LOGGING
|
||
nsCString
|
||
GeckoStyleContext::GetCachedStyleDataAsString(uint32_t aStructs)
|
||
{
|
||
nsCString structs;
|
||
for (nsStyleStructID i = nsStyleStructID(0);
|
||
i < nsStyleStructID_Length;
|
||
i = nsStyleStructID(i + 1)) {
|
||
if (aStructs & nsCachedStyleData::GetBitForSID(i)) {
|
||
const void* data = GetCachedStyleData(i);
|
||
if (!structs.IsEmpty()) {
|
||
structs.Append(' ');
|
||
}
|
||
structs.AppendPrintf("%s=%p", StructName(i), data);
|
||
if (HasCachedDependentStyleData(i)) {
|
||
structs.AppendLiteral("(dependent)");
|
||
} else {
|
||
structs.AppendLiteral("(owned)");
|
||
}
|
||
}
|
||
}
|
||
return structs;
|
||
}
|
||
|
||
int32_t&
|
||
GeckoStyleContext::LoggingDepth()
|
||
{
|
||
static int32_t depth = 0;
|
||
return depth;
|
||
}
|
||
|
||
void
|
||
GeckoStyleContext::LogStyleContextTree(int32_t aLoggingDepth, uint32_t aStructs)
|
||
{
|
||
LoggingDepth() = aLoggingDepth;
|
||
LogStyleContextTree(true, aStructs);
|
||
}
|
||
|
||
void
|
||
GeckoStyleContext::LogStyleContextTree(bool aFirst, uint32_t aStructs)
|
||
{
|
||
nsCString structs = GetCachedStyleDataAsString(aStructs);
|
||
if (!structs.IsEmpty()) {
|
||
structs.Append(' ');
|
||
}
|
||
|
||
nsCString pseudo;
|
||
if (mPseudoTag) {
|
||
nsAutoString pseudoTag;
|
||
mPseudoTag->ToString(pseudoTag);
|
||
AppendUTF16toUTF8(pseudoTag, pseudo);
|
||
pseudo.Append(' ');
|
||
}
|
||
|
||
nsCString flags;
|
||
if (IsStyleIfVisited()) {
|
||
flags.AppendLiteral("IS_STYLE_IF_VISITED ");
|
||
}
|
||
if (HasChildThatUsesGrandancestorStyle()) {
|
||
flags.AppendLiteral("CHILD_USES_GRANDANCESTOR_STYLE ");
|
||
}
|
||
if (IsShared()) {
|
||
flags.AppendLiteral("IS_SHARED ");
|
||
}
|
||
|
||
nsCString parent;
|
||
if (aFirst) {
|
||
parent.AppendPrintf("parent=%p ", mParent.get());
|
||
}
|
||
|
||
LOG_RESTYLE("%p(%d) %s%s%s%s",
|
||
this, mRefCnt,
|
||
structs.get(), pseudo.get(), flags.get(), parent.get());
|
||
|
||
LOG_RESTYLE_INDENT();
|
||
|
||
if (nullptr != mChild) {
|
||
GeckoStyleContext* child = mChild;
|
||
do {
|
||
child->LogStyleContextTree(false, aStructs);
|
||
child = child->mNextSibling;
|
||
} while (mChild != child);
|
||
}
|
||
if (nullptr != mEmptyChild) {
|
||
GeckoStyleContext* child = mEmptyChild;
|
||
do {
|
||
child->LogStyleContextTree(false, aStructs);
|
||
child = child->mNextSibling;
|
||
} while (mEmptyChild != child);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
static bool
|
||
ShouldSuppressLineBreak(const nsStyleContext* aContext,
|
||
const nsStyleDisplay* aDisplay,
|
||
const nsStyleContext* aParentContext,
|
||
const nsStyleDisplay* aParentDisplay)
|
||
{
|
||
// The display change should only occur for "in-flow" children
|
||
if (aDisplay->IsOutOfFlowStyle()) {
|
||
return false;
|
||
}
|
||
// Display value of any anonymous box should not be touched. In most
|
||
// cases, anonymous boxes are actually not in ruby frame, but instead,
|
||
// some other frame with a ruby display value. Non-element pseudos
|
||
// which represents text frames, as well as ruby pseudos are excluded
|
||
// because we still want to set the flag for them.
|
||
if ((aContext->GetPseudoType() == CSSPseudoElementType::InheritingAnonBox ||
|
||
aContext->GetPseudoType() == CSSPseudoElementType::NonInheritingAnonBox) &&
|
||
!nsCSSAnonBoxes::IsNonElement(aContext->GetPseudo()) &&
|
||
!RubyUtils::IsRubyPseudo(aContext->GetPseudo())) {
|
||
return false;
|
||
}
|
||
if (aParentContext->ShouldSuppressLineBreak()) {
|
||
// Line break suppressing bit is propagated to any children of
|
||
// line participants, which include inline, contents, and inline
|
||
// ruby boxes.
|
||
if (aParentDisplay->mDisplay == mozilla::StyleDisplay::Inline ||
|
||
aParentDisplay->mDisplay == mozilla::StyleDisplay::Contents ||
|
||
aParentDisplay->mDisplay == mozilla::StyleDisplay::Ruby ||
|
||
aParentDisplay->mDisplay == mozilla::StyleDisplay::RubyBaseContainer) {
|
||
return true;
|
||
}
|
||
}
|
||
// Any descendant of ruby level containers is non-breakable, but
|
||
// the level containers themselves are breakable. We have to check
|
||
// the container display type against all ruby display type here
|
||
// because any of the ruby boxes could be anonymous.
|
||
// Note that, when certain HTML tags, e.g. form controls, have ruby
|
||
// level container display type, they could also escape from this flag
|
||
// while they shouldn't. However, it is generally fine since they
|
||
// won't usually break the assertion that there is no line break
|
||
// inside ruby, because:
|
||
// 1. their display types, the ruby level container types, are inline-
|
||
// outside, which means they won't cause any forced line break; and
|
||
// 2. they never start an inline span, which means their children, if
|
||
// any, won't be able to break the line its ruby ancestor lays; and
|
||
// 3. their parent frame is always a ruby content frame (due to
|
||
// anonymous ruby box generation), which makes line layout suppress
|
||
// any optional line break around this frame.
|
||
// However, there is one special case which is BR tag, because it
|
||
// directly affects the line layout. This case is handled by the BR
|
||
// frame which checks the flag of its parent frame instead of itself.
|
||
if ((aParentDisplay->IsRubyDisplayType() &&
|
||
aDisplay->mDisplay != mozilla::StyleDisplay::RubyBaseContainer &&
|
||
aDisplay->mDisplay != mozilla::StyleDisplay::RubyTextContainer) ||
|
||
// Since ruby base and ruby text may exist themselves without any
|
||
// non-anonymous frame outside, we should also check them.
|
||
aDisplay->mDisplay == mozilla::StyleDisplay::RubyBase ||
|
||
aDisplay->mDisplay == mozilla::StyleDisplay::RubyText) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void
|
||
GeckoStyleContext::FinishConstruction()
|
||
{
|
||
MOZ_ASSERT(RuleNode());
|
||
|
||
if (mParent) {
|
||
mParent->AddChild(this);
|
||
}
|
||
|
||
SetStyleBits();
|
||
}
|
||
|
||
void
|
||
GeckoStyleContext::SetStyleBits()
|
||
{
|
||
if ((mParent && mParent->HasPseudoElementData()) || IsPseudoElement()) {
|
||
AddStyleBit(NS_STYLE_HAS_PSEUDO_ELEMENT_DATA);
|
||
}
|
||
|
||
// Set the NS_STYLE_IN_DISPLAY_NONE_SUBTREE bit
|
||
const nsStyleDisplay* disp = StyleDisplay();
|
||
if ((mParent && mParent->IsInDisplayNoneSubtree()) ||
|
||
disp->mDisplay == mozilla::StyleDisplay::None) {
|
||
AddStyleBit(NS_STYLE_IN_DISPLAY_NONE_SUBTREE);
|
||
}
|
||
|
||
// Mark text combined for text-combine-upright, as needed.
|
||
if (mPseudoTag == nsCSSAnonBoxes::mozText && mParent &&
|
||
mParent->StyleVisibility()->mWritingMode !=
|
||
NS_STYLE_WRITING_MODE_HORIZONTAL_TB &&
|
||
mParent->StyleText()->mTextCombineUpright ==
|
||
NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL) {
|
||
AddStyleBit(NS_STYLE_IS_TEXT_COMBINED);
|
||
}
|
||
}
|
||
|
||
// Flex & grid containers blockify their children.
|
||
// "The display value of a flex item is blockified"
|
||
// https://drafts.csswg.org/css-flexbox-1/#flex-items
|
||
// "The display value of a grid item is blockified"
|
||
// https://drafts.csswg.org/css-grid/#grid-items
|
||
static bool
|
||
ShouldBlockifyChildren(const nsStyleDisplay* aStyleDisp)
|
||
{
|
||
auto displayVal = aStyleDisp->mDisplay;
|
||
return mozilla::StyleDisplay::Flex == displayVal ||
|
||
mozilla::StyleDisplay::InlineFlex == displayVal ||
|
||
mozilla::StyleDisplay::Grid == displayVal ||
|
||
mozilla::StyleDisplay::InlineGrid == displayVal;
|
||
}
|
||
|
||
#ifdef DEBUG
|
||
void
|
||
GeckoStyleContext::AssertStructsNotUsedElsewhere(
|
||
GeckoStyleContext* aDestroyingContext,
|
||
int32_t aLevels) const
|
||
{
|
||
if (aLevels == 0) {
|
||
return;
|
||
}
|
||
|
||
void* data;
|
||
|
||
if (mBits & NS_STYLE_IS_GOING_AWAY) {
|
||
return;
|
||
}
|
||
|
||
if (this != aDestroyingContext) {
|
||
nsInheritedStyleData& destroyingInheritedData =
|
||
aDestroyingContext->mCachedInheritedData;
|
||
#define STYLE_STRUCT_INHERITED(name_, checkdata_cb) \
|
||
data = destroyingInheritedData.mStyleStructs[eStyleStruct_##name_]; \
|
||
if (data && \
|
||
!(aDestroyingContext->mBits & NS_STYLE_INHERIT_BIT(name_)) && \
|
||
(mCachedInheritedData.mStyleStructs[eStyleStruct_##name_] == data)) { \
|
||
printf_stderr("style struct %p found on style context %p\n", data, this);\
|
||
nsString url; \
|
||
nsresult rv = PresContext()->Document()->GetURL(url); \
|
||
if (NS_SUCCEEDED(rv)) { \
|
||
printf_stderr(" in %s\n", NS_ConvertUTF16toUTF8(url).get()); \
|
||
} \
|
||
MOZ_ASSERT(false, "destroying " #name_ " style struct still present " \
|
||
"in style context tree"); \
|
||
}
|
||
#define STYLE_STRUCT_RESET(name_, checkdata_cb)
|
||
|
||
#include "nsStyleStructList.h"
|
||
|
||
#undef STYLE_STRUCT_INHERITED
|
||
#undef STYLE_STRUCT_RESET
|
||
|
||
if (mCachedResetData) {
|
||
nsResetStyleData* destroyingResetData =
|
||
aDestroyingContext->mCachedResetData;
|
||
if (destroyingResetData) {
|
||
#define STYLE_STRUCT_INHERITED(name_, checkdata_cb_)
|
||
#define STYLE_STRUCT_RESET(name_, checkdata_cb) \
|
||
data = destroyingResetData->mStyleStructs[eStyleStruct_##name_]; \
|
||
if (data && \
|
||
!(aDestroyingContext->mBits & NS_STYLE_INHERIT_BIT(name_)) && \
|
||
(mCachedResetData->mStyleStructs[eStyleStruct_##name_] == data)) { \
|
||
printf_stderr("style struct %p found on style context %p\n", data, \
|
||
this); \
|
||
nsString url; \
|
||
nsresult rv = PresContext()->Document()->GetURL(url); \
|
||
if (NS_SUCCEEDED(rv)) { \
|
||
printf_stderr(" in %s\n", NS_ConvertUTF16toUTF8(url).get()); \
|
||
} \
|
||
MOZ_ASSERT(false, "destroying " #name_ " style struct still present "\
|
||
"in style context tree"); \
|
||
}
|
||
|
||
#include "nsStyleStructList.h"
|
||
|
||
#undef STYLE_STRUCT_INHERITED
|
||
#undef STYLE_STRUCT_RESET
|
||
}
|
||
}
|
||
}
|
||
|
||
if (mChild) {
|
||
const GeckoStyleContext* child = mChild;
|
||
do {
|
||
child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
|
||
child = child->mNextSibling;
|
||
} while (child != mChild);
|
||
}
|
||
|
||
if (mEmptyChild) {
|
||
const GeckoStyleContext* child = mEmptyChild;
|
||
do {
|
||
child->AssertStructsNotUsedElsewhere(aDestroyingContext, aLevels - 1);
|
||
child = child->mNextSibling;
|
||
} while (child != mEmptyChild);
|
||
}
|
||
}
|
||
#endif
|
||
|
||
|
||
void
|
||
GeckoStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup)
|
||
{
|
||
#define GET_UNIQUE_STYLE_DATA(name_) \
|
||
static_cast<nsStyle##name_*>(GetUniqueStyleData(eStyleStruct_##name_))
|
||
|
||
// CSS Inline Layout Level 3 - 3.5 Sizing Initial Letters:
|
||
// For an N-line drop initial in a Western script, the cap-height of the
|
||
// letter needs to be (N – 1) times the line-height, plus the cap-height
|
||
// of the surrounding text.
|
||
if (mPseudoTag == nsCSSPseudoElements::firstLetter) {
|
||
const nsStyleTextReset* textReset = StyleTextReset();
|
||
if (textReset->mInitialLetterSize != 0.0f) {
|
||
GeckoStyleContext* containerSC = GetParent();
|
||
const nsStyleDisplay* containerDisp = containerSC->StyleDisplay();
|
||
while (containerDisp->mDisplay == mozilla::StyleDisplay::Contents) {
|
||
if (!containerSC->GetParent()) {
|
||
break;
|
||
}
|
||
containerSC = containerSC->GetParent();
|
||
containerDisp = containerSC->StyleDisplay();
|
||
}
|
||
nscoord containerLH =
|
||
ReflowInput::CalcLineHeight(nullptr, containerSC, NS_AUTOHEIGHT, 1.0f);
|
||
RefPtr<nsFontMetrics> containerFM =
|
||
nsLayoutUtils::GetFontMetricsForStyleContext(containerSC);
|
||
MOZ_ASSERT(containerFM, "Should have fontMetrics!!");
|
||
nscoord containerCH = containerFM->CapHeight();
|
||
RefPtr<nsFontMetrics> firstLetterFM =
|
||
nsLayoutUtils::GetFontMetricsForStyleContext(this);
|
||
MOZ_ASSERT(firstLetterFM, "Should have fontMetrics!!");
|
||
nscoord firstLetterCH = firstLetterFM->CapHeight();
|
||
nsStyleFont* mutableStyleFont = GET_UNIQUE_STYLE_DATA(Font);
|
||
float invCapHeightRatio =
|
||
mutableStyleFont->mFont.size / NSCoordToFloat(firstLetterCH);
|
||
mutableStyleFont->mFont.size =
|
||
NSToCoordRound(((textReset->mInitialLetterSize - 1) * containerLH +
|
||
containerCH) *
|
||
invCapHeightRatio);
|
||
}
|
||
}
|
||
|
||
// Change writing mode of text frame for text-combine-upright. We use
|
||
// style structs of the parent to avoid triggering computation before
|
||
// we change the writing mode.
|
||
// It is safe to look at the parent's style because we are looking at
|
||
// inherited properties, and ::-moz-text never matches any rules.
|
||
if (mPseudoTag == nsCSSAnonBoxes::mozText && mParent &&
|
||
mParent->StyleVisibility()->mWritingMode !=
|
||
NS_STYLE_WRITING_MODE_HORIZONTAL_TB &&
|
||
mParent->StyleText()->mTextCombineUpright ==
|
||
NS_STYLE_TEXT_COMBINE_UPRIGHT_ALL) {
|
||
MOZ_ASSERT(!PeekStyleVisibility(), "If StyleVisibility was already "
|
||
"computed, some properties may have been computed "
|
||
"incorrectly based on the old writing mode value");
|
||
nsStyleVisibility* mutableVis = GET_UNIQUE_STYLE_DATA(Visibility);
|
||
mutableVis->mWritingMode = NS_STYLE_WRITING_MODE_HORIZONTAL_TB;
|
||
}
|
||
|
||
// See if we have any text decorations.
|
||
// First see if our parent has text decorations. If our parent does, then we inherit the bit.
|
||
if (mParent && mParent->HasTextDecorationLines()) {
|
||
AddStyleBit(NS_STYLE_HAS_TEXT_DECORATION_LINES);
|
||
} else {
|
||
// We might have defined a decoration.
|
||
if (StyleTextReset()->HasTextDecorationLines()) {
|
||
AddStyleBit(NS_STYLE_HAS_TEXT_DECORATION_LINES);
|
||
}
|
||
}
|
||
|
||
// CSS 2.1 10.1: Propagate the root element's 'direction' to the ICB.
|
||
// (PageContentFrame/CanvasFrame etc will inherit 'direction')
|
||
if (mPseudoTag == nsCSSAnonBoxes::viewport) {
|
||
nsPresContext* presContext = PresContext();
|
||
mozilla::dom::Element* docElement = presContext->Document()->GetRootElement();
|
||
if (docElement) {
|
||
RefPtr<nsStyleContext> rootStyle =
|
||
presContext->StyleSet()->AsGecko()->ResolveStyleFor(docElement, nullptr);
|
||
auto dir = rootStyle->StyleVisibility()->mDirection;
|
||
if (dir != StyleVisibility()->mDirection) {
|
||
nsStyleVisibility* uniqueVisibility = GET_UNIQUE_STYLE_DATA(Visibility);
|
||
uniqueVisibility->mDirection = dir;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Correct tables.
|
||
const nsStyleDisplay* disp = StyleDisplay();
|
||
if (disp->mDisplay == mozilla::StyleDisplay::Table) {
|
||
// -moz-center and -moz-right are used for HTML's alignment
|
||
// This is covering the <div align="right"><table>...</table></div> case.
|
||
// In this case, we don't want to inherit the text alignment into the table.
|
||
const nsStyleText* text = StyleText();
|
||
|
||
if (text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
|
||
text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
|
||
text->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)
|
||
{
|
||
nsStyleText* uniqueText = GET_UNIQUE_STYLE_DATA(Text);
|
||
uniqueText->mTextAlign = NS_STYLE_TEXT_ALIGN_START;
|
||
}
|
||
}
|
||
|
||
// Fixup the "justify-items: auto" value based on our parent style here if
|
||
// needed.
|
||
//
|
||
// Note that this only pulls a unique struct in the case the parent has the
|
||
// "legacy" modifier (which is not the default), and the computed value would
|
||
// change as a result.
|
||
//
|
||
// We check the parent first just to avoid unconditionally pulling the
|
||
// nsStylePosition struct on every style context.
|
||
if (mParent &&
|
||
mParent->StylePosition()->mJustifyItems & NS_STYLE_JUSTIFY_LEGACY &&
|
||
StylePosition()->mSpecifiedJustifyItems == NS_STYLE_JUSTIFY_AUTO &&
|
||
StylePosition()->mJustifyItems != mParent->StylePosition()->mJustifyItems) {
|
||
nsStylePosition* uniquePosition = GET_UNIQUE_STYLE_DATA(Position);
|
||
uniquePosition->mJustifyItems = mParent->StylePosition()->mJustifyItems;
|
||
}
|
||
|
||
// CSS2.1 section 9.2.4 specifies fixups for the 'display' property of
|
||
// the root element. We can't implement them in nsRuleNode because we
|
||
// don't want to store all display structs that aren't 'block',
|
||
// 'inline', or 'table' in the style context tree on the off chance
|
||
// that the root element has its style reresolved later. So do them
|
||
// here if needed, by changing the style data, so that other code
|
||
// doesn't get confused by looking at the style data.
|
||
if (!mParent &&
|
||
// We don't want to blockify various anon boxes that just happen to not
|
||
// inherit from anything. So restrict blockification only to actual
|
||
// elements, the viewport (which should be block anyway, but in SVG
|
||
// document's isn't because we lazy-load ua.css there), and the ::backdrop
|
||
// pseudo-element. This last is explicitly allowed to have any specified
|
||
// display type in the spec, but computes to a blockified display type per
|
||
// various provisions of
|
||
// https://fullscreen.spec.whatwg.org/#new-stacking-layer
|
||
(!mPseudoTag ||
|
||
mPseudoTag == nsCSSAnonBoxes::viewport ||
|
||
mPseudoTag == nsCSSPseudoElements::backdrop)) {
|
||
auto displayVal = disp->mDisplay;
|
||
if (displayVal != mozilla::StyleDisplay::Contents) {
|
||
nsRuleNode::EnsureBlockDisplay(displayVal, true);
|
||
} else {
|
||
// http://dev.w3.org/csswg/css-display/#transformations
|
||
// "... a display-outside of 'contents' computes to block-level
|
||
// on the root element."
|
||
displayVal = mozilla::StyleDisplay::Block;
|
||
}
|
||
if (displayVal != disp->mDisplay) {
|
||
nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
|
||
disp = mutable_display;
|
||
|
||
// If we're in this code, then mOriginalDisplay doesn't matter
|
||
// for purposes of the cascade (because this nsStyleDisplay
|
||
// isn't living in the ruletree anyway), and for determining
|
||
// hypothetical boxes it's better to have mOriginalDisplay
|
||
// matching mDisplay here.
|
||
mutable_display->mOriginalDisplay = mutable_display->mDisplay =
|
||
displayVal;
|
||
}
|
||
}
|
||
|
||
// Adjust the "display" values of flex and grid items (but not for raw text
|
||
// or placeholders). CSS3 Flexbox section 4 says:
|
||
// # The computed 'display' of a flex item is determined
|
||
// # by applying the table in CSS 2.1 Chapter 9.7.
|
||
// ...which converts inline-level elements to their block-level equivalents.
|
||
// Any block-level element directly contained by elements with ruby display
|
||
// values are converted to their inline-level equivalents.
|
||
if (!aSkipParentDisplayBasedStyleFixup && mParent) {
|
||
// Skip display:contents ancestors to reach the potential container.
|
||
// (If there are only display:contents ancestors between this node and
|
||
// a flex/grid container ancestor, then this node is a flex/grid item, since
|
||
// its parent *in the frame tree* will be the flex/grid container. So we treat
|
||
// it like a flex/grid item here.)
|
||
GeckoStyleContext* containerContext = GetParent();
|
||
const nsStyleDisplay* containerDisp = containerContext->StyleDisplay();
|
||
while (containerDisp->mDisplay == mozilla::StyleDisplay::Contents) {
|
||
if (!containerContext->GetParent()) {
|
||
break;
|
||
}
|
||
containerContext = containerContext->GetParent();
|
||
containerDisp = containerContext->StyleDisplay();
|
||
}
|
||
if (ShouldBlockifyChildren(containerDisp) &&
|
||
!nsCSSAnonBoxes::IsNonElement(GetPseudo())) {
|
||
// NOTE: Technically, we shouldn't modify the 'display' value of
|
||
// positioned elements, since they aren't flex/grid items. However,
|
||
// we don't need to worry about checking for that, because if we're
|
||
// positioned, we'll have already been through a call to
|
||
// EnsureBlockDisplay() in nsRuleNode, so this call here won't change
|
||
// anything. So we're OK.
|
||
auto displayVal = disp->mDisplay;
|
||
nsRuleNode::EnsureBlockDisplay(displayVal);
|
||
if (displayVal != disp->mDisplay) {
|
||
NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle(),
|
||
"We shouldn't be changing the display value of "
|
||
"positioned content (and we should have already "
|
||
"converted its display value to be block-level...)");
|
||
nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
|
||
disp = mutable_display;
|
||
mutable_display->mDisplay = displayVal;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Note: This must come after the blockification above, otherwise we fail
|
||
// the grid-item-blockifying-001.html reftest.
|
||
if (mParent && ::ShouldSuppressLineBreak(this, disp, mParent,
|
||
mParent->StyleDisplay())) {
|
||
mBits |= NS_STYLE_SUPPRESS_LINEBREAK;
|
||
auto displayVal = disp->mDisplay;
|
||
nsRuleNode::EnsureInlineDisplay(displayVal);
|
||
if (displayVal != disp->mDisplay) {
|
||
nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
|
||
disp = mutable_display;
|
||
mutable_display->mDisplay = displayVal;
|
||
}
|
||
}
|
||
// Suppress border/padding of ruby level containers
|
||
if (disp->mDisplay == mozilla::StyleDisplay::RubyBaseContainer ||
|
||
disp->mDisplay == mozilla::StyleDisplay::RubyTextContainer) {
|
||
CreateEmptyStyleData(eStyleStruct_Border);
|
||
CreateEmptyStyleData(eStyleStruct_Padding);
|
||
}
|
||
if (disp->IsRubyDisplayType()) {
|
||
// Per CSS Ruby spec section Bidi Reordering, for all ruby boxes,
|
||
// the 'normal' and 'embed' values of 'unicode-bidi' should compute to
|
||
// 'isolate', and 'bidi-override' should compute to 'isolate-override'.
|
||
const nsStyleTextReset* textReset = StyleTextReset();
|
||
uint8_t unicodeBidi = textReset->mUnicodeBidi;
|
||
if (unicodeBidi == NS_STYLE_UNICODE_BIDI_NORMAL ||
|
||
unicodeBidi == NS_STYLE_UNICODE_BIDI_EMBED) {
|
||
unicodeBidi = NS_STYLE_UNICODE_BIDI_ISOLATE;
|
||
} else if (unicodeBidi == NS_STYLE_UNICODE_BIDI_BIDI_OVERRIDE) {
|
||
unicodeBidi = NS_STYLE_UNICODE_BIDI_ISOLATE_OVERRIDE;
|
||
}
|
||
if (unicodeBidi != textReset->mUnicodeBidi) {
|
||
nsStyleTextReset* mutableTextReset = GET_UNIQUE_STYLE_DATA(TextReset);
|
||
mutableTextReset->mUnicodeBidi = unicodeBidi;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* According to https://drafts.csswg.org/css-writing-modes-3/#block-flow:
|
||
*
|
||
* If a box has a different block flow direction than its containing block:
|
||
* * If the box has a specified display of inline, its display computes
|
||
* to inline-block. [CSS21]
|
||
* ...etc.
|
||
*/
|
||
if (disp->mDisplay == mozilla::StyleDisplay::Inline &&
|
||
!nsCSSAnonBoxes::IsNonElement(mPseudoTag) &&
|
||
mParent) {
|
||
auto cbContext = GetParent();
|
||
while (cbContext->StyleDisplay()->mDisplay == mozilla::StyleDisplay::Contents) {
|
||
cbContext = cbContext->GetParent();
|
||
}
|
||
MOZ_ASSERT(cbContext, "the root context can't have display:contents");
|
||
// We don't need the full mozilla::WritingMode value (incorporating dir
|
||
// and text-orientation) here; just the writing-mode property is enough.
|
||
if (StyleVisibility()->mWritingMode !=
|
||
cbContext->StyleVisibility()->mWritingMode) {
|
||
nsStyleDisplay* mutable_display = GET_UNIQUE_STYLE_DATA(Display);
|
||
disp = mutable_display;
|
||
mutable_display->mOriginalDisplay = mutable_display->mDisplay =
|
||
mozilla::StyleDisplay::InlineBlock;
|
||
}
|
||
}
|
||
|
||
// Compute User Interface style, to trigger loads of cursors
|
||
StyleUserInterface();
|
||
#undef GET_UNIQUE_STYLE_DATA
|
||
}
|
||
|
||
bool
|
||
GeckoStyleContext::HasNoChildren() const
|
||
{
|
||
return (nullptr == mChild) && (nullptr == mEmptyChild);
|
||
}
|
||
|
||
void
|
||
GeckoStyleContext::SetStyle(nsStyleStructID aSID, void* aStruct)
|
||
{
|
||
// This method should only be called from nsRuleNode! It is not a public
|
||
// method!
|
||
|
||
NS_ASSERTION(aSID >= 0 && aSID < nsStyleStructID_Length, "out of bounds");
|
||
|
||
// NOTE: nsCachedStyleData::GetStyleData works roughly the same way.
|
||
// See the comments there (in nsRuleNode.h) for more details about
|
||
// what this is doing and why.
|
||
|
||
void** dataSlot;
|
||
if (nsCachedStyleData::IsReset(aSID)) {
|
||
if (!mCachedResetData) {
|
||
mCachedResetData = new (PresContext()) nsResetStyleData;
|
||
}
|
||
dataSlot = &mCachedResetData->mStyleStructs[aSID];
|
||
} else {
|
||
dataSlot = &mCachedInheritedData.mStyleStructs[aSID];
|
||
}
|
||
NS_ASSERTION(!*dataSlot || (mBits & nsCachedStyleData::GetBitForSID(aSID)),
|
||
"Going to leak style data");
|
||
*dataSlot = aStruct;
|
||
}
|
||
|
||
|
||
const void*
|
||
GeckoStyleContext::StyleData(nsStyleStructID aSID)
|
||
{
|
||
const void* cachedData = GetCachedStyleData(aSID);
|
||
if (cachedData)
|
||
return cachedData; // We have computed data stored on this node in the context tree.
|
||
// Our style source will take care of it for us.
|
||
const void* newData = AsGecko()->RuleNode()->GetStyleData(aSID, this->AsGecko(), true);
|
||
if (!nsCachedStyleData::IsReset(aSID)) {
|
||
// always cache inherited data on the style context; the rule
|
||
// node set the bit in mBits for us if needed.
|
||
mCachedInheritedData.mStyleStructs[aSID] = const_cast<void*>(newData);
|
||
}
|
||
|
||
return newData;
|
||
}
|
||
|
||
void
|
||
GeckoStyleContext::DestroyCachedStructs(nsPresContext* aPresContext)
|
||
{
|
||
mCachedInheritedData.DestroyStructs(mBits, aPresContext);
|
||
if (mCachedResetData) {
|
||
mCachedResetData->Destroy(mBits, aPresContext);
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
GeckoStyleContext::SwapStyleData(GeckoStyleContext* aNewContext, uint32_t aStructs)
|
||
{
|
||
static_assert(nsStyleStructID_Length <= 32, "aStructs is not big enough");
|
||
|
||
for (nsStyleStructID i = nsStyleStructID_Inherited_Start;
|
||
i < nsStyleStructID_Inherited_Start + nsStyleStructID_Inherited_Count;
|
||
i = nsStyleStructID(i + 1)) {
|
||
uint32_t bit = nsCachedStyleData::GetBitForSID(i);
|
||
if (!(aStructs & bit)) {
|
||
continue;
|
||
}
|
||
void*& thisData = mCachedInheritedData.mStyleStructs[i];
|
||
void*& otherData = aNewContext->mCachedInheritedData.mStyleStructs[i];
|
||
if (mBits & bit) {
|
||
if (thisData == otherData) {
|
||
thisData = nullptr;
|
||
}
|
||
} else if (!(aNewContext->mBits & bit) && thisData && otherData) {
|
||
std::swap(thisData, otherData);
|
||
}
|
||
}
|
||
|
||
for (nsStyleStructID i = nsStyleStructID_Reset_Start;
|
||
i < nsStyleStructID_Reset_Start + nsStyleStructID_Reset_Count;
|
||
i = nsStyleStructID(i + 1)) {
|
||
uint32_t bit = nsCachedStyleData::GetBitForSID(i);
|
||
if (!(aStructs & bit)) {
|
||
continue;
|
||
}
|
||
if (!mCachedResetData) {
|
||
mCachedResetData = new (PresContext()) nsResetStyleData;
|
||
}
|
||
if (!aNewContext->mCachedResetData) {
|
||
aNewContext->mCachedResetData = new (PresContext()) nsResetStyleData;
|
||
}
|
||
void*& thisData = mCachedResetData->mStyleStructs[i];
|
||
void*& otherData = aNewContext->mCachedResetData->mStyleStructs[i];
|
||
if (mBits & bit) {
|
||
if (thisData == otherData) {
|
||
thisData = nullptr;
|
||
}
|
||
} else if (!(aNewContext->mBits & bit) && thisData && otherData) {
|
||
std::swap(thisData, otherData);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void
|
||
GeckoStyleContext::SetStyleIfVisited(already_AddRefed<GeckoStyleContext> aStyleIfVisited)
|
||
{
|
||
MOZ_ASSERT(!IsStyleIfVisited(), "this context is not visited data");
|
||
NS_ASSERTION(!mStyleIfVisited, "should only be set once");
|
||
|
||
mStyleIfVisited = aStyleIfVisited;
|
||
|
||
MOZ_ASSERT(mStyleIfVisited->IsStyleIfVisited(),
|
||
"other context is visited data");
|
||
MOZ_ASSERT(!mStyleIfVisited->GetStyleIfVisited(),
|
||
"other context does not have visited data");
|
||
NS_ASSERTION(GetStyleIfVisited()->GetPseudo() == GetPseudo(),
|
||
"pseudo tag mismatch");
|
||
if (GetParent() && GetParent()->GetStyleIfVisited()) {
|
||
MOZ_ASSERT(GetStyleIfVisited()->GetParent() ==
|
||
GetParent()->GetStyleIfVisited() ||
|
||
GetStyleIfVisited()->GetParent() ==
|
||
GetParent(),
|
||
"parent mismatch");
|
||
} else {
|
||
MOZ_ASSERT(GetStyleIfVisited()->GetParent() ==
|
||
GetParent(),
|
||
"parent mismatch");
|
||
}
|
||
}
|
||
|