widget/tests/TestAppShellSteadyState.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /**
     2  * Any copyright is dedicated to the Public Domain.
     3  * http://creativecommons.org/publicdomain/zero/1.0/
     4  */
     6 #include "TestHarness.h"
     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"
    22 #include "nsAppShellCID.h"
    23 #include "nsIInterfaceRequestorUtils.h"
    24 #include "nsNetUtil.h"
    25 #include "nsThreadUtils.h"
    26 #include "mozilla/Attributes.h"
    28 #ifdef XP_WIN
    29 #include <windows.h>
    30 #endif
    32 using namespace mozilla;
    34 typedef void (*TestFunc)(nsIAppShell*);
    36 bool gStableStateEventHasRun = false;
    38 class ExitAppShellRunnable : public nsRunnable
    39 {
    40   nsCOMPtr<nsIAppShell> mAppShell;
    42 public:
    43   ExitAppShellRunnable(nsIAppShell* aAppShell)
    44   : mAppShell(aAppShell)
    45   { }
    47   NS_IMETHOD
    48   Run()
    49   {
    50     return mAppShell->Exit();
    51   }
    52 };
    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 };
    68 class CheckStableStateRunnable : public nsRunnable
    69 {
    70   bool mShouldHaveRun;
    72 public:
    73   CheckStableStateRunnable(bool aShouldHaveRun)
    74   : mShouldHaveRun(aShouldHaveRun)
    75   { }
    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 };
    90 class ScheduleStableStateRunnable : public CheckStableStateRunnable
    91 {
    92 protected:
    93   nsCOMPtr<nsIAppShell> mAppShell;
    95 public:
    96   ScheduleStableStateRunnable(nsIAppShell* aAppShell)
    97   : CheckStableStateRunnable(false), mAppShell(aAppShell)
    98   { }
   100   NS_IMETHOD
   101   Run()
   102   {
   103     CheckStableStateRunnable::Run();
   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     }
   111     return rv;
   112   }
   113 };
   115 class NextTestRunnable : public nsRunnable
   116 {
   117   nsCOMPtr<nsIAppShell> mAppShell;
   119 public:
   120   NextTestRunnable(nsIAppShell* aAppShell)
   121   : mAppShell(aAppShell)
   122   { }
   124   NS_IMETHOD Run();
   125 };
   127 class ScheduleNestedStableStateRunnable : public ScheduleStableStateRunnable
   128 {
   129 public:
   130   ScheduleNestedStableStateRunnable(nsIAppShell* aAppShell)
   131   : ScheduleStableStateRunnable(aAppShell)
   132   { }
   134   NS_IMETHOD
   135   Run()
   136   {
   137     ScheduleStableStateRunnable::Run();
   139     nsCOMPtr<nsIRunnable> runnable = new CheckStableStateRunnable(false);
   140     if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
   141       fail("Failed to dispatch check runnable");
   142     }
   144     if (NS_FAILED(NS_ProcessPendingEvents(nullptr))) {
   145       fail("Failed to process all pending events");
   146     }
   148     runnable = new CheckStableStateRunnable(true);
   149     if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
   150       fail("Failed to dispatch check runnable");
   151     }
   153     runnable = new NextTestRunnable(mAppShell);
   154     if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
   155       fail("Failed to dispatch next test runnable");
   156     }
   158     return NS_OK;
   159   }
   160 };
   162 class EventListener MOZ_FINAL : public nsIDOMEventListener
   163 {
   164   nsCOMPtr<nsIAppShell> mAppShell;
   166   static nsIDOMWindowUtils* sWindowUtils;
   167   static nsIAppShell* sAppShell;
   169 public:
   170   NS_DECL_ISUPPORTS
   172   EventListener(nsIAppShell* aAppShell)
   173   : mAppShell(aAppShell)
   174   { }
   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     }
   185     if (type.EqualsLiteral("load")) {
   186       passed("Got load event");
   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       }
   194       nsCOMPtr<nsIDocument> document = do_QueryInterface(target);
   195       if (!document) {
   196         fail("Failed to QI to nsIDocument!");
   197         return NS_ERROR_FAILURE;
   198       }
   200       nsCOMPtr<nsPIDOMWindow> window = document->GetWindow();
   201       if (!window) {
   202         fail("Failed to get window from document!");
   203         return NS_ERROR_FAILURE;
   204       }
   206       nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
   207       if (!utils) {
   208         fail("Failed to get DOMWindowUtils!");
   209         return NS_ERROR_FAILURE;
   210       }
   212       if (!ScheduleTimer(utils)) {
   213         return NS_ERROR_FAILURE;
   214       }
   216       return NS_OK;
   217     }
   219     if (type.EqualsLiteral("keypress")) {
   220       passed("Got keypress event");
   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       }
   229       return NS_OK;
   230     }
   232     fail("Got an unexpected event: %s", NS_ConvertUTF16toUTF8(type).get());
   233     return NS_OK;
   234   }
   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;
   244       if (gStableStateEventHasRun) {
   245         fail("StableStateRunnable ran at wrong time");
   246       } else {
   247         passed("StableStateRunnable state correct (false)");
   248       }
   250       int32_t layout = 0x409; // US
   251       int32_t keyCode = 0x41; // VK_A
   252       NS_NAMED_LITERAL_STRING(a, "a");
   254       if (NS_FAILED(utils->SendNativeKeyEvent(layout, keyCode, 0, a, a))) {
   255         fail("Failed to synthesize native event");
   256       }
   258       return;
   259     }
   261     KillTimer(nullptr, idEvent);
   263     nsCOMPtr<nsIAppShell> appShell = dont_AddRef(sAppShell);
   265     if (!gStableStateEventHasRun) {
   266       fail("StableStateRunnable didn't run yet");
   267     } else {
   268       passed("StableStateRunnable state correct (true)");
   269     }
   271     nsCOMPtr<nsIRunnable> runnable = new NextTestRunnable(appShell);
   272     if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
   273       fail("Failed to dispatch next test runnable");
   274     }
   276   }
   277 #endif
   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     }
   289     nsCOMPtr<nsIDOMWindowUtils> utils = aWindowUtils;
   290     utils.forget(&sWindowUtils);
   292     nsCOMPtr<nsIAppShell> appShell = mAppShell;
   293     appShell.forget(&sAppShell);
   295     return true;
   296 #else
   297     return false;
   298 #endif
   299   }
   300 };
   302 nsIDOMWindowUtils* EventListener::sWindowUtils = nullptr;
   303 nsIAppShell* EventListener::sAppShell = nullptr;
   305 NS_IMPL_ISUPPORTS(EventListener, nsIDOMEventListener)
   307 already_AddRefed<nsIAppShell>
   308 GetAppShell()
   309 {
   310   static const char* platforms[] = {
   311     "android", "mac", "gonk", "gtk", "qt", "win"
   312   };
   314   NS_NAMED_LITERAL_CSTRING(contractPrefix, "@mozilla.org/widget/appshell/");
   315   NS_NAMED_LITERAL_CSTRING(contractSuffix, ";1");
   317   for (size_t index = 0; index < ArrayLength(platforms); index++) {
   318     nsAutoCString contractID(contractPrefix);
   319     contractID.AppendASCII(platforms[index]);
   320     contractID.Append(contractSuffix);
   322     nsCOMPtr<nsIAppShell> appShell = do_GetService(contractID.get());
   323     if (appShell) {
   324       return appShell.forget();
   325     }
   326   }
   328   return nullptr;
   329 }
   331 void
   332 Test1(nsIAppShell* aAppShell)
   333 {
   334   // Schedule stable state runnable to be run before next event.
   336   nsCOMPtr<nsIRunnable> runnable = new StableStateRunnable();
   337   if (NS_FAILED(aAppShell->RunBeforeNextEvent(runnable))) {
   338     fail("RunBeforeNextEvent failed");
   339   }
   341   runnable = new CheckStableStateRunnable(true);
   342   if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
   343     fail("Failed to dispatch check runnable");
   344   }
   346   runnable = new NextTestRunnable(aAppShell);
   347   if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
   348     fail("Failed to dispatch next test runnable");
   349   }
   350 }
   352 void
   353 Test2(nsIAppShell* aAppShell)
   354 {
   355   // Schedule stable state runnable to be run before next event from another
   356   // runnable.
   358   nsCOMPtr<nsIRunnable> runnable = new ScheduleStableStateRunnable(aAppShell);
   359   if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
   360     fail("Failed to dispatch schedule runnable");
   361   }
   363   runnable = new CheckStableStateRunnable(true);
   364   if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
   365     fail("Failed to dispatch check runnable");
   366   }
   368   runnable = new NextTestRunnable(aAppShell);
   369   if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
   370     fail("Failed to dispatch next test runnable");
   371   }
   372 }
   374 void
   375 Test3(nsIAppShell* aAppShell)
   376 {
   377   // Schedule steadystate runnable to be run before next event with nested loop.
   379   nsCOMPtr<nsIRunnable> runnable =
   380     new ScheduleNestedStableStateRunnable(aAppShell);
   381   if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
   382     fail("Failed to dispatch schedule runnable");
   383   }
   384 }
   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
   394   nsCOMPtr<nsIAppShellService> appService =
   395     do_GetService(NS_APPSHELLSERVICE_CONTRACTID);
   396   if (!appService) {
   397     fail("Failed to get appshell service!");
   398     return false;
   399   }
   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   }
   407   uint32_t flags = nsIWebBrowserChrome::CHROME_DEFAULT;
   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   }
   416   nsCOMPtr<nsIDOMWindow> window = do_GetInterface(xulWindow);
   417   if (!window) {
   418     fail("Can't get dom window!");
   419     return false;
   420   }
   422   nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(window);
   423   if (!target) {
   424     fail("Can't QI to nsIDOMEventTarget!");
   425     return false;
   426   }
   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   }
   437   return true;
   438 }
   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 }
   451 const TestFunc gTests[] = {
   452   Test1, Test2, Test3, Test4
   453 };
   455 size_t gTestIndex = 0;
   457 NS_IMETHODIMP
   458 NextTestRunnable::Run()
   459 {
   460   if (gTestIndex > 0) {
   461     passed("Finished test %u", gTestIndex);
   462   }
   464   gStableStateEventHasRun = false;
   466   if (gTestIndex < ArrayLength(gTests)) {
   467     gTests[gTestIndex++](mAppShell);
   468   }
   469   else {
   470     nsCOMPtr<nsIRunnable> exitRunnable = new ExitAppShellRunnable(mAppShell);
   472     nsresult rv = NS_DispatchToCurrentThread(exitRunnable);
   473     if (NS_FAILED(rv)) {
   474       fail("Failed to dispatch exit runnable!");
   475     }
   476   }
   478   return NS_OK;
   479 }
   481 int main(int argc, char** argv)
   482 {
   483   ScopedLogging log;
   484   ScopedXPCOM xpcom("TestAppShellSteadyState");
   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   }
   500   return gFailCount != 0;
   501 }

mercurial