1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/windows/winrt/MetroAppShell.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,519 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "MetroAppShell.h" 1.10 + 1.11 +#include "mozilla/AutoRestore.h" 1.12 +#include "mozilla/TimeStamp.h" 1.13 +#include "mozilla/widget/AudioSession.h" 1.14 + 1.15 +#include "nsIObserverService.h" 1.16 +#include "nsIAppStartup.h" 1.17 +#include "nsToolkitCompsCID.h" 1.18 +#include "nsIPowerManagerService.h" 1.19 + 1.20 +#include "nsXULAppAPI.h" 1.21 +#include "nsServiceManagerUtils.h" 1.22 +#include "WinUtils.h" 1.23 +#include "nsWinMetroUtils.h" 1.24 +#include "MetroUtils.h" 1.25 +#include "MetroApp.h" 1.26 +#include "FrameworkView.h" 1.27 +#include "WakeLockListener.h" 1.28 + 1.29 +#include <shellapi.h> 1.30 + 1.31 +using namespace mozilla; 1.32 +using namespace mozilla::widget; 1.33 +using namespace mozilla::widget::winrt; 1.34 +using namespace Microsoft::WRL; 1.35 +using namespace Microsoft::WRL::Wrappers; 1.36 +using namespace ABI::Windows::UI::Core; 1.37 +using namespace ABI::Windows::Foundation; 1.38 + 1.39 +// ProcessNextNativeEvent message wait timeout, see bug 907410. 1.40 +#define MSG_WAIT_TIMEOUT 250 1.41 +// MetroInput will occasionally ask us to flush all input so that the dom is 1.42 +// up to date. This is the maximum amount of time we'll agree to spend in 1.43 +// NS_ProcessPendingEvents. 1.44 +#define PURGE_MAX_TIMEOUT 50 1.45 + 1.46 +namespace mozilla { 1.47 +namespace widget { 1.48 +namespace winrt { 1.49 +extern ComPtr<MetroApp> sMetroApp; 1.50 +} } } 1.51 + 1.52 +namespace mozilla { 1.53 +namespace widget { 1.54 +// pulled from win32 app shell 1.55 +extern UINT sAppShellGeckoMsgId; 1.56 +} } 1.57 + 1.58 +static ComPtr<ICoreWindowStatic> sCoreStatic; 1.59 +static bool sIsDispatching = false; 1.60 +static bool sShouldPurgeThreadQueue = false; 1.61 +static bool sBlockNativeEvents = false; 1.62 +static TimeStamp sPurgeThreadQueueStart; 1.63 + 1.64 +MetroAppShell::~MetroAppShell() 1.65 +{ 1.66 + if (mEventWnd) { 1.67 + SendMessage(mEventWnd, WM_CLOSE, 0, 0); 1.68 + } 1.69 +} 1.70 + 1.71 +nsresult 1.72 +MetroAppShell::Init() 1.73 +{ 1.74 + LogFunction(); 1.75 + 1.76 + WNDCLASSW wc; 1.77 + HINSTANCE module = GetModuleHandle(nullptr); 1.78 + 1.79 + const char16_t *const kWindowClass = L"nsAppShell:EventWindowClass"; 1.80 + if (!GetClassInfoW(module, kWindowClass, &wc)) { 1.81 + wc.style = 0; 1.82 + wc.lpfnWndProc = EventWindowProc; 1.83 + wc.cbClsExtra = 0; 1.84 + wc.cbWndExtra = 0; 1.85 + wc.hInstance = module; 1.86 + wc.hIcon = nullptr; 1.87 + wc.hCursor = nullptr; 1.88 + wc.hbrBackground = (HBRUSH) nullptr; 1.89 + wc.lpszMenuName = (LPCWSTR) nullptr; 1.90 + wc.lpszClassName = kWindowClass; 1.91 + RegisterClassW(&wc); 1.92 + } 1.93 + 1.94 + mEventWnd = CreateWindowW(kWindowClass, L"nsAppShell:EventWindow", 1.95 + 0, 0, 0, 10, 10, nullptr, nullptr, module, nullptr); 1.96 + NS_ENSURE_STATE(mEventWnd); 1.97 + 1.98 + nsresult rv; 1.99 + nsCOMPtr<nsIObserverService> observerService = 1.100 + do_GetService("@mozilla.org/observer-service;1", &rv); 1.101 + if (NS_SUCCEEDED(rv)) { 1.102 + observerService->AddObserver(this, "dl-start", false); 1.103 + observerService->AddObserver(this, "dl-done", false); 1.104 + observerService->AddObserver(this, "dl-cancel", false); 1.105 + observerService->AddObserver(this, "dl-failed", false); 1.106 + } 1.107 + 1.108 + return nsBaseAppShell::Init(); 1.109 +} 1.110 + 1.111 +HRESULT 1.112 +SHCreateShellItemArrayFromShellItemDynamic(IShellItem *psi, REFIID riid, void **ppv) 1.113 +{ 1.114 + HMODULE shell32DLL = LoadLibraryW(L"shell32.dll"); 1.115 + if (!shell32DLL) { 1.116 + return E_FAIL; 1.117 + } 1.118 + 1.119 + typedef BOOL (WINAPI* SHFn)(IShellItem *psi, REFIID riid, void **ppv); 1.120 + 1.121 + HRESULT hr = E_FAIL; 1.122 + SHFn SHCreateShellItemArrayFromShellItemDynamicPtr = 1.123 + (SHFn)GetProcAddress(shell32DLL, "SHCreateShellItemArrayFromShellItem"); 1.124 + FreeLibrary(shell32DLL); 1.125 + if (SHCreateShellItemArrayFromShellItemDynamicPtr) { 1.126 + hr = SHCreateShellItemArrayFromShellItemDynamicPtr(psi, riid, ppv); 1.127 + } 1.128 + 1.129 + FreeLibrary(shell32DLL); 1.130 + return hr; 1.131 +} 1.132 + 1.133 +HRESULT 1.134 +WinLaunchDeferredMetroFirefox() 1.135 +{ 1.136 + // Create an instance of the Firefox Metro CEH which is used to launch the browser 1.137 + const CLSID CLSID_FirefoxMetroDEH = {0x5100FEC1,0x212B, 0x4BF5 ,{0x9B,0xF8, 0x3E,0x65, 0x0F,0xD7,0x94,0xA3}}; 1.138 + 1.139 + nsRefPtr<IExecuteCommand> executeCommand; 1.140 + HRESULT hr = CoCreateInstance(CLSID_FirefoxMetroDEH, 1.141 + nullptr, 1.142 + CLSCTX_LOCAL_SERVER, 1.143 + IID_IExecuteCommand, 1.144 + getter_AddRefs(executeCommand)); 1.145 + if (FAILED(hr)) 1.146 + return hr; 1.147 + 1.148 + // Get the currently running exe path 1.149 + WCHAR exePath[MAX_PATH + 1] = { L'\0' }; 1.150 + if (!::GetModuleFileNameW(0, exePath, MAX_PATH)) 1.151 + return hr; 1.152 + 1.153 + // Convert the path to a long path since GetModuleFileNameW returns the path 1.154 + // that was used to launch Firefox which is not necessarily a long path. 1.155 + if (!::GetLongPathNameW(exePath, exePath, MAX_PATH)) 1.156 + return hr; 1.157 + 1.158 + // Create an IShellItem for the current browser path 1.159 + nsRefPtr<IShellItem> shellItem; 1.160 + hr = WinUtils::SHCreateItemFromParsingName(exePath, nullptr, IID_IShellItem, 1.161 + getter_AddRefs(shellItem)); 1.162 + if (FAILED(hr)) 1.163 + return hr; 1.164 + 1.165 + // Convert to an IShellItemArray which is used for the path to launch 1.166 + nsRefPtr<IShellItemArray> shellItemArray; 1.167 + hr = SHCreateShellItemArrayFromShellItemDynamic(shellItem, IID_IShellItemArray, getter_AddRefs(shellItemArray)); 1.168 + if (FAILED(hr)) 1.169 + return hr; 1.170 + 1.171 + // Set the path to launch and parameters needed 1.172 + nsRefPtr<IObjectWithSelection> selection; 1.173 + hr = executeCommand->QueryInterface(IID_IObjectWithSelection, getter_AddRefs(selection)); 1.174 + if (FAILED(hr)) 1.175 + return hr; 1.176 + hr = selection->SetSelection(shellItemArray); 1.177 + if (FAILED(hr)) 1.178 + return hr; 1.179 + 1.180 + if (nsWinMetroUtils::sUpdatePending) { 1.181 + hr = executeCommand->SetParameters(L"--metro-update"); 1.182 + } else { 1.183 + hr = executeCommand->SetParameters(L"--metro-restart"); 1.184 + } 1.185 + if (FAILED(hr)) 1.186 + return hr; 1.187 + 1.188 + // Run the default browser through the CEH 1.189 + return executeCommand->Execute(); 1.190 +} 1.191 + 1.192 +static WakeLockListener* 1.193 +InitWakeLock() 1.194 +{ 1.195 + nsCOMPtr<nsIPowerManagerService> powerManagerService = 1.196 + do_GetService(POWERMANAGERSERVICE_CONTRACTID); 1.197 + if (powerManagerService) { 1.198 + WakeLockListener* pLock = new WakeLockListener(); 1.199 + powerManagerService->AddWakeLockListener(pLock); 1.200 + return pLock; 1.201 + } 1.202 + else { 1.203 + NS_WARNING("Failed to retrieve PowerManagerService, wakelocks will be broken!"); 1.204 + } 1.205 + return nullptr; 1.206 +} 1.207 + 1.208 +static void 1.209 +ShutdownWakeLock(WakeLockListener* aLock) 1.210 +{ 1.211 + if (!aLock) { 1.212 + return; 1.213 + } 1.214 + nsCOMPtr<nsIPowerManagerService> powerManagerService = 1.215 + do_GetService(POWERMANAGERSERVICE_CONTRACTID); 1.216 + if (powerManagerService) { 1.217 + powerManagerService->RemoveWakeLockListener(aLock); 1.218 + } 1.219 +} 1.220 + 1.221 +// Called by appstartup->run in xre, which is initiated by a call to 1.222 +// XRE_metroStartup in MetroApp. This call is on the metro main thread. 1.223 +NS_IMETHODIMP 1.224 +MetroAppShell::Run(void) 1.225 +{ 1.226 + LogFunction(); 1.227 + nsresult rv = NS_OK; 1.228 + 1.229 + switch(XRE_GetProcessType()) { 1.230 + case GeckoProcessType_Content: 1.231 + case GeckoProcessType_IPDLUnitTest: 1.232 + mozilla::widget::StartAudioSession(); 1.233 + rv = nsBaseAppShell::Run(); 1.234 + mozilla::widget::StopAudioSession(); 1.235 + break; 1.236 + case GeckoProcessType_Plugin: 1.237 + NS_WARNING("We don't support plugins currently."); 1.238 + // Just exit 1.239 + rv = NS_ERROR_NOT_IMPLEMENTED; 1.240 + break; 1.241 + case GeckoProcessType_Default: { 1.242 + { 1.243 + nsRefPtr<WakeLockListener> wakeLock = InitWakeLock(); 1.244 + mozilla::widget::StartAudioSession(); 1.245 + sMetroApp->ActivateBaseView(); 1.246 + rv = nsBaseAppShell::Run(); 1.247 + mozilla::widget::StopAudioSession(); 1.248 + ShutdownWakeLock(wakeLock); 1.249 + } 1.250 + 1.251 + nsCOMPtr<nsIAppStartup> appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID)); 1.252 + bool restartingInMetro = false, restartingInDesktop = false; 1.253 + 1.254 + if (!appStartup || NS_FAILED(appStartup->GetRestarting(&restartingInDesktop))) { 1.255 + WinUtils::Log("appStartup->GetRestarting() unsuccessful"); 1.256 + } 1.257 + 1.258 + if (appStartup && NS_SUCCEEDED(appStartup->GetRestartingTouchEnvironment(&restartingInMetro)) && 1.259 + restartingInMetro) { 1.260 + restartingInDesktop = false; 1.261 + } 1.262 + 1.263 + // This calls XRE_metroShutdown() in xre. Shuts down gecko, including 1.264 + // releasing the profile, and destroys MessagePump. 1.265 + sMetroApp->Shutdown(); 1.266 + 1.267 + // Handle update restart or browser switch requests 1.268 + if (restartingInDesktop) { 1.269 + WinUtils::Log("Relaunching desktop browser"); 1.270 + // We can't call into the ceh to do this. Microsoft prevents switching to 1.271 + // desktop unless we go through shell execute. 1.272 + SHELLEXECUTEINFOW sinfo; 1.273 + memset(&sinfo, 0, sizeof(SHELLEXECUTEINFOW)); 1.274 + sinfo.cbSize = sizeof(SHELLEXECUTEINFOW); 1.275 + // Per microsoft's metro style enabled desktop browser documentation 1.276 + // SEE_MASK_FLAG_LOG_USAGE is needed if we want to change from immersive 1.277 + // mode to desktop mode. 1.278 + sinfo.fMask = SEE_MASK_FLAG_LOG_USAGE; 1.279 + // The ceh will filter out this fake target. 1.280 + sinfo.lpFile = L"http://-desktop/"; 1.281 + sinfo.lpVerb = L"open"; 1.282 + sinfo.lpParameters = L"--desktop-restart"; 1.283 + sinfo.nShow = SW_SHOWNORMAL; 1.284 + ShellExecuteEx(&sinfo); 1.285 + } else if (restartingInMetro) { 1.286 + HRESULT hresult = WinLaunchDeferredMetroFirefox(); 1.287 + WinUtils::Log("Relaunching metro browser (hr=%X)", hresult); 1.288 + } 1.289 + 1.290 + // This will free the real main thread in CoreApplication::Run() 1.291 + // once winrt cleans up this thread. 1.292 + sMetroApp->CoreExit(); 1.293 + } 1.294 + break; 1.295 + } 1.296 + 1.297 + return rv; 1.298 +} 1.299 + 1.300 +// Called in certain cases where we have async input events in the thread 1.301 +// queue and need to make sure they get dispatched before the stack unwinds. 1.302 +void // static 1.303 +MetroAppShell::MarkEventQueueForPurge() 1.304 +{ 1.305 + sShouldPurgeThreadQueue = true; 1.306 + 1.307 + // If we're dispatching native events, wait until the dispatcher is 1.308 + // off the stack. 1.309 + if (sIsDispatching) { 1.310 + return; 1.311 + } 1.312 + 1.313 + // Safe to process pending events now 1.314 + DispatchAllGeckoEvents(); 1.315 +} 1.316 + 1.317 +// Notification from MetroInput that all events it wanted delivered 1.318 +// have been dispatched. It is safe to start processing windowing 1.319 +// events. 1.320 +void // static 1.321 +MetroAppShell::InputEventsDispatched() 1.322 +{ 1.323 + sBlockNativeEvents = false; 1.324 +} 1.325 + 1.326 +// static 1.327 +void 1.328 +MetroAppShell::DispatchAllGeckoEvents() 1.329 +{ 1.330 + // Only do this if requested and when we're not shutting down 1.331 + if (!sShouldPurgeThreadQueue || MetroApp::sGeckoShuttingDown) { 1.332 + return; 1.333 + } 1.334 + 1.335 + NS_ASSERTION(NS_IsMainThread(), "DispatchAllGeckoEvents should be called on the main thread"); 1.336 + 1.337 + sShouldPurgeThreadQueue = false; 1.338 + sPurgeThreadQueueStart = TimeStamp::Now(); 1.339 + 1.340 + sBlockNativeEvents = true; 1.341 + nsIThread *thread = NS_GetCurrentThread(); 1.342 + NS_ProcessPendingEvents(thread, PURGE_MAX_TIMEOUT); 1.343 + sBlockNativeEvents = false; 1.344 +} 1.345 + 1.346 +static void 1.347 +ProcessNativeEvents(CoreProcessEventsOption eventOption) 1.348 +{ 1.349 + HRESULT hr; 1.350 + if (!sCoreStatic) { 1.351 + hr = GetActivationFactory(HStringReference(L"Windows.UI.Core.CoreWindow").Get(), sCoreStatic.GetAddressOf()); 1.352 + NS_ASSERTION(SUCCEEDED(hr), "GetActivationFactory failed?"); 1.353 + AssertHRESULT(hr); 1.354 + } 1.355 + 1.356 + ComPtr<ICoreWindow> window; 1.357 + AssertHRESULT(sCoreStatic->GetForCurrentThread(window.GetAddressOf())); 1.358 + ComPtr<ICoreDispatcher> dispatcher; 1.359 + hr = window->get_Dispatcher(&dispatcher); 1.360 + NS_ASSERTION(SUCCEEDED(hr), "get_Dispatcher failed?"); 1.361 + AssertHRESULT(hr); 1.362 + dispatcher->ProcessEvents(eventOption); 1.363 +} 1.364 + 1.365 +// static 1.366 +bool 1.367 +MetroAppShell::ProcessOneNativeEventIfPresent() 1.368 +{ 1.369 + if (sIsDispatching) { 1.370 + // Calling into ProcessNativeEvents is harmless, but won't actually process any 1.371 + // native events. So we log here so we can spot this and get a handle on the 1.372 + // corner cases where this can happen. 1.373 + WinUtils::Log("WARNING: Reentrant call into process events detected, returning early."); 1.374 + return false; 1.375 + } 1.376 + 1.377 + { 1.378 + AutoRestore<bool> dispatching(sIsDispatching); 1.379 + sIsDispatching = true; 1.380 + ProcessNativeEvents(CoreProcessEventsOption::CoreProcessEventsOption_ProcessOneIfPresent); 1.381 + } 1.382 + 1.383 + DispatchAllGeckoEvents(); 1.384 + 1.385 + return !!HIWORD(::GetQueueStatus(MOZ_QS_ALLEVENT)); 1.386 +} 1.387 + 1.388 +bool 1.389 +MetroAppShell::ProcessNextNativeEvent(bool mayWait) 1.390 +{ 1.391 + // NS_ProcessPendingEvents will process thread events *and* call 1.392 + // nsBaseAppShell::OnProcessNextEvent to process native events. However 1.393 + // we do not want native events getting dispatched while we are trying 1.394 + // to dispatch pending input in DispatchAllGeckoEvents since a native 1.395 + // event may be a UIA Automation call coming in to check focus. 1.396 + if (sBlockNativeEvents) { 1.397 + if ((TimeStamp::Now() - sPurgeThreadQueueStart).ToMilliseconds() 1.398 + < PURGE_MAX_TIMEOUT) { 1.399 + return false; 1.400 + } 1.401 + sBlockNativeEvents = false; 1.402 + } 1.403 + 1.404 + if (ProcessOneNativeEventIfPresent()) { 1.405 + return true; 1.406 + } 1.407 + if (mayWait) { 1.408 + DWORD result = ::MsgWaitForMultipleObjectsEx(0, nullptr, MSG_WAIT_TIMEOUT, 1.409 + MOZ_QS_ALLEVENT, 1.410 + MWMO_INPUTAVAILABLE|MWMO_ALERTABLE); 1.411 + NS_WARN_IF_FALSE(result != WAIT_FAILED, "Wait failed"); 1.412 + } 1.413 + return ProcessOneNativeEventIfPresent(); 1.414 +} 1.415 + 1.416 +void 1.417 +MetroAppShell::NativeCallback() 1.418 +{ 1.419 + NS_ASSERTION(NS_IsMainThread(), "Native callbacks must be on the metro main thread"); 1.420 + 1.421 + // We shouldn't process native events during xpcom shutdown - this can 1.422 + // trigger unexpected xpcom event dispatching for the main thread when 1.423 + // the thread manager is in the process of shutting down non-main threads, 1.424 + // resulting in shutdown hangs. 1.425 + if (MetroApp::sGeckoShuttingDown) { 1.426 + return; 1.427 + } 1.428 + 1.429 + NativeEventCallback(); 1.430 +} 1.431 + 1.432 +// static 1.433 +LRESULT CALLBACK 1.434 +MetroAppShell::EventWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 1.435 +{ 1.436 + if (uMsg == sAppShellGeckoMsgId) { 1.437 + MetroAppShell *as = reinterpret_cast<MetroAppShell *>(lParam); 1.438 + as->NativeCallback(); 1.439 + NS_RELEASE(as); 1.440 + return TRUE; 1.441 + } 1.442 + return DefWindowProc(hwnd, uMsg, wParam, lParam); 1.443 +} 1.444 + 1.445 +void 1.446 +MetroAppShell::ScheduleNativeEventCallback() 1.447 +{ 1.448 + NS_ADDREF_THIS(); 1.449 + PostMessage(mEventWnd, sAppShellGeckoMsgId, 0, reinterpret_cast<LPARAM>(this)); 1.450 +} 1.451 + 1.452 +void 1.453 +MetroAppShell::DoProcessMoreGeckoEvents() 1.454 +{ 1.455 + ScheduleNativeEventCallback(); 1.456 +} 1.457 + 1.458 +static HANDLE 1.459 +PowerCreateRequestDyn(REASON_CONTEXT *context) 1.460 +{ 1.461 + typedef HANDLE (WINAPI * PowerCreateRequestPtr)(REASON_CONTEXT *context); 1.462 + static HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); 1.463 + static PowerCreateRequestPtr powerCreateRequest = 1.464 + (PowerCreateRequestPtr)GetProcAddress(kernel32, "PowerCreateRequest"); 1.465 + if (!powerCreateRequest) 1.466 + return INVALID_HANDLE_VALUE; 1.467 + return powerCreateRequest(context); 1.468 +} 1.469 + 1.470 +static BOOL 1.471 +PowerClearRequestDyn(HANDLE powerRequest, POWER_REQUEST_TYPE requestType) 1.472 +{ 1.473 + typedef BOOL (WINAPI * PowerClearRequestPtr)(HANDLE powerRequest, POWER_REQUEST_TYPE requestType); 1.474 + static HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); 1.475 + static PowerClearRequestPtr powerClearRequest = 1.476 + (PowerClearRequestPtr)GetProcAddress(kernel32, "PowerClearRequest"); 1.477 + if (!powerClearRequest) 1.478 + return FALSE; 1.479 + return powerClearRequest(powerRequest, requestType); 1.480 +} 1.481 + 1.482 +static BOOL 1.483 +PowerSetRequestDyn(HANDLE powerRequest, POWER_REQUEST_TYPE requestType) 1.484 +{ 1.485 + typedef BOOL (WINAPI * PowerSetRequestPtr)(HANDLE powerRequest, POWER_REQUEST_TYPE requestType); 1.486 + static HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); 1.487 + static PowerSetRequestPtr powerSetRequest = 1.488 + (PowerSetRequestPtr)GetProcAddress(kernel32, "PowerSetRequest"); 1.489 + if (!powerSetRequest) 1.490 + return FALSE; 1.491 + return powerSetRequest(powerRequest, requestType); 1.492 +} 1.493 + 1.494 +NS_IMETHODIMP 1.495 +MetroAppShell::Observe(nsISupports *subject, const char *topic, 1.496 + const char16_t *data) 1.497 +{ 1.498 + NS_ENSURE_ARG_POINTER(topic); 1.499 + if (!strcmp(topic, "dl-start")) { 1.500 + if (mPowerRequestCount++ == 0) { 1.501 + WinUtils::Log("Download started - Disallowing suspend"); 1.502 + REASON_CONTEXT context; 1.503 + context.Version = POWER_REQUEST_CONTEXT_VERSION; 1.504 + context.Flags = POWER_REQUEST_CONTEXT_SIMPLE_STRING; 1.505 + context.Reason.SimpleReasonString = L"downloading"; 1.506 + mPowerRequest.own(PowerCreateRequestDyn(&context)); 1.507 + PowerSetRequestDyn(mPowerRequest, PowerRequestExecutionRequired); 1.508 + } 1.509 + return NS_OK; 1.510 + } else if (!strcmp(topic, "dl-done") || 1.511 + !strcmp(topic, "dl-cancel") || 1.512 + !strcmp(topic, "dl-failed")) { 1.513 + if (--mPowerRequestCount == 0 && mPowerRequest) { 1.514 + WinUtils::Log("All downloads ended - Allowing suspend"); 1.515 + PowerClearRequestDyn(mPowerRequest, PowerRequestExecutionRequired); 1.516 + mPowerRequest.reset(); 1.517 + } 1.518 + return NS_OK; 1.519 + } 1.520 + 1.521 + return nsBaseAppShell::Observe(subject, topic, data); 1.522 +}