diff --git a/dom/animation/test/mochitest.ini b/dom/animation/test/mochitest.ini index 35c9b45963..78dcfa57a4 100644 --- a/dom/animation/test/mochitest.ini +++ b/dom/animation/test/mochitest.ini @@ -64,3 +64,6 @@ support-files = document-timeline/file_document-timeline.html skip-if = buildapp == 'mulet' [mozilla/test_deferred_start.html] support-files = mozilla/file_deferred_start.html +skip-if = (toolkit == 'gonk' && debug) +[mozilla/test_hide_and_show.html] +support-files = mozilla/file_hide_and_show.html diff --git a/dom/animation/test/mozilla/file_hide_and_show.html b/dom/animation/test/mozilla/file_hide_and_show.html new file mode 100644 index 0000000000..e5025bb267 --- /dev/null +++ b/dom/animation/test/mozilla/file_hide_and_show.html @@ -0,0 +1,75 @@ + + + + + + + diff --git a/dom/animation/test/mozilla/test_hide_and_show.html b/dom/animation/test/mozilla/test_hide_and_show.html new file mode 100644 index 0000000000..929a31bd47 --- /dev/null +++ b/dom/animation/test/mozilla/test_hide_and_show.html @@ -0,0 +1,14 @@ + + + + +
+ diff --git a/dom/base/Element.h b/dom/base/Element.h index bfe8003e41..47c3adce12 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -89,18 +89,27 @@ enum { // change will attempt to restyle descendants). ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT = ELEMENT_FLAG_BIT(3), - // All of those bits together, for convenience. - ELEMENT_ALL_RESTYLE_FLAGS = ELEMENT_HAS_PENDING_RESTYLE | - ELEMENT_IS_POTENTIAL_RESTYLE_ROOT | - ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE | - ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT, + // Set if this element has a pending restyle with an eRestyle_SomeDescendants + // restyle hint. + ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR = ELEMENT_FLAG_BIT(4), // Just the HAS_PENDING bits, for convenience - ELEMENT_PENDING_RESTYLE_FLAGS = ELEMENT_HAS_PENDING_RESTYLE | - ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE, + ELEMENT_PENDING_RESTYLE_FLAGS = + ELEMENT_HAS_PENDING_RESTYLE | + ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE, + + // Just the IS_POTENTIAL bits, for convenience + ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS = + ELEMENT_IS_POTENTIAL_RESTYLE_ROOT | + ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT, + + // All of the restyle bits together, for convenience. + ELEMENT_ALL_RESTYLE_FLAGS = ELEMENT_PENDING_RESTYLE_FLAGS | + ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS | + ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR, // Remaining bits are for subclasses - ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 4 + ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 5 }; #undef ELEMENT_FLAG_BIT diff --git a/dom/base/nsIStyleSheetLinkingElement.h b/dom/base/nsIStyleSheetLinkingElement.h index 948cdf9c45..77eaebf609 100644 --- a/dom/base/nsIStyleSheetLinkingElement.h +++ b/dom/base/nsIStyleSheetLinkingElement.h @@ -13,8 +13,8 @@ class nsICSSLoaderObserver; class nsIURI; #define NS_ISTYLESHEETLINKINGELEMENT_IID \ -{ 0xe5855604, 0x8a9a, 0x4181, \ - { 0xbe, 0x41, 0xdd, 0xf7, 0x08, 0x70, 0x3f, 0xbe } } +{ 0xa8b79f3b, 0x9d18, 0x4f9c, \ + { 0xb1, 0xaa, 0x8c, 0x9b, 0x1b, 0xaa, 0xac, 0xad } } namespace mozilla { class CSSStyleSheet; @@ -97,6 +97,14 @@ public: // some types of linking elements, but it's a better place than // anywhere else. virtual void SetLineNumber(uint32_t aLineNumber) = 0; + + /** + * Get the line number, as previously set by SetLineNumber. + * + * @return the line number of this element; or 1 if no line number + * was set + */ + virtual uint32_t GetLineNumber() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIStyleSheetLinkingElement, diff --git a/dom/base/nsStyleLinkElement.cpp b/dom/base/nsStyleLinkElement.cpp index c9b9b6e6ff..9fc8465436 100644 --- a/dom/base/nsStyleLinkElement.cpp +++ b/dom/base/nsStyleLinkElement.cpp @@ -129,6 +129,12 @@ nsStyleLinkElement::SetLineNumber(uint32_t aLineNumber) mLineNumber = aLineNumber; } +/* virtual */ uint32_t +nsStyleLinkElement::GetLineNumber() +{ + return mLineNumber; +} + /* static */ bool nsStyleLinkElement::IsImportEnabled() { diff --git a/dom/base/nsStyleLinkElement.h b/dom/base/nsStyleLinkElement.h index 9cd5f71d90..1d7dd5d374 100644 --- a/dom/base/nsStyleLinkElement.h +++ b/dom/base/nsStyleLinkElement.h @@ -52,6 +52,7 @@ public: virtual void OverrideBaseURI(nsIURI* aNewBaseURI) override; virtual void SetLineNumber(uint32_t aLineNumber) override; + virtual uint32_t GetLineNumber() override; enum RelValue { ePREFETCH = 0x00000001, diff --git a/dom/fetch/Fetch.cpp b/dom/fetch/Fetch.cpp index f76cd65519..abc08e6776 100644 --- a/dom/fetch/Fetch.cpp +++ b/dom/fetch/Fetch.cpp @@ -27,6 +27,7 @@ #include "mozilla/dom/File.h" #include "mozilla/dom/Headers.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/PromiseWorkerProxy.h" #include "mozilla/dom/Request.h" #include "mozilla/dom/Response.h" #include "mozilla/dom/ScriptSettings.h" @@ -47,28 +48,27 @@ namespace dom { using namespace workers; -class WorkerFetchResolver final : public FetchDriverObserver, - public WorkerFeature +class WorkerFetchResolver final : public FetchDriverObserver { friend class MainThreadFetchRunnable; friend class WorkerFetchResponseEndRunnable; friend class WorkerFetchResponseRunnable; - workers::WorkerPrivate* mWorkerPrivate; - - Mutex mCleanUpLock; - bool mCleanedUp; - // The following are initialized and used exclusively on the worker thread. - nsRefPtr mFetchPromise; - nsRefPtr mResponse; + nsRefPtr mPromiseProxy; public: - - WorkerFetchResolver(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise) - : mWorkerPrivate(aWorkerPrivate) - , mCleanUpLock("WorkerFetchResolver") - , mCleanedUp(false) - , mFetchPromise(aPromise) + // Returns null if worker is shutting down. + static already_AddRefed + Create(workers::WorkerPrivate* aWorkerPrivate, Promise* aPromise) { + MOZ_ASSERT(aWorkerPrivate); + aWorkerPrivate->AssertIsOnWorkerThread(); + nsRefPtr proxy = PromiseWorkerProxy::Create(aWorkerPrivate, aPromise); + if (!proxy) { + return nullptr; + } + + nsRefPtr r = new WorkerFetchResolver(proxy); + return r.forget(); } void @@ -77,58 +77,16 @@ public: void OnResponseEnd() override; - bool - Notify(JSContext* aCx, Status aStatus) override - { - if (aStatus > Running) { - CleanUp(aCx); - } - return true; - } - - void - CleanUp(JSContext* aCx) - { - MutexAutoLock lock(mCleanUpLock); - - if (mCleanedUp) { - return; - } - - MOZ_ASSERT(mWorkerPrivate); - mWorkerPrivate->AssertIsOnWorkerThread(); - MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx); - - mWorkerPrivate->RemoveFeature(aCx, this); - CleanUpUnchecked(); - } - - void - CleanUpUnchecked() - { - mResponse = nullptr; - if (mFetchPromise) { - mFetchPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); - mFetchPromise = nullptr; - } - mCleanedUp = true; - } - - workers::WorkerPrivate* - GetWorkerPrivate() const - { - // It's ok to race on |mCleanedUp|, because it will never cause us to fire - // the assertion when we should not. - MOZ_ASSERT(!mCleanedUp); - return mWorkerPrivate; - } - private: - ~WorkerFetchResolver() + explicit WorkerFetchResolver(PromiseWorkerProxy* aProxy) + : mPromiseProxy(aProxy) { - MOZ_ASSERT(mCleanedUp); - MOZ_ASSERT(!mFetchPromise); + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(mPromiseProxy); } + + ~WorkerFetchResolver() + {} }; class MainThreadFetchResolver final : public FetchDriverObserver @@ -153,34 +111,31 @@ class MainThreadFetchRunnable : public nsRunnable nsRefPtr mRequest; public: - MainThreadFetchRunnable(WorkerPrivate* aWorkerPrivate, - Promise* aPromise, + MainThreadFetchRunnable(WorkerFetchResolver* aResolver, InternalRequest* aRequest) - : mResolver(new WorkerFetchResolver(aWorkerPrivate, aPromise)) + : mResolver(aResolver) , mRequest(aRequest) { - MOZ_ASSERT(aWorkerPrivate); - aWorkerPrivate->AssertIsOnWorkerThread(); - if (!aWorkerPrivate->AddFeature(aWorkerPrivate->GetJSContext(), mResolver)) { - NS_WARNING("Could not add WorkerFetchResolver feature to worker"); - mResolver->CleanUpUnchecked(); - mResolver = nullptr; - } + MOZ_ASSERT(mResolver); } NS_IMETHODIMP Run() { AssertIsOnMainThread(); - // AddFeature() call failed, don't bother running. - if (!mResolver) { + nsRefPtr proxy = mResolver->mPromiseProxy; + MutexAutoLock lock(proxy->Lock()); + if (proxy->CleanedUp()) { + NS_WARNING("Aborting Fetch because worker already shut down"); return NS_OK; } - nsCOMPtr principal = mResolver->GetWorkerPrivate()->GetPrincipal(); - nsCOMPtr loadGroup = mResolver->GetWorkerPrivate()->GetLoadGroup(); + nsCOMPtr principal = proxy->GetWorkerPrivate()->GetPrincipal(); + MOZ_ASSERT(principal); + nsCOMPtr loadGroup = proxy->GetWorkerPrivate()->GetLoadGroup(); + MOZ_ASSERT(loadGroup); nsRefPtr fetch = new FetchDriver(mRequest, principal, loadGroup); - nsIDocument* doc = mResolver->GetWorkerPrivate()->GetDocument(); + nsIDocument* doc = proxy->GetWorkerPrivate()->GetDocument(); if (doc) { fetch->SetDocument(doc); } @@ -262,10 +217,15 @@ FetchRequest(nsIGlobalObject* aGlobal, const RequestOrUSVString& aInput, r->SetSkipServiceWorker(); } - nsRefPtr run = new MainThreadFetchRunnable(worker, p, r); - if (NS_FAILED(NS_DispatchToMainThread(run))) { - NS_WARNING("MainThreadFetchRunnable dispatch failed!"); + nsRefPtr resolver = WorkerFetchResolver::Create(worker, p); + if (!resolver) { + NS_WARNING("Could not add WorkerFetchResolver feature to worker"); + aRv.Throw(NS_ERROR_DOM_ABORT_ERR); + return nullptr; } + + nsRefPtr run = new MainThreadFetchRunnable(resolver, r); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(run))); } return p.forget(); @@ -304,8 +264,10 @@ class WorkerFetchResponseRunnable final : public WorkerRunnable // Passed from main thread to worker thread after being initialized. nsRefPtr mInternalResponse; public: - WorkerFetchResponseRunnable(WorkerFetchResolver* aResolver, InternalResponse* aResponse) - : WorkerRunnable(aResolver->GetWorkerPrivate(), WorkerThreadModifyBusyCount) + WorkerFetchResponseRunnable(WorkerPrivate* aWorkerPrivate, + WorkerFetchResolver* aResolver, + InternalResponse* aResponse) + : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) , mResolver(aResolver) , mInternalResponse(aResponse) { @@ -316,15 +278,13 @@ public: { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate()); - nsRefPtr promise = mResolver->mFetchPromise.forget(); + nsRefPtr promise = mResolver->mPromiseProxy->WorkerPromise(); if (mInternalResponse->Type() != ResponseType::Error) { nsRefPtr global = aWorkerPrivate->GlobalScope(); - mResolver->mResponse = new Response(global, mInternalResponse); - - promise->MaybeResolve(mResolver->mResponse); + nsRefPtr response = new Response(global, mInternalResponse); + promise->MaybeResolve(response); } else { ErrorResult result; result.ThrowTypeError(MSG_FETCH_FAILED); @@ -338,8 +298,9 @@ class WorkerFetchResponseEndRunnable final : public WorkerRunnable { nsRefPtr mResolver; public: - explicit WorkerFetchResponseEndRunnable(WorkerFetchResolver* aResolver) - : WorkerRunnable(aResolver->GetWorkerPrivate(), WorkerThreadModifyBusyCount) + WorkerFetchResponseEndRunnable(WorkerPrivate* aWorkerPrivate, + WorkerFetchResolver* aResolver) + : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) , mResolver(aResolver) { } @@ -349,9 +310,8 @@ public: { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - MOZ_ASSERT(aWorkerPrivate == mResolver->GetWorkerPrivate()); - mResolver->CleanUp(aCx); + mResolver->mPromiseProxy->CleanUp(aCx); return true; } }; @@ -361,17 +321,19 @@ WorkerFetchResolver::OnResponseAvailableInternal(InternalResponse* aResponse) { AssertIsOnMainThread(); - MutexAutoLock lock(mCleanUpLock); - if (mCleanedUp) { + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { return; } nsRefPtr r = - new WorkerFetchResponseRunnable(this, aResponse); + new WorkerFetchResponseRunnable(mPromiseProxy->GetWorkerPrivate(), this, + aResponse); - AutoSafeJSContext cx; - if (!r->Dispatch(cx)) { - NS_WARNING("Could not dispatch fetch resolve"); + AutoJSAPI jsapi; + jsapi.Init(); + if (!r->Dispatch(jsapi.cx())) { + NS_WARNING("Could not dispatch fetch response"); } } @@ -379,17 +341,18 @@ void WorkerFetchResolver::OnResponseEnd() { AssertIsOnMainThread(); - MutexAutoLock lock(mCleanUpLock); - if (mCleanedUp) { + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { return; } nsRefPtr r = - new WorkerFetchResponseEndRunnable(this); + new WorkerFetchResponseEndRunnable(mPromiseProxy->GetWorkerPrivate(), this); - AutoSafeJSContext cx; - if (!r->Dispatch(cx)) { - NS_WARNING("Could not dispatch fetch resolve end"); + AutoJSAPI jsapi; + jsapi.Init(); + if (!r->Dispatch(jsapi.cx())) { + NS_WARNING("Could not dispatch fetch response end"); } } diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 6b29d6feec..aa410ba1c2 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -1712,7 +1712,7 @@ public: void WorkerRunInternal(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { - nsRefPtr workerPromise = mPromiseProxy->GetWorkerPromise(); + nsRefPtr workerPromise = mPromiseProxy->WorkerPromise(); ErrorResult result; nsAutoTArray, 5> notifications; @@ -1761,27 +1761,19 @@ public: { AssertIsOnMainThread(); MOZ_ASSERT(mPromiseProxy, "Was Done() called twice?"); - MutexAutoLock lock(mPromiseProxy->GetCleanUpLock()); - if (mPromiseProxy->IsClean()) { + + nsRefPtr proxy = mPromiseProxy.forget(); + MutexAutoLock lock(proxy->Lock()); + if (proxy->CleanedUp()) { return NS_OK; } - MOZ_ASSERT(mPromiseProxy->GetWorkerPrivate()); nsRefPtr r = - new WorkerGetResultRunnable(mPromiseProxy->GetWorkerPrivate(), - mPromiseProxy, + new WorkerGetResultRunnable(proxy->GetWorkerPrivate(), + proxy, Move(mStrings)); - if (!r->Dispatch(aCx)) { - nsRefPtr cr = - new PromiseWorkerProxyControlRunnable(mPromiseProxy->GetWorkerPrivate(), - mPromiseProxy); - - DebugOnly ok = cr->Dispatch(aCx); - MOZ_ASSERT(ok); - } - - mPromiseProxy = nullptr; + r->Dispatch(aCx); return NS_OK; } @@ -1798,31 +1790,18 @@ class WorkerGetRunnable final : public nsRunnable const nsString mTag; const nsString mScope; public: - WorkerGetRunnable(WorkerPrivate* aWorkerPrivate, - Promise* aWorkerPromise, + WorkerGetRunnable(PromiseWorkerProxy* aProxy, const nsAString& aTag, const nsAString& aScope) - : mTag(aTag), mScope(aScope) + : mPromiseProxy(aProxy), mTag(aTag), mScope(aScope) { - aWorkerPrivate->AssertIsOnWorkerThread(); - mPromiseProxy = - PromiseWorkerProxy::Create(aWorkerPrivate, - aWorkerPromise); - - if (!mPromiseProxy || !mPromiseProxy->GetWorkerPromise()) { - aWorkerPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); - mPromiseProxy = nullptr; - } + MOZ_ASSERT(mPromiseProxy); } NS_IMETHOD Run() override { AssertIsOnMainThread(); - if (!mPromiseProxy) { - return NS_OK; - } - nsCOMPtr callback = new WorkerGetCallback(mPromiseProxy, mScope); @@ -1837,8 +1816,8 @@ public: return rv; } - MutexAutoLock lock(mPromiseProxy->GetCleanUpLock()); - if (mPromiseProxy->IsClean()) { + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { return NS_OK; } @@ -1877,13 +1856,18 @@ Notification::WorkerGet(WorkerPrivate* aWorkerPrivate, return nullptr; } - nsRefPtr r = - new WorkerGetRunnable(aWorkerPrivate, p, aFilter.mTag, aScope); - if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)))) { + nsRefPtr proxy = + PromiseWorkerProxy::Create(aWorkerPrivate, p); + if (!proxy) { aRv.Throw(NS_ERROR_DOM_ABORT_ERR); return nullptr; } + nsRefPtr r = + new WorkerGetRunnable(proxy, aFilter.mTag, aScope); + // Since this is called from script via + // ServiceWorkerRegistration::GetNotifications, we can assert dispatch. + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r))); return p.forget(); } diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index 49b6596ea2..faf1d5b9ea 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -1509,8 +1509,7 @@ public: MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate); MOZ_ASSERT(mPromiseWorkerProxy); - nsRefPtr workerPromise = mPromiseWorkerProxy->GetWorkerPromise(); - MOZ_ASSERT(workerPromise); + nsRefPtr workerPromise = mPromiseWorkerProxy->WorkerPromise(); // Here we convert the buffer to a JS::Value. JS::Rooted value(aCx); @@ -1553,11 +1552,10 @@ PromiseWorkerProxy::Create(workers::WorkerPrivate* aWorkerPrivate, // We do this to make sure the worker thread won't shut down before the // promise is resolved/rejected on the worker thread. - if (!aWorkerPrivate->AddFeature(aWorkerPrivate->GetJSContext(), proxy)) { + if (!proxy->AddRefObject()) { // Probably the worker is terminating. We cannot complete the operation // and we have to release all the resources. - proxy->mCleanedUp = true; - proxy->mWorkerPromise = nullptr; + proxy->CleanProperties(); return nullptr; } @@ -1574,40 +1572,76 @@ PromiseWorkerProxy::PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate, , mCleanedUp(false) , mCallbacks(aCallbacks) , mCleanUpLock("cleanUpLock") + , mFeatureAdded(false) { } PromiseWorkerProxy::~PromiseWorkerProxy() { MOZ_ASSERT(mCleanedUp); + MOZ_ASSERT(!mFeatureAdded); MOZ_ASSERT(!mWorkerPromise); + MOZ_ASSERT(!mWorkerPrivate); } -workers::WorkerPrivate* -PromiseWorkerProxy::GetWorkerPrivate() const +void +PromiseWorkerProxy::CleanProperties() { - // It's ok to race on |mCleanedUp|, because it will never cause us to fire - // the assertion when we should not. - MOZ_ASSERT(!mCleanedUp); - -#ifdef DEBUG - if (NS_IsMainThread()) { - mCleanUpLock.AssertCurrentThreadOwns(); - } -#endif - - return mWorkerPrivate; -} - -Promise* -PromiseWorkerProxy::GetWorkerPromise() const -{ - #ifdef DEBUG workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); worker->AssertIsOnWorkerThread(); #endif + // Ok to do this unprotected from Create(). + // CleanUp() holds the lock before calling this. + mCleanedUp = true; + mWorkerPromise = nullptr; + mWorkerPrivate = nullptr; +} + +bool +PromiseWorkerProxy::AddRefObject() +{ + MOZ_ASSERT(mWorkerPrivate); + mWorkerPrivate->AssertIsOnWorkerThread(); + MOZ_ASSERT(!mFeatureAdded); + if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), + this)) { + return false; + } + + mFeatureAdded = true; + // Maintain a reference so that we have a valid object to clean up when + // removing the feature. + AddRef(); + return true; +} + +workers::WorkerPrivate* +PromiseWorkerProxy::GetWorkerPrivate() const +{ +#ifdef DEBUG + if (NS_IsMainThread()) { + mCleanUpLock.AssertCurrentThreadOwns(); + } +#endif + // Safe to check this without a lock since we assert lock ownership on the + // main thread above. + MOZ_ASSERT(!mCleanedUp); + MOZ_ASSERT(mFeatureAdded); + + return mWorkerPrivate; +} + +Promise* +PromiseWorkerProxy::WorkerPromise() const +{ +#ifdef DEBUG + workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(worker); + worker->AssertIsOnWorkerThread(); +#endif + MOZ_ASSERT(mWorkerPromise); return mWorkerPromise; } @@ -1621,14 +1655,6 @@ PromiseWorkerProxy::StoreISupports(nsISupports* aSupports) mSupportsArray.AppendElement(supports); } -bool -PromiseWorkerProxyControlRunnable::WorkerRun(JSContext* aCx, - workers::WorkerPrivate* aWorkerPrivate) -{ - mProxy->CleanUp(aCx); - return true; -} - void PromiseWorkerProxy::RunCallback(JSContext* aCx, JS::Handle aValue, @@ -1636,9 +1662,9 @@ PromiseWorkerProxy::RunCallback(JSContext* aCx, { MOZ_ASSERT(NS_IsMainThread()); - MutexAutoLock lock(GetCleanUpLock()); + MutexAutoLock lock(Lock()); // If the worker thread's been cancelled we don't need to resolve the Promise. - if (IsClean()) { + if (CleanedUp()) { return; } @@ -1657,11 +1683,7 @@ PromiseWorkerProxy::RunCallback(JSContext* aCx, Move(buffer), aFunc); - if (!runnable->Dispatch(aCx)) { - nsRefPtr runnable = - new PromiseWorkerProxyControlRunnable(mWorkerPrivate, this); - mWorkerPrivate->DispatchControlRunnable(runnable.forget()); - } + runnable->Dispatch(aCx); } void @@ -1681,10 +1703,6 @@ PromiseWorkerProxy::RejectedCallback(JSContext* aCx, bool PromiseWorkerProxy::Notify(JSContext* aCx, Status aStatus) { - MOZ_ASSERT(mWorkerPrivate); - mWorkerPrivate->AssertIsOnWorkerThread(); - MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx); - if (aStatus >= Canceling) { CleanUp(aCx); } @@ -1695,24 +1713,29 @@ PromiseWorkerProxy::Notify(JSContext* aCx, Status aStatus) void PromiseWorkerProxy::CleanUp(JSContext* aCx) { - MutexAutoLock lock(mCleanUpLock); + // Can't release Mutex while it is still locked, so scope the lock. + { + MutexAutoLock lock(Lock()); - // |mWorkerPrivate| might not be safe to use anymore if we have already - // cleaned up and RemoveFeature(), so we need to check |mCleanedUp| first. - if (mCleanedUp) { - return; + // |mWorkerPrivate| is not safe to use anymore if we have already + // cleaned up and RemoveFeature(), so we need to check |mCleanedUp| first. + if (CleanedUp()) { + return; + } + + MOZ_ASSERT(mWorkerPrivate); + mWorkerPrivate->AssertIsOnWorkerThread(); + MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx); + + // Release the Promise and remove the PromiseWorkerProxy from the features of + // the worker thread since the Promise has been resolved/rejected or the + // worker thread has been cancelled. + MOZ_ASSERT(mFeatureAdded); + mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this); + mFeatureAdded = false; + CleanProperties(); } - - MOZ_ASSERT(mWorkerPrivate); - mWorkerPrivate->AssertIsOnWorkerThread(); - MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx); - - // Release the Promise and remove the PromiseWorkerProxy from the features of - // the worker thread since the Promise has been resolved/rejected or the - // worker thread has been cancelled. - mWorkerPromise = nullptr; - mWorkerPrivate->RemoveFeature(aCx, this); - mCleanedUp = true; + Release(); } // Specializations of MaybeRejectBrokenly we actually support. diff --git a/dom/promise/PromiseWorkerProxy.h b/dom/promise/PromiseWorkerProxy.h index ff0499ea20..fb96c9d0f7 100644 --- a/dom/promise/PromiseWorkerProxy.h +++ b/dom/promise/PromiseWorkerProxy.h @@ -24,8 +24,8 @@ namespace workers { class WorkerPrivate; } // namespace workers -// A proxy to catch the resolved/rejected Promise's result from the main thread -// and resolve/reject that on the worker thread eventually. +// A proxy to (eventually) mirror a resolved/rejected Promise's result from the +// main thread to a Promise on the worker thread. // // How to use: // @@ -36,13 +36,27 @@ class WorkerPrivate; // if (aRv.Failed()) { // return nullptr; // } -// // Pass |promise| around to the WorkerMainThreadRunnable +// +// 2. Create a PromiseWorkerProxy wrapping the Promise. If this fails, the +// worker is shutting down and you should fail the original call. This is +// only likely to happen in (Gecko-specific) worker onclose handlers. +// +// nsRefPtr proxy = +// PromiseWorkerProxy::Create(workerPrivate, promise); +// if (!proxy) { +// // You may also reject the Promise with an AbortError or similar. +// return nullptr; +// } +// +// 3. Dispatch a runnable to the main thread, with a reference to the proxy to +// perform the main thread operation. PromiseWorkerProxy is thread-safe +// refcounted. +// +// 4. Return the worker thread promise to the JS caller: +// // return promise.forget(); // -// 2. In your WorkerMainThreadRunnable's ctor, create a PromiseWorkerProxy -// which holds a nsRefPtr to the Promise created at #1. -// -// 3. In your WorkerMainThreadRunnable::MainThreadRun(), obtain a Promise on +// 5. In your main thread runnable Run(), obtain a Promise on // the main thread and call its AppendNativeHandler(PromiseNativeHandler*) // to bind the PromiseWorkerProxy created at #2. // @@ -52,17 +66,49 @@ class WorkerPrivate; // // PromiseWorkerProxy can also be used in situations where there is no main // thread Promise, or where special handling is required on the worker thread -// for promise resolution. Create a PromiseWorkerProxy as in steps 1 and -// 2 above. When the main thread is ready to resolve the worker thread promise, -// dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the -// worker. This might be null! In the WorkerRunnable's WorkerRun() use -// GetWorkerPromise() to access the Promise and resolve/reject it. Then call -// CleanUp() on the worker thread. +// for promise resolution. Create a PromiseWorkerProxy as in steps 1 to 3 +// above. When the main thread is ready to resolve the worker thread promise: // -// IMPORTANT: Dispatching the runnable to the worker thread may fail causing -// the promise to leak. To successfully release the promise on the -// worker thread in this case, use |PromiseWorkerProxyControlRunnable| to -// dispatch a control runnable that will deref the object on the correct thread. +// 1. Acquire the mutex before attempting to access the worker private. +// +// AssertIsOnMainThread(); +// MutexAutoLock lock(proxy->Lock()); +// if (proxy->CleanedUp()) { +// // Worker has already shut down, can't access worker private. +// return; +// } +// +// 2. Dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the +// worker. +// +// nsRefPtr runnable = +// new FinishTaskWorkerRunnable(proxy->GetWorkerPrivate(), proxy, result); +// AutoJSAPI jsapi; +// jsapi.Init(); +// if (!r->Dispatch(jsapi.cx())) { +// // Worker is alive but not Running any more, so the Promise can't +// // be resolved, give up. The proxy will get Release()d at some +// // point. +// +// // Usually do nothing, but you may want to log the fact. +// } +// +// 3. In the WorkerRunnable's WorkerRun() use WorkerPromise() to access the +// Promise and resolve/reject it. Then call CleanUp(). +// +// bool +// WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override +// { +// aWorkerPrivate->AssertIsOnWorkerThread(); +// nsRefPtr promise = mProxy->WorkerPromise(); +// promise->MaybeResolve(mResult); +// mProxy->CleanUp(aCx); +// } +// +// Note: If a PromiseWorkerProxy is not cleaned up by a WorkerRunnable - this +// can happen if the main thread Promise is never fulfilled - it will +// stay alive till the worker reaches a Canceling state, even if all external +// references to it are dropped. class PromiseWorkerProxy : public PromiseNativeHandler, public workers::WorkerFeature @@ -77,20 +123,29 @@ public: Promise* aWorkerPromise, const JSStructuredCloneCallbacks* aCallbacks = nullptr); + // Main thread callers must hold Lock() and check CleanUp() before calling this. + // Worker thread callers, this will assert that the proxy has not been cleaned + // up. workers::WorkerPrivate* GetWorkerPrivate() const; - Promise* GetWorkerPromise() const; + // This should only be used within WorkerRunnable::WorkerRun() running on the + // worker thread! Do not call this after calling CleanUp(). + Promise* WorkerPromise() const; void StoreISupports(nsISupports* aSupports); + // Worker thread only. Calling this invalidates several assumptions, so be + // sure this is the last thing you do. + // 1. WorkerPrivate() will no longer return a valid worker. + // 2. WorkerPromise() will crash! void CleanUp(JSContext* aCx); - Mutex& GetCleanUpLock() + Mutex& Lock() { return mCleanUpLock; } - bool IsClean() const + bool CleanedUp() const { mCleanUpLock.AssertCurrentThreadOwns(); return mCleanedUp; @@ -112,6 +167,11 @@ private: virtual ~PromiseWorkerProxy(); + bool AddRefObject(); + + // If not called from Create(), be sure to hold Lock(). + void CleanProperties(); + // Function pointer for calling Promise::{ResolveInternal,RejectInternal}. typedef void (Promise::*RunCallbackFunc)(JSContext*, JS::Handle); @@ -120,11 +180,15 @@ private: JS::Handle aValue, RunCallbackFunc aFunc); + // Any thread with appropriate checks. workers::WorkerPrivate* mWorkerPrivate; - // This lives on the worker thread. + // Worker thread only. nsRefPtr mWorkerPromise; + // Modified on the worker thread. + // It is ok to *read* this without a lock on the worker. + // Main thread must always acquire a lock. bool mCleanedUp; // To specify if the cleanUp() has been done. const JSStructuredCloneCallbacks* mCallbacks; @@ -135,32 +199,10 @@ private: // Ensure the worker and the main thread won't race to access |mCleanedUp|. Mutex mCleanUpLock; + + // Maybe get rid of this entirely and rely on mCleanedUp + DebugOnly mFeatureAdded; }; - -// Helper runnable used for releasing the proxied promise when the worker -// is not accepting runnables and the promise object would leak. -// See the instructions above. -class PromiseWorkerProxyControlRunnable final : public workers::WorkerControlRunnable -{ - nsRefPtr mProxy; - -public: - PromiseWorkerProxyControlRunnable(workers::WorkerPrivate* aWorkerPrivate, - PromiseWorkerProxy* aProxy) - : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) - , mProxy(aProxy) - { - MOZ_ASSERT(aProxy); - } - - virtual bool - WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override; - -private: - ~PromiseWorkerProxyControlRunnable() - {} -}; - } // namespace dom } // namespace mozilla diff --git a/dom/push/PushManager.cpp b/dom/push/PushManager.cpp index 5e8e0977c8..498aae1c46 100644 --- a/dom/push/PushManager.cpp +++ b/dom/push/PushManager.cpp @@ -216,30 +216,6 @@ WorkerPushSubscription::Constructor(GlobalObject& aGlobal, const nsAString& aEnd return sub.forget(); } -namespace { -// The caller MUST take ownership of the proxy's lock before it calls this. -void -ReleasePromiseWorkerProxy(already_AddRefed aProxy) -{ - AssertIsOnMainThread(); - nsRefPtr proxy = aProxy; - MOZ_ASSERT(proxy); - proxy->GetCleanUpLock().AssertCurrentThreadOwns(); - if (proxy->IsClean()) { - return; - } - - AutoJSAPI jsapi; - jsapi.Init(); - - nsRefPtr cr = - new PromiseWorkerProxyControlRunnable(proxy->GetWorkerPrivate(), - proxy); - - MOZ_ALWAYS_TRUE(cr->Dispatch(jsapi.cx())); -} -} // anonymous namespace - class UnsubscribeResultRunnable final : public WorkerRunnable { public: @@ -260,15 +236,14 @@ public: MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - nsRefPtr proxy = mProxy.forget(); - nsRefPtr promise = proxy->GetWorkerPromise(); + nsRefPtr promise = mProxy->WorkerPromise(); if (NS_SUCCEEDED(mStatus)) { promise->MaybeResolve(mSuccess); } else { promise->MaybeReject(NS_ERROR_DOM_NETWORK_ERR); } - proxy->CleanUp(aCx); + mProxy->CleanUp(aCx); return true; } private: @@ -295,12 +270,12 @@ public: OnUnsubscribe(nsresult aStatus, bool aSuccess) override { AssertIsOnMainThread(); - if (!mProxy) { - return NS_OK; - } + MOZ_ASSERT(mProxy, "OnUnsubscribe() called twice?"); - MutexAutoLock lock(mProxy->GetCleanUpLock()); - if (mProxy->IsClean()) { + nsRefPtr proxy = mProxy.forget(); + + MutexAutoLock lock(proxy->Lock()); + if (proxy->CleanedUp()) { return NS_OK; } @@ -308,29 +283,14 @@ public: jsapi.Init(); nsRefPtr r = - new UnsubscribeResultRunnable(mProxy, aStatus, aSuccess); - if (!r->Dispatch(jsapi.cx())) { - ReleasePromiseWorkerProxy(mProxy.forget()); - } - - mProxy = nullptr; + new UnsubscribeResultRunnable(proxy, aStatus, aSuccess); + r->Dispatch(jsapi.cx()); return NS_OK; } private: ~WorkerUnsubscribeResultCallback() { - AssertIsOnMainThread(); - if (mProxy) { - MutexAutoLock lock(mProxy->GetCleanUpLock()); - if (!mProxy->IsClean()) { - AutoJSAPI jsapi; - jsapi.Init(); - nsRefPtr cr = - new PromiseWorkerProxyControlRunnable(mProxy->GetWorkerPrivate(), mProxy); - cr->Dispatch(jsapi.cx()); - } - } } nsRefPtr mProxy; @@ -354,8 +314,8 @@ public: Run() override { AssertIsOnMainThread(); - MutexAutoLock lock(mProxy->GetCleanUpLock()); - if (mProxy->IsClean()) { + MutexAutoLock lock(mProxy->Lock()); + if (mProxy->CleanedUp()) { return NS_OK; } @@ -448,8 +408,7 @@ public: bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { - nsRefPtr proxy = mProxy.forget(); - nsRefPtr promise = proxy->GetWorkerPromise(); + nsRefPtr promise = mProxy->WorkerPromise(); if (NS_SUCCEEDED(mStatus)) { if (mEndpoint.IsEmpty()) { promise->MaybeResolve(JS::NullHandleValue); @@ -462,7 +421,7 @@ public: promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); } - proxy->CleanUp(aCx); + mProxy->CleanUp(aCx); return true; } private: @@ -490,13 +449,12 @@ public: OnPushEndpoint(nsresult aStatus, const nsAString& aEndpoint) override { AssertIsOnMainThread(); + MOZ_ASSERT(mProxy, "OnPushEndpoint() called twice?"); - if (!mProxy) { - return NS_OK; - } + nsRefPtr proxy = mProxy.forget(); - MutexAutoLock lock(mProxy->GetCleanUpLock()); - if (mProxy->IsClean()) { + MutexAutoLock lock(proxy->Lock()); + if (proxy->CleanedUp()) { return NS_OK; } @@ -504,30 +462,14 @@ public: jsapi.Init(); nsRefPtr r = - new GetSubscriptionResultRunnable(mProxy, aStatus, aEndpoint, mScope); - if (!r->Dispatch(jsapi.cx())) { - ReleasePromiseWorkerProxy(mProxy.forget()); - } - - mProxy = nullptr; + new GetSubscriptionResultRunnable(proxy, aStatus, aEndpoint, mScope); + r->Dispatch(jsapi.cx()); return NS_OK; } protected: ~GetSubscriptionCallback() - { - AssertIsOnMainThread(); - if (mProxy) { - MutexAutoLock lock(mProxy->GetCleanUpLock()); - if (!mProxy->IsClean()) { - AutoJSAPI jsapi; - jsapi.Init(); - nsRefPtr cr = - new PromiseWorkerProxyControlRunnable(mProxy->GetWorkerPrivate(), mProxy); - cr->Dispatch(jsapi.cx()); - } - } - } + {} private: nsRefPtr mProxy; @@ -550,8 +492,8 @@ public: Run() override { AssertIsOnMainThread(); - MutexAutoLock lock(mProxy->GetCleanUpLock()); - if (mProxy->IsClean()) { + MutexAutoLock lock(mProxy->Lock()); + if (mProxy->CleanedUp()) { return NS_OK; } @@ -564,9 +506,11 @@ public: return NS_OK; } + nsCOMPtr principal = mProxy->GetWorkerPrivate()->GetPrincipal(); + uint32_t permission = nsIPermissionManager::DENY_ACTION; nsresult rv = permManager->TestExactPermissionFromPrincipal( - mProxy->GetWorkerPrivate()->GetPrincipal(), + principal, "push", &permission); @@ -582,9 +526,6 @@ public: return NS_OK; } - nsCOMPtr principal = mProxy->GetWorkerPrivate()->GetPrincipal(); - mProxy = nullptr; - if (mAction == WorkerPushManager::SubscribeAction) { rv = client->Subscribe(mScope, principal, callback); } else { @@ -667,17 +608,17 @@ public: MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - nsRefPtr proxy = mProxy.forget(); - nsRefPtr promise = proxy->GetWorkerPromise(); + nsRefPtr promise = mProxy->WorkerPromise(); if (NS_SUCCEEDED(mStatus)) { MOZ_ASSERT(uint32_t(mState) < ArrayLength(PushPermissionStateValues::strings)); - nsAutoCString stringState(PushPermissionStateValues::strings[uint32_t(mState)].value, PushPermissionStateValues::strings[uint32_t(mState)].length); + nsAutoCString stringState(PushPermissionStateValues::strings[uint32_t(mState)].value, + PushPermissionStateValues::strings[uint32_t(mState)].length); promise->MaybeResolve(NS_ConvertUTF8toUTF16(stringState)); } else { promise->MaybeReject(aCx, JS::UndefinedHandleValue); } - proxy->CleanUp(aCx); + mProxy->CleanUp(aCx); return true; } @@ -701,8 +642,8 @@ public: Run() override { AssertIsOnMainThread(); - MutexAutoLock lock(mProxy->GetCleanUpLock()); - if (mProxy->IsClean()) { + MutexAutoLock lock(mProxy->Lock()); + if (mProxy->CleanedUp()) { return NS_OK; } @@ -740,9 +681,7 @@ public: jsapi.Init(); nsRefPtr r = new PermissionResultRunnable(mProxy, rv, state); - if (!r->Dispatch(jsapi.cx())) { - ReleasePromiseWorkerProxy(mProxy.forget()); - } + r->Dispatch(jsapi.cx()); return NS_OK; } diff --git a/dom/workers/ServiceWorkerClients.cpp b/dom/workers/ServiceWorkerClients.cpp index d48851cfcc..8347550d5a 100644 --- a/dom/workers/ServiceWorkerClients.cpp +++ b/dom/workers/ServiceWorkerClients.cpp @@ -65,7 +65,7 @@ public: MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - Promise* promise = mPromiseProxy->GetWorkerPromise(); + Promise* promise = mPromiseProxy->WorkerPromise(); MOZ_ASSERT(promise); nsTArray> ret; @@ -74,30 +74,24 @@ public: new ServiceWorkerWindowClient(promise->GetParentObject(), mValue.ElementAt(i)))); } + promise->MaybeResolve(ret); - - // release the reference on the worker thread. mPromiseProxy->CleanUp(aCx); - return true; } }; class MatchAllRunnable final : public nsRunnable { - WorkerPrivate* mWorkerPrivate; nsRefPtr mPromiseProxy; nsCString mScope; public: - MatchAllRunnable(WorkerPrivate* aWorkerPrivate, - PromiseWorkerProxy* aPromiseProxy, + MatchAllRunnable(PromiseWorkerProxy* aPromiseProxy, const nsCString& aScope) - : mWorkerPrivate(aWorkerPrivate), - mPromiseProxy(aPromiseProxy), + : mPromiseProxy(aPromiseProxy), mScope(aScope) { - MOZ_ASSERT(aWorkerPrivate); - aWorkerPrivate->AssertIsOnWorkerThread(); + MOZ_ASSERT(mPromiseProxy); } NS_IMETHOD @@ -105,33 +99,22 @@ public: { AssertIsOnMainThread(); - MutexAutoLock lock(mPromiseProxy->GetCleanUpLock()); - if (mPromiseProxy->IsClean()) { - // Don't resolve the promise if it was already released. + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { return NS_OK; } nsRefPtr swm = ServiceWorkerManager::GetInstance(); nsTArray result; - swm->GetAllClients(mWorkerPrivate->GetPrincipal(), mScope, result); + swm->GetAllClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), mScope, result); nsRefPtr r = - new ResolvePromiseWorkerRunnable(mWorkerPrivate, mPromiseProxy, result); - - AutoSafeJSContext cx; - if (r->Dispatch(cx)) { - return NS_OK; - } - - // Dispatch to worker thread failed because the worker is shutting down. - // Use a control runnable to release the runnable on the worker thread. - nsRefPtr releaseRunnable = - new PromiseWorkerProxyControlRunnable(mWorkerPrivate, mPromiseProxy); - - if (!releaseRunnable->Dispatch(cx)) { - NS_RUNTIMEABORT("Failed to dispatch MatchAll promise control runnable."); - } + new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(), + mPromiseProxy, result); + AutoJSAPI jsapi; + jsapi.Init(); + r->Dispatch(jsapi.cx()); return NS_OK; } }; @@ -158,7 +141,7 @@ public: MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - Promise* promise = mPromiseProxy->GetWorkerPromise(); + nsRefPtr promise = mPromiseProxy->WorkerPromise(); MOZ_ASSERT(promise); if (NS_SUCCEEDED(mResult)) { @@ -167,9 +150,7 @@ public: promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); } - // Release the reference on the worker thread. mPromiseProxy->CleanUp(aCx); - return true; } }; @@ -178,14 +159,14 @@ class ClaimRunnable final : public nsRunnable { nsRefPtr mPromiseProxy; nsCString mScope; - // We grab the ID so we don't have to hold a lock the entire time the claim - // operation is happening on the main thread. uint64_t mServiceWorkerID; public: ClaimRunnable(PromiseWorkerProxy* aPromiseProxy, const nsCString& aScope) : mPromiseProxy(aPromiseProxy) , mScope(aScope) + // Safe to call GetWorkerPrivate() since we are being called on the worker + // thread via script (so no clean up has occured yet). , mServiceWorkerID(aPromiseProxy->GetWorkerPrivate()->ServiceWorkerID()) { MOZ_ASSERT(aPromiseProxy); @@ -194,9 +175,8 @@ public: NS_IMETHOD Run() override { - MutexAutoLock lock(mPromiseProxy->GetCleanUpLock()); - if (mPromiseProxy->IsClean()) { - // Don't resolve the promise if it was already released. + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { return NS_OK; } @@ -214,20 +194,7 @@ public: AutoJSAPI jsapi; jsapi.Init(); - JSContext* cx = jsapi.cx(); - if (r->Dispatch(cx)) { - return NS_OK; - } - - // Dispatch to worker thread failed because the worker is shutting down. - // Use a control runnable to release the runnable on the worker thread. - nsRefPtr releaseRunnable = - new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy); - - if (!releaseRunnable->Dispatch(cx)) { - NS_RUNTIMEABORT("Failed to dispatch Claim control runnable."); - } - + r->Dispatch(jsapi.cx()); return NS_OK; } }; @@ -257,21 +224,15 @@ ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions, nsRefPtr promiseProxy = PromiseWorkerProxy::Create(workerPrivate, promise); - if (!promiseProxy->GetWorkerPromise()) { - // Don't dispatch if adding the worker feature failed. + if (!promiseProxy) { + promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); return promise.forget(); } nsRefPtr r = - new MatchAllRunnable(workerPrivate, - promiseProxy, + new MatchAllRunnable(promiseProxy, NS_ConvertUTF16toUTF8(scope)); - nsresult rv = NS_DispatchToMainThread(r); - - if (NS_WARN_IF(NS_FAILED(rv))) { - promise->MaybeReject(NS_ERROR_NOT_AVAILABLE); - } - + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r))); return promise.forget(); } @@ -301,8 +262,8 @@ ServiceWorkerClients::Claim(ErrorResult& aRv) nsRefPtr promiseProxy = PromiseWorkerProxy::Create(workerPrivate, promise); - if (!promiseProxy->GetWorkerPromise()) { - // Don't dispatch if adding the worker feature failed. + if (!promiseProxy) { + promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); return promise.forget(); } @@ -312,10 +273,6 @@ ServiceWorkerClients::Claim(ErrorResult& aRv) nsRefPtr runnable = new ClaimRunnable(promiseProxy, NS_ConvertUTF16toUTF8(scope)); - aRv = NS_DispatchToMainThread(runnable); - if (NS_WARN_IF(aRv.Failed())) { - promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); - } - + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); return promise.forget(); } diff --git a/dom/workers/ServiceWorkerRegistration.cpp b/dom/workers/ServiceWorkerRegistration.cpp index d51e0e42d9..edaaca4dff 100644 --- a/dom/workers/ServiceWorkerRegistration.cpp +++ b/dom/workers/ServiceWorkerRegistration.cpp @@ -301,7 +301,7 @@ public: bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { - Promise* promise = mPromiseProxy->GetWorkerPromise(); + Promise* promise = mPromiseProxy->WorkerPromise(); if (NS_SUCCEEDED(mStatus)) { promise->MaybeResolve(JS::UndefinedHandleValue); } else { @@ -318,7 +318,6 @@ class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallbac ~WorkerThreadUpdateCallback() { - Finish(NS_ERROR_FAILURE); } public: @@ -351,8 +350,8 @@ public: nsRefPtr proxy = mPromiseProxy.forget(); - MutexAutoLock lock(proxy->GetCleanUpLock()); - if (proxy->IsClean()) { + MutexAutoLock lock(proxy->Lock()); + if (proxy->CleanedUp()) { return; } @@ -361,11 +360,7 @@ public: nsRefPtr r = new UpdateResultRunnable(proxy, aStatus); - if (!r->Dispatch(jsapi.cx())) { - nsRefPtr r = - new PromiseWorkerProxyControlRunnable(proxy->GetWorkerPrivate(), proxy); - r->Dispatch(jsapi.cx()); - } + r->Dispatch(jsapi.cx()); } }; @@ -384,8 +379,8 @@ public: AssertIsOnMainThread(); ErrorResult result; - MutexAutoLock lock(mPromiseProxy->GetCleanUpLock()); - if (mPromiseProxy->IsClean()) { + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { return NS_OK; } @@ -445,10 +440,9 @@ class FulfillUnregisterPromiseRunnable final : public WorkerRunnable nsRefPtr mPromiseWorkerProxy; Maybe mState; public: - FulfillUnregisterPromiseRunnable(WorkerPrivate* aWorkerPrivate, - PromiseWorkerProxy* aProxy, + FulfillUnregisterPromiseRunnable(PromiseWorkerProxy* aProxy, Maybe aState) - : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount) + : WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount) , mPromiseWorkerProxy(aProxy) , mState(aState) { @@ -459,8 +453,7 @@ public: bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { - Promise* promise = mPromiseWorkerProxy->GetWorkerPromise(); - MOZ_ASSERT(promise); + nsRefPtr promise = mPromiseWorkerProxy->WorkerPromise(); if (mState.isSome()) { promise->MaybeResolve(mState.value()); } else { @@ -481,6 +474,7 @@ public: explicit WorkerUnregisterCallback(PromiseWorkerProxy* aProxy) : mPromiseWorkerProxy(aProxy) { + MOZ_ASSERT(aProxy); } NS_IMETHODIMP @@ -501,7 +495,7 @@ public: private: ~WorkerUnregisterCallback() - { } + {} void Finish(Maybe aState) @@ -511,24 +505,18 @@ private: return; } - MutexAutoLock lock(mPromiseWorkerProxy->GetCleanUpLock()); - if (mPromiseWorkerProxy->IsClean()) { + nsRefPtr proxy = mPromiseWorkerProxy.forget(); + MutexAutoLock lock(proxy->Lock()); + if (proxy->CleanedUp()) { return; } nsRefPtr r = - new FulfillUnregisterPromiseRunnable(mPromiseWorkerProxy->GetWorkerPrivate(), - mPromiseWorkerProxy, aState); + new FulfillUnregisterPromiseRunnable(proxy, aState); AutoJSAPI jsapi; jsapi.Init(); - if (!r->Dispatch(jsapi.cx())) { - nsRefPtr cr = - new PromiseWorkerProxyControlRunnable( - mPromiseWorkerProxy->GetWorkerPrivate(), - mPromiseWorkerProxy); - cr->Dispatch(jsapi.cx()); - } + r->Dispatch(jsapi.cx()); } }; @@ -544,12 +532,12 @@ class StartUnregisterRunnable final : public nsRunnable const nsString mScope; public: - StartUnregisterRunnable(WorkerPrivate* aWorker, Promise* aPromise, + StartUnregisterRunnable(PromiseWorkerProxy* aProxy, const nsAString& aScope) - : mPromiseWorkerProxy(PromiseWorkerProxy::Create(aWorker, aPromise)) + : mPromiseWorkerProxy(aProxy) , mScope(aScope) { - // mPromiseWorkerProxy may be null if AddFeature failed. + MOZ_ASSERT(aProxy); } NS_IMETHOD @@ -557,8 +545,6 @@ public: { AssertIsOnMainThread(); - nsRefPtr cb = new WorkerUnregisterCallback(mPromiseWorkerProxy); - // XXXnsm: There is a rare chance of this failing if the worker gets // destroyed. In that case, unregister() called from a SW is no longer // guaranteed to run. We should fix this by having a main thread proxy @@ -566,8 +552,8 @@ public: // principal. Can that be trusted? nsCOMPtr principal; { - MutexAutoLock lock(mPromiseWorkerProxy->GetCleanUpLock()); - if (mPromiseWorkerProxy->IsClean()) { + MutexAutoLock lock(mPromiseWorkerProxy->Lock()); + if (mPromiseWorkerProxy->CleanedUp()) { return NS_OK; } @@ -577,6 +563,8 @@ public: } MOZ_ASSERT(principal); + nsRefPtr cb = + new WorkerUnregisterCallback(mPromiseWorkerProxy); nsCOMPtr swm = mozilla::services::GetServiceWorkerManager(); nsresult rv = swm->Unregister(principal, cb, mScope); @@ -962,8 +950,8 @@ ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv) nsRefPtr proxy = PromiseWorkerProxy::Create(worker, promise); if (!proxy) { - promise->MaybeResolve(NS_ERROR_DOM_ABORT_ERR); - return promise.forget(); + aRv.Throw(NS_ERROR_DOM_ABORT_ERR); + return nullptr; } nsRefPtr r = new UpdateRunnable(proxy, mScope); @@ -992,7 +980,13 @@ ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv) return nullptr; } - nsRefPtr r = new StartUnregisterRunnable(worker, promise, mScope); + nsRefPtr proxy = PromiseWorkerProxy::Create(worker, promise); + if (!proxy) { + aRv.Throw(NS_ERROR_DOM_ABORT_ERR); + return nullptr; + } + + nsRefPtr r = new StartUnregisterRunnable(proxy, mScope); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r))); return promise.forget(); diff --git a/dom/workers/ServiceWorkerWindowClient.cpp b/dom/workers/ServiceWorkerWindowClient.cpp index 3e0e2a06d6..c3dc860cea 100644 --- a/dom/workers/ServiceWorkerWindowClient.cpp +++ b/dom/workers/ServiceWorkerWindowClient.cpp @@ -51,7 +51,7 @@ public: MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - Promise* promise = mPromiseProxy->GetWorkerPromise(); + nsRefPtr promise = mPromiseProxy->WorkerPromise(); MOZ_ASSERT(promise); if (mClientInfo) { @@ -80,7 +80,6 @@ public: , mPromiseProxy(aPromiseProxy) { MOZ_ASSERT(mPromiseProxy); - MOZ_ASSERT(mPromiseProxy->GetWorkerPromise()); } NS_IMETHOD @@ -110,28 +109,18 @@ private: DispatchResult(UniquePtr&& aClientInfo) { AssertIsOnMainThread(); - MutexAutoLock lock(mPromiseProxy->GetCleanUpLock()); - if (mPromiseProxy->IsClean()) { + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { return; } - WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate(); - MOZ_ASSERT(workerPrivate); - nsRefPtr resolveRunnable = - new ResolveOrRejectPromiseRunnable(workerPrivate, mPromiseProxy, - Move(aClientInfo)); + new ResolveOrRejectPromiseRunnable(mPromiseProxy->GetWorkerPrivate(), + mPromiseProxy, Move(aClientInfo)); AutoJSAPI jsapi; jsapi.Init(); - JSContext* cx = jsapi.cx(); - if (!resolveRunnable->Dispatch(cx)) { - nsRefPtr controlRunnable = - new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy); - if (!controlRunnable->Dispatch(cx)) { - NS_RUNTIMEABORT("Failed to dispatch Focus promise control runnable."); - } - } + resolveRunnable->Dispatch(jsapi.cx()); } }; @@ -155,17 +144,14 @@ ServiceWorkerWindowClient::Focus(ErrorResult& aRv) const if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) { nsRefPtr promiseProxy = PromiseWorkerProxy::Create(workerPrivate, promise); - if (!promiseProxy->GetWorkerPromise()) { - // Don't dispatch if adding the worker feature failed. - return promise.forget(); + if (promiseProxy) { + nsRefPtr r = new ClientFocusRunnable(mWindowId, + promiseProxy); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r))); + } else { + promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); } - nsRefPtr r = new ClientFocusRunnable(mWindowId, - promiseProxy); - aRv = NS_DispatchToMainThread(r); - if (NS_WARN_IF(aRv.Failed())) { - promise->MaybeReject(aRv); - } } else { promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR); } diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 28afa0a9ad..8a7b801407 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -546,9 +546,7 @@ public: MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); - Promise* promise = mPromiseProxy->GetWorkerPromise(); - MOZ_ASSERT(promise); - + nsRefPtr promise = mPromiseProxy->WorkerPromise(); promise->MaybeResolve(JS::UndefinedHandleValue); // Release the reference on the worker thread. @@ -579,13 +577,12 @@ public: nsRefPtr swm = ServiceWorkerManager::GetInstance(); MOZ_ASSERT(swm); - MutexAutoLock lock(mPromiseProxy->GetCleanUpLock()); - if (mPromiseProxy->IsClean()) { + MutexAutoLock lock(mPromiseProxy->Lock()); + if (mPromiseProxy->CleanedUp()) { return NS_OK; } - WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate(); - MOZ_ASSERT(workerPrivate); + WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate(); swm->SetSkipWaitingFlag(workerPrivate->GetPrincipal(), mScope, workerPrivate->ServiceWorkerID()); @@ -594,20 +591,7 @@ public: AutoJSAPI jsapi; jsapi.Init(); - JSContext* cx = jsapi.cx(); - if (runnable->Dispatch(cx)) { - return NS_OK; - } - - // Dispatch to worker thread failed because the worker is shutting down. - // Use a control runnable to release the runnable on the worker thread. - nsRefPtr releaseRunnable = - new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy); - - if (!releaseRunnable->Dispatch(cx)) { - NS_RUNTIMEABORT("Failed to dispatch Claim control runnable."); - } - + runnable->Dispatch(jsapi.cx()); return NS_OK; } }; @@ -627,8 +611,7 @@ ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv) nsRefPtr promiseProxy = PromiseWorkerProxy::Create(mWorkerPrivate, promise); - if (!promiseProxy->GetWorkerPromise()) { - // Don't dispatch if adding the worker feature failed. + if (!promiseProxy) { promise->MaybeResolve(JS::UndefinedHandleValue); return promise.forget(); } @@ -637,11 +620,7 @@ ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv) new WorkerScopeSkipWaitingRunnable(promiseProxy, NS_ConvertUTF16toUTF8(mScope)); - aRv = NS_DispatchToMainThread(runnable); - if (NS_WARN_IF(aRv.Failed())) { - promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); - } - + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable))); return promise.forget(); } diff --git a/layout/base/ArenaObjectID.h b/layout/base/ArenaObjectID.h new file mode 100644 index 0000000000..db8dfe872b --- /dev/null +++ b/layout/base/ArenaObjectID.h @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +/* enum type for objects that can be allocated by an nsPresArena */ + +#ifndef mozilla_ArenaObjectID_h +#define mozilla_ArenaObjectID_h + +#include "nsQueryFrame.h" + +namespace mozilla { + +enum ArenaObjectID { + eArenaObjectID_DummyBeforeFirstObjectID = nsQueryFrame::NON_FRAME_MARKER - 1, + +#define PRES_ARENA_OBJECT(name_) \ + eArenaObjectID_##name_, +#include "nsPresArenaObjectList.h" +#undef PRES_ARENA_OBJECT + + /** + * The PresArena implementation uses this bit to distinguish objects + * allocated by size from objects allocated by type ID (that is, frames + * using AllocateByFrameID and other objects using AllocateByObjectID). + * It should not collide with any Object ID (above) or frame ID (in + * nsQueryFrame.h). It is not 0x80000000 to avoid the question of + * whether enumeration constants are signed. + */ + eArenaObjectID_NON_OBJECT_MARKER = 0x40000000 +}; + +}; + +#endif diff --git a/layout/base/ArenaRefPtr.h b/layout/base/ArenaRefPtr.h new file mode 100644 index 0000000000..983f8d4a82 --- /dev/null +++ b/layout/base/ArenaRefPtr.h @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=2 sw=2 et tw=78: + * 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/. + */ + +/* smart pointer for strong references to nsPresArena-allocated objects + that might be held onto until the arena's destruction */ + +#include "mozilla/Assertions.h" +#include "mozilla/nsRefPtr.h" + +#ifndef mozilla_ArenaRefPtr_h +#define mozilla_ArenaRefPtr_h + +class nsPresArena; + +namespace mozilla { + +/** + * A class for holding strong references to nsPresArena-allocated + * objects. + * + * Since the arena's lifetime is not related to the refcounts + * of the objects allocated within it, it is possible to have a strong + * reference to an arena-allocated object that lives until the + * destruction of the arena. An ArenaRefPtr acts like a weak reference + * in that it will clear its referent if the arena is about to go away. + * + * T must be a class that has these two methods: + * + * static mozilla::ArenaObjectID ArenaObjectID(); + * U* Arena(); + * + * where U is a class that has these two methods: + * + * void RegisterArenaRefPtr(ArenaRefPtr*); + * void DeregisterArenaRefPtr(ArenaRefPtr*); + * + * Currently, both nsPresArena and nsIPresShell can be used as U. + * + * The ArenaObjectID method must return the mozilla::ArenaObjectID that + * uniquely identifies T, and the Arena method must return the nsPresArena + * (or a proxy for it) in which the object was allocated. + */ +template +class ArenaRefPtr +{ + friend class ::nsPresArena; + +public: + ArenaRefPtr() + { + AssertValidType(); + } + + template + MOZ_IMPLICIT ArenaRefPtr(already_AddRefed& aRhs) + { + AssertValidType(); + assign(aRhs); + } + + template + MOZ_IMPLICIT ArenaRefPtr(already_AddRefed&& aRhs) + { + AssertValidType(); + assign(aRhs); + } + + MOZ_IMPLICIT ArenaRefPtr(T* aRhs) + { + AssertValidType(); + assign(aRhs); + } + + template + ArenaRefPtr& operator=(already_AddRefed& aRhs) + { + assign(aRhs); + return *this; + } + + template + ArenaRefPtr& operator=(already_AddRefed&& aRhs) + { + assign(aRhs); + return *this; + } + + ArenaRefPtr& operator=(T* aRhs) + { + assign(aRhs); + return *this; + } + + ~ArenaRefPtr() { assign(nullptr); } + +#ifdef MOZ_HAVE_REF_QUALIFIERS + operator T*() const & { return get(); } + operator T*() const && = delete; + explicit operator bool() const { return !!mPtr; } + bool operator!() const { return !mPtr; } +#else + operator T*() const { return get(); } +#endif + + T* operator->() const { return mPtr.operator->(); } + T& operator*() const { return *get(); } + + T* get() const { return mPtr; } + +private: + void AssertValidType(); + + /** + * Clears the pointer to the arena-allocated object but skips the usual + * step of deregistering the ArenaRefPtr from the nsPresArena. This + * method is called by nsPresArena when clearing all registered ArenaRefPtrs + * so that it can deregister them all at once, avoiding hash table churn. + */ + void ClearWithoutDeregistering() + { + mPtr = nullptr; + } + + template + void assign(already_AddRefed& aSmartPtr) + { + nsRefPtr newPtr(aSmartPtr); + assignFrom(newPtr); + } + + template + void assign(already_AddRefed&& aSmartPtr) + { + nsRefPtr newPtr(aSmartPtr); + assignFrom(newPtr); + } + + void assign(T* aPtr) { assignFrom(aPtr); } + + template + void assignFrom(I& aPtr) + { + if (aPtr == mPtr) { + return; + } + bool sameArena = mPtr && aPtr && mPtr->Arena() == aPtr->Arena(); + if (mPtr && !sameArena) { + MOZ_ASSERT(mPtr->Arena()); + mPtr->Arena()->DeregisterArenaRefPtr(this); + } + mPtr = Move(aPtr); + if (mPtr && !sameArena) { + MOZ_ASSERT(mPtr->Arena()); + mPtr->Arena()->RegisterArenaRefPtr(this); + } + } + + nsRefPtr mPtr; +}; + +} // namespace mozilla + +#endif // mozilla_ArenaRefPtr_h diff --git a/layout/base/ArenaRefPtrInlines.h b/layout/base/ArenaRefPtrInlines.h new file mode 100644 index 0000000000..a85e98569c --- /dev/null +++ b/layout/base/ArenaRefPtrInlines.h @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=2 sw=2 et tw=78: + * 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/. + */ + +/* inline methods that belong in ArenaRefPtr.h, except that they require + the inclusion of headers for all types that ArenaRefPtr can handle */ + +#ifndef mozilla_ArenaRefPtrInlines_h +#define mozilla_ArenaRefPtrInlines_h + +#include "mozilla/ArenaObjectID.h" +#include "mozilla/Assertions.h" +#include "nsStyleStruct.h" + +namespace mozilla { + +template +void +ArenaRefPtr::AssertValidType() +{ +#ifdef DEBUG + bool ok = +#define PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT(name_) \ + T::ArenaObjectID() == eArenaObjectID_##name_ || +#include "nsPresArenaObjectList.h" +#undef PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT + false; + MOZ_ASSERT(ok, "ArenaRefPtr template parameter T must be declared in " + "nsPresArenaObjectList with " + "PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT"); +#endif +} + +} // namespace mozilla + +template +void +nsPresArena::RegisterArenaRefPtr(mozilla::ArenaRefPtr* aPtr) +{ + MOZ_ASSERT(!mArenaRefPtrs.Contains(aPtr)); + mArenaRefPtrs.Put(aPtr, T::ArenaObjectID()); +} + +#endif diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 1c1de5c7c8..0c43d8c199 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -64,6 +64,15 @@ FrameTagToString(const nsIFrame* aFrame) aFrame->ListTag(result); return result; } + +static nsCString +ElementTagToString(dom::Element* aElement) +{ + nsCString result; + nsDependentAtomString buf(aElement->NodeInfo()->NameAtom()); + result.AppendPrintf("(%s@%p)", NS_ConvertUTF16toUTF8(buf).get(), aElement); + return result; +} #endif RestyleManager::RestyleManager(nsPresContext* aPresContext) @@ -74,6 +83,7 @@ RestyleManager::RestyleManager(nsPresContext* aPresContext) , mInStyleRefresh(false) , mSkipAnimationRules(false) , mHavePendingNonAnimationRestyles(false) + , mRestyleGeneration(1) , mHoverGeneration(0) , mRebuildAllExtraHint(nsChangeHint(0)) , mRebuildAllRestyleHint(nsRestyleHint(0)) @@ -81,8 +91,10 @@ RestyleManager::RestyleManager(nsPresContext* aPresContext) MostRecentRefresh()) , mAnimationGeneration(0) , mReframingStyleContexts(nullptr) + , mAnimationsWithDestroyedFrame(nullptr) , mPendingRestyles(ELEMENT_HAS_PENDING_RESTYLE | - ELEMENT_IS_POTENTIAL_RESTYLE_ROOT) + ELEMENT_IS_POTENTIAL_RESTYLE_ROOT | + ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR) #ifdef DEBUG , mIsProcessingRestyles(false) #endif @@ -168,9 +180,7 @@ SyncViewsAndInvalidateDescendants(nsIFrame* aFrame, nsIFrame::ChildListIterator lists(aFrame); for (; !lists.IsDone(); lists.Next()) { - nsFrameList::Enumerator childFrames(lists.CurrentList()); - for (; !childFrames.AtEnd(); childFrames.Next()) { - nsIFrame* child = childFrames.get(); + for (nsIFrame* child : lists.CurrentList()) { if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { // only do frames that don't have placeholders if (nsGkAtoms::placeholderFrame == child->GetType()) { @@ -261,7 +271,7 @@ DoApplyRenderingChangeToTree(nsIFrame* aFrame, // painting is suppressed. needInvalidatingPaint = true; aFrame->InvalidateFrameSubtree(); - if (aChange & nsChangeHint_UpdateEffects && + if ((aChange & nsChangeHint_UpdateEffects) && aFrame->IsFrameOfType(nsIFrame::eSVG) && !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) { // Need to update our overflow rects: @@ -603,8 +613,16 @@ RestyleManager::StyleChangeReflow(nsIFrame* aFrame, nsChangeHint aHint) if (dirtyType == nsIPresShell::eResize && !dirtyBits) return; + nsIPresShell::ReflowRootHandling rootHandling; + if (aHint & nsChangeHint_ReflowChangesSizeOrPosition) { + rootHandling = nsIPresShell::ePositionOrSizeChange; + } else { + rootHandling = nsIPresShell::eNoPositionOrSizeChange; + } + do { - mPresContext->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits); + mPresContext->PresShell()->FrameNeedsReflow(aFrame, dirtyType, dirtyBits, + rootHandling); aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame); } while (aFrame); } @@ -617,9 +635,7 @@ RestyleManager::AddSubtreeToOverflowTracker(nsIFrame* aFrame) OverflowChangedTracker::CHILDREN_CHANGED); nsIFrame::ChildListIterator lists(aFrame); for (; !lists.IsDone(); lists.Next()) { - nsFrameList::Enumerator childFrames(lists.CurrentList()); - for (; !childFrames.AtEnd(); childFrames.Next()) { - nsIFrame* child = childFrames.get(); + for (nsIFrame* child : lists.CurrentList()) { AddSubtreeToOverflowTracker(child); } } @@ -638,9 +654,7 @@ FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame, uint32_t aPositionMas nsIFrame::kFixedList); for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) { if (!skip.Contains(lists.CurrentID())) { - for (nsFrameList::Enumerator childFrames(lists.CurrentList()); - !childFrames.AtEnd(); childFrames.Next()) { - nsIFrame* f = childFrames.get(); + for (nsIFrame* f : lists.CurrentList()) { if (f->GetType() == nsGkAtoms::placeholderFrame) { nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(f); // If SVG text frames could appear here, they could confuse us since @@ -754,7 +768,7 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) } } - if ((hint & nsChangeHint_AddOrRemoveTransform) && frame && + if ((hint & nsChangeHint_UpdateContainingBlock) && frame && !(hint & nsChangeHint_ReconstructFrame)) { if (NeedToReframeForAddingOrRemovingTransform(frame) || frame->GetType() == nsGkAtoms::fieldSetFrame || @@ -768,13 +782,12 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) // Normally frame construction would set state bits as needed, // but we're not going to reconstruct the frame so we need to set them. // It's because we need to set this state on each affected frame - // that we can't coalesce nsChangeHint_AddOrRemoveTransform hints up + // that we can't coalesce nsChangeHint_UpdateContainingBlock hints up // to ancestors (i.e. it can't be an inherited change hint). if (cont->IsAbsPosContaininingBlock()) { - // If a transform has been added, we'll be taking this path, - // but we may be taking this path even if a transform has been - // removed. It's OK to add the bit even if it's not needed. - cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED); + if (cont->StyleDisplay()->HasTransform(cont)) { + cont->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED); + } if (!cont->IsAbsoluteContainer() && (cont->GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) { cont->MarkAsAbsoluteContainingBlock(); @@ -830,7 +843,10 @@ RestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList) nsSVGEffects::UpdateEffects(cont); } } - if (hint & nsChangeHint_InvalidateRenderingObservers) { + if ((hint & nsChangeHint_InvalidateRenderingObservers) || + ((hint & nsChangeHint_UpdateOpacityLayer) && + frame->IsFrameOfType(nsIFrame::eSVG) && + !(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) { nsSVGEffects::InvalidateRenderingObservers(frame); } if (hint & nsChangeHint_NeedReflow) { @@ -1061,6 +1077,44 @@ RestyleManager::ReframingStyleContexts::~ReframingStyleContexts() mRestyleManager->mPresContext->FrameConstructor()->CreateNeededFrames(); } +RestyleManager::AnimationsWithDestroyedFrame::AnimationsWithDestroyedFrame( + RestyleManager* aRestyleManager) + : mRestyleManager(aRestyleManager) + , mRestorePointer(mRestyleManager->mAnimationsWithDestroyedFrame) +{ + MOZ_ASSERT(!mRestyleManager->mAnimationsWithDestroyedFrame, + "shouldn't construct recursively"); + mRestyleManager->mAnimationsWithDestroyedFrame = this; +} + +void +RestyleManager::AnimationsWithDestroyedFrame::StopAnimationsForElementsWithoutFrames() +{ + StopAnimationsWithoutFrame(mContents, + nsCSSPseudoElements::ePseudo_NotPseudoElement); + StopAnimationsWithoutFrame(mBeforeContents, + nsCSSPseudoElements::ePseudo_before); + StopAnimationsWithoutFrame(mAfterContents, + nsCSSPseudoElements::ePseudo_after); +} + +void +RestyleManager::AnimationsWithDestroyedFrame::StopAnimationsWithoutFrame( + nsTArray>& aArray, + nsCSSPseudoElements::Type aPseudoType) +{ + nsAnimationManager* animationManager = + mRestyleManager->PresContext()->AnimationManager(); + for (nsIContent* content : aArray) { + if (content->GetPrimaryFrame()) { + continue; + } + dom::Element* element = content->AsElement(); + + animationManager->StopAnimationsForElement(element, aPseudoType); + } +} + static inline dom::Element* ElementForStyleContext(nsIContent* aParentContent, nsIFrame* aFrame, @@ -1161,13 +1215,15 @@ RestyleManager::AttributeWillChange(Element* aElement, int32_t aModType, const nsAttrValue* aNewValue) { + RestyleHintData rsdata; nsRestyleHint rshint = mPresContext->StyleSet()->HasAttributeDependentStyle(aElement, aAttribute, aModType, false, - aNewValue); - PostRestyleEvent(aElement, rshint, NS_STYLE_HINT_NONE); + aNewValue, + rsdata); + PostRestyleEvent(aElement, rshint, NS_STYLE_HINT_NONE, &rsdata); } // Forwarded nsIMutationObserver method, to handle restyling (and @@ -1250,14 +1306,15 @@ RestyleManager::AttributeChanged(Element* aElement, // See if we can optimize away the style re-resolution -- must be called after // the frame's AttributeChanged() in case it does something that affects the style + RestyleHintData rsdata; nsRestyleHint rshint = mPresContext->StyleSet()->HasAttributeDependentStyle(aElement, aAttribute, aModType, true, - aOldValue); - - PostRestyleEvent(aElement, rshint, hint); + aOldValue, + rsdata); + PostRestyleEvent(aElement, rshint, hint, &rsdata); } /* static */ uint64_t @@ -1471,7 +1528,8 @@ RestyleManager::RestyleForRemove(Element* aContainer, // This should be an assert, but this is called incorrectly in // nsHTMLEditor::DeleteRefToAnonymousNode and the assertions were clogging // up the logs. Make it an assert again when that's fixed. - NS_WARNING("anonymous nodes should not be in child lists (bug 439258)"); + MOZ_ASSERT(aOldChild->GetProperty(nsGkAtoms::restylableAnonymousNode), + "anonymous nodes should not be in child lists (bug 439258)"); } uint32_t selectorFlags = aContainer ? (aContainer->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0; @@ -1756,6 +1814,10 @@ RestyleManager::EndProcessingRestyles() { FlushOverflowChangedTracker(); + MOZ_ASSERT(mAnimationsWithDestroyedFrame); + mAnimationsWithDestroyedFrame-> + StopAnimationsForElementsWithoutFrames(); + // Set mInStyleRefresh to false now, since the EndUpdate call might // add more restyles. mInStyleRefresh = false; @@ -2002,9 +2064,7 @@ VerifyStyleTree(nsPresContext* aPresContext, nsIFrame* aFrame, nsIFrame::ChildListIterator lists(aFrame); for (; !lists.IsDone(); lists.Next()) { - nsFrameList::Enumerator childFrames(lists.CurrentList()); - for (; !childFrames.AtEnd(); childFrames.Next()) { - nsIFrame* child = childFrames.get(); + for (nsIFrame* child : lists.CurrentList()) { if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { // only do frames that are in flow if (nsGkAtoms::placeholderFrame == child->GetType()) { @@ -2377,9 +2437,11 @@ RestyleManager::ReparentStyleContext(nsIFrame* aFrame) // up resolving all the structs the old context resolved. if (!copyFromContinuation) { uint32_t equalStructs; + uint32_t samePointerStructs; DebugOnly styleChange = oldContext->CalcStyleDifference(newContext, nsChangeHint(0), - &equalStructs); + &equalStructs, + &samePointerStructs); // The style change is always 0 because we have the same rulenode and // CalcStyleDifference optimizes us away. That's OK, though: // reparenting should never trigger a frame reconstruct, and whenever @@ -2392,9 +2454,7 @@ RestyleManager::ReparentStyleContext(nsIFrame* aFrame) nsIFrame::ChildListIterator lists(aFrame); for (; !lists.IsDone(); lists.Next()) { - nsFrameList::Enumerator childFrames(lists.CurrentList()); - for (; !childFrames.AtEnd(); childFrames.Next()) { - nsIFrame* child = childFrames.get(); + for (nsIFrame* child : lists.CurrentList()) { // only do frames that are in flow if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && child != providerChild) { @@ -2443,10 +2503,12 @@ RestyleManager::ReparentStyleContext(nsIFrame* aFrame) // context ends up resolving all the structs the old context // resolved. uint32_t equalStructs; + uint32_t samePointerStructs; DebugOnly styleChange = oldExtraContext->CalcStyleDifference(newExtraContext, nsChangeHint(0), - &equalStructs); + &equalStructs, + &samePointerStructs); // The style change is always 0 because we have the same // rulenode and CalcStyleDifference optimizes us away. That's // OK, though: reparenting should never trigger a frame @@ -2498,6 +2560,7 @@ ElementRestyler::ElementRestyler(nsPresContext* aPresContext, , mResolvedChild(nullptr) , mContextsToClear(aContextsToClear) , mSwappedStructOwners(aSwappedStructOwners) + , mIsRootOfRestyle(true) #ifdef ACCESSIBILITY , mDesiredA11yNotifications(eSendAllNotifications) , mKidsDesiredA11yNotifications(mDesiredA11yNotifications) @@ -2531,6 +2594,7 @@ ElementRestyler::ElementRestyler(const ElementRestyler& aParentRestyler, , mResolvedChild(nullptr) , mContextsToClear(aParentRestyler.mContextsToClear) , mSwappedStructOwners(aParentRestyler.mSwappedStructOwners) + , mIsRootOfRestyle(false) #ifdef ACCESSIBILITY , mDesiredA11yNotifications(aParentRestyler.mKidsDesiredA11yNotifications) , mKidsDesiredA11yNotifications(mDesiredA11yNotifications) @@ -2578,6 +2642,7 @@ ElementRestyler::ElementRestyler(ParentContextFromChildFrame, , mResolvedChild(nullptr) , mContextsToClear(aParentRestyler.mContextsToClear) , mSwappedStructOwners(aParentRestyler.mSwappedStructOwners) + , mIsRootOfRestyle(false) #ifdef ACCESSIBILITY , mDesiredA11yNotifications(aParentRestyler.mDesiredA11yNotifications) , mKidsDesiredA11yNotifications(mDesiredA11yNotifications) @@ -2617,6 +2682,7 @@ ElementRestyler::ElementRestyler(nsPresContext* aPresContext, , mResolvedChild(nullptr) , mContextsToClear(aContextsToClear) , mSwappedStructOwners(aSwappedStructOwners) + , mIsRootOfRestyle(true) #ifdef ACCESSIBILITY , mDesiredA11yNotifications(eSendAllNotifications) , mKidsDesiredA11yNotifications(mDesiredA11yNotifications) @@ -2644,7 +2710,7 @@ ElementRestyler::AddLayerChangesForAnimation() // If we have a transform layer but don't have any transform style, we // probably just removed the transform but haven't destroyed the layer // yet. In this case we will add the appropriate change hint - // (nsChangeHint_AddOrRemoveTransform) when we compare style contexts + // (nsChangeHint_UpdateContainingBlock) when we compare style contexts // so we can skip adding any change hint here. (If we *were* to add // nsChangeHint_UpdateTransformLayer, ApplyRenderingChangeToTree would // complain that we're updating a transform layer without a transform). @@ -2664,7 +2730,8 @@ void ElementRestyler::CaptureChange(nsStyleContext* aOldContext, nsStyleContext* aNewContext, nsChangeHint aChangeToAssume, - uint32_t* aEqualStructs) + uint32_t* aEqualStructs, + uint32_t* aSamePointerStructs) { static_assert(nsStyleStructID_Length <= 32, "aEqualStructs is not big enough"); @@ -2678,7 +2745,8 @@ ElementRestyler::CaptureChange(nsStyleContext* aOldContext, nsChangeHint ourChange = aOldContext->CalcStyleDifference(aNewContext, mParentFrameHintsNotHandledForDescendants, - aEqualStructs); + aEqualStructs, + aSamePointerStructs); NS_ASSERTION(!(ourChange & nsChangeHint_AllReflowHints) || (ourChange & nsChangeHint_NeedReflow), "Reflow hint bits set without actually asking for a reflow"); @@ -2738,53 +2806,372 @@ private: * mSelectorsForDescendants. If the element does match one of the selectors, * we cause it to be restyled with eRestyle_Self. * - * We traverse down the flattened tree unless we find an element that - * (a) already has a pending restyle, or (b) does not have a pending restyle - * but does match one of the selectors in mSelectorsForDescendants. For (a), - * we add the current mSelectorsForDescendants into the existing restyle data, - * and for (b) we add a new pending restyle with that array. So in both - * cases, when we come to restyling this element back up in - * ProcessPendingRestyles, we will again find the eRestyle_SomeDescendants - * hint and its selectors array. + * We traverse down the frame tree (and through the flattened content tree + * when we find undisplayed content) unless we find an element that (a) already + * has a pending restyle, or (b) does not have a pending restyle but does match + * one of the selectors in mSelectorsForDescendants. For (a), we add the + * current mSelectorsForDescendants into the existing restyle data, and for (b) + * we add a new pending restyle with that array. So in both cases, when we + * come to restyling this element back up in ProcessPendingRestyles, we will + * again find the eRestyle_SomeDescendants hint and its selectors array. * * This ensures that we don't visit descendant elements and check them * against mSelectorsForDescendants more than once. */ void -ElementRestyler::AddPendingRestylesForDescendantsMatchingSelectors(Element* aElement) +ElementRestyler::ConditionallyRestyleChildren() { - if (mRestyleTracker.HasRestyleData(aElement)) { - nsRestyleHint rshint = eRestyle_SomeDescendants; - if (SelectorMatchesForRestyle(aElement)) { - rshint |= eRestyle_Self; - } - // XXX Traversing up the tree in AddPendingRestyle can be wasteful, - // as we can do this multiple times for descendants of the element - // we stopped restyling at, up in Restyle(). We should track the - // current restyle root as we traverse down here to avoid that. - RestyleHintData data; - data.mSelectorsForDescendants = mSelectorsForDescendants; - mRestyleTracker.AddPendingRestyle(aElement, rshint, nsChangeHint(0), &data); + MOZ_ASSERT(mContent == mFrame->GetContent()); + + if (!mContent->IsElement() || mSelectorsForDescendants.IsEmpty()) { return; } - if (SelectorMatchesForRestyle(aElement)) { - RestyleHintData data; - data.mSelectorsForDescendants = mSelectorsForDescendants; - mRestyleTracker.AddPendingRestyle(aElement, - eRestyle_Self | eRestyle_SomeDescendants, - nsChangeHint(0), &data); + Element* element = mContent->AsElement(); + + LOG_RESTYLE("traversing descendants of frame %s (with element %s) to " + "propagate eRestyle_SomeDescendants for these %d selectors:", + FrameTagToString(mFrame).get(), + ElementTagToString(element).get(), + int(mSelectorsForDescendants.Length())); + LOG_RESTYLE_INDENT(); +#ifdef RESTYLE_LOGGING + for (nsCSSSelector* sel : mSelectorsForDescendants) { + LOG_RESTYLE("%s", sel->RestrictedSelectorToString().get()); + } +#endif + + Element* restyleRoot = mRestyleTracker.FindClosestRestyleRoot(element); + ConditionallyRestyleChildren(mFrame, restyleRoot); +} + +void +ElementRestyler::ConditionallyRestyleChildren(nsIFrame* aFrame, + Element* aRestyleRoot) +{ + MOZ_ASSERT(aFrame->GetContent()); + MOZ_ASSERT(aFrame->GetContent()->IsElement()); + + ConditionallyRestyleUndisplayedDescendants(aFrame, aRestyleRoot); + ConditionallyRestyleContentChildren(aFrame, aRestyleRoot); +} + +// The structure of this method parallels RestyleContentChildren. +// If you update this method, you probably want to update that one too. +void +ElementRestyler::ConditionallyRestyleContentChildren(nsIFrame* aFrame, + Element* aRestyleRoot) +{ + MOZ_ASSERT(aFrame->GetContent()); + MOZ_ASSERT(aFrame->GetContent()->IsElement()); + + if (aFrame->GetContent()->HasFlag(mRestyleTracker.RootBit())) { + aRestyleRoot = aFrame->GetContent()->AsElement(); + } + + for (nsIFrame* f = aFrame; f; + f = GetNextContinuationWithSameStyle(f, f->StyleContext())) { + nsIFrame::ChildListIterator lists(f); + for (; !lists.IsDone(); lists.Next()) { + for (nsIFrame* child : lists.CurrentList()) { + // Out-of-flows are reached through their placeholders. Continuations + // and block-in-inline splits are reached through those chains. + if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && + !GetPrevContinuationWithSameStyle(child)) { + // only do frames that are in flow + if (child->GetType() == nsGkAtoms::placeholderFrame) { // placeholder + // get out of flow frame and recur there + nsIFrame* outOfFlowFrame = + nsPlaceholderFrame::GetRealFrameForPlaceholder(child); + + // |nsFrame::GetParentStyleContext| checks being out + // of flow so that this works correctly. + do { + if (GetPrevContinuationWithSameStyle(outOfFlowFrame)) { + continue; + } + if (!ConditionallyRestyle(outOfFlowFrame, aRestyleRoot)) { + ConditionallyRestyleChildren(outOfFlowFrame, aRestyleRoot); + } + } while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation())); + } else { // regular child frame + if (child != mResolvedChild) { + if (!ConditionallyRestyle(child, aRestyleRoot)) { + ConditionallyRestyleChildren(child, aRestyleRoot); + } + } + } + } + } + } + } +} + +// The structure of this method parallels RestyleUndisplayedDescendants. +// If you update this method, you probably want to update that one too. +void +ElementRestyler::ConditionallyRestyleUndisplayedDescendants( + nsIFrame* aFrame, + Element* aRestyleRoot) +{ + nsIContent* undisplayedParent; + if (MustCheckUndisplayedContent(aFrame, undisplayedParent)) { + DoConditionallyRestyleUndisplayedDescendants(undisplayedParent, + aRestyleRoot); + } +} + +// The structure of this method parallels DoRestyleUndisplayedDescendants. +// If you update this method, you probably want to update that one too. +void +ElementRestyler::DoConditionallyRestyleUndisplayedDescendants( + nsIContent* aParent, + Element* aRestyleRoot) +{ + nsCSSFrameConstructor* fc = mPresContext->FrameConstructor(); + UndisplayedNode* nodes = fc->GetAllUndisplayedContentIn(aParent); + ConditionallyRestyleUndisplayedNodes(nodes, aParent, + NS_STYLE_DISPLAY_NONE, aRestyleRoot); + nodes = fc->GetAllDisplayContentsIn(aParent); + ConditionallyRestyleUndisplayedNodes(nodes, aParent, + NS_STYLE_DISPLAY_CONTENTS, aRestyleRoot); +} + +// The structure of this method parallels RestyleUndisplayedNodes. +// If you update this method, you probably want to update that one too. +void +ElementRestyler::ConditionallyRestyleUndisplayedNodes( + UndisplayedNode* aUndisplayed, + nsIContent* aUndisplayedParent, + const uint8_t aDisplay, + Element* aRestyleRoot) +{ + MOZ_ASSERT(aDisplay == NS_STYLE_DISPLAY_NONE || + aDisplay == NS_STYLE_DISPLAY_CONTENTS); + + if (!aUndisplayed) { return; } + if (aUndisplayedParent && + aUndisplayedParent->IsElement() && + aUndisplayedParent->HasFlag(mRestyleTracker.RootBit())) { + aRestyleRoot = aUndisplayedParent->AsElement(); + } + + for (UndisplayedNode* undisplayed = aUndisplayed; undisplayed; + undisplayed = undisplayed->mNext) { + + if (!undisplayed->mContent->IsElement()) { + continue; + } + + Element* element = undisplayed->mContent->AsElement(); + + if (!ConditionallyRestyle(element, aRestyleRoot)) { + if (aDisplay == NS_STYLE_DISPLAY_NONE) { + ConditionallyRestyleContentDescendants(element, aRestyleRoot); + } else { // NS_STYLE_DISPLAY_CONTENTS + DoConditionallyRestyleUndisplayedDescendants(element, aRestyleRoot); + } + } + } +} + +void +ElementRestyler::ConditionallyRestyleContentDescendants(Element* aElement, + Element* aRestyleRoot) +{ + if (aElement->HasFlag(mRestyleTracker.RootBit())) { + aRestyleRoot = aElement; + } + FlattenedChildIterator it(aElement); for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { if (n->IsElement()) { - AddPendingRestylesForDescendantsMatchingSelectors(n->AsElement()); + Element* e = n->AsElement(); + if (!ConditionallyRestyle(e, aRestyleRoot)) { + ConditionallyRestyleContentDescendants(e, aRestyleRoot); + } } } } +bool +ElementRestyler::ConditionallyRestyle(nsIFrame* aFrame, Element* aRestyleRoot) +{ + MOZ_ASSERT(aFrame->GetContent()); + + if (!aFrame->GetContent()->IsElement()) { + return true; + } + + return ConditionallyRestyle(aFrame->GetContent()->AsElement(), aRestyleRoot); +} + +bool +ElementRestyler::ConditionallyRestyle(Element* aElement, Element* aRestyleRoot) +{ + LOG_RESTYLE("considering element %s for eRestyle_SomeDescendants", + ElementTagToString(aElement).get()); + LOG_RESTYLE_INDENT(); + + if (aElement->HasFlag(mRestyleTracker.RootBit())) { + aRestyleRoot = aElement; + } + + if (mRestyleTracker.HasRestyleData(aElement)) { + nsRestyleHint rshint = eRestyle_SomeDescendants; + if (SelectorMatchesForRestyle(aElement)) { + LOG_RESTYLE("element has existing restyle data and matches a selector"); + rshint |= eRestyle_Self; + } else { + LOG_RESTYLE("element has existing restyle data but doesn't match selectors"); + } + RestyleHintData data; + data.mSelectorsForDescendants = mSelectorsForDescendants; + mRestyleTracker.AddPendingRestyle(aElement, rshint, nsChangeHint(0), &data, + Some(aRestyleRoot)); + return true; + } + + if (SelectorMatchesForRestyle(aElement)) { + LOG_RESTYLE("element has no restyle data but matches a selector"); + RestyleHintData data; + data.mSelectorsForDescendants = mSelectorsForDescendants; + mRestyleTracker.AddPendingRestyle(aElement, + eRestyle_Self | eRestyle_SomeDescendants, + nsChangeHint(0), &data, + Some(aRestyleRoot)); + return true; + } + + return false; +} + +bool +ElementRestyler::MustCheckUndisplayedContent(nsIFrame* aFrame, + nsIContent*& aUndisplayedParent) +{ + // When the root element is display:none, we still construct *some* + // frames that have the root element as their mContent, down to the + // DocElementContainingBlock. + if (aFrame->StyleContext()->GetPseudo()) { + aUndisplayedParent = nullptr; + return aFrame == mPresContext->FrameConstructor()-> + GetDocElementContainingBlock(); + } + + aUndisplayedParent = aFrame->GetContent(); + return !!aUndisplayedParent; +} + +/** + * Helper for MoveStyleContextsForChildren, below. Appends the style + * contexts to be moved to mFrame's current (new) style context to + * aContextsToMove. + */ +bool +ElementRestyler::MoveStyleContextsForContentChildren( + nsIFrame* aParent, + nsStyleContext* aOldContext, + nsTArray& aContextsToMove) +{ + nsIFrame::ChildListIterator lists(aParent); + for (; !lists.IsDone(); lists.Next()) { + for (nsIFrame* child : lists.CurrentList()) { + // Bail out if we have out-of-flow frames. + // FIXME: It might be safe to just continue here instead of bailing out. + if (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { + return false; + } + if (GetPrevContinuationWithSameStyle(child)) { + continue; + } + // Bail out if we have placeholder frames. + // FIXME: It is probably safe to just continue here instead of bailing out. + if (nsGkAtoms::placeholderFrame == child->GetType()) { + return false; + } + nsStyleContext* sc = child->StyleContext(); + if (sc->GetParent() != aOldContext) { + return false; + } + nsIAtom* type = child->GetType(); + if (type == nsGkAtoms::letterFrame || + type == nsGkAtoms::lineFrame) { + return false; + } + if (sc->HasChildThatUsesGrandancestorStyle()) { + // XXX Not sure if we need this? + return false; + } + nsIAtom* pseudoTag = sc->GetPseudo(); + if (pseudoTag && pseudoTag != nsCSSAnonBoxes::mozNonElement) { + return false; + } + aContextsToMove.AppendElement(sc); + } + } + return true; +} + +/** + * Traverses to child elements (through the current frame's same style + * continuations, just like RestyleChildren does) and moves any style context + * for those children to be parented under mFrame's current (new) style + * context. + * + * False is returned if it encounters any conditions on the child elements' + * frames and style contexts that means it is impossible to move a + * style context. If false is returned, no style contexts will have been + * moved. + */ +bool +ElementRestyler::MoveStyleContextsForChildren(nsStyleContext* aOldContext) +{ + // Bail out if there are undisplayed or display:contents children. + // FIXME: We could get this to work if we need to. + nsIContent* undisplayedParent; + if (MustCheckUndisplayedContent(mFrame, undisplayedParent)) { + nsCSSFrameConstructor* fc = mPresContext->FrameConstructor(); + if (fc->GetAllUndisplayedContentIn(undisplayedParent) || + fc->GetAllDisplayContentsIn(undisplayedParent)) { + return false; + } + } + + nsTArray contextsToMove; + + MOZ_ASSERT(!MustReframeForBeforePseudo(), + "shouldn't need to reframe ::before as we would have had " + "eRestyle_Subtree and wouldn't get in here"); + + DebugOnly lastContinuation; + for (nsIFrame* f = mFrame; f; + f = GetNextContinuationWithSameStyle(f, f->StyleContext())) { + lastContinuation = f; + if (!MoveStyleContextsForContentChildren(f, aOldContext, contextsToMove)) { + return false; + } + } + + MOZ_ASSERT(!MustReframeForAfterPseudo(lastContinuation), + "shouldn't need to reframe ::after as we would have had " + "eRestyle_Subtree and wouldn't get in here"); + + nsStyleContext* newParent = mFrame->StyleContext(); + for (nsStyleContext* child : contextsToMove) { + // We can have duplicate entries in contextsToMove, so only move + // each style context once. + if (child->GetParent() != newParent) { + child->MoveTo(newParent); + } + } + + return true; +} + /** * Recompute style for mFrame (which should not have a prev continuation * with the same style), all of its next continuations with the same @@ -2871,6 +3258,8 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint) nsRefPtr oldContext = mFrame->StyleContext(); + nsTArray swaps; + // TEMPORARY (until bug 918064): Call RestyleSelf for each // continuation or block-in-inline sibling. @@ -2885,7 +3274,8 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint) bool haveMoreContinuations = false; for (nsIFrame* f = mFrame; f; ) { - RestyleResult thisResult = RestyleSelf(f, thisRestyleHint, &swappedStructs); + RestyleResult thisResult = + RestyleSelf(f, thisRestyleHint, &swappedStructs, swaps); if (thisResult != eRestyleResult_Stop) { // Calls to RestyleSelf for later same-style continuations must not @@ -2894,8 +3284,9 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint) if (result == eRestyleResult_Stop) { // We received eRestyleResult_Stop for earlier same-style - // continuations, and eRestyleResult_Continue(AndForceDescendants) for - // this one; go back and force-restyle the earlier continuations. + // continuations, and eRestyleResult_StopWithStyleChange or + // eRestyleResult_Continue(AndForceDescendants) for this one; go + // back and force-restyle the earlier continuations. result = thisResult; f = mFrame; continue; @@ -2969,21 +3360,56 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint) } mRestyleTracker.AddRestyleRootsIfAwaitingRestyle(descendants); - - if (mContent->IsElement()) { - if ((aRestyleHint & eRestyle_SomeDescendants) && - !mSelectorsForDescendants.IsEmpty()) { - FlattenedChildIterator it(mContent->AsElement()); - for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { - if (n->IsElement()) { - AddPendingRestylesForDescendantsMatchingSelectors(n->AsElement()); - } - } - } + if (aRestyleHint & eRestyle_SomeDescendants) { + ConditionallyRestyleChildren(); } return; } + if (result == eRestyleResult_StopWithStyleChange && + !(mHintsHandled & nsChangeHint_ReconstructFrame)) { + MOZ_ASSERT(mFrame->StyleContext() != oldContext, + "eRestyleResult_StopWithStyleChange should only be returned " + "if we got a new style context or we will reconstruct"); + MOZ_ASSERT(swappedStructs == 0, + "should have ensured we didn't swap structs when " + "returning eRestyleResult_StopWithStyleChange"); + + // We need to ensure that all of the frames that inherit their style + // from oldContext are able to be moved across to newContext. + // MoveStyleContextsForChildren will check for certain conditions + // to ensure it is safe to move all of the relevant child style + // contexts to newContext. If these conditions fail, it will + // return false, and we'll have to continue restyling. + const bool canStop = MoveStyleContextsForChildren(oldContext); + + if (canStop) { + // Send the accessibility notifications that RestyleChildren otherwise + // would have sent. + if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) { + InitializeAccessibilityNotifications(mFrame->StyleContext()); + SendAccessibilityNotifications(); + } + + mRestyleTracker.AddRestyleRootsIfAwaitingRestyle(descendants); + if (aRestyleHint & eRestyle_SomeDescendants) { + ConditionallyRestyleChildren(); + } + return; + } + + // Turns out we couldn't stop restyling here. Process the struct + // swaps that RestyleSelf would've done had we not returned + // eRestyleResult_StopWithStyleChange. + for (SwapInstruction& swap : swaps) { + LOG_RESTYLE("swapping style structs between %p and %p", + swap.mOldContext.get(), swap.mNewContext.get()); + swap.mOldContext->SwapStyleData(swap.mNewContext, swap.mStructsToSwap); + swappedStructs |= swap.mStructsToSwap; + } + swaps.Clear(); + } + if (!swappedStructs) { // If we swapped any structs from the old context, then we need to keep // it alive until after the RestyleChildren call so that we can fix up @@ -3040,8 +3466,10 @@ ElementRestyler::Restyle(nsRestyleHint aRestyleHint) * conditions that would preclude stopping restyling, and * eRestyleResult_Continue if it does. */ -ElementRestyler::RestyleResult -ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf) +void +ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf, + RestyleResult& aRestyleResult, + bool& aCanStopWithStyleChange) { // We can't handle situations where the primary style context of a frame // has not had any style data changes, but its additional style contexts @@ -3049,7 +3477,9 @@ ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf) // style contexts. if (aSelf->GetAdditionalStyleContext(0)) { LOG_RESTYLE_CONTINUE("there are additional style contexts"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } // Style changes might have moved children between the two nsLetterFrames @@ -3061,12 +3491,16 @@ ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf) if (type == nsGkAtoms::letterFrame) { LOG_RESTYLE_CONTINUE("frame is a letter frame"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } if (type == nsGkAtoms::lineFrame) { LOG_RESTYLE_CONTINUE("frame is a line frame"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } // Some style computations depend not on the parent's style, but a grandparent @@ -3079,19 +3513,25 @@ ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf) nsStyleContext* oldContext = aSelf->StyleContext(); if (oldContext->HasChildThatUsesGrandancestorStyle()) { LOG_RESTYLE_CONTINUE("the old context uses grandancestor style"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } // We ignore all situations that involve :visited style. if (oldContext->GetStyleIfVisited()) { LOG_RESTYLE_CONTINUE("the old style context has StyleIfVisited"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } nsStyleContext* parentContext = oldContext->GetParent(); if (parentContext && parentContext->GetStyleIfVisited()) { LOG_RESTYLE_CONTINUE("the old style context's parent has StyleIfVisited"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } // We also ignore frames for pseudos, as their style contexts have @@ -3102,7 +3542,9 @@ ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf) nsIAtom* pseudoTag = oldContext->GetPseudo(); if (pseudoTag && pseudoTag != nsCSSAnonBoxes::mozNonElement) { LOG_RESTYLE_CONTINUE("the old style context is for a pseudo"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } nsIFrame* parent = mFrame->GetParent(); @@ -3114,23 +3556,37 @@ ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf) nsIAtom* parentPseudoTag = parent->StyleContext()->GetPseudo(); if (parentPseudoTag && parentPseudoTag != nsCSSAnonBoxes::mozNonElement) { LOG_RESTYLE_CONTINUE("the old style context's parent is for a pseudo"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + // Parent style context pseudo-ness doesn't affect whether we can + // return eRestyleResult_StopWithStyleChange. + // + // If we had later conditions to check in this function, we would + // continue to check them, in case we set aCanStopWithStyleChange to + // false. } } - - return eRestyleResult_Stop; } -ElementRestyler::RestyleResult +void ElementRestyler::ComputeRestyleResultFromNewContext(nsIFrame* aSelf, - nsStyleContext* aNewContext) + nsStyleContext* aNewContext, + RestyleResult& aRestyleResult, + bool& aCanStopWithStyleChange) { + // If we've already determined that we must continue styling, we don't + // need to check anything. + if (aRestyleResult == eRestyleResult_Continue && !aCanStopWithStyleChange) { + return; + } + // Keep restyling if the new style context has any style-if-visted style, so // that we can avoid the style context tree surgery having to deal to deal // with visited styles. if (aNewContext->GetStyleIfVisited()) { LOG_RESTYLE_CONTINUE("the new style context has StyleIfVisited"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } // If link-related information has changed, or the pseudo for the frame has @@ -3140,11 +3596,23 @@ ElementRestyler::ComputeRestyleResultFromNewContext(nsIFrame* aSelf, if (oldContext->IsLinkContext() != aNewContext->IsLinkContext() || oldContext->RelevantLinkVisited() != aNewContext->RelevantLinkVisited() || oldContext->GetPseudo() != aNewContext->GetPseudo() || - oldContext->GetPseudoType() != aNewContext->GetPseudoType() || - oldContext->RuleNode() != aNewContext->RuleNode()) { + oldContext->GetPseudoType() != aNewContext->GetPseudoType()) { LOG_RESTYLE_CONTINUE("the old and new style contexts have different link/" - "visited/pseudo/rulenodes"); - return eRestyleResult_Continue; + "visited/pseudo"); + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; + } + + if (oldContext->RuleNode() != aNewContext->RuleNode()) { + LOG_RESTYLE_CONTINUE("the old and new style contexts have different " + "rulenodes"); + aRestyleResult = eRestyleResult_Continue; + // Continue to check other conditions if aCanStopWithStyleChange might + // still need to be set to false. + if (!aCanStopWithStyleChange) { + return; + } } // If the old and new style contexts differ in their @@ -3155,31 +3623,37 @@ ElementRestyler::ComputeRestyleResultFromNewContext(nsIFrame* aSelf, aNewContext->HasTextDecorationLines()) { LOG_RESTYLE_CONTINUE("NS_STYLE_HAS_TEXT_DECORATION_LINES differs between old" " and new style contexts"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } if (oldContext->HasPseudoElementData() != aNewContext->HasPseudoElementData()) { LOG_RESTYLE_CONTINUE("NS_STYLE_HAS_PSEUDO_ELEMENT_DATA differs between old" " and new style contexts"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } if (oldContext->ShouldSuppressLineBreak() != aNewContext->ShouldSuppressLineBreak()) { - LOG_RESTYLE_CONTINUE("NS_STYLE_SUPPRESS_LINEBREAK differes" + LOG_RESTYLE_CONTINUE("NS_STYLE_SUPPRESS_LINEBREAK differs" "between old and new style contexts"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } if (oldContext->IsInDisplayNoneSubtree() != aNewContext->IsInDisplayNoneSubtree()) { LOG_RESTYLE_CONTINUE("NS_STYLE_IN_DISPLAY_NONE_SUBTREE differs between old" " and new style contexts"); - return eRestyleResult_Continue; + aRestyleResult = eRestyleResult_Continue; + aCanStopWithStyleChange = false; + return; } - - return eRestyleResult_Stop; } bool @@ -3206,10 +3680,25 @@ ElementRestyler::MustRestyleSelf(nsRestyleHint aRestyleHint, SelectorMatchesForRestyle(aElement)); } +bool +ElementRestyler::CanReparentStyleContext(nsRestyleHint aRestyleHint) +{ + // If we had any restyle hints other than the ones listed below, + // which don't control whether the current frame/element needs + // a new style context by looking up a new rule node, or if + // we are reconstructing the entire rule tree, then we can't + // use ReparentStyleContext. + return !(aRestyleHint & ~(eRestyle_Force | + eRestyle_ForceDescendants | + eRestyle_SomeDescendants)) && + !mPresContext->StyleSet()->IsInRuleTreeReconstruct(); +} + ElementRestyler::RestyleResult ElementRestyler::RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint, - uint32_t* aSwappedStructs) + uint32_t* aSwappedStructs, + nsTArray& aSwaps) { MOZ_ASSERT(!(aRestyleHint & eRestyle_LaterSiblings), "eRestyle_LaterSiblings must not be part of aRestyleHint"); @@ -3227,14 +3716,44 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, RestyleManager::RestyleHintToString(aRestyleHint).get()); LOG_RESTYLE_INDENT(); - RestyleResult result; + // Initially assume that it is safe to stop restyling. + // + // Throughout most of this function, we update the following two variables + // independently. |result| is set to eRestyleResult_Continue when we + // detect a condition that would not allow us to return eRestyleResult_Stop. + // |canStopWithStyleChange| is set to false when we detect a condition + // that would not allow us to return eRestyleResult_StopWithStyleChange. + // + // Towards the end of this function, we reconcile these two variables -- + // if |canStopWithStyleChange| is true, we convert |result| into + // eRestyleResult_StopWithStyleChange. + RestyleResult result = eRestyleResult_Stop; + bool canStopWithStyleChange = true; if (aRestyleHint & ~eRestyle_SomeDescendants) { + // If we are doing any restyling of the current element, or if we're + // forced to continue, we must. result = eRestyleResult_Continue; - } else { - result = ComputeRestyleResultFromFrame(aSelf); + + // If we have to restyle children, we can't return + // eRestyleResult_StopWithStyleChange. + if (aRestyleHint & (eRestyle_Subtree | eRestyle_Force | + eRestyle_ForceDescendants)) { + canStopWithStyleChange = false; + } } + // We only consider returning eRestyleResult_StopWithStyleChange if this + // is the root of the restyle. (Otherwise, we would need to track the + // style changes of the ancestors we just restyled.) + if (!mIsRootOfRestyle) { + canStopWithStyleChange = false; + } + + // Look at the frame and its current style context for conditions + // that would change our RestyleResult. + ComputeRestyleResultFromFrame(aSelf, result, canStopWithStyleChange); + nsChangeHint assumeDifferenceHint = NS_STYLE_HINT_NONE; nsRefPtr oldContext = aSelf->StyleContext(); nsStyleSet* styleSet = mPresContext->StyleSet(); @@ -3282,6 +3801,7 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, LOG_RESTYLE_CONTINUE("we had a provider frame"); // Continue restyling past the odd style context inheritance. result = eRestyleResult_Continue; + canStopWithStyleChange = false; } if (providerFrame != aSelf->GetParent()) { @@ -3317,8 +3837,7 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, else { Element* element = ElementForStyleContext(mParentContent, aSelf, pseudoType); if (!MustRestyleSelf(aRestyleHint, element)) { - if (!(aRestyleHint & ~(eRestyle_Force | eRestyle_ForceDescendants)) && - !styleSet->IsInRuleTreeReconstruct()) { + if (CanReparentStyleContext(aRestyleHint)) { LOG_RESTYLE("reparenting style context"); newContext = styleSet->ReparentStyleContext(oldContext, parentContext, element); @@ -3420,6 +3939,7 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, newContext = oldContext; // Never consider stopping restyling at the root. result = eRestyleResult_Continue; + canStopWithStyleChange = false; } } @@ -3429,23 +3949,23 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, (const char*) ""); if (newContext != oldContext) { - if (result == eRestyleResult_Stop) { - if (oldContext->IsShared()) { - // If the old style context was shared, then we can't return - // eRestyleResult_Stop and patch its parent to point to the - // new parent style context, as that change might not be valid - // for the other frames sharing the style context. - LOG_RESTYLE_CONTINUE("the old style context is shared"); - result = eRestyleResult_Continue; - } else { - // Look at some details of the new style context to see if it would - // be safe to stop restyling, if we discover it has the same style - // data as the old style context. - result = ComputeRestyleResultFromNewContext(aSelf, newContext); - } + if (oldContext->IsShared()) { + // If the old style context was shared, then we can't return + // eRestyleResult_Stop and patch its parent to point to the + // new parent style context, as that change might not be valid + // for the other frames sharing the style context. + LOG_RESTYLE_CONTINUE("the old style context is shared"); + result = eRestyleResult_Continue; } + // Look at some details of the new style context to see if it would + // be safe to stop restyling, if we discover it has the same style + // data as the old style context. + ComputeRestyleResultFromNewContext(aSelf, newContext, + result, canStopWithStyleChange); + uint32_t equalStructs = 0; + uint32_t samePointerStructs = 0; if (copyFromContinuation) { // In theory we should know whether there was any style data difference, @@ -3455,7 +3975,8 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, // determine whether it is safe to stop restyling. if (result == eRestyleResult_Stop) { oldContext->CalcStyleDifference(newContext, nsChangeHint(0), - &equalStructs); + &equalStructs, + &samePointerStructs); if (equalStructs != NS_STYLE_INHERIT_MASK) { // At least one struct had different data in it, so we must // continue restyling children. @@ -3472,9 +3993,10 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, if (changedStyle) { LOG_RESTYLE_CONTINUE("TryStartingTransition changed the new style context"); result = eRestyleResult_Continue; + canStopWithStyleChange = false; } CaptureChange(oldContext, newContext, assumeDifferenceHint, - &equalStructs); + &equalStructs, &samePointerStructs); if (equalStructs != NS_STYLE_INHERIT_MASK) { // At least one struct had different data in it, so we must // continue restyling children. @@ -3485,6 +4007,25 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, } } + if (canStopWithStyleChange) { + // If any inherited struct pointers are different, or if any + // reset struct pointers are different and we have descendants + // that rely on those reset struct pointers, we can't return + // eRestyleResult_StopWithStyleChange. + if ((samePointerStructs & NS_STYLE_INHERITED_STRUCT_MASK) != + NS_STYLE_INHERITED_STRUCT_MASK) { + LOG_RESTYLE("can't return eRestyleResult_StopWithStyleChange since " + "there is different inherited data"); + canStopWithStyleChange = false; + } else if ((samePointerStructs & NS_STYLE_RESET_STRUCT_MASK) != + NS_STYLE_RESET_STRUCT_MASK && + oldContext->HasChildThatUsesResetStyle()) { + LOG_RESTYLE("can't return eRestyleResult_StopWithStyleChange since " + "there is different reset data and descendants use it"); + canStopWithStyleChange = false; + } + } + if (result == eRestyleResult_Stop) { // Since we currently have eRestyleResult_Stop, we know at this // point that all of our style structs are equal in terms of styles. @@ -3504,11 +4045,14 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, // // (b) when we were unable to swap the structs on the parent because // either or both of the old parent and new parent are shared. + // + // FIXME This loop could be rewritten as bit operations on + // oldContext->mBits and samePointerStructs. for (nsStyleStructID sid = nsStyleStructID(0); sid < nsStyleStructID_Length; sid = nsStyleStructID(sid + 1)) { if (oldContext->HasCachedInheritedStyleData(sid) && - !oldContext->HasSameCachedStyleData(newContext, sid)) { + !(samePointerStructs & nsCachedStyleData::GetBitForSID(sid))) { LOG_RESTYLE_CONTINUE("there are different struct pointers"); result = eRestyleResult_Continue; break; @@ -3516,6 +4060,21 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, } } + // From this point we no longer do any assignments of + // eRestyleResult_Continue to |result|. If canStopWithStyleChange is true, + // it means that we can convert |result| (whether it is + // eRestyleResult_Continue or eRestyleResult_Stop) into + // eRestyleResult_StopWithStyleChange. + if (canStopWithStyleChange) { + LOG_RESTYLE("converting %s into eRestyleResult_StopWithStyleChange", + RestyleResultToString(result).get()); + result = eRestyleResult_StopWithStyleChange; + } + + if (aRestyleHint & eRestyle_ForceDescendants) { + result = eRestyleResult_ContinueAndForceDescendants; + } + if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) { // If the frame gets regenerated, let it keep its old context, // which is important to maintain various invariants about @@ -3539,10 +4098,20 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, LOG_RESTYLE("not swapping style structs, since the new context is " "shared"); } else { - LOG_RESTYLE("swapping style structs between %p and %p", - oldContext.get(), newContext.get()); - oldContext->SwapStyleData(newContext, equalStructs); - *aSwappedStructs |= equalStructs; + if (result == eRestyleResult_StopWithStyleChange) { + LOG_RESTYLE("recording a style struct swap between %p and %p to " + "do if eRestyleResult_StopWithStyleChange fails", + oldContext.get(), newContext.get()); + SwapInstruction* swap = aSwaps.AppendElement(); + swap->mOldContext = oldContext; + swap->mNewContext = newContext; + swap->mStructsToSwap = equalStructs; + } else { + LOG_RESTYLE("swapping style structs between %p and %p", + oldContext.get(), newContext.get()); + oldContext->SwapStyleData(newContext, equalStructs); + *aSwappedStructs |= equalStructs; + } #ifdef RESTYLE_LOGGING uint32_t structs = RestyleManager::StructsToLog() & equalStructs; if (structs) { @@ -3569,6 +4138,10 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, // parent, but we don't have that information here. mSwappedStructOwners.AppendElement(newContext->GetParent()); } + } else { + if (aRestyleHint & eRestyle_ForceDescendants) { + result = eRestyleResult_ContinueAndForceDescendants; + } } oldContext = nullptr; @@ -3591,7 +4164,10 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, Element* element = extraPseudoType != nsCSSPseudoElements::ePseudo_AnonBox ? mContent->AsElement() : nullptr; if (!MustRestyleSelf(aRestyleHint, element)) { - if (styleSet->IsInRuleTreeReconstruct()) { + if (CanReparentStyleContext(aRestyleHint)) { + newExtraContext = + styleSet->ReparentStyleContext(oldExtraContext, newContext, element); + } else { // Use ResolveStyleWithReplacement as a substitute for // ReparentStyleContext that rebuilds the path in the rule tree // rather than reusing the rule node, as we need to do during a @@ -3606,9 +4182,6 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, styleSet->ResolveStyleWithReplacement(element, pseudoElement, newContext, oldExtraContext, nsRestyleHint(0)); - } else { - newExtraContext = - styleSet->ReparentStyleContext(oldExtraContext, newContext, element); } } else if (extraPseudoType == nsCSSPseudoElements::ePseudo_AnonBox) { newExtraContext = styleSet->ResolveAnonymousBoxStyle(extraPseudoTag, @@ -3631,8 +4204,9 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, if (oldExtraContext != newExtraContext) { uint32_t equalStructs; + uint32_t samePointerStructs; CaptureChange(oldExtraContext, newExtraContext, assumeDifferenceHint, - &equalStructs); + &equalStructs, &samePointerStructs); if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) { LOG_RESTYLE("setting new extra style context"); aSelf->SetAdditionalStyleContext(contextIndex, newExtraContext); @@ -3642,10 +4216,6 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf, } } - if (aRestyleHint & eRestyle_ForceDescendants) { - result = eRestyleResult_ContinueAndForceDescendants; - } - LOG_RESTYLE("returning %s", RestyleResultToString(result).get()); return result; @@ -3729,10 +4299,12 @@ ElementRestyler::RestyleChildrenOfDisplayContentsElement( const bool mightReframePseudos = aRestyleHint & eRestyle_Subtree; DoRestyleUndisplayedDescendants(nsRestyleHint(0), mContent, aNewContext); if (!(mHintsHandled & nsChangeHint_ReconstructFrame) && mightReframePseudos) { - MaybeReframeForBeforePseudo(aParentFrame, nullptr, mContent, aNewContext); + MaybeReframeForPseudo(nsCSSPseudoElements::ePseudo_before, + aParentFrame, nullptr, mContent, aNewContext); } if (!(mHintsHandled & nsChangeHint_ReconstructFrame) && mightReframePseudos) { - MaybeReframeForAfterPseudo(aParentFrame, nullptr, mContent, aNewContext); + MaybeReframeForPseudo(nsCSSPseudoElements::ePseudo_after, + aParentFrame, nullptr, mContent, aNewContext); } if (!(mHintsHandled & nsChangeHint_ReconstructFrame)) { InitializeAccessibilityNotifications(aNewContext); @@ -3744,9 +4316,7 @@ ElementRestyler::RestyleChildrenOfDisplayContentsElement( // XXX ContentIsDescendantOf check below) nsIFrame::ChildListIterator lists(aParentFrame); for ( ; !lists.IsDone(); lists.Next()) { - nsFrameList::Enumerator childFrames(lists.CurrentList()); - for (; !childFrames.AtEnd(); childFrames.Next()) { - nsIFrame* f = childFrames.get(); + for (nsIFrame* f : lists.CurrentList()) { if (nsContentUtils::ContentIsDescendantOf(f->GetContent(), mContent) && !f->GetPrevContinuation()) { if (!(f->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { @@ -3821,8 +4391,16 @@ ElementRestyler::ComputeStyleChangeFor(nsIFrame* aFrame, selectorsForDescendants.AppendElements( aRestyleHintData.mSelectorsForDescendants); nsTArray visibleKidsOfHiddenElement; - for (nsIFrame* ibSibling = aFrame; ibSibling; - ibSibling = GetNextBlockInInlineSibling(propTable, ibSibling)) { + nsIFrame* nextIBSibling; + for (nsIFrame* ibSibling = aFrame; ibSibling; ibSibling = nextIBSibling) { + nextIBSibling = GetNextBlockInInlineSibling(propTable, ibSibling); + + if (nextIBSibling) { + // Don't allow some ib-split siblings to be processed with + // eRestyleResult_StopWithStyleChange and others not. + aRestyleHint |= eRestyle_Force; + } + // Outer loop over ib-split siblings for (nsIFrame* cont = ibSibling; cont; cont = cont->GetNextContinuation()) { if (GetPrevContinuationWithSameStyle(cont)) { @@ -3853,28 +4431,20 @@ ElementRestyler::ComputeStyleChangeFor(nsIFrame* aFrame, } } +// The structure of this method parallels ConditionallyRestyleUndisplayedDescendants. +// If you update this method, you probably want to update that one too. void ElementRestyler::RestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint) { - // When the root element is display:none, we still construct *some* - // frames that have the root element as their mContent, down to the - // DocElementContainingBlock. - bool checkUndisplayed; nsIContent* undisplayedParent; - if (mFrame->StyleContext()->GetPseudo()) { - checkUndisplayed = mFrame == mPresContext->FrameConstructor()-> - GetDocElementContainingBlock(); - undisplayedParent = nullptr; - } else { - checkUndisplayed = !!mFrame->GetContent(); - undisplayedParent = mFrame->GetContent(); - } - if (checkUndisplayed) { + if (MustCheckUndisplayedContent(mFrame, undisplayedParent)) { DoRestyleUndisplayedDescendants(aChildRestyleHint, undisplayedParent, mFrame->StyleContext()); } } +// The structure of this method parallels DoConditionallyRestyleUndisplayedDescendants. +// If you update this method, you probably want to update that one too. void ElementRestyler::DoRestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint, nsIContent* aParent, @@ -3889,6 +4459,8 @@ ElementRestyler::DoRestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint aParentContext, NS_STYLE_DISPLAY_CONTENTS); } +// The structure of this method parallels ConditionallyRestyleUndisplayedNodes. +// If you update this method, you probably want to update that one too. void ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint, UndisplayedNode* aUndisplayed, @@ -3936,11 +4508,12 @@ ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint, if (MustRestyleSelf(thisChildHint, element)) { undisplayedContext = styleSet->ResolveStyleFor(element, aParentContext, mTreeMatchContext); - } else if (thisChildHint || - styleSet->IsInRuleTreeReconstruct()) { - // XXX Should the above condition ignore eRestyle_Force(Descendants) - // like the corresponding check in RestyleSelf? - + } else if (CanReparentStyleContext(thisChildHint)) { + undisplayedContext = + styleSet->ReparentStyleContext(undisplayed->mStyle, + aParentContext, + element); + } else { // Use ResolveStyleWithReplacement either for actual // replacements, or as a substitute for ReparentStyleContext // that rebuilds the path in the rule tree rather than reusing @@ -3952,11 +4525,6 @@ ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint, aParentContext, undisplayed->mStyle, rshint); - } else { - undisplayedContext = - styleSet->ReparentStyleContext(undisplayed->mStyle, - aParentContext, - element); } const nsStyleDisplay* display = undisplayedContext->StyleDisplay(); if (display->mDisplay != aDisplay) { @@ -3980,43 +4548,9 @@ ElementRestyler::RestyleUndisplayedNodes(nsRestyleHint aChildRestyleHint, void ElementRestyler::MaybeReframeForBeforePseudo() { - MaybeReframeForBeforePseudo(mFrame, mFrame, mFrame->GetContent(), - mFrame->StyleContext()); -} - -void -ElementRestyler::MaybeReframeForBeforePseudo(nsIFrame* aGenConParentFrame, - nsIFrame* aFrame, - nsIContent* aContent, - nsStyleContext* aStyleContext) -{ - // Make sure not to do this for pseudo-frames or frames that - // can't have generated content. - nsContainerFrame* cif; - if (!aStyleContext->GetPseudo() && - ((aGenConParentFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT) || - // Our content insertion frame might have gotten flagged. - ((cif = aGenConParentFrame->GetContentInsertionFrame()) && - (cif->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT)))) { - // Check for a ::before pseudo style and the absence of a ::before content, - // but only if aFrame is null or is the first continuation/ib-split. - if (!aFrame || - nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame)) { - // Checking for a ::before frame is cheaper than getting the - // ::before style context. - if (!nsLayoutUtils::GetBeforeFrameForContent(aGenConParentFrame, aContent) && - nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext, - nsCSSPseudoElements::ePseudo_before, - mPresContext)) { - // Have to create the new ::before frame. - LOG_RESTYLE("MaybeReframeForBeforePseudo, appending " - "nsChangeHint_ReconstructFrame"); - NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame); - mChangeList->AppendChange(aFrame, aContent, - nsChangeHint_ReconstructFrame); - } - } - } + MaybeReframeForPseudo(nsCSSPseudoElements::ePseudo_before, + mFrame, mFrame, mFrame->GetContent(), + mFrame->StyleContext()); } /** @@ -4027,43 +4561,91 @@ void ElementRestyler::MaybeReframeForAfterPseudo(nsIFrame* aFrame) { MOZ_ASSERT(aFrame); - MaybeReframeForAfterPseudo(aFrame, aFrame, aFrame->GetContent(), - aFrame->StyleContext()); + MaybeReframeForPseudo(nsCSSPseudoElements::ePseudo_after, + aFrame, aFrame, aFrame->GetContent(), + aFrame->StyleContext()); } -void -ElementRestyler::MaybeReframeForAfterPseudo(nsIFrame* aGenConParentFrame, - nsIFrame* aFrame, - nsIContent* aContent, - nsStyleContext* aStyleContext) +#ifdef DEBUG +bool +ElementRestyler::MustReframeForBeforePseudo() { - // Make sure not to do this for pseudo-frames or frames that - // can't have generated content. - nsContainerFrame* cif; - if (!aStyleContext->GetPseudo() && - ((aGenConParentFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT) || - // Our content insertion frame might have gotten flagged. - ((cif = aGenConParentFrame->GetContentInsertionFrame()) && - (cif->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT)))) { - // Check for an ::after pseudo style and the absence of an ::after content, - // but only if aFrame is null or is the last continuation/ib-split. - if (!aFrame || - !nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) { - // Checking for an ::after frame is cheaper than getting the - // ::after style context. - if (!nsLayoutUtils::GetAfterFrameForContent(aGenConParentFrame, aContent) && - nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext, - nsCSSPseudoElements::ePseudo_after, - mPresContext)) { - // Have to create the new ::after frame. - LOG_RESTYLE("MaybeReframeForAfterPseudo, appending " - "nsChangeHint_ReconstructFrame"); - NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame); - mChangeList->AppendChange(aFrame, mContent, - nsChangeHint_ReconstructFrame); - } + return MustReframeForPseudo(nsCSSPseudoElements::ePseudo_before, + mFrame, mFrame, mFrame->GetContent(), + mFrame->StyleContext()); +} + +bool +ElementRestyler::MustReframeForAfterPseudo(nsIFrame* aFrame) +{ + MOZ_ASSERT(aFrame); + return MustReframeForPseudo(nsCSSPseudoElements::ePseudo_after, + aFrame, aFrame, aFrame->GetContent(), + aFrame->StyleContext()); +} +#endif + +void +ElementRestyler::MaybeReframeForPseudo(nsCSSPseudoElements::Type aPseudoType, + nsIFrame* aGenConParentFrame, + nsIFrame* aFrame, + nsIContent* aContent, + nsStyleContext* aStyleContext) +{ + if (MustReframeForPseudo(aPseudoType, aGenConParentFrame, aFrame, aContent, + aStyleContext)) { + // Have to create the new ::before/::after frame. + LOG_RESTYLE("MaybeReframeForPseudo, appending " + "nsChangeHint_ReconstructFrame"); + NS_UpdateHint(mHintsHandled, nsChangeHint_ReconstructFrame); + mChangeList->AppendChange(aFrame, aContent, nsChangeHint_ReconstructFrame); + } +} + +bool +ElementRestyler::MustReframeForPseudo(nsCSSPseudoElements::Type aPseudoType, + nsIFrame* aGenConParentFrame, + nsIFrame* aFrame, + nsIContent* aContent, + nsStyleContext* aStyleContext) +{ + MOZ_ASSERT(aPseudoType == nsCSSPseudoElements::ePseudo_before || + aPseudoType == nsCSSPseudoElements::ePseudo_after); + + // Make sure not to do this for pseudo-frames... + if (aStyleContext->GetPseudo()) { + return false; + } + + // ... or frames that can't have generated content. + if (!(aGenConParentFrame->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT)) { + // Our content insertion frame might have gotten flagged. + nsContainerFrame* cif = aGenConParentFrame->GetContentInsertionFrame(); + if (!cif || !(cif->GetStateBits() & NS_FRAME_MAY_HAVE_GENERATED_CONTENT)) { + return false; } } + + if (aPseudoType == nsCSSPseudoElements::ePseudo_before) { + // Check for a ::before pseudo style and the absence of a ::before content, + // but only if aFrame is null or is the first continuation/ib-split. + if ((aFrame && !nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame)) || + nsLayoutUtils::GetBeforeFrameForContent(aGenConParentFrame, aContent)) { + return false; + } + } else { + // Similarly for ::after, but check for being the last continuation/ + // ib-split. + if ((aFrame && nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) || + nsLayoutUtils::GetAfterFrameForContent(aGenConParentFrame, aContent)) { + return false; + } + } + + // Checking for a ::before frame (which we do above) is cheaper than getting + // the ::before style context here. + return nsLayoutUtils::HasPseudoStyle(aContent, aStyleContext, aPseudoType, + mPresContext); } void @@ -4107,6 +4689,8 @@ ElementRestyler::InitializeAccessibilityNotifications(nsStyleContext* aNewContex #endif } +// The structure of this method parallels ConditionallyRestyleContentChildren. +// If you update this method, you probably want to update that one too. void ElementRestyler::RestyleContentChildren(nsIFrame* aParent, nsRestyleHint aChildRestyleHint) @@ -4119,9 +4703,7 @@ ElementRestyler::RestyleContentChildren(nsIFrame* aParent, ancestorPusher.PushAncestorAndStyleScope(mContent); } for (; !lists.IsDone(); lists.Next()) { - nsFrameList::Enumerator childFrames(lists.CurrentList()); - for (; !childFrames.AtEnd(); childFrames.Next()) { - nsIFrame* child = childFrames.get(); + for (nsIFrame* child : lists.CurrentList()) { // Out-of-flows are reached through their placeholders. Continuations // and block-in-inline splits are reached through those chains. if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && @@ -4491,6 +5073,9 @@ ElementRestyler::RestyleResultToString(RestyleResult aRestyleResult) case eRestyleResult_Stop: result.AssignLiteral("eRestyleResult_Stop"); break; + case eRestyleResult_StopWithStyleChange: + result.AssignLiteral("eRestyleResult_StopWithStyleChange"); + break; case eRestyleResult_Continue: result.AssignLiteral("eRestyleResult_Continue"); break; diff --git a/layout/base/RestyleManager.h b/layout/base/RestyleManager.h index 13afc16188..96d64427a6 100644 --- a/layout/base/RestyleManager.h +++ b/layout/base/RestyleManager.h @@ -49,6 +49,8 @@ private: { MOZ_ASSERT(!mReframingStyleContexts, "temporary member should be nulled out before destruction"); + MOZ_ASSERT(!mAnimationsWithDestroyedFrame, + "leaving dangling pointers from AnimationsWithDestroyedFrame"); } public: @@ -89,6 +91,10 @@ public: int32_t aModType, const nsAttrValue* aOldValue); + // Get an integer that increments every time we process pending restyles. + // The value is never 0. + uint32_t GetRestyleGeneration() const { return mRestyleGeneration; } + // Get an integer that increments every time there is a style change // as a result of a change to the :hover content state. uint32_t GetHoverGeneration() const { return mHoverGeneration; } @@ -127,6 +133,10 @@ public: */ nsresult ReparentStyleContext(nsIFrame* aFrame); + void ClearSelectors() { + mPendingRestyles.ClearSelectors(); + } + private: // Used when restyling an element with a frame. void ComputeAndProcessStyleChange(nsIFrame* aFrame, @@ -243,6 +253,65 @@ public: nsStyleContext* aOldStyleContext, nsRefPtr* aNewStyleContext /* inout */); + // AnimationsWithDestroyedFrame is used to stop animations on elements that + // have no frame at the end of the restyling process. + // It only lives during the restyling process. + class MOZ_STACK_CLASS AnimationsWithDestroyedFrame final { + public: + // Construct a AnimationsWithDestroyedFrame object. The caller must + // ensure that aRestyleManager lives at least as long as the + // object. (This is generally easy since the caller is typically a + // method of RestyleManager.) + explicit AnimationsWithDestroyedFrame(RestyleManager* aRestyleManager); + ~AnimationsWithDestroyedFrame() + { + } + + // This method takes the content node for the generated content for + // animation on ::before and ::after, rather than the content node for + // the real element. + void Put(nsIContent* aContent, nsStyleContext* aStyleContext) { + MOZ_ASSERT(aContent); + nsCSSPseudoElements::Type pseudoType = aStyleContext->GetPseudoType(); + if (pseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) { + mContents.AppendElement(aContent); + } else if (pseudoType == nsCSSPseudoElements::ePseudo_before) { + MOZ_ASSERT(aContent->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentbefore); + mBeforeContents.AppendElement(aContent->GetParent()); + } else if (pseudoType == nsCSSPseudoElements::ePseudo_after) { + MOZ_ASSERT(aContent->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentafter); + mAfterContents.AppendElement(aContent->GetParent()); + } + } + + void StopAnimationsForElementsWithoutFrames(); + + private: + void StopAnimationsWithoutFrame(nsTArray>& aArray, + nsCSSPseudoElements::Type aPseudoType); + + RestyleManager* mRestyleManager; + AutoRestore mRestorePointer; + + // Below three arrays might include elements that have already had their + // animations stopped. + // + // mBeforeContents and mAfterContents hold the real element rather than + // the content node for the generated content (which might change during + // a reframe) + nsTArray> mContents; + nsTArray> mBeforeContents; + nsTArray> mAfterContents; + }; + + /** + * Return the current AnimationsWithDestroyedFrame struct, or null if we're + * not currently in a restyling operation. + */ + AnimationsWithDestroyedFrame* GetAnimationsWithDestroyedFrame() { + return mAnimationsWithDestroyedFrame; + } + private: void RestyleForEmptyChange(Element* aContainer); @@ -454,6 +523,12 @@ private: // Fast-path the common case (esp. for the animation restyle // tracker) of not having anything to do. if (aRestyleTracker.Count() || ShouldStartRebuildAllFor(aRestyleTracker)) { + if (++mRestyleGeneration == 0) { + // Keep mRestyleGeneration from being 0, since that's what + // nsPresContext::GetRestyleGeneration returns when it no + // longer has a RestyleManager. + ++mRestyleGeneration; + } aRestyleTracker.DoProcessRestyles(); } } @@ -474,6 +549,7 @@ private: bool mSkipAnimationRules : 1; bool mHavePendingNonAnimationRestyles : 1; + uint32_t mRestyleGeneration; uint32_t mHoverGeneration; nsChangeHint mRebuildAllExtraHint; nsRestyleHint mRebuildAllRestyleHint; @@ -487,6 +563,7 @@ private: uint64_t mAnimationGeneration; ReframingStyleContexts* mReframingStyleContexts; + AnimationsWithDestroyedFrame* mAnimationsWithDestroyedFrame; RestyleTracker mPendingRestyles; @@ -616,9 +693,13 @@ private: // the work of the earlier values is also done. enum RestyleResult { - // do not restyle children + // we left the old style context on the frame; do not restyle children eRestyleResult_Stop = 1, + // we got a new style context on this frame, but we know that children + // do not depend on the changed values; do not restyle children + eRestyleResult_StopWithStyleChange, + // continue restyling children eRestyleResult_Continue, @@ -626,12 +707,20 @@ private: eRestyleResult_ContinueAndForceDescendants }; + struct SwapInstruction + { + nsRefPtr mOldContext; + nsRefPtr mNewContext; + uint32_t mStructsToSwap; + }; + /** * First half of Restyle(). */ RestyleResult RestyleSelf(nsIFrame* aSelf, nsRestyleHint aRestyleHint, - uint32_t* aSwappedStructs); + uint32_t* aSwappedStructs, + nsTArray& aSwaps); /** * Restyle the children of this frame (and, in turn, their children). @@ -655,26 +744,47 @@ private: */ bool MustRestyleSelf(nsRestyleHint aRestyleHint, Element* aElement); + /** + * Returns true iff aRestyleHint indicates that we can call + * ReparentStyleContext rather than any other restyling method of + * nsStyleSet that looks up a new rule node, and if we are + * not in the process of reconstructing the whole rule tree. + * This is used to check whether it is appropriate to call + * ReparentStyleContext. + */ + bool CanReparentStyleContext(nsRestyleHint aRestyleHint); + /** * Helpers for Restyle(). */ void AddLayerChangesForAnimation(); + bool MoveStyleContextsForContentChildren(nsIFrame* aParent, + nsStyleContext* aOldContext, + nsTArray& aContextsToMove); + bool MoveStyleContextsForChildren(nsStyleContext* aOldContext); + /** * Helpers for RestyleSelf(). */ void CaptureChange(nsStyleContext* aOldContext, nsStyleContext* aNewContext, nsChangeHint aChangeToAssume, - uint32_t* aEqualStructs); - RestyleResult ComputeRestyleResultFromFrame(nsIFrame* aSelf); - RestyleResult ComputeRestyleResultFromNewContext(nsIFrame* aSelf, - nsStyleContext* aNewContext); + uint32_t* aEqualStructs, + uint32_t* aSamePointerStructs); + void ComputeRestyleResultFromFrame(nsIFrame* aSelf, + RestyleResult& aRestyleResult, + bool& aCanStopWithStyleChange); + void ComputeRestyleResultFromNewContext(nsIFrame* aSelf, + nsStyleContext* aNewContext, + RestyleResult& aRestyleResult, + bool& aCanStopWithStyleChange); - /** - * Helpers for RestyleChildren(). - */ + // Helpers for RestyleChildren(). void RestyleUndisplayedDescendants(nsRestyleHint aChildRestyleHint); + bool MustCheckUndisplayedContent(nsIFrame* aFrame, + nsIContent*& aUndisplayedParent); + /** * In the following two methods, aParentStyleContext is either * mFrame->StyleContext() if we have a frame, or a display:contents @@ -689,15 +799,21 @@ private: nsStyleContext* aParentStyleContext, const uint8_t aDisplay); void MaybeReframeForBeforePseudo(); - void MaybeReframeForBeforePseudo(nsIFrame* aGenConParentFrame, - nsIFrame* aFrame, - nsIContent* aContent, - nsStyleContext* aStyleContext); void MaybeReframeForAfterPseudo(nsIFrame* aFrame); - void MaybeReframeForAfterPseudo(nsIFrame* aGenConParentFrame, - nsIFrame* aFrame, - nsIContent* aContent, - nsStyleContext* aStyleContext); + void MaybeReframeForPseudo(nsCSSPseudoElements::Type aPseudoType, + nsIFrame* aGenConParentFrame, + nsIFrame* aFrame, + nsIContent* aContent, + nsStyleContext* aStyleContext); +#ifdef DEBUG + bool MustReframeForBeforePseudo(); + bool MustReframeForAfterPseudo(nsIFrame* aFrame); +#endif + bool MustReframeForPseudo(nsCSSPseudoElements::Type aPseudoType, + nsIFrame* aGenConParentFrame, + nsIFrame* aFrame, + nsIContent* aContent, + nsStyleContext* aStyleContext); void RestyleContentChildren(nsIFrame* aParent, nsRestyleHint aChildRestyleHint); void InitializeAccessibilityNotifications(nsStyleContext* aNewContext); @@ -715,7 +831,27 @@ private: eNotifyHidden }; - void AddPendingRestylesForDescendantsMatchingSelectors(Element* aElement); + // These methods handle the eRestyle_SomeDescendants hint by traversing + // down the frame tree (and then when reaching undisplayed content, + // the flattened content tree) find elements that match a selector + // in mSelectorsForDescendants and call AddPendingRestyle for them. + void ConditionallyRestyleChildren(); + void ConditionallyRestyleChildren(nsIFrame* aFrame, + Element* aRestyleRoot); + void ConditionallyRestyleContentChildren(nsIFrame* aFrame, + Element* aRestyleRoot); + void ConditionallyRestyleUndisplayedDescendants(nsIFrame* aFrame, + Element* aRestyleRoot); + void DoConditionallyRestyleUndisplayedDescendants(nsIContent* aParent, + Element* aRestyleRoot); + void ConditionallyRestyleUndisplayedNodes(UndisplayedNode* aUndisplayed, + nsIContent* aUndisplayedParent, + const uint8_t aDisplay, + Element* aRestyleRoot); + void ConditionallyRestyleContentDescendants(Element* aElement, + Element* aRestyleRoot); + bool ConditionallyRestyle(nsIFrame* aFrame, Element* aRestyleRoot); + bool ConditionallyRestyle(Element* aElement, Element* aRestyleRoot); #ifdef RESTYLE_LOGGING int32_t& LoggingDepth() { return mLoggingDepth; } @@ -755,6 +891,8 @@ private: // stay alive until the end of the restyle. (See comment in // ElementRestyler::Restyle.) nsTArray>& mSwappedStructOwners; + // Whether this is the root of the restyle. + bool mIsRootOfRestyle; #ifdef ACCESSIBILITY const DesiredA11yNotifications mDesiredA11yNotifications; diff --git a/layout/base/RestyleTracker.cpp b/layout/base/RestyleTracker.cpp index 02d29d2ac3..f3c15b9ac9 100644 --- a/layout/base/RestyleTracker.cpp +++ b/layout/base/RestyleTracker.cpp @@ -162,7 +162,8 @@ CollectRestyles(nsISupports* aElement, // Unset the restyle bits now, so if they get readded later as we // process we won't clobber that adding of the bit. element->UnsetFlags(collector->tracker->RestyleBit() | - collector->tracker->RootBit()); + collector->tracker->RootBit() | + collector->tracker->ConditionalDescendantsBit()); RestyleEnumerateData** restyleArrayPtr = collector->restyleArrayPtr; RestyleEnumerateData* currentRestyle = *restyleArrayPtr; @@ -247,6 +248,12 @@ RestyleTracker::DoProcessRestyles() docShell->GetRecordProfileTimelineMarkers(&isTimelineRecording); } + // Create a AnimationsWithDestroyedFrame during restyling process to + // stop animations on elements that have no frame at the end of the + // restyling process. + RestyleManager::AnimationsWithDestroyedFrame + animationsWithDestroyedFrame(mRestyleManager); + // Create a ReframingStyleContexts struct on the stack and put it in our // mReframingStyleContexts for almost all of the remaining scope of // this function. @@ -446,6 +453,9 @@ RestyleTracker::DoProcessRestyles() } } + // mPendingRestyles is now empty. + mHaveSelectors = false; + mRestyleManager->EndProcessingRestyles(); } @@ -511,4 +521,23 @@ RestyleTracker::AddRestyleRootsIfAwaitingRestyle( } } +void +RestyleTracker::ClearSelectors() +{ + if (!mHaveSelectors) { + return; + } + for (auto it = mPendingRestyles.Iter(); !it.Done(); it.Next()) { + RestyleData* data = it.Data(); + if (data->mRestyleHint & eRestyle_SomeDescendants) { + data->mRestyleHint = + (data->mRestyleHint & ~eRestyle_SomeDescendants) | eRestyle_Subtree; + data->mRestyleHintData.mSelectorsForDescendants.Clear(); + } else { + MOZ_ASSERT(data->mRestyleHintData.mSelectorsForDescendants.IsEmpty()); + } + } + mHaveSelectors = false; +} + } // namespace mozilla diff --git a/layout/base/RestyleTracker.h b/layout/base/RestyleTracker.h index 69bc74192c..bb7bcd2bdc 100644 --- a/layout/base/RestyleTracker.h +++ b/layout/base/RestyleTracker.h @@ -17,8 +17,9 @@ #include "mozilla/SplayTree.h" #include "mozilla/RestyleLogging.h" #include "GeckoProfiler.h" +#include "mozilla/Maybe.h" -#if defined(MOZ_ENABLE_PROFILER_SPS) +#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API) #include "ProfilerBacktrace.h" #endif @@ -230,6 +231,7 @@ public: explicit RestyleTracker(Element::FlagsType aRestyleBits) : mRestyleBits(aRestyleBits) , mHaveLaterSiblingRestyles(false) + , mHaveSelectors(false) { NS_PRECONDITION((mRestyleBits & ~ELEMENT_ALL_RESTYLE_FLAGS) == 0, "Why do we have these bits set?"); @@ -238,10 +240,10 @@ public: NS_PRECONDITION((mRestyleBits & ELEMENT_PENDING_RESTYLE_FLAGS) != ELEMENT_PENDING_RESTYLE_FLAGS, "Shouldn't have both restyle flags set"); - NS_PRECONDITION((mRestyleBits & ~ELEMENT_PENDING_RESTYLE_FLAGS) != 0, + NS_PRECONDITION((mRestyleBits & ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS) != 0, "Must have root flag"); - NS_PRECONDITION((mRestyleBits & ~ELEMENT_PENDING_RESTYLE_FLAGS) != - (ELEMENT_ALL_RESTYLE_FLAGS & ~ELEMENT_PENDING_RESTYLE_FLAGS), + NS_PRECONDITION((mRestyleBits & ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS) != + ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS, "Shouldn't have both root flags"); } @@ -256,10 +258,19 @@ public: /** * Add a restyle for the given element to the tracker. Returns true * if the element already had eRestyle_LaterSiblings set on it. + * + * aRestyleRoot is the closest restyle root for aElement. If the caller + * does not know what the closest restyle root is, Nothing should be + * passed. A Some(nullptr) restyle root can be passed if there is no + * ancestor element that is a restyle root. */ bool AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint, nsChangeHint aMinChangeHint, - const RestyleHintData* aRestyleHintData = nullptr); + const RestyleHintData* aRestyleHintData = nullptr, + mozilla::Maybe aRestyleRoot = + mozilla::Nothing()); + + Element* FindClosestRestyleRoot(Element* aElement); /** * Process the restyles we've been tracking. @@ -273,7 +284,13 @@ public: // Return our ELEMENT_IS_POTENTIAL_(ANIMATION_)RESTYLE_ROOT bit Element::FlagsType RootBit() const { - return mRestyleBits & ~ELEMENT_PENDING_RESTYLE_FLAGS; + return mRestyleBits & ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS; + } + + // Return our ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR bit if present, + // or 0 if it is not. + Element::FlagsType ConditionalDescendantsBit() const { + return mRestyleBits & ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR; } struct Hints { @@ -302,7 +319,7 @@ public: // that we called AddPendingRestyle for and found the element this is // the RestyleData for as its nearest restyle root. nsTArray> mDescendants; -#if defined(MOZ_ENABLE_PROFILER_SPS) +#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API) UniquePtr mBacktrace; #endif }; @@ -342,6 +359,14 @@ public: void AddRestyleRootsIfAwaitingRestyle( const nsTArray>& aElements); + /** + * Converts any eRestyle_SomeDescendants restyle hints in the pending restyle + * table into eRestyle_Subtree hints and clears out the associated arrays of + * nsCSSSelector pointers. This is called in response to a style sheet change + * that might have cause an nsCSSSelector to be destroyed. + */ + void ClearSelectors(); + /** * The document we're associated with. */ @@ -371,8 +396,9 @@ private: typedef nsClassHashtable PendingRestyleTable; typedef nsAutoTArray< nsRefPtr, 32> RestyleRootArray; // Our restyle bits. These will be a subset of ELEMENT_ALL_RESTYLE_FLAGS, and - // will include one flag from ELEMENT_PENDING_RESTYLE_FLAGS and one flag - // that's not in ELEMENT_PENDING_RESTYLE_FLAGS. + // will include one flag from ELEMENT_PENDING_RESTYLE_FLAGS, one flag + // from ELEMENT_POTENTIAL_RESTYLE_ROOT_FLAGS, and might also include + // ELEMENT_IS_CONDITIONAL_RESTYLE_ANCESTOR. Element::FlagsType mRestyleBits; RestyleManager* mRestyleManager; // Owns us // A hashtable that maps elements to pointers to RestyleData structs. The @@ -392,6 +418,9 @@ private: // flag. We need this to avoid enumerating the hashtable looking // for such entries when we can't possibly have any. bool mHaveLaterSiblingRestyles; + // True if we have some entries with selectors in the restyle hint data. + // We use this to skip iterating over mPendingRestyles in ClearSelectors. + bool mHaveSelectors; }; inline bool @@ -402,6 +431,11 @@ RestyleTracker::AddPendingRestyleToTable(Element* aElement, { RestyleData* existingData; + if (aRestyleHintData && + !aRestyleHintData->mSelectorsForDescendants.IsEmpty()) { + mHaveSelectors = true; + } + // Check the RestyleBit() flag before doing the hashtable Get, since // it's possible that the data in the hashtable isn't actually // relevant anymore (if the flag is not set). @@ -412,6 +446,13 @@ RestyleTracker::AddPendingRestyleToTable(Element* aElement, existingData = nullptr; } + if (aRestyleHint & eRestyle_SomeDescendants) { + NS_ASSERTION(ConditionalDescendantsBit(), + "why are we getting eRestyle_SomeDescendants in an " + "animation-only restyle?"); + aElement->SetFlags(ConditionalDescendantsBit()); + } + if (!existingData) { RestyleData* rd = new RestyleData(aRestyleHint, aMinChangeHint, aRestyleHintData); @@ -437,11 +478,40 @@ RestyleTracker::AddPendingRestyleToTable(Element* aElement, return hadRestyleLaterSiblings; } +inline mozilla::dom::Element* +RestyleTracker::FindClosestRestyleRoot(Element* aElement) +{ + Element* cur = aElement; + while (!cur->HasFlag(RootBit())) { + nsIContent* parent = cur->GetFlattenedTreeParent(); + // Stop if we have no parent or the parent is not an element or + // we're part of the viewport scrollbars (because those are not + // frametree descendants of the primary frame of the root + // element). + // XXXbz maybe the primary frame of the root should be the root scrollframe? + if (!parent || !parent->IsElement() || + // If we've hit the root via a native anonymous kid and that + // this native anonymous kid is not obviously a descendant + // of the root's primary frame, assume we're under the root + // scrollbars. Since those don't get reresolved when + // reresolving the root, we need to make sure to add the + // element to mRestyleRoots. + (cur->IsInNativeAnonymousSubtree() && !parent->GetParent() && + cur->GetPrimaryFrame() && + cur->GetPrimaryFrame()->GetParent() != parent->GetPrimaryFrame())) { + return nullptr; + } + cur = parent->AsElement(); + } + return cur; +} + inline bool RestyleTracker::AddPendingRestyle(Element* aElement, nsRestyleHint aRestyleHint, nsChangeHint aMinChangeHint, - const RestyleHintData* aRestyleHintData) + const RestyleHintData* aRestyleHintData, + mozilla::Maybe aRestyleRoot) { bool hadRestyleLaterSiblings = AddPendingRestyleToTable(aElement, aRestyleHint, aMinChangeHint, @@ -452,29 +522,11 @@ RestyleTracker::AddPendingRestyle(Element* aElement, // ReResolveStyleContext on it or just reframe it). if ((aRestyleHint & ~eRestyle_LaterSiblings) || (aMinChangeHint & nsChangeHint_ReconstructFrame)) { - Element* cur = aElement; - while (!cur->HasFlag(RootBit())) { - nsIContent* parent = cur->GetFlattenedTreeParent(); - // Stop if we have no parent or the parent is not an element or - // we're part of the viewport scrollbars (because those are not - // frametree descendants of the primary frame of the root - // element). - // XXXbz maybe the primary frame of the root should be the root scrollframe? - if (!parent || !parent->IsElement() || - // If we've hit the root via a native anonymous kid and that - // this native anonymous kid is not obviously a descendant - // of the root's primary frame, assume we're under the root - // scrollbars. Since those don't get reresolved when - // reresolving the root, we need to make sure to add the - // element to mRestyleRoots. - (cur->IsInNativeAnonymousSubtree() && !parent->GetParent() && - cur->GetPrimaryFrame() && - cur->GetPrimaryFrame()->GetParent() != parent->GetPrimaryFrame())) { - mRestyleRoots.AppendElement(aElement); - cur = aElement; - break; - } - cur = parent->AsElement(); + Element* cur = + aRestyleRoot ? *aRestyleRoot : FindClosestRestyleRoot(aElement); + if (!cur) { + mRestyleRoots.AppendElement(aElement); + cur = aElement; } // At this point some ancestor of aElement (possibly aElement // itself) is in mRestyleRoots. Set the root bit on aElement, to diff --git a/layout/base/crashtests/1140198.html b/layout/base/crashtests/1140198.html new file mode 100644 index 0000000000..2e3f075b43 --- /dev/null +++ b/layout/base/crashtests/1140198.html @@ -0,0 +1,16 @@ + + + + + + + diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 8fccf62e82..22861595e5 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -460,4 +460,5 @@ load 1061028.html load 1116104.html load 1107508-1.html load 1127198-1.html +load 1140198.html load 1297835.html diff --git a/layout/base/moz.build b/layout/base/moz.build index ef816d6e77..42375ed3b9 100644 --- a/layout/base/moz.build +++ b/layout/base/moz.build @@ -82,6 +82,7 @@ EXPORTS += [ 'nsIReflowCallback.h', 'nsLayoutUtils.h', 'nsPresArena.h', + 'nsPresArenaObjectList.h', 'nsPresContext.h', 'nsPresState.h', 'nsRefreshDriver.h', @@ -94,6 +95,9 @@ EXPORTS += [ ] EXPORTS.mozilla += [ + 'ArenaObjectID.h', + 'ArenaRefPtr.h', + 'ArenaRefPtrInlines.h', 'GeometryUtils.h', 'PaintTracker.h', 'RestyleLogging.h', diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index a0d1c5b791..2dc55d704f 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -2606,8 +2606,7 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element* aDocEle newFrame = frameItems.FirstChild(); NS_ASSERTION(frameItems.OnlyChild(), "multiple root element frames"); } else { - MOZ_ASSERT(display->mDisplay == NS_STYLE_DISPLAY_BLOCK || - display->mDisplay == NS_STYLE_DISPLAY_CONTENTS, + MOZ_ASSERT(display->mDisplay == NS_STYLE_DISPLAY_BLOCK, "Unhandled display type for root element"); contentFrame = NS_NewBlockFormattingContext(mPresShell, styleContext); nsFrameItems frameItems; diff --git a/layout/base/nsCaret.cpp b/layout/base/nsCaret.cpp index 47ec4c97c9..2ed4cf9afa 100644 --- a/layout/base/nsCaret.cpp +++ b/layout/base/nsCaret.cpp @@ -217,8 +217,8 @@ nsCaret::ComputeMetrics(nsIFrame* aFrame, int32_t aOffset, nscoord aCaretHeight) // between 0 and 1 goes up to 1 so we don't let the caret disappear. int32_t tpp = aFrame->PresContext()->AppUnitsPerDevPixel(); Metrics result; - result.mCaretWidth = NS_ROUND_CARET_TO_PIXELS(caretWidth, tpp); - result.mBidiIndicatorSize = NS_ROUND_CARET_TO_PIXELS(bidiIndicatorSize, tpp); + result.mCaretWidth = NS_ROUND_BORDER_TO_PIXELS(caretWidth, tpp); + result.mBidiIndicatorSize = NS_ROUND_BORDER_TO_PIXELS(bidiIndicatorSize, tpp); return result; } @@ -616,7 +616,7 @@ NS_IMETHODIMP nsCaret::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aDomSel, int16_t aReason) { - if (aReason & nsISelectionListener::MOUSEUP_REASON)//this wont do + if ((aReason & nsISelectionListener::MOUSEUP_REASON) || !IsVisible())//this wont do return NS_OK; nsCOMPtr domSel(do_QueryReferent(mDomSelectionWeak)); diff --git a/layout/base/nsChangeHint.h b/layout/base/nsChangeHint.h index 83076e5812..14b7fc7a4b 100644 --- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -129,7 +129,7 @@ enum nsChangeHint { * has changed whether the frame is a container for fixed-pos or abs-pos * elements, but reframing is otherwise not needed. */ - nsChangeHint_AddOrRemoveTransform = 0x20000, + nsChangeHint_UpdateContainingBlock = 0x20000, /** * This change hint has *no* change handling behavior. However, it @@ -287,7 +287,7 @@ inline nsChangeHint operator^=(nsChangeHint& aLeft, nsChangeHint aRight) nsChangeHint_UpdateParentOverflow | \ nsChangeHint_ChildrenOnlyTransform | \ nsChangeHint_RecomputePosition | \ - nsChangeHint_AddOrRemoveTransform | \ + nsChangeHint_UpdateContainingBlock | \ nsChangeHint_BorderStyleNoneChange | \ nsChangeHint_NeedReflow | \ nsChangeHint_ReflowChangesSizeOrPosition | \ @@ -305,7 +305,7 @@ inline nsChangeHint NS_HintsNotHandledForDescendantsIn(nsChangeHint aChangeHint) nsChangeHint_UpdateParentOverflow | nsChangeHint_ChildrenOnlyTransform | nsChangeHint_RecomputePosition | - nsChangeHint_AddOrRemoveTransform | + nsChangeHint_UpdateContainingBlock | nsChangeHint_BorderStyleNoneChange | nsChangeHint_UpdateComputedBSize)); diff --git a/layout/base/nsIPresShell.h b/layout/base/nsIPresShell.h index e3ee9a556c..311f9807f9 100644 --- a/layout/base/nsIPresShell.h +++ b/layout/base/nsIPresShell.h @@ -20,6 +20,7 @@ #ifndef nsIPresShell_h___ #define nsIPresShell_h___ +#include "mozilla/ArenaObjectID.h" #include "mozilla/EventForwards.h" #include "mozilla/MemoryReporting.h" #include "mozilla/StaticPtr.h" @@ -246,7 +247,7 @@ public: * the same aSize value. AllocateByObjectID returns zero-filled memory. * AllocateByObjectID is infallible and will abort on out-of-memory. */ - void* AllocateByObjectID(nsPresArena::ObjectID aID, size_t aSize) + void* AllocateByObjectID(mozilla::ArenaObjectID aID, size_t aSize) { #ifdef DEBUG mPresArenaAllocCount++; @@ -256,7 +257,7 @@ public: return result; } - void FreeByObjectID(nsPresArena::ObjectID aID, void* aPtr) + void FreeByObjectID(mozilla::ArenaObjectID aID, void* aPtr) { #ifdef DEBUG mPresArenaAllocCount--; @@ -291,6 +292,23 @@ public: mFrameArena.FreeBySize(aSize, aPtr); } + template + void RegisterArenaRefPtr(mozilla::ArenaRefPtr* aPtr) + { + mFrameArena.RegisterArenaRefPtr(aPtr); + } + + template + void DeregisterArenaRefPtr(mozilla::ArenaRefPtr* aPtr) + { + mFrameArena.DeregisterArenaRefPtr(aPtr); + } + + void ClearArenaRefPtrs(mozilla::ArenaObjectID aObjectID) + { + mFrameArena.ClearArenaRefPtrs(aObjectID); + } + nsIDocument* GetDocument() const { return mDocument; } nsPresContext* GetPresContext() const { return mPresContext; } @@ -507,9 +525,19 @@ public: eTreeChange, // mark intrinsic widths dirty on aFrame and its ancestors eStyleChange // Do eTreeChange, plus all of aFrame's descendants }; + enum ReflowRootHandling { + ePositionOrSizeChange, // aFrame is changing position or size + eNoPositionOrSizeChange, // ... NOT changing ... + eInferFromBitToAdd // is changing iff (aBitToAdd == NS_FRAME_IS_DIRTY) + + // Note: With eStyleChange, these can also apply to out-of-flows + // in addition to aFrame. + }; virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty, - nsFrameState aBitToAdd) = 0; + nsFrameState aBitToAdd, + ReflowRootHandling aRootHandling = + eInferFromBitToAdd) = 0; /** * Calls FrameNeedsReflow on all fixed position children of the root frame. diff --git a/layout/base/nsPresArena.cpp b/layout/base/nsPresArena.cpp index a8a96ae54b..5000422b35 100644 --- a/layout/base/nsPresArena.cpp +++ b/layout/base/nsPresArena.cpp @@ -23,9 +23,12 @@ #include "nsDebug.h" #include "nsArenaMemoryStats.h" #include "nsPrintfCString.h" +#include "nsStyleContext.h" #include +using namespace mozilla; + // Size to use for PLArena block allocations. static const size_t ARENA_PAGE_SIZE = 8192; @@ -36,6 +39,8 @@ nsPresArena::nsPresArena() nsPresArena::~nsPresArena() { + ClearArenaRefPtrs(); + #if defined(MOZ_HAVE_MEM_CHECKS) for (auto iter = mFreeLists.Iter(); !iter.Done(); iter.Next()) { FreeList* entry = iter.Get(); @@ -47,9 +52,62 @@ nsPresArena::~nsPresArena() } } #endif + PL_FinishArenaPool(&mPool); } +/* inline */ void +nsPresArena::ClearArenaRefPtrWithoutDeregistering(void* aPtr, + ArenaObjectID aObjectID) +{ + switch (aObjectID) { +#define PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT(name_) \ + case eArenaObjectID_##name_: \ + static_cast*>(aPtr)->ClearWithoutDeregistering(); \ + return; +#include "nsPresArenaObjectList.h" +#undef PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT + default: + break; + } + switch (aObjectID) { +#define PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(name_) \ + case eArenaObjectID_##name_: \ + MOZ_ASSERT(false, #name_ " must be declared in nsPresArenaObjectList.h "\ + "with PRES_ARENA_OBJECT_SUPPORTS_ARENAREFPTR"); \ + break; +#include "nsPresArenaObjectList.h" +#undef PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT + default: + MOZ_ASSERT(false, "unexpected ArenaObjectID value"); + break; + } +} + +void +nsPresArena::ClearArenaRefPtrs() +{ + for (auto iter = mArenaRefPtrs.Iter(); !iter.Done(); iter.Next()) { + void* ptr = iter.Key(); + ArenaObjectID id = iter.UserData(); + ClearArenaRefPtrWithoutDeregistering(ptr, id); + } + mArenaRefPtrs.Clear(); +} + +void +nsPresArena::ClearArenaRefPtrs(ArenaObjectID aObjectID) +{ + for (auto iter = mArenaRefPtrs.Iter(); !iter.Done(); iter.Next()) { + void* ptr = iter.Key(); + ArenaObjectID id = iter.UserData(); + if (id == aObjectID) { + ClearArenaRefPtrWithoutDeregistering(ptr, id); + iter.Remove(); + } + } +} + void* nsPresArena::Allocate(uint32_t aCode, size_t aSize) { @@ -170,17 +228,17 @@ nsPresArena::AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf, break; #include "nsFrameIdList.h" #undef FRAME_ID - case nsLineBox_id: + case eArenaObjectID_nsLineBox: p = &aArenaStats->mLineBoxes; break; - case nsRuleNode_id: + case eArenaObjectID_nsRuleNode: p = &aArenaStats->mRuleNodes; break; - case nsStyleContext_id: + case eArenaObjectID_nsStyleContext: p = &aArenaStats->mStyleContexts; break; #define STYLE_STRUCT(name_, checkdata_cb_) \ - case nsStyle##name_##_id: + case eArenaObjectID_nsStyle##name_: #include "nsStyleStructList.h" #undef STYLE_STRUCT p = &aArenaStats->mStyleStructs; diff --git a/layout/base/nsPresArena.h b/layout/base/nsPresArena.h index 5c0e3f19d6..32407541ea 100644 --- a/layout/base/nsPresArena.h +++ b/layout/base/nsPresArena.h @@ -10,11 +10,14 @@ #ifndef nsPresArena_h___ #define nsPresArena_h___ +#include "mozilla/ArenaObjectID.h" +#include "mozilla/ArenaRefPtr.h" #include "mozilla/MemoryChecking.h" // Note: Do not remove this, needed for MOZ_HAVE_MEM_CHECKS below #include "mozilla/MemoryReporting.h" #include #include "nscore.h" -#include "nsQueryFrame.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" #include "nsTArray.h" #include "nsTHashtable.h" #include "plarena.h" @@ -26,51 +29,18 @@ public: nsPresArena(); ~nsPresArena(); - enum ObjectID { - nsLineBox_id = nsQueryFrame::NON_FRAME_MARKER, - nsRuleNode_id, - nsStyleContext_id, - nsInheritedStyleData_id, - nsResetStyleData_id, - nsConditionalResetStyleData_id, - nsConditionalResetStyleDataEntry_id, - nsFrameList_id, - - CustomCounterStyle_id, - DependentBuiltinCounterStyle_id, - - First_nsStyleStruct_id, - DummyBeforeStyleStructs_id = First_nsStyleStruct_id - 1, - - #define STYLE_STRUCT(name_, checkdata_cb_) \ - nsStyle##name_##_id, - #include "nsStyleStructList.h" - #undef STYLE_STRUCT - - DummyAfterStyleStructs_id, - Last_nsStyleStruct_id = DummyAfterStyleStructs_id - 1, - - /** - * The PresArena implementation uses this bit to distinguish objects - * allocated by size from objects allocated by type ID (that is, frames - * using AllocateByFrameID and other objects using AllocateByObjectID). - * It should not collide with any Object ID (above) or frame ID (in - * nsQueryFrame.h). It is not 0x80000000 to avoid the question of - * whether enumeration constants are signed. - */ - NON_OBJECT_MARKER = 0x40000000 - }; - /** * Pool allocation with recycler lists indexed by object size, aSize. */ void* AllocateBySize(size_t aSize) { - return Allocate(uint32_t(aSize) | uint32_t(NON_OBJECT_MARKER), aSize); + return Allocate(uint32_t(aSize) | + uint32_t(mozilla::eArenaObjectID_NON_OBJECT_MARKER), aSize); } void FreeBySize(size_t aSize, void* aPtr) { - Free(uint32_t(aSize) | uint32_t(NON_OBJECT_MARKER), aPtr); + Free(uint32_t(aSize) | + uint32_t(mozilla::eArenaObjectID_NON_OBJECT_MARKER), aPtr); } /** @@ -90,15 +60,54 @@ public: * Pool allocation with recycler lists indexed by object-type ID (see above). * Every aID must always be used with the same object size, aSize. */ - void* AllocateByObjectID(ObjectID aID, size_t aSize) + void* AllocateByObjectID(mozilla::ArenaObjectID aID, size_t aSize) { return Allocate(aID, aSize); } - void FreeByObjectID(ObjectID aID, void* aPtr) + void FreeByObjectID(mozilla::ArenaObjectID aID, void* aPtr) { Free(aID, aPtr); } + /** + * Register an ArenaRefPtr to be cleared when this arena is about to + * be destroyed. + * + * (Defined in ArenaRefPtrInlines.h.) + * + * @param aPtr The ArenaRefPtr to clear. + * @param aObjectID The ArenaObjectID value that uniquely identifies + * the type of object the ArenaRefPtr holds. + */ + template + void RegisterArenaRefPtr(mozilla::ArenaRefPtr* aPtr); + + /** + * Deregister an ArenaRefPtr that was previously registered with + * RegisterArenaRefPtr. + */ + template + void DeregisterArenaRefPtr(mozilla::ArenaRefPtr* aPtr) + { + MOZ_ASSERT(mArenaRefPtrs.Contains(aPtr)); + mArenaRefPtrs.Remove(aPtr); + } + + /** + * Clears all currently registered ArenaRefPtrs. This will be called during + * the destructor, but can be called by users of nsPresArena who want to + * ensure arena-allocated objects are released earlier. + */ + void ClearArenaRefPtrs(); + + /** + * Clears all currently registered ArenaRefPtrs for the given ArenaObjectID. + * This is called when we reconstruct the rule tree so that style contexts + * pointing into the old rule tree aren't released afterwards, triggering an + * assertion in ~nsStyleContext. + */ + void ClearArenaRefPtrs(mozilla::ArenaObjectID aObjectID); + /** * Increment aArenaStats with sizes of interesting objects allocated in this * arena and its mOther field with the size of everything else. @@ -110,6 +119,10 @@ private: void* Allocate(uint32_t aCode, size_t aSize); void Free(uint32_t aCode, void* aPtr); + inline void ClearArenaRefPtrWithoutDeregistering( + void* aPtr, + mozilla::ArenaObjectID aObjectID); + // All keys to this hash table fit in 32 bits (see below) so we do not // bother actually hashing them. class FreeList : public PLDHashEntryHdr @@ -144,6 +157,7 @@ private: nsTHashtable mFreeLists; PLArenaPool mPool; + nsDataHashtable, mozilla::ArenaObjectID> mArenaRefPtrs; }; #endif diff --git a/layout/base/nsPresArenaObjectList.h b/layout/base/nsPresArenaObjectList.h new file mode 100644 index 0000000000..c226aa3b4e --- /dev/null +++ b/layout/base/nsPresArenaObjectList.h @@ -0,0 +1,72 @@ +/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */ +/* 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/. */ + +/* a list of all types that can be allocated in an nsPresArena, for + preprocessing */ + +#ifdef STYLE_STRUCT +#error Sorry nsPresArenaObjectList.h needs to use STYLE_STRUCT! +#endif + +#ifdef PRES_ARENA_OBJECT +#if defined(PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT) || \ + defined(PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT) +#error Must not define PRES_ARENA_OBJECT along with \ + PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT or \ + PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT. +#endif +#define PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(name_) PRES_ARENA_OBJECT(name_) +#define PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT(name_) PRES_ARENA_OBJECT(name_) +#define DEFINED_PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT +#define DEFINED_PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT +#endif + +#ifndef PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT +#define PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(name_) /* nothing */ +#define DEFINED_PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT +#endif + +#ifndef PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT +#define PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT(name_) /* nothing */ +#define DEFINED_PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT +#endif + +// Use PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT to mention an nsPresArena- +// allocated object that does not support ArenaRefPtr, and use +// PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT to mention one that does. +// +// All PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT classes must be #included into +// nsPresArena.cpp. +// +// Files including nsPresArenaObjectList.h can either define one or both +// of PRES_ARENA_OBJECT_{WITH,WITHOUT}_ARENAREFPTR_SUPPORT to capture those +// classes separately, or PRES_ARENA_OBJECT to capture all nsPresArena- +// allocated classes. + +PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(nsLineBox) +PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(nsRuleNode) +PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT(nsStyleContext) +PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(nsInheritedStyleData) +PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(nsResetStyleData) +PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(nsConditionalResetStyleData) +PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(nsConditionalResetStyleDataEntry) +PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(nsFrameList) +PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(CustomCounterStyle) +PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(DependentBuiltinCounterStyle) + +#define STYLE_STRUCT(name_, checkdata_cb_) \ + PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT(nsStyle##name_) +#include "nsStyleStructList.h" +#undef STYLE_STRUCT + +#ifdef DEFINED_PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT +#undef PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT +#undef DEFINED_PRES_ARENA_OBJECT_WITHOUT_ARENAREFPTR_SUPPORT +#endif + +#ifdef DEFINED_PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT +#undef PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT +#undef DEFINED_PRES_ARENA_OBJECT_WITH_ARENAREFPTR_SUPPORT +#endif diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index 97c2b1d86b..667e043313 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -2789,6 +2789,15 @@ nsPresContext::IsDeviceSizePageSize() return isDeviceSizePageSize; } +uint64_t +nsPresContext::GetRestyleGeneration() const +{ + if (!mRestyleManager) { + return 0; + } + return mRestyleManager->GetRestyleGeneration(); +} + nsRootPresContext::nsRootPresContext(nsIDocument* aDocument, nsPresContextType aType) : nsPresContext(aDocument, aType), diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index 478511baaf..11b16bd579 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -914,6 +914,11 @@ public: mAllInvalidated = false; } + /** + * Returns the RestyleManager's restyle generation counter. + */ + uint64_t GetRestyleGeneration() const; + /** * Returns whether there are any pending restyles or reflows. */ diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 70af4647ab..28fb5ee6b7 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1235,6 +1235,23 @@ PresShell::Destroy() mViewManager = nullptr; } + // mFrameArena will be destroyed soon. Clear out any ArenaRefPtrs + // pointing to objects in the arena now. This is done: + // + // (a) before mFrameArena's destructor runs so that our + // mPresArenaAllocCount gets down to 0 and doesn't trip the assertion + // in ~PresShell, + // (b) before the mPresContext->SetShell(nullptr) below, so + // that when we clear the ArenaRefPtrs they'll still be able to + // get back to this PresShell to deregister themselves (e.g. note + // how nsStyleContext::Arena returns the PresShell got from its + // rule node's nsPresContext, which would return null if we'd already + // called mPresContext->SetShell(nullptr)), and + // (c) before the mStyleSet->BeginShutdown() call just below, so that + // the nsStyleContexts don't complain they're being destroyed later + // than the rule tree is. + mFrameArena.ClearArenaRefPtrs(); + mStyleSet->BeginShutdown(); nsRefreshDriver* rd = GetPresContext()->RefreshDriver(); @@ -2548,7 +2565,8 @@ PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame) void PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty, - nsFrameState aBitToAdd) + nsFrameState aBitToAdd, + ReflowRootHandling aRootHandling) { NS_PRECONDITION(aBitToAdd == NS_FRAME_IS_DIRTY || aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN || @@ -2598,13 +2616,24 @@ PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty, bool wasDirty = NS_SUBTREE_DIRTY(subtreeRoot); subtreeRoot->AddStateBits(aBitToAdd); - // Now if subtreeRoot is a reflow root we can cut off this reflow at it if - // the bit being added is NS_FRAME_HAS_DIRTY_CHILDREN. - bool targetFrameDirty = (aBitToAdd == NS_FRAME_IS_DIRTY); + // Determine whether we need to keep looking for the next ancestor + // reflow root if subtreeRoot itself is a reflow root. + bool targetNeedsReflowFromParent; + switch (aRootHandling) { + case ePositionOrSizeChange: + targetNeedsReflowFromParent = true; + break; + case eNoPositionOrSizeChange: + targetNeedsReflowFromParent = false; + break; + case eInferFromBitToAdd: + targetNeedsReflowFromParent = (aBitToAdd == NS_FRAME_IS_DIRTY); + break; + } #define FRAME_IS_REFLOW_ROOT(_f) \ ((_f->GetStateBits() & NS_FRAME_REFLOW_ROOT) && \ - (_f != subtreeRoot || !targetFrameDirty)) + (_f != subtreeRoot || !targetNeedsReflowFromParent)) // Mark the intrinsic widths as dirty on the frame, all of its ancestors, diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h index fb51838d56..f5ddc27bb7 100644 --- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -112,7 +112,9 @@ public: virtual nsIFrame* GetPlaceholderFrameFor(nsIFrame* aFrame) const override; virtual void FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty, - nsFrameState aBitToAdd) override; + nsFrameState aBitToAdd, + ReflowRootHandling aRootHandling = + eInferFromBitToAdd) override; virtual void FrameNeedsToContinueReflow(nsIFrame *aFrame) override; virtual void CancelAllPendingReflows() override; virtual bool IsSafeToFlush() const override; diff --git a/layout/generic/WritingModes.h b/layout/generic/WritingModes.h index 06b088e44e..6af3e44c12 100644 --- a/layout/generic/WritingModes.h +++ b/layout/generic/WritingModes.h @@ -1135,6 +1135,29 @@ public: return mMargin.TopBottom(); } + /* + * Return margin values for line-relative sides, as defined in + * http://www.w3.org/TR/css-writing-modes-3/#line-directions: + * + * line-left + * Nominally the side from which LTR text would start. + * line-right + * Nominally the side from which RTL text would start. (Opposite of + * line-left.) + */ + nscoord LineLeft(WritingMode aWritingMode) const + { + // We don't need to CHECK_WRITING_MODE here because the IStart or IEnd + // accessor that we call will do it. + return aWritingMode.IsBidiLTR() + ? IStart(aWritingMode) : IEnd(aWritingMode); + } + nscoord LineRight(WritingMode aWritingMode) const + { + return aWritingMode.IsBidiLTR() + ? IEnd(aWritingMode) : IStart(aWritingMode); + } + /** * Return a LogicalSize representing the total size of the inline- * and block-dimension margins. @@ -1891,41 +1914,4 @@ nsStylePosition::MaxBSizeDependsOnContainer(mozilla::WritingMode aWM) const : MaxHeightDependsOnContainer(); } -inline uint8_t -nsStyleTableBorder::LogicalCaptionSide(mozilla::WritingMode aWM) const -{ - // sanity-check that constants we're using have the expected relationships - static_assert(NS_STYLE_CAPTION_SIDE_BSTART == mozilla::eLogicalSideBStart && - NS_STYLE_CAPTION_SIDE_BEND == mozilla::eLogicalSideBEnd && - NS_STYLE_CAPTION_SIDE_ISTART == mozilla::eLogicalSideIStart && - NS_STYLE_CAPTION_SIDE_IEND == mozilla::eLogicalSideIEnd, - "bad logical caption-side values"); - static_assert((NS_STYLE_CAPTION_SIDE_TOP - NS_SIDE_TOP == - NS_STYLE_CAPTION_SIDE_BOTTOM - NS_SIDE_BOTTOM) && - (NS_STYLE_CAPTION_SIDE_LEFT - NS_SIDE_LEFT == - NS_STYLE_CAPTION_SIDE_RIGHT - NS_SIDE_RIGHT) && - (NS_STYLE_CAPTION_SIDE_LEFT - NS_SIDE_LEFT == - NS_STYLE_CAPTION_SIDE_TOP - NS_SIDE_TOP), - "mismatch between caption-side and side values"); - switch (mCaptionSide) { - case NS_STYLE_CAPTION_SIDE_TOP: - case NS_STYLE_CAPTION_SIDE_RIGHT: - case NS_STYLE_CAPTION_SIDE_BOTTOM: - case NS_STYLE_CAPTION_SIDE_LEFT: { - uint8_t side = mCaptionSide - (NS_STYLE_CAPTION_SIDE_TOP - NS_SIDE_TOP); - return aWM.LogicalSideForPhysicalSide(mozilla::css::Side(side)); - } - - case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: - return aWM.IsVertical() ? aWM.LogicalSideForPhysicalSide(NS_SIDE_TOP) - : NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE; - - case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: - return aWM.IsVertical() ? aWM.LogicalSideForPhysicalSide(NS_SIDE_BOTTOM) - : NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE; - } - MOZ_ASSERT(mCaptionSide <= NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE); - return mCaptionSide; -} - #endif // WritingModes_h_ diff --git a/layout/generic/crashtests/1157011.html b/layout/generic/crashtests/1157011.html new file mode 100644 index 0000000000..c5fe133a87 --- /dev/null +++ b/layout/generic/crashtests/1157011.html @@ -0,0 +1,4 @@ + + +
f
+ diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 51019996f8..2f9f335460 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -589,6 +589,7 @@ load 1146103.html load 1146107.html load 1146114.html load 1156222.html +load 1157011.html load 1222783.xhtml load details-display-none-summary-1.html load details-display-none-summary-2.html diff --git a/layout/generic/nsBRFrame.cpp b/layout/generic/nsBRFrame.cpp index 8c8156d7b8..457976c7d7 100644 --- a/layout/generic/nsBRFrame.cpp +++ b/layout/generic/nsBRFrame.cpp @@ -96,9 +96,13 @@ BRFrame::Reflow(nsPresContext* aPresContext, aMetrics.SetBlockStartAscent(0); // Only when the BR is operating in a line-layout situation will it - // behave like a BR. BR is suppressed when it is inside ruby frames. + // behave like a BR. Additionally, we suppress breaks from BR inside + // of ruby frames. To determine if we're inside ruby, we have to rely + // on the *parent's* ShouldSuppressLineBreak() method, instead of our + // own, because we may have custom "display" value that makes our + // ShouldSuppressLineBreak() return false. nsLineLayout* ll = aReflowState.mLineLayout; - if (ll && !StyleContext()->ShouldSuppressLineBreak()) { + if (ll && !GetParent()->StyleContext()->ShouldSuppressLineBreak()) { // Note that the compatibility mode check excludes AlmostStandards // mode, since this is the inline box model. See bug 161691. if ( ll->LineIsEmpty() || @@ -165,7 +169,7 @@ BRFrame::Reflow(nsPresContext* aPresContext, BRFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, nsIFrame::InlineMinISizeData *aData) { - if (!StyleContext()->ShouldSuppressLineBreak()) { + if (!GetParent()->StyleContext()->ShouldSuppressLineBreak()) { aData->ForceBreak(aRenderingContext); } } @@ -174,7 +178,7 @@ BRFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext, BRFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext, nsIFrame::InlinePrefISizeData *aData) { - if (!StyleContext()->ShouldSuppressLineBreak()) { + if (!GetParent()->StyleContext()->ShouldSuppressLineBreak()) { aData->ForceBreak(aRenderingContext); } } diff --git a/layout/generic/nsContainerFrame.cpp b/layout/generic/nsContainerFrame.cpp index 71aaf91291..2480f71a84 100644 --- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -922,13 +922,13 @@ nsContainerFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext, AutoMaybeDisableFontInflation an(this); WritingMode tableWM = GetParent()->GetWritingMode(); - uint8_t captionSide = StyleTableBorder()->LogicalCaptionSide(tableWM); + uint8_t captionSide = StyleTableBorder()->mCaptionSide; if (aWM.IsOrthogonalTo(tableWM)) { - if (captionSide == NS_STYLE_CAPTION_SIDE_BSTART || - captionSide == NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE || - captionSide == NS_STYLE_CAPTION_SIDE_BEND || - captionSide == NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE) { + if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || + captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) { // For an orthogonal caption on a block-dir side of the table, // shrink-wrap to min-isize. result.ISize(aWM) = GetMinISize(aRenderingContext); @@ -944,11 +944,11 @@ nsContainerFrame::ComputeAutoSize(nsRenderingContext* aRenderingContext, } } } else { - if (captionSide == NS_STYLE_CAPTION_SIDE_ISTART || - captionSide == NS_STYLE_CAPTION_SIDE_IEND) { + if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || + captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { result.ISize(aWM) = GetMinISize(aRenderingContext); - } else if (captionSide == NS_STYLE_CAPTION_SIDE_BSTART || - captionSide == NS_STYLE_CAPTION_SIDE_BEND) { + } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { // The outer frame constrains our available isize to the isize of // the table. Grow if our min-isize is bigger than that, but not // larger than the containing block isize. (It would really be nice diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 274f7be647..2e2a739c0c 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -697,6 +697,17 @@ nsFrame::DestroyFrom(nsIFrame* aDestructRoot) } } + if (nsLayoutUtils::HasCurrentAnimations(static_cast(this))) { + // If no new frame for this element is created by the end of the + // restyling process, stop animations for this frame + RestyleManager::AnimationsWithDestroyedFrame* adf = + presContext->RestyleManager()->GetAnimationsWithDestroyedFrame(); + // AnimationsWithDestroyedFrame only lives during the restyling process. + if (adf) { + adf->Put(mContent, mStyleContext); + } + } + shell->NotifyDestroyingFrame(this); if (mState & NS_FRAME_EXTERNAL_REFERENCE) { diff --git a/layout/generic/nsFrameList.cpp b/layout/generic/nsFrameList.cpp index ab41fb56f4..61c3062426 100644 --- a/layout/generic/nsFrameList.cpp +++ b/layout/generic/nsFrameList.cpp @@ -4,14 +4,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsFrameList.h" -#include "nsContainerFrame.h" -#include "nsLayoutUtils.h" -#include "nsPresContext.h" -#include "nsIPresShell.h" +#include "mozilla/ArenaObjectID.h" +#include "nsBidiPresUtils.h" +#include "nsContainerFrame.h" #include "nsGkAtoms.h" #include "nsILineIterator.h" -#include "nsBidiPresUtils.h" +#include "nsIPresShell.h" +#include "nsLayoutUtils.h" +#include "nsPresContext.h" + +using namespace mozilla; namespace mozilla { namespace layout { @@ -24,7 +27,7 @@ const AlignedFrameListBytes gEmptyFrameListBytes = { 0 }; void* nsFrameList::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW { - return aPresShell->AllocateByObjectID(nsPresArena::nsFrameList_id, sz); + return aPresShell->AllocateByObjectID(eArenaObjectID_nsFrameList, sz); } void @@ -33,7 +36,7 @@ nsFrameList::Delete(nsIPresShell* aPresShell) NS_PRECONDITION(this != &EmptyList(), "Shouldn't Delete() this list"); NS_ASSERTION(IsEmpty(), "Shouldn't Delete() a non-empty list"); - aPresShell->FreeByObjectID(nsPresArena::nsFrameList_id, this); + aPresShell->FreeByObjectID(eArenaObjectID_nsFrameList, this); } void diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index 19d0bdac53..9c6923ebab 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -1985,9 +1985,9 @@ IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay, if (aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CAPTION) { return false; } - uint8_t captionSide = aFrame->StyleTableBorder()->LogicalCaptionSide(aWM); - return captionSide == NS_STYLE_CAPTION_SIDE_ISTART || - captionSide == NS_STYLE_CAPTION_SIDE_IEND; + uint8_t captionSide = aFrame->StyleTableBorder()->mCaptionSide; + return captionSide == NS_STYLE_CAPTION_SIDE_LEFT || + captionSide == NS_STYLE_CAPTION_SIDE_RIGHT; } static nsFlexContainerFrame* diff --git a/layout/generic/nsLeafFrame.h b/layout/generic/nsLeafFrame.h index 97bb91a3b0..5c8b69088b 100644 --- a/layout/generic/nsLeafFrame.h +++ b/layout/generic/nsLeafFrame.h @@ -81,15 +81,20 @@ protected: virtual ~nsLeafFrame(); /** - * Return the intrinsic width of the frame's content area. Note that this + * Return the intrinsic isize of the frame's content area. Note that this * should not include borders or padding and should not depend on the applied * styles. + * One exception to this is that the intrinsic (logical) size of an + diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8-ref.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8-ref.html new file mode 100644 index 0000000000..33e90d3f80 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8-ref.html @@ -0,0 +1,19 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/reftest.list b/layout/reftests/writing-mode/reftest.list index 86c600c353..cea38f9a91 100644 --- a/layout/reftests/writing-mode/reftest.list +++ b/layout/reftests/writing-mode/reftest.list @@ -150,6 +150,9 @@ test-pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLin == 1172774-percent-padding-4.html 1172774-percent-vertical-ref.html == 1174450-intrinsic-sizing.html 1174450-intrinsic-sizing-ref.html == 1175789-underline-overline-1.html 1175789-underline-overline-1-ref.html +== 1188061-1-nsChangeHint_ClearAncestorIntrinsics.html 1188061-1-nsChangeHint_ClearAncestorIntrinsics-ref.html +== 1188061-2-nsChangeHint_UpdateComputedBSize.html 1188061-2-nsChangeHint_UpdateComputedBSize-ref.html +== 1196887-1-computed-display-inline-block.html 1196887-1-computed-display-inline-block-ref.html # Suite of tests from Gérard Talbot in bug 1079151 include abspos/reftest.list diff --git a/layout/reftests/writing-mode/tables/reftest.list b/layout/reftests/writing-mode/tables/reftest.list index 35555b1b48..868f9e7d47 100644 --- a/layout/reftests/writing-mode/tables/reftest.list +++ b/layout/reftests/writing-mode/tables/reftest.list @@ -74,10 +74,6 @@ fuzzy-if(winWidget,48,600) fuzzy-if(cocoaWidget,19,97) HTTP(../..) == wm-row-pro fuzzy-if(winWidget,48,600) fuzzy-if(cocoaWidget,19,97) HTTP(../..) == wm-row-progression-006.xht multicol-count-002-ref.xht fuzzy-if(winWidget,48,600) fuzzy-if(cocoaWidget,19,97) HTTP(../..) == wm-row-progression-007.xht multicol-count-002-ref.xht -fuzzy-if(Android,255,38) == table-caption-block-start-1.html table-caption-block-start-1-ref.html -fuzzy-if(Android,255,38) == table-caption-block-end-1.html table-caption-block-end-1-ref.html -== table-caption-inline-start-1.html table-caption-inline-start-1-ref.html -== table-caption-inline-end-1.html table-caption-inline-end-1-ref.html fuzzy-if(Android,255,38) == table-caption-top-1.html table-caption-top-1-ref.html fuzzy-if(Android,255,38) == table-caption-bottom-1.html table-caption-bottom-1-ref.html == table-caption-left-1.html table-caption-left-1-ref.html diff --git a/layout/reftests/writing-mode/tables/table-caption-block-end-1-ref.html b/layout/reftests/writing-mode/tables/table-caption-block-end-1-ref.html deleted file mode 100644 index fd446c3f5b..0000000000 --- a/layout/reftests/writing-mode/tables/table-caption-block-end-1-ref.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - -
-
-
table caption
-
- -
-
-
table caption
-
- -
-
-
table caption
-
- -
-
-
table caption
-
diff --git a/layout/reftests/writing-mode/tables/table-caption-block-end-1.html b/layout/reftests/writing-mode/tables/table-caption-block-end-1.html deleted file mode 100644 index 645f7dc644..0000000000 --- a/layout/reftests/writing-mode/tables/table-caption-block-end-1.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - -
table caption
- - - - -
table caption
- - - - -
table caption
- - - - -
table caption
diff --git a/layout/reftests/writing-mode/tables/table-caption-block-start-1-ref.html b/layout/reftests/writing-mode/tables/table-caption-block-start-1-ref.html deleted file mode 100644 index d4f8ab717c..0000000000 --- a/layout/reftests/writing-mode/tables/table-caption-block-start-1-ref.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - -
-
table caption
-
-
- -
-
table caption
-
-
- -
-
table caption
-
-
- -
-
table caption
-
-
diff --git a/layout/reftests/writing-mode/tables/table-caption-block-start-1.html b/layout/reftests/writing-mode/tables/table-caption-block-start-1.html deleted file mode 100644 index f8a04bb67e..0000000000 --- a/layout/reftests/writing-mode/tables/table-caption-block-start-1.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - -
table caption
- - - - -
table caption
- - - - -
table caption
- - - - -
table caption
diff --git a/layout/reftests/writing-mode/tables/table-caption-bottom-1-ref.html b/layout/reftests/writing-mode/tables/table-caption-bottom-1-ref.html index 8ffc4937d5..ae36e72e88 100644 --- a/layout/reftests/writing-mode/tables/table-caption-bottom-1-ref.html +++ b/layout/reftests/writing-mode/tables/table-caption-bottom-1-ref.html @@ -44,11 +44,11 @@ body > div {
-
-
table caption
+
+
table caption
-
-
table caption
+
+
table caption
diff --git a/layout/reftests/writing-mode/tables/table-caption-inline-end-1-ref.html b/layout/reftests/writing-mode/tables/table-caption-inline-end-1-ref.html deleted file mode 100644 index d26b51e26a..0000000000 --- a/layout/reftests/writing-mode/tables/table-caption-inline-end-1-ref.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - -
-
-
table caption
-
- -
-
table caption
-
-
- -
-
-
table caption
-
- -
-
-
table caption
-
diff --git a/layout/reftests/writing-mode/tables/table-caption-inline-end-1.html b/layout/reftests/writing-mode/tables/table-caption-inline-end-1.html deleted file mode 100644 index 835d4a6f0a..0000000000 --- a/layout/reftests/writing-mode/tables/table-caption-inline-end-1.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - -
table caption
- - - - -
table caption
- - - - -
table caption
- - - - -
table caption
diff --git a/layout/reftests/writing-mode/tables/table-caption-inline-start-1-ref.html b/layout/reftests/writing-mode/tables/table-caption-inline-start-1-ref.html deleted file mode 100644 index bbc533a0d1..0000000000 --- a/layout/reftests/writing-mode/tables/table-caption-inline-start-1-ref.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - -
-
table caption
-
-
- -
-
-
table caption
-
- -
-
table caption
-
-
- -
-
table caption
-
-
diff --git a/layout/reftests/writing-mode/tables/table-caption-inline-start-1.html b/layout/reftests/writing-mode/tables/table-caption-inline-start-1.html deleted file mode 100644 index 86f59a40a7..0000000000 --- a/layout/reftests/writing-mode/tables/table-caption-inline-start-1.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - -
table caption
- - - - -
table caption
- - - - -
table caption
- - - - -
table caption
diff --git a/layout/reftests/writing-mode/tables/table-caption-left-1-ref.html b/layout/reftests/writing-mode/tables/table-caption-left-1-ref.html index 7c10436c96..b9f46b0813 100644 --- a/layout/reftests/writing-mode/tables/table-caption-left-1-ref.html +++ b/layout/reftests/writing-mode/tables/table-caption-left-1-ref.html @@ -45,11 +45,21 @@ body > div {
-
table caption
-
+
table caption
+
+
+ +
+
table caption
+
-
-
table caption
+
table caption
+
+
+ +
+
table caption
+
diff --git a/layout/reftests/writing-mode/tables/table-caption-left-1.html b/layout/reftests/writing-mode/tables/table-caption-left-1.html index 469fb2783c..1096bcaeb3 100644 --- a/layout/reftests/writing-mode/tables/table-caption-left-1.html +++ b/layout/reftests/writing-mode/tables/table-caption-left-1.html @@ -55,7 +55,17 @@ caption { + + + +
table caption
+
table caption
+ + + + +
table caption
diff --git a/layout/reftests/writing-mode/tables/table-caption-right-1-ref.html b/layout/reftests/writing-mode/tables/table-caption-right-1-ref.html index 7a2b42cdd5..7e93276d1e 100644 --- a/layout/reftests/writing-mode/tables/table-caption-right-1-ref.html +++ b/layout/reftests/writing-mode/tables/table-caption-right-1-ref.html @@ -45,11 +45,21 @@ body > div {
-
-
table caption
+
+
table caption
+
+ +
+
+
table caption
-
table caption
-
+
+
table caption
+
+ +
+
+
table caption
diff --git a/layout/reftests/writing-mode/tables/table-caption-right-1.html b/layout/reftests/writing-mode/tables/table-caption-right-1.html index d4e36485cf..e6a4cd7347 100644 --- a/layout/reftests/writing-mode/tables/table-caption-right-1.html +++ b/layout/reftests/writing-mode/tables/table-caption-right-1.html @@ -55,7 +55,17 @@ caption { + + + +
table caption
+
table caption
+ + + + +
table caption
diff --git a/layout/reftests/writing-mode/tables/table-caption-top-1-ref.html b/layout/reftests/writing-mode/tables/table-caption-top-1-ref.html index 8c3ff8621f..ac90c6ec20 100644 --- a/layout/reftests/writing-mode/tables/table-caption-top-1-ref.html +++ b/layout/reftests/writing-mode/tables/table-caption-top-1-ref.html @@ -44,11 +44,11 @@ body > div {
-
table caption
-
+
table caption
+
-
table caption
-
+
table caption
+
diff --git a/layout/style/AnimationCommon.cpp b/layout/style/AnimationCommon.cpp index 684ecf4eb5..4934cca722 100644 --- a/layout/style/AnimationCommon.cpp +++ b/layout/style/AnimationCommon.cpp @@ -198,7 +198,9 @@ CommonAnimationManager::HasDocumentStateDependentStyle(StateRuleProcessorData* a } nsRestyleHint -CommonAnimationManager::HasAttributeDependentStyle(AttributeRuleProcessorData* aData) +CommonAnimationManager::HasAttributeDependentStyle( + AttributeRuleProcessorData* aData, + RestyleHintData& aRestyleHintDataResult) { return nsRestyleHint(0); } @@ -219,6 +221,7 @@ CommonAnimationManager::RulesMatching(ElementRuleProcessorData* aData) nsCSSPseudoElements::ePseudo_NotPseudoElement); if (rule) { aData->mRuleWalker->Forward(rule); + aData->mRuleWalker->CurrentNode()->SetIsAnimationRule(); } } @@ -238,6 +241,7 @@ CommonAnimationManager::RulesMatching(PseudoElementRuleProcessorData* aData) nsIStyleRule *rule = GetAnimationRule(aData->mElement, aData->mPseudoType); if (rule) { aData->mRuleWalker->Forward(rule); + aData->mRuleWalker->CurrentNode()->SetIsAnimationRule(); } } @@ -445,6 +449,15 @@ AnimValuesStyleRule::MapRuleInfoInto(nsRuleData* aRuleData) // Don't apply transitions or animations to things inside of // pseudo-elements. // FIXME (Bug 522599): Add tests for this. + + // Prevent structs from being cached on the rule node since we're inside + // a pseudo-element, as we could determine cacheability differently + // when walking the rule tree for a style context that is not inside + // a pseudo-element. Note that nsRuleNode::GetStyle##name_ and GetStyleData + // will never look at cached structs when we're animating things inside + // a pseduo-element, so that we don't incorrectly return a struct that + // is only appropriate for non-pseudo-elements. + aRuleData->mConditions.SetUncacheable(); return; } diff --git a/layout/style/AnimationCommon.h b/layout/style/AnimationCommon.h index cdbbd9f110..701623be7b 100644 --- a/layout/style/AnimationCommon.h +++ b/layout/style/AnimationCommon.h @@ -48,7 +48,8 @@ public: virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) override; virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override; virtual nsRestyleHint - HasAttributeDependentStyle(AttributeRuleProcessorData* aData) override; + HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + RestyleHintData& aRestyleHintDataResult) override; virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) override; virtual void RulesMatching(ElementRuleProcessorData* aData) override; virtual void RulesMatching(PseudoElementRuleProcessorData* aData) override; diff --git a/layout/style/CSSStyleSheet.cpp b/layout/style/CSSStyleSheet.cpp index ca754f2f6a..f7dc4dcc11 100644 --- a/layout/style/CSSStyleSheet.cpp +++ b/layout/style/CSSStyleSheet.cpp @@ -1731,6 +1731,15 @@ CSSStyleSheet::List(FILE* out, int32_t aIndent) const void CSSStyleSheet::ClearRuleCascades() { + // We might be in ClearRuleCascades because we had a modification + // to the sheet that resulted in an nsCSSSelector being destroyed. + // Tell the RestyleManager for each document we're used in + // so that they can drop any nsCSSSelector pointers (used for + // eRestyle_SomeDescendants) in their mPendingRestyles. + for (nsStyleSet* styleSet : mStyleSets) { + styleSet->ClearSelectors(); + } + bool removedSheetFromRuleProcessorCache = false; if (mRuleProcessors) { nsCSSRuleProcessor **iter = mRuleProcessors->Elements(), diff --git a/layout/style/CounterStyleManager.cpp b/layout/style/CounterStyleManager.cpp index 81d32317d3..95c52c4675 100644 --- a/layout/style/CounterStyleManager.cpp +++ b/layout/style/CounterStyleManager.cpp @@ -6,6 +6,7 @@ #include "CounterStyleManager.h" +#include "mozilla/ArenaObjectID.h" #include "mozilla/ArrayUtils.h" #include "mozilla/CheckedInt.h" #include "mozilla/MathAlgorithms.h" @@ -973,7 +974,7 @@ public: void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW { return aPresContext->PresShell()->AllocateByObjectID( - nsPresArena::DependentBuiltinCounterStyle_id, sz); + eArenaObjectID_DependentBuiltinCounterStyle, sz); } private: @@ -981,7 +982,7 @@ private: { nsIPresShell* shell = mManager->PresContext()->PresShell(); this->~DependentBuiltinCounterStyle(); - shell->FreeByObjectID(nsPresArena::DependentBuiltinCounterStyle_id, this); + shell->FreeByObjectID(eArenaObjectID_DependentBuiltinCounterStyle, this); } CounterStyleManager* mManager; @@ -1091,7 +1092,7 @@ public: void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW { return aPresContext->PresShell()->AllocateByObjectID( - nsPresArena::CustomCounterStyle_id, sz); + eArenaObjectID_CustomCounterStyle, sz); } private: @@ -1099,7 +1100,7 @@ private: { nsIPresShell* shell = mManager->PresContext()->PresShell(); this->~CustomCounterStyle(); - shell->FreeByObjectID(nsPresArena::CustomCounterStyle_id, this); + shell->FreeByObjectID(eArenaObjectID_CustomCounterStyle, this); } const nsTArray& GetSymbols(); diff --git a/layout/style/SVGAttrAnimationRuleProcessor.cpp b/layout/style/SVGAttrAnimationRuleProcessor.cpp index 3a1230f511..9eb31b1b23 100644 --- a/layout/style/SVGAttrAnimationRuleProcessor.cpp +++ b/layout/style/SVGAttrAnimationRuleProcessor.cpp @@ -62,7 +62,9 @@ SVGAttrAnimationRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessor } /* virtual */ nsRestyleHint -SVGAttrAnimationRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData) +SVGAttrAnimationRuleProcessor::HasAttributeDependentStyle( + AttributeRuleProcessorData* aData, + RestyleHintData& aRestyleHintDataResult) { return nsRestyleHint(0); } diff --git a/layout/style/SVGAttrAnimationRuleProcessor.h b/layout/style/SVGAttrAnimationRuleProcessor.h index 7a3fb65144..1750c761bd 100644 --- a/layout/style/SVGAttrAnimationRuleProcessor.h +++ b/layout/style/SVGAttrAnimationRuleProcessor.h @@ -44,7 +44,8 @@ public: virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) override; virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override; virtual nsRestyleHint - HasAttributeDependentStyle(AttributeRuleProcessorData* aData) override; + HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + RestyleHintData& aRestyleHintDataResult) override; virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) override; virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_MUST_OVERRIDE override; diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index 95eb154d96..fec9c46435 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -559,7 +559,7 @@ nsCSSSelector::ToString(nsAString& aString, CSSStyleSheet* aSheet, const nsCSSSelector *s = stack.ElementAt(index); stack.RemoveElementAt(index); - s->AppendToStringWithoutCombinators(aString, aSheet); + s->AppendToStringWithoutCombinators(aString, aSheet, false); // Append the combinator, if needed. if (!stack.IsEmpty()) { @@ -583,24 +583,85 @@ nsCSSSelector::ToString(nsAString& aString, CSSStyleSheet* aSheet, } void -nsCSSSelector::AppendToStringWithoutCombinators - (nsAString& aString, CSSStyleSheet* aSheet) const +nsCSSSelector::AppendToStringWithoutCombinators( + nsAString& aString, + CSSStyleSheet* aSheet, + bool aUseStandardNamespacePrefixes) const { - AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, false); + AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, false, + aUseStandardNamespacePrefixes); for (const nsCSSSelector* negation = mNegations; negation; negation = negation->mNegations) { aString.AppendLiteral(":not("); - negation->AppendToStringWithoutCombinatorsOrNegations(aString, aSheet, - true); + negation->AppendToStringWithoutCombinatorsOrNegations( + aString, aSheet, true, aUseStandardNamespacePrefixes); aString.Append(char16_t(')')); } } +#ifdef DEBUG +nsCString +nsCSSSelector::RestrictedSelectorToString() const +{ + MOZ_ASSERT(IsRestrictedSelector()); + + nsString result; + AppendToStringWithoutCombinators(result, nullptr, true); + return NS_ConvertUTF16toUTF8(result); +} + +static bool +AppendStandardNamespacePrefixToString(nsAString& aString, int32_t aNameSpace) +{ + if (aNameSpace == kNameSpaceID_Unknown) { + // Wildcard namespace; no prefix to write. + return false; + } + switch (aNameSpace) { + case kNameSpaceID_None: + break; + case kNameSpaceID_XML: + aString.AppendLiteral("xml"); + break; + case kNameSpaceID_XHTML: + aString.AppendLiteral("html"); + break; + case kNameSpaceID_XLink: + aString.AppendLiteral("xlink"); + break; + case kNameSpaceID_XSLT: + aString.AppendLiteral("xsl"); + break; + case kNameSpaceID_XBL: + aString.AppendLiteral("xbl"); + break; + case kNameSpaceID_MathML: + aString.AppendLiteral("math"); + break; + case kNameSpaceID_RDF: + aString.AppendLiteral("rdf"); + break; + case kNameSpaceID_XUL: + aString.AppendLiteral("xul"); + break; + case kNameSpaceID_SVG: + aString.AppendLiteral("svg"); + break; + default: + aString.AppendLiteral("ns"); + aString.AppendInt(aNameSpace); + break; + } + return true; +} +#endif + void nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations (nsAString& aString, CSSStyleSheet* aSheet, - bool aIsNegated) const + bool aIsNegated, + bool aUseStandardNamespacePrefixes) const { nsAutoString temp; bool isPseudoElement = IsPseudoElement(); @@ -616,7 +677,18 @@ nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations // null, that means that the only namespaces we could have are the // wildcard namespace (which can be implicit in this case) and the "none" // namespace, which then needs to be explicitly specified. - if (!sheetNS) { + if (aUseStandardNamespacePrefixes) { +#ifdef DEBUG + // We have no sheet to look up prefix information from. This is + // only for debugging, so use some "standard" prefixes that + // are recognizable. + wroteNamespace = + AppendStandardNamespacePrefixToString(aString, mNameSpace); + if (wroteNamespace) { + aString.Append(char16_t('|')); + } +#endif + } else if (!sheetNS) { NS_ASSERTION(mNameSpace == kNameSpaceID_Unknown || mNameSpace == kNameSpaceID_None, "How did we get this namespace?"); @@ -740,7 +812,12 @@ nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations aString.Append(char16_t('*')); aString.Append(char16_t('|')); } else if (list->mNameSpace != kNameSpaceID_None) { - if (aSheet) { + if (aUseStandardNamespacePrefixes) { +#ifdef DEBUG + AppendStandardNamespacePrefixToString(aString, list->mNameSpace); + aString.Append(char16_t('|')); +#endif + } else if (aSheet) { nsXMLNameSpaceMap *sheetNS = aSheet->GetNameSpaceMap(); nsIAtom *prefixAtom = sheetNS->FindPrefix(list->mNameSpace); // Default namespaces don't apply to attribute selectors, so diff --git a/layout/style/StyleRule.h b/layout/style/StyleRule.h index 8397e16ad9..317dafb335 100644 --- a/layout/style/StyleRule.h +++ b/layout/style/StyleRule.h @@ -159,22 +159,27 @@ public: void ToString(nsAString& aString, mozilla::CSSStyleSheet* aSheet, bool aAppend = false) const; -#ifdef DEBUG bool IsRestrictedSelector() const { return PseudoType() == nsCSSPseudoElements::ePseudo_NotPseudoElement; } + +#ifdef DEBUG + nsCString RestrictedSelectorToString() const; #endif private: void AddPseudoClassInternal(nsPseudoClassList *aPseudoClass); nsCSSSelector* Clone(bool aDeepNext, bool aDeepNegations) const; - void AppendToStringWithoutCombinators(nsAString& aString, - mozilla::CSSStyleSheet* aSheet) const; - void AppendToStringWithoutCombinatorsOrNegations(nsAString& aString, - mozilla::CSSStyleSheet* aSheet, - bool aIsNegated) - const; + void AppendToStringWithoutCombinators( + nsAString& aString, + mozilla::CSSStyleSheet* aSheet, + bool aUseStandardNamespacePrefixes) const; + void AppendToStringWithoutCombinatorsOrNegations( + nsAString& aString, + mozilla::CSSStyleSheet* aSheet, + bool aIsNegated, + bool aUseStandardNamespacePrefixes) const; // Returns true if this selector can have a namespace specified (which // happens if and only if the default namespace would apply to this // selector). diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp index 50ff0a30fb..19aff1c33f 100644 --- a/layout/style/nsAnimationManager.cpp +++ b/layout/style/nsAnimationManager.cpp @@ -546,6 +546,22 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext, return GetAnimationRule(aElement, aStyleContext->GetPseudoType()); } +void +nsAnimationManager::StopAnimationsForElement( + mozilla::dom::Element* aElement, + nsCSSPseudoElements::Type aPseudoType) +{ + MOZ_ASSERT(aElement); + AnimationCollection* collection = + GetAnimations(aElement, aPseudoType, false); + if (!collection) { + return; + } + + nsAutoAnimationMutationBatch mb(aElement->OwnerDoc()); + collection->Destroy(); +} + struct KeyframeData { float mKey; uint32_t mIndex; // store original order since sort algorithm is not stable diff --git a/layout/style/nsAnimationManager.h b/layout/style/nsAnimationManager.h index 1eaf6b7ebd..8d924d9af7 100644 --- a/layout/style/nsAnimationManager.h +++ b/layout/style/nsAnimationManager.h @@ -308,6 +308,12 @@ public: void DispatchEvents() { mEventDispatcher.DispatchEvents(mPresContext); } void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); } + // Stop animations on the element. This method takes the real element + // rather than the element for the generated content for animations on + // ::before and ::after. + void StopAnimationsForElement(mozilla::dom::Element* aElement, + nsCSSPseudoElements::Type aPseudoType); + protected: virtual ~nsAnimationManager() {} diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index 350eaf4894..e08a279650 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -149,10 +149,6 @@ CSS_KEY(bidi-override, bidi_override) CSS_KEY(blink, blink) CSS_KEY(block, block) CSS_KEY(block-axis, block_axis) -CSS_KEY(block-end, block_end) -CSS_KEY(block-end-outside, block_end_outside) -CSS_KEY(block-start, block_start) -CSS_KEY(block-start-outside, block_start_outside) CSS_KEY(blur, blur) CSS_KEY(bold, bold) CSS_KEY(bold-fraktur, bold_fraktur) @@ -310,10 +306,8 @@ CSS_KEY(initial, initial) CSS_KEY(inline, inline) CSS_KEY(inline-axis, inline_axis) CSS_KEY(inline-block, inline_block) -CSS_KEY(inline-end, inline_end) CSS_KEY(inline-flex, inline_flex) CSS_KEY(inline-grid, inline_grid) -CSS_KEY(inline-start, inline_start) CSS_KEY(inline-table, inline_table) CSS_KEY(inset, inset) CSS_KEY(inside, inside) diff --git a/layout/style/nsCSSProperty.h b/layout/style/nsCSSProperty.h index 79d92a6887..0e0536da78 100644 --- a/layout/style/nsCSSProperty.h +++ b/layout/style/nsCSSProperty.h @@ -8,6 +8,8 @@ #ifndef nsCSSProperty_h___ #define nsCSSProperty_h___ +#include + /* Declare the enum list using the magic of preprocessing enum values are "eCSSProperty_foo" (where foo is the property) @@ -64,6 +66,17 @@ enum nsCSSProperty { eCSSPropertyExtra_variable }; +namespace mozilla { + +template<> +inline PLDHashNumber +Hash(const nsCSSProperty& aValue) +{ + return uint32_t(aValue); +} + +} // namespace mozilla + // The "descriptors" that can appear in a @font-face rule. // They have the syntax of properties but different value rules. enum nsCSSFontDesc { diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index a93a6f6a49..19429221f5 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -916,12 +916,6 @@ const KTableValue nsCSSProps::kBoxSizingKTable[] = { }; const KTableValue nsCSSProps::kCaptionSideKTable[] = { - eCSSKeyword_block_start, NS_STYLE_CAPTION_SIDE_BSTART, - eCSSKeyword_block_end, NS_STYLE_CAPTION_SIDE_BEND, - eCSSKeyword_inline_start, NS_STYLE_CAPTION_SIDE_ISTART, - eCSSKeyword_inline_end, NS_STYLE_CAPTION_SIDE_IEND, - eCSSKeyword_block_start_outside, NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE, - eCSSKeyword_block_end_outside, NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE, eCSSKeyword_top, NS_STYLE_CAPTION_SIDE_TOP, eCSSKeyword_right, NS_STYLE_CAPTION_SIDE_RIGHT, eCSSKeyword_bottom, NS_STYLE_CAPTION_SIDE_BOTTOM, diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index c8acc39826..6fd8df9b63 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -2832,7 +2832,9 @@ EnumerateSelectors(nsTArray& aSelectors, AttributeEnumData* aDat } nsRestyleHint -nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData) +nsCSSRuleProcessor::HasAttributeDependentStyle( + AttributeRuleProcessorData* aData, + RestyleHintData& aRestyleHintDataResult) { // We could try making use of aData->mModType, but :not rules make it a bit // of a pain to do so... So just ignore it for now. diff --git a/layout/style/nsCSSRuleProcessor.h b/layout/style/nsCSSRuleProcessor.h index a60af54228..3fc709b284 100644 --- a/layout/style/nsCSSRuleProcessor.h +++ b/layout/style/nsCSSRuleProcessor.h @@ -144,7 +144,9 @@ public: virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override; virtual nsRestyleHint - HasAttributeDependentStyle(AttributeRuleProcessorData* aData) override; + HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + mozilla::RestyleHintData& aRestyleHintDataResult) + override; virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) override; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index d45f571ec9..79e639ba11 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -222,7 +222,9 @@ nsComputedDOMStyle::nsComputedDOMStyle(dom::Element* aElement, : mDocumentWeak(nullptr), mOuterFrame(nullptr), mInnerFrame(nullptr), mPresShell(nullptr), mStyleType(aStyleType), - mExposeVisitedStyle(false) + mStyleContextGeneration(0), + mExposeVisitedStyle(false), + mResolvedStyleContext(false) { MOZ_ASSERT(aElement && aPresShell); @@ -262,9 +264,23 @@ nsComputedDOMStyle::nsComputedDOMStyle(dom::Element* aElement, nsComputedDOMStyle::~nsComputedDOMStyle() { + ClearStyleContext(); } -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsComputedDOMStyle, mContent) +NS_IMPL_CYCLE_COLLECTION_CLASS(nsComputedDOMStyle) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsComputedDOMStyle) + tmp->ClearStyleContext(); // remove observer before clearing mContent + NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsComputedDOMStyle) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsComputedDOMStyle) NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsComputedDOMStyle) return tmp->IsBlack(); @@ -281,6 +297,7 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END // QueryInterface implementation for nsComputedDOMStyle NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsComputedDOMStyle) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) NS_INTERFACE_MAP_END_INHERITING(nsDOMCSSDeclaration) @@ -333,7 +350,7 @@ nsComputedDOMStyle::GetLength(uint32_t* aLength) // Make sure we have up to date style so that we can include custom // properties. UpdateCurrentStyleSources(false); - if (mStyleContextHolder) { + if (mStyleContext) { length += StyleVariables()->mVariables.Count(); } @@ -572,13 +589,39 @@ nsComputedDOMStyle::GetCSSParsingEnvironment(CSSParsingEnvironment& aCSSParseEnv aCSSParseEnv.mPrincipal = nullptr; } +void +nsComputedDOMStyle::ClearStyleContext() +{ + if (mResolvedStyleContext) { + mResolvedStyleContext = false; + mContent->RemoveMutationObserver(this); + } + mStyleContext = nullptr; +} + +void +nsComputedDOMStyle::SetResolvedStyleContext(nsRefPtr&& aContext) +{ + if (!mResolvedStyleContext) { + mResolvedStyleContext = true; + mContent->AddMutationObserver(this); + } + mStyleContext = aContext; +} + +void +nsComputedDOMStyle::SetFrameStyleContext(nsStyleContext* aContext) +{ + ClearStyleContext(); + mStyleContext = aContext; +} + void nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) { - MOZ_ASSERT(!mStyleContextHolder); - nsCOMPtr document = do_QueryReferent(mDocumentWeak); if (!document) { + ClearStyleContext(); return; } @@ -601,11 +644,25 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) mPresShell = document->GetShell(); if (!mPresShell || !mPresShell->GetPresContext()) { + ClearStyleContext(); return; } + uint64_t currentGeneration = + mPresShell->GetPresContext()->GetRestyleGeneration(); + + if (mStyleContext) { + if (mStyleContextGeneration == currentGeneration) { + // Our cached style context is still valid. + return; + } + // We've processed some restyles, so the cached style context might + // be out of date. + mStyleContext = nullptr; + } + // XXX the !mContent->IsHTMLElement(nsGkAtoms::area) - // check is needed due to bug 135040 (to avoid using + // check is needed due to bug 135040 (to avoid using // mPrimaryFrame). Remove it once that's fixed. if (!mPseudo && mStyleType == eAll && !mContent->IsHTMLElement(nsGkAtoms::area)) { @@ -623,19 +680,19 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) "the inner table"); } - mStyleContextHolder = mInnerFrame->StyleContext(); - NS_ASSERTION(mStyleContextHolder, "Frame without style context?"); + SetFrameStyleContext(mInnerFrame->StyleContext()); + NS_ASSERTION(mStyleContext, "Frame without style context?"); } } - if (!mStyleContextHolder || mStyleContextHolder->HasPseudoElementData()) { + if (!mStyleContext || mStyleContext->HasPseudoElementData()) { #ifdef DEBUG - if (mStyleContextHolder) { + if (mStyleContext) { // We want to check that going through this path because of // HasPseudoElementData is rare, because it slows us down a good // bit. So check that we're really inside something associated // with a pseudo-element that contains elements. - nsStyleContext *topWithPseudoElementData = mStyleContextHolder; + nsStyleContext* topWithPseudoElementData = mStyleContext; while (topWithPseudoElementData->GetParent()->HasPseudoElementData()) { topWithPseudoElementData = topWithPseudoElementData->GetParent(); } @@ -651,17 +708,26 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) } #endif // Need to resolve a style context - mStyleContextHolder = + nsRefPtr resolvedStyleContext = nsComputedDOMStyle::GetStyleContextForElementNoFlush( mContent->AsElement(), mPseudo, presShellForContent ? presShellForContent.get() : mPresShell, mStyleType); - if (!mStyleContextHolder) { + if (!resolvedStyleContext) { + ClearStyleContext(); return; } - NS_ASSERTION(mPseudo || !mStyleContextHolder->HasPseudoElementData(), + // No need to re-get the generation, even though GetStyleContextForElement + // will flush, since we flushed style at the top of this function. + NS_ASSERTION(mPresShell && + currentGeneration == + mPresShell->GetPresContext()->GetRestyleGeneration(), + "why should we have flushed style again?"); + + SetResolvedStyleContext(Move(resolvedStyleContext)); + NS_ASSERTION(mPseudo || !mStyleContext->HasPseudoElementData(), "should not have pseudo-element data"); } @@ -669,10 +735,10 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush) // require chrome privilege. MOZ_ASSERT(!mExposeVisitedStyle || nsContentUtils::IsCallerChrome(), "mExposeVisitedStyle set incorrectly"); - if (mExposeVisitedStyle && mStyleContextHolder->RelevantLinkVisited()) { - nsStyleContext *styleIfVisited = mStyleContextHolder->GetStyleIfVisited(); + if (mExposeVisitedStyle && mStyleContext->RelevantLinkVisited()) { + nsStyleContext *styleIfVisited = mStyleContext->GetStyleIfVisited(); if (styleIfVisited) { - mStyleContextHolder = styleIfVisited; + mStyleContext = styleIfVisited; } } } @@ -684,9 +750,12 @@ nsComputedDOMStyle::ClearCurrentStyleSources() mInnerFrame = nullptr; mPresShell = nullptr; - // Release the current style context for it should be re-resolved - // whenever a frame is not available. - mStyleContextHolder = nullptr; + // Release the current style context if we got it off the frame. + // For a style context we resolved, keep it around so that we + // can re-use it next time this object is queried. + if (!mResolvedStyleContext) { + mStyleContext = nullptr; + } } already_AddRefed @@ -733,7 +802,7 @@ nsComputedDOMStyle::GetPropertyCSSValue(const nsAString& aPropertyName, ErrorRes } UpdateCurrentStyleSources(needsLayoutFlush); - if (!mStyleContextHolder) { + if (!mStyleContext) { aRv.Throw(NS_ERROR_NOT_AVAILABLE); return nullptr; } @@ -802,7 +871,7 @@ nsComputedDOMStyle::IndexedGetter(uint32_t aIndex, bool& aFound, // Custom properties are exposed with indexed properties just after all // of the built-in properties. UpdateCurrentStyleSources(false); - if (!mStyleContextHolder) { + if (!mStyleContext) { aFound = false; return; } @@ -1277,8 +1346,8 @@ nsComputedDOMStyle::DoGetTransform() RuleNodeCacheConditions dummy; gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(display->mSpecifiedTransform->mHead, - mStyleContextHolder, - mStyleContextHolder->PresContext(), + mStyleContext, + mStyleContext->PresContext(), dummy, refBox, float(mozilla::AppUnitsPerCSSPixel())); @@ -3827,7 +3896,7 @@ nsComputedDOMStyle::DoGetAlignSelf() if (computedAlignSelf == NS_STYLE_ALIGN_SELF_AUTO) { // "align-self: auto" needs to compute to parent's align-items value. - nsStyleContext* parentStyleContext = mStyleContextHolder->GetParent(); + nsStyleContext* parentStyleContext = mStyleContext->GetParent(); if (parentStyleContext) { computedAlignSelf = parentStyleContext->StylePosition()->mAlignItems; @@ -3926,7 +3995,7 @@ nsComputedDOMStyle::DoGetJustifyItems() nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue; nsAutoString str; auto justify = StylePosition()-> - ComputedJustifyItems(StyleDisplay(), mStyleContextHolder->GetParent()); + ComputedJustifyItems(StyleDisplay(), mStyleContext->GetParent()); nsCSSValue::AppendAlignJustifyValueToString(justify, str); val->SetString(str); return val; @@ -4659,7 +4728,7 @@ nsComputedDOMStyle::GetLineHeightCoord(nscoord& aCoord) // lie about font size inflation since we lie about font size (since // the inflation only applies to text) - aCoord = nsHTMLReflowState::CalcLineHeight(mContent, mStyleContextHolder, + aCoord = nsHTMLReflowState::CalcLineHeight(mContent, mStyleContext, blockHeight, 1.0f); // CalcLineHeight uses font->mFont.size, but we want to use @@ -6008,6 +6077,17 @@ nsComputedDOMStyle::DoGetCustomProperty(const nsAString& aPropertyName) return val; } +void +nsComputedDOMStyle::ParentChainChanged(nsIContent* aContent) +{ + NS_ASSERTION(mContent == aContent, "didn't we register mContent?"); + NS_ASSERTION(mResolvedStyleContext, + "should have only registered an observer when " + "mResolvedStyleContext is true"); + + ClearStyleContext(); +} + /* static */ nsComputedStyleMap* nsComputedDOMStyle::GetComputedStyleMap() { diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 0c6ede7c89..6d35c6ecd8 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -9,6 +9,8 @@ #define nsComputedDOMStyle_h__ #include "nsAutoPtr.h" +#include "mozilla/ArenaRefPtr.h" +#include "mozilla/ArenaRefPtrInlines.h" #include "mozilla/Attributes.h" #include "nsCOMPtr.h" #include "nscore.h" @@ -43,6 +45,7 @@ class nsStyleSides; struct nsTimingFunction; class nsComputedDOMStyle final : public nsDOMCSSDeclaration + , public nsStubMutationObserver { public: typedef nsCSSProps::KTableValue KTableValue; @@ -108,6 +111,9 @@ public: static void RegisterPrefChangeCallbacks(); static void UnregisterPrefChangeCallbacks(); + // nsIMutationObserver + NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED + private: virtual ~nsComputedDOMStyle(); @@ -122,13 +128,18 @@ private: mozilla::dom::CSSValue* CreateTextAlignValue(uint8_t aAlign, bool aAlignTrue, const KTableValue aTable[]); - // This indicates error by leaving mStyleContextHolder null. + // This indicates error by leaving mStyleContext null. void UpdateCurrentStyleSources(bool aNeedsLayoutFlush); void ClearCurrentStyleSources(); + // Helper functions called by UpdateCurrentStyleSources. + void ClearStyleContext(); + void SetResolvedStyleContext(nsRefPtr&& aContext); + void SetFrameStyleContext(nsStyleContext* aContext); + #define STYLE_STRUCT(name_, checkdata_cb_) \ const nsStyle##name_ * Style##name_() { \ - return mStyleContextHolder->Style##name_(); \ + return mStyleContext->Style##name_(); \ } #include "nsStyleStructList.h" #undef STYLE_STRUCT @@ -600,12 +611,23 @@ private: nsWeakPtr mDocumentWeak; nsCOMPtr mContent; - /* - * Strong reference to the style context while we're accessing the data from - * it. This can be either a style context we resolved ourselves or a style - * context we got from our frame. + /** + * Strong reference to the style context we access data from. This can be + * either a style context we resolved ourselves or a style context we got + * from our frame. + * + * If we got the style context from the frame, we clear out mStyleContext + * in ClearCurrentStyleSources. If we resolved one ourselves, then + * ClearCurrentStyleSources leaves it in mStyleContext for use the next + * time this nsComputedDOMStyle object is queried. UpdateCurrentStyleSources + * in this case will check that the style context is still valid to be used, + * by checking whether flush styles results in any restyles having been + * processed. + * + * Since an ArenaRefPtr is used to hold the style context, it will be cleared + * if the pres arena from which it was allocated goes away. */ - nsRefPtr mStyleContextHolder; + mozilla::ArenaRefPtr mStyleContext; nsCOMPtr mPseudo; /* @@ -631,8 +653,20 @@ private: */ StyleType mStyleType; + /** + * The nsComputedDOMStyle generation at the time we last resolved a style + * context and stored it in mStyleContext. + */ + uint64_t mStyleContextGeneration; + bool mExposeVisitedStyle; + /** + * Whether we resolved a style context last time we called + * UpdateCurrentStyleSources. Initially false. + */ + bool mResolvedStyleContext; + #ifdef DEBUG bool mFlushedPendingReflows; #endif diff --git a/layout/style/nsHTMLCSSStyleSheet.cpp b/layout/style/nsHTMLCSSStyleSheet.cpp index 9cde112e2b..a8669a6075 100644 --- a/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/layout/style/nsHTMLCSSStyleSheet.cpp @@ -144,7 +144,9 @@ nsHTMLCSSStyleSheet::HasDocumentStateDependentStyle(StateRuleProcessorData* aDat // Test if style is dependent on attribute /* virtual */ nsRestyleHint -nsHTMLCSSStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aData) +nsHTMLCSSStyleSheet::HasAttributeDependentStyle( + AttributeRuleProcessorData* aData, + RestyleHintData& aRestyleHintDataResult) { // Perhaps should check that it's XUL, SVG, (or HTML) namespace, but // it doesn't really matter. diff --git a/layout/style/nsHTMLCSSStyleSheet.h b/layout/style/nsHTMLCSSStyleSheet.h index f1ecf2fe42..234f3e24cd 100644 --- a/layout/style/nsHTMLCSSStyleSheet.h +++ b/layout/style/nsHTMLCSSStyleSheet.h @@ -43,7 +43,8 @@ public: virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) override; virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override; virtual nsRestyleHint - HasAttributeDependentStyle(AttributeRuleProcessorData* aData) override; + HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + mozilla::RestyleHintData& aRestyleHintDataResult) override; virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) override; virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_MUST_OVERRIDE override; diff --git a/layout/style/nsHTMLStyleSheet.cpp b/layout/style/nsHTMLStyleSheet.cpp index 39f9e665ae..36c61c2fe5 100644 --- a/layout/style/nsHTMLStyleSheet.cpp +++ b/layout/style/nsHTMLStyleSheet.cpp @@ -335,7 +335,9 @@ nsHTMLStyleSheet::HasDocumentStateDependentStyle(StateRuleProcessorData* aData) } /* virtual */ nsRestyleHint -nsHTMLStyleSheet::HasAttributeDependentStyle(AttributeRuleProcessorData* aData) +nsHTMLStyleSheet::HasAttributeDependentStyle( + AttributeRuleProcessorData* aData, + RestyleHintData& aRestyleHintDataResult) { // Do nothing on before-change checks if (!aData->mAttrHasChanged) { diff --git a/layout/style/nsHTMLStyleSheet.h b/layout/style/nsHTMLStyleSheet.h index 5ada9333bb..390cfb782b 100644 --- a/layout/style/nsHTMLStyleSheet.h +++ b/layout/style/nsHTMLStyleSheet.h @@ -45,7 +45,8 @@ public: virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) override; virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) override; virtual nsRestyleHint - HasAttributeDependentStyle(AttributeRuleProcessorData* aData) override; + HasAttributeDependentStyle(AttributeRuleProcessorData* aData, + mozilla::RestyleHintData& aRestyleHintDataResult) override; virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) override; virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const MOZ_MUST_OVERRIDE override; diff --git a/layout/style/nsIStyleRuleProcessor.h b/layout/style/nsIStyleRuleProcessor.h index 00182dd380..2075050d40 100644 --- a/layout/style/nsIStyleRuleProcessor.h +++ b/layout/style/nsIStyleRuleProcessor.h @@ -115,8 +115,9 @@ public: * only, and may err on the side of reporting more dependencies than * really exist. */ - virtual nsRestyleHint - HasAttributeDependentStyle(AttributeRuleProcessorData* aData) = 0; + virtual nsRestyleHint HasAttributeDependentStyle( + AttributeRuleProcessorData* aData, + mozilla::RestyleHintData& aRestyleHintDataResult) = 0; /** * Do any processing that needs to happen as a result of a change in diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index e0c5ffcbb8..f264493e73 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -1402,7 +1402,7 @@ void* nsRuleNode::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW { // Check the recycle list first. - return aPresContext->PresShell()->AllocateByObjectID(nsPresArena::nsRuleNode_id, sz); + return aPresContext->PresShell()->AllocateByObjectID(eArenaObjectID_nsRuleNode, sz); } // Overridden to prevent the global delete from being called, since the memory @@ -1457,7 +1457,7 @@ nsRuleNode::DestroyInternal(nsRuleNode ***aDestroyQueueTail) // Don't let the memory be freed, since it will be recycled // instead. Don't call the global operator delete. - mPresContext->PresShell()->FreeByObjectID(nsPresArena::nsRuleNode_id, this); + mPresContext->PresShell()->FreeByObjectID(eArenaObjectID_nsRuleNode, this); } nsRuleNode* nsRuleNode::CreateRootNode(nsPresContext* aPresContext) @@ -1475,7 +1475,8 @@ nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent, mNextSibling(nullptr), mDependentBits((uint32_t(aLevel) << NS_RULE_NODE_LEVEL_SHIFT) | (aIsImportant ? NS_RULE_NODE_IS_IMPORTANT : 0)), - mNoneBits(0), + mNoneBits(aParent ? aParent->mNoneBits & NS_RULE_NODE_HAS_ANIMATION_DATA : + 0), mRefCnt(0) { MOZ_ASSERT(aContext); @@ -2754,6 +2755,14 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex /* We can't be cached in the rule node. We have to be put right */ \ /* on the style context. */ \ aContext->SetStyle(eStyleStruct_##type_, data_); \ + if (aContext->GetParent()) { \ + /* This is pessimistic; we could be uncacheable because we had a */ \ + /* relative font-weight, for example, which does not need to defeat */ \ + /* the restyle optimizations in RestyleManager.cpp that look at */ \ + /* NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE. */ \ + aContext->GetParent()-> \ + AddStyleBit(NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE); \ + } \ } \ \ return data_; @@ -4357,13 +4366,13 @@ nsRuleNode::ComputeTextData(void* aStartStruct, parentText->mTextCombineUpright, NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE, 0, 0, 0, 0); - // -moz-text-discard: enum, inherit, initial + // -moz-control-character-visibility: enum, inherit, initial SetDiscrete(*aRuleData->ValueForControlCharacterVisibility(), text->mControlCharacterVisibility, conditions, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mControlCharacterVisibility, - NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN, 0, 0, 0, 0); + NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE, 0, 0, 0, 0); COMPUTE_END_INHERITED(Text, text) } @@ -8015,7 +8024,7 @@ nsRuleNode::ComputeTableBorderData(void* aStartStruct, table->mCaptionSide, conditions, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentTable->mCaptionSide, - NS_STYLE_CAPTION_SIDE_BSTART, 0, 0, 0, 0); + NS_STYLE_CAPTION_SIDE_TOP, 0, 0, 0, 0); // empty-cells: enum, inherit, initial SetDiscrete(*aRuleData->ValueForEmptyCells(), @@ -9347,9 +9356,14 @@ nsRuleNode::GetStyleData(nsStyleStructID aSID, "in some way."); const void *data; - data = mStyleData.GetStyleData(aSID, aContext); - if (MOZ_LIKELY(data != nullptr)) - return data; // We have a fully specified struct. Just return it. + + // Never use cached data for animated style inside a pseudo-element; + // see comment on cacheability in AnimValuesStyleRule::MapRuleInfoInto. + if (!(HasAnimationData() && ParentHasPseudoElementData(aContext))) { + data = mStyleData.GetStyleData(aSID, aContext); + if (MOZ_LIKELY(data != nullptr)) + return data; // We have a fully specified struct. Just return it. + } if (MOZ_UNLIKELY(!aComputeData)) return nullptr; @@ -9805,3 +9819,10 @@ nsRuleNode::ComputeColor(const nsCSSValue& aValue, nsPresContext* aPresContext, MOZ_ASSERT(ok || !(aPresContext && aStyleContext)); return ok; } + +/* static */ bool +nsRuleNode::ParentHasPseudoElementData(nsStyleContext* aContext) +{ + nsStyleContext* parent = aContext->GetParent(); + return parent && parent->HasPseudoElementData(); +} diff --git a/layout/style/nsRuleNode.h b/layout/style/nsRuleNode.h index 854719adb9..3e8014e5bf 100644 --- a/layout/style/nsRuleNode.h +++ b/layout/style/nsRuleNode.h @@ -11,6 +11,7 @@ #ifndef nsRuleNode_h___ #define nsRuleNode_h___ +#include "mozilla/ArenaObjectID.h" #include "mozilla/PodOperations.h" #include "mozilla/RangedArray.h" #include "mozilla/RuleNodeCacheConditions.h" @@ -35,7 +36,7 @@ struct nsInheritedStyleData void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsInheritedStyleData_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsInheritedStyleData, sz); } void DestroyStructs(uint64_t aBits, nsPresContext* aContext) { @@ -54,7 +55,7 @@ struct nsInheritedStyleData void Destroy(uint64_t aBits, nsPresContext* aContext) { DestroyStructs(aBits, aContext); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsInheritedStyleData_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsInheritedStyleData, this); } nsInheritedStyleData() { @@ -83,7 +84,7 @@ struct nsResetStyleData void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsResetStyleData_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsResetStyleData, sz); } void Destroy(uint64_t aBits, nsPresContext* aContext) { @@ -99,7 +100,7 @@ struct nsResetStyleData #undef STYLE_STRUCT_INHERITED aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsResetStyleData_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsResetStyleData, this); } }; @@ -117,8 +118,8 @@ struct nsConditionalResetStyleData : mConditions(aConditions), mStyleStruct(aStyleStruct), mNext(aNext) {} void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { - return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsConditionalResetStyleDataEntry_id, sz); + return aContext->PresShell()->AllocateByObjectID( + mozilla::eArenaObjectID_nsConditionalResetStyleDataEntry, sz); } const mozilla::RuleNodeCacheConditions mConditions; @@ -146,8 +147,8 @@ struct nsConditionalResetStyleData } void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { - return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsConditionalResetStyleData_id, sz); + return aContext->PresShell()->AllocateByObjectID( + mozilla::eArenaObjectID_nsConditionalResetStyleData, sz); } void* GetStyleData(nsStyleStructID aSID) const { @@ -221,7 +222,7 @@ struct nsConditionalResetStyleData static_cast(e->mStyleStruct)->Destroy(aContext); \ Entry* next = e->mNext; \ aContext->PresShell()->FreeByObjectID( \ - nsPresArena::nsConditionalResetStyleDataEntry_id, e); \ + mozilla::eArenaObjectID_nsConditionalResetStyleDataEntry, e); \ e = next; \ } while (e); \ } \ @@ -233,8 +234,8 @@ struct nsConditionalResetStyleData #undef STYLE_STRUCT_RESET #undef STYLE_STRUCT_INHERITED - aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsConditionalResetStyleData_id, this); + aContext->PresShell()->FreeByObjectID( + mozilla::eArenaObjectID_nsConditionalResetStyleData, this); } }; @@ -843,6 +844,29 @@ public: return (mDependentBits & NS_RULE_NODE_USED_DIRECTLY) != 0; } + /** + * Is the mRule of this rule node an AnimValuesStyleRule? + */ + void SetIsAnimationRule() { + MOZ_ASSERT(!HaveChildren() || + (mDependentBits & NS_RULE_NODE_IS_ANIMATION_RULE), + "SetIsAnimationRule must only set the IS_ANIMATION_RULE bit " + "before the rule node has children"); + mDependentBits |= NS_RULE_NODE_IS_ANIMATION_RULE; + mNoneBits |= NS_RULE_NODE_HAS_ANIMATION_DATA; + } + bool IsAnimationRule() const { + return (mDependentBits & NS_RULE_NODE_IS_ANIMATION_RULE) != 0; + } + + /** + * Is the mRule of this rule node or any of its ancestors an + * AnimValuesStyleRule? + */ + bool HasAnimationData() const { + return (mNoneBits & NS_RULE_NODE_HAS_ANIMATION_DATA) != 0; + } + // NOTE: Does not |AddRef|. Null only for the root. nsIStyleRule* GetRule() const { return mRule; } // NOTE: Does not |AddRef|. Never null. @@ -866,9 +890,14 @@ public: "in some way."); \ \ const nsStyle##name_ *data; \ - data = mStyleData.GetStyle##name_(); \ - if (MOZ_LIKELY(data != nullptr)) \ - return data; \ + \ + /* Never use cached data for animated style inside a pseudo-element; */ \ + /* see comment on cacheability in AnimValuesStyleRule::MapRuleInfoInto */ \ + if (!(HasAnimationData() && ParentHasPseudoElementData(aContext))) { \ + data = mStyleData.GetStyle##name_(); \ + if (MOZ_LIKELY(data != nullptr)) \ + return data; \ + } \ \ if (!aComputeData) \ return nullptr; \ @@ -891,9 +920,14 @@ public: "in some way."); \ \ const nsStyle##name_ *data; \ - data = mStyleData.GetStyle##name_(aContext); \ - if (MOZ_LIKELY(data != nullptr)) \ - return data; \ + \ + /* Never use cached data for animated style inside a pseudo-element; */ \ + /* see comment on cacheability in AnimValuesStyleRule::MapRuleInfoInto */ \ + if (!(HasAnimationData() && ParentHasPseudoElementData(aContext))) { \ + data = mStyleData.GetStyle##name_(aContext); \ + if (MOZ_LIKELY(data != nullptr)) \ + return data; \ + } \ \ if (!aComputeData) \ return nullptr; \ @@ -1017,6 +1051,8 @@ public: nsPresContext* aPresContext, nsStyleContext* aStyleContext, nscolor& aResult); + + static bool ParentHasPseudoElementData(nsStyleContext* aContext); }; #endif diff --git a/layout/style/nsStyleConsts.h b/layout/style/nsStyleConsts.h index b4a61ec061..80be81f420 100644 --- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -957,18 +957,16 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) { #define NS_STYLE_TABLE_EMPTY_CELLS_HIDE 0 #define NS_STYLE_TABLE_EMPTY_CELLS_SHOW 1 -#define NS_STYLE_CAPTION_SIDE_BSTART 0 // matches eLogicalSideBStart -#define NS_STYLE_CAPTION_SIDE_BEND 1 // matches eLogicalSideBEnd -#define NS_STYLE_CAPTION_SIDE_ISTART 2 // matches eLogicalSideIStart -#define NS_STYLE_CAPTION_SIDE_IEND 3 // matches eLogicalSideIEnd -#define NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE 4 -#define NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE 5 -#define NS_STYLE_CAPTION_SIDE_TOP 6 -#define NS_STYLE_CAPTION_SIDE_RIGHT 7 -#define NS_STYLE_CAPTION_SIDE_BOTTOM 8 -#define NS_STYLE_CAPTION_SIDE_LEFT 9 -#define NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE 10 -#define NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE 11 +// Constants for the caption-side property. Note that despite having "physical" +// names, these are actually interpreted according to the table's writing-mode: +// TOP and BOTTOM are treated as block-start and -end respectively, and LEFT +// and RIGHT are treated as line-left and -right. +#define NS_STYLE_CAPTION_SIDE_TOP 0 +#define NS_STYLE_CAPTION_SIDE_RIGHT 1 +#define NS_STYLE_CAPTION_SIDE_BOTTOM 2 +#define NS_STYLE_CAPTION_SIDE_LEFT 3 +#define NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE 4 +#define NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE 5 // constants for cell "scope" attribute #define NS_STYLE_CELL_SCOPE_ROW 0 diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp index b3c4a25897..c647aa5fc3 100644 --- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -24,6 +24,7 @@ #include "nsIDocument.h" #include "nsPrintfCString.h" #include "mozilla/Preferences.h" +#include "mozilla/ArenaObjectID.h" #ifdef DEBUG // #define NOISY_DEBUG @@ -308,18 +309,30 @@ nsStyleContext::MoveTo(nsStyleContext* aNewParent) // Assertions checking for visited style are just to avoid some tricky // cases we can't be bothered handling at the moment. MOZ_ASSERT(!IsStyleIfVisited()); + MOZ_ASSERT(!mParent->IsStyleIfVisited()); MOZ_ASSERT(!aNewParent->IsStyleIfVisited()); + MOZ_ASSERT(!mStyleIfVisited || mStyleIfVisited->mParent == mParent); nsStyleContext* oldParent = mParent; + if (oldParent->HasChildThatUsesResetStyle()) { + aNewParent->AddStyleBit(NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE); + } + aNewParent->AddRef(); - mParent->RemoveChild(this); - mParent = aNewParent; mParent->AddChild(this); - oldParent->Release(); + + if (mStyleIfVisited) { + oldParent = mStyleIfVisited->mParent; + aNewParent->AddRef(); + mStyleIfVisited->mParent->RemoveChild(mStyleIfVisited); + mStyleIfVisited->mParent = aNewParent; + mStyleIfVisited->mParent->AddChild(mStyleIfVisited); + oldParent->Release(); + } } already_AddRefed @@ -546,9 +559,28 @@ ShouldSuppressLineBreak(const nsStyleDisplay* aStyleDisplay, // the level containers themselves are breakable. We have to check // the container display type against all ruby display type here // because any of the ruby boxes could be anonymous. - if (aContainerDisplay->IsRubyDisplayType() && - aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_RUBY_BASE_CONTAINER && - aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER) { + // Note that, when certain HTML tags, e.g. form controls, have ruby + // level container display type, they could also escape from this flag + // while they shouldn't. However, it is generally fine since they + // won't usually break the assertion that there is no line break + // inside ruby, because: + // 1. their display types, the ruby level container types, are inline- + // outside, which means they won't cause any forced line break; and + // 2. they never start an inline span, which means their children, if + // any, won't be able to break the line its ruby ancestor lays; and + // 3. their parent frame is always a ruby content frame (due to + // anonymous ruby box generation), which makes line layout suppress + // any optional line break around this frame. + // However, there is one special case which is BR tag, because it + // directly affects the line layout. This case is handled by the BR + // frame which checks the flag of its parent frame instead of itself. + if ((aContainerDisplay->IsRubyDisplayType() && + aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_RUBY_BASE_CONTAINER && + aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER) || + // Since ruby base and ruby text may exist themselves without any + // non-anonymous frame outside, we should also check them. + aStyleDisplay->mDisplay == NS_STYLE_DISPLAY_RUBY_BASE || + aStyleDisplay->mDisplay == NS_STYLE_DISPLAY_RUBY_TEXT) { return true; } return false; @@ -617,7 +649,14 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) // doesn't get confused by looking at the style data. if (!mParent) { uint8_t displayVal = disp->mDisplay; - nsRuleNode::EnsureBlockDisplay(displayVal, true); + if (displayVal != NS_STYLE_DISPLAY_CONTENTS) { + nsRuleNode::EnsureBlockDisplay(displayVal, true); + } else { + // http://dev.w3.org/csswg/css-display/#transformations + // "... a display-outside of 'contents' computes to block-level + // on the root element." + displayVal = NS_STYLE_DISPLAY_BLOCK; + } if (displayVal != disp->mDisplay) { nsStyleDisplay *mutable_display = static_cast(GetUniqueStyleData(eStyleStruct_Display)); @@ -733,6 +772,26 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) } } + /* + * According to https://drafts.csswg.org/css-writing-modes-3/#block-flow: + * + * If a box has a different block flow direction than its containing block: + * * If the box has a specified display of inline, its display computes + * to inline-block. [CSS21] + * ...etc. + */ + if (disp->mDisplay == NS_STYLE_DISPLAY_INLINE && mParent) { + // We don't need the full mozilla::WritingMode value (incorporating dir + // and text-orientation) here; just the writing-mode property is enough. + if (StyleVisibility()->mWritingMode != + mParent->StyleVisibility()->mWritingMode) { + nsStyleDisplay *mutable_display = + static_cast(GetUniqueStyleData(eStyleStruct_Display)); + mutable_display->mOriginalDisplay = mutable_display->mDisplay = + NS_STYLE_DISPLAY_INLINE_BLOCK; + } + } + // Compute User Interface style, to trigger loads of cursors StyleUserInterface(); } @@ -740,7 +799,8 @@ nsStyleContext::ApplyStyleFixups(bool aSkipParentDisplayBasedStyleFixup) nsChangeHint nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, nsChangeHint aParentHintsNotHandledForDescendants, - uint32_t* aEqualStructs) + uint32_t* aEqualStructs, + uint32_t* aSamePointerStructs) { PROFILER_LABEL("nsStyleContext", "CalcStyleDifference", js::ProfileEntry::Category::CSS); @@ -814,7 +874,7 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, differenceAlwaysHandledForDescendants) & \ aParentHintsNotHandledForDescendants)) { \ nsChangeHint difference = \ - this##struct_->CalcDifference(*other##struct_); \ + this##struct_->CalcDifference(*other##struct_ EXTRA_DIFF_ARGS); \ NS_ASSERTION(NS_IsHintSubset(difference, maxDifference), \ "CalcDifference() returned bigger hint than " \ "MaxDifference()"); \ @@ -826,7 +886,7 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, /* We still must call CalcDifference to see if there were any */ \ /* changes so that we can set *aEqualStructs appropriately. */ \ nsChangeHint difference = \ - this##struct_->CalcDifference(*other##struct_); \ + this##struct_->CalcDifference(*other##struct_ EXTRA_DIFF_ARGS); \ NS_ASSERTION(NS_IsHintSubset(difference, maxDifference), \ "CalcDifference() returned bigger hint than " \ "MaxDifference()"); \ @@ -845,6 +905,7 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, // smallest. This lets us skip later ones if we already have a hint // that subsumes their MaxDifference. (As the hints get // finer-grained, this optimization is becoming less useful, though.) +#define EXTRA_DIFF_ARGS /* nothing */ DO_STRUCT_DIFFERENCE(Display); DO_STRUCT_DIFFERENCE(XUL); DO_STRUCT_DIFFERENCE(Column); @@ -860,7 +921,11 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, DO_STRUCT_DIFFERENCE(Quotes); DO_STRUCT_DIFFERENCE(SVGReset); DO_STRUCT_DIFFERENCE(SVG); +#undef EXTRA_DIFF_ARGS +#define EXTRA_DIFF_ARGS , this DO_STRUCT_DIFFERENCE(Position); +#undef EXTRA_DIFF_ARGS +#define EXTRA_DIFF_ARGS /* nothing */ DO_STRUCT_DIFFERENCE(Font); DO_STRUCT_DIFFERENCE(Margin); DO_STRUCT_DIFFERENCE(Padding); @@ -868,12 +933,33 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, DO_STRUCT_DIFFERENCE(TextReset); DO_STRUCT_DIFFERENCE(Background); DO_STRUCT_DIFFERENCE(Color); +#undef EXTRA_DIFF_ARGS #undef DO_STRUCT_DIFFERENCE MOZ_ASSERT(styleStructCount == nsStyleStructID_Length, "missing a call to DO_STRUCT_DIFFERENCE"); + // We check for struct pointer equality here rather than as part of the + // DO_STRUCT_DIFFERENCE calls, since those calls can result in structs + // we previously examined and found to be null on this style context + // getting computed by later DO_STRUCT_DIFFERENCE calls (which can + // happen when the nsRuleNode::ComputeXXXData method looks up another + // struct.) This is important for callers in RestyleManager that + // need to know the equality or not of the final set of cached struct + // pointers. + *aSamePointerStructs = 0; + +#define STYLE_STRUCT(name_, callback_) \ + { \ + const nsStyle##name_* data = PeekStyle##name_(); \ + if (!data || data == aOther->Style##name_()) { \ + *aSamePointerStructs |= NS_STYLE_INHERIT_BIT(name_); \ + } \ + } +#include "nsStyleStructList.h" +#undef STYLE_STRUCT + // Note that we do not check whether this->RelevantLinkVisited() != // aOther->RelevantLinkVisited(); we don't need to since // nsCSSFrameConstructor::DoContentStateChanged always adds @@ -1089,7 +1175,8 @@ void* nsStyleContext::operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW { // Check the recycle list first. - return aPresContext->PresShell()->AllocateByObjectID(nsPresArena::nsStyleContext_id, sz); + return aPresContext->PresShell()-> + AllocateByObjectID(eArenaObjectID_nsStyleContext, sz); } // Overridden to prevent the global delete from being called, since the memory @@ -1105,7 +1192,8 @@ nsStyleContext::Destroy() // Don't let the memory be freed, since it will be recycled // instead. Don't call the global operator delete. - presContext->PresShell()->FreeByObjectID(nsPresArena::nsStyleContext_id, this); + presContext->PresShell()-> + FreeByObjectID(eArenaObjectID_nsStyleContext, this); } already_AddRefed @@ -1122,6 +1210,12 @@ NS_NewStyleContext(nsStyleContext* aParentContext, return context.forget(); } +nsIPresShell* +nsStyleContext::Arena() +{ + return mRuleNode->PresContext()->PresShell(); +} + static inline void ExtractAnimationValue(nsCSSProperty aProperty, nsStyleContext* aStyleContext, @@ -1259,13 +1353,6 @@ nsStyleContext::LookupStruct(const nsACString& aName, nsStyleStructID& aResult) } #endif -bool -nsStyleContext::HasSameCachedStyleData(nsStyleContext* aOther, - nsStyleStructID aSID) -{ - return GetCachedStyleData(aSID) == aOther->GetCachedStyleData(aSID); -} - void nsStyleContext::SwapStyleData(nsStyleContext* aNewContext, uint32_t aStructs) { diff --git a/layout/style/nsStyleContext.h b/layout/style/nsStyleContext.h index a687a8dbe8..d25fc32ec8 100644 --- a/layout/style/nsStyleContext.h +++ b/layout/style/nsStyleContext.h @@ -73,6 +73,13 @@ public: void* operator new(size_t sz, nsPresContext* aPresContext) CPP_THROW_NEW; void Destroy(); + // These two methods are for use by ArenaRefPtr. + static mozilla::ArenaObjectID ArenaObjectID() + { + return mozilla::eArenaObjectID_nsStyleContext; + } + nsIPresShell* Arena(); + #ifdef DEBUG /** * Initializes a cached pref, which is only used in DEBUG code. @@ -173,6 +180,9 @@ public: bool HasPseudoElementData() const { return !!(mBits & NS_STYLE_HAS_PSEUDO_ELEMENT_DATA); } + bool HasChildThatUsesResetStyle() const + { return mBits & NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE; } + // Is the only link whose visitedness is allowed to influence the // style of the node this style context is for (which is that element // or its nearest ancestor that is a link) visited? @@ -271,12 +281,6 @@ public: #undef STYLE_STRUCT_RESET #undef STYLE_STRUCT_INHERITED - /** - * Returns whether this style context and aOther both have the same - * cached style struct pointer for a given style struct. - */ - bool HasSameCachedStyleData(nsStyleContext* aOther, nsStyleStructID aSID); - /** * Returns whether this style context has cached, inherited style data for a * given style struct. @@ -360,7 +364,8 @@ public: */ nsChangeHint CalcStyleDifference(nsStyleContext* aOther, nsChangeHint aParentHintsNotHandledForDescendants, - uint32_t* aEqualStructs); + uint32_t* aEqualStructs, + uint32_t* aSamePointerStructs); /** * Get a color that depends on link-visitedness using this and diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index a923d78734..0955e626bd 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -260,6 +260,10 @@ nsStyleSet::BeginReconstruct() NS_ASSERTION(!mInReconstruct, "Unmatched begin/end?"); NS_ASSERTION(mRuleTree, "Reconstructing before first construction?"); + // Clear any ArenaRefPtr-managed style contexts, as we don't want them + // held on to after the rule tree has been reconstructed. + PresContext()->PresShell()->ClearArenaRefPtrs(eArenaObjectID_nsStyleContext); + // Create a new rule tree root nsRuleNode* newTree = nsRuleNode::CreateRootNode(mRuleTree->PresContext()); @@ -385,6 +389,14 @@ SortStyleSheetsByScope(nsTArray& aSheets) nsresult nsStyleSet::GatherRuleProcessors(sheetType aType) { + // We might be in GatherRuleProcessors because we are dropping a sheet, + // resulting in an nsCSSSelector being destroyed. Tell the + // RestyleManager for each document we're used in so that they can + // drop any nsCSSSelector pointers (used for eRestyle_SomeDescendants) + // in their mPendingRestyles. + if (IsCSSSheetType(aType)) { + ClearSelectors(); + } nsCOMPtr oldRuleProcessor(mRuleProcessors[aType]); nsTArray> oldScopedDocRuleProcessors; if (aType == eAgentSheet || aType == eUserSheet) { @@ -845,6 +857,7 @@ ReplaceAnimationRule(nsRuleNode *aOldRuleNode, if (aNewAnimRule) { n = n->Transition(aNewAnimRule, nsStyleSet::eAnimationSheet, false); + n->SetIsAnimationRule(); } for (uint32_t i = moreSpecificNodes.Length(); i-- != 0; ) { @@ -1439,6 +1452,7 @@ struct RuleNodeInfo { nsIStyleRule* mRule; uint8_t mLevel; bool mIsImportant; + bool mIsAnimationRule; }; struct CascadeLevel { @@ -1508,6 +1522,7 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, curRule->mRule = ruleNode->GetRule(); curRule->mLevel = ruleNode->GetLevel(); curRule->mIsImportant = ruleNode->IsImportantRule(); + curRule->mIsAnimationRule = ruleNode->IsAnimationRule(); } nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); @@ -1538,6 +1553,7 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, GetAnimationRule(aElement, aPseudoType); if (rule) { ruleWalker.ForwardOnPossiblyCSSRule(rule); + ruleWalker.CurrentNode()->SetIsAnimationRule(); } } break; @@ -1550,6 +1566,7 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, GetAnimationRule(aElement, aPseudoType); if (rule) { ruleWalker.ForwardOnPossiblyCSSRule(rule); + ruleWalker.CurrentNode()->SetIsAnimationRule(); } } break; @@ -1615,6 +1632,9 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, if (!doReplace) { ruleWalker.ForwardOnPossiblyCSSRule(ruleInfo.mRule); + if (ruleInfo.mIsAnimationRule) { + ruleWalker.CurrentNode()->SetIsAnimationRule(); + } } } } @@ -2369,14 +2389,16 @@ struct MOZ_STACK_CLASS AttributeData : public AttributeRuleProcessorData { aAttrHasChanged, aOtherValue, aTreeMatchContext), mHint(nsRestyleHint(0)) {} - nsRestyleHint mHint; + nsRestyleHint mHint; + RestyleHintData mHintData; }; static bool SheetHasAttributeStyle(nsIStyleRuleProcessor* aProcessor, void *aData) { AttributeData* data = (AttributeData*)aData; - nsRestyleHint hint = aProcessor->HasAttributeDependentStyle(data); + nsRestyleHint hint = + aProcessor->HasAttributeDependentStyle(data, data->mHintData); data->mHint = nsRestyleHint(data->mHint | hint); return true; // continue } @@ -2387,7 +2409,9 @@ nsStyleSet::HasAttributeDependentStyle(Element* aElement, nsIAtom* aAttribute, int32_t aModType, bool aAttrHasChanged, - const nsAttrValue* aOtherValue) + const nsAttrValue* aOtherValue, + mozilla::RestyleHintData& + aRestyleHintDataResult) { TreeMatchContext treeContext(false, nsRuleWalker::eLinksVisitedOrUnvisited, aElement->OwnerDoc()); @@ -2395,6 +2419,11 @@ nsStyleSet::HasAttributeDependentStyle(Element* aElement, AttributeData data(PresContext(), aElement, aAttribute, aModType, aAttrHasChanged, aOtherValue, treeContext); WalkRuleProcessors(SheetHasAttributeStyle, &data, false); + if (!(data.mHint & eRestyle_Subtree)) { + // No point keeping the list of selectors around if we are going to + // restyle the whole subtree unconditionally. + aRestyleHintDataResult = Move(data.mHintData); + } return data.mHint; } @@ -2481,3 +2510,13 @@ nsStyleSet::HasRuleProcessorUsedByMultipleStyleSets(sheetType aSheetType) static_cast(mRuleProcessors[aSheetType].get()); return rp->IsUsedByMultipleStyleSets(); } + +void +nsStyleSet::ClearSelectors() +{ + // We might be called before we've done our first rule tree construction. + if (!mRuleTree) { + return; + } + PresContext()->RestyleManager()->ClearSelectors(); +} diff --git a/layout/style/nsStyleSet.h b/layout/style/nsStyleSet.h index e807af8e03..ff041525f1 100644 --- a/layout/style/nsStyleSet.h +++ b/layout/style/nsStyleSet.h @@ -286,7 +286,9 @@ class nsStyleSet final nsIAtom* aAttribute, int32_t aModType, bool aAttrHasChanged, - const nsAttrValue* aOtherValue); + const nsAttrValue* aOtherValue, + mozilla::RestyleHintData& + aRestyleHintDataResult); /* * Do any processing that needs to happen as a result of a change in @@ -345,6 +347,12 @@ class nsStyleSet final return mSheets[aType].ObjectAt(aIndex); } + void AppendAllXBLStyleSheets(nsTArray& aArray) const { + if (mBindingManager) { + mBindingManager->AppendAllSheets(aArray); + } + } + nsresult RemoveDocStyleSheet(nsIStyleSheet* aSheet); nsresult AddDocStyleSheet(nsIStyleSheet* aSheet, nsIDocument* aDocument); @@ -397,6 +405,10 @@ class nsStyleSet final bool HasRuleProcessorUsedByMultipleStyleSets(sheetType aSheetType); + // Tells the RestyleManager for the document using this style set + // to drop any nsCSSSelector pointers it has. + void ClearSelectors(); + private: nsStyleSet(const nsStyleSet& aCopy) = delete; nsStyleSet& operator=(const nsStyleSet& aCopy) = delete; diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 155017ee97..77bf83bfac 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -167,7 +167,7 @@ void nsStyleFont::Destroy(nsPresContext* aContext) { this->~nsStyleFont(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleFont_id, this); + FreeByObjectID(eArenaObjectID_nsStyleFont, this); } void @@ -256,7 +256,7 @@ nsChangeHint nsStyleFont::CalcFontDifference(const nsFont& aFont1, const nsFont& if ((aFont1.decorations == aFont2.decorations)) { return NS_STYLE_HINT_NONE; } - return NS_STYLE_HINT_VISUAL; + return nsChangeHint_RepaintFrame; } return NS_STYLE_HINT_REFLOW; } @@ -310,7 +310,7 @@ void nsStyleMargin::Destroy(nsPresContext* aContext) { this->~nsStyleMargin(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleMargin_id, this); + FreeByObjectID(eArenaObjectID_nsStyleMargin, this); } @@ -333,8 +333,9 @@ nsChangeHint nsStyleMargin::CalcDifference(const nsStyleMargin& aOther) const } // Margin differences can't affect descendant intrinsic sizes and // don't need to force children to reflow. - return NS_CombineHint(nsChangeHint_NeedReflow, - nsChangeHint_ClearAncestorIntrinsics); + return nsChangeHint_NeedReflow | + nsChangeHint_ReflowChangesSizeOrPosition | + nsChangeHint_ClearAncestorIntrinsics; } nsStylePadding::nsStylePadding() @@ -360,7 +361,7 @@ void nsStylePadding::Destroy(nsPresContext* aContext) { this->~nsStylePadding(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStylePadding_id, this); + FreeByObjectID(eArenaObjectID_nsStylePadding, this); } void nsStylePadding::RecalcData() @@ -512,7 +513,7 @@ nsStyleBorder::Destroy(nsPresContext* aContext) { UntrackImage(aContext); this->~nsStyleBorder(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleBorder_id, this); + FreeByObjectID(eArenaObjectID_nsStyleBorder, this); } nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const @@ -538,28 +539,28 @@ nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const // See the explanation in nsChangeHint.h of // nsChangeHint_BorderStyleNoneChange . // Furthermore, even though we know *this* side is 0 width, just - // assume a visual hint for some other change rather than bother + // assume a repaint hint for some other change rather than bother // tracking this result through the rest of the function. if (HasVisibleStyle(ix) != aOther.HasVisibleStyle(ix)) { - return NS_CombineHint(NS_STYLE_HINT_VISUAL, + return NS_CombineHint(nsChangeHint_RepaintFrame, nsChangeHint_BorderStyleNoneChange); } } // Note that mBorderStyle stores not only the border style but also // color-related flags. Given that we've already done an mComputedBorder - // comparison, border-style differences can only lead to a VISUAL hint. So + // comparison, border-style differences can only lead to a repaint hint. So // it's OK to just compare the values directly -- if either the actual // style or the color flags differ we want to repaint. NS_FOR_CSS_SIDES(ix) { if (mBorderStyle[ix] != aOther.mBorderStyle[ix] || mBorderColor[ix] != aOther.mBorderColor[ix]) - return NS_STYLE_HINT_VISUAL; + return nsChangeHint_RepaintFrame; } if (mBorderRadius != aOther.mBorderRadius || !mBorderColors != !aOther.mBorderColors) - return NS_STYLE_HINT_VISUAL; + return nsChangeHint_RepaintFrame; if (IsBorderImageLoaded() || aOther.IsBorderImageLoaded()) { if (mBorderImageSource != aOther.mBorderImageSource || @@ -569,7 +570,7 @@ nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const mBorderImageFill != aOther.mBorderImageFill || mBorderImageWidth != aOther.mBorderImageWidth || mBorderImageOutset != aOther.mBorderImageOutset) - return NS_STYLE_HINT_VISUAL; + return nsChangeHint_RepaintFrame; } // Note that at this point if mBorderColors is non-null so is @@ -578,7 +579,7 @@ nsChangeHint nsStyleBorder::CalcDifference(const nsStyleBorder& aOther) const NS_FOR_CSS_SIDES(ix) { if (!nsBorderColors::Equal(mBorderColors[ix], aOther.mBorderColors[ix])) - return NS_STYLE_HINT_VISUAL; + return nsChangeHint_RepaintFrame; } } @@ -1119,6 +1120,8 @@ nsStyleClipPath::ReleaseRef() NS_ASSERTION(mURL, "expected pointer"); mURL->Release(); } + // mBasicShap, mURL, etc. are all pointers in a union of pointers. Nulling + // one of them nulls all of them: mURL = nullptr; } @@ -1496,7 +1499,9 @@ IsAutonessEqual(const nsStyleSides& aSides1, const nsStyleSides& aSides2) return true; } -nsChangeHint nsStylePosition::CalcDifference(const nsStylePosition& aOther) const +nsChangeHint +nsStylePosition::CalcDifference(const nsStylePosition& aOther, + nsStyleContext* aContext) const { nsChangeHint hint = nsChangeHint(0); @@ -1582,35 +1587,31 @@ nsChangeHint nsStylePosition::CalcDifference(const nsStylePosition& aOther) cons NS_UpdateHint(hint, nsChangeHint_NeedReflow); } - // Properties that apply only to multi-line flex containers: - // 'align-content' can change the positioning & sizing of a multi-line flex - // container's children when there's extra space in the cross axis, but it - // shouldn't affect the container's own sizing. - // - // NOTE: If we get here, we know that mFlexWrap == aOther.mFlexWrap - // (otherwise, we would've returned earlier). So it doesn't matter which one - // of those we check to see if we're multi-line. - if (mFlexWrap != NS_STYLE_FLEX_WRAP_NOWRAP && - mAlignContent != aOther.mAlignContent) { + // 'align-content' doesn't apply to a single-line flexbox but we don't know + // if we're a flex container at this point so we can't optimize for that. + if (mAlignContent != aOther.mAlignContent) { NS_UpdateHint(hint, nsChangeHint_NeedReflow); } - if (mHeight != aOther.mHeight || - mMinHeight != aOther.mMinHeight || - mMaxHeight != aOther.mMaxHeight) { - // Height changes can affect descendant intrinsic sizes due to replaced - // elements with percentage heights in descendants which also have - // percentage heights. This is handled via nsChangeHint_UpdateComputedBSize + bool widthChanged = mWidth != aOther.mWidth || + mMinWidth != aOther.mMinWidth || + mMaxWidth != aOther.mMaxWidth; + bool heightChanged = mHeight != aOther.mHeight || + mMinHeight != aOther.mMinHeight || + mMaxHeight != aOther.mMaxHeight; + bool isVertical = WritingMode(aContext).IsVertical(); + if (isVertical ? widthChanged : heightChanged) { + // Block-size changes can affect descendant intrinsic sizes due to replaced + // elements with percentage bsizes in descendants which also have + // percentage bsizes. This is handled via nsChangeHint_UpdateComputedBSize // which clears intrinsic sizes for frames that have such replaced elements. NS_UpdateHint(hint, nsChangeHint_NeedReflow | nsChangeHint_UpdateComputedBSize | nsChangeHint_ReflowChangesSizeOrPosition); } - if (mWidth != aOther.mWidth || - mMinWidth != aOther.mMinWidth || - mMaxWidth != aOther.mMaxWidth) { - // None of our width differences can affect descendant intrinsic + if (isVertical ? heightChanged : widthChanged) { + // None of our inline-size differences can affect descendant intrinsic // sizes and none of them need to force children to reflow. NS_UpdateHint(hint, NS_SubtractHint(nsChangeHint_AllReflowHints, NS_CombineHint(nsChangeHint_ClearDescendantIntrinsics, @@ -1703,7 +1704,7 @@ nsStyleTableBorder::nsStyleTableBorder() mBorderCollapse = NS_STYLE_BORDER_SEPARATE; mEmptyCells = NS_STYLE_TABLE_EMPTY_CELLS_SHOW; - mCaptionSide = NS_STYLE_CAPTION_SIDE_BSTART; + mCaptionSide = NS_STYLE_CAPTION_SIDE_TOP; mBorderSpacingCol = 0; mBorderSpacingRow = 0; } @@ -1764,7 +1765,7 @@ nsChangeHint nsStyleColor::CalcDifference(const nsStyleColor& aOther) const { if (mColor == aOther.mColor) return NS_STYLE_HINT_NONE; - return NS_STYLE_HINT_VISUAL; + return nsChangeHint_RepaintFrame; } // -------------------- @@ -2234,7 +2235,7 @@ nsStyleBackground::Destroy(nsPresContext* aContext) this->~nsStyleBackground(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleBackground_id, this); + FreeByObjectID(eArenaObjectID_nsStyleBackground, this); } nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) const @@ -2251,18 +2252,20 @@ nsChangeHint nsStyleBackground::CalcDifference(const nsStyleBackground& aOther) if (moreLayers->mLayers[i] != lessLayers->mLayers[i]) { if ((moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element) || (lessLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element)) - return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_VISUAL); + return NS_CombineHint(nsChangeHint_UpdateEffects, + nsChangeHint_RepaintFrame); hasVisualDifference = true; } } else { if (moreLayers->mLayers[i].mImage.GetType() == eStyleImageType_Element) - return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_VISUAL); + return NS_CombineHint(nsChangeHint_UpdateEffects, + nsChangeHint_RepaintFrame); hasVisualDifference = true; } } if (hasVisualDifference || mBackgroundColor != aOther.mBackgroundColor) - return NS_STYLE_HINT_VISUAL; + return nsChangeHint_RepaintFrame; if (mAttachmentCount != aOther.mAttachmentCount || mClipCount != aOther.mClipCount || @@ -2820,7 +2823,7 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const // We do not need to apply nsChangeHint_UpdateTransformLayer since // nsChangeHint_RepaintFrame will forcibly invalidate the frame area and // ensure layers are rebuilt (or removed). - NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_AddOrRemoveTransform, + NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_UpdateContainingBlock, NS_CombineHint(nsChangeHint_UpdateOverflow, nsChangeHint_RepaintFrame))); } else { @@ -2853,7 +2856,8 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const NS_CombineHint(nsChangeHint_UpdateOverflow, nsChangeHint_RepaintFrame); for (uint8_t index = 0; index < 3; ++index) if (mTransformOrigin[index] != aOther.mTransformOrigin[index]) { - NS_UpdateHint(transformHint, kUpdateOverflowAndRepaintHint); + NS_UpdateHint(transformHint, NS_CombineHint(nsChangeHint_UpdateTransformLayer, + nsChangeHint_UpdatePostTransformOverflow)); break; } @@ -2863,6 +2867,11 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const break; } + if (HasPerspectiveStyle() != aOther.HasPerspectiveStyle()) { + // A change from/to being a containing block for position:fixed. + NS_UpdateHint(hint, nsChangeHint_UpdateContainingBlock); + } + if (mChildPerspective != aOther.mChildPerspective || mTransformStyle != aOther.mTransformStyle || mTransformBox != aOther.mTransformBox) @@ -2970,6 +2979,9 @@ nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) nsChangeHint hint = nsChangeHint(0); if (mDirection != aOther.mDirection || mWritingMode != aOther.mWritingMode) { + // It's important that a change in mWritingMode results in frame + // reconstruction, because it may affect intrinsic size (see + // nsSubDocumentFrame::GetIntrinsicISize/BSize). NS_UpdateHint(hint, nsChangeHint_ReconstructFrame); } else { if ((mImageOrientation != aOther.mImageOrientation)) { @@ -3138,7 +3150,7 @@ nsStyleContent::Destroy(nsPresContext* aContext) this->~nsStyleContent(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleContent_id, this); + FreeByObjectID(eArenaObjectID_nsStyleContent, this); } nsStyleContent::nsStyleContent(const nsStyleContent& aSource) @@ -3354,10 +3366,12 @@ nsChangeHint nsStyleTextReset::CalcDifference(const nsStyleTextReset& aOther) co uint8_t otherLineStyle = aOther.GetDecorationStyle(); if (mTextDecorationLine != aOther.mTextDecorationLine || lineStyle != otherLineStyle) { - // Repaint for other style decoration lines because they must be in - // default overflow rect - nsChangeHint hint = NS_STYLE_HINT_VISUAL; + // Changes to our text-decoration line can impact our overflow area & + // also our descendants' overflow areas (particularly for text-frame + // descendants). So, we update those areas & trigger a repaint. + nsChangeHint hint = nsChangeHint_RepaintFrame; NS_UpdateHint(hint, nsChangeHint_UpdateSubtreeOverflow); + NS_UpdateHint(hint, nsChangeHint_SchedulePaint); return hint; } @@ -3367,11 +3381,11 @@ nsChangeHint nsStyleTextReset::CalcDifference(const nsStyleTextReset& aOther) co GetDecorationColor(decColor, isFG); aOther.GetDecorationColor(otherDecColor, otherIsFG); if (isFG != otherIsFG || (!isFG && decColor != otherDecColor)) { - return NS_STYLE_HINT_VISUAL; + return nsChangeHint_RepaintFrame; } if (mTextOverflow != aOther.mTextOverflow) { - return NS_STYLE_HINT_VISUAL; + return nsChangeHint_RepaintFrame; } return NS_STYLE_HINT_NONE; } @@ -3419,7 +3433,7 @@ nsStyleText::nsStyleText(void) mRubyPosition = NS_STYLE_RUBY_POSITION_OVER; mTextSizeAdjust = NS_STYLE_TEXT_SIZE_ADJUST_AUTO; mTextCombineUpright = NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE; - mControlCharacterVisibility = NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN; + mControlCharacterVisibility = NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE; mLetterSpacing.SetNormalValue(); mLineHeight.SetNormalValue(); diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 6f40b656f1..3875ec99c2 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -11,6 +11,7 @@ #ifndef nsStyleStruct_h___ #define nsStyleStruct_h___ +#include "mozilla/ArenaObjectID.h" #include "mozilla/Attributes.h" #include "mozilla/CSSVariableValues.h" #include "nsColor.h" @@ -30,9 +31,9 @@ #include "CounterStyleManager.h" class nsIFrame; -class nsTextFrame; class nsIURI; class nsStyleContext; +class nsTextFrame; class imgIContainer; // Includes nsStyleStructID. @@ -42,6 +43,14 @@ class imgIContainer; // NS_STYLE_INHERIT_BIT defined in nsStyleStructFwd.h #define NS_STYLE_INHERIT_MASK 0x000ffffff +// Bits for inherited structs. +#define NS_STYLE_INHERITED_STRUCT_MASK \ + ((nsStyleStructID_size_t(1) << nsStyleStructID_Inherited_Count) - 1) +// Bits for reset structs. +#define NS_STYLE_RESET_STRUCT_MASK \ + (((nsStyleStructID_size_t(1) << nsStyleStructID_Reset_Count) - 1) \ + << nsStyleStructID_Inherited_Count) + // Additional bits for nsStyleContext's mBits: // See nsStyleContext::HasTextDecorationLines #define NS_STYLE_HAS_TEXT_DECORATION_LINES 0x001000000 @@ -64,16 +73,22 @@ class imgIContainer; #define NS_STYLE_IN_DISPLAY_NONE_SUBTREE 0x100000000 // See nsStyleContext::FindChildWithRules #define NS_STYLE_INELIGIBLE_FOR_SHARING 0x200000000 +// See nsStyleContext::HasChildThatUsesResetStyle +#define NS_STYLE_HAS_CHILD_THAT_USES_RESET_STYLE 0x400000000 // See nsStyleContext::GetPseudoEnum -#define NS_STYLE_CONTEXT_TYPE_SHIFT 34 +#define NS_STYLE_CONTEXT_TYPE_SHIFT 35 // Additional bits for nsRuleNode's mDependentBits: +#define NS_RULE_NODE_IS_ANIMATION_RULE 0x01000000 #define NS_RULE_NODE_GC_MARK 0x02000000 #define NS_RULE_NODE_USED_DIRECTLY 0x04000000 #define NS_RULE_NODE_IS_IMPORTANT 0x08000000 #define NS_RULE_NODE_LEVEL_MASK 0xf0000000 #define NS_RULE_NODE_LEVEL_SHIFT 28 +// Additional bits for nsRuleNode's mNoneBits: +#define NS_RULE_NODE_HAS_ANIMATION_DATA 0x80000000 + // The lifetime of these objects is managed by the presshell's arena. struct nsStyleFont { @@ -106,7 +121,7 @@ public: void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleFont_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleFont, sz); } void Destroy(nsPresContext* aContext); @@ -338,7 +353,7 @@ struct nsStyleColor { nsChangeHint CalcDifference(const nsStyleColor& aOther) const; static nsChangeHint MaxDifference() { - return NS_STYLE_HINT_VISUAL; + return nsChangeHint_RepaintFrame; } static nsChangeHint DifferenceAlwaysHandledForDescendants() { // CalcDifference never returns the reflow hints that are sometimes @@ -348,12 +363,12 @@ struct nsStyleColor { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleColor_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleColor, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleColor(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleColor_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleColor, this); } // Don't add ANY members to this struct! We can achieve caching in the rule @@ -368,14 +383,14 @@ struct nsStyleBackground { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleBackground_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleBackground, sz); } void Destroy(nsPresContext* aContext); nsChangeHint CalcDifference(const nsStyleBackground& aOther) const; static nsChangeHint MaxDifference() { return NS_CombineHint(nsChangeHint_UpdateEffects, - NS_CombineHint(NS_STYLE_HINT_VISUAL, + NS_CombineHint(nsChangeHint_RepaintFrame, nsChangeHint_NeutralChange)); } static nsChangeHint DifferenceAlwaysHandledForDescendants() { @@ -473,12 +488,12 @@ struct nsStyleBackground { return !(*this == aOther); } }; - + struct Repeat; friend struct Repeat; struct Repeat { uint8_t mXRepeat, mYRepeat; - + // Initialize nothing Repeat() {} @@ -603,19 +618,20 @@ struct nsStyleMargin { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleMargin_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleMargin, sz); } void Destroy(nsPresContext* aContext); void RecalcData(); nsChangeHint CalcDifference(const nsStyleMargin& aOther) const; static nsChangeHint MaxDifference() { - return NS_CombineHint(nsChangeHint_NeedReflow, - nsChangeHint_ClearAncestorIntrinsics); + return nsChangeHint_NeedReflow | + nsChangeHint_ReflowChangesSizeOrPosition | + nsChangeHint_ClearAncestorIntrinsics; } static nsChangeHint DifferenceAlwaysHandledForDescendants() { - // CalcDifference can return both nsChangeHint_ClearAncestorIntrinsics and - // nsChangeHint_NeedReflow as hints not handled for descendants. + // CalcDifference can return all of the reflow hints sometimes not + // handled for descendants as hints not handled for descendants. return nsChangeHint(0); } @@ -646,7 +662,7 @@ struct nsStylePadding { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStylePadding_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStylePadding, sz); } void Destroy(nsPresContext* aContext); @@ -657,8 +673,14 @@ struct nsStylePadding { nsChangeHint_ClearDescendantIntrinsics); } static nsChangeHint DifferenceAlwaysHandledForDescendants() { - // CalcDifference can return nsChangeHint_ClearAncestorIntrinsics as an - // hint not handled for descendants. + // CalcDifference can return nsChangeHint_ClearAncestorIntrinsics as + // a hint not handled for descendants. We could (and perhaps + // should) return nsChangeHint_NeedReflow and + // nsChangeHint_ReflowChangesSizeOrPosition as always handled for + // descendants, but since they're always returned in conjunction + // with nsChangeHint_ClearAncestorIntrinsics (which is not), it + // won't ever lead to any optimization in + // nsStyleContext::CalcStyleDifference. return nsChangeHint(0); } @@ -809,13 +831,10 @@ public: nsCSSShadowItem mArray[1]; // This MUST be the last item }; -// Border widths are rounded to the nearest integer number of pixels, but values -// between zero and one device pixels are always rounded up to one device pixel. +// Border widths are rounded to the nearest-below integer number of pixels, +// but values between zero and one device pixels are always rounded up to +// one device pixel. #define NS_ROUND_BORDER_TO_PIXELS(l,tpp) \ - ((l) == 0) ? 0 : std::max((tpp), ((l) + ((tpp) / 2)) / (tpp) * (tpp)) -// Caret widths are rounded to the nearest-below integer number of pixels, but values -// between zero and one device pixels are always rounded up to one device pixel. -#define NS_ROUND_CARET_TO_PIXELS(l,tpp) \ ((l) == 0) ? 0 : std::max((tpp), (l) / (tpp) * (tpp)) // Outline offset is rounded to the nearest integer number of pixels, but values // between zero and one device pixels are always rounded up to one device pixel. @@ -839,7 +858,7 @@ struct nsStyleBorder { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleBorder_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleBorder, sz); } void Destroy(nsPresContext* aContext); @@ -1069,12 +1088,12 @@ struct nsStyleOutline { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleOutline_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleOutline, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleOutline(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleOutline_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleOutline, this); } void RecalcData(nsPresContext* aContext); @@ -1165,12 +1184,12 @@ struct nsStyleList { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleList_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleList, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleList(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleList_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleList, this); } nsChangeHint CalcDifference(const nsStyleList& aOther) const; @@ -1266,7 +1285,8 @@ struct nsStyleGridTemplate { } inline bool operator!=(const nsStyleGridTemplate& aOther) const { - return mLineNameLists != aOther.mLineNameLists || + return mIsSubgrid != aOther.mIsSubgrid || + mLineNameLists != aOther.mLineNameLists || mMinTrackSizingFunctions != aOther.mMinTrackSizingFunctions || mMaxTrackSizingFunctions != aOther.mMaxTrackSizingFunctions; } @@ -1396,12 +1416,12 @@ struct nsStyleTextReset { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleTextReset_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleTextReset, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleTextReset(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleTextReset_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleTextReset, this); } uint8_t GetDecorationStyle() const @@ -1473,12 +1493,12 @@ struct nsStyleText { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleText_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleText, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleText(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleText_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleText, this); } nsChangeHint CalcDifference(const nsStyleText& aOther) const; @@ -1665,12 +1685,12 @@ struct nsStyleVisibility { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleVisibility_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleVisibility, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleVisibility(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleVisibility_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleVisibility, this); } nsChangeHint CalcDifference(const nsStyleVisibility& aOther) const; @@ -1894,12 +1914,12 @@ struct nsStyleDisplay { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleDisplay_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleDisplay, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleDisplay(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleDisplay_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleDisplay, this); } nsChangeHint CalcDifference(const nsStyleDisplay& aOther) const; @@ -1910,7 +1930,7 @@ struct nsStyleDisplay { nsChangeHint_UpdateTransformLayer | nsChangeHint_UpdateOverflow | nsChangeHint_UpdatePostTransformOverflow | - nsChangeHint_AddOrRemoveTransform | + nsChangeHint_UpdateContainingBlock | nsChangeHint_NeutralChange); } static nsChangeHint DifferenceAlwaysHandledForDescendants() { @@ -1978,7 +1998,7 @@ struct nsStyleDisplay { uint8_t mTransformBox; // [reset] see nsStyleConsts.h nsRefPtr mSpecifiedTransform; // [reset] nsStyleCoord mTransformOrigin[3]; // [reset] percent, coord, calc, 3rd param is coord, calc only - nsStyleCoord mChildPerspective; // [reset] coord + nsStyleCoord mChildPerspective; // [reset] none, coord nsStyleCoord mPerspectiveOrigin[2]; // [reset] percent, coord, calc nsAutoTArray mTransitions; // [reset] @@ -2159,12 +2179,12 @@ struct nsStyleTable { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleTable_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleTable, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleTable(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleTable_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleTable, this); } nsChangeHint CalcDifference(const nsStyleTable& aOther) const; @@ -2190,15 +2210,16 @@ struct nsStylePosition { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStylePosition_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStylePosition, sz); } void Destroy(nsPresContext* aContext) { this->~nsStylePosition(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStylePosition_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStylePosition, this); } - nsChangeHint CalcDifference(const nsStylePosition& aOther) const; + nsChangeHint CalcDifference(const nsStylePosition& aOther, + nsStyleContext* aContext) const; static nsChangeHint MaxDifference() { return NS_CombineHint(NS_STYLE_HINT_REFLOW, nsChangeHint(nsChangeHint_RecomputePosition | @@ -2348,12 +2369,12 @@ struct nsStyleTableBorder { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleTableBorder_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleTableBorder, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleTableBorder(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleTableBorder_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleTableBorder, this); } nsChangeHint CalcDifference(const nsStyleTableBorder& aOther) const; @@ -2368,11 +2389,6 @@ struct nsStyleTableBorder { nsChangeHint_ClearAncestorIntrinsics; } - // Return the mCaptionSide value, with physical values resolved to - // the appropriate logical value for the given writing mode. - // (The definition of this is in WritingModes.h.) - inline uint8_t LogicalCaptionSide(mozilla::WritingMode aWM) const; - nscoord mBorderSpacingCol;// [inherited] nscoord mBorderSpacingRow;// [inherited] uint8_t mBorderCollapse;// [inherited] @@ -2449,12 +2465,12 @@ struct nsStyleQuotes { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleQuotes_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleQuotes, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleQuotes(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleQuotes_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleQuotes, this); } void SetInitial(); @@ -2531,7 +2547,7 @@ struct nsStyleContent { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleContent_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleContent, sz); } void Destroy(nsPresContext* aContext); @@ -2640,12 +2656,12 @@ struct nsStyleUIReset { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleUIReset_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleUIReset, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleUIReset(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleUIReset_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleUIReset, this); } nsChangeHint CalcDifference(const nsStyleUIReset& aOther) const; @@ -2702,12 +2718,12 @@ struct nsStyleUserInterface { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleUserInterface_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleUserInterface, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleUserInterface(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleUserInterface_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleUserInterface, this); } nsChangeHint CalcDifference(const nsStyleUserInterface& aOther) const; @@ -2749,12 +2765,12 @@ struct nsStyleXUL { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleXUL_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleXUL, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleXUL(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleXUL_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleXUL, this); } nsChangeHint CalcDifference(const nsStyleXUL& aOther) const; @@ -2785,12 +2801,12 @@ struct nsStyleColumn { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleColumn_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleColumn, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleColumn(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleColumn_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleColumn, this); } nsChangeHint CalcDifference(const nsStyleColumn& aOther) const; @@ -2878,12 +2894,12 @@ struct nsStyleSVG { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleSVG_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleSVG, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleSVG(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleSVG_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleSVG, this); } nsChangeHint CalcDifference(const nsStyleSVG& aOther) const; @@ -3160,12 +3176,12 @@ struct nsStyleSVGReset { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleSVGReset_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleSVGReset(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleSVGReset_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleSVGReset, this); } nsChangeHint CalcDifference(const nsStyleSVGReset& aOther) const; @@ -3182,7 +3198,7 @@ struct nsStyleSVGReset { } bool HasFilters() const { - return mFilters.Length() > 0; + return !mFilters.IsEmpty(); } bool HasNonScalingStroke() const { @@ -3211,12 +3227,12 @@ struct nsStyleVariables { void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> - AllocateByObjectID(nsPresArena::nsStyleVariables_id, sz); + AllocateByObjectID(mozilla::eArenaObjectID_nsStyleVariables, sz); } void Destroy(nsPresContext* aContext) { this->~nsStyleVariables(); aContext->PresShell()-> - FreeByObjectID(nsPresArena::nsStyleVariables_id, this); + FreeByObjectID(mozilla::eArenaObjectID_nsStyleVariables, this); } nsChangeHint CalcDifference(const nsStyleVariables& aOther) const; diff --git a/layout/style/nsStyleStructFwd.h b/layout/style/nsStyleStructFwd.h index 4ed02b79e3..c9e7a68914 100644 --- a/layout/style/nsStyleStructFwd.h +++ b/layout/style/nsStyleStructFwd.h @@ -65,4 +65,6 @@ eStyleStruct_BackendOnly = nsStyleStructID_Length // A bit corresponding to each struct ID #define NS_STYLE_INHERIT_BIT(sid_) (1 << uint64_t(eStyleStruct_##sid_)) +typedef decltype(nsStyleStructID(0) + nsStyleStructID(0)) nsStyleStructID_size_t; + #endif /* nsStyleStructFwd_h_ */ diff --git a/layout/style/nsStyleStructInlines.h b/layout/style/nsStyleStructInlines.h index 3883d7ceb1..e2da9c5d58 100644 --- a/layout/style/nsStyleStructInlines.h +++ b/layout/style/nsStyleStructInlines.h @@ -138,8 +138,10 @@ nsStyleDisplay::HasTransform(const nsIFrame* aContextFrame) const bool nsStyleDisplay::IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const { + NS_ASSERTION(aContextFrame->StyleDisplay() == this, + "unexpected aContextFrame"); return (HasTransform(aContextFrame) || HasPerspectiveStyle() || - !aContextFrame->StyleSVGReset()->mFilters.IsEmpty()) && + aContextFrame->StyleSVGReset()->HasFilters()) && !aContextFrame->IsSVGText(); } diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini index 8d88b643a0..6aed36a953 100644 --- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -116,6 +116,7 @@ support-files = file_bug1055933_circle-xxl.png [test_bug1089417.html] support-files = file_bug1089417_iframe.html [test_bug1112014.html] +[test_bug1203766.html] [test_cascade.html] [test_ch_ex_no_infloops.html] [test_compute_data_with_start_struct.html] diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index b44b07bcb0..b7064f3f91 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -2365,8 +2365,8 @@ var gCSSProperties = { domProp: "captionSide", inherited: true, type: CSS_TYPE_LONGHAND, - initial_values: [ "block-start" ], - other_values: [ "block-end", "inline-start", "inline-end", "block-start-outside", "block-end-outside", "top", "bottom", "left", "right", "top-outside", "bottom-outside" ], + initial_values: [ "top" ], + other_values: [ "bottom", "left", "right", "top-outside", "bottom-outside" ], invalid_values: [] }, "clear": { diff --git a/layout/style/test/test_bug1203766.html b/layout/style/test/test_bug1203766.html new file mode 100644 index 0000000000..5d8152440b --- /dev/null +++ b/layout/style/test/test_bug1203766.html @@ -0,0 +1,112 @@ + + +Test for bug 1203766 + + + +Mozilla Bug 1203766 +

+
+
+
+
+
diff --git a/layout/tables/nsTableOuterFrame.cpp b/layout/tables/nsTableOuterFrame.cpp index e72f397a8d..882fdd774c 100644 --- a/layout/tables/nsTableOuterFrame.cpp +++ b/layout/tables/nsTableOuterFrame.cpp @@ -156,7 +156,7 @@ nsTableOuterFrame::RemoveFrame(ChildListID aListID, // The inner frame can't be removed so this should be the caption NS_PRECONDITION(kCaptionList == aListID, "can't remove inner frame"); - if (HasSideCaption(GetWritingMode())) { + if (HasSideCaption()) { // The old caption isize had an effect on the inner table isize, so // we're going to need to reflow it. Mark it dirty InnerTableFrame()->AddStateBits(NS_FRAME_IS_DIRTY); @@ -307,7 +307,7 @@ nsTableOuterFrame::GetMinISize(nsRenderingContext *aRenderingContext) nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mCaptionFrames.FirstChild(), nsLayoutUtils::MIN_ISIZE); - if (HasSideCaption(GetWritingMode())) { + if (HasSideCaption()) { iSize += capISize; } else { if (capISize > iSize) { @@ -326,12 +326,11 @@ nsTableOuterFrame::GetPrefISize(nsRenderingContext *aRenderingContext) maxISize = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, InnerTableFrame(), nsLayoutUtils::PREF_ISIZE); - WritingMode wm = GetWritingMode(); if (mCaptionFrames.NotEmpty()) { - uint8_t captionSide = GetLogicalCaptionSide(wm); + uint8_t captionSide = GetCaptionSide(); switch (captionSide) { - case NS_STYLE_CAPTION_SIDE_ISTART: - case NS_STYLE_CAPTION_SIDE_IEND: + case NS_STYLE_CAPTION_SIDE_LEFT: + case NS_STYLE_CAPTION_SIDE_RIGHT: { nscoord capMin = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, @@ -343,14 +342,14 @@ nsTableOuterFrame::GetPrefISize(nsRenderingContext *aRenderingContext) default: { nsLayoutUtils::IntrinsicISizeType iwt; - if (captionSide == NS_STYLE_CAPTION_SIDE_BSTART || - captionSide == NS_STYLE_CAPTION_SIDE_BEND) { + if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { // Don't let the caption's pref isize expand the table's pref // isize. iwt = nsLayoutUtils::MIN_ISIZE; } else { - NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE || - captionSide == NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE, + NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE, "unexpected caption side"); iwt = nsLayoutUtils::PREF_ISIZE; } @@ -420,13 +419,13 @@ nsTableOuterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, // GetPrefISize. See bug 349457 for an example. // Match the availableISize logic in Reflow. - uint8_t captionSide = GetLogicalCaptionSide(aWM); + uint8_t captionSide = GetCaptionSide(); nscoord inlineSize; if (captionSide == NO_SIDE) { inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, aCBSize, kidAvailableISize); - } else if (captionSide == NS_STYLE_CAPTION_SIDE_ISTART || - captionSide == NS_STYLE_CAPTION_SIDE_IEND) { + } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || + captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { nscoord capISize = ChildShrinkWrapISize(aRenderingContext, mCaptionFrames.FirstChild(), aWM, aCBSize, kidAvailableISize); @@ -434,8 +433,8 @@ nsTableOuterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, InnerTableFrame(), aWM, aCBSize, kidAvailableISize - capISize); - } else if (captionSide == NS_STYLE_CAPTION_SIDE_BSTART || - captionSide == NS_STYLE_CAPTION_SIDE_BEND) { + } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { nscoord margin; inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, aCBSize, kidAvailableISize, &margin); @@ -446,8 +445,8 @@ nsTableOuterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, inlineSize = capISize; } } else { - NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE || - captionSide == NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE, + NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE, "unexpected caption-side"); inlineSize = ChildShrinkWrapISize(aRenderingContext, InnerTableFrame(), aWM, aCBSize, kidAvailableISize); @@ -463,11 +462,10 @@ nsTableOuterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, } uint8_t -nsTableOuterFrame::GetLogicalCaptionSide(WritingMode aWM) +nsTableOuterFrame::GetCaptionSide() { if (mCaptionFrames.NotEmpty()) { - return mCaptionFrames.FirstChild()->StyleTableBorder()-> - LogicalCaptionSide(aWM); + return mCaptionFrames.FirstChild()->StyleTableBorder()->mCaptionSide; } else { return NO_SIDE; // no caption @@ -498,17 +496,17 @@ nsTableOuterFrame::SetDesiredSize(uint8_t aCaptionSide, // compute the overall inline-size switch (aCaptionSide) { - case NS_STYLE_CAPTION_SIDE_ISTART: + case NS_STYLE_CAPTION_SIDE_LEFT: aISize = - std::max(aInnerMargin.IStart(aWM), + std::max(aInnerMargin.LineLeft(aWM), aCaptionMargin.IStartEnd(aWM) + aCaptionSize.ISize(aWM)) + - aInnerSize.ISize(aWM) + aInnerMargin.IEnd(aWM); + aInnerSize.ISize(aWM) + aInnerMargin.LineRight(aWM); break; - case NS_STYLE_CAPTION_SIDE_IEND: + case NS_STYLE_CAPTION_SIDE_RIGHT: aISize = - std::max(aInnerMargin.IEnd(aWM), + std::max(aInnerMargin.LineRight(aWM), aCaptionMargin.IStartEnd(aWM) + aCaptionSize.ISize(aWM)) + - aInnerSize.ISize(aWM) + aInnerMargin.IStart(aWM); + aInnerSize.ISize(aWM) + aInnerMargin.LineLeft(aWM); break; default: aISize = @@ -519,22 +517,22 @@ nsTableOuterFrame::SetDesiredSize(uint8_t aCaptionSide, // compute the overall block-size switch (aCaptionSide) { - case NS_STYLE_CAPTION_SIDE_BSTART: - case NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE: + case NS_STYLE_CAPTION_SIDE_TOP: + case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: aBSize = aInnerSize.BSize(aWM) + aInnerMargin.BEnd(aWM); aBSize += std::max(aInnerMargin.BStart(aWM), aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM)); break; - case NS_STYLE_CAPTION_SIDE_BEND: - case NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE: + case NS_STYLE_CAPTION_SIDE_BOTTOM: + case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: aBSize = aInnerSize.BSize(aWM) + aInnerMargin.BStart(aWM); aBSize += std::max(aInnerMargin.BEnd(aWM), aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM)); break; - case NS_STYLE_CAPTION_SIDE_ISTART: - case NS_STYLE_CAPTION_SIDE_IEND: + case NS_STYLE_CAPTION_SIDE_LEFT: + case NS_STYLE_CAPTION_SIDE_RIGHT: aBSize = aInnerMargin.BStart(aWM); aBSize += std::max(aInnerSize.BSize(aWM) + aInnerMargin.BEnd(aWM), @@ -579,28 +577,28 @@ nsTableOuterFrame::GetCaptionOrigin(uint32_t aCaptionSide, // inline-dir computation switch (aCaptionSide) { - case NS_STYLE_CAPTION_SIDE_BEND: - case NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE: + case NS_STYLE_CAPTION_SIDE_BOTTOM: + case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: aOrigin.I(aWM) = aCaptionMargin.IStart(aWM); - if (aCaptionSide == NS_STYLE_CAPTION_SIDE_BEND) { + if (aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { // We placed the caption using only the table's isize as available // isize, and we should position it this way as well. aOrigin.I(aWM) += aInnerMargin.IStart(aWM); } break; - case NS_STYLE_CAPTION_SIDE_ISTART: + case NS_STYLE_CAPTION_SIDE_LEFT: + case NS_STYLE_CAPTION_SIDE_RIGHT: aOrigin.I(aWM) = aCaptionMargin.IStart(aWM); - break; - case NS_STYLE_CAPTION_SIDE_IEND: - aOrigin.I(aWM) = aInnerMargin.IStart(aWM) + aInnerSize.ISize(aWM) + - aCaptionMargin.IStart(aWM); + if (aWM.IsBidiLTR() == (aCaptionSide == NS_STYLE_CAPTION_SIDE_RIGHT)) { + aOrigin.I(aWM) += aInnerMargin.IStart(aWM) + aInnerSize.ISize(aWM); + } break; default: // block-start - NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_BSTART || - aCaptionSide == NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE, + NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP || + aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE, "unexpected caption side"); aOrigin.I(aWM) = aCaptionMargin.IStart(aWM); - if (aCaptionSide == NS_STYLE_CAPTION_SIDE_BSTART) { + if (aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP) { // We placed the caption using only the table's isize as available // isize, and we should position it this way as well. aOrigin.I(aWM) += aInnerMargin.IStart(aWM); @@ -609,8 +607,8 @@ nsTableOuterFrame::GetCaptionOrigin(uint32_t aCaptionSide, } // block-dir computation switch (aCaptionSide) { - case NS_STYLE_CAPTION_SIDE_IEND: - case NS_STYLE_CAPTION_SIDE_ISTART: + case NS_STYLE_CAPTION_SIDE_RIGHT: + case NS_STYLE_CAPTION_SIDE_LEFT: aOrigin.B(aWM) = aInnerMargin.BStart(aWM); switch (GetCaptionVerticalAlign()) { case NS_STYLE_VERTICAL_ALIGN_MIDDLE: @@ -627,13 +625,13 @@ nsTableOuterFrame::GetCaptionOrigin(uint32_t aCaptionSide, break; } break; - case NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE: - case NS_STYLE_CAPTION_SIDE_BEND: + case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: + case NS_STYLE_CAPTION_SIDE_BOTTOM: aOrigin.B(aWM) = aInnerMargin.BStart(aWM) + aInnerSize.BSize(aWM) + aCaptionMargin.BStart(aWM); break; - case NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE: - case NS_STYLE_CAPTION_SIDE_BSTART: + case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: + case NS_STYLE_CAPTION_SIDE_TOP: aOrigin.B(aWM) = aInnerMargin.BStart(aWM) + aCaptionMargin.BStart(aWM); break; default: @@ -675,21 +673,23 @@ nsTableOuterFrame::GetInnerOrigin(uint32_t aCaptionSide, // inline-dir computation switch (aCaptionSide) { - case NS_STYLE_CAPTION_SIDE_ISTART: - if (aInnerMargin.IStart(aWM) < minCapISize) { - // shift the inner table to get some place for the caption - aInnerMargin.IEnd(aWM) += aInnerMargin.IStart(aWM) - minCapISize; - aInnerMargin.IEnd(aWM) = std::max(0, aInnerMargin.IEnd(aWM)); - aInnerMargin.IStart(aWM) = minCapISize; + case NS_STYLE_CAPTION_SIDE_LEFT: + case NS_STYLE_CAPTION_SIDE_RIGHT: + if (aWM.IsBidiLTR() == (aCaptionSide == NS_STYLE_CAPTION_SIDE_LEFT)) { + if (aInnerMargin.IStart(aWM) < minCapISize) { + // shift the inner table to get some place for the caption + aInnerMargin.IEnd(aWM) += aInnerMargin.IStart(aWM) - minCapISize; + aInnerMargin.IEnd(aWM) = std::max(0, aInnerMargin.IEnd(aWM)); + aInnerMargin.IStart(aWM) = minCapISize; + } } aOrigin.I(aWM) = aInnerMargin.IStart(aWM); break; default: - NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_BSTART || - aCaptionSide == NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE || - aCaptionSide == NS_STYLE_CAPTION_SIDE_BEND || - aCaptionSide == NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE || - aCaptionSide == NS_STYLE_CAPTION_SIDE_IEND || + NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP || + aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || + aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM || + aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE || aCaptionSide == NO_SIDE, "unexpected caption side"); aOrigin.I(aWM) = aInnerMargin.IStart(aWM); @@ -698,12 +698,12 @@ nsTableOuterFrame::GetInnerOrigin(uint32_t aCaptionSide, // block-dir computation switch (aCaptionSide) { - case NS_STYLE_CAPTION_SIDE_BEND: - case NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE: + case NS_STYLE_CAPTION_SIDE_BOTTOM: + case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: aOrigin.B(aWM) = aInnerMargin.BStart(aWM); break; - case NS_STYLE_CAPTION_SIDE_ISTART: - case NS_STYLE_CAPTION_SIDE_IEND: + case NS_STYLE_CAPTION_SIDE_LEFT: + case NS_STYLE_CAPTION_SIDE_RIGHT: aOrigin.B(aWM) = aInnerMargin.BStart(aWM); switch (GetCaptionVerticalAlign()) { case NS_STYLE_VERTICAL_ALIGN_MIDDLE: @@ -721,8 +721,8 @@ nsTableOuterFrame::GetInnerOrigin(uint32_t aCaptionSide, } break; case NO_SIDE: - case NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE: - case NS_STYLE_CAPTION_SIDE_BSTART: + case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: + case NS_STYLE_CAPTION_SIDE_TOP: aOrigin.B(aWM) = aInnerMargin.BStart(aWM) + aCaptionSize.BSize(aWM) + aCaptionMargin.BStartEnd(aWM); break; @@ -771,9 +771,9 @@ nsTableOuterFrame::OuterBeginReflowChild(nsPresContext* aPresContext, // see if we need to reset top-of-page due to a caption if (aChildRS->mFlags.mIsTopOfPage && mCaptionFrames.FirstChild() == aChildFrame) { - uint8_t captionSide = GetLogicalCaptionSide(wm); - if (captionSide == NS_STYLE_CAPTION_SIDE_BEND || - captionSide == NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE) { + uint8_t captionSide = GetCaptionSide(); + if (captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) { aChildRS->mFlags.mIsTopOfPage = false; } } @@ -860,15 +860,15 @@ nsTableOuterFrame::Reflow(nsPresContext* aPresContext, // ComputeAutoSize has to match this logic. WritingMode wm = aOuterRS.GetWritingMode(); - uint8_t captionSide = GetLogicalCaptionSide(wm); + uint8_t captionSide = GetCaptionSide(); WritingMode captionWM = wm; // will be changed below if necessary if (captionSide == NO_SIDE) { // We don't have a caption. OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, innerRS, aOuterRS.ComputedSize(wm).ISize(wm)); - } else if (captionSide == NS_STYLE_CAPTION_SIDE_ISTART || - captionSide == NS_STYLE_CAPTION_SIDE_IEND) { + } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT || + captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) { // ComputeAutoSize takes care of making side captions small. Compute // the caption's size first, and tell the table to fit in what's left. OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS, @@ -878,8 +878,8 @@ nsTableOuterFrame::Reflow(nsPresContext* aPresContext, captionRS->ComputedSizeWithMarginBorderPadding(wm).ISize(wm); OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS, innerRS, innerAvailISize); - } else if (captionSide == NS_STYLE_CAPTION_SIDE_BSTART || - captionSide == NS_STYLE_CAPTION_SIDE_BEND) { + } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) { // Compute the table's size first, and then prevent the caption from // being larger in the inline dir unless it has to be. // @@ -901,8 +901,8 @@ nsTableOuterFrame::Reflow(nsPresContext* aPresContext, captionRS, innerBorderISize); captionWM = captionRS->GetWritingMode(); } else { - NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE || - captionSide == NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE, + NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE || + captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE, "unexpected caption-side"); // Size the table and the caption independently. captionWM = mCaptionFrames.FirstChild()->GetWritingMode(); @@ -932,10 +932,10 @@ nsTableOuterFrame::Reflow(nsPresContext* aPresContext, if (NS_UNCONSTRAINEDSIZE != aOuterRS.AvailableBSize()) { nscoord captionBSize = 0; switch (captionSide) { - case NS_STYLE_CAPTION_SIDE_BSTART: - case NS_STYLE_CAPTION_SIDE_BEND: - case NS_STYLE_CAPTION_SIDE_BSTART_OUTSIDE: - case NS_STYLE_CAPTION_SIDE_BEND_OUTSIDE: + case NS_STYLE_CAPTION_SIDE_TOP: + case NS_STYLE_CAPTION_SIDE_BOTTOM: + case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE: + case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: captionBSize = captionSize.BSize(wm) + captionMargin.BStartEnd(wm); break; } diff --git a/layout/tables/nsTableOuterFrame.h b/layout/tables/nsTableOuterFrame.h index 23956c747e..eb0453592c 100644 --- a/layout/tables/nsTableOuterFrame.h +++ b/layout/tables/nsTableOuterFrame.h @@ -188,14 +188,15 @@ protected: void InitChildReflowState(nsPresContext& aPresContext, nsHTMLReflowState& aReflowState); - // Get a NS_STYLE_CAPTION_SIDE_* value, with physically-specified sides - // resolved to logical sides, or NO_SIDE if no caption is present. - uint8_t GetLogicalCaptionSide(mozilla::WritingMode aWM); + // Get a NS_STYLE_CAPTION_SIDE_* value, or NO_SIDE if no caption is present. + // (Remember that caption-side values are interpreted logically, despite + // having "physical" names.) + uint8_t GetCaptionSide(); - bool HasSideCaption(mozilla::WritingMode aWM) { - uint8_t captionSide = GetLogicalCaptionSide(aWM); - return captionSide == NS_STYLE_CAPTION_SIDE_ISTART || - captionSide == NS_STYLE_CAPTION_SIDE_IEND; + bool HasSideCaption() { + uint8_t captionSide = GetCaptionSide(); + return captionSide == NS_STYLE_CAPTION_SIDE_LEFT || + captionSide == NS_STYLE_CAPTION_SIDE_RIGHT; } uint8_t GetCaptionVerticalAlign(); diff --git a/mfbt/XorShift128PlusRNG.h b/mfbt/XorShift128PlusRNG.h new file mode 100644 index 0000000000..fb4139fe0e --- /dev/null +++ b/mfbt/XorShift128PlusRNG.h @@ -0,0 +1,97 @@ +/* -*- 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/. */ + +/* The xorshift128+ pseudo-random number generator. */ + +#ifndef mozilla_XorShift128Plus_h +#define mozilla_XorShift128Plus_h + +#include "mozilla/Assertions.h" +#include "mozilla/FloatingPoint.h" + +#include +#include +#include + +namespace mozilla { +namespace non_crypto { + +/* + * A stream of pseudo-random numbers generated using the xorshift+ technique + * described here: + * + * Vigna, Sebastiano (2014). "Further scramblings of Marsaglia's xorshift + * generators". arXiv:1404.0390 (http://arxiv.org/abs/1404.0390) + * + * That paper says: + * + * In particular, we propose a tightly coded xorshift128+ generator that + * does not fail systematically any test from the BigCrush suite of TestU01 + * (even reversed) and generates 64 pseudorandom bits in 1.10 ns on an + * Intel(R) Core(TM) i7-4770 CPU @3.40GHz (Haswell). It is the fastest + * generator we are aware of with such empirical statistical properties. + * + * This generator is not suitable as a cryptographically secure random number + * generator. + */ +class XorShift128PlusRNG { + uint64_t mState[2]; + + public: + /* + * Construct a xorshift128+ pseudo-random number stream using |aInitial0| and + * |aInitial1| as the initial state. These may not both be zero; ideally, they + * should have an almost even mix of zero and one bits. + */ + XorShift128PlusRNG(uint64_t aInitial0, uint64_t aInitial1) { + setState(aInitial0, aInitial1); + } + + /* Return a pseudo-random 64-bit number. */ + uint64_t next() { + uint64_t s1 = mState[0]; + const uint64_t s0 = mState[1]; + mState[0] = s0; + s1 ^= s1 << 23; + mState[1] = s1 ^ s0 ^ (s1 >> 17) ^ (s0 >> 26); + return mState[1] + s0; + } + + /* + * Return a pseudo-random floating-point value in the range [0, 1). + * More precisely, choose an integer in the range [0, 2**53) and + * divide it by 2**53. + */ + double nextDouble() { + /* + * Because the IEEE 64-bit floating point format stores the leading '1' bit + * of the mantissa implicitly, it effectively represents a mantissa in the + * range [0, 2**53) in only 52 bits. FloatingPoint::kExponentShift + * is the width of the bitfield in the in-memory format, so we must add one + * to get the mantissa's range. + */ + static const int kMantissaBits = + mozilla::FloatingPoint::kExponentShift + 1; + uint64_t mantissa = next() & ((1ULL << kMantissaBits) - 1); + return ldexp(static_cast(mantissa), -kMantissaBits); + } + + /* + * Set the stream's current state to |aState0| and |aState1|. These must not + * both be zero; ideally, they should have an almost even mix of zero and one + * bits. + */ + void setState(uint64_t aState0, uint64_t aState1) { + MOZ_ASSERT(aState0 || aState1); + mState[0] = aState0; + mState[1] = aState1; + } +}; + +} // namespace non_crypto +} // namespace mozilla + +#endif // mozilla_XorShift128Plus_h diff --git a/mfbt/moz.build b/mfbt/moz.build index d4fe3e8198..d3ba9e1de6 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -95,6 +95,7 @@ EXPORTS.mozilla = [ 'Variant.h', 'Vector.h', 'WeakPtr.h', + 'XorShift128PlusRNG.h', 'unused.h', ] diff --git a/mfbt/tests/TestXorShift128PlusRNG.cpp b/mfbt/tests/TestXorShift128PlusRNG.cpp new file mode 100644 index 0000000000..dbe83d049e --- /dev/null +++ b/mfbt/tests/TestXorShift128PlusRNG.cpp @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include + +#include "mozilla/Assertions.h" +#include "mozilla/PodOperations.h" +#include "mozilla/XorShift128PlusRNG.h" + +using mozilla::non_crypto::XorShift128PlusRNG; + +static void +TestDumbSequence() +{ + XorShift128PlusRNG rng(1, 4); + + // Calculated by hand following the algorithm given in the paper. The upper + // bits are mostly zero because we started with a poor seed; once it has run + // for a while, we'll get an even mix of ones and zeros in all 64 bits. + MOZ_RELEASE_ASSERT(rng.next() == 0x800049); + MOZ_RELEASE_ASSERT(rng.next() == 0x3000186); + MOZ_RELEASE_ASSERT(rng.next() == 0x400003001145); + + // Using ldexp here lets us write out the mantissa in hex, so we can compare + // them with the results generated by hand. + MOZ_RELEASE_ASSERT(rng.nextDouble() + == ldexp(static_cast(0x1400003105049), -53)); + MOZ_RELEASE_ASSERT(rng.nextDouble() + == ldexp(static_cast(0x2000802e49146), -53)); + MOZ_RELEASE_ASSERT(rng.nextDouble() + == ldexp(static_cast(0x248300468544d), -53)); +} + +static size_t +Population(uint64_t n) +{ + size_t pop = 0; + + while (n > 0) { + n &= n-1; // Clear the rightmost 1-bit in n. + pop++; + } + + return pop; +} + +static void +TestPopulation() +{ + XorShift128PlusRNG rng(698079309544035222ULL, 6012389156611637584ULL); + + // Give it some time to warm up; it should tend towards more + // even distributions of zeros and ones. + for (size_t i = 0; i < 40; i++) + rng.next(); + + for (size_t i = 0; i < 40; i++) { + size_t pop = Population(rng.next()); + MOZ_RELEASE_ASSERT(24 <= pop && pop <= 40); + } +} + +static void +TestSetState() +{ + static const uint64_t seed[2] = { 1795644156779822404ULL, 14162896116325912595ULL }; + XorShift128PlusRNG rng(seed[0], seed[1]); + + const size_t n = 10; + uint64_t log[n]; + + for (size_t i = 0; i < n; i++) + log[i] = rng.next(); + + rng.setState(seed[0], seed[1]); + + for (size_t i = 0; i < n; i++) + MOZ_RELEASE_ASSERT(log[i] == rng.next()); +} + +static void +TestDoubleDistribution() +{ + XorShift128PlusRNG rng(0xa207aaede6859736, 0xaca6ca5060804791); + + const size_t n = 100; + size_t bins[n]; + mozilla::PodArrayZero(bins); + + // This entire file runs in 0.006s on my laptop. Generating + // more numbers lets us put tighter bounds on the bins. + for (size_t i = 0; i < 100000; i++) { + double d = rng.nextDouble(); + MOZ_RELEASE_ASSERT(0.0 <= d && d < 1.0); + bins[(int) (d * n)]++; + } + + for (size_t i = 0; i < n; i++) { + MOZ_RELEASE_ASSERT(900 <= bins[i] && bins[i] <= 1100); + } +} + +int +main() +{ + TestDumbSequence(); + TestPopulation(); + TestSetState(); + TestDoubleDistribution(); + + return 0; +} diff --git a/mfbt/tests/moz.build b/mfbt/tests/moz.build index 0ddb14323e..a2b495acc3 100644 --- a/mfbt/tests/moz.build +++ b/mfbt/tests/moz.build @@ -42,6 +42,7 @@ CppUnitTests([ 'TestVariant', 'TestVector', 'TestWeakPtr', + 'TestXorShift128PlusRNG', ]) if not CONFIG['MOZ_ASAN']: diff --git a/toolkit/devtools/server/actors/csscoverage.js b/toolkit/devtools/server/actors/csscoverage.js index ea2e19b89f..02a42d1e58 100644 --- a/toolkit/devtools/server/actors/csscoverage.js +++ b/toolkit/devtools/server/actors/csscoverage.js @@ -564,8 +564,9 @@ function getImportedSheets(stylesheet) { * @see deconstructRuleId(ruleId) */ function ruleToId(rule) { - let loc = stylesheets.getRuleLocation(rule); - return sheetToUrl(rule.parentStyleSheet) + "|" + loc.line + "|" + loc.column; + let line = DOMUtils.getRelativeRuleLine(rule); + let column = DOMUtils.getRuleColumn(rule); + return sheetToUrl(rule.parentStyleSheet) + "|" + line + "|" + column; } /** diff --git a/toolkit/devtools/server/actors/stylesheets.js b/toolkit/devtools/server/actors/stylesheets.js index 95881ca3bb..82d4d5e103 100644 --- a/toolkit/devtools/server/actors/stylesheets.js +++ b/toolkit/devtools/server/actors/stylesheets.js @@ -912,53 +912,6 @@ let StyleSheetActor = protocol.ActorClass({ } }) -/** - * Find the line/column for a rule. - * This is like DOMUtils.getRule[Line|Column] except for inline + + +
Styled Node
+
Styled Node
+
Styled Node
+ + diff --git a/tools/profiler/public/GeckoProfiler.h b/tools/profiler/public/GeckoProfiler.h index e82944e989..f8ec8c7369 100644 --- a/tools/profiler/public/GeckoProfiler.h +++ b/tools/profiler/public/GeckoProfiler.h @@ -53,7 +53,6 @@ #include "js/TypeDecls.h" #endif #include "mozilla/UniquePtr.h" -#include "mozilla/GuardObjects.h" namespace mozilla { class TimeStamp; diff --git a/xpcom/glue/nsHashKeys.h b/xpcom/glue/nsHashKeys.h index 9ed477dab3..462728a10a 100644 --- a/xpcom/glue/nsHashKeys.h +++ b/xpcom/glue/nsHashKeys.h @@ -651,6 +651,17 @@ private: nsCOMPtr mKey; }; +namespace mozilla { + +template +PLDHashNumber +Hash(const T& aValue) +{ + return aValue.Hash(); +} + +} // namespace mozilla + /** * Hashtable key class to use with objects for which Hash() and operator==() * are defined. @@ -669,7 +680,7 @@ public: bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mKey; } static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } - static PLDHashNumber HashKey(KeyTypePointer aKey) { return aKey->Hash(); } + static PLDHashNumber HashKey(KeyTypePointer aKey) { return ::mozilla::Hash(*aKey); } enum { ALLOW_MEMMOVE = true }; private: