Bug 1663784 - Implement basic nsISystemStatusBar service for Windows. r=mhowell

This basic implementation uses the application icon for the icon that goes
into the system tray for now. A later patch in this series will change that
icon to one defined within the associated <xul:menu> element's styles.

Differential Revision: https://phabricator.services.mozilla.com/D89787
This commit is contained in:
Mike Conley 2020-09-30 21:14:05 +00:00
Родитель 58ca6739aa
Коммит 9f45e1f865
5 изменённых файлов: 241 добавлений и 1 удалений

Просмотреть файл

@ -63,7 +63,6 @@ elif toolkit == 'cocoa':
'nsIMacSharingService.idl',
'nsIMacWebAppUtils.idl',
'nsIStandaloneNativeMenu.idl',
'nsISystemStatusBar.idl',
'nsITaskbarProgress.idl',
'nsITouchBarHelper.idl',
'nsITouchBarInput.idl',
@ -120,6 +119,7 @@ XPIDL_SOURCES += [
'nsIScreenManager.idl',
'nsISharePicker.idl',
'nsISound.idl',
'nsISystemStatusBar.idl',
'nsITransferable.idl',
'nsIUserIdleService.idl',
'nsIUserIdleServiceInternal.idl',

Просмотреть файл

@ -0,0 +1,197 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sts=2 sw=2 et cin: */
/* 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/. */
#include <strsafe.h>
#include "SystemStatusBar.h"
#include "mozilla/dom/Element.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/LinkedList.h"
#include "mozilla/StaticPtr.h"
#include "nsMenuFrame.h"
#include "nsMenuPopupFrame.h"
#include "nsXULPopupManager.h"
namespace mozilla::widget {
using mozilla::LinkedListElement;
using mozilla::dom::Element;
class StatusBarEntry final : public LinkedListElement<RefPtr<StatusBarEntry>> {
public:
explicit StatusBarEntry(Element* aMenu);
nsresult Init();
LRESULT OnMessage(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
const Element* GetMenu() { return mMenu; };
NS_INLINE_DECL_REFCOUNTING(StatusBarEntry)
private:
~StatusBarEntry();
RefPtr<Element> mMenu;
NOTIFYICONDATAW mIconData;
boolean mInitted;
};
StatusBarEntry::StatusBarEntry(Element* aMenu) : mMenu(aMenu), mInitted(false) {
mIconData = {/* cbSize */ sizeof(NOTIFYICONDATA),
/* hWnd */ 0,
/* uID */ 2,
/* uFlags */ NIF_ICON | NIF_MESSAGE | NIF_TIP,
/* uCallbackMessage */ WM_USER,
/* hIcon */ 0,
/* szTip */ L"", // This is updated in Init()
/* dwState */ 0,
/* dwStateMask */ 0,
/* szInfo */ L"",
/* uVersion */ {NOTIFYICON_VERSION},
/* szInfoTitle */ L"",
/* dwInfoFlags */ 0};
}
StatusBarEntry::~StatusBarEntry() {
if (!mInitted) {
return;
}
::Shell_NotifyIconW(NIM_DELETE, &mIconData);
VERIFY(::DestroyWindow(mIconData.hWnd));
}
nsresult StatusBarEntry::Init() {
HWND iconWindow;
NS_ENSURE_TRUE(iconWindow = ::CreateWindowExW(
/* extended style */ 0,
/* className */ L"IconWindowClass",
/* title */ 0,
/* style */ WS_CAPTION,
/* x, y, cx, cy */ 0, 0, 0, 0,
/* parent */ 0,
/* menu */ 0,
/* instance */ 0,
/* create struct */ 0),
NS_ERROR_FAILURE);
::SetWindowLongPtr(iconWindow, GWLP_USERDATA, (LONG_PTR)this);
mIconData.hWnd = iconWindow;
mIconData.hIcon = ::LoadIcon(::GetModuleHandle(NULL), IDI_APPLICATION);
nsAutoString labelAttr;
mMenu->GetAttr(kNameSpaceID_None, nsGkAtoms::label, labelAttr);
const nsString& label = PromiseFlatString(labelAttr);
size_t destLength = sizeof mIconData.szTip / (sizeof mIconData.szTip[0]);
wchar_t* tooltip = &(mIconData.szTip[0]);
::StringCchCopyNW(tooltip, destLength, label.get(), label.Length());
::Shell_NotifyIconW(NIM_ADD, &mIconData);
::Shell_NotifyIconW(NIM_SETVERSION, &mIconData);
mInitted = true;
return NS_OK;
}
LRESULT StatusBarEntry::OnMessage(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
if (msg == WM_USER && (lp == WM_LBUTTONUP || lp == WM_RBUTTONUP)) {
nsMenuFrame* menu = do_QueryFrame(mMenu->GetPrimaryFrame());
if (!menu) {
return TRUE;
}
nsMenuPopupFrame* popupFrame = menu->GetPopup();
if (!popupFrame) {
return TRUE;
}
nsIWidget* widget = popupFrame->GetNearestWidget();
if (!widget) {
return TRUE;
}
HWND win = static_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW));
if (!win) {
return TRUE;
}
// The menu that is being opened is a Gecko <xul:menu>, and the popup code
// that manages it expects that the window that the <xul:menu> belongs to
// will be in the foreground when it opens. If we don't do this, then if the
// icon is clicked when the window is _not_ in the foreground, then the
// opened menu will not be keyboard focusable, nor will it close on its own
// if the user clicks away from the menu (at least, not until the user
// focuses any window in the parent process).
::SetForegroundWindow(win);
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
POINT pt;
::GetCursorPos(&pt);
pm->ShowPopup(popupFrame->GetContent(), nullptr, EmptyString(), pt.x, pt.y,
false, false, true, nullptr);
}
return DefWindowProc(hWnd, msg, wp, lp);
}
NS_IMPL_ISUPPORTS(SystemStatusBar, nsISystemStatusBar)
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
StatusBarEntry* entry =
(StatusBarEntry*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
if (entry) {
return entry->OnMessage(hWnd, msg, wp, lp);
}
return TRUE;
}
static StaticRefPtr<SystemStatusBar> sSingleton;
SystemStatusBar& SystemStatusBar::GetSingleton() {
if (!sSingleton) {
sSingleton = new SystemStatusBar();
ClearOnShutdown(&sSingleton);
}
return *sSingleton;
}
already_AddRefed<SystemStatusBar> SystemStatusBar::GetAddRefedSingleton() {
RefPtr<SystemStatusBar> sm = &GetSingleton();
return sm.forget();
}
nsresult SystemStatusBar::Init() {
WNDCLASS classStruct = {/* style */ 0,
/* lpfnWndProc */ &WindowProc,
/* cbClsExtra */ 0,
/* cbWndExtra */ 0,
/* hInstance */ 0,
/* hIcon */ 0,
/* hCursor */ 0,
/* hbrBackground */ 0,
/* lpszMenuName */ 0,
/* lpszClassName */ L"IconWindowClass"};
NS_ENSURE_TRUE(::RegisterClass(&classStruct), NS_ERROR_FAILURE);
return NS_OK;
}
NS_IMETHODIMP
SystemStatusBar::AddItem(Element* aElement) {
RefPtr<StatusBarEntry> entry = new StatusBarEntry(aElement);
nsresult rv = entry->Init();
NS_ENSURE_SUCCESS(rv, rv);
mStatusBarEntries.insertBack(entry);
return NS_OK;
}
NS_IMETHODIMP
SystemStatusBar::RemoveItem(Element* aElement) {
for (StatusBarEntry* entry : mStatusBarEntries) {
if (entry->GetMenu() == aElement) {
entry->removeFrom(mStatusBarEntries);
return NS_OK;
}
}
return NS_ERROR_NOT_AVAILABLE;
}
} // namespace mozilla::widget

Просмотреть файл

@ -0,0 +1,32 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#ifndef widget_windows_SystemStatusBar_h
#define widget_windows_SystemStatusBar_h
#include "nsISystemStatusBar.h"
namespace mozilla::widget {
class StatusBarEntry;
class SystemStatusBar final : public nsISystemStatusBar {
public:
explicit SystemStatusBar() = default;
NS_DECL_ISUPPORTS
NS_DECL_NSISYSTEMSTATUSBAR
static SystemStatusBar& GetSingleton();
static already_AddRefed<SystemStatusBar> GetAddRefedSingleton();
nsresult Init();
private:
~SystemStatusBar() = default;
mozilla::LinkedList<RefPtr<StatusBarEntry>> mStatusBarEntries;
};
} // namespace mozilla::widget
#endif // widget_windows_SystemStatusBar_h

Просмотреть файл

@ -156,6 +156,16 @@ Classes = [
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
'overridable': True,
},
{
'cid': '{b6e1a890-b2b8-4883-a65f-9476f6185313}',
'contract_ids': ['@mozilla.org/widget/systemstatusbar;1'],
'singleton': True,
'init_method': 'Init',
'type': 'mozilla::widget::SystemStatusBar',
'constructor': 'mozilla::widget::SystemStatusBar::GetAddRefedSingleton',
'headers': ['/widget/windows/SystemStatusBar.h'],
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
},
]
if buildconfig.substs['CC_TYPE'] in ('msvc', 'clang-cl'):

Просмотреть файл

@ -77,6 +77,7 @@ UNIFIED_SOURCES += [
'ProcInfo.cpp',
'RemoteBackbuffer.cpp',
'ScreenHelperWin.cpp',
'SystemStatusBar.cpp',
'TaskbarPreview.cpp',
'TaskbarPreviewButton.cpp',
'TaskbarTabPreview.cpp',