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:
Cameron McCormack 2019-06-27 00:25:03 +00:00
Родитель 396af2948c
Коммит 8538a62557
17 изменённых файлов: 529 добавлений и 125 удалений

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

@ -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;
}