/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This wakelock listener is used for Window7 and above. class WinWakeLockListener final : public nsIDOMMozWakeLockListener { public:
NS_DECL_ISUPPORTS
WinWakeLockListener() { MOZ_ASSERT(XRE_IsParentProcess()); }
void RequestWakelockIfNeeded(POWER_REQUEST_TYPE aType) { if (GetHandle(aType)) {
WAKE_LOCK_LOG("Already requested lock for %s", RequestTypeStr(aType)); return;
}
WAKE_LOCK_LOG("Prepare a wakelock for %s", RequestTypeStr(aType));
HANDLE handle = CreateHandle(aType); if (!handle) {
WAKE_LOCK_LOG("Failed due to no handle for %s", RequestTypeStr(aType)); return;
}
void ReleaseWakelockIfNeeded(POWER_REQUEST_TYPE aType) { if (!GetHandle(aType)) {
WAKE_LOCK_LOG("Already released lock for %s", RequestTypeStr(aType)); return;
}
WAKE_LOCK_LOG("Prepare to release wakelock for %s", RequestTypeStr(aType)); if (!PowerClearRequest(GetHandle(aType), aType)) {
WAKE_LOCK_LOG("Failed to release %s lock, error=%lu",
RequestTypeStr(aType), GetLastError()); return;
}
SetHandle(nullptr, aType);
WAKE_LOCK_LOG("Released wakelock for %s", RequestTypeStr(aType));
}
// Note that since we're on x86-ish processors here, ReleaseAcquire is the // semantics that normal loads and stores would use anyway. static Atomic<size_t, ReleaseAcquire> sOutstandingNativeEventCallbacks;
if (uMsg == sAppShellGeckoMsgId) { // The app shell might have been destroyed between this message being // posted and being executed, so be extra careful. if (!sOutstandingNativeEventCallbacks) { returnTRUE;
}
nsAppShell* as = reinterpret_cast<nsAppShell*>(lParam);
as->NativeEventCallback();
--sOutstandingNativeEventCallbacks; returnTRUE;
}
if (mEventWnd) { // DestroyWindow doesn't do anything when called from a non UI thread. // Since mEventWnd was created on the UI thread, it must be destroyed on // the UI thread.
SendMessage(mEventWnd, WM_CLOSE, 0, 0);
}
if (!strcmp(aTopic, "sessionstore-restoring-on-startup")) {
nsWindow::SetIsRestoringSession(true); // Now that we've handled the observer notification, we can remove it
obsServ->RemoveObserver(this, "sessionstore-restoring-on-startup"); return NS_OK;
}
if (!strcmp(aTopic, "sessionstore-windows-restored")) {
nsWindow::SetIsRestoringSession(false); // Now that we've handled the observer notification, we can remove it
obsServ->RemoveObserver(this, "sessionstore-windows-restored"); return NS_OK;
}
}
// Struct containing information about the user atom table. (See // DiagnoseUserAtomTable(), below.) struct AtomTableInformation { // Number of atoms in use. (Exactly 0x4000 == 16384, if all are.)
UINT in_use = 0; // Number of atoms confirmed not in use.
UINT free = 0; // Number of atoms which gave errors when checked.
UINT errors = 0;
// Last atom which gave an unexpected error...
UINT lastErrorAtom = ~0u; // ... and the error it gave.
WinErrorState lastErrorState;
};
// Return a summary of the state of the atom table.
MOZ_NEVER_INLINE static AtomTableInformation DiagnoseUserAtomTable() { // Restore error state on exit, for the sake of automated minidump analyses. autoconst _restoreErrState =
mozilla::MakeScopeExit([oldErrState = WinErrorState::Get()]() {
WinErrorState::Apply(oldErrState);
});
AtomTableInformation retval;
// Expected error-state on failure-return when the atom is assigned, but not // enough space was provided for the full string.
constexpr WinErrorState kBufferTooSmall = {
.error = ERROR_INSUFFICIENT_BUFFER,
.ntStatus = ((NTSTATUS)0xC0000023), // == STATUS_BUFFER_TOO_SMALL
}; // Expected error-state on failure-return when the atom is not assigned.
constexpr WinErrorState kInvalidAtom = {
.error = ERROR_INVALID_HANDLE,
.ntStatus = ((NTSTATUS)STATUS_INVALID_HANDLE),
};
// Iterate over only the dynamic portion of the atom table. for (UINT atom = 0xC000; atom <= 0xFFFF; ++atom) { // The actual atom values are PII. Don't acquire them in their entirety, and // don't keep more information about them than is needed.
WCHAR buf[2] = {}; // USE OF UNDOCUMENTED BEHAVIOR: The user atom table is shared by message // names, window-class names, and clipboard-format names. Only the last has // a documented getter-mechanism. BOOLconst ok = ::GetClipboardFormatNameW(atom, buf, 1);
WinErrorState const errState = WinErrorState::Get(); if (ok || errState == kBufferTooSmall) {
++retval.in_use;
} elseif (errState == kInvalidAtom) {
++retval.free;
} else { // Unexpected error-state.
++retval.errors;
retval.lastErrorAtom = atom;
retval.lastErrorState = errState;
}
}
return retval;
}
#ifdefined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) && defined(_M_X64) static constexpr int kMaxStepsUser32 = 0x1800; static constexpr int kMaxErrorStatesUser32 = 0x200; using User32SingleStepData =
ModuleSingleStepData<kMaxStepsUser32, kMaxErrorStatesUser32>;
// Collect data for bug 1571516. We don't automatically send up `GetLastError` // or `GetLastNtStatus` data for beta/release builds, so extract the relevant // error values and store them on the stack, where they can be viewed in // minidumps -- in fact, do so after each individual API call. This takes the // form of various local variables whose initial character is an underscore, // most of which are also marked [[maybe_unused]]. // // We tag this function `[[clang::optnone]]` to prevent the compiler from // eliding those values as _actually_ unused, as well as to generally simplify // the haruspex's task once the minidumps are in. (As this function should be // called at most once per process, the minor performance hit is not a concern.) // /* static */ [[clang::optnone]] MOZ_NEVER_INLINE HWND
nsAppShell::StaticCreateEventWindow() { // note the incoming error-state; this may be relevant to errors we get later auto _initialErr [[maybe_unused]] = WinErrorState::Get(); // reset the error-state, to avoid ambiguity below
WinErrorState::Clear();
// Diagnostic variable. Only collected in the event of a failure in one of the // functions that attempts to register an atom.
AtomTableInformation _atomTableInfo [[maybe_unused]];
// Attempt to register the window message. On failure, retain the initial // value of `sAppShellGeckoMsgId`. autoconst _msgId = ::RegisterWindowMessageW(kAppShellGeckoEventId); if (_msgId) {
sAppShellGeckoMsgId = _msgId;
} autoconst _sAppShellGeckoMsgId [[maybe_unused]] = sAppShellGeckoMsgId; autoconst _rwmErr [[maybe_unused]] = WinErrorState::Get(); if (!_msgId) _atomTableInfo = DiagnoseUserAtomTable();
NS_ASSERTION(sAppShellGeckoMsgId, "Could not register hidden window event message!");
ATOM _windowClassAtom = ::RegisterClassW(&wc);
_rcErr = WinErrorState::Get();
if (!_windowClassAtom) _atomTableInfo = DiagnoseUserAtomTable();
#ifdefined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) && defined(_M_X64) if (!_windowClassAtom) { // Retry with single-step data collection
WindowsDiagnosticsError rv = CollectUser32SingleStepData(
[&wc, &_windowClassAtom]() {
_windowClassAtom = ::RegisterClassW(&wc);
},
[&_windowClassAtom](const User32SingleStepData& aData) { // Crashing here gives access to the single step data on stack
MOZ_DIAGNOSTIC_ASSERT(
_windowClassAtom, "RegisterClassW for EventWindowClass failed twice");
}); autoconst _cssdErr [[maybe_unused]] = WinErrorState::Get();
MOZ_DIAGNOSTIC_ASSERT(
rv == WindowsDiagnosticsError::None, "Failed to collect single step data for RegisterClassW"); // If we reach this point then somehow the single-stepped call succeeded // and we can proceed
} #endif// MOZ_DIAGNOSTIC_ASSERT_ENABLED && _M_X64
MOZ_RELEASE_ASSERT(_windowClassAtom, "RegisterClassW for EventWindowClass failed");
WinErrorState::Clear();
}
#ifdefined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) && defined(_M_X64) if (!eventWnd) { // Retry with single-step data collection
WindowsDiagnosticsError rv = CollectUser32SingleStepData(
[module, &eventWnd]() {
eventWnd =
CreateWindowW(kWindowClass, L"nsAppShell:EventWindow", 0, 0, 0,
10, 10, HWND_MESSAGE, nullptr, module, nullptr);
},
[&eventWnd](const User32SingleStepData& aData) { // Crashing here gives access to the single step data on stack
MOZ_DIAGNOSTIC_ASSERT(eventWnd, "CreateWindowW for EventWindow failed twice");
}); autoconst _cssdErr [[maybe_unused]] = WinErrorState::Get();
MOZ_DIAGNOSTIC_ASSERT(
rv == WindowsDiagnosticsError::None, "Failed to collect single step data for CreateWindowW"); // If we reach this point then somehow the single-stepped call succeeded and // we can proceed
} #endif// MOZ_DIAGNOSTIC_ASSERT_ENABLED && _M_X64
MOZ_RELEASE_ASSERT(eventWnd, "CreateWindowW for EventWindow failed");
if (XRE_IsParentProcess()) {
sTaskbarButtonCreatedMsg = ::RegisterWindowMessageW(kTaskbarButtonEventId);
NS_ASSERTION(sTaskbarButtonCreatedMsg, "Could not register taskbar button creation message");
}
// The hidden message window is used for interrupting the processing of native // events, so that we can process gecko events. Therefore, we only need it if // we are processing native events. Disabling this is required for win32k // syscall lockdown. if (XRE_UseNativeEventProcessing()) { if (nsresult rv = this->InitEventWindow(); NS_FAILED(rv)) { return rv;
}
} elseif (XRE_IsContentProcess() && !IsWin32kLockedDown()) { // We're not generally processing native events, but still using GDI and we // still have some internal windows, e.g. from calling CoInitializeEx. // So we use a class that will do a single event pump where previously we // might have processed multiple events to make sure any occasional messages // to these windows are processed. This also allows any internal Windows // messages to be processed to ensure the GDI data remains fresh.
nsCOMPtr<nsIThreadInternal> threadInt =
do_QueryInterface(NS_GetCurrentThread()); if (threadInt) {
threadInt->SetObserver(new SingleNativeEventPump());
}
}
if (XRE_IsParentProcess()) {
ScreenManager& screenManager = ScreenManager::GetSingleton(); if (gfxPlatform::IsHeadless()) {
screenManager.SetHelper(mozilla::MakeUnique<HeadlessScreenHelper>());
} else {
screenManager.SetHelper(mozilla::MakeUnique<ScreenHelperWin>());
ScreenHelperWin::RefreshScreens();
}
if (!WinUtils::GetTimezoneName(mTimezoneName)) {
NS_WARNING("Unable to get system timezone name, timezone may be invalid\n");
}
return nsBaseAppShell::Init();
}
NS_IMETHODIMP
nsAppShell::Run(void) { bool wantAudio = true; if (XRE_IsParentProcess()) { #ifdef MOZ_BACKGROUNDTASKS if (BackgroundTasks::IsBackgroundTaskMode()) {
wantAudio = false;
} #endif if (MOZ_LIKELY(wantAudio)) {
mozilla::widget::StartAudioSession();
}
// Add an observer that disables the screen saver when requested by Gecko. // For example when we're playing video in the foreground tab. Whole firefox // only needs one wakelock instance, so we would only create one listener in // chrome process to prevent requesting unnecessary wakelock.
AddScreenWakeLockListener();
}
nsresult rv = nsBaseAppShell::Run();
if (XRE_IsParentProcess()) {
RemoveScreenWakeLockListener();
if (MOZ_LIKELY(wantAudio)) {
mozilla::widget::StopAudioSession();
}
}
return rv;
}
void nsAppShell::DoProcessMoreGeckoEvents() { // Called by nsBaseAppShell's NativeEventCallback() after it has finished // processing pending gecko events and there are still gecko events pending // for the thread. (This can happen if NS_ProcessPendingEvents reached it's // starvation timeout limit.) The default behavior in nsBaseAppShell is to // call ScheduleNativeEventCallback to post a follow up native event callback // message. This triggers an additional call to NativeEventCallback for more // gecko event processing.
// There's a deadlock risk here with certain internal Windows modal loops. In // our dispatch code, we prioritize messages so that input is handled first. // However Windows modal dispatch loops often prioritize posted messages. If // we find ourselves in a tight gecko timer loop where NS_ProcessPendingEvents // takes longer than the timer duration, NS_HasPendingEvents(thread) will // always be true. ScheduleNativeEventCallback will be called on every // NativeEventCallback callback, and in a Windows modal dispatch loop, the // callback message will be processed first -> input gets starved, dead lock.
// To avoid, don't post native callback messages from NativeEventCallback // when we're in a modal loop. This gets us back into the Windows modal // dispatch loop dispatching input messages. Once we drop out of the modal // loop, we use mNativeCallbackPending to fire off a final NativeEventCallback // if we need it, which insures NS_ProcessPendingEvents gets called and all // gecko events get processed. if (mEventloopNestingLevel < 2) {
OnDispatchedEvent();
mNativeCallbackPending = false;
} else {
mNativeCallbackPending = true;
}
}
void nsAppShell::ScheduleNativeEventCallback() {
MOZ_ASSERT(mEventWnd, "We should have created mEventWnd in Init, if this is called.");
// Post a message to the hidden message window
++sOutstandingNativeEventCallbacks;
{
MutexAutoLock lock(mLastNativeEventScheduledMutex); // Time stamp this event so we can detect cases where the event gets // dropping in sub classes / modal loops we do not control.
mLastNativeEventScheduled = TimeStamp::NowLoRes();
}
::PostMessage(mEventWnd, sAppShellGeckoMsgId, 0, reinterpret_cast<LPARAM>(this));
}
bool nsAppShell::ProcessNextNativeEvent(bool mayWait) { // Notify ipc we are spinning a (possibly nested) gecko event loop.
mozilla::ipc::MessageChannel::NotifyGeckoEventDispatch();
bool gotMessage = false;
do {
MSG msg;
// For avoiding deadlock between our process and plugin process by // mouse wheel messages, we're handling actually when we receive one of // following internal messages which is posted by native mouse wheel // message handler. Any other events, especially native modifier key // events, should not be handled between native message and posted // internal message because it may make different modifier key state or // mouse cursor position between them. if (mozilla::widget::MouseScrollHandler::IsWaitingInternalMessage()) {
gotMessage = WinUtils::PeekMessage(&msg, nullptr, MOZ_WM_MOUSEWHEEL_FIRST,
MOZ_WM_MOUSEWHEEL_LAST, PM_REMOVE);
NS_ASSERTION(gotMessage, "waiting internal wheel message, but it has not come");
}
if (gotMessage) { if (msg.message == WM_QUIT) {
::PostQuitMessage(msg.wParam); Exit();
} else { // If we had UI activity we would be processing it now so we know we // have either kUIActivity or kActivityNoUIAVail.
mozilla::BackgroundHangMonitor().NotifyActivity();
if (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST &&
IMEHandler::ProcessRawKeyMessage(msg)) { continue; // the message is consumed.
}
#ifdefined(_X86_) // Store Printer dialog messages for reposting on x86, because on x86 // Windows 7 they are not processed by a window procedure, but are // explicitly waited for in the winspool.drv code that will be further // up the stack (winspool!WaitForCompletionMessage). These are // undocumented Windows Message identifiers found in winspool.drv. if (msg.message == 0x5b7a || msg.message == 0x5b7f ||
msg.message == 0x5b80 || msg.message == 0x5b81) {
mMsgsToRepost.push_back(msg); continue;
} #endif
// Windows documentation suggets that WM_SETTINGSCHANGE is the message // to watch for timezone changes, but experimentation showed that it // doesn't fire on changing the timezone, but that WM_TIMECHANGE does, // even if there's no immediate effect on the clock (e.g., changing // from Pacific Daylight at UTC-7 to Arizona at UTC-7). if (msg.message == WM_TIMECHANGE) { // The message may not give us sufficient information to determine // if the timezone changed, so keep track of it ourselves. wchar_t systemTimezone[128]; bool getSystemTimeSucceeded =
WinUtils::GetTimezoneName(systemTimezone); if (getSystemTimeSucceeded && wcscmp(systemTimezone, mTimezoneName)) {
nsBaseAppShell::OnSystemTimezoneChange();
wcscpy_s(mTimezoneName, 128, systemTimezone);
}
}
::TranslateMessage(&msg);
::DispatchMessageW(&msg);
}
} elseif (mayWait) { // Block and wait for any posted application message
mozilla::BackgroundHangMonitor().NotifyWait();
{
AUTO_PROFILER_LABEL("nsAppShell::ProcessNextNativeEvent::Wait", IDLE);
WinUtils::WaitForMessage();
}
}
} while (!gotMessage && mayWait);
// See DoProcessNextNativeEvent, mEventloopNestingLevel will be // one when a modal loop unwinds. if (mNativeCallbackPending && mEventloopNestingLevel == 1)
DoProcessMoreGeckoEvents();
// Check for starved native callbacks. If we haven't processed one // of these events in NATIVE_EVENT_STARVATION_LIMIT, fire one off. staticconst mozilla::TimeDuration nativeEventStarvationLimit =
mozilla::TimeDuration::FromSeconds(NATIVE_EVENT_STARVATION_LIMIT);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.