Bug 1264409 - Make last transaction ID available via nsIDOMWindowUtils, and pass transaction ID through MozAfterPaint. r?mattwoodrow
MozReview-Commit-ID: 7wCYif8F6ws
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2301,16 +2301,29 @@ nsDOMWindowUtils::AdvanceTimeAndRefresh(
transaction->SendSetTestSampleTime(driver->MostRecentRefresh());
}
}
return NS_OK;
}
NS_IMETHODIMP
+nsDOMWindowUtils::GetLastTransactionId(uint64_t *aLastTransactionId)
+{
+ nsPresContext* presContext = GetPresContext();
+ if (!presContext) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsRefreshDriver* driver = presContext->GetRootPresContext()->RefreshDriver();
+ *aLastTransactionId = driver->LastTransactionId();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsDOMWindowUtils::RestoreNormalRefresh()
{
// Kick the compositor out of test mode before the refresh driver, so that
// the refresh driver doesn't send an update that gets ignored by the
// compositor.
RefPtr<LayerTransactionChild> transaction = GetLayerTransaction();
if (transaction && transaction->IPCOpen()) {
transaction->SendLeaveTestMode();
--- a/dom/events/NotifyPaintEvent.cpp
+++ b/dom/events/NotifyPaintEvent.cpp
@@ -14,25 +14,28 @@
namespace mozilla {
namespace dom {
NotifyPaintEvent::NotifyPaintEvent(EventTarget* aOwner,
nsPresContext* aPresContext,
WidgetEvent* aEvent,
EventMessage aEventMessage,
- nsInvalidateRequestList* aInvalidateRequests)
+ nsInvalidateRequestList* aInvalidateRequests,
+ uint64_t aTransactionId)
: Event(aOwner, aPresContext, aEvent)
{
if (mEvent) {
mEvent->mMessage = aEventMessage;
}
if (aInvalidateRequests) {
mInvalidateRequests.AppendElements(Move(aInvalidateRequests->mRequests));
}
+
+ mTransactionId = aTransactionId;
}
NS_INTERFACE_MAP_BEGIN(NotifyPaintEvent)
NS_INTERFACE_MAP_ENTRY(nsIDOMNotifyPaintEvent)
NS_INTERFACE_MAP_END_INHERITING(Event)
NS_IMPL_ADDREF_INHERITED(NotifyPaintEvent, Event)
NS_IMPL_RELEASE_INHERITED(NotifyPaintEvent, Event)
@@ -149,26 +152,40 @@ NotifyPaintEvent::Deserialize(const IPC:
NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &req.mRect), false);
NS_ENSURE_TRUE(IPC::ReadParam(aMsg, aIter, &req.mFlags), false);
mInvalidateRequests.AppendElement(req);
}
return true;
}
+NS_IMETHODIMP
+NotifyPaintEvent::GetTransactionId(uint64_t* aTransactionId)
+{
+ *aTransactionId = mTransactionId;
+ return NS_OK;
+}
+
+uint64_t
+NotifyPaintEvent::TransactionId()
+{
+ return mTransactionId;
+}
+
} // namespace dom
} // namespace mozilla
using namespace mozilla;
using namespace mozilla::dom;
already_AddRefed<NotifyPaintEvent>
NS_NewDOMNotifyPaintEvent(EventTarget* aOwner,
nsPresContext* aPresContext,
WidgetEvent* aEvent,
EventMessage aEventMessage,
- nsInvalidateRequestList* aInvalidateRequests)
+ nsInvalidateRequestList* aInvalidateRequests,
+ uint64_t aTransactionId)
{
RefPtr<NotifyPaintEvent> it =
new NotifyPaintEvent(aOwner, aPresContext, aEvent, aEventMessage,
- aInvalidateRequests);
+ aInvalidateRequests, aTransactionId);
return it.forget();
}
--- a/dom/events/NotifyPaintEvent.h
+++ b/dom/events/NotifyPaintEvent.h
@@ -24,17 +24,18 @@ class NotifyPaintEvent : public Event,
public nsIDOMNotifyPaintEvent
{
public:
NotifyPaintEvent(EventTarget* aOwner,
nsPresContext* aPresContext,
WidgetEvent* aEvent,
EventMessage aEventMessage,
- nsInvalidateRequestList* aInvalidateRequests);
+ nsInvalidateRequestList* aInvalidateRequests,
+ uint64_t aTransactionId);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMNOTIFYPAINTEVENT
// Forward to base class
NS_FORWARD_TO_EVENT_NO_SERIALIZATION_NO_DUPLICATION
NS_IMETHOD DuplicatePrivateData() override
@@ -50,31 +51,35 @@ public:
}
already_AddRefed<DOMRectList> ClientRects();
already_AddRefed<DOMRect> BoundingClientRect();
already_AddRefed<PaintRequestList> PaintRequests();
+ uint64_t TransactionId();
+
protected:
~NotifyPaintEvent() {}
private:
nsRegion GetRegion();
nsTArray<nsInvalidateRequestList::Request> mInvalidateRequests;
+ uint64_t mTransactionId;
};
} // namespace dom
} // namespace mozilla
// This empties aInvalidateRequests.
already_AddRefed<mozilla::dom::NotifyPaintEvent>
NS_NewDOMNotifyPaintEvent(mozilla::dom::EventTarget* aOwner,
nsPresContext* aPresContext,
mozilla::WidgetEvent* aEvent,
mozilla::EventMessage aEventMessage =
mozilla::eVoidEvent,
nsInvalidateRequestList* aInvalidateRequests =
- nullptr);
+ nullptr,
+ uint64_t aTransactionId = 0);
#endif // mozilla_dom_NotifyPaintEvent_h_
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -102,16 +102,21 @@ interface nsIDOMWindowUtils : nsISupport
unsigned long redraw([optional] in unsigned long aCount);
/**
* Force a synchronous layer transaction for this window if necessary.
*/
void updateLayerTree();
/**
+ * Get the last used layer transaction id for this window's refresh driver.
+ */
+ readonly attribute unsigned long long lastTransactionId;
+
+ /**
* Information retrieved from the <meta name="viewport"> tag.
* See nsContentUtils::GetViewportInfo for more information.
*/
void getViewportInfo(in uint32_t aDisplayWidth, in uint32_t aDisplayHeight,
out double aDefaultZoom, out boolean aAllowZoom,
out double aMinZoom, out double aMaxZoom,
out uint32_t aWidth, out uint32_t aHeight,
out boolean aAutoSize);
--- a/dom/interfaces/events/nsIDOMNotifyPaintEvent.idl
+++ b/dom/interfaces/events/nsIDOMNotifyPaintEvent.idl
@@ -30,10 +30,12 @@ interface nsIDOMNotifyPaintEvent : nsISu
* is in CSS pixels relative to the viewport origin.
* If the caller is not trusted (e.g., regular Web content) then only painting
* caused by the current document is reported; in particular, painting in subdocuments
* is not reported.
*/
readonly attribute nsIDOMClientRect boundingClientRect;
readonly attribute nsISupports /* PaintRequestList */ paintRequests;
+
+ readonly attribute unsigned long long transactionId;
};
--- a/dom/webidl/NotifyPaintEvent.webidl
+++ b/dom/webidl/NotifyPaintEvent.webidl
@@ -8,9 +8,11 @@
interface NotifyPaintEvent : Event
{
readonly attribute DOMRectList clientRects;
readonly attribute DOMRect boundingClientRect;
readonly attribute PaintRequestList paintRequests;
+
+ readonly attribute unsigned long long transactionId;
};
--- a/gfx/layers/TransactionIdAllocator.h
+++ b/gfx/layers/TransactionIdAllocator.h
@@ -24,16 +24,23 @@ public:
* only be called while IsInRefresh().
*
* If too many id's are allocated without being returned then
* the refresh driver will suspend until they catch up.
*/
virtual uint64_t GetTransactionId() = 0;
/**
+ * Return the transaction id that for the last non-revoked transaction.
+ * This allows the caller to tell whether a composite was triggered by
+ * a paint that occurred after a call to TransactionId().
+ */
+ virtual uint64_t LastTransactionId() const = 0;
+
+ /**
* Notify that all work (including asynchronous composites)
* for a given transaction id has been completed.
*
* If the refresh driver has been suspended because
* of having too many outstanding id's, then this may
* resume it.
*/
virtual void NotifyTransactionCompleted(uint64_t aTransactionId) = 0;
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -414,21 +414,21 @@ ClientLayerManager::DidComposite(uint64_
{
MOZ_ASSERT(mWidget);
// |aTransactionId| will be > 0 if the compositor is acknowledging a shadow
// layers transaction.
if (aTransactionId) {
nsIWidgetListener *listener = mWidget->GetWidgetListener();
if (listener) {
- listener->DidCompositeWindow(aCompositeStart, aCompositeEnd);
+ listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
}
listener = mWidget->GetAttachedWidgetListener();
if (listener) {
- listener->DidCompositeWindow(aCompositeStart, aCompositeEnd);
+ listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
}
mTransactionIdAllocator->NotifyTransactionCompleted(aTransactionId);
}
// These observers fire whether or not we were in a transaction.
for (size_t i = 0; i < mDidCompositeObservers.Length(); i++) {
mDidCompositeObservers[i]->DidComposite();
}
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2152,17 +2152,17 @@ nsPresContext::EnsureSafeToHandOutCSSRul
// Nothing to do.
return;
}
RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree);
}
void
-nsPresContext::FireDOMPaintEvent(nsInvalidateRequestList* aList)
+nsPresContext::FireDOMPaintEvent(nsInvalidateRequestList* aList, uint64_t aTransactionId)
{
nsPIDOMWindowInner* ourWindow = mDocument->GetInnerWindow();
if (!ourWindow)
return;
nsCOMPtr<EventTarget> dispatchTarget = do_QueryInterface(ourWindow);
nsCOMPtr<EventTarget> eventTarget = dispatchTarget;
if (!IsChrome() && !mSendAfterPaintToContent) {
@@ -2177,17 +2177,17 @@ nsPresContext::FireDOMPaintEvent(nsInval
}
// Events sent to the window get propagated to the chrome event handler
// automatically.
//
// This will empty our list in case dispatching the event causes more damage
// (hopefully it won't, or we're likely to get an infinite loop! At least
// it won't be blocking app execution though).
RefPtr<NotifyPaintEvent> event =
- NS_NewDOMNotifyPaintEvent(eventTarget, this, nullptr, eAfterPaint, aList);
+ NS_NewDOMNotifyPaintEvent(eventTarget, this, nullptr, eAfterPaint, aList, aTransactionId);
// Even if we're not telling the window about the event (so eventTarget is
// the chrome event handler, not the window), the window is still
// logically the event target.
event->SetTarget(eventTarget);
event->SetTrusted(true);
EventDispatcher::DispatchDOMEvent(dispatchTarget, nullptr,
static_cast<Event*>(event), this, nullptr);
@@ -2374,62 +2374,67 @@ nsPresContext::SetNotifySubDocInvalidati
/* static */ void
nsPresContext::ClearNotifySubDocInvalidationData(ContainerLayer* aContainer)
{
aContainer->SetUserData(&gNotifySubDocInvalidationData, nullptr);
}
struct NotifyDidPaintSubdocumentCallbackClosure {
uint32_t mFlags;
+ uint64_t mTransactionId;
bool mNeedsAnotherDidPaintNotification;
};
static bool
NotifyDidPaintSubdocumentCallback(nsIDocument* aDocument, void* aData)
{
NotifyDidPaintSubdocumentCallbackClosure* closure =
static_cast<NotifyDidPaintSubdocumentCallbackClosure*>(aData);
nsIPresShell* shell = aDocument->GetShell();
if (shell) {
nsPresContext* pc = shell->GetPresContext();
if (pc) {
- pc->NotifyDidPaintForSubtree(closure->mFlags);
+ pc->NotifyDidPaintForSubtree(closure->mFlags, closure->mTransactionId);
if (pc->IsDOMPaintEventPending()) {
closure->mNeedsAnotherDidPaintNotification = true;
}
}
}
return true;
}
class DelayedFireDOMPaintEvent : public nsRunnable {
public:
DelayedFireDOMPaintEvent(nsPresContext* aPresContext,
- nsInvalidateRequestList* aList)
+ nsInvalidateRequestList* aList,
+ uint64_t aTransactionId)
: mPresContext(aPresContext)
+ , mTransactionId(aTransactionId)
{
MOZ_ASSERT(mPresContext->GetContainerWeak(),
"DOMPaintEvent requested for a detached pres context");
mList.TakeFrom(aList);
}
NS_IMETHOD Run() override
{
// The pres context might have been detached during the delay -
// that's fine, just don't fire the event.
if (mPresContext->GetContainerWeak()) {
- mPresContext->FireDOMPaintEvent(&mList);
+ mPresContext->FireDOMPaintEvent(&mList, mTransactionId);
}
return NS_OK;
}
RefPtr<nsPresContext> mPresContext;
+ uint64_t mTransactionId;
nsInvalidateRequestList mList;
};
void
-nsPresContext::NotifyDidPaintForSubtree(uint32_t aFlags)
+nsPresContext::NotifyDidPaintForSubtree(uint32_t aFlags, uint64_t aTransactionId,
+ const mozilla::TimeStamp& aTimeStamp)
{
if (IsRoot()) {
static_cast<nsRootPresContext*>(this)->CancelDidPaintTimer();
if (!mFireAfterPaintEvents) {
return;
}
}
@@ -2446,21 +2451,22 @@ nsPresContext::NotifyDidPaintForSubtree(
if (aFlags & nsIPresShell::PAINT_LAYERS) {
mUndeliveredInvalidateRequestsBeforeLastPaint.TakeFrom(
&mInvalidateRequestsSinceLastPaint);
mAllInvalidated = false;
}
if (aFlags & nsIPresShell::PAINT_COMPOSITE) {
nsCOMPtr<nsIRunnable> ev =
- new DelayedFireDOMPaintEvent(this, &mUndeliveredInvalidateRequestsBeforeLastPaint);
+ new DelayedFireDOMPaintEvent(this, &mUndeliveredInvalidateRequestsBeforeLastPaint,
+ aTransactionId);
nsContentUtils::AddScriptRunner(ev);
}
- NotifyDidPaintSubdocumentCallbackClosure closure = { aFlags, false };
+ NotifyDidPaintSubdocumentCallbackClosure closure = { aFlags, aTransactionId, false };
mDocument->EnumerateSubDocuments(NotifyDidPaintSubdocumentCallback, &closure);
if (!closure.mNeedsAnotherDidPaintNotification &&
mInvalidateRequestsSinceLastPaint.IsEmpty() &&
mUndeliveredInvalidateRequestsBeforeLastPaint.IsEmpty()) {
// Nothing more to do for the moment.
mFireAfterPaintEvents = false;
} else {
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -921,18 +921,19 @@ public:
// and, if necessary, synchronously rebuilding all style data.
void EnsureSafeToHandOutCSSRules();
void NotifyInvalidation(uint32_t aFlags);
void NotifyInvalidation(const nsRect& aRect, uint32_t aFlags);
// aRect is in device pixels
void NotifyInvalidation(const nsIntRect& aRect, uint32_t aFlags);
// aFlags are nsIPresShell::PAINT_ flags
- void NotifyDidPaintForSubtree(uint32_t aFlags);
- void FireDOMPaintEvent(nsInvalidateRequestList* aList);
+ void NotifyDidPaintForSubtree(uint32_t aFlags, uint64_t aTransactionId = 0,
+ const mozilla::TimeStamp& aTimeStamp = mozilla::TimeStamp());
+ void FireDOMPaintEvent(nsInvalidateRequestList* aList, uint64_t aTransactionId);
// Callback for catching invalidations in ContainerLayers
// Passed to LayerProperties::ComputeDifference
static void NotifySubDocInvalidation(mozilla::layers::ContainerLayer* aContainer,
const nsIntRegion& aRegion);
void SetNotifySubDocInvalidationData(mozilla::layers::ContainerLayer* aContainer);
static void ClearNotifySubDocInvalidationData(mozilla::layers::ContainerLayer* aContainer);
bool IsDOMPaintEventPending();
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -2002,16 +2002,22 @@ nsRefreshDriver::GetTransactionId()
!mTestControllingRefreshes) {
mWaitingForTransaction = true;
mSkippedPaints = false;
}
return mPendingTransaction;
}
+uint64_t
+nsRefreshDriver::LastTransactionId() const
+{
+ return mPendingTransaction;
+}
+
void
nsRefreshDriver::RevokeTransactionId(uint64_t aTransactionId)
{
MOZ_ASSERT(aTransactionId == mPendingTransaction);
if (mPendingTransaction == mCompletedTransaction + 2 &&
mWaitingForTransaction) {
MOZ_ASSERT(!mSkippedPaints, "How did we skip a paint when we're in the middle of one?");
FinishedWaitingForTransaction();
--- a/layout/base/nsRefreshDriver.h
+++ b/layout/base/nsRefreshDriver.h
@@ -314,17 +314,18 @@ public:
* time.
*
* Return `false` if `aJank` needs to be grown to accomodate the
* data but we didn't have enough memory.
*/
static bool GetJankLevels(mozilla::Vector<uint64_t>& aJank);
// mozilla::layers::TransactionIdAllocator
- virtual uint64_t GetTransactionId() override;
+ uint64_t GetTransactionId() override;
+ uint64_t LastTransactionId() const override;
void NotifyTransactionCompleted(uint64_t aTransactionId) override;
void RevokeTransactionId(uint64_t aTransactionId) override;
mozilla::TimeStamp GetTransactionStart() override;
bool IsWaitingForPaint(mozilla::TimeStamp aTime);
// nsARefreshObserver
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override { return TransactionIdAllocator::AddRef(); }
--- a/view/nsView.cpp
+++ b/view/nsView.cpp
@@ -1073,27 +1073,29 @@ nsView::PaintWindow(nsIWidget* aWidget,
void
nsView::DidPaintWindow()
{
RefPtr<nsViewManager> vm = mViewManager;
vm->DidPaintWindow();
}
void
-nsView::DidCompositeWindow(const TimeStamp& aCompositeStart,
+nsView::DidCompositeWindow(uint64_t aTransactionId,
+ const TimeStamp& aCompositeStart,
const TimeStamp& aCompositeEnd)
{
nsIPresShell* presShell = mViewManager->GetPresShell();
if (presShell) {
nsAutoScriptBlocker scriptBlocker;
nsPresContext* context = presShell->GetPresContext();
nsRootPresContext* rootContext = context->GetRootPresContext();
MOZ_ASSERT(rootContext, "rootContext must be valid.");
- rootContext->NotifyDidPaintForSubtree(nsIPresShell::PAINT_COMPOSITE);
+ rootContext->NotifyDidPaintForSubtree(nsIPresShell::PAINT_COMPOSITE, aTransactionId,
+ aCompositeEnd);
// If the two timestamps are identical, this was likely a fake composite
// event which wouldn't be terribly useful to display.
if (aCompositeStart == aCompositeEnd) {
return;
}
nsIDocShell* docShell = context->GetDocShell();
--- a/view/nsView.h
+++ b/view/nsView.h
@@ -381,17 +381,18 @@ public:
virtual nsView* GetView() override { return this; }
virtual bool WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) override;
virtual bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) override;
virtual bool RequestWindowClose(nsIWidget* aWidget) override;
virtual void WillPaintWindow(nsIWidget* aWidget) override;
virtual bool PaintWindow(nsIWidget* aWidget,
LayoutDeviceIntRegion aRegion) override;
virtual void DidPaintWindow() override;
- virtual void DidCompositeWindow(const mozilla::TimeStamp& aCompositeStart,
+ virtual void DidCompositeWindow(uint64_t aTransactionId,
+ const mozilla::TimeStamp& aCompositeStart,
const mozilla::TimeStamp& aCompositeEnd) override;
virtual void RequestRepaint() override;
virtual nsEventStatus HandleEvent(mozilla::WidgetGUIEvent* aEvent,
bool aUseAttachedEvents) override;
virtual ~nsView();
nsPoint GetOffsetTo(const nsView* aOther, const int32_t aAPD) const;
--- a/widget/nsIWidgetListener.cpp
+++ b/widget/nsIWidgetListener.cpp
@@ -107,17 +107,18 @@ nsIWidgetListener::PaintWindow(nsIWidget
}
void
nsIWidgetListener::DidPaintWindow()
{
}
void
-nsIWidgetListener::DidCompositeWindow(const TimeStamp& aCompositeStart,
+nsIWidgetListener::DidCompositeWindow(uint64_t aTransactionId,
+ const TimeStamp& aCompositeStart,
const TimeStamp& aCompositeEnd)
{
}
void
nsIWidgetListener::RequestRepaint()
{
}
--- a/widget/nsIWidgetListener.h
+++ b/widget/nsIWidgetListener.h
@@ -141,17 +141,18 @@ public:
/**
* Indicates that a paint occurred.
* This is called at a time when it is OK to change the geometry of
* this widget or of other widgets.
* Must be called after every call to PaintWindow.
*/
virtual void DidPaintWindow();
- virtual void DidCompositeWindow(const mozilla::TimeStamp& aCompositeStart,
+ virtual void DidCompositeWindow(uint64_t aTransactionId,
+ const mozilla::TimeStamp& aCompositeStart,
const mozilla::TimeStamp& aCompositeEnd);
/**
* Request that layout schedules a repaint on the next refresh driver tick.
*/
virtual void RequestRepaint();
/**