зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1554571 - Part 3: Cache computed styles of scrollbar part anonymous content. r=emilio
Differential Revision: https://phabricator.services.mozilla.com/D33123 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
396af2948c
Коммит
8538a62557
|
@ -3843,6 +3843,18 @@ static void SetFlagsOnSubtree(nsIContent* aNode, uintptr_t aFlagsToSet) {
|
|||
}
|
||||
}
|
||||
|
||||
static void GatherSubtreeElements(Element* aElement,
|
||||
nsTArray<Element*>& aElements) {
|
||||
aElements.AppendElement(aElement);
|
||||
StyleChildrenIterator iter(aElement);
|
||||
for (nsIContent* c = iter.GetNextChild(); c; c = iter.GetNextChild()) {
|
||||
if (!c->IsElement()) {
|
||||
continue;
|
||||
}
|
||||
GatherSubtreeElements(c->AsElement(), aElements);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult nsCSSFrameConstructor::GetAnonymousContent(
|
||||
nsIContent* aParent, nsIFrame* aParentFrame,
|
||||
nsTArray<nsIAnonymousContentCreator::ContentInfo>& aContent) {
|
||||
|
@ -3883,11 +3895,74 @@ nsresult nsCSSFrameConstructor::GetAnonymousContent(
|
|||
}
|
||||
}
|
||||
|
||||
// Some situations where we don't cache anonymous content styles:
|
||||
//
|
||||
// * when visibility is anything other than visible; we rely on visibility
|
||||
// inheriting into anonymous content, but don't bother adding this state
|
||||
// to the AnonymousContentKey, since it's not so common
|
||||
//
|
||||
// * when the medium is anything other than screen; some UA style sheet rules
|
||||
// apply in e.g. print medium, and will give different results from the
|
||||
// cached styles
|
||||
bool allowStyleCaching =
|
||||
StaticPrefs::layout_css_cached_scrollbar_styles_enabled() &&
|
||||
aParentFrame->StyleVisibility()->mVisible == NS_STYLE_VISIBILITY_VISIBLE &&
|
||||
mPresShell->GetPresContext()->Medium() == nsGkAtoms::screen;
|
||||
|
||||
// Compute styles for the anonymous content tree.
|
||||
ServoStyleSet* styleSet = mPresShell->StyleSet();
|
||||
// Eagerly compute styles for the anonymous content tree.
|
||||
for (auto& info : aContent) {
|
||||
if (info.mContent->IsElement()) {
|
||||
styleSet->StyleNewSubtree(info.mContent->AsElement());
|
||||
Element* e = Element::FromNode(info.mContent);
|
||||
if (!e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (info.mKey == AnonymousContentKey::None || !allowStyleCaching) {
|
||||
// Most NAC subtrees do not use caching of computed styles. Just go
|
||||
// ahead and eagerly style the subtree.
|
||||
styleSet->StyleNewSubtree(e);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have a NAC subtree for which we can use cached styles.
|
||||
AutoTArray<RefPtr<ComputedStyle>, 2> cachedStyles;
|
||||
AutoTArray<Element*, 2> elements;
|
||||
|
||||
GatherSubtreeElements(e, elements);
|
||||
styleSet->GetCachedAnonymousContentStyles(info.mKey, cachedStyles);
|
||||
|
||||
if (cachedStyles.IsEmpty()) {
|
||||
// We haven't store cached styles for this kind of NAC subtree yet.
|
||||
// Eagerly compute those styles, then cache them for later.
|
||||
styleSet->StyleNewSubtree(e);
|
||||
for (Element* e : elements) {
|
||||
if (e->HasServoData()) {
|
||||
cachedStyles.AppendElement(ServoStyleSet::ResolveServoStyle(*e));
|
||||
} else {
|
||||
cachedStyles.AppendElement(nullptr);
|
||||
}
|
||||
}
|
||||
styleSet->PutCachedAnonymousContentStyles(info.mKey,
|
||||
std::move(cachedStyles));
|
||||
continue;
|
||||
}
|
||||
|
||||
// We previously stored cached styles for this kind of NAC subtree.
|
||||
// Iterate over them and set them on the subtree's elements.
|
||||
MOZ_ASSERT(cachedStyles.Length() == elements.Length(),
|
||||
"should always produce the same size NAC subtree");
|
||||
for (size_t i = 0, len = cachedStyles.Length(); i != len; ++i) {
|
||||
if (cachedStyles[i]) {
|
||||
#ifdef DEBUG
|
||||
// Assert that our cached style is the same as one we could compute.
|
||||
RefPtr<ComputedStyle> cs = styleSet->ResolveStyleLazily(*elements[i]);
|
||||
MOZ_ASSERT(
|
||||
cachedStyles[i]->EqualForCachedAnonymousContentStyle(*cs),
|
||||
"cached anonymous content styles should be identical to those we "
|
||||
"would compute normally");
|
||||
#endif
|
||||
Servo_SetExplicitStyle(elements[i], cachedStyles[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* values to identify particular subtrees of native anonymous content */
|
||||
|
||||
#ifndef mozilla_AnonymousContentKey_h
|
||||
#define mozilla_AnonymousContentKey_h
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// clang-format off
|
||||
|
||||
// We currently use cached anonymous content styles only for scrollbar parts,
|
||||
// and we can fit the type of scrollbar part element along with its different
|
||||
// options (such as orientation, and other attribute values that can affect
|
||||
// styling) into a uint8_t.
|
||||
//
|
||||
// The lower three bits hold a Type_* value identifying the type of
|
||||
// element, and the remaining bits store Flag_* values.
|
||||
//
|
||||
// A value of 0 is used to represent an anonymous content subtree that we don't
|
||||
// cache styles for.
|
||||
enum class AnonymousContentKey : uint8_t {
|
||||
None = 0x00,
|
||||
|
||||
// all
|
||||
Type_ScrollCorner = 0x01,
|
||||
Type_Resizer = 0x02,
|
||||
Type_Scrollbar = 0x03,
|
||||
Type_ScrollbarButton = 0x04,
|
||||
Type_Slider = 0x05,
|
||||
|
||||
// scrollbar, scrollbarbutton, slider
|
||||
Flag_Vertical = 0x08,
|
||||
|
||||
// resizer
|
||||
Flag_Resizer_Right = 0x08,
|
||||
Flag_Resizer_Bottom = 0x10,
|
||||
Flag_Resizer_Flip = 0x20,
|
||||
|
||||
// scrollbarbutton
|
||||
Flag_ScrollbarButton_Down = 0x10,
|
||||
Flag_ScrollbarButton_Bottom = 0x20,
|
||||
Flag_ScrollbarButton_Decrement = 0x40,
|
||||
};
|
||||
|
||||
// clang-format on
|
||||
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AnonymousContentKey)
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_AnonymousContentKey_h
|
|
@ -134,6 +134,7 @@ EXPORTS += [
|
|||
EXPORTS.mozilla += [
|
||||
'!FrameIdList.h',
|
||||
'!FrameTypeList.h',
|
||||
'AnonymousContentKey.h',
|
||||
'AspectRatio.h',
|
||||
'AutoCopyListener.h',
|
||||
'ColumnUtils.h',
|
||||
|
|
|
@ -4756,6 +4756,51 @@ void ScrollFrameHelper::ReloadChildFrames() {
|
|||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Element> ScrollFrameHelper::MakeScrollbar(
|
||||
NodeInfo* aNodeInfo, bool aVertical, AnonymousContentKey& aKey) {
|
||||
MOZ_ASSERT(aNodeInfo);
|
||||
MOZ_ASSERT(
|
||||
aNodeInfo->Equals(nsGkAtoms::scrollbar, nullptr, kNameSpaceID_XUL));
|
||||
|
||||
static constexpr nsLiteralString kOrientValues[2] = {
|
||||
NS_LITERAL_STRING("horizontal"),
|
||||
NS_LITERAL_STRING("vertical"),
|
||||
};
|
||||
|
||||
aKey = AnonymousContentKey::Type_Scrollbar;
|
||||
if (aVertical) {
|
||||
aKey |= AnonymousContentKey::Flag_Vertical;
|
||||
}
|
||||
|
||||
RefPtr<Element> e;
|
||||
NS_TrustedNewXULElement(getter_AddRefs(e), do_AddRef(aNodeInfo));
|
||||
|
||||
#ifdef DEBUG
|
||||
// Scrollbars can get restyled by theme changes. Whether such a restyle
|
||||
// will actually reconstruct them correctly if it involves a frame
|
||||
// reconstruct... I don't know. :(
|
||||
e->SetProperty(nsGkAtoms::restylableAnonymousNode,
|
||||
reinterpret_cast<void*>(true));
|
||||
#endif // DEBUG
|
||||
|
||||
e->SetAttr(kNameSpaceID_None, nsGkAtoms::orient, kOrientValues[aVertical],
|
||||
false);
|
||||
e->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
|
||||
NS_LITERAL_STRING("always"), false);
|
||||
|
||||
if (mIsRoot) {
|
||||
e->SetProperty(nsGkAtoms::docLevelNativeAnonymousContent,
|
||||
reinterpret_cast<void*>(true));
|
||||
e->SetAttr(kNameSpaceID_None, nsGkAtoms::root_, NS_LITERAL_STRING("true"),
|
||||
false);
|
||||
|
||||
// Don't bother making style caching take [root="true"] styles into account.
|
||||
aKey = AnonymousContentKey::None;
|
||||
}
|
||||
|
||||
return e.forget();
|
||||
}
|
||||
|
||||
bool ScrollFrameHelper::IsForTextControlWithNoScrollbars() const {
|
||||
nsIFrame* parent = mOuter->GetParent();
|
||||
// The anonymous <div> used by <inputs> never gets scrollbars.
|
||||
|
@ -4773,6 +4818,8 @@ bool ScrollFrameHelper::IsForTextControlWithNoScrollbars() const {
|
|||
|
||||
nsresult ScrollFrameHelper::CreateAnonymousContent(
|
||||
nsTArray<nsIAnonymousContentCreator::ContentInfo>& aElements) {
|
||||
typedef nsIAnonymousContentCreator::ContentInfo ContentInfo;
|
||||
|
||||
nsPresContext* presContext = mOuter->PresContext();
|
||||
|
||||
// Don't create scrollbars if we're an SVG document being used as an image,
|
||||
|
@ -4833,62 +4880,28 @@ nsresult ScrollFrameHelper::CreateAnonymousContent(
|
|||
|
||||
nsNodeInfoManager* nodeInfoManager =
|
||||
presContext->Document()->NodeInfoManager();
|
||||
RefPtr<NodeInfo> nodeInfo = nodeInfoManager->GetNodeInfo(
|
||||
nsGkAtoms::scrollbar, nullptr, kNameSpaceID_XUL, nsINode::ELEMENT_NODE);
|
||||
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
if (canHaveHorizontal) {
|
||||
RefPtr<NodeInfo> ni = nodeInfo;
|
||||
NS_TrustedNewXULElement(getter_AddRefs(mHScrollbarContent), ni.forget());
|
||||
#ifdef DEBUG
|
||||
// Scrollbars can get restyled by theme changes. Whether such a restyle
|
||||
// will actually reconstruct them correctly if it involves a frame
|
||||
// reconstruct... I don't know. :(
|
||||
mHScrollbarContent->SetProperty(nsGkAtoms::restylableAnonymousNode,
|
||||
reinterpret_cast<void*>(true));
|
||||
#endif // DEBUG
|
||||
{
|
||||
RefPtr<NodeInfo> nodeInfo = nodeInfoManager->GetNodeInfo(
|
||||
nsGkAtoms::scrollbar, nullptr, kNameSpaceID_XUL, nsINode::ELEMENT_NODE);
|
||||
NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
|
||||
NS_LITERAL_STRING("horizontal"), false);
|
||||
mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
|
||||
NS_LITERAL_STRING("always"), false);
|
||||
if (mIsRoot) {
|
||||
mHScrollbarContent->SetProperty(nsGkAtoms::docLevelNativeAnonymousContent,
|
||||
reinterpret_cast<void*>(true));
|
||||
|
||||
mHScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::root_,
|
||||
NS_LITERAL_STRING("true"), false);
|
||||
if (canHaveHorizontal) {
|
||||
AnonymousContentKey key;
|
||||
mHScrollbarContent = MakeScrollbar(nodeInfo, /* aVertical */ false, key);
|
||||
aElements.AppendElement(ContentInfo(mHScrollbarContent, key));
|
||||
}
|
||||
if (!aElements.AppendElement(mHScrollbarContent))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (canHaveVertical) {
|
||||
RefPtr<NodeInfo> ni = nodeInfo;
|
||||
NS_TrustedNewXULElement(getter_AddRefs(mVScrollbarContent), ni.forget());
|
||||
#ifdef DEBUG
|
||||
// Scrollbars can get restyled by theme changes. Whether such a restyle
|
||||
// will actually reconstruct them correctly if it involves a frame
|
||||
// reconstruct... I don't know. :(
|
||||
mVScrollbarContent->SetProperty(nsGkAtoms::restylableAnonymousNode,
|
||||
reinterpret_cast<void*>(true));
|
||||
#endif // DEBUG
|
||||
|
||||
mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
|
||||
NS_LITERAL_STRING("vertical"), false);
|
||||
mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
|
||||
NS_LITERAL_STRING("always"), false);
|
||||
if (mIsRoot) {
|
||||
mVScrollbarContent->SetProperty(nsGkAtoms::docLevelNativeAnonymousContent,
|
||||
reinterpret_cast<void*>(true));
|
||||
mVScrollbarContent->SetAttr(kNameSpaceID_None, nsGkAtoms::root_,
|
||||
NS_LITERAL_STRING("true"), false);
|
||||
if (canHaveVertical) {
|
||||
AnonymousContentKey key;
|
||||
mVScrollbarContent = MakeScrollbar(nodeInfo, /* aVertical */ true, key);
|
||||
aElements.AppendElement(ContentInfo(mVScrollbarContent, key));
|
||||
}
|
||||
if (!aElements.AppendElement(mVScrollbarContent))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (isResizable) {
|
||||
AnonymousContentKey key = AnonymousContentKey::Type_Resizer;
|
||||
|
||||
RefPtr<NodeInfo> nodeInfo;
|
||||
nodeInfo = nodeInfoManager->GetNodeInfo(
|
||||
nsGkAtoms::resizer, nullptr, kNameSpaceID_XUL, nsINode::ELEMENT_NODE);
|
||||
|
@ -4901,20 +4914,25 @@ nsresult ScrollFrameHelper::CreateAnonymousContent(
|
|||
case StyleResize::Horizontal:
|
||||
if (IsScrollbarOnRight()) {
|
||||
dir.AssignLiteral("right");
|
||||
key |= AnonymousContentKey::Flag_Resizer_Right;
|
||||
} else {
|
||||
dir.AssignLiteral("left");
|
||||
}
|
||||
break;
|
||||
case StyleResize::Vertical:
|
||||
dir.AssignLiteral("bottom");
|
||||
key |= AnonymousContentKey::Flag_Resizer_Bottom;
|
||||
if (!IsScrollbarOnRight()) {
|
||||
mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::flip,
|
||||
EmptyString(), false);
|
||||
key |= AnonymousContentKey::Flag_Resizer_Flip;
|
||||
}
|
||||
break;
|
||||
case StyleResize::Both:
|
||||
key |= AnonymousContentKey::Flag_Resizer_Bottom;
|
||||
if (IsScrollbarOnRight()) {
|
||||
dir.AssignLiteral("bottomright");
|
||||
key |= AnonymousContentKey::Flag_Resizer_Right;
|
||||
} else {
|
||||
dir.AssignLiteral("bottomleft");
|
||||
}
|
||||
|
@ -4940,12 +4958,13 @@ nsresult ScrollFrameHelper::CreateAnonymousContent(
|
|||
mResizerContent->SetAttr(kNameSpaceID_None, nsGkAtoms::clickthrough,
|
||||
NS_LITERAL_STRING("always"), false);
|
||||
|
||||
if (!aElements.AppendElement(mResizerContent))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
aElements.AppendElement(ContentInfo(mResizerContent, key));
|
||||
}
|
||||
|
||||
if (canHaveHorizontal && canHaveVertical) {
|
||||
nodeInfo =
|
||||
AnonymousContentKey key = AnonymousContentKey::Type_ScrollCorner;
|
||||
|
||||
RefPtr<NodeInfo> nodeInfo =
|
||||
nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollcorner, nullptr,
|
||||
kNameSpaceID_XUL, nsINode::ELEMENT_NODE);
|
||||
NS_TrustedNewXULElement(getter_AddRefs(mScrollCornerContent),
|
||||
|
@ -4955,8 +4974,15 @@ nsresult ScrollFrameHelper::CreateAnonymousContent(
|
|||
nsGkAtoms::docLevelNativeAnonymousContent,
|
||||
reinterpret_cast<void*>(true));
|
||||
}
|
||||
if (!aElements.AppendElement(mScrollCornerContent))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
aElements.AppendElement(ContentInfo(mScrollCornerContent, key));
|
||||
}
|
||||
|
||||
// Don't cache styles if we are a child of a <select> element, since we have
|
||||
// some UA style sheet rules that depend on the <select>'s attributes.
|
||||
if (mOuter->GetContent()->IsHTMLElement(nsGkAtoms::select)) {
|
||||
for (auto& info : aElements) {
|
||||
info.mKey = AnonymousContentKey::None;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -513,12 +513,17 @@ class ScrollFrameHelper : public nsIReflowCallback {
|
|||
void UpdateMinimumScaleSize(const nsRect& aScrollableOverflow,
|
||||
const nsSize& aICBSize);
|
||||
|
||||
|
||||
// Return the scroll frame's "true outer size".
|
||||
// This is mOuter->GetSize(), except when mOuter has been sized to reflect
|
||||
// a virtual (layout) viewport in which case this returns the outer size
|
||||
// used to size the physical (visual) viewport.
|
||||
nsSize TrueOuterSize() const;
|
||||
|
||||
already_AddRefed<Element> MakeScrollbar(dom::NodeInfo* aNodeInfo,
|
||||
bool aVertical,
|
||||
AnonymousContentKey& aKey);
|
||||
|
||||
// owning references to the nsIAnonymousContentCreator-built content
|
||||
nsCOMPtr<mozilla::dom::Element> mHScrollbarContent;
|
||||
nsCOMPtr<mozilla::dom::Element> mVScrollbarContent;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#ifndef nsIAnonymousContentCreator_h___
|
||||
#define nsIAnonymousContentCreator_h___
|
||||
|
||||
#include "mozilla/AnonymousContentKey.h"
|
||||
#include "mozilla/ComputedStyle.h"
|
||||
|
||||
#include "nsQueryFrame.h"
|
||||
|
@ -31,9 +32,12 @@ class nsIAnonymousContentCreator {
|
|||
NS_DECL_QUERYFRAME_TARGET(nsIAnonymousContentCreator)
|
||||
|
||||
struct ContentInfo {
|
||||
explicit ContentInfo(nsIContent* aContent) : mContent(aContent) {}
|
||||
explicit ContentInfo(nsIContent* aContent,
|
||||
mozilla::AnonymousContentKey aKey = mozilla::AnonymousContentKey::None)
|
||||
: mContent(aContent), mKey(aKey) {}
|
||||
|
||||
nsIContent* mContent;
|
||||
mozilla::AnonymousContentKey mKey;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -394,4 +394,17 @@ void ComputedStyle::AddSizeOfIncludingThis(nsWindowSizes& aSizes,
|
|||
mCachedInheritingStyles.AddSizeOfIncludingThis(aSizes, aCVsSize);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool ComputedStyle::EqualForCachedAnonymousContentStyle(
|
||||
const ComputedStyle& aOther) const {
|
||||
// One thing we can't add UA rules to prevent is different -x-lang
|
||||
// values being inherited in. So we use this FFI function function rather
|
||||
// than rely on CalcStyleDifference, which can't tell us which specific
|
||||
// properties have changed.
|
||||
return Servo_ComputedValues_EqualForCachedAnonymousContentStyle(this,
|
||||
&aOther);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -240,6 +240,10 @@ class ComputedStyle {
|
|||
nsChangeHint CalcStyleDifference(const ComputedStyle& aNewContext,
|
||||
uint32_t* aEqualStructs) const;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool EqualForCachedAnonymousContentStyle(const ComputedStyle&) const;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get a color that depends on link-visitedness using this and
|
||||
|
|
|
@ -2037,3 +2037,7 @@ void Gecko_LoadData_DeregisterLoad(const StyleLoadData* aData) {
|
|||
MOZ_ASSERT(aData->load_id != 0);
|
||||
ImageLoader::DeregisterCSSImageFromAllLoaders(*aData);
|
||||
}
|
||||
|
||||
void Gecko_PrintfStderr(const nsCString* aStr) {
|
||||
printf_stderr("%s", aStr->get());
|
||||
}
|
||||
|
|
|
@ -732,6 +732,8 @@ bool Gecko_MediaFeatures_IsResourceDocument(const mozilla::dom::Document*);
|
|||
nsAtom* Gecko_MediaFeatures_GetOperatingSystemVersion(
|
||||
const mozilla::dom::Document*);
|
||||
|
||||
void Gecko_PrintfStderr(const nsCString*);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // mozilla_GeckoBindings_h
|
||||
|
|
|
@ -990,6 +990,8 @@ bool ServoStyleSet::EnsureUniqueInnerOnCSSSheets() {
|
|||
void ServoStyleSet::ClearCachedStyleData() {
|
||||
ClearNonInheritingComputedStyles();
|
||||
Servo_StyleSet_RebuildCachedData(mRawSet.get());
|
||||
mCachedAnonymousContentStyles.Clear();
|
||||
PodArrayZero(mCachedAnonymousContentStyleIndexes);
|
||||
}
|
||||
|
||||
void ServoStyleSet::ForceDirtyAllShadowStyles() {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#ifndef mozilla_ServoStyleSet_h
|
||||
#define mozilla_ServoStyleSet_h
|
||||
|
||||
#include "mozilla/AnonymousContentKey.h"
|
||||
#include "mozilla/AtomArray.h"
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
|
@ -294,8 +295,8 @@ class ServoStyleSet {
|
|||
* Clears any cached style data that may depend on all sorts of computed
|
||||
* values.
|
||||
*
|
||||
* Right now this clears the non-inheriting ComputedStyle cache, and resets
|
||||
* the default computed values.
|
||||
* Right now this clears the non-inheriting ComputedStyle cache, resets the
|
||||
* default computed values, and clears cached anonymous content style.
|
||||
*
|
||||
* This does _not_, however, clear the stylist.
|
||||
*/
|
||||
|
@ -553,6 +554,42 @@ class ServoStyleSet {
|
|||
nsCSSAnonBoxes::NonInheriting::_Count, RefPtr<ComputedStyle>>
|
||||
mNonInheritingComputedStyles;
|
||||
|
||||
public:
|
||||
void PutCachedAnonymousContentStyles(
|
||||
AnonymousContentKey aKey, nsTArray<RefPtr<ComputedStyle>>&& aStyles) {
|
||||
auto index = static_cast<size_t>(aKey);
|
||||
|
||||
MOZ_ASSERT(mCachedAnonymousContentStyles.Length() + aStyles.Length() < 256,
|
||||
"(index, length) pairs must be bigger");
|
||||
MOZ_ASSERT(mCachedAnonymousContentStyleIndexes[index].second == 0,
|
||||
"shouldn't need to overwrite existing cached styles");
|
||||
MOZ_ASSERT(!aStyles.IsEmpty(), "should have some styles to cache");
|
||||
|
||||
mCachedAnonymousContentStyleIndexes[index] = std::make_pair(
|
||||
mCachedAnonymousContentStyles.Length(), aStyles.Length());
|
||||
mCachedAnonymousContentStyles.AppendElements(std::move(aStyles));
|
||||
}
|
||||
|
||||
void GetCachedAnonymousContentStyles(
|
||||
AnonymousContentKey aKey, nsTArray<RefPtr<ComputedStyle>>& aStyles) {
|
||||
auto index = static_cast<size_t>(aKey);
|
||||
auto loc = mCachedAnonymousContentStyleIndexes[index];
|
||||
aStyles.AppendElements(mCachedAnonymousContentStyles.Elements() + loc.first,
|
||||
loc.second);
|
||||
}
|
||||
|
||||
private:
|
||||
// Map of AnonymousContentKey values to an (index, length) pair pointing into
|
||||
// mCachedAnonymousContentStyles.
|
||||
//
|
||||
// We assert that the index and length values fit into uint8_ts.
|
||||
std::pair<uint8_t, uint8_t>
|
||||
mCachedAnonymousContentStyleIndexes[1
|
||||
<< sizeof(AnonymousContentKey) * 8]{};
|
||||
|
||||
// Stores cached ComputedStyles for certain native anonymous content.
|
||||
nsTArray<RefPtr<ComputedStyle>> mCachedAnonymousContentStyles;
|
||||
|
||||
StylistState mStylistState = StylistState::NotDirty;
|
||||
bool mAuthorStyleDisabled = false;
|
||||
bool mNeedsRestyleAfterEnsureUniqueInner = false;
|
||||
|
|
|
@ -282,6 +282,48 @@ int32_t nsScrollbarFrame::MoveToNewPosition() {
|
|||
return curpos;
|
||||
}
|
||||
|
||||
static already_AddRefed<Element> MakeScrollbarButton(
|
||||
dom::NodeInfo* aNodeInfo, bool aVertical, bool aBottom, bool aDown,
|
||||
AnonymousContentKey& aKey) {
|
||||
MOZ_ASSERT(aNodeInfo);
|
||||
MOZ_ASSERT(
|
||||
aNodeInfo->Equals(nsGkAtoms::scrollbarbutton, nullptr, kNameSpaceID_XUL));
|
||||
|
||||
static constexpr nsLiteralString kSbattrValues[2][2] = {
|
||||
{
|
||||
NS_LITERAL_STRING("scrollbar-up-top"),
|
||||
NS_LITERAL_STRING("scrollbar-up-bottom"),
|
||||
},
|
||||
{
|
||||
NS_LITERAL_STRING("scrollbar-down-top"),
|
||||
NS_LITERAL_STRING("scrollbar-down-bottom"),
|
||||
},
|
||||
};
|
||||
|
||||
static constexpr nsLiteralString kTypeValues[2] = {
|
||||
NS_LITERAL_STRING("decrement"),
|
||||
NS_LITERAL_STRING("increment"),
|
||||
};
|
||||
|
||||
aKey = AnonymousContentKey::Type_ScrollbarButton;
|
||||
if (aVertical) {
|
||||
aKey |= AnonymousContentKey::Flag_Vertical;
|
||||
}
|
||||
if (aBottom) {
|
||||
aKey |= AnonymousContentKey::Flag_ScrollbarButton_Bottom;
|
||||
}
|
||||
if (aDown) {
|
||||
aKey |= AnonymousContentKey::Flag_ScrollbarButton_Down;
|
||||
}
|
||||
|
||||
RefPtr<Element> e;
|
||||
NS_TrustedNewXULElement(getter_AddRefs(e), do_AddRef(aNodeInfo));
|
||||
e->SetAttr(kNameSpaceID_None, nsGkAtoms::sbattr,
|
||||
kSbattrValues[aDown][aBottom], false);
|
||||
e->SetAttr(kNameSpaceID_None, nsGkAtoms::type, kTypeValues[aDown], false);
|
||||
return e.forget();
|
||||
}
|
||||
|
||||
nsresult nsScrollbarFrame::CreateAnonymousContent(
|
||||
nsTArray<ContentInfo>& aElements) {
|
||||
nsNodeInfoManager* nodeInfoManager = mContent->NodeInfo()->NodeInfoManager();
|
||||
|
@ -296,80 +338,79 @@ nsresult nsScrollbarFrame::CreateAnonymousContent(
|
|||
|
||||
nsAutoString orient;
|
||||
el->GetAttr(kNameSpaceID_None, nsGkAtoms::orient, orient);
|
||||
bool vertical = orient.EqualsLiteral("vertical");
|
||||
|
||||
NS_TrustedNewXULElement(
|
||||
getter_AddRefs(mUpTopButton),
|
||||
RefPtr<dom::NodeInfo> sbbNodeInfo =
|
||||
nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollbarbutton, nullptr,
|
||||
kNameSpaceID_XUL, nsINode::ELEMENT_NODE));
|
||||
mUpTopButton->SetAttr(kNameSpaceID_None, nsGkAtoms::sbattr,
|
||||
NS_LITERAL_STRING("scrollbar-up-top"), false);
|
||||
mUpTopButton->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
|
||||
NS_LITERAL_STRING("decrement"), false);
|
||||
kNameSpaceID_XUL, nsINode::ELEMENT_NODE);
|
||||
|
||||
if (!aElements.AppendElement(mUpTopButton)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
{
|
||||
AnonymousContentKey key;
|
||||
mUpTopButton =
|
||||
MakeScrollbarButton(sbbNodeInfo, vertical, /* aBottom */ false,
|
||||
/* aDown */ false, key);
|
||||
aElements.AppendElement(ContentInfo(mUpTopButton, key));
|
||||
}
|
||||
|
||||
NS_TrustedNewXULElement(
|
||||
getter_AddRefs(mDownTopButton),
|
||||
nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollbarbutton, nullptr,
|
||||
kNameSpaceID_XUL, nsINode::ELEMENT_NODE));
|
||||
mDownTopButton->SetAttr(kNameSpaceID_None, nsGkAtoms::sbattr,
|
||||
NS_LITERAL_STRING("scrollbar-down-top"), false);
|
||||
mDownTopButton->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
|
||||
NS_LITERAL_STRING("increment"), false);
|
||||
|
||||
if (!aElements.AppendElement(mDownTopButton)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
{
|
||||
AnonymousContentKey key;
|
||||
mDownTopButton =
|
||||
MakeScrollbarButton(sbbNodeInfo, vertical, /* aBottom */ false,
|
||||
/* aDown */ true, key);
|
||||
aElements.AppendElement(ContentInfo(mDownTopButton, key));
|
||||
}
|
||||
|
||||
NS_TrustedNewXULElement(
|
||||
getter_AddRefs(mSlider),
|
||||
nodeInfoManager->GetNodeInfo(nsGkAtoms::slider, nullptr, kNameSpaceID_XUL,
|
||||
nsINode::ELEMENT_NODE));
|
||||
mSlider->SetAttr(kNameSpaceID_None, nsGkAtoms::orient, orient, false);
|
||||
mSlider->SetAttr(kNameSpaceID_None, nsGkAtoms::flex, NS_LITERAL_STRING("1"),
|
||||
false);
|
||||
{
|
||||
AnonymousContentKey key = AnonymousContentKey::Type_Slider;
|
||||
if (vertical) {
|
||||
key |= AnonymousContentKey::Flag_Vertical;
|
||||
}
|
||||
|
||||
if (!aElements.AppendElement(mSlider)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_TrustedNewXULElement(
|
||||
getter_AddRefs(mSlider),
|
||||
nodeInfoManager->GetNodeInfo(nsGkAtoms::slider, nullptr,
|
||||
kNameSpaceID_XUL, nsINode::ELEMENT_NODE));
|
||||
mSlider->SetAttr(kNameSpaceID_None, nsGkAtoms::orient, orient, false);
|
||||
mSlider->SetAttr(kNameSpaceID_None, nsGkAtoms::flex, NS_LITERAL_STRING("1"),
|
||||
false);
|
||||
|
||||
aElements.AppendElement(ContentInfo(mSlider, key));
|
||||
|
||||
NS_TrustedNewXULElement(
|
||||
getter_AddRefs(mThumb),
|
||||
nodeInfoManager->GetNodeInfo(nsGkAtoms::thumb, nullptr,
|
||||
kNameSpaceID_XUL, nsINode::ELEMENT_NODE));
|
||||
mThumb->SetAttr(kNameSpaceID_None, nsGkAtoms::orient, orient, false);
|
||||
mThumb->SetAttr(kNameSpaceID_None, nsGkAtoms::align,
|
||||
NS_LITERAL_STRING("center"), false);
|
||||
mThumb->SetAttr(kNameSpaceID_None, nsGkAtoms::pack,
|
||||
NS_LITERAL_STRING("center"), false);
|
||||
mSlider->AppendChildTo(mThumb, false);
|
||||
}
|
||||
|
||||
NS_TrustedNewXULElement(
|
||||
getter_AddRefs(mThumb),
|
||||
nodeInfoManager->GetNodeInfo(nsGkAtoms::thumb, nullptr, kNameSpaceID_XUL,
|
||||
nsINode::ELEMENT_NODE));
|
||||
mThumb->SetAttr(kNameSpaceID_None, nsGkAtoms::orient, orient, false);
|
||||
mThumb->SetAttr(kNameSpaceID_None, nsGkAtoms::align,
|
||||
NS_LITERAL_STRING("center"), false);
|
||||
mThumb->SetAttr(kNameSpaceID_None, nsGkAtoms::pack,
|
||||
NS_LITERAL_STRING("center"), false);
|
||||
mSlider->AppendChildTo(mThumb, false);
|
||||
|
||||
NS_TrustedNewXULElement(
|
||||
getter_AddRefs(mUpBottomButton),
|
||||
nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollbarbutton, nullptr,
|
||||
kNameSpaceID_XUL, nsINode::ELEMENT_NODE));
|
||||
mUpBottomButton->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
|
||||
NS_LITERAL_STRING("decrement"), false);
|
||||
mUpBottomButton->SetAttr(kNameSpaceID_None, nsGkAtoms::sbattr,
|
||||
NS_LITERAL_STRING("scrollbar-up-bottom"), false);
|
||||
|
||||
if (!aElements.AppendElement(mUpBottomButton)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
{
|
||||
AnonymousContentKey key;
|
||||
mUpBottomButton =
|
||||
MakeScrollbarButton(sbbNodeInfo, vertical, /* aBottom */ true,
|
||||
/* aDown */ false, key);
|
||||
aElements.AppendElement(ContentInfo(mUpBottomButton, key));
|
||||
}
|
||||
|
||||
NS_TrustedNewXULElement(
|
||||
getter_AddRefs(mDownBottomButton),
|
||||
nodeInfoManager->GetNodeInfo(nsGkAtoms::scrollbarbutton, nullptr,
|
||||
kNameSpaceID_XUL, nsINode::ELEMENT_NODE));
|
||||
mDownBottomButton->SetAttr(kNameSpaceID_None, nsGkAtoms::sbattr,
|
||||
NS_LITERAL_STRING("scrollbar-down-bottom"), false);
|
||||
mDownBottomButton->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
|
||||
NS_LITERAL_STRING("increment"), false);
|
||||
{
|
||||
AnonymousContentKey key;
|
||||
mDownBottomButton =
|
||||
MakeScrollbarButton(sbbNodeInfo, vertical, /* aBottom */ true,
|
||||
/* aDown */ true, key);
|
||||
aElements.AppendElement(ContentInfo(mDownBottomButton, key));
|
||||
}
|
||||
|
||||
if (!aElements.AppendElement(mDownBottomButton)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
// Don't cache styles if we are inside a <select> element, since we have
|
||||
// some UA style sheet rules that depend on the <select>'s attributes.
|
||||
if (GetContent()->GetParent() &&
|
||||
GetContent()->GetParent()->IsHTMLElement(nsGkAtoms::select)) {
|
||||
for (auto& info : aElements) {
|
||||
info.mKey = AnonymousContentKey::None;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateChildrenAttributeValue(nsGkAtoms::curpos, false);
|
||||
|
|
|
@ -4707,6 +4707,14 @@ VARCACHE_PREF(
|
|||
RelaxedAtomicUint32, 4096
|
||||
)
|
||||
|
||||
// Is the codepath for using cached scrollbar styles enabled?
|
||||
VARCACHE_PREF(
|
||||
Live,
|
||||
"layout.css.cached-scrollbar-styles.enabled",
|
||||
layout_css_cached_scrollbar_styles_enabled,
|
||||
bool, false
|
||||
)
|
||||
|
||||
// Is path() supported in clip-path?
|
||||
VARCACHE_PREF(
|
||||
Live,
|
||||
|
|
|
@ -2836,6 +2836,19 @@ impl ComputedValues {
|
|||
pub fn resolve_color(&self, color: computed::Color) -> RGBA {
|
||||
color.to_rgba(self.get_inherited_text().clone_color())
|
||||
}
|
||||
|
||||
/// Returns which longhand properties have different values in the two
|
||||
/// ComputedValues.
|
||||
#[cfg(feature = "gecko_debug")]
|
||||
pub fn differing_properties(&self, other: &ComputedValues) -> LonghandIdSet {
|
||||
let mut set = LonghandIdSet::new();
|
||||
% for prop in data.longhands:
|
||||
if self.clone_${prop.ident}() != other.clone_${prop.ident}() {
|
||||
set.insert(LonghandId::${prop.camel_case});
|
||||
}
|
||||
% endfor
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
|
|
|
@ -3746,6 +3746,69 @@ pub extern "C" fn Servo_ComputedValues_GetStyleRuleList(
|
|||
}))
|
||||
}
|
||||
|
||||
/// println_stderr!() calls Gecko's printf_stderr(), which, unlike eprintln!(),
|
||||
/// will funnel output to Android logcat.
|
||||
#[cfg(feature = "gecko_debug")]
|
||||
macro_rules! println_stderr {
|
||||
($($e:expr),+) => {
|
||||
{
|
||||
let mut s = nsCString::new();
|
||||
write!(s, $($e),+).unwrap();
|
||||
s.write_char('\n').unwrap();
|
||||
unsafe { bindings::Gecko_PrintfStderr(&s); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko_debug")]
|
||||
fn dump_properties_and_rules(cv: &ComputedValues, properties: &LonghandIdSet) {
|
||||
println_stderr!(" Properties:");
|
||||
for p in properties.iter() {
|
||||
let mut v = String::new();
|
||||
cv.get_longhand_property_value(p, &mut CssWriter::new(&mut v)).unwrap();
|
||||
println_stderr!(" {:?}: {}", p, v);
|
||||
}
|
||||
println_stderr!(" Rules:");
|
||||
let global_style_data = &*GLOBAL_STYLE_DATA;
|
||||
let guard = global_style_data.shared_lock.read();
|
||||
for rn in cv.rules().self_and_ancestors() {
|
||||
if rn.importance().important() {
|
||||
continue;
|
||||
}
|
||||
if let Some(d) = rn.style_source().and_then(|s| s.as_declarations()) {
|
||||
println_stderr!(" [DeclarationBlock: {:?}]", d);
|
||||
}
|
||||
if let Some(r) = rn.style_source().and_then(|s| s.as_rule()) {
|
||||
let mut s = nsString::new();
|
||||
r.read_with(&guard).to_css(&guard, &mut s).unwrap();
|
||||
println_stderr!(" {}", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "gecko_debug")]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_ComputedValues_EqualForCachedAnonymousContentStyle(
|
||||
a: &ComputedValues,
|
||||
b: &ComputedValues,
|
||||
) -> bool {
|
||||
let mut differing_properties = a.differing_properties(b);
|
||||
|
||||
// Ignore any difference in -x-lang, which we can't override in the
|
||||
// rules in minimal-xul.css, but which makes no difference for the
|
||||
// anonymous content subtrees we cache style for.
|
||||
differing_properties.remove(LonghandId::XLang);
|
||||
|
||||
if !differing_properties.is_empty() {
|
||||
println_stderr!("Actual style:");
|
||||
dump_properties_and_rules(a, &differing_properties);
|
||||
println_stderr!("Expected style:");
|
||||
dump_properties_and_rules(b, &differing_properties);
|
||||
}
|
||||
|
||||
differing_properties.is_empty()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_StyleSet_Init(doc: &structs::Document) -> *mut RawServoStyleSet {
|
||||
let data = Box::new(PerDocumentStyleData::new(doc));
|
||||
|
|
|
@ -38,6 +38,61 @@
|
|||
visibility: collapse;
|
||||
}
|
||||
|
||||
/* Rules required for style caching of anonymous content scrollbar parts */
|
||||
|
||||
scrollcorner, resizer, scrollbar, scrollbarbutton, slider {
|
||||
/* All scrollbar parts must not inherit any properties from the scrollable
|
||||
* element (except for visibility), for the anonymous content style caching
|
||||
* system to work.
|
||||
*/
|
||||
all: initial;
|
||||
visibility: inherit;
|
||||
|
||||
/* These properties are not included in 'all'. */
|
||||
-moz-list-reversed: initial;
|
||||
-moz-font-smoothing-background-color: initial;
|
||||
|
||||
/* Using initial is not sufficient for direction, since its initial value can
|
||||
* depend on the document's language.
|
||||
*
|
||||
* LTR is what we want for all scrollbar parts anyway, so that e.g. we don't
|
||||
* reverse the rendering of a horizontal scrollbar.
|
||||
*/
|
||||
direction: ltr;
|
||||
|
||||
/* Similarly for font properties, whose initial values depend on the
|
||||
* document's language. Scrollbar parts don't have any text or rely on
|
||||
* font metrics.
|
||||
*/
|
||||
font: 16px sans-serif;
|
||||
|
||||
/* Duplicate the rules from the '*' rule above, which were clobbered by the
|
||||
* 'all: initial' declaration.
|
||||
*
|
||||
* The other zero specificity rules above are on :root, and scrollbar parts
|
||||
* cannot match :root, so no need to duplicate them.
|
||||
*/
|
||||
-moz-user-focus: ignore;
|
||||
-moz-user-select: none;
|
||||
display: -moz-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
thumb {
|
||||
/* Prevent -moz-user-modify declaration from designmode.css having an
|
||||
* effect. */
|
||||
-moz-user-modify: initial;
|
||||
}
|
||||
|
||||
/* There are other rules that set direction and cursor on scrollbar,
|
||||
* expecting them to inherit into its children. Explicitly inherit those,
|
||||
* overriding the 'all: initial; direction: ltr;' declarations above.
|
||||
*/
|
||||
scrollbarbutton, slider, thumb {
|
||||
direction: inherit;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
/********** resizer **********/
|
||||
|
||||
resizer {
|
||||
|
@ -97,11 +152,6 @@ resizer[dir="topright"] {
|
|||
|
||||
/********** scrollbar **********/
|
||||
|
||||
/* Scrollbars are never flipped even if BiDI kicks in. */
|
||||
scrollbar[orient="horizontal"] {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
thumb {
|
||||
display: -moz-box !important;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче