Bug 1671598 [wpt PR 26128] - [AspectRatio] Fix divide by zero with a small float...
[gecko.git] / widget / windows / SystemStatusBar.cpp
blob8bc4671343058100482b4079884cb12e279bb875
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sts=2 sw=2 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <strsafe.h>
8 #include "SystemStatusBar.h"
10 #include "mozilla/dom/Element.h"
11 #include "mozilla/ClearOnShutdown.h"
12 #include "mozilla/LinkedList.h"
13 #include "mozilla/StaticPtr.h"
14 #include "nsComputedDOMStyle.h"
15 #include "nsIContentPolicy.h"
16 #include "nsMenuFrame.h"
17 #include "nsMenuPopupFrame.h"
18 #include "nsXULPopupManager.h"
19 #include "IconLoaderHelperWin.h"
21 namespace mozilla::widget {
23 using mozilla::LinkedListElement;
24 using mozilla::dom::Element;
25 using mozilla::widget::IconLoaderListenerWin;
27 class StatusBarEntry final : public LinkedListElement<RefPtr<StatusBarEntry>>,
28 public IconLoaderListenerWin {
29 public:
30 explicit StatusBarEntry(Element* aMenu);
31 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
32 NS_DECL_CYCLE_COLLECTION_CLASS(StatusBarEntry)
33 nsresult Init();
34 LRESULT OnMessage(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp);
35 const Element* GetMenu() { return mMenu; };
37 nsresult OnComplete();
39 private:
40 ~StatusBarEntry();
41 RefPtr<mozilla::widget::IconLoader> mIconLoader;
42 RefPtr<mozilla::widget::IconLoaderHelperWin> mIconLoaderHelper;
43 RefPtr<Element> mMenu;
44 NOTIFYICONDATAW mIconData;
45 boolean mInitted;
48 NS_IMPL_CYCLE_COLLECTION_CLASS(StatusBarEntry)
50 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StatusBarEntry)
51 tmp->OnComplete();
52 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
53 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StatusBarEntry)
54 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIconLoader)
55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIconLoaderHelper)
56 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenu)
57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
59 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StatusBarEntry)
60 NS_INTERFACE_MAP_ENTRY(nsISupports)
61 NS_INTERFACE_MAP_END
63 NS_IMPL_CYCLE_COLLECTING_ADDREF(StatusBarEntry)
64 NS_IMPL_CYCLE_COLLECTING_RELEASE(StatusBarEntry)
66 StatusBarEntry::StatusBarEntry(Element* aMenu) : mMenu(aMenu), mInitted(false) {
67 mIconData = {/* cbSize */ sizeof(NOTIFYICONDATA),
68 /* hWnd */ 0,
69 /* uID */ 2,
70 /* uFlags */ NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_SHOWTIP,
71 /* uCallbackMessage */ WM_USER,
72 /* hIcon */ 0,
73 /* szTip */ L"", // This is updated in Init()
74 /* dwState */ 0,
75 /* dwStateMask */ 0,
76 /* szInfo */ L"",
77 /* uVersion */ {NOTIFYICON_VERSION_4},
78 /* szInfoTitle */ L"",
79 /* dwInfoFlags */ 0};
80 MOZ_ASSERT(mMenu);
83 StatusBarEntry::~StatusBarEntry() {
84 if (!mInitted) {
85 return;
87 ::Shell_NotifyIconW(NIM_DELETE, &mIconData);
88 VERIFY(::DestroyWindow(mIconData.hWnd));
91 nsresult StatusBarEntry::Init() {
92 MOZ_ASSERT(NS_IsMainThread());
94 // First, look at the content node's "image" attribute.
95 nsAutoString imageURIString;
96 bool hasImageAttr =
97 mMenu->GetAttr(kNameSpaceID_None, nsGkAtoms::image, imageURIString);
99 nsresult rv;
100 RefPtr<ComputedStyle> sc;
101 nsCOMPtr<nsIURI> iconURI;
102 if (!hasImageAttr) {
103 // If the content node has no "image" attribute, get the
104 // "list-style-image" property from CSS.
105 RefPtr<mozilla::dom::Document> document = mMenu->GetComposedDoc();
106 if (!document) {
107 return NS_ERROR_FAILURE;
110 sc = nsComputedDOMStyle::GetComputedStyle(mMenu, nullptr);
111 if (!sc) {
112 return NS_ERROR_FAILURE;
115 iconURI = sc->StyleList()->GetListStyleImageURI();
116 } else {
117 uint64_t dummy = 0;
118 nsContentPolicyType policyType;
119 nsCOMPtr<nsIPrincipal> triggeringPrincipal = mMenu->NodePrincipal();
120 nsContentUtils::GetContentPolicyTypeForUIImageLoading(
121 mMenu, getter_AddRefs(triggeringPrincipal), policyType, &dummy);
122 if (policyType != nsIContentPolicy::TYPE_INTERNAL_IMAGE) {
123 return NS_ERROR_ILLEGAL_VALUE;
126 // If this menu item shouldn't have an icon, the string will be empty,
127 // and NS_NewURI will fail.
128 rv = NS_NewURI(getter_AddRefs(iconURI), imageURIString);
129 if (NS_FAILED(rv)) return rv;
132 mIconLoaderHelper = new IconLoaderHelperWin(this);
133 nsIntRect rect;
134 mIconLoader = new IconLoader(mIconLoaderHelper, mMenu, rect);
136 if (iconURI) {
137 rv = mIconLoader->LoadIcon(iconURI);
140 HWND iconWindow;
141 NS_ENSURE_TRUE(iconWindow = ::CreateWindowExW(
142 /* extended style */ 0,
143 /* className */ L"IconWindowClass",
144 /* title */ 0,
145 /* style */ WS_CAPTION,
146 /* x, y, cx, cy */ 0, 0, 0, 0,
147 /* parent */ 0,
148 /* menu */ 0,
149 /* instance */ 0,
150 /* create struct */ 0),
151 NS_ERROR_FAILURE);
152 ::SetWindowLongPtr(iconWindow, GWLP_USERDATA, (LONG_PTR)this);
154 mIconData.hWnd = iconWindow;
155 mIconData.hIcon = mIconLoaderHelper->GetNativeIconImage();
157 nsAutoString labelAttr;
158 mMenu->GetAttr(kNameSpaceID_None, nsGkAtoms::label, labelAttr);
159 const nsString& label = PromiseFlatString(labelAttr);
161 size_t destLength = sizeof mIconData.szTip / (sizeof mIconData.szTip[0]);
162 wchar_t* tooltip = &(mIconData.szTip[0]);
163 ::StringCchCopyNW(tooltip, destLength, label.get(), label.Length());
165 ::Shell_NotifyIconW(NIM_ADD, &mIconData);
166 ::Shell_NotifyIconW(NIM_SETVERSION, &mIconData);
168 mInitted = true;
169 return NS_OK;
172 nsresult StatusBarEntry::OnComplete() {
173 RefPtr<StatusBarEntry> kungFuDeathGrip = this;
174 mIconData.hIcon = mIconLoaderHelper->GetNativeIconImage();
176 ::Shell_NotifyIconW(NIM_MODIFY, &mIconData);
178 // To simplify things, we won't react to CSS changes to update the icon
179 // with this implementation. We can get rid of the IconLoader and Helper
180 // at this point, which will also free the allocated HICON.
181 mIconLoaderHelper->Destroy();
182 mIconLoader->ReleaseJSObjects();
183 mIconLoader->Destroy();
184 mIconLoader = nullptr;
185 mIconLoaderHelper = nullptr;
186 return NS_OK;
189 LRESULT StatusBarEntry::OnMessage(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
190 if (msg == WM_USER &&
191 (LOWORD(lp) == WM_LBUTTONUP || LOWORD(lp) == WM_RBUTTONUP)) {
192 nsMenuFrame* menu = do_QueryFrame(mMenu->GetPrimaryFrame());
193 if (!menu) {
194 return TRUE;
197 nsMenuPopupFrame* popupFrame = menu->GetPopup();
198 if (!popupFrame) {
199 return TRUE;
201 nsIWidget* widget = popupFrame->GetNearestWidget();
202 if (!widget) {
203 return TRUE;
206 HWND win = static_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW));
207 if (!win) {
208 return TRUE;
211 // The menu that is being opened is a Gecko <xul:menu>, and the popup code
212 // that manages it expects that the window that the <xul:menu> belongs to
213 // will be in the foreground when it opens. If we don't do this, then if the
214 // icon is clicked when the window is _not_ in the foreground, then the
215 // opened menu will not be keyboard focusable, nor will it close on its own
216 // if the user clicks away from the menu (at least, not until the user
217 // focuses any window in the parent process).
218 ::SetForegroundWindow(win);
219 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
220 pm->ShowPopupAtScreen(popupFrame->GetContent(), GET_X_LPARAM(wp),
221 GET_Y_LPARAM(wp), false, nullptr);
224 return DefWindowProc(hWnd, msg, wp, lp);
227 NS_IMPL_ISUPPORTS(SystemStatusBar, nsISystemStatusBar)
229 static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
230 StatusBarEntry* entry =
231 (StatusBarEntry*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
232 if (entry) {
233 return entry->OnMessage(hWnd, msg, wp, lp);
235 return TRUE;
238 static StaticRefPtr<SystemStatusBar> sSingleton;
240 SystemStatusBar& SystemStatusBar::GetSingleton() {
241 if (!sSingleton) {
242 sSingleton = new SystemStatusBar();
243 ClearOnShutdown(&sSingleton);
245 return *sSingleton;
248 already_AddRefed<SystemStatusBar> SystemStatusBar::GetAddRefedSingleton() {
249 RefPtr<SystemStatusBar> sm = &GetSingleton();
250 return sm.forget();
253 nsresult SystemStatusBar::Init() {
254 WNDCLASS classStruct = {/* style */ 0,
255 /* lpfnWndProc */ &WindowProc,
256 /* cbClsExtra */ 0,
257 /* cbWndExtra */ 0,
258 /* hInstance */ 0,
259 /* hIcon */ 0,
260 /* hCursor */ 0,
261 /* hbrBackground */ 0,
262 /* lpszMenuName */ 0,
263 /* lpszClassName */ L"IconWindowClass"};
264 NS_ENSURE_TRUE(::RegisterClass(&classStruct), NS_ERROR_FAILURE);
265 return NS_OK;
268 NS_IMETHODIMP
269 SystemStatusBar::AddItem(Element* aElement) {
270 RefPtr<StatusBarEntry> entry = new StatusBarEntry(aElement);
271 nsresult rv = entry->Init();
272 NS_ENSURE_SUCCESS(rv, rv);
274 mStatusBarEntries.insertBack(entry);
275 return NS_OK;
278 NS_IMETHODIMP
279 SystemStatusBar::RemoveItem(Element* aElement) {
280 for (StatusBarEntry* entry : mStatusBarEntries) {
281 if (entry->GetMenu() == aElement) {
282 entry->removeFrom(mStatusBarEntries);
283 return NS_OK;
286 return NS_ERROR_NOT_AVAILABLE;
289 } // namespace mozilla::widget