This commit is contained in:
Ryan VanderMeulen 2017-12-10 16:06:08 -05:00
Родитель b592b428c8 e20ec3b2a6
Коммит ee4484bd10
17 изменённых файлов: 129 добавлений и 136 удалений

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

@ -7,6 +7,10 @@
const ORIGIN = "https://example.com";
const PERMISSIONS_PAGE = getRootDirectory(gTestPath).replace("chrome://mochitests/content", ORIGIN) + "permissions.html";
// Ignore promise rejection caused by clicking Deny button.
const { PromiseTestUtils } = Cu.import("resource://testing-common/PromiseTestUtils.jsm", {});
PromiseTestUtils.whitelistRejectionsGlobally(/The request is not allowed/);
const EXPIRE_TIME_MS = 100;
const TIMEOUT_MS = 500;

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

@ -54,7 +54,7 @@ function promiseWindow(url) {
}
Services.obs.removeObserver(obs, "domwindowopened");
resolve(win);
executeSoon(() => resolve(win));
}, {once: true});
}, "domwindowopened");
});
@ -120,7 +120,7 @@ async function assertWebRTCIndicatorStatus(expected) {
win.addEventListener("unload", function listener(e) {
if (e.target == win.document) {
win.removeEventListener("unload", listener);
resolve();
executeSoon(resolve);
}
});
});
@ -141,7 +141,7 @@ async function assertWebRTCIndicatorStatus(expected) {
if (document.readyState != "complete")
return;
document.removeEventListener("readystatechange", onReadyStateChange);
resolve();
executeSoon(resolve);
});
});
}
@ -166,7 +166,7 @@ function promisePopupEvent(popup, eventSuffix) {
let eventType = "popup" + eventSuffix;
return new Promise(resolve => {
popup.addEventListener(eventType, function(event) {
resolve();
executeSoon(resolve);
}, {once: true});
});
@ -297,7 +297,7 @@ function promisePopupNotificationShown(aName, aAction) {
ok(PopupNotifications.isPanelOpen, "notification panel open");
ok(!!PopupNotifications.panel.firstChild, "notification panel populated");
resolve();
executeSoon(resolve);
}, {once: true});
if (aAction)

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

@ -30,18 +30,25 @@ add_task(async function testAddOnBeforeCreatedWidget() {
let viewNode = document.getElementById(kWidgetId + "idontexistyet");
ok(widgetNode, "Widget should exist");
ok(viewNode, "Panelview should exist");
let widgetPanel;
let panelShownPromise;
let viewShownPromise = new Promise(resolve => {
viewNode.addEventListener("ViewShown", () => {
widgetPanel = document.getElementById("customizationui-widget-panel");
ok(widgetPanel, "Widget panel should exist");
// Add the popupshown event listener directly inside the ViewShown event
// listener to avoid missing the event.
panelShownPromise = promisePanelElementShown(window, widgetPanel);
resolve();
}, { once: true });
});
widgetNode.click();
await viewShownPromise;
await panelShownPromise;
let tempPanel = document.getElementById("customizationui-widget-panel");
let panelShownPromise = promisePanelElementShown(window, tempPanel);
await Promise.all([
BrowserTestUtils.waitForEvent(viewNode, "ViewShown"),
panelShownPromise
]);
let panelHiddenPromise = promisePanelElementHidden(window, tempPanel);
tempPanel.hidePopup();
let panelHiddenPromise = promisePanelElementHidden(window, widgetPanel);
widgetPanel.hidePopup();
await panelHiddenPromise;
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);

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

@ -25,9 +25,11 @@ async function testModeMenuitem(mode, modePref) {
is(normalItem.getAttribute("active"), "true",
"Normal mode menuitem should be active by default");
// Hover over the mode menuitem and wait until the UI density is updated.
// Hover over the mode menuitem and wait for the event that updates the UI
// density.
let mouseoverPromise = BrowserTestUtils.waitForEvent(item, "mouseover");
EventUtils.synthesizeMouseAtCenter(item, { type: "mouseover" });
await BrowserTestUtils.waitForAttribute("uidensity", win, mode);
await mouseoverPromise;
is(win.getAttribute("uidensity"), mode,
`UI Density should be set to ${mode} on ${mode} menuitem hover`);

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

@ -361,7 +361,7 @@ add_task(async function test_close_tab_after_crash() {
// Crash the tab
await BrowserTestUtils.crashBrowser(browser);
let promise = promiseEvent(gBrowser.tabContainer, "TabClose");
let promise = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabClose");
// Click the close tab button
clickButton(browser, "closeTab");

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

@ -471,20 +471,12 @@ function promiseDelayedStartupFinished(aWindow) {
return new Promise(resolve => whenDelayedStartupFinished(aWindow, resolve));
}
function promiseEvent(element, eventType, isCapturing = false) {
return new Promise(resolve => {
element.addEventListener(eventType, function(event) {
resolve(event);
}, {capture: isCapturing, once: true});
});
}
function promiseTabRestored(tab) {
return promiseEvent(tab, "SSTabRestored");
return BrowserTestUtils.waitForEvent(tab, "SSTabRestored");
}
function promiseTabRestoring(tab) {
return promiseEvent(tab, "SSTabRestoring");
return BrowserTestUtils.waitForEvent(tab, "SSTabRestoring");
}
function sendMessage(browser, name, data = {}) {

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

@ -75,7 +75,7 @@ function openPopup(aPopup) {
return new Promise(resolve => {
aPopup.addEventListener("popupshown", function() {
resolve();
TestUtils.executeSoon(resolve);
}, {once: true});
aPopup.focus();
@ -90,7 +90,7 @@ function waitForWindowLoad(aWin) {
return new Promise(resolve => {
aWin.addEventListener("load", function() {
resolve();
TestUtils.executeSoon(resolve);
}, {capture: true, once: true});
});

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

@ -48,6 +48,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMIntersectionObserver)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -55,6 +56,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMIntersectionObserver)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEntries)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@ -80,10 +82,7 @@ DOMIntersectionObserver::Constructor(const mozilla::dom::GlobalObject& aGlobal,
RefPtr<DOMIntersectionObserver> observer =
new DOMIntersectionObserver(window.forget(), aCb);
if (aOptions.mRoot) {
observer->mRoot = aOptions.mRoot;
observer->mRoot->RegisterIntersectionObserver(observer);
}
observer->mRoot = aOptions.mRoot;
if (!observer->SetRootMargin(aOptions.mRootMargin)) {
aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR,
@ -182,13 +181,8 @@ DOMIntersectionObserver::Unobserve(Element& aTarget)
}
void
DOMIntersectionObserver::UnlinkElement(Element& aTarget)
DOMIntersectionObserver::UnlinkTarget(Element& aTarget)
{
if (mRoot && mRoot == &aTarget) {
mRoot = nullptr;
Disconnect();
return;
}
mObservationTargets.RemoveElement(&aTarget);
if (mObservationTargets.Length() == 0) {
Disconnect();

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

@ -116,11 +116,7 @@ class DOMIntersectionObserver final : public nsISupports,
public:
DOMIntersectionObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner,
mozilla::dom::IntersectionCallback& aCb)
: mOwner(aOwner),
mDocument(mOwner->GetExtantDoc()),
mCallback(&aCb),
mRoot(nullptr),
mConnected(false)
: mOwner(aOwner), mDocument(mOwner->GetExtantDoc()), mCallback(&aCb), mConnected(false)
{
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@ -156,7 +152,7 @@ public:
void Observe(Element& aTarget);
void Unobserve(Element& aTarget);
void UnlinkElement(Element& aTarget);
void UnlinkTarget(Element& aTarget);
void Disconnect();
void TakeRecords(nsTArray<RefPtr<DOMIntersectionObserverEntry>>& aRetVal);
@ -180,12 +176,11 @@ protected:
nsCOMPtr<nsPIDOMWindowInner> mOwner;
RefPtr<nsIDocument> mDocument;
RefPtr<mozilla::dom::IntersectionCallback> mCallback;
// Raw pointer which is explicitly cleared by UnlinkElement().
Element* mRoot;
RefPtr<Element> mRoot;
nsCSSRect mRootMargin;
nsTArray<double> mThresholds;
// Holds raw pointers which are explicitly cleared by UnlinkElement().
// Holds raw pointers which are explicitly cleared by UnlinkTarget().
nsTArray<Element*> mObservationTargets;
nsTArray<RefPtr<DOMIntersectionObserverEntry>> mQueuedEntries;

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

@ -4332,7 +4332,7 @@ IntersectionObserverPropertyDtor(void* aObject, nsAtom* aPropertyName,
static_cast<IntersectionObserverList*>(aPropertyValue);
for (auto iter = observers->Iter(); !iter.Done(); iter.Next()) {
DOMIntersectionObserver* observer = iter.Key();
observer->UnlinkElement(*element);
observer->UnlinkTarget(*element);
}
delete observers;
}
@ -4354,7 +4354,7 @@ Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver)
}
observers->LookupForAdd(aObserver).OrInsert([]() {
// If element is being observed, value can be:
// Value can be:
// -2: Makes sure next calculated threshold always differs, leading to a
// notification task being scheduled.
// -1: Non-intersecting.
@ -4387,7 +4387,7 @@ Element::UnlinkIntersectionObservers()
}
for (auto iter = observers->Iter(); !iter.Done(); iter.Next()) {
DOMIntersectionObserver* observer = iter.Key();
observer->UnlinkElement(*this);
observer->UnlinkTarget(*this);
}
observers->Clear();
}

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

@ -610,7 +610,6 @@ skip-if = toolkit == 'android'
[test_bug1375050.html]
[test_bug1381710.html]
[test_bug1384661.html]
[test_bug1399603.html]
[test_bug1399605.html]
[test_bug1404385.html]
[test_bug1406102.html]

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

@ -1,63 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Test for Bug 1399603</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1399603">Mozilla Bug 1399603</a>
<p id="display"></p>
<div id="content">
<div id="root">
<div id="target"></div>
</div>
</div>
<pre id="test">
<script type="application/javascript">
function waitForNotification(f) {
requestAnimationFrame(function() {
setTimeout(function() { setTimeout(f); });
});
}
function forceGC() {
SpecialPowers.gc();
SpecialPowers.forceShrinkingGC();
SpecialPowers.forceCC();
SpecialPowers.gc();
SpecialPowers.forceShrinkingGC();
SpecialPowers.forceCC();
}
let content = document.getElementById('content');
let root = document.getElementById('root');
let target = document.getElementById('target');
let entries = [];
let observer = new IntersectionObserver(function(changes) {
entries = entries.concat(changes);
}, { root: root });
observer.observe(target);
waitForNotification(function () {
is(entries.length, 1, "Initial notification.");
root.removeChild(target);
content.removeChild(root);
root = null;
forceGC();
waitForNotification(function () {
is(entries.length, 1, "No new notifications.");
SimpleTest.finish();
});
});
SimpleTest.waitForExplicitFinish();
</script>
</pre>
<div id="log">
</div>
</body>
</html>

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

@ -4136,33 +4136,36 @@ EventStateManager::DispatchMouseOrPointerEvent(WidgetMouseEvent* aMouseEvent,
return nullptr;
}
nsCOMPtr<nsIContent> targetContent = aTargetContent;
nsCOMPtr<nsIContent> relatedContent = aRelatedContent;
nsAutoPtr<WidgetMouseEvent> dispatchEvent;
CreateMouseOrPointerWidgetEvent(aMouseEvent, aMessage,
aRelatedContent, dispatchEvent);
relatedContent, dispatchEvent);
AutoWeakFrame previousTarget = mCurrentTarget;
mCurrentTargetContent = aTargetContent;
mCurrentTargetContent = targetContent;
nsIFrame* targetFrame = nullptr;
nsEventStatus status = nsEventStatus_eIgnore;
ESMEventCB callback(aTargetContent);
EventDispatcher::Dispatch(aTargetContent, mPresContext, dispatchEvent, nullptr,
ESMEventCB callback(targetContent);
EventDispatcher::Dispatch(targetContent, mPresContext, dispatchEvent, nullptr,
&status, &callback);
if (mPresContext) {
// Although the primary frame was checked in event callback, it may not be
// the same object after event dispatch and handling, so refetch it.
targetFrame = mPresContext->GetPrimaryFrameFor(aTargetContent);
targetFrame = mPresContext->GetPrimaryFrameFor(targetContent);
// If we are entering/leaving remote content, dispatch a mouse enter/exit
// event to the remote frame.
if (IsRemoteTarget(aTargetContent)) {
if (IsRemoteTarget(targetContent)) {
if (aMessage == eMouseOut) {
// For remote content, send a "top-level" widget mouse exit event.
nsAutoPtr<WidgetMouseEvent> remoteEvent;
CreateMouseOrPointerWidgetEvent(aMouseEvent, eMouseExitFromWidget,
aRelatedContent, remoteEvent);
relatedContent, remoteEvent);
remoteEvent->mExitFrom = WidgetMouseEvent::eTopLevel;
// mCurrentTarget is set to the new target, so we must reset it to the
@ -4174,7 +4177,7 @@ EventStateManager::DispatchMouseOrPointerEvent(WidgetMouseEvent* aMouseEvent,
} else if (aMessage == eMouseOver) {
nsAutoPtr<WidgetMouseEvent> remoteEvent;
CreateMouseOrPointerWidgetEvent(aMouseEvent, eMouseEnterIntoWidget,
aRelatedContent, remoteEvent);
relatedContent, remoteEvent);
HandleCrossProcessEvent(remoteEvent, &status);
}
}

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

@ -13,19 +13,19 @@ add_task(async function() {
SpecialPowers.pushPrefEnv({"set": [["ui.tooltipDelay", 0]]}, resolve);
});
// Send a mousemove at a known position to start the test.
await BrowserTestUtils.synthesizeMouseAtCenter("#p2", { type: "mousemove" }, browser);
let popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown", false, event => {
is(event.originalTarget.localName, "tooltip", "tooltip is showing");
return true;
});
await BrowserTestUtils.synthesizeMouseAtCenter("#p1", { type: "mousemove" }, browser);
await popupShownPromise;
let popupHiddenPromise = BrowserTestUtils.waitForEvent(document, "popuphidden", false, event => {
is(event.originalTarget.localName, "tooltip", "tooltip is hidden");
return true;
});
// Send a mousemove at a known position to start the test.
await BrowserTestUtils.synthesizeMouseAtCenter("#p2", { type: "mousemove" }, browser);
await BrowserTestUtils.synthesizeMouseAtCenter("#p1", { type: "mousemove" }, browser);
await popupShownPromise;
await BrowserTestUtils.synthesizeMouseAtCenter("#p2", { type: "mousemove" }, browser);
await popupHiddenPromise;

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

@ -741,8 +741,28 @@ this.BrowserTestUtils = {
* let promiseEvent = BrowserTestUtils.waitForEvent(element, "eventName");
* // Do some processing here that will cause the event to be fired
* // ...
* // Now yield until the Promise is fulfilled
* let receivedEvent = yield promiseEvent;
* // Now wait until the Promise is fulfilled
* let receivedEvent = await promiseEvent;
*
* The promise resolution/rejection handler for the returned promise is
* guaranteed not to be called until the next event tick after the event
* listener gets called, so that all other event listeners for the element
* are executed before the handler is executed.
*
* let promiseEvent = BrowserTestUtils.waitForEvent(element, "eventName");
* // Same event tick here.
* await promiseEvent;
* // Next event tick here.
*
* If some code, such like adding yet another event listener, needs to be
* executed in the same event tick, use raw addEventListener instead and
* place the code inside the event listener.
*
* element.addEventListener("load", () => {
* // Add yet another event listener in the same event tick as the load
* // event listener.
* p = BrowserTestUtils.waitForEvent(element, "ready");
* }, { once: true });
*
* @param {Element} subject
* The element that should receive the event.
@ -773,14 +793,14 @@ this.BrowserTestUtils = {
return;
}
subject.removeEventListener(eventName, listener, capture);
resolve(event);
TestUtils.executeSoon(() => resolve(event));
} catch (ex) {
try {
subject.removeEventListener(eventName, listener, capture);
} catch (ex2) {
// Maybe the provided object does not support removeEventListener.
}
reject(ex);
TestUtils.executeSoon(() => reject(ex));
}
}, capture, wantsUntrusted);
});

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

@ -68,3 +68,30 @@ add_task(async function() {
ok(true, "Should have returned a rejected promise trying to unregister an unknown about page");
});
});
add_task(async function testWaitForEvent() {
// A promise returned by BrowserTestUtils.waitForEvent should not be resolved
// in the same event tick as the event listener is called.
let eventListenerCalled = false;
let waitForEventResolved = false;
// Use capturing phase to make sure the event listener added by
// BrowserTestUtils.waitForEvent is called before the normal event listener
// below.
let eventPromise = BrowserTestUtils.waitForEvent(gBrowser, "dummyevent", true);
eventPromise.then(() => {
waitForEventResolved = true;
});
// Add normal event listener that is called after the event listener added by
// BrowserTestUtils.waitForEvent.
gBrowser.addEventListener("dummyevent", () => {
eventListenerCalled = true;
is(waitForEventResolved, false, "BrowserTestUtils.waitForEvent promise resolution handler shouldn't be called at this point.");
}, { once: true });
var event = new CustomEvent("dummyevent");
gBrowser.dispatchEvent(event);
await eventPromise;
is(eventListenerCalled, true, "dummyevent listener should be called");
});

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

@ -143,7 +143,10 @@ class DateTimeTestHelper {
async openPicker(pageUrl) {
this.tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
await BrowserTestUtils.synthesizeMouseAtCenter("input", {}, gBrowser.selectedBrowser);
// If dateTimePopupFrame doesn't exist yet, wait for the binding to be attached
// If dateTimePopupFrame doesn't exist yet, wait for the binding to be
// attached.
// FIXME: This has a race condition and we may miss the following events.
// (bug 1423498)
if (!this.panel.dateTimePopupFrame) {
await BrowserTestUtils.waitForEvent(this.panel, "DateTimePickerBindingReady");
}
@ -152,9 +155,19 @@ class DateTimeTestHelper {
}
async waitForPickerReady() {
await BrowserTestUtils.waitForEvent(this.frame, "load", true);
let readyPromise;
let loadPromise = new Promise(resolve => {
this.frame.addEventListener("load", () => {
// Add the PickerReady event listener directly inside the load event
// listener to avoid missing the event.
readyPromise = BrowserTestUtils.waitForEvent(this.frame.contentDocument, "PickerReady");
resolve();
}, { capture: true, once: true });
});
await loadPromise;
// Wait for picker elements to be ready
await BrowserTestUtils.waitForEvent(this.frame.contentDocument, "PickerReady");
await readyPromise;
}
/**