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 @@
+
+
+
+
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