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/. */
7 #include "nsEnumeratorUtils.h"
9 #include "nsReadableUtils.h"
10 #include "nsUnicharUtils.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),
48 nsWindowMediator::~nsWindowMediator() {
49 while (mOldestWindow
) UnregisterWindow(mOldestWindow
);
52 nsresult
nsWindowMediator::Init() {
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
);
64 NS_IMETHODIMP
nsWindowMediator::RegisterWindow(nsIAppWindow
* inWindow
) {
65 MOZ_RELEASE_ASSERT(NS_IsMainThread());
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
;
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
);
87 windowInfo
->InsertAfter(mOldestWindow
->mOlder
, nullptr);
89 mOldestWindow
= windowInfo
;
95 nsWindowMediator::UnregisterWindow(nsIAppWindow
* inWindow
) {
96 MOZ_RELEASE_ASSERT(NS_IsMainThread());
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
107 while (index
< mEnumeratorList
.Length()) {
108 mEnumeratorList
[index
]->WindowRemoved(inInfo
);
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;
128 nsWindowInfo
* nsWindowMediator::GetInfoFor(nsIAppWindow
* aWindow
) {
129 nsWindowInfo
*info
, *listEnd
;
131 if (!aWindow
) return nullptr;
133 info
= mOldestWindow
;
135 while (info
!= listEnd
) {
136 if (info
->mWindow
.get() == aWindow
) return info
;
137 info
= info
->mYounger
;
138 listEnd
= mOldestWindow
;
143 nsWindowInfo
* nsWindowMediator::GetInfoFor(nsIWidget
* aWindow
) {
144 nsWindowInfo
*info
, *listEnd
;
146 if (!aWindow
) return nullptr;
148 info
= mOldestWindow
;
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
;
163 nsWindowMediator::GetEnumerator(const char16_t
* inType
,
164 nsISimpleEnumerator
** outEnumerator
) {
165 MOZ_RELEASE_ASSERT(NS_IsMainThread());
166 NS_ENSURE_ARG_POINTER(outEnumerator
);
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
);
180 nsWindowMediator::GetAppWindowEnumerator(const char16_t
* inType
,
181 nsISimpleEnumerator
** outEnumerator
) {
182 MOZ_RELEASE_ASSERT(NS_IsMainThread());
183 NS_ENSURE_ARG_POINTER(outEnumerator
);
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
);
197 nsWindowMediator::GetZOrderAppWindowEnumerator(const char16_t
* aWindowType
,
199 nsISimpleEnumerator
** _retval
) {
200 MOZ_RELEASE_ASSERT(NS_IsMainThread());
201 NS_ENSURE_ARG_POINTER(_retval
);
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
;
210 enumerator
= new nsASAppWindowFrontToBackEnumerator(aWindowType
, *this);
212 enumerator
= new nsASAppWindowBackToFrontEnumerator(aWindowType
, *this);
214 enumerator
.forget(_retval
);
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
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
);
246 return NS_ERROR_FAILURE
;
253 nsWindowMediator::GetMostRecentBrowserWindow(mozIDOMWindowProxy
** outWindow
) {
254 nsresult rv
= GetMostRecentWindow(u
"navigator:browser", outWindow
);
255 NS_ENSURE_SUCCESS(rv
, rv
);
257 #ifdef MOZ_WIDGET_ANDROID
259 rv
= GetMostRecentWindow(u
"navigator:geckoview", outWindow
);
260 NS_ENSURE_SUCCESS(rv
, rv
);
264 #ifdef MOZ_THUNDERBIRD
266 rv
= GetMostRecentWindow(u
"mail:3pane", outWindow
);
267 NS_ENSURE_SUCCESS(rv
, rv
);
275 nsWindowMediator::GetMostRecentNonPBWindow(const char16_t
* aType
,
276 mozIDOMWindowProxy
** aWindow
) {
277 MOZ_RELEASE_ASSERT(NS_IsMainThread());
278 NS_ENSURE_ARG_POINTER(aWindow
);
281 nsWindowInfo
* info
= MostRecentWindowInfo(aType
, true);
282 nsCOMPtr
<nsPIDOMWindowOuter
> domWindow
;
283 if (info
&& info
->mWindow
) {
284 GetDOMWindow(info
->mWindow
, domWindow
);
288 return NS_ERROR_FAILURE
;
291 domWindow
.forget(aWindow
);
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
)) {
312 if (searchInfo
->mTimeStamp
< lastTimeStamp
) {
315 if (!searchInfo
->mWindow
) {
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()) {
326 nsCOMPtr
<nsPIDOMWindowOuter
> piwindow
= docShell
->GetWindow();
327 if (!piwindow
|| piwindow
->Closed()) {
332 foundInfo
= searchInfo
;
333 lastTimeStamp
= searchInfo
->mTimeStamp
;
340 nsWindowMediator::GetOuterWindowWithId(uint64_t aWindowID
,
341 mozIDOMWindowProxy
** aWindow
) {
342 RefPtr
<nsGlobalWindowOuter
> window
=
343 nsGlobalWindowOuter::GetOuterWindowWithId(aWindowID
);
344 window
.forget(aWindow
);
349 nsWindowMediator::GetCurrentInnerWindowWithId(uint64_t aWindowID
,
350 mozIDOMWindow
** aWindow
) {
351 RefPtr
<nsGlobalWindowInner
> window
=
352 nsGlobalWindowInner::GetInnerWindowWithId(aWindowID
);
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
);
368 nsWindowMediator::UpdateWindowTimeStamp(nsIAppWindow
* inWindow
) {
369 MOZ_RELEASE_ASSERT(NS_IsMainThread());
371 NS_ENSURE_STATE(mReady
);
372 nsWindowInfo
* info
= GetInfoFor(inWindow
);
374 // increment the window's time stamp
375 info
->mTimeStamp
= ++mTimeStamp
;
378 return NS_ERROR_FAILURE
;
381 NS_IMPL_ISUPPORTS(nsWindowMediator
, nsIWindowMediator
, nsIObserver
,
382 nsISupportsWeakReference
)
385 nsWindowMediator::AddListener(nsIWindowMediatorListener
* aListener
) {
386 NS_ENSURE_ARG_POINTER(aListener
);
388 mListeners
.AppendElement(aListener
);
394 nsWindowMediator::RemoveListener(nsIWindowMediatorListener
* aListener
) {
395 NS_ENSURE_ARG_POINTER(aListener
);
397 mListeners
.RemoveElement(aListener
);
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
);