1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/tests/TestAppShellSteadyState.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,501 @@ 1.4 +/** 1.5 + * Any copyright is dedicated to the Public Domain. 1.6 + * http://creativecommons.org/publicdomain/zero/1.0/ 1.7 + */ 1.8 + 1.9 +#include "TestHarness.h" 1.10 + 1.11 +#include "nsIAppShell.h" 1.12 +#include "nsIAppShellService.h" 1.13 +#include "nsIDocument.h" 1.14 +#include "nsIDOMEvent.h" 1.15 +#include "nsIDOMEventListener.h" 1.16 +#include "nsIDOMEventTarget.h" 1.17 +#include "nsIDOMWindow.h" 1.18 +#include "nsIDOMWindowUtils.h" 1.19 +#include "nsIInterfaceRequestor.h" 1.20 +#include "nsIRunnable.h" 1.21 +#include "nsIURI.h" 1.22 +#include "nsIWebBrowserChrome.h" 1.23 +#include "nsIXULWindow.h" 1.24 + 1.25 +#include "nsAppShellCID.h" 1.26 +#include "nsIInterfaceRequestorUtils.h" 1.27 +#include "nsNetUtil.h" 1.28 +#include "nsThreadUtils.h" 1.29 +#include "mozilla/Attributes.h" 1.30 + 1.31 +#ifdef XP_WIN 1.32 +#include <windows.h> 1.33 +#endif 1.34 + 1.35 +using namespace mozilla; 1.36 + 1.37 +typedef void (*TestFunc)(nsIAppShell*); 1.38 + 1.39 +bool gStableStateEventHasRun = false; 1.40 + 1.41 +class ExitAppShellRunnable : public nsRunnable 1.42 +{ 1.43 + nsCOMPtr<nsIAppShell> mAppShell; 1.44 + 1.45 +public: 1.46 + ExitAppShellRunnable(nsIAppShell* aAppShell) 1.47 + : mAppShell(aAppShell) 1.48 + { } 1.49 + 1.50 + NS_IMETHOD 1.51 + Run() 1.52 + { 1.53 + return mAppShell->Exit(); 1.54 + } 1.55 +}; 1.56 + 1.57 +class StableStateRunnable : public nsRunnable 1.58 +{ 1.59 +public: 1.60 + NS_IMETHOD 1.61 + Run() 1.62 + { 1.63 + if (gStableStateEventHasRun) { 1.64 + fail("StableStateRunnable already ran"); 1.65 + } 1.66 + gStableStateEventHasRun = true; 1.67 + return NS_OK; 1.68 + } 1.69 +}; 1.70 + 1.71 +class CheckStableStateRunnable : public nsRunnable 1.72 +{ 1.73 + bool mShouldHaveRun; 1.74 + 1.75 +public: 1.76 + CheckStableStateRunnable(bool aShouldHaveRun) 1.77 + : mShouldHaveRun(aShouldHaveRun) 1.78 + { } 1.79 + 1.80 + NS_IMETHOD 1.81 + Run() 1.82 + { 1.83 + if (mShouldHaveRun == gStableStateEventHasRun) { 1.84 + passed("StableStateRunnable state correct (%s)", 1.85 + mShouldHaveRun ? "true" : "false"); 1.86 + } else { 1.87 + fail("StableStateRunnable ran at wrong time"); 1.88 + } 1.89 + return NS_OK; 1.90 + } 1.91 +}; 1.92 + 1.93 +class ScheduleStableStateRunnable : public CheckStableStateRunnable 1.94 +{ 1.95 +protected: 1.96 + nsCOMPtr<nsIAppShell> mAppShell; 1.97 + 1.98 +public: 1.99 + ScheduleStableStateRunnable(nsIAppShell* aAppShell) 1.100 + : CheckStableStateRunnable(false), mAppShell(aAppShell) 1.101 + { } 1.102 + 1.103 + NS_IMETHOD 1.104 + Run() 1.105 + { 1.106 + CheckStableStateRunnable::Run(); 1.107 + 1.108 + nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable(); 1.109 + nsresult rv = mAppShell->RunBeforeNextEvent(runnable); 1.110 + if (NS_FAILED(rv)) { 1.111 + fail("RunBeforeNextEvent returned failure code %u", rv); 1.112 + } 1.113 + 1.114 + return rv; 1.115 + } 1.116 +}; 1.117 + 1.118 +class NextTestRunnable : public nsRunnable 1.119 +{ 1.120 + nsCOMPtr<nsIAppShell> mAppShell; 1.121 + 1.122 +public: 1.123 + NextTestRunnable(nsIAppShell* aAppShell) 1.124 + : mAppShell(aAppShell) 1.125 + { } 1.126 + 1.127 + NS_IMETHOD Run(); 1.128 +}; 1.129 + 1.130 +class ScheduleNestedStableStateRunnable : public ScheduleStableStateRunnable 1.131 +{ 1.132 +public: 1.133 + ScheduleNestedStableStateRunnable(nsIAppShell* aAppShell) 1.134 + : ScheduleStableStateRunnable(aAppShell) 1.135 + { } 1.136 + 1.137 + NS_IMETHOD 1.138 + Run() 1.139 + { 1.140 + ScheduleStableStateRunnable::Run(); 1.141 + 1.142 + nsCOMPtr<nsIRunnable> runnable = new CheckStableStateRunnable(false); 1.143 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.144 + fail("Failed to dispatch check runnable"); 1.145 + } 1.146 + 1.147 + if (NS_FAILED(NS_ProcessPendingEvents(nullptr))) { 1.148 + fail("Failed to process all pending events"); 1.149 + } 1.150 + 1.151 + runnable = new CheckStableStateRunnable(true); 1.152 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.153 + fail("Failed to dispatch check runnable"); 1.154 + } 1.155 + 1.156 + runnable = new NextTestRunnable(mAppShell); 1.157 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.158 + fail("Failed to dispatch next test runnable"); 1.159 + } 1.160 + 1.161 + return NS_OK; 1.162 + } 1.163 +}; 1.164 + 1.165 +class EventListener MOZ_FINAL : public nsIDOMEventListener 1.166 +{ 1.167 + nsCOMPtr<nsIAppShell> mAppShell; 1.168 + 1.169 + static nsIDOMWindowUtils* sWindowUtils; 1.170 + static nsIAppShell* sAppShell; 1.171 + 1.172 +public: 1.173 + NS_DECL_ISUPPORTS 1.174 + 1.175 + EventListener(nsIAppShell* aAppShell) 1.176 + : mAppShell(aAppShell) 1.177 + { } 1.178 + 1.179 + NS_IMETHOD 1.180 + HandleEvent(nsIDOMEvent* aEvent) 1.181 + { 1.182 + nsString type; 1.183 + if (NS_FAILED(aEvent->GetType(type))) { 1.184 + fail("Failed to get event type"); 1.185 + return NS_ERROR_FAILURE; 1.186 + } 1.187 + 1.188 + if (type.EqualsLiteral("load")) { 1.189 + passed("Got load event"); 1.190 + 1.191 + nsCOMPtr<nsIDOMEventTarget> target; 1.192 + if (NS_FAILED(aEvent->GetTarget(getter_AddRefs(target)))) { 1.193 + fail("Failed to get event type"); 1.194 + return NS_ERROR_FAILURE; 1.195 + } 1.196 + 1.197 + nsCOMPtr<nsIDocument> document = do_QueryInterface(target); 1.198 + if (!document) { 1.199 + fail("Failed to QI to nsIDocument!"); 1.200 + return NS_ERROR_FAILURE; 1.201 + } 1.202 + 1.203 + nsCOMPtr<nsPIDOMWindow> window = document->GetWindow(); 1.204 + if (!window) { 1.205 + fail("Failed to get window from document!"); 1.206 + return NS_ERROR_FAILURE; 1.207 + } 1.208 + 1.209 + nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window); 1.210 + if (!utils) { 1.211 + fail("Failed to get DOMWindowUtils!"); 1.212 + return NS_ERROR_FAILURE; 1.213 + } 1.214 + 1.215 + if (!ScheduleTimer(utils)) { 1.216 + return NS_ERROR_FAILURE; 1.217 + } 1.218 + 1.219 + return NS_OK; 1.220 + } 1.221 + 1.222 + if (type.EqualsLiteral("keypress")) { 1.223 + passed("Got keypress event"); 1.224 + 1.225 + nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable(); 1.226 + nsresult rv = mAppShell->RunBeforeNextEvent(runnable); 1.227 + if (NS_FAILED(rv)) { 1.228 + fail("RunBeforeNextEvent returned failure code %u", rv); 1.229 + return NS_ERROR_FAILURE; 1.230 + } 1.231 + 1.232 + return NS_OK; 1.233 + } 1.234 + 1.235 + fail("Got an unexpected event: %s", NS_ConvertUTF16toUTF8(type).get()); 1.236 + return NS_OK; 1.237 + } 1.238 + 1.239 +#ifdef XP_WIN 1.240 + static VOID CALLBACK 1.241 + TimerCallback(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) 1.242 + { 1.243 + if (sWindowUtils) { 1.244 + nsCOMPtr<nsIDOMWindowUtils> utils = dont_AddRef(sWindowUtils); 1.245 + sWindowUtils = nullptr; 1.246 + 1.247 + if (gStableStateEventHasRun) { 1.248 + fail("StableStateRunnable ran at wrong time"); 1.249 + } else { 1.250 + passed("StableStateRunnable state correct (false)"); 1.251 + } 1.252 + 1.253 + int32_t layout = 0x409; // US 1.254 + int32_t keyCode = 0x41; // VK_A 1.255 + NS_NAMED_LITERAL_STRING(a, "a"); 1.256 + 1.257 + if (NS_FAILED(utils->SendNativeKeyEvent(layout, keyCode, 0, a, a))) { 1.258 + fail("Failed to synthesize native event"); 1.259 + } 1.260 + 1.261 + return; 1.262 + } 1.263 + 1.264 + KillTimer(nullptr, idEvent); 1.265 + 1.266 + nsCOMPtr<nsIAppShell> appShell = dont_AddRef(sAppShell); 1.267 + 1.268 + if (!gStableStateEventHasRun) { 1.269 + fail("StableStateRunnable didn't run yet"); 1.270 + } else { 1.271 + passed("StableStateRunnable state correct (true)"); 1.272 + } 1.273 + 1.274 + nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(appShell); 1.275 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.276 + fail("Failed to dispatch next test runnable"); 1.277 + } 1.278 + 1.279 + } 1.280 +#endif 1.281 + 1.282 + bool 1.283 + ScheduleTimer(nsIDOMWindowUtils* aWindowUtils) 1.284 + { 1.285 +#ifdef XP_WIN 1.286 + UINT_PTR timerId = SetTimer(nullptr, 0, 1000, (TIMERPROC)TimerCallback); 1.287 + if (!timerId) { 1.288 + fail("SetTimer failed!"); 1.289 + return false; 1.290 + } 1.291 + 1.292 + nsCOMPtr<nsIDOMWindowUtils> utils = aWindowUtils; 1.293 + utils.forget(&sWindowUtils); 1.294 + 1.295 + nsCOMPtr<nsIAppShell> appShell = mAppShell; 1.296 + appShell.forget(&sAppShell); 1.297 + 1.298 + return true; 1.299 +#else 1.300 + return false; 1.301 +#endif 1.302 + } 1.303 +}; 1.304 + 1.305 +nsIDOMWindowUtils* EventListener::sWindowUtils = nullptr; 1.306 +nsIAppShell* EventListener::sAppShell = nullptr; 1.307 + 1.308 +NS_IMPL_ISUPPORTS(EventListener, nsIDOMEventListener) 1.309 + 1.310 +already_AddRefed<nsIAppShell> 1.311 +GetAppShell() 1.312 +{ 1.313 + static const char* platforms[] = { 1.314 + "android", "mac", "gonk", "gtk", "qt", "win" 1.315 + }; 1.316 + 1.317 + NS_NAMED_LITERAL_CSTRING(contractPrefix, "@mozilla.org/widget/appshell/"); 1.318 + NS_NAMED_LITERAL_CSTRING(contractSuffix, ";1"); 1.319 + 1.320 + for (size_t index = 0; index < ArrayLength(platforms); index++) { 1.321 + nsAutoCString contractID(contractPrefix); 1.322 + contractID.AppendASCII(platforms[index]); 1.323 + contractID.Append(contractSuffix); 1.324 + 1.325 + nsCOMPtr<nsIAppShell> appShell = do_GetService(contractID.get()); 1.326 + if (appShell) { 1.327 + return appShell.forget(); 1.328 + } 1.329 + } 1.330 + 1.331 + return nullptr; 1.332 +} 1.333 + 1.334 +void 1.335 +Test1(nsIAppShell* aAppShell) 1.336 +{ 1.337 + // Schedule stable state runnable to be run before next event. 1.338 + 1.339 + nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable(); 1.340 + if (NS_FAILED(aAppShell->RunBeforeNextEvent(runnable))) { 1.341 + fail("RunBeforeNextEvent failed"); 1.342 + } 1.343 + 1.344 + runnable = new CheckStableStateRunnable(true); 1.345 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.346 + fail("Failed to dispatch check runnable"); 1.347 + } 1.348 + 1.349 + runnable = new NextTestRunnable(aAppShell); 1.350 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.351 + fail("Failed to dispatch next test runnable"); 1.352 + } 1.353 +} 1.354 + 1.355 +void 1.356 +Test2(nsIAppShell* aAppShell) 1.357 +{ 1.358 + // Schedule stable state runnable to be run before next event from another 1.359 + // runnable. 1.360 + 1.361 + nsCOMPtr<nsIRunnable> runnable = new ScheduleStableStateRunnable(aAppShell); 1.362 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.363 + fail("Failed to dispatch schedule runnable"); 1.364 + } 1.365 + 1.366 + runnable = new CheckStableStateRunnable(true); 1.367 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.368 + fail("Failed to dispatch check runnable"); 1.369 + } 1.370 + 1.371 + runnable = new NextTestRunnable(aAppShell); 1.372 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.373 + fail("Failed to dispatch next test runnable"); 1.374 + } 1.375 +} 1.376 + 1.377 +void 1.378 +Test3(nsIAppShell* aAppShell) 1.379 +{ 1.380 + // Schedule steadystate runnable to be run before next event with nested loop. 1.381 + 1.382 + nsCOMPtr<nsIRunnable> runnable = 1.383 + new ScheduleNestedStableStateRunnable(aAppShell); 1.384 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.385 + fail("Failed to dispatch schedule runnable"); 1.386 + } 1.387 +} 1.388 + 1.389 +bool 1.390 +Test4Internal(nsIAppShell* aAppShell) 1.391 +{ 1.392 +#ifndef XP_WIN 1.393 + // Not sure how to test on other platforms. 1.394 + return false; 1.395 +#endif 1.396 + 1.397 + nsCOMPtr<nsIAppShellService> appService = 1.398 + do_GetService(NS_APPSHELLSERVICE_CONTRACTID); 1.399 + if (!appService) { 1.400 + fail("Failed to get appshell service!"); 1.401 + return false; 1.402 + } 1.403 + 1.404 + nsCOMPtr<nsIURI> uri; 1.405 + if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), "about:", nullptr))) { 1.406 + fail("Failed to create new uri"); 1.407 + return false; 1.408 + } 1.409 + 1.410 + uint32_t flags = nsIWebBrowserChrome::CHROME_DEFAULT; 1.411 + 1.412 + nsCOMPtr<nsIXULWindow> xulWindow; 1.413 + if (NS_FAILED(appService->CreateTopLevelWindow(nullptr, uri, flags, 100, 100, 1.414 + getter_AddRefs(xulWindow)))) { 1.415 + fail("Failed to create new window"); 1.416 + return false; 1.417 + } 1.418 + 1.419 + nsCOMPtr<nsIDOMWindow> window = do_GetInterface(xulWindow); 1.420 + if (!window) { 1.421 + fail("Can't get dom window!"); 1.422 + return false; 1.423 + } 1.424 + 1.425 + nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(window); 1.426 + if (!target) { 1.427 + fail("Can't QI to nsIDOMEventTarget!"); 1.428 + return false; 1.429 + } 1.430 + 1.431 + nsCOMPtr<nsIDOMEventListener> listener = new EventListener(aAppShell); 1.432 + if (NS_FAILED(target->AddEventListener(NS_LITERAL_STRING("keypress"), 1.433 + listener, false, false)) || 1.434 + NS_FAILED(target->AddEventListener(NS_LITERAL_STRING("load"), listener, 1.435 + false, false))) { 1.436 + fail("Can't add event listeners!"); 1.437 + return false; 1.438 + } 1.439 + 1.440 + return true; 1.441 +} 1.442 + 1.443 +void 1.444 +Test4(nsIAppShell* aAppShell) 1.445 +{ 1.446 + if (!Test4Internal(aAppShell)) { 1.447 + nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(aAppShell); 1.448 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.449 + fail("Failed to dispatch next test runnable"); 1.450 + } 1.451 + } 1.452 +} 1.453 + 1.454 +const TestFunc gTests[] = { 1.455 + Test1, Test2, Test3, Test4 1.456 +}; 1.457 + 1.458 +size_t gTestIndex = 0; 1.459 + 1.460 +NS_IMETHODIMP 1.461 +NextTestRunnable::Run() 1.462 +{ 1.463 + if (gTestIndex > 0) { 1.464 + passed("Finished test %u", gTestIndex); 1.465 + } 1.466 + 1.467 + gStableStateEventHasRun = false; 1.468 + 1.469 + if (gTestIndex < ArrayLength(gTests)) { 1.470 + gTests[gTestIndex++](mAppShell); 1.471 + } 1.472 + else { 1.473 + nsCOMPtr<nsIRunnable> exitRunnable = new ExitAppShellRunnable(mAppShell); 1.474 + 1.475 + nsresult rv = NS_DispatchToCurrentThread(exitRunnable); 1.476 + if (NS_FAILED(rv)) { 1.477 + fail("Failed to dispatch exit runnable!"); 1.478 + } 1.479 + } 1.480 + 1.481 + return NS_OK; 1.482 +} 1.483 + 1.484 +int main(int argc, char** argv) 1.485 +{ 1.486 + ScopedLogging log; 1.487 + ScopedXPCOM xpcom("TestAppShellSteadyState"); 1.488 + 1.489 + if (!xpcom.failed()) { 1.490 + nsCOMPtr<nsIAppShell> appShell = GetAppShell(); 1.491 + if (!appShell) { 1.492 + fail("Couldn't get appshell!"); 1.493 + } else { 1.494 + nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(appShell); 1.495 + if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) { 1.496 + fail("Failed to dispatch next test runnable"); 1.497 + } else if (NS_FAILED(appShell->Run())) { 1.498 + fail("Failed to run appshell"); 1.499 + } 1.500 + } 1.501 + } 1.502 + 1.503 + return gFailCount != 0; 1.504 +}