2 * Any copyright is dedicated to the Public Domain.
3 * http://creativecommons.org/publicdomain/zero/1.0/
6 #include "TestHarness.h"
8 #include "nsGlobalWindowOuter.h"
9 #include "nsIAppShell.h"
10 #include "nsIAppShellService.h"
11 #include "mozilla/dom/Document.h"
12 #include "nsIDOMEventListener.h"
13 #include "nsIDOMWindow.h"
14 #include "nsIDOMWindowUtils.h"
15 #include "nsIInterfaceRequestor.h"
16 #include "nsIRunnable.h"
18 #include "nsIWebBrowserChrome.h"
19 #include "nsIXULWindow.h"
21 #include "nsAppShellCID.h"
22 #include "nsIInterfaceRequestorUtils.h"
23 #include "nsNetUtil.h"
24 #include "nsThreadUtils.h"
25 #include "mozilla/Attributes.h"
26 #include "mozilla/dom/Event.h"
27 #include "mozilla/dom/EventTarget.h"
33 using namespace mozilla
;
35 typedef void (*TestFunc
)(nsIAppShell
*);
37 bool gStableStateEventHasRun
= false;
39 class ExitAppShellRunnable
: public Runnable
{
40 nsCOMPtr
<nsIAppShell
> mAppShell
;
43 explicit ExitAppShellRunnable(nsIAppShell
* aAppShell
)
44 : mAppShell(aAppShell
) {}
47 Run() override
{ return mAppShell
->Exit(); }
50 class StableStateRunnable
: public Runnable
{
54 if (gStableStateEventHasRun
) {
55 fail("StableStateRunnable already ran");
57 gStableStateEventHasRun
= true;
62 class CheckStableStateRunnable
: public Runnable
{
66 explicit CheckStableStateRunnable(bool aShouldHaveRun
)
67 : mShouldHaveRun(aShouldHaveRun
) {}
71 if (mShouldHaveRun
== gStableStateEventHasRun
) {
72 passed("StableStateRunnable state correct (%s)",
73 mShouldHaveRun
? "true" : "false");
75 fail("StableStateRunnable ran at wrong time");
81 class ScheduleStableStateRunnable
: public CheckStableStateRunnable
{
83 nsCOMPtr
<nsIAppShell
> mAppShell
;
86 explicit ScheduleStableStateRunnable(nsIAppShell
* aAppShell
)
87 : CheckStableStateRunnable(false), mAppShell(aAppShell
) {}
91 CheckStableStateRunnable::Run();
93 nsCOMPtr
<nsIRunnable
> runnable
= new StableStateRunnable();
94 nsresult rv
= mAppShell
->RunBeforeNextEvent(runnable
);
96 fail("RunBeforeNextEvent returned failure code %u", rv
);
103 class NextTestRunnable
: public Runnable
{
104 nsCOMPtr
<nsIAppShell
> mAppShell
;
107 explicit NextTestRunnable(nsIAppShell
* aAppShell
) : mAppShell(aAppShell
) {}
109 NS_IMETHOD
Run() override
;
112 class ScheduleNestedStableStateRunnable
: public ScheduleStableStateRunnable
{
114 explicit ScheduleNestedStableStateRunnable(nsIAppShell
* aAppShell
)
115 : ScheduleStableStateRunnable(aAppShell
) {}
119 ScheduleStableStateRunnable::Run();
121 nsCOMPtr
<nsIRunnable
> runnable
= new CheckStableStateRunnable(false);
122 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
123 fail("Failed to dispatch check runnable");
126 if (NS_FAILED(NS_ProcessPendingEvents(nullptr))) {
127 fail("Failed to process all pending events");
130 runnable
= new CheckStableStateRunnable(true);
131 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
132 fail("Failed to dispatch check runnable");
135 runnable
= new NextTestRunnable(mAppShell
);
136 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
137 fail("Failed to dispatch next test runnable");
144 class EventListener final
: public nsIDOMEventListener
{
145 nsCOMPtr
<nsIAppShell
> mAppShell
;
147 static nsIDOMWindowUtils
* sWindowUtils
;
148 static nsIAppShell
* sAppShell
;
155 explicit EventListener(nsIAppShell
* aAppShell
) : mAppShell(aAppShell
) {}
158 HandleEvent(dom::Event
* aEvent
) override
{
160 if (NS_FAILED(aEvent
->GetType(type
))) {
161 fail("Failed to get event type");
162 return NS_ERROR_FAILURE
;
165 if (type
.EqualsLiteral("load")) {
166 passed("Got load event");
168 nsCOMPtr
<dom::Document
> document
= do_QueryInterface(aEvent
->GetTarget());
170 fail("Failed to QI to Document!");
171 return NS_ERROR_FAILURE
;
174 nsCOMPtr
<nsPIDOMWindowOuter
> window
= document
->GetWindow();
176 fail("Failed to get window from document!");
177 return NS_ERROR_FAILURE
;
180 nsCOMPtr
<nsIDOMWindowUtils
> utils
=
181 nsGlobalWindowOuter::Cast(window
)->WindowUtils();
183 if (!ScheduleTimer(utils
)) {
184 return NS_ERROR_FAILURE
;
190 if (type
.EqualsLiteral("keypress")) {
191 passed("Got keypress event");
193 nsCOMPtr
<nsIRunnable
> runnable
= new StableStateRunnable();
194 nsresult rv
= mAppShell
->RunBeforeNextEvent(runnable
);
196 fail("RunBeforeNextEvent returned failure code %u", rv
);
197 return NS_ERROR_FAILURE
;
203 fail("Got an unexpected event: %s", NS_ConvertUTF16toUTF8(type
).get());
208 static VOID CALLBACK
TimerCallback(HWND hwnd
, UINT uMsg
, UINT idEvent
,
211 nsCOMPtr
<nsIDOMWindowUtils
> utils
= dont_AddRef(sWindowUtils
);
212 sWindowUtils
= nullptr;
214 if (gStableStateEventHasRun
) {
215 fail("StableStateRunnable ran at wrong time");
217 passed("StableStateRunnable state correct (false)");
220 int32_t layout
= 0x409; // US
221 int32_t keyCode
= 0x41; // VK_A
222 NS_NAMED_LITERAL_STRING(a
, "a");
225 utils
->SendNativeKeyEvent(layout
, keyCode
, 0, a
, a
, nullptr))) {
226 fail("Failed to synthesize native event");
232 KillTimer(nullptr, idEvent
);
234 nsCOMPtr
<nsIAppShell
> appShell
= dont_AddRef(sAppShell
);
236 if (!gStableStateEventHasRun
) {
237 fail("StableStateRunnable didn't run yet");
239 passed("StableStateRunnable state correct (true)");
242 nsCOMPtr
<nsIRunnable
> runnable
= new NextTestRunnable(appShell
);
243 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
244 fail("Failed to dispatch next test runnable");
249 bool ScheduleTimer(nsIDOMWindowUtils
* aWindowUtils
) {
251 UINT_PTR timerId
= SetTimer(nullptr, 0, 1000, (TIMERPROC
)TimerCallback
);
253 fail("SetTimer failed!");
257 nsCOMPtr
<nsIDOMWindowUtils
> utils
= aWindowUtils
;
258 utils
.forget(&sWindowUtils
);
260 nsCOMPtr
<nsIAppShell
> appShell
= mAppShell
;
261 appShell
.forget(&sAppShell
);
270 nsIDOMWindowUtils
* EventListener::sWindowUtils
= nullptr;
271 nsIAppShell
* EventListener::sAppShell
= nullptr;
273 NS_IMPL_ISUPPORTS(EventListener
, nsIDOMEventListener
)
275 already_AddRefed
<nsIAppShell
> GetAppShell() {
276 static const char* platforms
[] = {"android", "mac", "gtk", "qt", "win"};
278 NS_NAMED_LITERAL_CSTRING(contractPrefix
, "@mozilla.org/widget/appshell/");
279 NS_NAMED_LITERAL_CSTRING(contractSuffix
, ";1");
281 for (size_t index
= 0; index
< ArrayLength(platforms
); index
++) {
282 nsAutoCString
contractID(contractPrefix
);
283 contractID
.AppendASCII(platforms
[index
]);
284 contractID
.Append(contractSuffix
);
286 nsCOMPtr
<nsIAppShell
> appShell
= do_GetService(contractID
.get());
288 return appShell
.forget();
295 void Test1(nsIAppShell
* aAppShell
) {
296 // Schedule stable state runnable to be run before next event.
298 nsCOMPtr
<nsIRunnable
> runnable
= new StableStateRunnable();
299 if (NS_FAILED(aAppShell
->RunBeforeNextEvent(runnable
))) {
300 fail("RunBeforeNextEvent failed");
303 runnable
= new CheckStableStateRunnable(true);
304 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
305 fail("Failed to dispatch check runnable");
308 runnable
= new NextTestRunnable(aAppShell
);
309 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
310 fail("Failed to dispatch next test runnable");
314 void Test2(nsIAppShell
* aAppShell
) {
315 // Schedule stable state runnable to be run before next event from another
318 nsCOMPtr
<nsIRunnable
> runnable
= new ScheduleStableStateRunnable(aAppShell
);
319 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
320 fail("Failed to dispatch schedule runnable");
323 runnable
= new CheckStableStateRunnable(true);
324 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
325 fail("Failed to dispatch check runnable");
328 runnable
= new NextTestRunnable(aAppShell
);
329 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
330 fail("Failed to dispatch next test runnable");
334 void Test3(nsIAppShell
* aAppShell
) {
335 // Schedule steadystate runnable to be run before next event with nested loop.
337 nsCOMPtr
<nsIRunnable
> runnable
=
338 new ScheduleNestedStableStateRunnable(aAppShell
);
339 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
340 fail("Failed to dispatch schedule runnable");
344 bool Test4Internal(nsIAppShell
* aAppShell
) {
346 // Not sure how to test on other platforms.
349 nsCOMPtr
<nsIAppShellService
> appService
=
350 do_GetService(NS_APPSHELLSERVICE_CONTRACTID
);
352 fail("Failed to get appshell service!");
356 nsCOMPtr
<nsIURI
> uri
;
357 if (NS_FAILED(NS_NewURI(getter_AddRefs(uri
), "about:", nullptr))) {
358 fail("Failed to create new uri");
362 uint32_t flags
= nsIWebBrowserChrome::CHROME_DEFAULT
;
364 nsCOMPtr
<nsIXULWindow
> xulWindow
;
365 if (NS_FAILED(appService
->CreateTopLevelWindow(
366 nullptr, uri
, flags
, 100, 100, nullptr, getter_AddRefs(xulWindow
)))) {
367 fail("Failed to create new window");
371 nsCOMPtr
<nsIDOMWindow
> window
= do_GetInterface(xulWindow
);
373 fail("Can't get dom window!");
377 RefPTr
<dom::EventTarget
> target
= do_QueryInterface(window
);
379 fail("Can't QI to EventTarget!");
383 nsCOMPtr
<nsIDOMEventListener
> listener
= new EventListener(aAppShell
);
384 if (NS_FAILED(target
->AddEventListener(NS_LITERAL_STRING("keypress"),
385 listener
, false, false)) ||
386 NS_FAILED(target
->AddEventListener(NS_LITERAL_STRING("load"), listener
,
388 fail("Can't add event listeners!");
396 void Test4(nsIAppShell
* aAppShell
) {
397 if (!Test4Internal(aAppShell
)) {
398 nsCOMPtr
<nsIRunnable
> runnable
= new NextTestRunnable(aAppShell
);
399 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
400 fail("Failed to dispatch next test runnable");
405 const TestFunc gTests
[] = {Test1
, Test2
, Test3
, Test4
};
407 size_t gTestIndex
= 0;
410 NextTestRunnable::Run() {
411 if (gTestIndex
> 0) {
412 passed("Finished test %u", gTestIndex
);
415 gStableStateEventHasRun
= false;
417 if (gTestIndex
< ArrayLength(gTests
)) {
418 gTests
[gTestIndex
++](mAppShell
);
420 nsCOMPtr
<nsIRunnable
> exitRunnable
= new ExitAppShellRunnable(mAppShell
);
422 nsresult rv
= NS_DispatchToCurrentThread(exitRunnable
);
424 fail("Failed to dispatch exit runnable!");
431 int main(int argc
, char** argv
) {
433 ScopedXPCOM
xpcom("TestAppShellSteadyState");
435 if (!xpcom
.failed()) {
436 nsCOMPtr
<nsIAppShell
> appShell
= GetAppShell();
438 fail("Couldn't get appshell!");
440 nsCOMPtr
<nsIRunnable
> runnable
= new NextTestRunnable(appShell
);
441 if (NS_FAILED(NS_DispatchToCurrentThread(runnable
))) {
442 fail("Failed to dispatch next test runnable");
443 } else if (NS_FAILED(appShell
->Run())) {
444 fail("Failed to run appshell");
449 return gFailCount
!= 0;