Bug 612190. Make MozAfterPaint actually fire after paint. r=dbaron,a=me (blocks a blocker)

This commit is contained in:
Robert O'Callahan 2011-01-15 22:40:33 +13:00
Родитель 7193a092f0
Коммит 785dc45b3a
4 изменённых файлов: 116 добавлений и 27 удалений

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

@ -1103,24 +1103,33 @@ nsPresContext::Observe(nsISupports* aSubject,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
static nsPresContext*
GetParentPresContext(nsPresContext* aPresContext)
{
nsIPresShell* shell = aPresContext->GetPresShell();
if (shell) {
nsIFrame* rootFrame = shell->FrameManager()->GetRootFrame();
if (rootFrame) {
nsIFrame* f = nsLayoutUtils::GetCrossDocParentFrame(rootFrame);
if (f)
return f->PresContext();
}
}
return nsnull;
}
// We may want to replace this with something faster, maybe caching the root prescontext // We may want to replace this with something faster, maybe caching the root prescontext
nsRootPresContext* nsRootPresContext*
nsPresContext::GetRootPresContext() nsPresContext::GetRootPresContext()
{ {
nsPresContext* pc = this; nsPresContext* pc = this;
for (;;) { for (;;) {
if (pc->mShell) { nsPresContext* parent = GetParentPresContext(pc);
nsIFrame* rootFrame = pc->mShell->FrameManager()->GetRootFrame(); if (!parent)
if (rootFrame) { break;
nsIFrame* f = nsLayoutUtils::GetCrossDocParentFrame(rootFrame); pc = parent;
if (f) {
pc = f->PresContext();
continue;
}
}
}
return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nsnull;
} }
return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nsnull;
} }
void void
@ -2178,11 +2187,17 @@ nsPresContext::NotifyInvalidation(const nsRect& aRect, PRUint32 aFlags)
if (aRect.IsEmpty() || !MayHavePaintEventListener()) if (aRect.IsEmpty() || !MayHavePaintEventListener())
return; return;
if (!IsDOMPaintEventPending()) { nsPresContext* pc;
// No event is pending. Dispatch one now. for (pc = this; pc; pc = GetParentPresContext(pc)) {
nsCOMPtr<nsIRunnable> ev = if (pc->mFireAfterPaintEvents)
NS_NewRunnableMethod(this, &nsPresContext::FireDOMPaintEvent); break;
NS_DispatchToCurrentThread(ev); pc->mFireAfterPaintEvents = PR_TRUE;
}
if (!pc) {
nsRootPresContext* rpc = GetRootPresContext();
if (rpc) {
rpc->EnsureEventualDidPaintEvent();
}
} }
nsInvalidateRequestList::Request* request = nsInvalidateRequestList::Request* request =
@ -2194,6 +2209,39 @@ nsPresContext::NotifyInvalidation(const nsRect& aRect, PRUint32 aFlags)
request->mFlags = aFlags; request->mFlags = aFlags;
} }
static PRBool
NotifyDidPaintSubdocumentCallback(nsIDocument* aDocument, void* aData)
{
nsIPresShell* shell = aDocument->GetShell();
if (shell) {
nsPresContext* pc = shell->GetPresContext();
if (pc) {
pc->NotifyDidPaintForSubtree();
}
}
return PR_TRUE;
}
void
nsPresContext::NotifyDidPaintForSubtree()
{
if (!mFireAfterPaintEvents)
return;
mFireAfterPaintEvents = PR_FALSE;
if (IsRoot()) {
static_cast<nsRootPresContext*>(this)->CancelDidPaintTimer();
}
if (!mInvalidateRequests.mRequests.IsEmpty()) {
nsCOMPtr<nsIRunnable> ev =
NS_NewRunnableMethod(this, &nsPresContext::FireDOMPaintEvent);
nsContentUtils::AddScriptRunner(ev);
}
mDocument->EnumerateSubDocuments(NotifyDidPaintSubdocumentCallback, nsnull);
}
PRBool PRBool
nsPresContext::HasCachedStyleData() nsPresContext::HasCachedStyleData()
{ {
@ -2400,6 +2448,7 @@ nsRootPresContext::~nsRootPresContext()
{ {
NS_ASSERTION(mRegisteredPlugins.Count() == 0, NS_ASSERTION(mRegisteredPlugins.Count() == 0,
"All plugins should have been unregistered"); "All plugins should have been unregistered");
CancelDidPaintTimer();
} }
void void
@ -2734,3 +2783,23 @@ nsRootPresContext::RootForgetUpdatePluginGeometryFrame(nsIFrame* aFrame)
mUpdatePluginGeometryForFrame = nsnull; mUpdatePluginGeometryForFrame = nsnull;
} }
} }
static void
NotifyDidPaintForSubtreeCallback(nsITimer *aTimer, void *aClosure)
{
nsPresContext* presContext = (nsPresContext*)aClosure;
nsAutoScriptBlocker blockScripts;
presContext->NotifyDidPaintForSubtree();
}
void
nsRootPresContext::EnsureEventualDidPaintEvent()
{
if (mNotifyDidPaintTimer)
return;
mNotifyDidPaintTimer = do_CreateInstance("@mozilla.org/timer;1");
if (!mNotifyDidPaintTimer)
return;
mNotifyDidPaintTimer->InitWithFuncCallback(NotifyDidPaintForSubtreeCallback,
(void*)this, 100, nsITimer::TYPE_ONE_SHOT);
}

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

@ -862,11 +862,12 @@ public:
PRBool EnsureSafeToHandOutCSSRules(); PRBool EnsureSafeToHandOutCSSRules();
void NotifyInvalidation(const nsRect& aRect, PRUint32 aFlags); void NotifyInvalidation(const nsRect& aRect, PRUint32 aFlags);
void NotifyDidPaintForSubtree();
void FireDOMPaintEvent(); void FireDOMPaintEvent();
PRBool IsDOMPaintEventPending() { PRBool IsDOMPaintEventPending() {
return !mInvalidateRequests.mRequests.IsEmpty(); return !mInvalidateRequests.mRequests.IsEmpty();
} }
void ClearMozAfterPaintEvents() { void ClearMozAfterPaintEvents() {
mInvalidateRequests.mRequests.Clear(); mInvalidateRequests.mRequests.Clear();
} }
@ -1157,6 +1158,7 @@ protected:
unsigned mProcessingAnimationStyleChange : 1; unsigned mProcessingAnimationStyleChange : 1;
unsigned mContainsUpdatePluginGeometryFrame : 1; unsigned mContainsUpdatePluginGeometryFrame : 1;
unsigned mFireAfterPaintEvents : 1;
// Cache whether we are chrome or not because it is expensive. // Cache whether we are chrome or not because it is expensive.
// mIsChromeIsCached tells us if mIsChrome is valid or we need to get the // mIsChromeIsCached tells us if mIsChrome is valid or we need to get the
@ -1202,6 +1204,20 @@ public:
nsRootPresContext(nsIDocument* aDocument, nsPresContextType aType) NS_HIDDEN; nsRootPresContext(nsIDocument* aDocument, nsPresContextType aType) NS_HIDDEN;
virtual ~nsRootPresContext(); virtual ~nsRootPresContext();
/**
* Ensure that NotifyDidPaintForSubtree is eventually called on this
* object after a timeout.
*/
void EnsureEventualDidPaintEvent();
void CancelDidPaintTimer()
{
if (mNotifyDidPaintTimer) {
mNotifyDidPaintTimer->Cancel();
mNotifyDidPaintTimer = nsnull;
}
}
/** /**
* Registers a plugin to receive geometry updates (position and clip * Registers a plugin to receive geometry updates (position and clip
* region) so it can update its widget. * region) so it can update its widget.
@ -1276,6 +1292,7 @@ public:
PRUint32 GetDOMGeneration() { return mDOMGeneration; } PRUint32 GetDOMGeneration() { return mDOMGeneration; }
private: private:
nsCOMPtr<nsITimer> mNotifyDidPaintTimer;
nsTHashtable<nsPtrHashKey<nsObjectFrame> > mRegisteredPlugins; nsTHashtable<nsPtrHashKey<nsObjectFrame> > mRegisteredPlugins;
// if mNeedsToUpdatePluginGeometry is set, then this is the frame to // if mNeedsToUpdatePluginGeometry is set, then this is the frame to
// use as the root of the subtree to search for plugin updates, or // use as the root of the subtree to search for plugin updates, or

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

@ -6093,13 +6093,14 @@ PresShell::Paint(nsIView* aDisplayRoot,
NSCoordToFloat(bounds__.YMost())); NSCoordToFloat(bounds__.YMost()));
#endif #endif
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Paint);
NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell"); NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
NS_ASSERTION(aDisplayRoot, "null view"); NS_ASSERTION(aDisplayRoot, "null view");
NS_ASSERTION(aViewToPaint, "null view"); NS_ASSERTION(aViewToPaint, "null view");
NS_ASSERTION(aWidgetToPaint, "Can't paint without a widget"); NS_ASSERTION(aWidgetToPaint, "Can't paint without a widget");
nsPresContext* presContext = GetPresContext();
AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
nsIFrame* frame = aPaintDefaultBackground nsIFrame* frame = aPaintDefaultBackground
? nsnull : static_cast<nsIFrame*>(aDisplayRoot->GetClientData()); ? nsnull : static_cast<nsIFrame*>(aDisplayRoot->GetClientData());
@ -6131,6 +6132,7 @@ PresShell::Paint(nsIView* aDisplayRoot,
nsLayoutUtils::PAINT_WIDGET_LAYERS); nsLayoutUtils::PAINT_WIDGET_LAYERS);
frame->EndDeferringInvalidatesForDisplayRoot(); frame->EndDeferringInvalidatesForDisplayRoot();
presContext->NotifyDidPaintForSubtree();
return NS_OK; return NS_OK;
} }
@ -6159,6 +6161,7 @@ PresShell::Paint(nsIView* aDisplayRoot,
if (frame) { if (frame) {
frame->EndDeferringInvalidatesForDisplayRoot(); frame->EndDeferringInvalidatesForDisplayRoot();
} }
presContext->NotifyDidPaintForSubtree();
return NS_OK; return NS_OK;
} }

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

@ -174,24 +174,24 @@ function runTest3() {
gotScrolledOutInMainDoc = true; gotScrolledOutInMainDoc = true;
} }
function IFRAMEListener(event) {
if (doesRectContainListElement(
iframe.contentDocument.getElementById("d2").getBoundingClientRect(), event.clientRects))
gotScrolledOutInSubdoc = true;
}
function check() { function check() {
ok(!gotScrolledOutInMainDoc, "scrolled-out invalidation should not propagate to main doc"); ok(!gotScrolledOutInMainDoc, "scrolled-out invalidation should not propagate to main doc");
ok(gotScrolledOutInSubdoc, "scrolled-out invalidation should notify in subdoc");
window.removeEventListener("MozAfterPaint", listener, false); window.removeEventListener("MozAfterPaint", listener, false);
iframe.contentWindow.removeEventListener("MozAfterPaint", IFRAMEListener, false); iframe.contentWindow.removeEventListener("MozAfterPaint", IFRAMEListener, false);
runNext(); runNext();
} }
function IFRAMEListener(event) {
if (doesRectContainListElement(
iframe.contentDocument.getElementById("d2").getBoundingClientRect(), event.clientRects)) {
ok(true, "scrolled-out invalidation should notify in subdoc");
setTimeout(check, 0);
}
}
function triggerPaint() { function triggerPaint() {
window.addEventListener("MozAfterPaint", listener, false); window.addEventListener("MozAfterPaint", listener, false);
iframe.contentWindow.addEventListener("MozAfterPaint", IFRAMEListener, false); iframe.contentWindow.addEventListener("MozAfterPaint", IFRAMEListener, false);
setTimeout(check, 100);
flash(iframe.contentDocument, 'd2'); flash(iframe.contentDocument, 'd2');
} }
triggerPaint(); triggerPaint();