mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Backed out 3 changesets (bug 1434376)for failing browser chrome at browser/base/content/test/performance/browser_urlbar_search_reflows.js on a CLOSED TREE
Backed out changeset b636251b75ab (bug 1434376) Backed out changeset fccbba9cb959 (bug 1434376) Backed out changeset b5128504011c (bug 1434376)
This commit is contained in:
parent
56da525c38
commit
1aac7df383
@ -8010,7 +8010,7 @@ var gIdentityHandler = {
|
||||
classes += " in-use";
|
||||
|
||||
// Synchronize control center and identity block blinking animations.
|
||||
window.promiseDocumentFlushed(() => {}).then(() => {
|
||||
BrowserUtils.promiseLayoutFlushed(document, "style", () => {
|
||||
let sharingIconBlink = document.getElementById("sharing-icon").getAnimations()[0];
|
||||
if (sharingIconBlink) {
|
||||
let startTime = sharingIconBlink.startTime;
|
||||
|
@ -283,7 +283,7 @@
|
||||
<parameter name="aTab"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
aTab.ownerGlobal.promiseDocumentFlushed(() => {
|
||||
BrowserUtils.promiseLayoutFlushed(aTab.ownerDocument, "style", () => {
|
||||
if (!aTab.parentNode) {
|
||||
return;
|
||||
}
|
||||
|
@ -82,8 +82,9 @@ async function withReflowObserver(testFn, expectedReflows = [], win = window) {
|
||||
},
|
||||
|
||||
reflowInterruptible(start, end) {
|
||||
// Interruptible reflows are the reflows caused by the refresh
|
||||
// driver ticking. These are fine.
|
||||
// We're not interested in interruptible reflows, but might as well take the
|
||||
// opportuntiy to dirty the root frame.
|
||||
dirtyFrameFn();
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
|
||||
|
@ -569,7 +569,11 @@ CustomizeMode.prototype = {
|
||||
}
|
||||
|
||||
// Wait until the next frame before setting the class to ensure
|
||||
// we do start the animation.
|
||||
// we do start the animation. We cannot use promiseLayoutFlushed
|
||||
// here because callback is only invoked when any actual reflow
|
||||
// happens, while that may not happen soonish enough. If we have
|
||||
// an observer for style flush, we may be able to replace the
|
||||
// nested rAFs with that.
|
||||
this.window.requestAnimationFrame(() => {
|
||||
this.window.requestAnimationFrame(() => {
|
||||
animationNode.classList.add("animate-out");
|
||||
@ -2417,8 +2421,7 @@ CustomizeMode.prototype = {
|
||||
panelOnTheLeft = true;
|
||||
}
|
||||
} else {
|
||||
await this.window.promiseDocumentFlushed(() => {});
|
||||
|
||||
await BrowserUtils.promiseLayoutFlushed(doc, "display", () => {});
|
||||
if (!this._customizing || !this._wantToBeInCustomizeMode) {
|
||||
return;
|
||||
}
|
||||
|
@ -495,8 +495,8 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
||||
// binding for the <panelmultiview> element may still be disconnected.
|
||||
// In this case, give the layout code a chance to run.
|
||||
if (!this.connected) {
|
||||
await this.window.promiseDocumentFlushed(() => {});
|
||||
|
||||
await BrowserUtils.promiseLayoutFlushed(this.document, "layout",
|
||||
() => {});
|
||||
// The XBL binding must be connected at this point. If this is not the
|
||||
// case, the calling code should be updated to unhide the panel.
|
||||
if (!this.connected) {
|
||||
@ -788,7 +788,7 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
||||
return;
|
||||
}
|
||||
|
||||
const { window } = this;
|
||||
const {window, document} = this;
|
||||
|
||||
let nextPanelView = PanelView.forNode(viewNode);
|
||||
let prevPanelView = PanelView.forNode(previousViewNode);
|
||||
@ -847,7 +847,7 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
||||
// description elements it contains.
|
||||
nextPanelView.descriptionHeightWorkaround();
|
||||
|
||||
viewRect = await window.promiseDocumentFlushed(() => {
|
||||
viewRect = await BrowserUtils.promiseLayoutFlushed(this.document, "layout", () => {
|
||||
return this._dwu.getBoundsWithoutFlushing(viewNode);
|
||||
});
|
||||
|
||||
@ -894,7 +894,7 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
||||
// sliding animation with smaller views.
|
||||
viewNode.style.width = viewRect.width + "px";
|
||||
|
||||
await window.promiseDocumentFlushed(() => {});
|
||||
await BrowserUtils.promiseLayoutFlushed(document, "layout", () => {});
|
||||
|
||||
// Kick off the transition!
|
||||
details.phase = TRANSITION_PHASES.TRANSITION;
|
||||
@ -1000,7 +1000,7 @@ var PanelMultiView = class extends this.AssociatedToNode {
|
||||
// We force 'display: none' on the previous view node to make sure that it
|
||||
// doesn't cause an annoying flicker whilst resetting the styles above.
|
||||
previousViewNode.style.display = "none";
|
||||
await this.window.promiseDocumentFlushed(() => {});
|
||||
await BrowserUtils.promiseLayoutFlushed(this.document, "layout", () => {});
|
||||
previousViewNode.style.removeProperty("display");
|
||||
}
|
||||
}
|
||||
|
@ -1608,8 +1608,10 @@ PlacesToolbar.prototype = {
|
||||
notify: function PT_notify(aTimer) {
|
||||
if (aTimer == this._updateNodesVisibilityTimer) {
|
||||
this._updateNodesVisibilityTimer = null;
|
||||
// Bug 1440070: This should use promiseDocumentFlushed, so that
|
||||
// _updateNodesVisibilityTimerCallback can use getBoundsWithoutFlush.
|
||||
// TODO: This should use promiseLayoutFlushed("layout"), so that
|
||||
// _updateNodesVisibilityTimerCallback can use getBoundsWithoutFlush. But
|
||||
// for yet unknown reasons doing that causes intermittent failures,
|
||||
// apparently due the flush not happening in a meaningful time.
|
||||
window.requestAnimationFrame(this._updateNodesVisibilityTimerCallback.bind(this));
|
||||
} else if (aTimer == this._ibTimer) {
|
||||
// * Timer to turn off indicator bar.
|
||||
|
@ -856,41 +856,6 @@ nsGlobalWindowInner::IsBackgroundInternal() const
|
||||
return !mOuterWindow || mOuterWindow->IsBackground();
|
||||
}
|
||||
|
||||
class PromiseDocumentFlushedResolver final {
|
||||
public:
|
||||
PromiseDocumentFlushedResolver(Promise* aPromise,
|
||||
PromiseDocumentFlushedCallback& aCallback)
|
||||
: mPromise(aPromise)
|
||||
, mCallback(&aCallback)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~PromiseDocumentFlushedResolver() = default;
|
||||
|
||||
void Call()
|
||||
{
|
||||
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
|
||||
|
||||
ErrorResult error;
|
||||
JS::Rooted<JS::Value> returnVal(RootingCx());
|
||||
mCallback->Call(&returnVal, error);
|
||||
|
||||
if (error.Failed()) {
|
||||
mPromise->MaybeReject(error);
|
||||
} else {
|
||||
mPromise->MaybeResolve(returnVal);
|
||||
}
|
||||
}
|
||||
|
||||
void Cancel()
|
||||
{
|
||||
mPromise->MaybeReject(NS_ERROR_ABORT);
|
||||
}
|
||||
|
||||
RefPtr<Promise> mPromise;
|
||||
RefPtr<PromiseDocumentFlushedCallback> mCallback;
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
//*** nsGlobalWindowInner: Object Management
|
||||
//*****************************************************************************
|
||||
@ -924,8 +889,6 @@ nsGlobalWindowInner::nsGlobalWindowInner(nsGlobalWindowOuter *aOuterWindow)
|
||||
mCleanedUp(false),
|
||||
mDialogAbuseCount(0),
|
||||
mAreDialogsEnabled(true),
|
||||
mObservingDidRefresh(false),
|
||||
mIteratingDocumentFlushedResolvers(false),
|
||||
mCanSkipCCGeneration(0),
|
||||
mBeforeUnloadListenerCount(0)
|
||||
{
|
||||
@ -1330,13 +1293,6 @@ nsGlobalWindowInner::FreeInnerObjects()
|
||||
while (mDoc->EventHandlingSuppressed()) {
|
||||
mDoc->UnsuppressEventHandlingAndFireEvents(false);
|
||||
}
|
||||
|
||||
if (mObservingDidRefresh) {
|
||||
nsIPresShell* shell = mDoc->GetShell();
|
||||
if (shell) {
|
||||
Unused << shell->RemovePostRefreshObserver(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove our reference to the document and the document principal.
|
||||
@ -1378,11 +1334,6 @@ nsGlobalWindowInner::FreeInnerObjects()
|
||||
}
|
||||
mBeforeUnloadListenerCount = 0;
|
||||
}
|
||||
|
||||
// If we have any promiseDocumentFlushed callbacks, fire them now so
|
||||
// that the Promises can resolve.
|
||||
CallDocumentFlushedResolvers();
|
||||
mObservingDidRefresh = false;
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
@ -1538,12 +1489,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPromises)
|
||||
|
||||
for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mPromise);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentFlushedResolvers[i]->mCallback);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
|
||||
@ -1638,11 +1583,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPromises)
|
||||
for (size_t i = 0; i < tmp->mDocumentFlushedResolvers.Length(); i++) {
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mPromise);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentFlushedResolvers[i]->mCallback);
|
||||
}
|
||||
tmp->mDocumentFlushedResolvers.Clear();
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
@ -7383,125 +7323,6 @@ nsGlobalWindowInner::BeginWindowMove(Event& aMouseDownEvent, Element* aPanel,
|
||||
aError = widget->BeginMoveDrag(mouseEvent);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
nsGlobalWindowInner::PromiseDocumentFlushed(PromiseDocumentFlushedCallback& aCallback,
|
||||
ErrorResult& aError)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(IsChromeWindow());
|
||||
|
||||
if (!IsCurrentInnerWindow()) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mIteratingDocumentFlushedResolvers) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mDoc) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIPresShell* shell = mDoc->GetShell();
|
||||
if (!shell) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We need to associate the lifetime of the Promise to the lifetime
|
||||
// of the caller's global. That way, if the window we're observing
|
||||
// refresh driver ticks on goes away before our observer is fired,
|
||||
// we can still resolve the Promise.
|
||||
nsIGlobalObject* global = GetIncumbentGlobal();
|
||||
if (!global) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Promise> resultPromise = Promise::Create(global, aError);
|
||||
if (aError.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UniquePtr<PromiseDocumentFlushedResolver> flushResolver(
|
||||
new PromiseDocumentFlushedResolver(resultPromise, aCallback));
|
||||
|
||||
if (!shell->NeedFlush(FlushType::Style)) {
|
||||
flushResolver->Call();
|
||||
return resultPromise.forget();
|
||||
}
|
||||
|
||||
if (!mObservingDidRefresh) {
|
||||
bool success = shell->AddPostRefreshObserver(this);
|
||||
if (!success) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
mObservingDidRefresh = true;
|
||||
}
|
||||
|
||||
mDocumentFlushedResolvers.AppendElement(Move(flushResolver));
|
||||
return resultPromise.forget();
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindowInner::CallDocumentFlushedResolvers()
|
||||
{
|
||||
MOZ_ASSERT(!mIteratingDocumentFlushedResolvers);
|
||||
mIteratingDocumentFlushedResolvers = true;
|
||||
for (const auto& documentFlushedResolver : mDocumentFlushedResolvers) {
|
||||
documentFlushedResolver->Call();
|
||||
}
|
||||
mDocumentFlushedResolvers.Clear();
|
||||
mIteratingDocumentFlushedResolvers = false;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindowInner::CancelDocumentFlushedResolvers()
|
||||
{
|
||||
MOZ_ASSERT(!mIteratingDocumentFlushedResolvers);
|
||||
mIteratingDocumentFlushedResolvers = true;
|
||||
for (const auto& documentFlushedResolver : mDocumentFlushedResolvers) {
|
||||
documentFlushedResolver->Cancel();
|
||||
}
|
||||
mDocumentFlushedResolvers.Clear();
|
||||
mIteratingDocumentFlushedResolvers = false;
|
||||
}
|
||||
|
||||
void
|
||||
nsGlobalWindowInner::DidRefresh()
|
||||
{
|
||||
auto rejectionGuard = MakeScopeExit([&] {
|
||||
CancelDocumentFlushedResolvers();
|
||||
mObservingDidRefresh = false;
|
||||
});
|
||||
|
||||
MOZ_ASSERT(mDoc);
|
||||
|
||||
nsIPresShell* shell = mDoc->GetShell();
|
||||
MOZ_ASSERT(shell);
|
||||
|
||||
if (shell->NeedStyleFlush() || shell->HasPendingReflow()) {
|
||||
// By the time our observer fired, something has already invalidated
|
||||
// style and maybe layout. We'll wait until the next refresh driver
|
||||
// tick instead.
|
||||
rejectionGuard.release();
|
||||
return;
|
||||
}
|
||||
|
||||
bool success = shell->RemovePostRefreshObserver(this);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
rejectionGuard.release();
|
||||
|
||||
CallDocumentFlushedResolvers();
|
||||
mObservingDidRefresh = false;
|
||||
}
|
||||
|
||||
already_AddRefed<nsWindowRoot>
|
||||
nsGlobalWindowInner::GetWindowRoot(mozilla::ErrorResult& aError)
|
||||
{
|
||||
|
@ -57,7 +57,6 @@
|
||||
#include "nsCheapSets.h"
|
||||
#include "mozilla/dom/ImageBitmapSource.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
class nsIArray;
|
||||
class nsIBaseWindow;
|
||||
@ -90,8 +89,6 @@ class IdleRequestExecutor;
|
||||
|
||||
class DialogValueHolder;
|
||||
|
||||
class PromiseDocumentFlushedResolver;
|
||||
|
||||
namespace mozilla {
|
||||
class AbstractThread;
|
||||
class ThrottledEventQueue;
|
||||
@ -214,8 +211,7 @@ class nsGlobalWindowInner : public mozilla::dom::EventTarget,
|
||||
public nsIScriptObjectPrincipal,
|
||||
public nsSupportsWeakReference,
|
||||
public nsIInterfaceRequestor,
|
||||
public PRCListStr,
|
||||
public nsAPostRefreshObserver
|
||||
public PRCListStr
|
||||
{
|
||||
public:
|
||||
typedef mozilla::TimeStamp TimeStamp;
|
||||
@ -948,12 +944,6 @@ public:
|
||||
mozilla::dom::Element* aPanel,
|
||||
mozilla::ErrorResult& aError);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise>
|
||||
PromiseDocumentFlushed(mozilla::dom::PromiseDocumentFlushedCallback& aCallback,
|
||||
mozilla::ErrorResult& aError);
|
||||
|
||||
void DidRefresh() override;
|
||||
|
||||
void GetDialogArgumentsOuter(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
|
||||
nsIPrincipal& aSubjectPrincipal,
|
||||
mozilla::ErrorResult& aError);
|
||||
@ -1279,9 +1269,6 @@ private:
|
||||
mChromeFields.mGroupMessageManagers.Clear();
|
||||
}
|
||||
|
||||
void CallDocumentFlushedResolvers();
|
||||
void CancelDocumentFlushedResolvers();
|
||||
|
||||
public:
|
||||
// Dispatch a runnable related to the global.
|
||||
virtual nsresult Dispatch(mozilla::TaskCategory aCategory,
|
||||
@ -1428,14 +1415,6 @@ protected:
|
||||
// currently enabled on this window.
|
||||
bool mAreDialogsEnabled;
|
||||
|
||||
// This flag keeps track of whether this window is currently
|
||||
// observing DidRefresh notifications from the refresh driver.
|
||||
bool mObservingDidRefresh;
|
||||
// This flag keeps track of whether or not we're going through
|
||||
// promiseDocumentFlushed resolvers. When true, promiseDocumentFlushed
|
||||
// cannot be called.
|
||||
bool mIteratingDocumentFlushedResolvers;
|
||||
|
||||
nsTArray<uint32_t> mEnabledSensors;
|
||||
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
@ -1462,8 +1441,6 @@ protected:
|
||||
|
||||
nsTArray<RefPtr<mozilla::dom::Promise>> mPendingPromises;
|
||||
|
||||
nsTArray<mozilla::UniquePtr<PromiseDocumentFlushedResolver>> mDocumentFlushedResolvers;
|
||||
|
||||
static InnerWindowByIdTable* sInnerWindowsById;
|
||||
|
||||
// Members in the mChromeFields member should only be used in chrome windows.
|
||||
|
@ -48,7 +48,6 @@ skip-if = !e10s # this only makes sense with e10s-multi
|
||||
[browser_messagemanager_unload.js]
|
||||
[browser_pagehide_on_tab_close.js]
|
||||
skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s.
|
||||
[browser_promiseDocumentFlushed.js]
|
||||
[browser_state_notifications.js]
|
||||
skip-if = true # Bug 1271028
|
||||
[browser_use_counters.js]
|
||||
|
@ -1,246 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Dirties style and layout on the current browser window.
|
||||
*
|
||||
* @param {Number} Optional factor by which to modify the DOM. Useful for
|
||||
* when multiple calls to dirtyTheDOM may occur, and you need them
|
||||
* to dirty the DOM differently from one another. If you only need
|
||||
* to dirty the DOM once, this can be omitted.
|
||||
*/
|
||||
function dirtyStyleAndLayout(factor = 1) {
|
||||
gNavToolbox.style.padding = factor + "px";
|
||||
}
|
||||
|
||||
/**
|
||||
* Dirties style of the current browser window, but NOT layout.
|
||||
*/
|
||||
function dirtyStyle() {
|
||||
gNavToolbox.style.color = "red";
|
||||
}
|
||||
|
||||
const gWindowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
/**
|
||||
* Asserts that no style or layout flushes are required by the
|
||||
* current window.
|
||||
*/
|
||||
function assertNoFlushesRequired() {
|
||||
Assert.ok(!gWindowUtils.needsFlush(Ci.nsIDOMWindowUtils.FLUSH_STYLE),
|
||||
"No flushes are required.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the DOM has been dirtied, and so style and layout flushes
|
||||
* are required.
|
||||
*/
|
||||
function assertFlushesRequired() {
|
||||
Assert.ok(gWindowUtils.needsFlush(Ci.nsIDOMWindowUtils.FLUSH_LAYOUT),
|
||||
"Style and layout flushes are required.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes style changes from dirtyTheDOM() from the browser window,
|
||||
* and resolves once the refresh driver ticks.
|
||||
*/
|
||||
async function cleanTheDOM() {
|
||||
gNavToolbox.style.padding = "";
|
||||
gNavToolbox.style.color = "";
|
||||
await window.promiseDocumentFlushed(() => {});
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
registerCleanupFunction(cleanTheDOM);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that if the DOM is dirty, that promiseDocumentFlushed will
|
||||
* resolve once layout and style have been flushed.
|
||||
*/
|
||||
add_task(async function test_basic() {
|
||||
dirtyStyleAndLayout();
|
||||
assertFlushesRequired();
|
||||
|
||||
await window.promiseDocumentFlushed(() => {});
|
||||
assertNoFlushesRequired();
|
||||
|
||||
dirtyStyle();
|
||||
assertFlushesRequired();
|
||||
|
||||
await window.promiseDocumentFlushed(() => {});
|
||||
assertNoFlushesRequired();
|
||||
|
||||
// The DOM should be clean already, but we'll do this anyway to isolate
|
||||
// failures from other tests.
|
||||
await cleanTheDOM();
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that values returned by the callback passed to promiseDocumentFlushed
|
||||
* get passed down through the Promise resolution.
|
||||
*/
|
||||
add_task(async function test_can_get_results_from_callback() {
|
||||
const NEW_PADDING = "2px";
|
||||
|
||||
gNavToolbox.style.padding = NEW_PADDING;
|
||||
|
||||
assertFlushesRequired();
|
||||
|
||||
let paddings = await window.promiseDocumentFlushed(() => {
|
||||
let style = window.getComputedStyle(gNavToolbox);
|
||||
return {
|
||||
left: style.paddingLeft,
|
||||
right: style.paddingRight,
|
||||
top: style.paddingTop,
|
||||
bottom: style.paddingBottom,
|
||||
};
|
||||
});
|
||||
|
||||
for (let prop in paddings) {
|
||||
Assert.equal(paddings[prop], NEW_PADDING,
|
||||
"Got expected padding");
|
||||
}
|
||||
|
||||
await cleanTheDOM();
|
||||
|
||||
gNavToolbox.style.padding = NEW_PADDING;
|
||||
|
||||
assertFlushesRequired();
|
||||
|
||||
let rect = await window.promiseDocumentFlushed(() => {
|
||||
let observer = {
|
||||
reflow() {
|
||||
Assert.ok(false, "A reflow should not have occurred.");
|
||||
},
|
||||
reflowInterruptible() {},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
|
||||
Ci.nsISupportsWeakReference])
|
||||
};
|
||||
|
||||
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
docShell.addWeakReflowObserver(observer);
|
||||
|
||||
let toolboxRect = gNavToolbox.getBoundingClientRect();
|
||||
|
||||
docShell.removeWeakReflowObserver(observer);
|
||||
return toolboxRect;
|
||||
});
|
||||
|
||||
// The actual values of this rect aren't super important for
|
||||
// the purposes of this test - we just want to know that a valid
|
||||
// rect was returned, so checking for properties being greater than
|
||||
// 0 is sufficient.
|
||||
for (let property of ["width", "height"]) {
|
||||
Assert.ok(rect[property] > 0, `Rect property ${property} > 0 (${rect[property]})`);
|
||||
}
|
||||
|
||||
await cleanTheDOM();
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that if promiseDocumentFlushed is requested on a window
|
||||
* that closes before it gets a chance to do a refresh driver
|
||||
* tick, the promiseDocumentFlushed Promise is still resolved, and
|
||||
* the callback is still called.
|
||||
*/
|
||||
add_task(async function test_resolved_in_window_close() {
|
||||
let win = await BrowserTestUtils.openNewBrowserWindow();
|
||||
|
||||
await win.promiseDocumentFlushed(() => {});
|
||||
|
||||
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell);
|
||||
docShell.contentViewer.pausePainting();
|
||||
|
||||
win.gNavToolbox.style.padding = "5px";
|
||||
|
||||
const EXPECTED = 1234;
|
||||
let promise = win.promiseDocumentFlushed(() => {
|
||||
// Despite the window not painting before closing, this
|
||||
// callback should be fired when the window gets torn
|
||||
// down and should stil be able to return a result.
|
||||
return EXPECTED;
|
||||
});
|
||||
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
Assert.equal(await promise, EXPECTED);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that re-entering promiseDocumentFlushed is not possible
|
||||
* from within a promiseDocumentFlushed callback. Doing so will
|
||||
* result in the outer Promise rejecting with NS_ERROR_FAILURE.
|
||||
*/
|
||||
add_task(async function test_reentrancy() {
|
||||
dirtyStyleAndLayout();
|
||||
assertFlushesRequired();
|
||||
|
||||
let promise = window.promiseDocumentFlushed(() => {
|
||||
return window.promiseDocumentFlushed(() => {
|
||||
Assert.ok(false, "Should never run this.");
|
||||
});
|
||||
});
|
||||
|
||||
await Assert.rejects(promise, ex => ex.result == Cr.NS_ERROR_FAILURE);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests the expected execution order of a series of promiseDocumentFlushed
|
||||
* calls, their callbacks, and the resolutions of their Promises.
|
||||
*
|
||||
* When multiple promiseDocumentFlushed callbacks are queued, the callbacks
|
||||
* should always been run first before any of the Promises are resolved.
|
||||
*
|
||||
* The callbacks should run in the order that they were queued in via
|
||||
* promiseDocumentFlushed. The Promise resolutions should similarly run
|
||||
* in the order that promiseDocumentFlushed was called in.
|
||||
*/
|
||||
add_task(async function test_execution_order() {
|
||||
let result = [];
|
||||
|
||||
dirtyStyleAndLayout(1);
|
||||
let promise1 = window.promiseDocumentFlushed(() => {
|
||||
result.push(0);
|
||||
}).then(() => {
|
||||
result.push(2);
|
||||
});
|
||||
|
||||
let promise2 = window.promiseDocumentFlushed(() => {
|
||||
result.push(1);
|
||||
}).then(() => {
|
||||
result.push(3);
|
||||
});
|
||||
|
||||
await Promise.all([promise1, promise2]);
|
||||
|
||||
Assert.equal(result.length, 4,
|
||||
"Should have run all callbacks and Promises.");
|
||||
|
||||
let promise3 = window.promiseDocumentFlushed(() => {
|
||||
result.push(4);
|
||||
}).then(() => {
|
||||
result.push(6);
|
||||
});
|
||||
|
||||
let promise4 = window.promiseDocumentFlushed(() => {
|
||||
result.push(5);
|
||||
}).then(() => {
|
||||
result.push(7);
|
||||
});
|
||||
|
||||
await Promise.all([promise3, promise4]);
|
||||
|
||||
Assert.equal(result.length, 8,
|
||||
"Should have run all callbacks and Promises.");
|
||||
|
||||
for (let i = 0; i < result.length; ++i) {
|
||||
Assert.equal(result[i], i,
|
||||
"Callbacks and Promises should have run in the expected order.");
|
||||
}
|
||||
});
|
@ -372,8 +372,6 @@ partial interface Window {
|
||||
};
|
||||
#endif
|
||||
|
||||
callback PromiseDocumentFlushedCallback = any ();
|
||||
|
||||
// Mozilla extensions for Chrome windows.
|
||||
partial interface Window {
|
||||
// The STATE_* constants need to match the corresponding enum in nsGlobalWindow.cpp.
|
||||
@ -446,47 +444,6 @@ partial interface Window {
|
||||
[Throws, Func="nsGlobalWindowInner::IsPrivilegedChromeWindow"]
|
||||
void beginWindowMove(Event mouseDownEvent, optional Element? panel = null);
|
||||
|
||||
/**
|
||||
* Calls the given function as soon as a style or layout flush for the
|
||||
* top-level document is not necessary, and returns a Promise which
|
||||
* resolves to the callback's return value after it executes.
|
||||
*
|
||||
* In the event that the window goes away before a flush can occur, the
|
||||
* callback will still be called and the Promise resolved as the window
|
||||
* tears itself down.
|
||||
*
|
||||
* Note that the callback can be called either synchronously or asynchronously
|
||||
* depending on whether or not flushes are pending:
|
||||
*
|
||||
* The callback will be called synchronously when calling
|
||||
* promiseDocumentFlushed when NO flushes are already pending. This is
|
||||
* to ensure that no script has a chance to dirty the DOM before the callback
|
||||
* is called.
|
||||
*
|
||||
* The callback will be called asynchronously if a flush is pending.
|
||||
*
|
||||
* The expected execution order is that all pending callbacks will
|
||||
* be fired first (and in the order that they were queued) and then the
|
||||
* Promise resolution handlers will all be invoked later on during the
|
||||
* next microtask checkpoint.
|
||||
*
|
||||
* promiseDocumentFlushed does not support re-entrancy - so calling it from
|
||||
* within a promiseDocumentFlushed callback will result in the inner call
|
||||
* throwing an NS_ERROR_FAILURE exception, and the outer Promise rejecting
|
||||
* with that exception.
|
||||
*
|
||||
* The callback function *must not make any changes which would require
|
||||
* a style or layout flush*.
|
||||
*
|
||||
* Also throws NS_ERROR_FAILURE if the window is not in a state where flushes
|
||||
* can be waited for (for example, the PresShell has not yet been created).
|
||||
*
|
||||
* @param {function} callback
|
||||
* @returns {Promise}
|
||||
*/
|
||||
[Throws, Func="nsGlobalWindowInner::IsPrivilegedChromeWindow"]
|
||||
Promise<any> promiseDocumentFlushed(PromiseDocumentFlushedCallback callback);
|
||||
|
||||
[Func="IsChromeOrXBL"]
|
||||
readonly attribute boolean isChromeWindow;
|
||||
};
|
||||
|
@ -2330,13 +2330,13 @@ public:
|
||||
mFillRule = aFillRule;
|
||||
}
|
||||
|
||||
mozilla::Position& GetPosition() {
|
||||
Position& GetPosition() {
|
||||
MOZ_ASSERT(mType == StyleBasicShapeType::Circle ||
|
||||
mType == StyleBasicShapeType::Ellipse,
|
||||
"expected circle or ellipse");
|
||||
return mPosition;
|
||||
}
|
||||
const mozilla::Position& GetPosition() const {
|
||||
const Position& GetPosition() const {
|
||||
MOZ_ASSERT(mType == StyleBasicShapeType::Circle ||
|
||||
mType == StyleBasicShapeType::Ellipse,
|
||||
"expected circle or ellipse");
|
||||
@ -2396,7 +2396,7 @@ private:
|
||||
// (top, right, bottom, left) for inset
|
||||
nsTArray<nsStyleCoord> mCoordinates;
|
||||
// position of center for ellipse or circle
|
||||
mozilla::Position mPosition;
|
||||
Position mPosition;
|
||||
// corner radii for inset (0 if not set)
|
||||
nsStyleCorners mRadius;
|
||||
};
|
||||
|
@ -14,6 +14,48 @@ ChromeUtils.defineModuleGetter(this, "PlacesUtils",
|
||||
|
||||
Cu.importGlobalProperties(["URL"]);
|
||||
|
||||
let reflowObservers = new WeakMap();
|
||||
|
||||
function ReflowObserver(doc) {
|
||||
this._doc = doc;
|
||||
|
||||
doc.docShell.addWeakReflowObserver(this);
|
||||
reflowObservers.set(this._doc, this);
|
||||
|
||||
this.callbacks = [];
|
||||
}
|
||||
|
||||
ReflowObserver.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI(["nsIReflowObserver", "nsISupportsWeakReference"]),
|
||||
|
||||
_onReflow() {
|
||||
reflowObservers.delete(this._doc);
|
||||
this._doc.docShell.removeWeakReflowObserver(this);
|
||||
|
||||
for (let callback of this.callbacks) {
|
||||
try {
|
||||
callback();
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
reflow() {
|
||||
this._onReflow();
|
||||
},
|
||||
|
||||
reflowInterruptible() {
|
||||
this._onReflow();
|
||||
},
|
||||
};
|
||||
|
||||
const FLUSH_TYPES = {
|
||||
"style": Ci.nsIDOMWindowUtils.FLUSH_STYLE,
|
||||
"layout": Ci.nsIDOMWindowUtils.FLUSH_LAYOUT,
|
||||
"display": Ci.nsIDOMWindowUtils.FLUSH_DISPLAY,
|
||||
};
|
||||
|
||||
var BrowserUtils = {
|
||||
|
||||
/**
|
||||
@ -386,7 +428,8 @@ var BrowserUtils = {
|
||||
}
|
||||
let bounds = dwu.getBoundsWithoutFlushing(toolbarItem);
|
||||
if (!bounds.height) {
|
||||
await window.promiseDocumentFlushed(() => {
|
||||
let document = element.ownerDocument;
|
||||
await BrowserUtils.promiseLayoutFlushed(document, "layout", () => {
|
||||
bounds = dwu.getBoundsWithoutFlushing(toolbarItem);
|
||||
});
|
||||
}
|
||||
@ -644,6 +687,65 @@ var BrowserUtils = {
|
||||
return [url, postData];
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls the given function when the given document has just reflowed,
|
||||
* and returns a promise which resolves to its return value after it
|
||||
* has been called.
|
||||
*
|
||||
* The function *must not trigger any reflows*, or make any changes
|
||||
* which would require a layout flush.
|
||||
*
|
||||
* @param {Document} doc
|
||||
* @param {function} callback
|
||||
* @returns {Promise}
|
||||
*/
|
||||
promiseReflowed(doc, callback) {
|
||||
let observer = reflowObservers.get(doc);
|
||||
if (!observer) {
|
||||
observer = new ReflowObserver(doc);
|
||||
reflowObservers.set(doc, observer);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
observer.callbacks.push(() => {
|
||||
try {
|
||||
resolve(callback());
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls the given function as soon as a layout flush of the given
|
||||
* type is not necessary, and returns a promise which resolves to the
|
||||
* callback's return value after it executes.
|
||||
*
|
||||
* The function *must not trigger any reflows*, or make any changes
|
||||
* which would require a layout flush.
|
||||
*
|
||||
* @param {Document} doc
|
||||
* @param {string} flushType
|
||||
* The flush type required. Must be one of:
|
||||
*
|
||||
* - "style"
|
||||
* - "layout"
|
||||
* - "display"
|
||||
* @param {function} callback
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async promiseLayoutFlushed(doc, flushType, callback) {
|
||||
let utils = doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
if (!utils.needsFlush(FLUSH_TYPES[flushType])) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
return this.promiseReflowed(doc, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate a document fragment for a localized string that has DOM
|
||||
* node replacements. This avoids using getFormattedString followed
|
||||
|
@ -10,12 +10,6 @@
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
|
||||
// GetTickCount().
|
||||
#ifdef GetCurrentTime
|
||||
#undef GetCurrentTime
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Utility class that converts time values represented as an unsigned integral
|
||||
|
Loading…
Reference in New Issue
Block a user