Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / xpfe / appshell / nsWindowMediator.cpp
blobe44a5e7008f7de7d4d4c550176a176af382f8c72
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsCOMPtr.h"
7 #include "nsEnumeratorUtils.h"
8 #include "nsString.h"
9 #include "nsReadableUtils.h"
10 #include "nsUnicharUtils.h"
11 #include "nsTArray.h"
12 #include "nsIBaseWindow.h"
13 #include "nsIWidget.h"
14 #include "nsIObserverService.h"
15 #include "nsISimpleEnumerator.h"
16 #include "nsAppShellWindowEnumerator.h"
17 #include "nsWindowMediator.h"
18 #include "nsIWindowMediatorListener.h"
19 #include "nsGlobalWindowInner.h"
20 #include "nsGlobalWindowOuter.h"
21 #include "nsServiceManagerUtils.h"
23 #include "nsIDocShell.h"
24 #include "nsIInterfaceRequestor.h"
25 #include "nsIInterfaceRequestorUtils.h"
26 #include "nsIAppWindow.h"
28 using namespace mozilla;
30 nsresult nsWindowMediator::GetDOMWindow(
31 nsIAppWindow* inWindow, nsCOMPtr<nsPIDOMWindowOuter>& outDOMWindow) {
32 nsCOMPtr<nsIDocShell> docShell;
34 outDOMWindow = nullptr;
35 inWindow->GetDocShell(getter_AddRefs(docShell));
36 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
38 outDOMWindow = docShell->GetWindow();
39 return outDOMWindow ? NS_OK : NS_ERROR_FAILURE;
42 nsWindowMediator::nsWindowMediator()
43 : mOldestWindow(nullptr),
44 mTopmostWindow(nullptr),
45 mTimeStamp(0),
46 mReady(false) {}
48 nsWindowMediator::~nsWindowMediator() {
49 while (mOldestWindow) UnregisterWindow(mOldestWindow);
52 nsresult nsWindowMediator::Init() {
53 nsresult rv;
54 nsCOMPtr<nsIObserverService> obsSvc =
55 do_GetService("@mozilla.org/observer-service;1", &rv);
56 NS_ENSURE_SUCCESS(rv, rv);
57 rv = obsSvc->AddObserver(this, "xpcom-shutdown", true);
58 NS_ENSURE_SUCCESS(rv, rv);
60 mReady = true;
61 return NS_OK;
64 NS_IMETHODIMP nsWindowMediator::RegisterWindow(nsIAppWindow* inWindow) {
65 MOZ_RELEASE_ASSERT(NS_IsMainThread());
67 if (!mReady) {
68 NS_ERROR("Mediator is not initialized or about to die.");
69 return NS_ERROR_FAILURE;
72 if (GetInfoFor(inWindow)) {
73 NS_ERROR("multiple window registration");
74 return NS_ERROR_FAILURE;
77 mTimeStamp++;
79 // Create window info struct and add to list of windows
80 nsWindowInfo* windowInfo = new nsWindowInfo(inWindow, mTimeStamp);
82 for (const auto& listener : mListeners.ForwardRange()) {
83 listener->OnOpenWindow(inWindow);
86 if (mOldestWindow)
87 windowInfo->InsertAfter(mOldestWindow->mOlder, nullptr);
88 else
89 mOldestWindow = windowInfo;
91 return NS_OK;
94 NS_IMETHODIMP
95 nsWindowMediator::UnregisterWindow(nsIAppWindow* inWindow) {
96 MOZ_RELEASE_ASSERT(NS_IsMainThread());
97 MOZ_ASSERT(mReady);
98 NS_ENSURE_STATE(mReady);
99 nsWindowInfo* info = GetInfoFor(inWindow);
100 if (info) return UnregisterWindow(info);
101 return NS_ERROR_INVALID_ARG;
104 nsresult nsWindowMediator::UnregisterWindow(nsWindowInfo* inInfo) {
105 // Inform the iterators
106 uint32_t index = 0;
107 while (index < mEnumeratorList.Length()) {
108 mEnumeratorList[index]->WindowRemoved(inInfo);
109 index++;
112 nsIAppWindow* window = inInfo->mWindow.get();
113 for (const auto& listener : mListeners.ForwardRange()) {
114 listener->OnCloseWindow(window);
117 // Remove from the lists and free up
118 if (inInfo == mOldestWindow) mOldestWindow = inInfo->mYounger;
119 if (inInfo == mTopmostWindow) mTopmostWindow = inInfo->mLower;
120 inInfo->Unlink(true, true);
121 if (inInfo == mOldestWindow) mOldestWindow = nullptr;
122 if (inInfo == mTopmostWindow) mTopmostWindow = nullptr;
123 delete inInfo;
125 return NS_OK;
128 nsWindowInfo* nsWindowMediator::GetInfoFor(nsIAppWindow* aWindow) {
129 nsWindowInfo *info, *listEnd;
131 if (!aWindow) return nullptr;
133 info = mOldestWindow;
134 listEnd = nullptr;
135 while (info != listEnd) {
136 if (info->mWindow.get() == aWindow) return info;
137 info = info->mYounger;
138 listEnd = mOldestWindow;
140 return nullptr;
143 nsWindowInfo* nsWindowMediator::GetInfoFor(nsIWidget* aWindow) {
144 nsWindowInfo *info, *listEnd;
146 if (!aWindow) return nullptr;
148 info = mOldestWindow;
149 listEnd = nullptr;
151 nsCOMPtr<nsIWidget> scanWidget;
152 while (info != listEnd) {
153 nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(info->mWindow));
154 if (base) base->GetMainWidget(getter_AddRefs(scanWidget));
155 if (aWindow == scanWidget.get()) return info;
156 info = info->mYounger;
157 listEnd = mOldestWindow;
159 return nullptr;
162 NS_IMETHODIMP
163 nsWindowMediator::GetEnumerator(const char16_t* inType,
164 nsISimpleEnumerator** outEnumerator) {
165 MOZ_RELEASE_ASSERT(NS_IsMainThread());
166 NS_ENSURE_ARG_POINTER(outEnumerator);
167 if (!mReady) {
168 // If we get here with mReady false, we most likely did observe
169 // xpcom-shutdown. We will return an empty enumerator such that
170 // we make happy Javascripts calling late without throwing.
171 return NS_NewEmptyEnumerator(outEnumerator);
173 RefPtr<nsAppShellWindowEnumerator> enumerator =
174 new nsASDOMWindowEarlyToLateEnumerator(inType, *this);
175 enumerator.forget(outEnumerator);
176 return NS_OK;
179 NS_IMETHODIMP
180 nsWindowMediator::GetAppWindowEnumerator(const char16_t* inType,
181 nsISimpleEnumerator** outEnumerator) {
182 MOZ_RELEASE_ASSERT(NS_IsMainThread());
183 NS_ENSURE_ARG_POINTER(outEnumerator);
184 if (!mReady) {
185 // If we get here with mReady false, we most likely did observe
186 // xpcom-shutdown. We will return an empty enumerator such that
187 // we make happy Javascripts calling late without throwing.
188 return NS_NewEmptyEnumerator(outEnumerator);
190 RefPtr<nsAppShellWindowEnumerator> enumerator =
191 new nsASAppWindowEarlyToLateEnumerator(inType, *this);
192 enumerator.forget(outEnumerator);
193 return NS_OK;
196 NS_IMETHODIMP
197 nsWindowMediator::GetZOrderAppWindowEnumerator(const char16_t* aWindowType,
198 bool aFrontToBack,
199 nsISimpleEnumerator** _retval) {
200 MOZ_RELEASE_ASSERT(NS_IsMainThread());
201 NS_ENSURE_ARG_POINTER(_retval);
202 if (!mReady) {
203 // If we get here with mReady false, we most likely did observe
204 // xpcom-shutdown. We will return an empty enumerator such that
205 // we make happy Javascripts calling late without throwing.
206 return NS_NewEmptyEnumerator(_retval);
208 RefPtr<nsAppShellWindowEnumerator> enumerator;
209 if (aFrontToBack)
210 enumerator = new nsASAppWindowFrontToBackEnumerator(aWindowType, *this);
211 else
212 enumerator = new nsASAppWindowBackToFrontEnumerator(aWindowType, *this);
214 enumerator.forget(_retval);
215 return NS_OK;
218 void nsWindowMediator::AddEnumerator(nsAppShellWindowEnumerator* inEnumerator) {
219 mEnumeratorList.AppendElement(inEnumerator);
222 int32_t nsWindowMediator::RemoveEnumerator(
223 nsAppShellWindowEnumerator* inEnumerator) {
224 return mEnumeratorList.RemoveElement(inEnumerator);
227 // Returns the window of type inType ( if null return any window type ) which
228 // has the most recent time stamp
229 NS_IMETHODIMP
230 nsWindowMediator::GetMostRecentWindow(const char16_t* inType,
231 mozIDOMWindowProxy** outWindow) {
232 MOZ_RELEASE_ASSERT(NS_IsMainThread());
233 NS_ENSURE_ARG_POINTER(outWindow);
234 *outWindow = nullptr;
235 if (!mReady) return NS_OK;
237 // Find the most window with the highest time stamp that matches
238 // the requested type
239 nsWindowInfo* info = MostRecentWindowInfo(inType, false);
240 if (info && info->mWindow) {
241 nsCOMPtr<nsPIDOMWindowOuter> DOMWindow;
242 if (NS_SUCCEEDED(GetDOMWindow(info->mWindow, DOMWindow))) {
243 DOMWindow.forget(outWindow);
244 return NS_OK;
246 return NS_ERROR_FAILURE;
249 return NS_OK;
252 NS_IMETHODIMP
253 nsWindowMediator::GetMostRecentBrowserWindow(mozIDOMWindowProxy** outWindow) {
254 nsresult rv = GetMostRecentWindow(u"navigator:browser", outWindow);
255 NS_ENSURE_SUCCESS(rv, rv);
257 #ifdef MOZ_WIDGET_ANDROID
258 if (!*outWindow) {
259 rv = GetMostRecentWindow(u"navigator:geckoview", outWindow);
260 NS_ENSURE_SUCCESS(rv, rv);
262 #endif
264 #ifdef MOZ_THUNDERBIRD
265 if (!*outWindow) {
266 rv = GetMostRecentWindow(u"mail:3pane", outWindow);
267 NS_ENSURE_SUCCESS(rv, rv);
269 #endif
271 return NS_OK;
274 NS_IMETHODIMP
275 nsWindowMediator::GetMostRecentNonPBWindow(const char16_t* aType,
276 mozIDOMWindowProxy** aWindow) {
277 MOZ_RELEASE_ASSERT(NS_IsMainThread());
278 NS_ENSURE_ARG_POINTER(aWindow);
279 *aWindow = nullptr;
281 nsWindowInfo* info = MostRecentWindowInfo(aType, true);
282 nsCOMPtr<nsPIDOMWindowOuter> domWindow;
283 if (info && info->mWindow) {
284 GetDOMWindow(info->mWindow, domWindow);
287 if (!domWindow) {
288 return NS_ERROR_FAILURE;
291 domWindow.forget(aWindow);
292 return NS_OK;
295 nsWindowInfo* nsWindowMediator::MostRecentWindowInfo(
296 const char16_t* inType, bool aSkipPrivateBrowsingOrClosed) {
297 int32_t lastTimeStamp = -1;
298 nsAutoString typeString(inType);
299 bool allWindows = !inType || typeString.IsEmpty();
301 // Find the most recent window with the highest time stamp that matches
302 // the requested type and has the correct browsing mode.
303 nsWindowInfo* searchInfo = mOldestWindow;
304 nsWindowInfo* listEnd = nullptr;
305 nsWindowInfo* foundInfo = nullptr;
306 for (; searchInfo != listEnd; searchInfo = searchInfo->mYounger) {
307 listEnd = mOldestWindow;
309 if (!allWindows && !searchInfo->TypeEquals(typeString)) {
310 continue;
312 if (searchInfo->mTimeStamp < lastTimeStamp) {
313 continue;
315 if (!searchInfo->mWindow) {
316 continue;
318 if (aSkipPrivateBrowsingOrClosed) {
319 nsCOMPtr<nsIDocShell> docShell;
320 searchInfo->mWindow->GetDocShell(getter_AddRefs(docShell));
321 nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
322 if (!loadContext || loadContext->UsePrivateBrowsing()) {
323 continue;
326 nsCOMPtr<nsPIDOMWindowOuter> piwindow = docShell->GetWindow();
327 if (!piwindow || piwindow->Closed()) {
328 continue;
332 foundInfo = searchInfo;
333 lastTimeStamp = searchInfo->mTimeStamp;
336 return foundInfo;
339 NS_IMETHODIMP
340 nsWindowMediator::GetOuterWindowWithId(uint64_t aWindowID,
341 mozIDOMWindowProxy** aWindow) {
342 RefPtr<nsGlobalWindowOuter> window =
343 nsGlobalWindowOuter::GetOuterWindowWithId(aWindowID);
344 window.forget(aWindow);
345 return NS_OK;
348 NS_IMETHODIMP
349 nsWindowMediator::GetCurrentInnerWindowWithId(uint64_t aWindowID,
350 mozIDOMWindow** aWindow) {
351 RefPtr<nsGlobalWindowInner> window =
352 nsGlobalWindowInner::GetInnerWindowWithId(aWindowID);
354 // not found
355 if (!window) return NS_OK;
357 nsCOMPtr<nsPIDOMWindowOuter> outer = window->GetOuterWindow();
358 NS_ENSURE_TRUE(outer, NS_ERROR_UNEXPECTED);
360 // outer is already using another inner, so it's same as not found
361 if (outer->GetCurrentInnerWindow() != window) return NS_OK;
363 window.forget(aWindow);
364 return NS_OK;
367 NS_IMETHODIMP
368 nsWindowMediator::UpdateWindowTimeStamp(nsIAppWindow* inWindow) {
369 MOZ_RELEASE_ASSERT(NS_IsMainThread());
370 MOZ_ASSERT(mReady);
371 NS_ENSURE_STATE(mReady);
372 nsWindowInfo* info = GetInfoFor(inWindow);
373 if (info) {
374 // increment the window's time stamp
375 info->mTimeStamp = ++mTimeStamp;
376 return NS_OK;
378 return NS_ERROR_FAILURE;
381 NS_IMPL_ISUPPORTS(nsWindowMediator, nsIWindowMediator, nsIObserver,
382 nsISupportsWeakReference)
384 NS_IMETHODIMP
385 nsWindowMediator::AddListener(nsIWindowMediatorListener* aListener) {
386 NS_ENSURE_ARG_POINTER(aListener);
388 mListeners.AppendElement(aListener);
390 return NS_OK;
393 NS_IMETHODIMP
394 nsWindowMediator::RemoveListener(nsIWindowMediatorListener* aListener) {
395 NS_ENSURE_ARG_POINTER(aListener);
397 mListeners.RemoveElement(aListener);
399 return NS_OK;
402 NS_IMETHODIMP
403 nsWindowMediator::Observe(nsISupports* aSubject, const char* aTopic,
404 const char16_t* aData) {
405 if (!strcmp(aTopic, "xpcom-shutdown") && mReady) {
406 MOZ_RELEASE_ASSERT(NS_IsMainThread());
407 while (mOldestWindow) UnregisterWindow(mOldestWindow);
408 mReady = false;
410 return NS_OK;