summaryrefslogtreecommitdiffstats
path: root/widget/tests/TestAppShellSteadyState.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/tests/TestAppShellSteadyState.cpp')
-rw-r--r--widget/tests/TestAppShellSteadyState.cpp503
1 files changed, 503 insertions, 0 deletions
diff --git a/widget/tests/TestAppShellSteadyState.cpp b/widget/tests/TestAppShellSteadyState.cpp
new file mode 100644
index 000000000..03888fc0a
--- /dev/null
+++ b/widget/tests/TestAppShellSteadyState.cpp
@@ -0,0 +1,503 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "TestHarness.h"
+
+#include "nsIAppShell.h"
+#include "nsIAppShellService.h"
+#include "nsIDocument.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMEventListener.h"
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIRunnable.h"
+#include "nsIURI.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsIXULWindow.h"
+
+#include "nsAppShellCID.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsNetUtil.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+using namespace mozilla;
+
+typedef void (*TestFunc)(nsIAppShell*);
+
+bool gStableStateEventHasRun = false;
+
+class ExitAppShellRunnable : public Runnable
+{
+ nsCOMPtr<nsIAppShell> mAppShell;
+
+public:
+ explicit ExitAppShellRunnable(nsIAppShell* aAppShell)
+ : mAppShell(aAppShell)
+ { }
+
+ NS_IMETHOD
+ Run() override
+ {
+ return mAppShell->Exit();
+ }
+};
+
+class StableStateRunnable : public Runnable
+{
+public:
+ NS_IMETHOD
+ Run() override
+ {
+ if (gStableStateEventHasRun) {
+ fail("StableStateRunnable already ran");
+ }
+ gStableStateEventHasRun = true;
+ return NS_OK;
+ }
+};
+
+class CheckStableStateRunnable : public Runnable
+{
+ bool mShouldHaveRun;
+
+public:
+ explicit CheckStableStateRunnable(bool aShouldHaveRun)
+ : mShouldHaveRun(aShouldHaveRun)
+ { }
+
+ NS_IMETHOD
+ Run() override
+ {
+ if (mShouldHaveRun == gStableStateEventHasRun) {
+ passed("StableStateRunnable state correct (%s)",
+ mShouldHaveRun ? "true" : "false");
+ } else {
+ fail("StableStateRunnable ran at wrong time");
+ }
+ return NS_OK;
+ }
+};
+
+class ScheduleStableStateRunnable : public CheckStableStateRunnable
+{
+protected:
+ nsCOMPtr<nsIAppShell> mAppShell;
+
+public:
+ explicit ScheduleStableStateRunnable(nsIAppShell* aAppShell)
+ : CheckStableStateRunnable(false), mAppShell(aAppShell)
+ { }
+
+ NS_IMETHOD
+ Run() override
+ {
+ CheckStableStateRunnable::Run();
+
+ nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable();
+ nsresult rv = mAppShell->RunBeforeNextEvent(runnable);
+ if (NS_FAILED(rv)) {
+ fail("RunBeforeNextEvent returned failure code %u", rv);
+ }
+
+ return rv;
+ }
+};
+
+class NextTestRunnable : public Runnable
+{
+ nsCOMPtr<nsIAppShell> mAppShell;
+
+public:
+ explicit NextTestRunnable(nsIAppShell* aAppShell)
+ : mAppShell(aAppShell)
+ { }
+
+ NS_IMETHOD Run();
+};
+
+class ScheduleNestedStableStateRunnable : public ScheduleStableStateRunnable
+{
+public:
+ explicit ScheduleNestedStableStateRunnable(nsIAppShell* aAppShell)
+ : ScheduleStableStateRunnable(aAppShell)
+ { }
+
+ NS_IMETHOD
+ Run() override
+ {
+ ScheduleStableStateRunnable::Run();
+
+ nsCOMPtr<nsIRunnable> runnable = new CheckStableStateRunnable(false);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch check runnable");
+ }
+
+ if (NS_FAILED(NS_ProcessPendingEvents(nullptr))) {
+ fail("Failed to process all pending events");
+ }
+
+ runnable = new CheckStableStateRunnable(true);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch check runnable");
+ }
+
+ runnable = new NextTestRunnable(mAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ }
+
+ return NS_OK;
+ }
+};
+
+class EventListener final : public nsIDOMEventListener
+{
+ nsCOMPtr<nsIAppShell> mAppShell;
+
+ static nsIDOMWindowUtils* sWindowUtils;
+ static nsIAppShell* sAppShell;
+
+ ~EventListener() {}
+
+public:
+ NS_DECL_ISUPPORTS
+
+ explicit EventListener(nsIAppShell* aAppShell)
+ : mAppShell(aAppShell)
+ { }
+
+ NS_IMETHOD
+ HandleEvent(nsIDOMEvent* aEvent) override
+ {
+ nsString type;
+ if (NS_FAILED(aEvent->GetType(type))) {
+ fail("Failed to get event type");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (type.EqualsLiteral("load")) {
+ passed("Got load event");
+
+ nsCOMPtr<nsIDOMEventTarget> target;
+ if (NS_FAILED(aEvent->GetTarget(getter_AddRefs(target)))) {
+ fail("Failed to get event type");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDocument> document = do_QueryInterface(target);
+ if (!document) {
+ fail("Failed to QI to nsIDocument!");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsPIDOMWindow> window = document->GetWindow();
+ if (!window) {
+ fail("Failed to get window from document!");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
+ if (!utils) {
+ fail("Failed to get DOMWindowUtils!");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!ScheduleTimer(utils)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ if (type.EqualsLiteral("keypress")) {
+ passed("Got keypress event");
+
+ nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable();
+ nsresult rv = mAppShell->RunBeforeNextEvent(runnable);
+ if (NS_FAILED(rv)) {
+ fail("RunBeforeNextEvent returned failure code %u", rv);
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ fail("Got an unexpected event: %s", NS_ConvertUTF16toUTF8(type).get());
+ return NS_OK;
+ }
+
+#ifdef XP_WIN
+ static VOID CALLBACK
+ TimerCallback(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
+ {
+ if (sWindowUtils) {
+ nsCOMPtr<nsIDOMWindowUtils> utils = dont_AddRef(sWindowUtils);
+ sWindowUtils = nullptr;
+
+ if (gStableStateEventHasRun) {
+ fail("StableStateRunnable ran at wrong time");
+ } else {
+ passed("StableStateRunnable state correct (false)");
+ }
+
+ int32_t layout = 0x409; // US
+ int32_t keyCode = 0x41; // VK_A
+ NS_NAMED_LITERAL_STRING(a, "a");
+
+ if (NS_FAILED(utils->SendNativeKeyEvent(layout, keyCode, 0, a, a, nullptr))) {
+ fail("Failed to synthesize native event");
+ }
+
+ return;
+ }
+
+ KillTimer(nullptr, idEvent);
+
+ nsCOMPtr<nsIAppShell> appShell = dont_AddRef(sAppShell);
+
+ if (!gStableStateEventHasRun) {
+ fail("StableStateRunnable didn't run yet");
+ } else {
+ passed("StableStateRunnable state correct (true)");
+ }
+
+ nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(appShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ }
+
+ }
+#endif
+
+ bool
+ ScheduleTimer(nsIDOMWindowUtils* aWindowUtils)
+ {
+#ifdef XP_WIN
+ UINT_PTR timerId = SetTimer(nullptr, 0, 1000, (TIMERPROC)TimerCallback);
+ if (!timerId) {
+ fail("SetTimer failed!");
+ return false;
+ }
+
+ nsCOMPtr<nsIDOMWindowUtils> utils = aWindowUtils;
+ utils.forget(&sWindowUtils);
+
+ nsCOMPtr<nsIAppShell> appShell = mAppShell;
+ appShell.forget(&sAppShell);
+
+ return true;
+#else
+ return false;
+#endif
+ }
+};
+
+nsIDOMWindowUtils* EventListener::sWindowUtils = nullptr;
+nsIAppShell* EventListener::sAppShell = nullptr;
+
+NS_IMPL_ISUPPORTS(EventListener, nsIDOMEventListener)
+
+already_AddRefed<nsIAppShell>
+GetAppShell()
+{
+ static const char* platforms[] = {
+ "android", "mac", "gonk", "gtk", "qt", "win"
+ };
+
+ NS_NAMED_LITERAL_CSTRING(contractPrefix, "@mozilla.org/widget/appshell/");
+ NS_NAMED_LITERAL_CSTRING(contractSuffix, ";1");
+
+ for (size_t index = 0; index < ArrayLength(platforms); index++) {
+ nsAutoCString contractID(contractPrefix);
+ contractID.AppendASCII(platforms[index]);
+ contractID.Append(contractSuffix);
+
+ nsCOMPtr<nsIAppShell> appShell = do_GetService(contractID.get());
+ if (appShell) {
+ return appShell.forget();
+ }
+ }
+
+ return nullptr;
+}
+
+void
+Test1(nsIAppShell* aAppShell)
+{
+ // Schedule stable state runnable to be run before next event.
+
+ nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable();
+ if (NS_FAILED(aAppShell->RunBeforeNextEvent(runnable))) {
+ fail("RunBeforeNextEvent failed");
+ }
+
+ runnable = new CheckStableStateRunnable(true);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch check runnable");
+ }
+
+ runnable = new NextTestRunnable(aAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ }
+}
+
+void
+Test2(nsIAppShell* aAppShell)
+{
+ // Schedule stable state runnable to be run before next event from another
+ // runnable.
+
+ nsCOMPtr<nsIRunnable> runnable = new ScheduleStableStateRunnable(aAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch schedule runnable");
+ }
+
+ runnable = new CheckStableStateRunnable(true);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch check runnable");
+ }
+
+ runnable = new NextTestRunnable(aAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ }
+}
+
+void
+Test3(nsIAppShell* aAppShell)
+{
+ // Schedule steadystate runnable to be run before next event with nested loop.
+
+ nsCOMPtr<nsIRunnable> runnable =
+ new ScheduleNestedStableStateRunnable(aAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch schedule runnable");
+ }
+}
+
+bool
+Test4Internal(nsIAppShell* aAppShell)
+{
+#ifndef XP_WIN
+ // Not sure how to test on other platforms.
+ return false;
+#else
+ nsCOMPtr<nsIAppShellService> appService =
+ do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
+ if (!appService) {
+ fail("Failed to get appshell service!");
+ return false;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), "about:", nullptr))) {
+ fail("Failed to create new uri");
+ return false;
+ }
+
+ uint32_t flags = nsIWebBrowserChrome::CHROME_DEFAULT;
+
+ nsCOMPtr<nsIXULWindow> xulWindow;
+ if (NS_FAILED(appService->CreateTopLevelWindow(nullptr, uri, flags, 100, 100, nullptr,
+ getter_AddRefs(xulWindow)))) {
+ fail("Failed to create new window");
+ return false;
+ }
+
+ nsCOMPtr<nsIDOMWindow> window = do_GetInterface(xulWindow);
+ if (!window) {
+ fail("Can't get dom window!");
+ return false;
+ }
+
+ nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(window);
+ if (!target) {
+ fail("Can't QI to nsIDOMEventTarget!");
+ return false;
+ }
+
+ nsCOMPtr<nsIDOMEventListener> listener = new EventListener(aAppShell);
+ if (NS_FAILED(target->AddEventListener(NS_LITERAL_STRING("keypress"),
+ listener, false, false)) ||
+ NS_FAILED(target->AddEventListener(NS_LITERAL_STRING("load"), listener,
+ false, false))) {
+ fail("Can't add event listeners!");
+ return false;
+ }
+
+ return true;
+#endif
+}
+
+void
+Test4(nsIAppShell* aAppShell)
+{
+ if (!Test4Internal(aAppShell)) {
+ nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(aAppShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ }
+ }
+}
+
+const TestFunc gTests[] = {
+ Test1, Test2, Test3, Test4
+};
+
+size_t gTestIndex = 0;
+
+NS_IMETHODIMP
+NextTestRunnable::Run()
+{
+ if (gTestIndex > 0) {
+ passed("Finished test %u", gTestIndex);
+ }
+
+ gStableStateEventHasRun = false;
+
+ if (gTestIndex < ArrayLength(gTests)) {
+ gTests[gTestIndex++](mAppShell);
+ }
+ else {
+ nsCOMPtr<nsIRunnable> exitRunnable = new ExitAppShellRunnable(mAppShell);
+
+ nsresult rv = NS_DispatchToCurrentThread(exitRunnable);
+ if (NS_FAILED(rv)) {
+ fail("Failed to dispatch exit runnable!");
+ }
+ }
+
+ return NS_OK;
+}
+
+int main(int argc, char** argv)
+{
+ ScopedLogging log;
+ ScopedXPCOM xpcom("TestAppShellSteadyState");
+
+ if (!xpcom.failed()) {
+ nsCOMPtr<nsIAppShell> appShell = GetAppShell();
+ if (!appShell) {
+ fail("Couldn't get appshell!");
+ } else {
+ nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(appShell);
+ if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+ fail("Failed to dispatch next test runnable");
+ } else if (NS_FAILED(appShell->Run())) {
+ fail("Failed to run appshell");
+ }
+ }
+ }
+
+ return gFailCount != 0;
+}