widget/windows/winrt/MetroAppShell.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.

michael@0 1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "MetroAppShell.h"
michael@0 7
michael@0 8 #include "mozilla/AutoRestore.h"
michael@0 9 #include "mozilla/TimeStamp.h"
michael@0 10 #include "mozilla/widget/AudioSession.h"
michael@0 11
michael@0 12 #include "nsIObserverService.h"
michael@0 13 #include "nsIAppStartup.h"
michael@0 14 #include "nsToolkitCompsCID.h"
michael@0 15 #include "nsIPowerManagerService.h"
michael@0 16
michael@0 17 #include "nsXULAppAPI.h"
michael@0 18 #include "nsServiceManagerUtils.h"
michael@0 19 #include "WinUtils.h"
michael@0 20 #include "nsWinMetroUtils.h"
michael@0 21 #include "MetroUtils.h"
michael@0 22 #include "MetroApp.h"
michael@0 23 #include "FrameworkView.h"
michael@0 24 #include "WakeLockListener.h"
michael@0 25
michael@0 26 #include <shellapi.h>
michael@0 27
michael@0 28 using namespace mozilla;
michael@0 29 using namespace mozilla::widget;
michael@0 30 using namespace mozilla::widget::winrt;
michael@0 31 using namespace Microsoft::WRL;
michael@0 32 using namespace Microsoft::WRL::Wrappers;
michael@0 33 using namespace ABI::Windows::UI::Core;
michael@0 34 using namespace ABI::Windows::Foundation;
michael@0 35
michael@0 36 // ProcessNextNativeEvent message wait timeout, see bug 907410.
michael@0 37 #define MSG_WAIT_TIMEOUT 250
michael@0 38 // MetroInput will occasionally ask us to flush all input so that the dom is
michael@0 39 // up to date. This is the maximum amount of time we'll agree to spend in
michael@0 40 // NS_ProcessPendingEvents.
michael@0 41 #define PURGE_MAX_TIMEOUT 50
michael@0 42
michael@0 43 namespace mozilla {
michael@0 44 namespace widget {
michael@0 45 namespace winrt {
michael@0 46 extern ComPtr<MetroApp> sMetroApp;
michael@0 47 } } }
michael@0 48
michael@0 49 namespace mozilla {
michael@0 50 namespace widget {
michael@0 51 // pulled from win32 app shell
michael@0 52 extern UINT sAppShellGeckoMsgId;
michael@0 53 } }
michael@0 54
michael@0 55 static ComPtr<ICoreWindowStatic> sCoreStatic;
michael@0 56 static bool sIsDispatching = false;
michael@0 57 static bool sShouldPurgeThreadQueue = false;
michael@0 58 static bool sBlockNativeEvents = false;
michael@0 59 static TimeStamp sPurgeThreadQueueStart;
michael@0 60
michael@0 61 MetroAppShell::~MetroAppShell()
michael@0 62 {
michael@0 63 if (mEventWnd) {
michael@0 64 SendMessage(mEventWnd, WM_CLOSE, 0, 0);
michael@0 65 }
michael@0 66 }
michael@0 67
michael@0 68 nsresult
michael@0 69 MetroAppShell::Init()
michael@0 70 {
michael@0 71 LogFunction();
michael@0 72
michael@0 73 WNDCLASSW wc;
michael@0 74 HINSTANCE module = GetModuleHandle(nullptr);
michael@0 75
michael@0 76 const char16_t *const kWindowClass = L"nsAppShell:EventWindowClass";
michael@0 77 if (!GetClassInfoW(module, kWindowClass, &wc)) {
michael@0 78 wc.style = 0;
michael@0 79 wc.lpfnWndProc = EventWindowProc;
michael@0 80 wc.cbClsExtra = 0;
michael@0 81 wc.cbWndExtra = 0;
michael@0 82 wc.hInstance = module;
michael@0 83 wc.hIcon = nullptr;
michael@0 84 wc.hCursor = nullptr;
michael@0 85 wc.hbrBackground = (HBRUSH) nullptr;
michael@0 86 wc.lpszMenuName = (LPCWSTR) nullptr;
michael@0 87 wc.lpszClassName = kWindowClass;
michael@0 88 RegisterClassW(&wc);
michael@0 89 }
michael@0 90
michael@0 91 mEventWnd = CreateWindowW(kWindowClass, L"nsAppShell:EventWindow",
michael@0 92 0, 0, 0, 10, 10, nullptr, nullptr, module, nullptr);
michael@0 93 NS_ENSURE_STATE(mEventWnd);
michael@0 94
michael@0 95 nsresult rv;
michael@0 96 nsCOMPtr<nsIObserverService> observerService =
michael@0 97 do_GetService("@mozilla.org/observer-service;1", &rv);
michael@0 98 if (NS_SUCCEEDED(rv)) {
michael@0 99 observerService->AddObserver(this, "dl-start", false);
michael@0 100 observerService->AddObserver(this, "dl-done", false);
michael@0 101 observerService->AddObserver(this, "dl-cancel", false);
michael@0 102 observerService->AddObserver(this, "dl-failed", false);
michael@0 103 }
michael@0 104
michael@0 105 return nsBaseAppShell::Init();
michael@0 106 }
michael@0 107
michael@0 108 HRESULT
michael@0 109 SHCreateShellItemArrayFromShellItemDynamic(IShellItem *psi, REFIID riid, void **ppv)
michael@0 110 {
michael@0 111 HMODULE shell32DLL = LoadLibraryW(L"shell32.dll");
michael@0 112 if (!shell32DLL) {
michael@0 113 return E_FAIL;
michael@0 114 }
michael@0 115
michael@0 116 typedef BOOL (WINAPI* SHFn)(IShellItem *psi, REFIID riid, void **ppv);
michael@0 117
michael@0 118 HRESULT hr = E_FAIL;
michael@0 119 SHFn SHCreateShellItemArrayFromShellItemDynamicPtr =
michael@0 120 (SHFn)GetProcAddress(shell32DLL, "SHCreateShellItemArrayFromShellItem");
michael@0 121 FreeLibrary(shell32DLL);
michael@0 122 if (SHCreateShellItemArrayFromShellItemDynamicPtr) {
michael@0 123 hr = SHCreateShellItemArrayFromShellItemDynamicPtr(psi, riid, ppv);
michael@0 124 }
michael@0 125
michael@0 126 FreeLibrary(shell32DLL);
michael@0 127 return hr;
michael@0 128 }
michael@0 129
michael@0 130 HRESULT
michael@0 131 WinLaunchDeferredMetroFirefox()
michael@0 132 {
michael@0 133 // Create an instance of the Firefox Metro CEH which is used to launch the browser
michael@0 134 const CLSID CLSID_FirefoxMetroDEH = {0x5100FEC1,0x212B, 0x4BF5 ,{0x9B,0xF8, 0x3E,0x65, 0x0F,0xD7,0x94,0xA3}};
michael@0 135
michael@0 136 nsRefPtr<IExecuteCommand> executeCommand;
michael@0 137 HRESULT hr = CoCreateInstance(CLSID_FirefoxMetroDEH,
michael@0 138 nullptr,
michael@0 139 CLSCTX_LOCAL_SERVER,
michael@0 140 IID_IExecuteCommand,
michael@0 141 getter_AddRefs(executeCommand));
michael@0 142 if (FAILED(hr))
michael@0 143 return hr;
michael@0 144
michael@0 145 // Get the currently running exe path
michael@0 146 WCHAR exePath[MAX_PATH + 1] = { L'\0' };
michael@0 147 if (!::GetModuleFileNameW(0, exePath, MAX_PATH))
michael@0 148 return hr;
michael@0 149
michael@0 150 // Convert the path to a long path since GetModuleFileNameW returns the path
michael@0 151 // that was used to launch Firefox which is not necessarily a long path.
michael@0 152 if (!::GetLongPathNameW(exePath, exePath, MAX_PATH))
michael@0 153 return hr;
michael@0 154
michael@0 155 // Create an IShellItem for the current browser path
michael@0 156 nsRefPtr<IShellItem> shellItem;
michael@0 157 hr = WinUtils::SHCreateItemFromParsingName(exePath, nullptr, IID_IShellItem,
michael@0 158 getter_AddRefs(shellItem));
michael@0 159 if (FAILED(hr))
michael@0 160 return hr;
michael@0 161
michael@0 162 // Convert to an IShellItemArray which is used for the path to launch
michael@0 163 nsRefPtr<IShellItemArray> shellItemArray;
michael@0 164 hr = SHCreateShellItemArrayFromShellItemDynamic(shellItem, IID_IShellItemArray, getter_AddRefs(shellItemArray));
michael@0 165 if (FAILED(hr))
michael@0 166 return hr;
michael@0 167
michael@0 168 // Set the path to launch and parameters needed
michael@0 169 nsRefPtr<IObjectWithSelection> selection;
michael@0 170 hr = executeCommand->QueryInterface(IID_IObjectWithSelection, getter_AddRefs(selection));
michael@0 171 if (FAILED(hr))
michael@0 172 return hr;
michael@0 173 hr = selection->SetSelection(shellItemArray);
michael@0 174 if (FAILED(hr))
michael@0 175 return hr;
michael@0 176
michael@0 177 if (nsWinMetroUtils::sUpdatePending) {
michael@0 178 hr = executeCommand->SetParameters(L"--metro-update");
michael@0 179 } else {
michael@0 180 hr = executeCommand->SetParameters(L"--metro-restart");
michael@0 181 }
michael@0 182 if (FAILED(hr))
michael@0 183 return hr;
michael@0 184
michael@0 185 // Run the default browser through the CEH
michael@0 186 return executeCommand->Execute();
michael@0 187 }
michael@0 188
michael@0 189 static WakeLockListener*
michael@0 190 InitWakeLock()
michael@0 191 {
michael@0 192 nsCOMPtr<nsIPowerManagerService> powerManagerService =
michael@0 193 do_GetService(POWERMANAGERSERVICE_CONTRACTID);
michael@0 194 if (powerManagerService) {
michael@0 195 WakeLockListener* pLock = new WakeLockListener();
michael@0 196 powerManagerService->AddWakeLockListener(pLock);
michael@0 197 return pLock;
michael@0 198 }
michael@0 199 else {
michael@0 200 NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!");
michael@0 201 }
michael@0 202 return nullptr;
michael@0 203 }
michael@0 204
michael@0 205 static void
michael@0 206 ShutdownWakeLock(WakeLockListener* aLock)
michael@0 207 {
michael@0 208 if (!aLock) {
michael@0 209 return;
michael@0 210 }
michael@0 211 nsCOMPtr<nsIPowerManagerService> powerManagerService =
michael@0 212 do_GetService(POWERMANAGERSERVICE_CONTRACTID);
michael@0 213 if (powerManagerService) {
michael@0 214 powerManagerService->RemoveWakeLockListener(aLock);
michael@0 215 }
michael@0 216 }
michael@0 217
michael@0 218 // Called by appstartup->run in xre, which is initiated by a call to
michael@0 219 // XRE_metroStartup in MetroApp. This call is on the metro main thread.
michael@0 220 NS_IMETHODIMP
michael@0 221 MetroAppShell::Run(void)
michael@0 222 {
michael@0 223 LogFunction();
michael@0 224 nsresult rv = NS_OK;
michael@0 225
michael@0 226 switch(XRE_GetProcessType()) {
michael@0 227 case GeckoProcessType_Content:
michael@0 228 case GeckoProcessType_IPDLUnitTest:
michael@0 229 mozilla::widget::StartAudioSession();
michael@0 230 rv = nsBaseAppShell::Run();
michael@0 231 mozilla::widget::StopAudioSession();
michael@0 232 break;
michael@0 233 case GeckoProcessType_Plugin:
michael@0 234 NS_WARNING("We don't support plugins currently.");
michael@0 235 // Just exit
michael@0 236 rv = NS_ERROR_NOT_IMPLEMENTED;
michael@0 237 break;
michael@0 238 case GeckoProcessType_Default: {
michael@0 239 {
michael@0 240 nsRefPtr<WakeLockListener> wakeLock = InitWakeLock();
michael@0 241 mozilla::widget::StartAudioSession();
michael@0 242 sMetroApp->ActivateBaseView();
michael@0 243 rv = nsBaseAppShell::Run();
michael@0 244 mozilla::widget::StopAudioSession();
michael@0 245 ShutdownWakeLock(wakeLock);
michael@0 246 }
michael@0 247
michael@0 248 nsCOMPtr<nsIAppStartup> appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID));
michael@0 249 bool restartingInMetro = false, restartingInDesktop = false;
michael@0 250
michael@0 251 if (!appStartup || NS_FAILED(appStartup->GetRestarting(&restartingInDesktop))) {
michael@0 252 WinUtils::Log("appStartup->GetRestarting() unsuccessful");
michael@0 253 }
michael@0 254
michael@0 255 if (appStartup && NS_SUCCEEDED(appStartup->GetRestartingTouchEnvironment(&restartingInMetro)) &&
michael@0 256 restartingInMetro) {
michael@0 257 restartingInDesktop = false;
michael@0 258 }
michael@0 259
michael@0 260 // This calls XRE_metroShutdown() in xre. Shuts down gecko, including
michael@0 261 // releasing the profile, and destroys MessagePump.
michael@0 262 sMetroApp->Shutdown();
michael@0 263
michael@0 264 // Handle update restart or browser switch requests
michael@0 265 if (restartingInDesktop) {
michael@0 266 WinUtils::Log("Relaunching desktop browser");
michael@0 267 // We can't call into the ceh to do this. Microsoft prevents switching to
michael@0 268 // desktop unless we go through shell execute.
michael@0 269 SHELLEXECUTEINFOW sinfo;
michael@0 270 memset(&sinfo, 0, sizeof(SHELLEXECUTEINFOW));
michael@0 271 sinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
michael@0 272 // Per microsoft's metro style enabled desktop browser documentation
michael@0 273 // SEE_MASK_FLAG_LOG_USAGE is needed if we want to change from immersive
michael@0 274 // mode to desktop mode.
michael@0 275 sinfo.fMask = SEE_MASK_FLAG_LOG_USAGE;
michael@0 276 // The ceh will filter out this fake target.
michael@0 277 sinfo.lpFile = L"http://-desktop/";
michael@0 278 sinfo.lpVerb = L"open";
michael@0 279 sinfo.lpParameters = L"--desktop-restart";
michael@0 280 sinfo.nShow = SW_SHOWNORMAL;
michael@0 281 ShellExecuteEx(&sinfo);
michael@0 282 } else if (restartingInMetro) {
michael@0 283 HRESULT hresult = WinLaunchDeferredMetroFirefox();
michael@0 284 WinUtils::Log("Relaunching metro browser (hr=%X)", hresult);
michael@0 285 }
michael@0 286
michael@0 287 // This will free the real main thread in CoreApplication::Run()
michael@0 288 // once winrt cleans up this thread.
michael@0 289 sMetroApp->CoreExit();
michael@0 290 }
michael@0 291 break;
michael@0 292 }
michael@0 293
michael@0 294 return rv;
michael@0 295 }
michael@0 296
michael@0 297 // Called in certain cases where we have async input events in the thread
michael@0 298 // queue and need to make sure they get dispatched before the stack unwinds.
michael@0 299 void // static
michael@0 300 MetroAppShell::MarkEventQueueForPurge()
michael@0 301 {
michael@0 302 sShouldPurgeThreadQueue = true;
michael@0 303
michael@0 304 // If we're dispatching native events, wait until the dispatcher is
michael@0 305 // off the stack.
michael@0 306 if (sIsDispatching) {
michael@0 307 return;
michael@0 308 }
michael@0 309
michael@0 310 // Safe to process pending events now
michael@0 311 DispatchAllGeckoEvents();
michael@0 312 }
michael@0 313
michael@0 314 // Notification from MetroInput that all events it wanted delivered
michael@0 315 // have been dispatched. It is safe to start processing windowing
michael@0 316 // events.
michael@0 317 void // static
michael@0 318 MetroAppShell::InputEventsDispatched()
michael@0 319 {
michael@0 320 sBlockNativeEvents = false;
michael@0 321 }
michael@0 322
michael@0 323 // static
michael@0 324 void
michael@0 325 MetroAppShell::DispatchAllGeckoEvents()
michael@0 326 {
michael@0 327 // Only do this if requested and when we're not shutting down
michael@0 328 if (!sShouldPurgeThreadQueue || MetroApp::sGeckoShuttingDown) {
michael@0 329 return;
michael@0 330 }
michael@0 331
michael@0 332 NS_ASSERTION(NS_IsMainThread(), "DispatchAllGeckoEvents should be called on the main thread");
michael@0 333
michael@0 334 sShouldPurgeThreadQueue = false;
michael@0 335 sPurgeThreadQueueStart = TimeStamp::Now();
michael@0 336
michael@0 337 sBlockNativeEvents = true;
michael@0 338 nsIThread *thread = NS_GetCurrentThread();
michael@0 339 NS_ProcessPendingEvents(thread, PURGE_MAX_TIMEOUT);
michael@0 340 sBlockNativeEvents = false;
michael@0 341 }
michael@0 342
michael@0 343 static void
michael@0 344 ProcessNativeEvents(CoreProcessEventsOption eventOption)
michael@0 345 {
michael@0 346 HRESULT hr;
michael@0 347 if (!sCoreStatic) {
michael@0 348 hr = GetActivationFactory(HStringReference(L"Windows.UI.Core.CoreWindow").Get(), sCoreStatic.GetAddressOf());
michael@0 349 NS_ASSERTION(SUCCEEDED(hr), "GetActivationFactory failed?");
michael@0 350 AssertHRESULT(hr);
michael@0 351 }
michael@0 352
michael@0 353 ComPtr<ICoreWindow> window;
michael@0 354 AssertHRESULT(sCoreStatic->GetForCurrentThread(window.GetAddressOf()));
michael@0 355 ComPtr<ICoreDispatcher> dispatcher;
michael@0 356 hr = window->get_Dispatcher(&dispatcher);
michael@0 357 NS_ASSERTION(SUCCEEDED(hr), "get_Dispatcher failed?");
michael@0 358 AssertHRESULT(hr);
michael@0 359 dispatcher->ProcessEvents(eventOption);
michael@0 360 }
michael@0 361
michael@0 362 // static
michael@0 363 bool
michael@0 364 MetroAppShell::ProcessOneNativeEventIfPresent()
michael@0 365 {
michael@0 366 if (sIsDispatching) {
michael@0 367 // Calling into ProcessNativeEvents is harmless, but won't actually process any
michael@0 368 // native events. So we log here so we can spot this and get a handle on the
michael@0 369 // corner cases where this can happen.
michael@0 370 WinUtils::Log("WARNING: Reentrant call into process events detected, returning early.");
michael@0 371 return false;
michael@0 372 }
michael@0 373
michael@0 374 {
michael@0 375 AutoRestore<bool> dispatching(sIsDispatching);
michael@0 376 sIsDispatching = true;
michael@0 377 ProcessNativeEvents(CoreProcessEventsOption::CoreProcessEventsOption_ProcessOneIfPresent);
michael@0 378 }
michael@0 379
michael@0 380 DispatchAllGeckoEvents();
michael@0 381
michael@0 382 return !!HIWORD(::GetQueueStatus(MOZ_QS_ALLEVENT));
michael@0 383 }
michael@0 384
michael@0 385 bool
michael@0 386 MetroAppShell::ProcessNextNativeEvent(bool mayWait)
michael@0 387 {
michael@0 388 // NS_ProcessPendingEvents will process thread events *and* call
michael@0 389 // nsBaseAppShell::OnProcessNextEvent to process native events. However
michael@0 390 // we do not want native events getting dispatched while we are trying
michael@0 391 // to dispatch pending input in DispatchAllGeckoEvents since a native
michael@0 392 // event may be a UIA Automation call coming in to check focus.
michael@0 393 if (sBlockNativeEvents) {
michael@0 394 if ((TimeStamp::Now() - sPurgeThreadQueueStart).ToMilliseconds()
michael@0 395 < PURGE_MAX_TIMEOUT) {
michael@0 396 return false;
michael@0 397 }
michael@0 398 sBlockNativeEvents = false;
michael@0 399 }
michael@0 400
michael@0 401 if (ProcessOneNativeEventIfPresent()) {
michael@0 402 return true;
michael@0 403 }
michael@0 404 if (mayWait) {
michael@0 405 DWORD result = ::MsgWaitForMultipleObjectsEx(0, nullptr, MSG_WAIT_TIMEOUT,
michael@0 406 MOZ_QS_ALLEVENT,
michael@0 407 MWMO_INPUTAVAILABLE|MWMO_ALERTABLE);
michael@0 408 NS_WARN_IF_FALSE(result != WAIT_FAILED, "Wait failed");
michael@0 409 }
michael@0 410 return ProcessOneNativeEventIfPresent();
michael@0 411 }
michael@0 412
michael@0 413 void
michael@0 414 MetroAppShell::NativeCallback()
michael@0 415 {
michael@0 416 NS_ASSERTION(NS_IsMainThread(), "Native callbacks must be on the metro main thread");
michael@0 417
michael@0 418 // We shouldn't process native events during xpcom shutdown - this can
michael@0 419 // trigger unexpected xpcom event dispatching for the main thread when
michael@0 420 // the thread manager is in the process of shutting down non-main threads,
michael@0 421 // resulting in shutdown hangs.
michael@0 422 if (MetroApp::sGeckoShuttingDown) {
michael@0 423 return;
michael@0 424 }
michael@0 425
michael@0 426 NativeEventCallback();
michael@0 427 }
michael@0 428
michael@0 429 // static
michael@0 430 LRESULT CALLBACK
michael@0 431 MetroAppShell::EventWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
michael@0 432 {
michael@0 433 if (uMsg == sAppShellGeckoMsgId) {
michael@0 434 MetroAppShell *as = reinterpret_cast<MetroAppShell *>(lParam);
michael@0 435 as->NativeCallback();
michael@0 436 NS_RELEASE(as);
michael@0 437 return TRUE;
michael@0 438 }
michael@0 439 return DefWindowProc(hwnd, uMsg, wParam, lParam);
michael@0 440 }
michael@0 441
michael@0 442 void
michael@0 443 MetroAppShell::ScheduleNativeEventCallback()
michael@0 444 {
michael@0 445 NS_ADDREF_THIS();
michael@0 446 PostMessage(mEventWnd, sAppShellGeckoMsgId, 0, reinterpret_cast<LPARAM>(this));
michael@0 447 }
michael@0 448
michael@0 449 void
michael@0 450 MetroAppShell::DoProcessMoreGeckoEvents()
michael@0 451 {
michael@0 452 ScheduleNativeEventCallback();
michael@0 453 }
michael@0 454
michael@0 455 static HANDLE
michael@0 456 PowerCreateRequestDyn(REASON_CONTEXT *context)
michael@0 457 {
michael@0 458 typedef HANDLE (WINAPI * PowerCreateRequestPtr)(REASON_CONTEXT *context);
michael@0 459 static HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
michael@0 460 static PowerCreateRequestPtr powerCreateRequest =
michael@0 461 (PowerCreateRequestPtr)GetProcAddress(kernel32, "PowerCreateRequest");
michael@0 462 if (!powerCreateRequest)
michael@0 463 return INVALID_HANDLE_VALUE;
michael@0 464 return powerCreateRequest(context);
michael@0 465 }
michael@0 466
michael@0 467 static BOOL
michael@0 468 PowerClearRequestDyn(HANDLE powerRequest, POWER_REQUEST_TYPE requestType)
michael@0 469 {
michael@0 470 typedef BOOL (WINAPI * PowerClearRequestPtr)(HANDLE powerRequest, POWER_REQUEST_TYPE requestType);
michael@0 471 static HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
michael@0 472 static PowerClearRequestPtr powerClearRequest =
michael@0 473 (PowerClearRequestPtr)GetProcAddress(kernel32, "PowerClearRequest");
michael@0 474 if (!powerClearRequest)
michael@0 475 return FALSE;
michael@0 476 return powerClearRequest(powerRequest, requestType);
michael@0 477 }
michael@0 478
michael@0 479 static BOOL
michael@0 480 PowerSetRequestDyn(HANDLE powerRequest, POWER_REQUEST_TYPE requestType)
michael@0 481 {
michael@0 482 typedef BOOL (WINAPI * PowerSetRequestPtr)(HANDLE powerRequest, POWER_REQUEST_TYPE requestType);
michael@0 483 static HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
michael@0 484 static PowerSetRequestPtr powerSetRequest =
michael@0 485 (PowerSetRequestPtr)GetProcAddress(kernel32, "PowerSetRequest");
michael@0 486 if (!powerSetRequest)
michael@0 487 return FALSE;
michael@0 488 return powerSetRequest(powerRequest, requestType);
michael@0 489 }
michael@0 490
michael@0 491 NS_IMETHODIMP
michael@0 492 MetroAppShell::Observe(nsISupports *subject, const char *topic,
michael@0 493 const char16_t *data)
michael@0 494 {
michael@0 495 NS_ENSURE_ARG_POINTER(topic);
michael@0 496 if (!strcmp(topic, "dl-start")) {
michael@0 497 if (mPowerRequestCount++ == 0) {
michael@0 498 WinUtils::Log("Download started - Disallowing suspend");
michael@0 499 REASON_CONTEXT context;
michael@0 500 context.Version = POWER_REQUEST_CONTEXT_VERSION;
michael@0 501 context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING;
michael@0 502 context.Reason.SimpleReasonString = L"downloading";
michael@0 503 mPowerRequest.own(PowerCreateRequestDyn(&context));
michael@0 504 PowerSetRequestDyn(mPowerRequest, PowerRequestExecutionRequired);
michael@0 505 }
michael@0 506 return NS_OK;
michael@0 507 } else if (!strcmp(topic, "dl-done") ||
michael@0 508 !strcmp(topic, "dl-cancel") ||
michael@0 509 !strcmp(topic, "dl-failed")) {
michael@0 510 if (--mPowerRequestCount == 0 && mPowerRequest) {
michael@0 511 WinUtils::Log("All downloads ended - Allowing suspend");
michael@0 512 PowerClearRequestDyn(mPowerRequest, PowerRequestExecutionRequired);
michael@0 513 mPowerRequest.reset();
michael@0 514 }
michael@0 515 return NS_OK;
michael@0 516 }
michael@0 517
michael@0 518 return nsBaseAppShell::Observe(subject, topic, data);
michael@0 519 }

mercurial