widget/tests/TestAppShellSteadyState.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial