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;
}
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
nsRootPresContext*
nsPresContext::GetRootPresContext()
{
nsPresContext* pc = this;
for (;;) {
if (pc->mShell) {
nsIFrame* rootFrame = pc->mShell->FrameManager()->GetRootFrame();
if (rootFrame) {
nsIFrame* f = nsLayoutUtils::GetCrossDocParentFrame(rootFrame);
if (f) {
pc = f->PresContext();
continue;
}
}
}
return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nsnull;
nsPresContext* parent = GetParentPresContext(pc);
if (!parent)
break;
pc = parent;
}
return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nsnull;
}
void
@ -2178,11 +2187,17 @@ nsPresContext::NotifyInvalidation(const nsRect& aRect, PRUint32 aFlags)
if (aRect.IsEmpty() || !MayHavePaintEventListener())
return;
if (!IsDOMPaintEventPending()) {
// No event is pending. Dispatch one now.
nsCOMPtr<nsIRunnable> ev =
NS_NewRunnableMethod(this, &nsPresContext::FireDOMPaintEvent);
NS_DispatchToCurrentThread(ev);
nsPresContext* pc;
for (pc = this; pc; pc = GetParentPresContext(pc)) {
if (pc->mFireAfterPaintEvents)
break;
pc->mFireAfterPaintEvents = PR_TRUE;
}
if (!pc) {
nsRootPresContext* rpc = GetRootPresContext();
if (rpc) {
rpc->EnsureEventualDidPaintEvent();
}
}
nsInvalidateRequestList::Request* request =
@ -2194,6 +2209,39 @@ nsPresContext::NotifyInvalidation(const nsRect& aRect, PRUint32 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
nsPresContext::HasCachedStyleData()
{
@ -2400,6 +2448,7 @@ nsRootPresContext::~nsRootPresContext()
{
NS_ASSERTION(mRegisteredPlugins.Count() == 0,
"All plugins should have been unregistered");
CancelDidPaintTimer();
}
void
@ -2734,3 +2783,23 @@ nsRootPresContext::RootForgetUpdatePluginGeometryFrame(nsIFrame* aFrame)
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();
void NotifyInvalidation(const nsRect& aRect, PRUint32 aFlags);
void NotifyDidPaintForSubtree();
void FireDOMPaintEvent();
PRBool IsDOMPaintEventPending() {
return !mInvalidateRequests.mRequests.IsEmpty();
}
void ClearMozAfterPaintEvents() {
mInvalidateRequests.mRequests.Clear();
}
@ -1157,6 +1158,7 @@ protected:
unsigned mProcessingAnimationStyleChange : 1;
unsigned mContainsUpdatePluginGeometryFrame : 1;
unsigned mFireAfterPaintEvents : 1;
// Cache whether we are chrome or not because it is expensive.
// 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;
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
* region) so it can update its widget.
@ -1276,6 +1292,7 @@ public:
PRUint32 GetDOMGeneration() { return mDOMGeneration; }
private:
nsCOMPtr<nsITimer> mNotifyDidPaintTimer;
nsTHashtable<nsPtrHashKey<nsObjectFrame> > mRegisteredPlugins;
// if mNeedsToUpdatePluginGeometry is set, then this is the frame to
// use as the root of the subtree to search for plugin updates, or

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

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

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

@ -174,24 +174,24 @@ function runTest3() {
gotScrolledOutInMainDoc = true;
}
function IFRAMEListener(event) {
if (doesRectContainListElement(
iframe.contentDocument.getElementById("d2").getBoundingClientRect(), event.clientRects))
gotScrolledOutInSubdoc = true;
}
function check() {
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);
iframe.contentWindow.removeEventListener("MozAfterPaint", IFRAMEListener, false);
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() {
window.addEventListener("MozAfterPaint", listener, false);
iframe.contentWindow.addEventListener("MozAfterPaint", IFRAMEListener, false);
setTimeout(check, 100);
flash(iframe.contentDocument, 'd2');
}
triggerPaint();