Bug 453650 - implement reflow observers for docShells; r=bz

This commit is contained in:
Tim Taubert 2013-06-01 12:00:12 +02:00
parent e59e54c50b
commit 1d11d0dc32
10 changed files with 253 additions and 1 deletions

View File

@ -24,6 +24,7 @@ XPIDL_SOURCES += [
'nsILoadContext.idl',
'nsIMarkupDocumentViewer.idl',
'nsIPrivacyTransitionObserver.idl',
'nsIReflowObserver.idl',
'nsIRefreshURI.idl',
'nsIScrollable.idl',
'nsITextScroll.idl',

View File

@ -83,6 +83,7 @@
#include "nsIOfflineCacheUpdate.h"
#include "nsITimedChannel.h"
#include "nsIPrivacyTransitionObserver.h"
#include "nsIReflowObserver.h"
#include "nsCPrefetchService.h"
#include "nsJSON.h"
#include "nsIDocShellTreeItem.h"
@ -2205,6 +2206,43 @@ nsDocShell::AddWeakPrivacyTransitionObserver(nsIPrivacyTransitionObserver* aObse
return mPrivacyObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsDocShell::AddWeakReflowObserver(nsIReflowObserver* aObserver)
{
nsWeakPtr weakObs = do_GetWeakReference(aObserver);
if (!weakObs) {
return NS_ERROR_FAILURE;
}
return mReflowObservers.AppendElement(weakObs) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsDocShell::RemoveWeakReflowObserver(nsIReflowObserver* aObserver)
{
nsWeakPtr obs = do_GetWeakReference(aObserver);
return mReflowObservers.RemoveElement(obs) ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsDocShell::NotifyReflowObservers(bool aInterruptible,
DOMHighResTimeStamp aStart,
DOMHighResTimeStamp aEnd)
{
nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mReflowObservers);
while (iter.HasMore()) {
nsWeakPtr ref = iter.GetNext();
nsCOMPtr<nsIReflowObserver> obs = do_QueryReferent(ref);
if (!obs) {
mReflowObservers.RemoveElement(ref);
} else if (aInterruptible) {
obs->ReflowInterruptible(aStart, aEnd);
} else {
obs->Reflow(aStart, aEnd);
}
}
return NS_OK;
}
NS_IMETHODIMP nsDocShell::GetAllowMetaRedirects(bool * aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);

View File

@ -872,6 +872,7 @@ private:
nsCString mForcedCharset;
nsCString mParentCharset;
nsTObserverArray<nsWeakPtr> mPrivacyObservers;
nsTObserverArray<nsWeakPtr> mReflowObservers;
int32_t mParentCharsetSource;
nsCString mOriginalUriString;

View File

@ -4,6 +4,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "domstubs.idl"
#include "nsIDocShellTreeItem.idl"
#include "nsIAtom.idl"
@ -39,8 +40,9 @@ interface nsIPrincipal;
interface nsIWebBrowserPrint;
interface nsIVariant;
interface nsIPrivacyTransitionObserver;
interface nsIReflowObserver;
[scriptable, builtinclass, uuid(2b192c9c-dea4-4696-a445-1bef7bc0db6d)]
[scriptable, builtinclass, uuid(d15b07e0-c604-11e2-8bdc-651679957a39)]
interface nsIDocShell : nsIDocShellTreeItem
{
/**
@ -616,6 +618,30 @@ interface nsIDocShell : nsIDocShellTreeItem
*/
void addWeakPrivacyTransitionObserver(in nsIPrivacyTransitionObserver obs);
/**
* Add an observer to the list of parties to be notified when reflows are
* occurring. |obs| must support weak references.
*/
void addWeakReflowObserver(in nsIReflowObserver obs);
/**
* Remove an observer from the list of parties to be notified about reflows.
*/
void removeWeakReflowObserver(in nsIReflowObserver obs);
/**
* Notify all attached observers that a reflow has just occurred.
*
* @param interruptible if true, the reflow was interruptible.
* @param start timestamp when reflow started, in milliseconds since
* navigationStart (accurate to 1/1000 of a ms)
* @param end timestamp when reflow ended, in milliseconds since
* navigationStart (accurate to 1/1000 of a ms)
*/
[noscript] void notifyReflowObservers(in bool interruptible,
in DOMHighResTimeStamp start,
in DOMHighResTimeStamp end);
/**
* Returns true if this docshell corresponds to an <iframe mozbrowser>.
* (<iframe mozapp mozbrowser> is not considered a browser.)

View File

@ -0,0 +1,32 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "domstubs.idl"
#include "nsISupports.idl"
[scriptable, uuid(832e692c-c4a6-11e2-8fd1-dce678957a39)]
interface nsIReflowObserver : nsISupports
{
/**
* Called when an uninterruptible reflow has occurred.
*
* @param start timestamp when reflow ended, in milliseconds since
* navigationStart (accurate to 1/1000 of a ms)
* @param end timestamp when reflow ended, in milliseconds since
* navigationStart (accurate to 1/1000 of a ms)
*/
void reflow(in DOMHighResTimeStamp start,
in DOMHighResTimeStamp end);
/**
* Called when an interruptible reflow has occurred.
*
* @param start timestamp when reflow ended, in milliseconds since
* navigationStart (accurate to 1/1000 of a ms)
* @param end timestamp when reflow ended, in milliseconds since
* navigationStart (accurate to 1/1000 of a ms)
*/
void reflowInterruptible(in DOMHighResTimeStamp start,
in DOMHighResTimeStamp end);
};

View File

@ -80,6 +80,7 @@ MOCHITEST_CHROME_FILES = \
test_bug449780.xul \
bug449780_window.xul \
bug454235-subframe.xul \
test_bug453650.xul \
test_bug456980.xul \
test_bug662200.xul \
bug662200_window.xul \

View File

@ -0,0 +1,116 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=453650
-->
<window title="Mozilla Bug 453650"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<title>Test for Bug 453650</title>
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<!-- test code goes here -->
<script type="application/javascript">
<![CDATA[
/** Test for Bug 453650 **/
SimpleTest.waitForExplicitFinish();
var Ci = Components.interfaces;
var Cr = Components.results;
var iter = runTests();
nextTest();
function runTests() {
var iframe = document.createElement("iframe");
iframe.style.width = "300px";
iframe.style.height = "300px";
iframe.setAttribute("src", "data:text/html,<h1 id='h'>hello</h1>");
document.documentElement.appendChild(iframe);
yield whenLoaded(iframe);
info("iframe loaded");
var h1 = iframe.contentDocument.getElementById("h");
h1.style.width = "400px";
yield waitForInterruptibleReflow(iframe.docShell);
h1.style.width = "300px";
waitForReflow(iframe.docShell);
yield is(300, h1.offsetWidth, "h1 has correct width");
}
function waitForInterruptibleReflow(docShell) {
waitForReflow(docShell, true);
}
function waitForReflow(docShell, interruptible = false) {
function done() {
docShell.removeWeakReflowObserver(observer);
SimpleTest.executeSoon(nextTest);
}
var observer = {
reflow: function (start, end) {
if (interruptible) {
ok(false, "expected interruptible reflow");
} else {
ok(true, "observed uninterruptible reflow");
}
info("times: " + start + ", " + end);
ok(start < end, "reflow start time lower than end time");
done();
},
reflowInterruptible: function (start, end) {
if (!interruptible) {
ok(false, "expected uninterruptible reflow");
} else {
ok(true, "observed interruptible reflow");
}
info("times: " + start + ", " + end);
ok(start < end, "reflow start time lower than end time");
done();
},
QueryInterface: function (iid) {
if (Ci.nsIReflowObserver.equals(iid) ||
Ci.nsISupportsWeakReference.equals(iid) ||
Ci.nsISupports.equals(iid))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
};
docShell.addWeakReflowObserver(observer);
}
function whenLoaded(iframe) {
iframe.addEventListener("load", function onLoad() {
iframe.removeEventListener("load", onLoad);
SimpleTest.executeSoon(nextTest);
});
}
function nextTest() {
try {
iter.next();
} catch (e if e instanceof StopIteration) {
SimpleTest.finish();
}
}
]]>
</script>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=453650"
target="_blank">Mozilla Bug 453650</a>
</body>
</window>

View File

@ -12,6 +12,7 @@ class nsWrapperCache;
[ptr] native nsWrapperCachePtr(nsWrapperCache);
typedef unsigned long long DOMTimeStamp;
typedef double DOMHighResTimeStamp;
// Core
interface nsIDOMAttr;

View File

@ -123,7 +123,9 @@
#include "nsSVGEffects.h"
#include "SVGFragmentIdentifier.h"
#include "nsPerformance.h"
#include "nsRefreshDriver.h"
#include "nsDOMNavigationTiming.h"
// Drag & Drop, Clipboard
#include "nsWidgetsCID.h"
@ -7651,6 +7653,8 @@ PresShell::WillDoReflow()
mPresContext->FlushUserFontSet();
mFrameConstructor->BeginUpdate();
mLastReflowStart = GetPerformanceNow();
}
void
@ -7659,6 +7663,16 @@ PresShell::DidDoReflow(bool aInterruptible, bool aWasInterrupted)
mFrameConstructor->EndUpdate();
HandlePostedReflowCallbacks(aInterruptible);
nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
if (container) {
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
if (docShell) {
DOMHighResTimeStamp now = GetPerformanceNow();
docShell->NotifyReflowObservers(aInterruptible, mLastReflowStart, now);
}
}
if (sSynthMouseMove) {
SynthesizeMouseMove(false);
}
@ -7674,6 +7688,23 @@ PresShell::DidDoReflow(bool aInterruptible, bool aWasInterrupted)
}
}
DOMHighResTimeStamp
PresShell::GetPerformanceNow()
{
DOMHighResTimeStamp now = 0;
nsPIDOMWindow* window = mDocument->GetInnerWindow();
if (window) {
nsPerformance* perf = window->GetPerformance();
if (perf) {
now = perf->Now();
}
}
return now;
}
static PLDHashOperator
MarkFramesDirtyToRoot(nsPtrHashKey<nsIFrame>* p, void* closure)
{

View File

@ -701,6 +701,8 @@ protected:
nscolor GetDefaultBackgroundColorToDraw();
DOMHighResTimeStamp GetPerformanceNow();
// The callback for the mPaintSuppressionTimer timer.
static void sPaintSuppressionCallback(nsITimer* aTimer, void* aPresShell);
@ -783,6 +785,9 @@ protected:
// moving/sizing loop is running, see bug 491700 for details.
nsCOMPtr<nsITimer> mReflowContinueTimer;
// The `performance.now()` value when we last started to process reflows.
DOMHighResTimeStamp mLastReflowStart;
// Information needed to properly handle scrolling content into view if the
// pre-scroll reflow flush can be interrupted. mContentToScrollTo is
// non-null between the initial scroll attempt and the first time we finish