зеркало из https://github.com/mozilla/gecko-dev.git
merge autoland to mozilla-central. r=merge a=merge
MozReview-Commit-ID: JWKcdNfAFqk
This commit is contained in:
Коммит
dd669d05e1
|
@ -7,6 +7,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "devtools",
|
||||
"resource://devtools/shared/Loader.jsm");
|
||||
|
||||
function isActiveSidebarTabTitle(inspector, expectedTabTitle, message) {
|
||||
const actualTabTitle = inspector.panelDoc.querySelector(".tabs-menu-item.is-active").innerText;
|
||||
is(actualTabTitle, expectedTabTitle, message);
|
||||
}
|
||||
|
||||
add_task(async function test_devtools_panels_elements_sidebar() {
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
|
||||
|
||||
|
@ -70,6 +75,9 @@ add_task(async function test_devtools_panels_elements_sidebar() {
|
|||
|
||||
is(shownSidebarInstance, "sidebar1", "Got the shown event on the first extension sidebar");
|
||||
|
||||
isActiveSidebarTabTitle(inspector, "Test Sidebar 1",
|
||||
"Got the expected title on the active sidebar tab");
|
||||
|
||||
const sidebarPanel1 = inspector.sidebar.getTabPanel(sidebarIds[0]);
|
||||
|
||||
ok(sidebarPanel1, "Got a rendered sidebar panel for the first registered extension sidebar");
|
||||
|
@ -94,6 +102,9 @@ add_task(async function test_devtools_panels_elements_sidebar() {
|
|||
is(shownSidebarInstance2, "sidebar2", "Got the shown event on the second extension sidebar");
|
||||
is(hiddenSidebarInstance1, "sidebar1", "Got the hidden event on the first extension sidebar");
|
||||
|
||||
isActiveSidebarTabTitle(inspector, "Test Sidebar 2",
|
||||
"Got the expected title on the active sidebar tab");
|
||||
|
||||
const sidebarPanel2 = inspector.sidebar.getTabPanel(sidebarIds[1]);
|
||||
|
||||
ok(sidebarPanel2, "Got a rendered sidebar panel for the second registered extension sidebar");
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
}
|
||||
|
||||
#main-window:not([tabsintitlebar]) > #titlebar {
|
||||
-moz-appearance: -moz-window-titlebar;
|
||||
height: 22px; /* The native titlebar on OS X is 22px tall. */
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ class ExtensionSidebar {
|
|||
this._provider = createElement(Provider, {
|
||||
store: this.store,
|
||||
key: this.id,
|
||||
title: this.title,
|
||||
}, ExtensionSidebarComponent({
|
||||
id: this.id,
|
||||
}));
|
||||
|
|
|
@ -22,6 +22,8 @@ add_task(async function () {
|
|||
|
||||
is(sidebar.title, sidebarTitle,
|
||||
"Got the expected title in the extension sidebar instance");
|
||||
is(sidebar.provider.props.title, sidebarTitle,
|
||||
"Got the expeted title in the provider props");
|
||||
|
||||
let inspectorStoreState = inspector.store.getState();
|
||||
|
||||
|
|
|
@ -77,15 +77,6 @@ function shouldSkipTest(test) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (SpecialPowers.DOMWindowUtils.isStyledByServo &&
|
||||
SpecialPowers.isDebugBuild &&
|
||||
navigator.platform.indexOf('Mac') >= 0) {
|
||||
if (test == "file_fullscreen-backdrop.html") {
|
||||
todo(false, `${test} skipped due to bug 1387942`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -2156,6 +2156,13 @@ MediaCacheStream::GetLength()
|
|||
return mStreamLength;
|
||||
}
|
||||
|
||||
int64_t
|
||||
MediaCacheStream::GetOffset() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mChannelOffset;
|
||||
}
|
||||
|
||||
int64_t
|
||||
MediaCacheStream::GetNextCachedData(int64_t aOffset)
|
||||
{
|
||||
|
|
|
@ -285,6 +285,8 @@ public:
|
|||
// If we've successfully read data beyond the originally reported length,
|
||||
// we return the end of the data we've read.
|
||||
int64_t GetLength();
|
||||
// Return the offset where next channel data will write to. Main thread only.
|
||||
int64_t GetOffset() const;
|
||||
// Returns the unique resource ID. Call only on the main thread or while
|
||||
// holding the media cache lock.
|
||||
int64_t GetResourceID() { return mResourceID; }
|
||||
|
|
|
@ -80,7 +80,6 @@ ChannelMediaResource::ChannelMediaResource(MediaResourceCallback* aCallback,
|
|||
nsIURI* aURI,
|
||||
bool aIsPrivateBrowsing)
|
||||
: BaseMediaResource(aCallback, aChannel, aURI)
|
||||
, mOffset(0)
|
||||
, mReopenOnError(false)
|
||||
, mCacheStream(this, aIsPrivateBrowsing)
|
||||
, mSuspendAgent(mChannel)
|
||||
|
@ -93,7 +92,6 @@ ChannelMediaResource::ChannelMediaResource(
|
|||
nsIURI* aURI,
|
||||
const MediaChannelStatistics& aStatistics)
|
||||
: BaseMediaResource(aCallback, aChannel, aURI)
|
||||
, mOffset(0)
|
||||
, mReopenOnError(false)
|
||||
, mCacheStream(this, /* aIsPrivateBrowsing = */ false)
|
||||
, mChannelStatistics(aStatistics)
|
||||
|
@ -152,17 +150,20 @@ ChannelMediaResource::Listener::OnDataAvailable(nsIRequest* aRequest,
|
|||
}
|
||||
|
||||
nsresult
|
||||
ChannelMediaResource::Listener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
|
||||
nsIChannel* aNewChannel,
|
||||
uint32_t aFlags,
|
||||
nsIAsyncVerifyRedirectCallback* cb)
|
||||
ChannelMediaResource::Listener::AsyncOnChannelRedirect(
|
||||
nsIChannel* aOld,
|
||||
nsIChannel* aNew,
|
||||
uint32_t aFlags,
|
||||
nsIAsyncVerifyRedirectCallback* cb)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
if (mResource)
|
||||
rv = mResource->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
|
||||
if (mResource) {
|
||||
rv = mResource->OnChannelRedirect(aOld, aNew, aFlags, mOffset);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
cb->OnRedirectVerifyCallback(NS_OK);
|
||||
return NS_OK;
|
||||
|
@ -284,26 +285,19 @@ ChannelMediaResource::OnStartRequest(nsIRequest* aRequest)
|
|||
if (rangeTotal != -1) {
|
||||
contentLength = std::max(contentLength, rangeTotal);
|
||||
}
|
||||
// Give some warnings if the ranges are unexpected.
|
||||
// XXX These could be error conditions.
|
||||
NS_WARNING_ASSERTION(
|
||||
mOffset == rangeStart,
|
||||
"response range start does not match current offset");
|
||||
mOffset = rangeStart;
|
||||
mCacheStream.NotifyDataStarted(rangeStart);
|
||||
}
|
||||
acceptsRanges = gotRangeHeader;
|
||||
} else if (mOffset > 0 && responseStatus == HTTP_OK_CODE) {
|
||||
} else if (GetOffset() > 0 && responseStatus == HTTP_OK_CODE) {
|
||||
// If we get an OK response but we were seeking, or requesting a byte
|
||||
// range, then we have to assume that seeking doesn't work. We also need
|
||||
// to tell the cache that it's getting data for the start of the stream.
|
||||
mCacheStream.NotifyDataStarted(0);
|
||||
mOffset = 0;
|
||||
|
||||
// The server claimed it supported range requests. It lied.
|
||||
acceptsRanges = false;
|
||||
}
|
||||
if (mOffset == 0 && contentLength >= 0 &&
|
||||
if (GetOffset() == 0 && contentLength >= 0 &&
|
||||
(responseStatus == HTTP_OK_CODE ||
|
||||
responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
|
||||
mCacheStream.NotifyDataLength(contentLength);
|
||||
|
@ -398,14 +392,14 @@ ChannelMediaResource::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
|
|||
// cause us to just re-read the stream, which would be really bad.
|
||||
if (mReopenOnError && aStatus != NS_ERROR_PARSED_DATA_CACHED &&
|
||||
aStatus != NS_BINDING_ABORTED &&
|
||||
(mOffset == 0 || (GetLength() > 0 && mOffset != GetLength() &&
|
||||
mCacheStream.IsTransportSeekable()))) {
|
||||
(GetOffset() == 0 || (GetLength() > 0 && GetOffset() != GetLength() &&
|
||||
mCacheStream.IsTransportSeekable()))) {
|
||||
// If the stream did close normally, restart the channel if we're either
|
||||
// at the start of the resource, or if the server is seekable and we're
|
||||
// not at the end of stream. We don't restart the stream if we're at the
|
||||
// end because not all web servers handle this case consistently; see:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1373618#c36
|
||||
nsresult rv = CacheClientSeek(mOffset, false);
|
||||
nsresult rv = CacheClientSeek(GetOffset(), false);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -430,12 +424,14 @@ ChannelMediaResource::OnStopRequest(nsIRequest* aRequest, nsresult aStatus)
|
|||
}
|
||||
|
||||
nsresult
|
||||
ChannelMediaResource::OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew,
|
||||
uint32_t aFlags)
|
||||
ChannelMediaResource::OnChannelRedirect(nsIChannel* aOld,
|
||||
nsIChannel* aNew,
|
||||
uint32_t aFlags,
|
||||
int64_t aOffset)
|
||||
{
|
||||
mChannel = aNew;
|
||||
mSuspendAgent.NotifyChannelOpened(mChannel);
|
||||
return SetupChannelHeaders();
|
||||
return SetupChannelHeaders(aOffset);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -444,11 +440,6 @@ ChannelMediaResource::CopySegmentToCache(nsIPrincipal* aPrincipal,
|
|||
uint32_t aCount,
|
||||
uint32_t* aWriteCount)
|
||||
{
|
||||
// Keep track of where we're up to.
|
||||
LOG("CopySegmentToCache at mOffset [%" PRId64 "] add "
|
||||
"[%d] bytes for decoder[%p]",
|
||||
mOffset, aCount, mCallback.get());
|
||||
mOffset += aCount;
|
||||
mCacheStream.NotifyDataReceived(aCount, aFromSegment, aPrincipal);
|
||||
*aWriteCount = aCount;
|
||||
return NS_OK;
|
||||
|
@ -528,25 +519,25 @@ ChannelMediaResource::Open(nsIStreamListener** aStreamListener)
|
|||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mOffset == 0, "Who set mOffset already?");
|
||||
mListener = new Listener(this);
|
||||
MOZ_ASSERT(GetOffset() == 0, "Who set offset already?");
|
||||
mListener = new Listener(this, 0);
|
||||
*aStreamListener = mListener;
|
||||
NS_ADDREF(*aStreamListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ChannelMediaResource::OpenChannel()
|
||||
ChannelMediaResource::OpenChannel(int64_t aOffset)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(mChannel);
|
||||
MOZ_ASSERT(!mListener, "Listener should have been removed by now");
|
||||
|
||||
mListener = new Listener(this);
|
||||
mListener = new Listener(this, aOffset);
|
||||
nsresult rv = mChannel->SetNotificationCallbacks(mListener.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = SetupChannelHeaders();
|
||||
rv = SetupChannelHeaders(aOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mChannel->AsyncOpen2(mListener);
|
||||
|
@ -561,7 +552,8 @@ ChannelMediaResource::OpenChannel()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult ChannelMediaResource::SetupChannelHeaders()
|
||||
nsresult
|
||||
ChannelMediaResource::SetupChannelHeaders(int64_t aOffset)
|
||||
{
|
||||
// Always use a byte range request even if we're reading from the start
|
||||
// of the resource.
|
||||
|
@ -571,7 +563,7 @@ nsresult ChannelMediaResource::SetupChannelHeaders()
|
|||
if (hc) {
|
||||
// Use |mOffset| if seeking in a complete file download.
|
||||
nsAutoCString rangeString("bytes=");
|
||||
rangeString.AppendInt(mOffset);
|
||||
rangeString.AppendInt(aOffset);
|
||||
rangeString.Append('-');
|
||||
nsresult rv = hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -584,7 +576,7 @@ nsresult ChannelMediaResource::SetupChannelHeaders()
|
|||
NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
|
||||
element->SetRequestHeaders(hc);
|
||||
} else {
|
||||
NS_ASSERTION(mOffset == 0, "Don't know how to seek on this channel type");
|
||||
NS_ASSERTION(aOffset == 0, "Don't know how to seek on this channel type");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -754,10 +746,10 @@ void ChannelMediaResource::Resume()
|
|||
// the channel dead; if the media cache wants to read some other data
|
||||
// in the future, it will call CacheClientSeek itself which will reopen the
|
||||
// channel.
|
||||
if (totalLength < 0 || mOffset < totalLength) {
|
||||
if (totalLength < 0 || GetOffset() < totalLength) {
|
||||
// There is (or may be) data to read at mOffset, so start reading it.
|
||||
// Need to recreate the channel.
|
||||
CacheClientSeek(mOffset, false);
|
||||
CacheClientSeek(GetOffset(), false);
|
||||
element->DownloadResumed();
|
||||
} else {
|
||||
// The channel remains dead. Do not notify DownloadResumed() which
|
||||
|
@ -863,8 +855,6 @@ ChannelMediaResource::CacheClientSeek(int64_t aOffset, bool aResume)
|
|||
|
||||
CloseChannel();
|
||||
|
||||
mOffset = aOffset;
|
||||
|
||||
if (aResume) {
|
||||
mSuspendAgent.Resume();
|
||||
}
|
||||
|
@ -878,7 +868,7 @@ ChannelMediaResource::CacheClientSeek(int64_t aOffset, bool aResume)
|
|||
nsresult rv = RecreateChannel();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return OpenChannel();
|
||||
return OpenChannel(aOffset);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -962,6 +952,12 @@ ChannelMediaResource::GetLength()
|
|||
return mCacheStream.GetLength();
|
||||
}
|
||||
|
||||
int64_t
|
||||
ChannelMediaResource::GetOffset() const
|
||||
{
|
||||
return mCacheStream.GetOffset();
|
||||
}
|
||||
|
||||
// ChannelSuspendAgent
|
||||
|
||||
bool
|
||||
|
|
|
@ -510,7 +510,10 @@ public:
|
|||
{
|
||||
~Listener() {}
|
||||
public:
|
||||
explicit Listener(ChannelMediaResource* aResource) : mResource(aResource) {}
|
||||
Listener(ChannelMediaResource* aResource, int64_t aOffset)
|
||||
: mResource(aResource)
|
||||
, mOffset(aOffset)
|
||||
{}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
|
@ -523,6 +526,7 @@ public:
|
|||
|
||||
private:
|
||||
RefPtr<ChannelMediaResource> mResource;
|
||||
const int64_t mOffset;
|
||||
};
|
||||
friend class Listener;
|
||||
|
||||
|
@ -536,17 +540,22 @@ protected:
|
|||
nsresult OnDataAvailable(nsIRequest* aRequest,
|
||||
nsIInputStream* aStream,
|
||||
uint32_t aCount);
|
||||
nsresult OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew, uint32_t aFlags);
|
||||
nsresult OnChannelRedirect(nsIChannel* aOld,
|
||||
nsIChannel* aNew,
|
||||
uint32_t aFlags,
|
||||
int64_t aOffset);
|
||||
|
||||
// Opens the channel, using an HTTP byte range request to start at mOffset
|
||||
// Opens the channel, using an HTTP byte range request to start at aOffset
|
||||
// if possible. Main thread only.
|
||||
nsresult OpenChannel();
|
||||
nsresult OpenChannel(int64_t aOffset);
|
||||
nsresult RecreateChannel();
|
||||
// Add headers to HTTP request. Main thread only.
|
||||
nsresult SetupChannelHeaders();
|
||||
nsresult SetupChannelHeaders(int64_t aOffset);
|
||||
// Closes the channel. Main thread only.
|
||||
void CloseChannel();
|
||||
|
||||
int64_t GetOffset() const;
|
||||
|
||||
// Parses 'Content-Range' header and returns results via parameters.
|
||||
// Returns error if header is not available, values are not parse-able or
|
||||
// values are out of range.
|
||||
|
@ -568,7 +577,6 @@ protected:
|
|||
uint32_t* aWriteCount);
|
||||
|
||||
// Main thread access only
|
||||
int64_t mOffset;
|
||||
RefPtr<Listener> mListener;
|
||||
// When this flag is set, if we get a network error we should silently
|
||||
// reopen the stream.
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace layers {
|
|||
|
||||
StackingContextHelper::StackingContextHelper()
|
||||
: mBuilder(nullptr)
|
||||
, mHasPerspectiveTransform(false)
|
||||
, mXScale(1.0f)
|
||||
, mYScale(1.0f)
|
||||
{
|
||||
|
@ -26,6 +27,7 @@ StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParen
|
|||
const Maybe<gfx::Matrix4x4>& aTransform,
|
||||
const nsTArray<wr::WrFilterOp>& aFilters)
|
||||
: mBuilder(&aBuilder)
|
||||
, mHasPerspectiveTransform(false)
|
||||
, mXScale(1.0f)
|
||||
, mYScale(1.0f)
|
||||
{
|
||||
|
@ -51,6 +53,7 @@ StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParen
|
|||
gfx::Matrix4x4* aTransformPtr,
|
||||
const nsTArray<wr::WrFilterOp>& aFilters)
|
||||
: mBuilder(&aBuilder)
|
||||
, mHasPerspectiveTransform(false)
|
||||
, mXScale(1.0f)
|
||||
, mYScale(1.0f)
|
||||
{
|
||||
|
@ -83,6 +86,7 @@ StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParen
|
|||
const nsTArray<wr::WrFilterOp>& aFilters,
|
||||
const gfx::CompositionOp& aMixBlendMode)
|
||||
: mBuilder(&aBuilder)
|
||||
, mHasPerspectiveTransform(false)
|
||||
, mXScale(1.0f)
|
||||
, mYScale(1.0f)
|
||||
{
|
||||
|
@ -92,6 +96,10 @@ StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParen
|
|||
mTransform = *aTransformPtr;
|
||||
}
|
||||
|
||||
if (aPerspectivePtr) {
|
||||
mHasPerspectiveTransform = true;
|
||||
}
|
||||
|
||||
bool is2d = !aTransformPtr || (aTransformPtr->Is2D() && !aPerspectivePtr);
|
||||
if (is2d) {
|
||||
nsRect itemBounds = aDisplayList->GetClippedBoundsWithRespectToASR(aDisplayListBuilder, aItem->GetActiveScrolledRoot());
|
||||
|
@ -102,6 +110,7 @@ StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParen
|
|||
mTransform.PostScale(aParentSC.mXScale, aParentSC.mYScale, 1.0);
|
||||
mTransform.NudgeToIntegersFixedEpsilon();
|
||||
|
||||
// Calculate the correct scale for current stacking context
|
||||
gfx::Size scale = mTransform.As2D().ScaleFactors(true);
|
||||
|
||||
// Restore the scale to default if the scale is too small
|
||||
|
@ -112,7 +121,7 @@ StackingContextHelper::StackingContextHelper(const StackingContextHelper& aParen
|
|||
|
||||
mTransform.PreScale(1.0f/scale.width, 1.0f/scale.height, 1.0);
|
||||
|
||||
// Store the inherited scale if has
|
||||
// Store the inherited scale for child
|
||||
this->mXScale = scale.width;
|
||||
this->mYScale = scale.height;
|
||||
} else {
|
||||
|
|
|
@ -83,13 +83,26 @@ public:
|
|||
// Same but rounds the rectangle to ints after transforming.
|
||||
wr::LayoutRect ToRelativeLayoutRectRounded(const LayoutDeviceRect& aRect) const;
|
||||
|
||||
// Export the inherited scale
|
||||
gfx::Size GetInheritedScale() const {
|
||||
return gfx::Size(mXScale, mYScale);
|
||||
}
|
||||
|
||||
// Provide interface to setup the inherited scale to support
|
||||
// special cases, like OMTA
|
||||
void SetInheritedScale(const gfx::Size& aScale) {
|
||||
mXScale = aScale.width;
|
||||
mYScale = aScale.height;
|
||||
}
|
||||
|
||||
bool IsBackfaceVisible() const { return mTransform.IsBackfaceVisible(); }
|
||||
bool HasPerspectiveTransform() const { return mHasPerspectiveTransform; }
|
||||
|
||||
private:
|
||||
wr::DisplayListBuilder* mBuilder;
|
||||
LayerPoint mOrigin;
|
||||
gfx::Matrix4x4 mTransform;
|
||||
|
||||
bool mHasPerspectiveTransform;
|
||||
float mXScale;
|
||||
float mYScale;
|
||||
};
|
||||
|
|
|
@ -556,8 +556,10 @@ UpdateBackdropIfNeeded(nsIFrame* aFrame,
|
|||
/* aPseudoElement = */ nullptr);
|
||||
|
||||
// NOTE(emilio): We can't use the changes handled for the owner of the
|
||||
// backdrop frame, since it's out of flow, and parented to the viewport frame.
|
||||
MOZ_ASSERT(backdropFrame->GetParent()->IsViewportFrame());
|
||||
// backdrop frame, since it's out of flow, and parented to the viewport or
|
||||
// canvas frame (depending on the `position` value).
|
||||
MOZ_ASSERT(backdropFrame->GetParent()->IsViewportFrame() ||
|
||||
backdropFrame->GetParent()->IsCanvasFrame());
|
||||
nsTArray<nsIFrame*> wrappersToRestyle;
|
||||
ServoRestyleState state(aStyleSet, aChangeList, wrappersToRestyle);
|
||||
aFrame->UpdateStyleOfOwnedChildFrame(backdropFrame, newContext, state);
|
||||
|
|
|
@ -6494,15 +6494,8 @@ nsTextFrame::PaintOneShadow(const PaintShadowParams& aParams,
|
|||
if (!shadowContext)
|
||||
return;
|
||||
|
||||
nscolor shadowColor;
|
||||
const nscolor* decorationOverrideColor;
|
||||
if (aShadowDetails->mHasColor) {
|
||||
shadowColor = aShadowDetails->mColor;
|
||||
decorationOverrideColor = &shadowColor;
|
||||
} else {
|
||||
shadowColor = aParams.foregroundColor;
|
||||
decorationOverrideColor = nullptr;
|
||||
}
|
||||
nscolor shadowColor = aShadowDetails->mHasColor ? aShadowDetails->mColor
|
||||
: aParams.foregroundColor;
|
||||
|
||||
if (aParams.textDrawer) {
|
||||
wr::TextShadow wrShadow;
|
||||
|
@ -6538,7 +6531,8 @@ nsTextFrame::PaintOneShadow(const PaintShadowParams& aParams,
|
|||
aParams.context == shadowContext ? shadowColor : NS_RGB(0, 0, 0);
|
||||
params.clipEdges = aParams.clipEdges;
|
||||
params.drawSoftHyphen = (GetStateBits() & TEXT_HYPHEN_BREAK) != 0;
|
||||
params.decorationOverrideColor = decorationOverrideColor;
|
||||
// Multi-color shadow is not allowed, so we use the same color of the text color.
|
||||
params.decorationOverrideColor = ¶ms.textColor;
|
||||
DrawText(aParams.range, aParams.textBaselinePt + shadowOffset, params);
|
||||
|
||||
contextBoxBlur.DoPaint();
|
||||
|
|
|
@ -7812,15 +7812,6 @@ nsDisplayTransform::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBu
|
|||
// transform animation, the transform value will be resolved
|
||||
// after animation sampling on the compositor
|
||||
transformForSC = nullptr;
|
||||
|
||||
// Pass default transform to compositor in case gecko fails to
|
||||
// get animated value after animation sampling.
|
||||
OptionalTransform transformForCompositor = newTransformMatrix;
|
||||
|
||||
OpAddCompositorAnimations
|
||||
anim(CompositorAnimations(animationInfo.GetAnimations(), animationsId),
|
||||
transformForCompositor, void_t());
|
||||
aManager->WrBridge()->AddWebRenderParentCommand(anim);
|
||||
}
|
||||
|
||||
gfx::Matrix4x4Typed<LayerPixel, LayerPixel> boundTransform = ViewAs< gfx::Matrix4x4Typed<LayerPixel, LayerPixel> >(newTransformMatrix);
|
||||
|
@ -7841,6 +7832,33 @@ nsDisplayTransform::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBu
|
|||
nullptr,
|
||||
filters);
|
||||
|
||||
if (animationsId) {
|
||||
// Get the inheritedScale from parent and pass the scale to compositor
|
||||
// to get correct sampling result
|
||||
gfx::Size scale = aSc.GetInheritedScale();
|
||||
for (layers::Animation& animation : animationInfo.GetAnimations()) {
|
||||
if (animation.property() == eCSSProperty_transform) {
|
||||
TransformData& transformData = animation.data().get_TransformData();
|
||||
transformData.inheritedXScale() = scale.width;
|
||||
transformData.inheritedYScale() = scale.height;
|
||||
transformData.hasPerspectiveParent() = aSc.HasPerspectiveTransform();
|
||||
}
|
||||
}
|
||||
|
||||
// Pass default transform to compositor in case gecko fails to
|
||||
// get animated value after animation sampling.
|
||||
OptionalTransform transformForCompositor = newTransformMatrix;
|
||||
OpAddCompositorAnimations
|
||||
anim(CompositorAnimations(animationInfo.GetAnimations(), animationsId),
|
||||
transformForCompositor, void_t());
|
||||
aManager->WrBridge()->AddWebRenderParentCommand(anim);
|
||||
|
||||
// Since we passed a nullptr transformForSC to the StackingContextHelper,
|
||||
// we now set up the correct inherited scale for the stacking context.
|
||||
newTransformMatrix.PostScale(scale.width, scale.height, 1.0f);
|
||||
sc.SetInheritedScale(newTransformMatrix.As2D().ScaleFactors(true));
|
||||
|
||||
}
|
||||
return mStoredList.CreateWebRenderCommands(aBuilder, sc, aParentCommands,
|
||||
aManager, aDisplayListBuilder);
|
||||
}
|
||||
|
|
|
@ -1651,11 +1651,13 @@ fuzzy-if(Android,8,300) fuzzy-if(skiaContent,1,40000) == 625409-1.html 625409-1-
|
|||
fuzzy-if(skiaContent,1,500) == 630835-1.html about:blank
|
||||
== 631352-1.html 631352-1-ref.html
|
||||
skip-if(!haveTestPlugin) fails-if(Android) fuzzy-if(winWidget&&!layersGPUAccelerated,102,535) fuzzy-if(skiaContent&&!Android,102,11000) HTTP == 632423-1.html 632423-1-ref.html
|
||||
pref(gfx.webrender.layers-free,true) skip-if(!webrender) == 632423-1.html 632423-1-ref.html
|
||||
skip-if(Android) random-if(winWidget||OSX==1010) == 632781-verybig.html 632781-ref.html
|
||||
== 632781-normalsize.html 632781-ref.html
|
||||
fuzzy-if(d2d&&/^Windows\x20NT\x206\.2/.test(http.oscpu),1,559) fuzzy-if(!isDebugBuild&>kWidget&&/^Linux\x20i686/.test(http.oscpu),102,140) == 633344-1.html 633344-1-ref.html # bug 1103623, Linux32 from GCC update
|
||||
fuzzy-if(skiaContent,1,500) == 634232-1.html 634232-1-ref.html
|
||||
fuzzy-if(skiaContent,3,120000) == 635302-1.html 635302-1-ref.html
|
||||
pref(gfx.webrender.layers-free,true) skip-if(!webrender) == 635302-1.html 635302-1-ref.html
|
||||
fuzzy(1,68) fuzzy-if(gtkWidget,1,70) fails-if(Android) fuzzy-if(skiaContent&&!Android,1,300) == 635373-1.html 635373-1-ref.html
|
||||
random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,20,118) fuzzy-if(skiaContent&&!Android,2,550) == 635373-2.html 635373-2-ref.html
|
||||
random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,20,116) fuzzy-if(skiaContent&&!Android,2,650) == 635373-3.html 635373-3-ref.html
|
||||
|
|
|
@ -43,6 +43,7 @@ fails == background-position-running.html background-position-ref.html # This te
|
|||
fails == background-position-important.html background-position-ref.html # This test fails the reftest-opaque-layer check since animating background-position overridden by a non-animated !important style also creates an active layer, and reftest-opaque-layer only handles items that are assigned to PaintedLayers.
|
||||
|
||||
== mask-position-after-finish-1a.html mask-anim-ref.html
|
||||
pref(gfx.webrender.layers-free,true) skip-if(!webrender) == mask-position-after-finish-1a.html mask-anim-ref.html
|
||||
== mask-position-after-finish-1b.html mask-anim-ref.html
|
||||
== mask-position-in-delay-1a.html mask-anim-ref.html
|
||||
== mask-position-in-delay-1b.html mask-anim-ref.html
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
fuzzy-if(Android,8,30) == background-image-zoom-1.html background-image-zoom-1-ref.html
|
||||
pref(gfx.webrender.layers-free,true) skip-if(!webrender) == background-image-zoom-1.html background-image-zoom-1-ref.html
|
||||
fails-if(usesRepeatResampling) == background-image-zoom-2.html about:blank
|
||||
== image-seam-1a.html image-seam-1-ref.html
|
||||
== image-seam-1b.html image-seam-1-ref.html
|
||||
|
|
|
@ -86,6 +86,7 @@ pref(layers.single-tile.enabled,false) != fast-scrolling.html about:blank
|
|||
== negative-w-component.html negative-w-component-ref.html
|
||||
|
||||
== mask-invalidation-1a.html mask-invalidation-1-ref.html
|
||||
pref(gfx.webrender.layers-free,true) skip-if(!webrender) == mask-invalidation-1a.html mask-invalidation-1-ref.html
|
||||
== mask-invalidation-1b.html mask-invalidation-1-ref.html
|
||||
|
||||
== mask-invalidation-2a.html mask-invalidation-2-ref.html
|
||||
|
|
|
@ -2,14 +2,23 @@
|
|||
|
||||
<!-- Shadows -->
|
||||
<!-- Blue underline/text -->
|
||||
<div style="position: absolute; top: 22px; left: 22px; color: blue; text-decoration: underline;"><span style="color: rgba(0, 0, 0, 0);">testforquirks</span></div>
|
||||
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: blue;">test</span></div>
|
||||
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: blue; text-decoration: underline">test</span></div>
|
||||
<!-- Red overline/text -->
|
||||
<div style="position: absolute; top: 22px; left: 22px; color: rgba(0, 0, 0, 0);">test<span style="text-decoration: overline; color: red;"><span style="color: rgba(0, 0, 0, 0);">forquirks</span></span></div>
|
||||
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: rgba(0, 0, 0, 0);">test</span><span style="color: red;">for</span></div>
|
||||
<!--
|
||||
There are some additional pixels appearing when two red texts are overlap. We
|
||||
use transparent color for the first one to prevent the situation and the failure
|
||||
of reftest.
|
||||
-->
|
||||
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: rgba(0, 0, 0, 0);">test</span><span style="color: transparent; text-decoration: red underline;">for</span></div>
|
||||
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: rgba(0, 0, 0, 0);">test</span><span style="color: red; text-decoration: overline;">for</span></div>
|
||||
<!-- Green text/underline -->
|
||||
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: rgba(0, 0, 0, 0);">testfor</span><span style="text-decoration: underline; color: green;"><span style="color: rgba(0, 0, 0, 0);">quirks</span></span></div>
|
||||
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: rgba(0, 0, 0, 0);">testfor</span><span style="color: green;">quirks</span></div>
|
||||
<!--
|
||||
There are some additional pixels appearing when two red texts are overlap. We
|
||||
use transparent color for the first one to prevent the situation and the failure
|
||||
of reftest.
|
||||
-->
|
||||
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: rgba(0, 0, 0, 0);">testfor</span><span style="text-decoration: green underline; color: transparent;">quirks</span></div>
|
||||
<div style="position: absolute; top: 22px; left: 22px;"><span style="color: rgba(0, 0, 0, 0);">testfor</span><span style="text-decoration: overline; color: green;">quirks</span></div>
|
||||
|
||||
<!-- "Real" text -->
|
||||
<!-- Blue underline/text -->
|
||||
|
|
|
@ -31,6 +31,7 @@ fuzzy-if(webrender,1,27) == compound-1a.html compound-1-ref.html
|
|||
== translate-1b.html translate-1-ref.html
|
||||
== translate-1c.html translate-1-ref.html
|
||||
== translate-1d.html translate-1-ref.html
|
||||
pref(gfx.webrender.layers-free,true) skip-if(!webrender) == translate-1d.html translate-1-ref.html
|
||||
== translate-1e.html translate-1-ref.html
|
||||
== translate-2a.html translate-2-ref.html
|
||||
== translate-3.html translate-3-ref.html
|
||||
|
@ -46,6 +47,7 @@ random == rotate-1f.html rotate-1-ref.html
|
|||
# rotate: 90deg rotations should be indistinguishable from objects constructed
|
||||
# to look the same.
|
||||
== rotate-2a.html rotate-2-ref.html
|
||||
pref(gfx.webrender.layers-free,true) skip-if(!webrender) == rotate-2a.html rotate-2-ref.html
|
||||
== rotate-2b.html rotate-2-ref.html
|
||||
# -transform-origin: We should NOT get the same images when using different
|
||||
# -transform-origins.
|
||||
|
|
|
@ -162,14 +162,14 @@ public class AnimatedProgressBar extends ThemedProgressBar {
|
|||
} else {
|
||||
canvas.getClipBounds(mRect);
|
||||
final float clipWidth = mRect.width() * mClipRatio;
|
||||
canvas.save();
|
||||
final int saveCount = canvas.save();
|
||||
if (mIsRtl) {
|
||||
canvas.clipRect(mRect.left, mRect.top, mRect.right - clipWidth, mRect.bottom);
|
||||
} else {
|
||||
canvas.clipRect(mRect.left + clipWidth, mRect.top, mRect.right, mRect.bottom);
|
||||
}
|
||||
super.onDraw(canvas);
|
||||
canvas.restore();
|
||||
canvas.restoreToCount(saveCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,8 +266,11 @@ public class AnimatedProgressBar extends ThemedProgressBar {
|
|||
mClosingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
|
||||
@Override
|
||||
public void onAnimationUpdate(ValueAnimator valueAnimator) {
|
||||
mClipRatio = (float) valueAnimator.getAnimatedValue();
|
||||
invalidate();
|
||||
final float ratio = (float) valueAnimator.getAnimatedValue();
|
||||
if (mClipRatio != ratio) {
|
||||
mClipRatio = ratio;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
});
|
||||
mClosingAnimator.addListener(new Animator.AnimatorListener() {
|
||||
|
|
|
@ -3587,7 +3587,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "webrender"
|
||||
version = "0.50.0"
|
||||
source = "git+https://github.com/servo/webrender#3d58efaecdc5897de057beab57adc031bc2e187e"
|
||||
source = "git+https://github.com/servo/webrender#cd1855550857b910f752359b48c5cc053419c358"
|
||||
dependencies = [
|
||||
"app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -3615,7 +3615,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "webrender_api"
|
||||
version = "0.50.0"
|
||||
source = "git+https://github.com/servo/webrender#3d58efaecdc5897de057beab57adc031bc2e187e"
|
||||
source = "git+https://github.com/servo/webrender#cd1855550857b910f752359b48c5cc053419c358"
|
||||
dependencies = [
|
||||
"app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -39,13 +39,22 @@ impl<K: Array> LRUCache<K> {
|
|||
#[inline]
|
||||
/// Touch a given entry, putting it first in the list.
|
||||
pub fn touch(&mut self, pos: usize) {
|
||||
let last_index = self.entries.len() - 1;
|
||||
if pos != last_index {
|
||||
if pos != 0 {
|
||||
let entry = self.entries.remove(pos).unwrap();
|
||||
self.entries.push_front(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the front entry in the list (most recently used).
|
||||
pub fn front(&self) -> Option<&K::Item> {
|
||||
self.entries.get(0)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the front entry in the list (most recently used).
|
||||
pub fn front_mut(&mut self) -> Option<&mut K::Item> {
|
||||
self.entries.get_mut(0)
|
||||
}
|
||||
|
||||
/// Iterate over the contents of this cache, from more to less recently
|
||||
/// used.
|
||||
pub fn iter(&self) -> arraydeque::Iter<K::Item> {
|
||||
|
|
|
@ -26,7 +26,7 @@ use selectors::matching::ElementSelectorFlags;
|
|||
use servo_arc::Arc;
|
||||
#[cfg(feature = "servo")] use servo_atoms::Atom;
|
||||
use shared_lock::StylesheetGuards;
|
||||
use sharing::StyleSharingCandidateCache;
|
||||
use sharing::StyleSharingCache;
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
#[cfg(feature = "servo")] use std::sync::Mutex;
|
||||
|
@ -679,7 +679,7 @@ impl StackLimitChecker {
|
|||
/// thread in order to be able to mutate it without locking.
|
||||
pub struct ThreadLocalStyleContext<E: TElement> {
|
||||
/// A cache to share style among siblings.
|
||||
pub style_sharing_candidate_cache: StyleSharingCandidateCache<E>,
|
||||
pub sharing_cache: StyleSharingCache<E>,
|
||||
/// The bloom filter used to fast-reject selector-matching.
|
||||
pub bloom_filter: StyleBloom<E>,
|
||||
/// A channel on which new animations that have been triggered by style
|
||||
|
@ -716,7 +716,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
|
|||
#[cfg(feature = "servo")]
|
||||
pub fn new(shared: &SharedStyleContext) -> Self {
|
||||
ThreadLocalStyleContext {
|
||||
style_sharing_candidate_cache: StyleSharingCandidateCache::new(),
|
||||
sharing_cache: StyleSharingCache::new(),
|
||||
bloom_filter: StyleBloom::new(),
|
||||
new_animations_sender: shared.local_context_creation_data.lock().unwrap().new_animations_sender.clone(),
|
||||
tasks: SequentialTaskList(Vec::new()),
|
||||
|
@ -733,7 +733,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
|
|||
/// Creates a new `ThreadLocalStyleContext` from a shared one.
|
||||
pub fn new(shared: &SharedStyleContext) -> Self {
|
||||
ThreadLocalStyleContext {
|
||||
style_sharing_candidate_cache: StyleSharingCandidateCache::new(),
|
||||
sharing_cache: StyleSharingCache::new(),
|
||||
bloom_filter: StyleBloom::new(),
|
||||
tasks: SequentialTaskList(Vec::new()),
|
||||
selector_flags: SelectorFlagsMap::new(),
|
||||
|
|
|
@ -68,7 +68,7 @@ use Atom;
|
|||
use applicable_declarations::ApplicableDeclarationBlock;
|
||||
use atomic_refcell::{AtomicRefCell, AtomicRefMut};
|
||||
use bloom::StyleBloom;
|
||||
use cache::{LRUCache, LRUCacheMutIterator};
|
||||
use cache::LRUCache;
|
||||
use context::{SelectorFlagsMap, SharedStyleContext, StyleContext};
|
||||
use data::ElementStyles;
|
||||
use dom::{TElement, SendElement};
|
||||
|
@ -342,8 +342,8 @@ impl<E: TElement> StyleSharingTarget<E> {
|
|||
pub fn share_style_if_possible(
|
||||
&mut self,
|
||||
context: &mut StyleContext<E>,
|
||||
) -> StyleSharingResult {
|
||||
let cache = &mut context.thread_local.style_sharing_candidate_cache;
|
||||
) -> Option<ElementStyles> {
|
||||
let cache = &mut context.thread_local.sharing_cache;
|
||||
let shared_context = &context.shared;
|
||||
let selector_flags_map = &mut context.thread_local.selector_flags;
|
||||
let bloom_filter = &context.thread_local.bloom_filter;
|
||||
|
@ -351,7 +351,7 @@ impl<E: TElement> StyleSharingTarget<E> {
|
|||
if cache.dom_depth != bloom_filter.matching_depth() {
|
||||
debug!("Can't share style, because DOM depth changed from {:?} to {:?}, element: {:?}",
|
||||
cache.dom_depth, bloom_filter.matching_depth(), self.element);
|
||||
return StyleSharingResult::CannotShare;
|
||||
return None;
|
||||
}
|
||||
debug_assert_eq!(bloom_filter.current_parent(),
|
||||
self.element.traversal_parent());
|
||||
|
@ -370,45 +370,58 @@ impl<E: TElement> StyleSharingTarget<E> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A cache miss result.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CacheMiss {
|
||||
/// The parents don't match.
|
||||
Parent,
|
||||
/// One element was NAC, while the other wasn't.
|
||||
NativeAnonymousContent,
|
||||
/// The local name of the element and the candidate don't match.
|
||||
LocalName,
|
||||
/// The namespace of the element and the candidate don't match.
|
||||
Namespace,
|
||||
/// One of the element or the candidate was a link, but the other one
|
||||
/// wasn't.
|
||||
Link,
|
||||
/// The element and the candidate match different kind of rules. This can
|
||||
/// only happen in Gecko.
|
||||
UserAndAuthorRules,
|
||||
/// The element and the candidate are in a different state.
|
||||
State,
|
||||
/// The element had an id attribute, which qualifies for a unique style.
|
||||
IdAttr,
|
||||
/// The element had a style attribute, which qualifies for a unique style.
|
||||
StyleAttr,
|
||||
/// The element and the candidate class names didn't match.
|
||||
Class,
|
||||
/// The presentation hints didn't match.
|
||||
PresHints,
|
||||
/// The element and the candidate didn't match the same set of revalidation
|
||||
/// selectors.
|
||||
Revalidation,
|
||||
struct SharingCacheBase<Candidate> {
|
||||
entries: LRUCache<[Candidate; SHARING_CACHE_BACKING_STORE_SIZE]>,
|
||||
}
|
||||
|
||||
/// The results of attempting to share a style.
|
||||
pub enum StyleSharingResult {
|
||||
/// We didn't find anybody to share the style with.
|
||||
CannotShare,
|
||||
/// The node's style can be shared. The integer specifies the index in the
|
||||
/// LRU cache that was hit and the damage that was done.
|
||||
StyleWasShared(usize, ElementStyles),
|
||||
impl<Candidate> Default for SharingCacheBase<Candidate> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
entries: LRUCache::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Candidate> SharingCacheBase<Candidate> {
|
||||
fn clear(&mut self) {
|
||||
self.entries.evict_all();
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.entries.num_entries() == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: TElement> SharingCache<E> {
|
||||
fn insert(&mut self, el: E, validation_data_holder: &mut StyleSharingTarget<E>) {
|
||||
self.entries.insert(StyleSharingCandidate {
|
||||
element: el,
|
||||
validation_data: validation_data_holder.take_validation_data(),
|
||||
});
|
||||
}
|
||||
|
||||
fn lookup<F>(&mut self, mut is_match: F) -> Option<ElementStyles>
|
||||
where
|
||||
F: FnMut(&mut StyleSharingCandidate<E>) -> bool
|
||||
{
|
||||
let mut index = None;
|
||||
for (i, candidate) in self.entries.iter_mut().enumerate() {
|
||||
if is_match(candidate) {
|
||||
index = Some(i);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
match index {
|
||||
None => None,
|
||||
Some(i) => {
|
||||
self.entries.touch(i);
|
||||
let front = self.entries.front_mut().unwrap();
|
||||
debug_assert!(is_match(front));
|
||||
Some(front.element.borrow_data().unwrap().styles.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Style sharing caches are are large allocations, so we store them in thread-local
|
||||
|
@ -423,20 +436,19 @@ pub enum StyleSharingResult {
|
|||
///
|
||||
/// [1] https://github.com/rust-lang/rust/issues/42763
|
||||
/// [2] https://github.com/rust-lang/rust/issues/13707
|
||||
type SharingCacheBase<Candidate> = LRUCache<[Candidate; SHARING_CACHE_BACKING_STORE_SIZE]>;
|
||||
type SharingCache<E> = SharingCacheBase<StyleSharingCandidate<E>>;
|
||||
type TypelessSharingCache = SharingCacheBase<FakeCandidate>;
|
||||
type StoredSharingCache = Arc<AtomicRefCell<TypelessSharingCache>>;
|
||||
|
||||
thread_local!(static SHARING_CACHE_KEY: StoredSharingCache =
|
||||
Arc::new(AtomicRefCell::new(LRUCache::new())));
|
||||
Arc::new(AtomicRefCell::new(TypelessSharingCache::default())));
|
||||
|
||||
/// An LRU cache of the last few nodes seen, so that we can aggressively try to
|
||||
/// reuse their styles.
|
||||
///
|
||||
/// Note that this cache is flushed every time we steal work from the queue, so
|
||||
/// storing nodes here temporarily is safe.
|
||||
pub struct StyleSharingCandidateCache<E: TElement> {
|
||||
pub struct StyleSharingCache<E: TElement> {
|
||||
/// The LRU cache, with the type cast away to allow persisting the allocation.
|
||||
cache_typeless: OwningHandle<StoredSharingCache, AtomicRefMut<'static, TypelessSharingCache>>,
|
||||
/// Bind this structure to the lifetime of E, since that's what we effectively store.
|
||||
|
@ -447,13 +459,14 @@ pub struct StyleSharingCandidateCache<E: TElement> {
|
|||
dom_depth: usize,
|
||||
}
|
||||
|
||||
impl<E: TElement> Drop for StyleSharingCandidateCache<E> {
|
||||
impl<E: TElement> Drop for StyleSharingCache<E> {
|
||||
fn drop(&mut self) {
|
||||
self.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: TElement> StyleSharingCandidateCache<E> {
|
||||
impl<E: TElement> StyleSharingCache<E> {
|
||||
#[allow(dead_code)]
|
||||
fn cache(&self) -> &SharingCache<E> {
|
||||
let base: &TypelessSharingCache = &*self.cache_typeless;
|
||||
unsafe { mem::transmute(base) }
|
||||
|
@ -470,31 +483,25 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
|||
assert_eq!(mem::align_of::<SharingCache<E>>(), mem::align_of::<TypelessSharingCache>());
|
||||
let cache_arc = SHARING_CACHE_KEY.with(|c| c.clone());
|
||||
let cache = OwningHandle::new_with_fn(cache_arc, |x| unsafe { x.as_ref() }.unwrap().borrow_mut());
|
||||
debug_assert_eq!(cache.num_entries(), 0);
|
||||
debug_assert!(cache.is_empty());
|
||||
|
||||
StyleSharingCandidateCache {
|
||||
StyleSharingCache {
|
||||
cache_typeless: cache,
|
||||
marker: PhantomData,
|
||||
dom_depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of entries in the cache.
|
||||
pub fn num_entries(&self) -> usize {
|
||||
self.cache().num_entries()
|
||||
}
|
||||
|
||||
fn iter_mut(&mut self) -> LRUCacheMutIterator<StyleSharingCandidate<E>> {
|
||||
self.cache_mut().iter_mut()
|
||||
}
|
||||
|
||||
/// Tries to insert an element in the style sharing cache.
|
||||
///
|
||||
/// Fails if we know it should never be in the cache.
|
||||
///
|
||||
/// NB: We pass a source for the validation data, rather than the data itself,
|
||||
/// to avoid memmoving at each function call. See rust issue #42763.
|
||||
pub fn insert_if_possible(&mut self,
|
||||
element: &E,
|
||||
style: &ComputedValues,
|
||||
validation_data: ValidationData,
|
||||
validation_data_holder: &mut StyleSharingTarget<E>,
|
||||
dom_depth: usize) {
|
||||
let parent = match element.traversal_parent() {
|
||||
Some(element) => element,
|
||||
|
@ -547,20 +554,12 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
|||
self.clear();
|
||||
self.dom_depth = dom_depth;
|
||||
}
|
||||
self.cache_mut().insert(StyleSharingCandidate {
|
||||
element: *element,
|
||||
validation_data: validation_data,
|
||||
});
|
||||
}
|
||||
|
||||
/// Touch a given index in the style sharing candidate cache.
|
||||
pub fn touch(&mut self, index: usize) {
|
||||
self.cache_mut().touch(index);
|
||||
self.cache_mut().insert(*element, validation_data_holder);
|
||||
}
|
||||
|
||||
/// Clear the style sharing candidate cache.
|
||||
pub fn clear(&mut self) {
|
||||
self.cache_mut().evict_all()
|
||||
self.cache_mut().clear();
|
||||
}
|
||||
|
||||
/// Attempts to share a style with another node.
|
||||
|
@ -570,48 +569,33 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
|||
selector_flags_map: &mut SelectorFlagsMap<E>,
|
||||
bloom_filter: &StyleBloom<E>,
|
||||
target: &mut StyleSharingTarget<E>,
|
||||
) -> StyleSharingResult {
|
||||
) -> Option<ElementStyles> {
|
||||
if shared_context.options.disable_style_sharing_cache {
|
||||
debug!("{:?} Cannot share style: style sharing cache disabled",
|
||||
target.element);
|
||||
return StyleSharingResult::CannotShare
|
||||
return None;
|
||||
}
|
||||
|
||||
if target.traversal_parent().is_none() {
|
||||
debug!("{:?} Cannot share style: element has no parent",
|
||||
target.element);
|
||||
return StyleSharingResult::CannotShare
|
||||
return None;
|
||||
}
|
||||
|
||||
if target.is_native_anonymous() {
|
||||
debug!("{:?} Cannot share style: NAC", target.element);
|
||||
return StyleSharingResult::CannotShare;
|
||||
return None;
|
||||
}
|
||||
|
||||
for (i, candidate) in self.iter_mut().enumerate() {
|
||||
let sharing_result =
|
||||
Self::test_candidate(
|
||||
target,
|
||||
candidate,
|
||||
&shared_context,
|
||||
bloom_filter,
|
||||
selector_flags_map
|
||||
);
|
||||
|
||||
match sharing_result {
|
||||
Ok(shared_styles) => {
|
||||
return StyleSharingResult::StyleWasShared(i, shared_styles)
|
||||
}
|
||||
Err(miss) => {
|
||||
debug!("Cache miss: {:?}", miss);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("{:?} Cannot share style: {} cache entries", target.element,
|
||||
self.cache().num_entries());
|
||||
|
||||
StyleSharingResult::CannotShare
|
||||
self.cache_mut().lookup(|candidate| {
|
||||
Self::test_candidate(
|
||||
target,
|
||||
candidate,
|
||||
&shared_context,
|
||||
bloom_filter,
|
||||
selector_flags_map
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn test_candidate(
|
||||
|
@ -620,13 +604,7 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
|||
shared: &SharedStyleContext,
|
||||
bloom: &StyleBloom<E>,
|
||||
selector_flags_map: &mut SelectorFlagsMap<E>
|
||||
) -> Result<ElementStyles, CacheMiss> {
|
||||
macro_rules! miss {
|
||||
($miss: ident) => {
|
||||
return Err(CacheMiss::$miss);
|
||||
}
|
||||
}
|
||||
|
||||
) -> bool {
|
||||
// Check that we have the same parent, or at least that the parents
|
||||
// share styles and permit sharing across their children. The latter
|
||||
// check allows us to share style between cousins if the parents
|
||||
|
@ -635,37 +613,44 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
|||
let candidate_parent = candidate.element.traversal_parent();
|
||||
if parent != candidate_parent &&
|
||||
!checks::can_share_style_across_parents(parent, candidate_parent) {
|
||||
miss!(Parent)
|
||||
trace!("Miss: Parent");
|
||||
return false;
|
||||
}
|
||||
|
||||
if target.is_native_anonymous() {
|
||||
debug_assert!(!candidate.element.is_native_anonymous(),
|
||||
"Why inserting NAC into the cache?");
|
||||
miss!(NativeAnonymousContent)
|
||||
trace!("Miss: Native Anonymous Content");
|
||||
return false;
|
||||
}
|
||||
|
||||
if *target.get_local_name() != *candidate.element.get_local_name() {
|
||||
miss!(LocalName)
|
||||
trace!("Miss: Local Name");
|
||||
return false;
|
||||
}
|
||||
|
||||
if *target.get_namespace() != *candidate.element.get_namespace() {
|
||||
miss!(Namespace)
|
||||
trace!("Miss: Namespace");
|
||||
return false;
|
||||
}
|
||||
|
||||
if target.is_link() != candidate.element.is_link() {
|
||||
miss!(Link)
|
||||
trace!("Miss: Link");
|
||||
return false;
|
||||
}
|
||||
|
||||
if target.matches_user_and_author_rules() !=
|
||||
candidate.element.matches_user_and_author_rules() {
|
||||
miss!(UserAndAuthorRules)
|
||||
trace!("Miss: User and Author Rules");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We do not ignore visited state here, because Gecko
|
||||
// needs to store extra bits on visited style contexts,
|
||||
// so these contexts cannot be shared
|
||||
if target.element.get_state() != candidate.get_state() {
|
||||
miss!(State)
|
||||
trace!("Miss: User and Author State");
|
||||
return false;
|
||||
}
|
||||
|
||||
let element_id = target.element.get_id();
|
||||
|
@ -674,32 +659,38 @@ impl<E: TElement> StyleSharingCandidateCache<E> {
|
|||
// It's possible that there are no styles for either id.
|
||||
if checks::may_have_rules_for_ids(shared, element_id.as_ref(),
|
||||
candidate_id.as_ref()) {
|
||||
miss!(IdAttr)
|
||||
trace!("Miss: ID Attr");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if !checks::have_same_style_attribute(target, candidate) {
|
||||
miss!(StyleAttr)
|
||||
trace!("Miss: Style Attr");
|
||||
return false;
|
||||
}
|
||||
|
||||
if !checks::have_same_class(target, candidate) {
|
||||
miss!(Class)
|
||||
trace!("Miss: Class");
|
||||
return false;
|
||||
}
|
||||
|
||||
if !checks::have_same_presentational_hints(target, candidate) {
|
||||
miss!(PresHints)
|
||||
trace!("Miss: Pres Hints");
|
||||
return false;
|
||||
}
|
||||
|
||||
if !checks::revalidate(target, candidate, shared, bloom,
|
||||
selector_flags_map) {
|
||||
miss!(Revalidation)
|
||||
trace!("Miss: Revalidation");
|
||||
return false;
|
||||
}
|
||||
|
||||
let data = candidate.element.borrow_data().unwrap();
|
||||
debug_assert!(target.has_current_styles_for_traversal(&data, shared.traversal_flags));
|
||||
debug_assert!(target.has_current_styles_for_traversal(
|
||||
&candidate.element.borrow_data().unwrap(),
|
||||
shared.traversal_flags)
|
||||
);
|
||||
debug!("Sharing allowed between {:?} and {:?}", target.element, candidate.element);
|
||||
|
||||
debug!("Sharing style between {:?} and {:?}",
|
||||
target.element, candidate.element);
|
||||
Ok(data.styles.clone())
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -624,7 +624,6 @@ where
|
|||
E: TElement,
|
||||
{
|
||||
use data::RestyleKind::*;
|
||||
use sharing::StyleSharingResult::*;
|
||||
|
||||
context.thread_local.statistics.elements_styled += 1;
|
||||
let kind = data.restyle_kind(context.shared);
|
||||
|
@ -656,12 +655,11 @@ where
|
|||
// Now that our bloom filter is set up, try the style sharing
|
||||
// cache.
|
||||
match target.share_style_if_possible(context) {
|
||||
StyleWasShared(index, styles) => {
|
||||
Some(styles) => {
|
||||
context.thread_local.statistics.styles_shared += 1;
|
||||
context.thread_local.style_sharing_candidate_cache.touch(index);
|
||||
styles
|
||||
}
|
||||
CannotShare => {
|
||||
None => {
|
||||
context.thread_local.statistics.elements_matched += 1;
|
||||
// Perform the matching and cascading.
|
||||
let new_styles = {
|
||||
|
@ -677,11 +675,11 @@ where
|
|||
};
|
||||
|
||||
context.thread_local
|
||||
.style_sharing_candidate_cache
|
||||
.sharing_cache
|
||||
.insert_if_possible(
|
||||
&element,
|
||||
new_styles.primary(),
|
||||
target.take_validation_data(),
|
||||
&mut target,
|
||||
context.thread_local.bloom_filter.matching_depth(),
|
||||
);
|
||||
|
||||
|
|
|
@ -31,6 +31,9 @@ fn size_of_selectors_dummy_types() {
|
|||
// selectors (with the inline hashes) with as few cache misses as possible.
|
||||
size_of_test!(test_size_of_rule, style::stylist::Rule, 32);
|
||||
|
||||
// Large pages generate tens of thousands of ComputedValues.
|
||||
size_of_test!(test_size_of_cv, ComputedValues, 272);
|
||||
|
||||
size_of_test!(test_size_of_option_arc_cv, Option<Arc<ComputedValues>>, 8);
|
||||
size_of_test!(test_size_of_option_rule_node, Option<StrongRuleNode>, 8);
|
||||
|
||||
|
|
|
@ -11767,7 +11767,7 @@
|
|||
},
|
||||
"READER_MODE_PARSE_RESULT" : {
|
||||
"record_in_processes": ["main", "content"],
|
||||
"expires_in_version": "58",
|
||||
"expires_in_version": "never",
|
||||
"alert_emails": ["firefox-dev@mozilla.org", "gijs@mozilla.com"],
|
||||
"kind": "enumerated",
|
||||
"n_values": 5,
|
||||
|
@ -11775,7 +11775,7 @@
|
|||
},
|
||||
"READER_MODE_DOWNLOAD_RESULT" : {
|
||||
"record_in_processes": ["main", "content"],
|
||||
"expires_in_version": "58",
|
||||
"expires_in_version": "never",
|
||||
"alert_emails": ["firefox-dev@mozilla.org", "gijs@mozilla.com"],
|
||||
"kind": "enumerated",
|
||||
"n_values": 5,
|
||||
|
|
|
@ -54,6 +54,14 @@ MOZ_ReportCrash(const char* aStr, const char* aFilename, int aLine)
|
|||
|
||||
#define NS_RUNTIMEABORT(msg) __coverity_panic__()
|
||||
|
||||
// Kills Structurally dead code (UNREACHABLE)
|
||||
#define NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(_class) \
|
||||
NS_IMETHODIMP_(bool) \
|
||||
NS_CYCLE_COLLECTION_CLASSNAME(_class)::CanSkipThisReal(void* p) \
|
||||
{ \
|
||||
__coverity_panic__(); \
|
||||
_class* tmp = DowncastCCParticipant<_class>(p);
|
||||
|
||||
int
|
||||
GET_JUMP_OFFSET(jsbytecode* pc)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче