widget/tests/TestAppShellSteadyState.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:9af98cd69d2b
1 /**
2 * Any copyright is dedicated to the Public Domain.
3 * http://creativecommons.org/publicdomain/zero/1.0/
4 */
5
6 #include "TestHarness.h"
7
8 #include "nsIAppShell.h"
9 #include "nsIAppShellService.h"
10 #include "nsIDocument.h"
11 #include "nsIDOMEvent.h"
12 #include "nsIDOMEventListener.h"
13 #include "nsIDOMEventTarget.h"
14 #include "nsIDOMWindow.h"
15 #include "nsIDOMWindowUtils.h"
16 #include "nsIInterfaceRequestor.h"
17 #include "nsIRunnable.h"
18 #include "nsIURI.h"
19 #include "nsIWebBrowserChrome.h"
20 #include "nsIXULWindow.h"
21
22 #include "nsAppShellCID.h"
23 #include "nsIInterfaceRequestorUtils.h"
24 #include "nsNetUtil.h"
25 #include "nsThreadUtils.h"
26 #include "mozilla/Attributes.h"
27
28 #ifdef XP_WIN
29 #include <windows.h>
30 #endif
31
32 using namespace mozilla;
33
34 typedef void (*TestFunc)(nsIAppShell*);
35
36 bool gStableStateEventHasRun = false;
37
38 class ExitAppShellRunnable : public nsRunnable
39 {
40 nsCOMPtr<nsIAppShell> mAppShell;
41
42 public:
43 ExitAppShellRunnable(nsIAppShell* aAppShell)
44 : mAppShell(aAppShell)
45 { }
46
47 NS_IMETHOD
48 Run()
49 {
50 return mAppShell->Exit();
51 }
52 };
53
54 class StableStateRunnable : public nsRunnable
55 {
56 public:
57 NS_IMETHOD
58 Run()
59 {
60 if (gStableStateEventHasRun) {
61 fail("StableStateRunnable already ran");
62 }
63 gStableStateEventHasRun = true;
64 return NS_OK;
65 }
66 };
67
68 class CheckStableStateRunnable : public nsRunnable
69 {
70 bool mShouldHaveRun;
71
72 public:
73 CheckStableStateRunnable(bool aShouldHaveRun)
74 : mShouldHaveRun(aShouldHaveRun)
75 { }
76
77 NS_IMETHOD
78 Run()
79 {
80 if (mShouldHaveRun == gStableStateEventHasRun) {
81 passed("StableStateRunnable state correct (%s)",
82 mShouldHaveRun ? "true" : "false");
83 } else {
84 fail("StableStateRunnable ran at wrong time");
85 }
86 return NS_OK;
87 }
88 };
89
90 class ScheduleStableStateRunnable : public CheckStableStateRunnable
91 {
92 protected:
93 nsCOMPtr<nsIAppShell> mAppShell;
94
95 public:
96 ScheduleStableStateRunnable(nsIAppShell* aAppShell)
97 : CheckStableStateRunnable(false), mAppShell(aAppShell)
98 { }
99
100 NS_IMETHOD
101 Run()
102 {
103 CheckStableStateRunnable::Run();
104
105 nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable();
106 nsresult rv = mAppShell->RunBeforeNextEvent(runnable);
107 if (NS_FAILED(rv)) {
108 fail("RunBeforeNextEvent returned failure code %u", rv);
109 }
110
111 return rv;
112 }
113 };
114
115 class NextTestRunnable : public nsRunnable
116 {
117 nsCOMPtr<nsIAppShell> mAppShell;
118
119 public:
120 NextTestRunnable(nsIAppShell* aAppShell)
121 : mAppShell(aAppShell)
122 { }
123
124 NS_IMETHOD Run();
125 };
126
127 class ScheduleNestedStableStateRunnable : public ScheduleStableStateRunnable
128 {
129 public:
130 ScheduleNestedStableStateRunnable(nsIAppShell* aAppShell)
131 : ScheduleStableStateRunnable(aAppShell)
132 { }
133
134 NS_IMETHOD
135 Run()
136 {
137 ScheduleStableStateRunnable::Run();
138
139 nsCOMPtr<nsIRunnable> runnable = new CheckStableStateRunnable(false);
140 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
141 fail("Failed to dispatch check runnable");
142 }
143
144 if (NS_FAILED(NS_ProcessPendingEvents(nullptr))) {
145 fail("Failed to process all pending events");
146 }
147
148 runnable = new CheckStableStateRunnable(true);
149 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
150 fail("Failed to dispatch check runnable");
151 }
152
153 runnable = new NextTestRunnable(mAppShell);
154 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
155 fail("Failed to dispatch next test runnable");
156 }
157
158 return NS_OK;
159 }
160 };
161
162 class EventListener MOZ_FINAL : public nsIDOMEventListener
163 {
164 nsCOMPtr<nsIAppShell> mAppShell;
165
166 static nsIDOMWindowUtils* sWindowUtils;
167 static nsIAppShell* sAppShell;
168
169 public:
170 NS_DECL_ISUPPORTS
171
172 EventListener(nsIAppShell* aAppShell)
173 : mAppShell(aAppShell)
174 { }
175
176 NS_IMETHOD
177 HandleEvent(nsIDOMEvent* aEvent)
178 {
179 nsString type;
180 if (NS_FAILED(aEvent->GetType(type))) {
181 fail("Failed to get event type");
182 return NS_ERROR_FAILURE;
183 }
184
185 if (type.EqualsLiteral("load")) {
186 passed("Got load event");
187
188 nsCOMPtr<nsIDOMEventTarget> target;
189 if (NS_FAILED(aEvent->GetTarget(getter_AddRefs(target)))) {
190 fail("Failed to get event type");
191 return NS_ERROR_FAILURE;
192 }
193
194 nsCOMPtr<nsIDocument> document = do_QueryInterface(target);
195 if (!document) {
196 fail("Failed to QI to nsIDocument!");
197 return NS_ERROR_FAILURE;
198 }
199
200 nsCOMPtr<nsPIDOMWindow> window = document->GetWindow();
201 if (!window) {
202 fail("Failed to get window from document!");
203 return NS_ERROR_FAILURE;
204 }
205
206 nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
207 if (!utils) {
208 fail("Failed to get DOMWindowUtils!");
209 return NS_ERROR_FAILURE;
210 }
211
212 if (!ScheduleTimer(utils)) {
213 return NS_ERROR_FAILURE;
214 }
215
216 return NS_OK;
217 }
218
219 if (type.EqualsLiteral("keypress")) {
220 passed("Got keypress event");
221
222 nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable();
223 nsresult rv = mAppShell->RunBeforeNextEvent(runnable);
224 if (NS_FAILED(rv)) {
225 fail("RunBeforeNextEvent returned failure code %u", rv);
226 return NS_ERROR_FAILURE;
227 }
228
229 return NS_OK;
230 }
231
232 fail("Got an unexpected event: %s", NS_ConvertUTF16toUTF8(type).get());
233 return NS_OK;
234 }
235
236 #ifdef XP_WIN
237 static VOID CALLBACK
238 TimerCallback(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
239 {
240 if (sWindowUtils) {
241 nsCOMPtr<nsIDOMWindowUtils> utils = dont_AddRef(sWindowUtils);
242 sWindowUtils = nullptr;
243
244 if (gStableStateEventHasRun) {
245 fail("StableStateRunnable ran at wrong time");
246 } else {
247 passed("StableStateRunnable state correct (false)");
248 }
249
250 int32_t layout = 0x409; // US
251 int32_t keyCode = 0x41; // VK_A
252 NS_NAMED_LITERAL_STRING(a, "a");
253
254 if (NS_FAILED(utils->SendNativeKeyEvent(layout, keyCode, 0, a, a))) {
255 fail("Failed to synthesize native event");
256 }
257
258 return;
259 }
260
261 KillTimer(nullptr, idEvent);
262
263 nsCOMPtr<nsIAppShell> appShell = dont_AddRef(sAppShell);
264
265 if (!gStableStateEventHasRun) {
266 fail("StableStateRunnable didn't run yet");
267 } else {
268 passed("StableStateRunnable state correct (true)");
269 }
270
271 nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(appShell);
272 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
273 fail("Failed to dispatch next test runnable");
274 }
275
276 }
277 #endif
278
279 bool
280 ScheduleTimer(nsIDOMWindowUtils* aWindowUtils)
281 {
282 #ifdef XP_WIN
283 UINT_PTR timerId = SetTimer(nullptr, 0, 1000, (TIMERPROC)TimerCallback);
284 if (!timerId) {
285 fail("SetTimer failed!");
286 return false;
287 }
288
289 nsCOMPtr<nsIDOMWindowUtils> utils = aWindowUtils;
290 utils.forget(&sWindowUtils);
291
292 nsCOMPtr<nsIAppShell> appShell = mAppShell;
293 appShell.forget(&sAppShell);
294
295 return true;
296 #else
297 return false;
298 #endif
299 }
300 };
301
302 nsIDOMWindowUtils* EventListener::sWindowUtils = nullptr;
303 nsIAppShell* EventListener::sAppShell = nullptr;
304
305 NS_IMPL_ISUPPORTS(EventListener, nsIDOMEventListener)
306
307 already_AddRefed<nsIAppShell>
308 GetAppShell()
309 {
310 static const char* platforms[] = {
311 "android", "mac", "gonk", "gtk", "qt", "win"
312 };
313
314 NS_NAMED_LITERAL_CSTRING(contractPrefix, "@mozilla.org/widget/appshell/");
315 NS_NAMED_LITERAL_CSTRING(contractSuffix, ";1");
316
317 for (size_t index = 0; index < ArrayLength(platforms); index++) {
318 nsAutoCString contractID(contractPrefix);
319 contractID.AppendASCII(platforms[index]);
320 contractID.Append(contractSuffix);
321
322 nsCOMPtr<nsIAppShell> appShell = do_GetService(contractID.get());
323 if (appShell) {
324 return appShell.forget();
325 }
326 }
327
328 return nullptr;
329 }
330
331 void
332 Test1(nsIAppShell* aAppShell)
333 {
334 // Schedule stable state runnable to be run before next event.
335
336 nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable();
337 if (NS_FAILED(aAppShell->RunBeforeNextEvent(runnable))) {
338 fail("RunBeforeNextEvent failed");
339 }
340
341 runnable = new CheckStableStateRunnable(true);
342 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
343 fail("Failed to dispatch check runnable");
344 }
345
346 runnable = new NextTestRunnable(aAppShell);
347 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
348 fail("Failed to dispatch next test runnable");
349 }
350 }
351
352 void
353 Test2(nsIAppShell* aAppShell)
354 {
355 // Schedule stable state runnable to be run before next event from another
356 // runnable.
357
358 nsCOMPtr<nsIRunnable> runnable = new ScheduleStableStateRunnable(aAppShell);
359 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
360 fail("Failed to dispatch schedule runnable");
361 }
362
363 runnable = new CheckStableStateRunnable(true);
364 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
365 fail("Failed to dispatch check runnable");
366 }
367
368 runnable = new NextTestRunnable(aAppShell);
369 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
370 fail("Failed to dispatch next test runnable");
371 }
372 }
373
374 void
375 Test3(nsIAppShell* aAppShell)
376 {
377 // Schedule steadystate runnable to be run before next event with nested loop.
378
379 nsCOMPtr<nsIRunnable> runnable =
380 new ScheduleNestedStableStateRunnable(aAppShell);
381 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
382 fail("Failed to dispatch schedule runnable");
383 }
384 }
385
386 bool
387 Test4Internal(nsIAppShell* aAppShell)
388 {
389 #ifndef XP_WIN
390 // Not sure how to test on other platforms.
391 return false;
392 #endif
393
394 nsCOMPtr<nsIAppShellService> appService =
395 do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
396 if (!appService) {
397 fail("Failed to get appshell service!");
398 return false;
399 }
400
401 nsCOMPtr<nsIURI> uri;
402 if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), "about:", nullptr))) {
403 fail("Failed to create new uri");
404 return false;
405 }
406
407 uint32_t flags = nsIWebBrowserChrome::CHROME_DEFAULT;
408
409 nsCOMPtr<nsIXULWindow> xulWindow;
410 if (NS_FAILED(appService->CreateTopLevelWindow(nullptr, uri, flags, 100, 100,
411 getter_AddRefs(xulWindow)))) {
412 fail("Failed to create new window");
413 return false;
414 }
415
416 nsCOMPtr<nsIDOMWindow> window = do_GetInterface(xulWindow);
417 if (!window) {
418 fail("Can't get dom window!");
419 return false;
420 }
421
422 nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(window);
423 if (!target) {
424 fail("Can't QI to nsIDOMEventTarget!");
425 return false;
426 }
427
428 nsCOMPtr<nsIDOMEventListener> listener = new EventListener(aAppShell);
429 if (NS_FAILED(target->AddEventListener(NS_LITERAL_STRING("keypress"),
430 listener, false, false)) ||
431 NS_FAILED(target->AddEventListener(NS_LITERAL_STRING("load"), listener,
432 false, false))) {
433 fail("Can't add event listeners!");
434 return false;
435 }
436
437 return true;
438 }
439
440 void
441 Test4(nsIAppShell* aAppShell)
442 {
443 if (!Test4Internal(aAppShell)) {
444 nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(aAppShell);
445 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
446 fail("Failed to dispatch next test runnable");
447 }
448 }
449 }
450
451 const TestFunc gTests[] = {
452 Test1, Test2, Test3, Test4
453 };
454
455 size_t gTestIndex = 0;
456
457 NS_IMETHODIMP
458 NextTestRunnable::Run()
459 {
460 if (gTestIndex > 0) {
461 passed("Finished test %u", gTestIndex);
462 }
463
464 gStableStateEventHasRun = false;
465
466 if (gTestIndex < ArrayLength(gTests)) {
467 gTests[gTestIndex++](mAppShell);
468 }
469 else {
470 nsCOMPtr<nsIRunnable> exitRunnable = new ExitAppShellRunnable(mAppShell);
471
472 nsresult rv = NS_DispatchToCurrentThread(exitRunnable);
473 if (NS_FAILED(rv)) {
474 fail("Failed to dispatch exit runnable!");
475 }
476 }
477
478 return NS_OK;
479 }
480
481 int main(int argc, char** argv)
482 {
483 ScopedLogging log;
484 ScopedXPCOM xpcom("TestAppShellSteadyState");
485
486 if (!xpcom.failed()) {
487 nsCOMPtr<nsIAppShell> appShell = GetAppShell();
488 if (!appShell) {
489 fail("Couldn't get appshell!");
490 } else {
491 nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(appShell);
492 if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
493 fail("Failed to dispatch next test runnable");
494 } else if (NS_FAILED(appShell->Run())) {
495 fail("Failed to run appshell");
496 }
497 }
498 }
499
500 return gFailCount != 0;
501 }

mercurial