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/. */
5 
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 "nsGlobalWindow.h"
20 
21 #include "nsIDocShell.h"
22 #include "nsIInterfaceRequestor.h"
23 #include "nsIInterfaceRequestorUtils.h"
24 #include "nsIAppWindow.h"
25 
26 using namespace mozilla;
27 
GetDOMWindow(nsIAppWindow * inWindow,nsCOMPtr<nsPIDOMWindowOuter> & outDOMWindow)28 nsresult nsWindowMediator::GetDOMWindow(
29     nsIAppWindow* inWindow, nsCOMPtr<nsPIDOMWindowOuter>& outDOMWindow) {
30   nsCOMPtr<nsIDocShell> docShell;
31 
32   outDOMWindow = nullptr;
33   inWindow->GetDocShell(getter_AddRefs(docShell));
34   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
35 
36   outDOMWindow = docShell->GetWindow();
37   return outDOMWindow ? NS_OK : NS_ERROR_FAILURE;
38 }
39 
nsWindowMediator()40 nsWindowMediator::nsWindowMediator()
41     : mEnumeratorList(),
42       mOldestWindow(nullptr),
43       mTopmostWindow(nullptr),
44       mTimeStamp(0),
45       mSortingZOrder(false),
46       mReady(false) {}
47 
~nsWindowMediator()48 nsWindowMediator::~nsWindowMediator() {
49   while (mOldestWindow) UnregisterWindow(mOldestWindow);
50 }
51 
Init()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);
59 
60   mReady = true;
61   return NS_OK;
62 }
63 
RegisterWindow(nsIAppWindow * inWindow)64 NS_IMETHODIMP nsWindowMediator::RegisterWindow(nsIAppWindow* inWindow) {
65   MOZ_RELEASE_ASSERT(NS_IsMainThread());
66 
67   if (!mReady) {
68     NS_ERROR("Mediator is not initialized or about to die.");
69     return NS_ERROR_FAILURE;
70   }
71 
72   if (GetInfoFor(inWindow)) {
73     NS_ERROR("multiple window registration");
74     return NS_ERROR_FAILURE;
75   }
76 
77   mTimeStamp++;
78 
79   // Create window info struct and add to list of windows
80   nsWindowInfo* windowInfo = new nsWindowInfo(inWindow, mTimeStamp);
81 
82   for (const auto& listener : mListeners.ForwardRange()) {
83     listener->OnOpenWindow(inWindow);
84   }
85 
86   if (mOldestWindow)
87     windowInfo->InsertAfter(mOldestWindow->mOlder, nullptr);
88   else
89     mOldestWindow = windowInfo;
90 
91   return NS_OK;
92 }
93 
94 NS_IMETHODIMP
UnregisterWindow(nsIAppWindow * inWindow)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;
102 }
103 
UnregisterWindow(nsWindowInfo * inInfo)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++;
110   }
111 
112   nsIAppWindow* window = inInfo->mWindow.get();
113   for (const auto& listener : mListeners.ForwardRange()) {
114     listener->OnCloseWindow(window);
115   }
116 
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;
124 
125   return NS_OK;
126 }
127 
GetInfoFor(nsIAppWindow * aWindow)128 nsWindowInfo* nsWindowMediator::GetInfoFor(nsIAppWindow* aWindow) {
129   nsWindowInfo *info, *listEnd;
130 
131   if (!aWindow) return nullptr;
132 
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;
139   }
140   return nullptr;
141 }
142 
GetInfoFor(nsIWidget * aWindow)143 nsWindowInfo* nsWindowMediator::GetInfoFor(nsIWidget* aWindow) {
144   nsWindowInfo *info, *listEnd;
145 
146   if (!aWindow) return nullptr;
147 
148   info = mOldestWindow;
149   listEnd = nullptr;
150 
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;
158   }
159   return nullptr;
160 }
161 
162 NS_IMETHODIMP
GetEnumerator(const char16_t * inType,nsISimpleEnumerator ** outEnumerator)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);
172   }
173   RefPtr<nsAppShellWindowEnumerator> enumerator =
174       new nsASDOMWindowEarlyToLateEnumerator(inType, *this);
175   enumerator.forget(outEnumerator);
176   return NS_OK;
177 }
178 
179 NS_IMETHODIMP
GetAppWindowEnumerator(const char16_t * inType,nsISimpleEnumerator ** outEnumerator)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);
189   }
190   RefPtr<nsAppShellWindowEnumerator> enumerator =
191       new nsASAppWindowEarlyToLateEnumerator(inType, *this);
192   enumerator.forget(outEnumerator);
193   return NS_OK;
194 }
195 
196 NS_IMETHODIMP
GetZOrderAppWindowEnumerator(const char16_t * aWindowType,bool aFrontToBack,nsISimpleEnumerator ** _retval)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);
207   }
208   RefPtr<nsAppShellWindowEnumerator> enumerator;
209   if (aFrontToBack)
210     enumerator = new nsASAppWindowFrontToBackEnumerator(aWindowType, *this);
211   else
212     enumerator = new nsASAppWindowBackToFrontEnumerator(aWindowType, *this);
213 
214   enumerator.forget(_retval);
215   return NS_OK;
216 }
217 
AddEnumerator(nsAppShellWindowEnumerator * inEnumerator)218 void nsWindowMediator::AddEnumerator(nsAppShellWindowEnumerator* inEnumerator) {
219   mEnumeratorList.AppendElement(inEnumerator);
220 }
221 
RemoveEnumerator(nsAppShellWindowEnumerator * inEnumerator)222 int32_t nsWindowMediator::RemoveEnumerator(
223     nsAppShellWindowEnumerator* inEnumerator) {
224   return mEnumeratorList.RemoveElement(inEnumerator);
225 }
226 
227 // Returns the window of type inType ( if null return any window type ) which
228 // has the most recent time stamp
229 NS_IMETHODIMP
GetMostRecentWindow(const char16_t * inType,mozIDOMWindowProxy ** outWindow)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;
236 
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;
245     }
246     return NS_ERROR_FAILURE;
247   }
248 
249   return NS_OK;
250 }
251 
252 NS_IMETHODIMP
GetMostRecentBrowserWindow(mozIDOMWindowProxy ** outWindow)253 nsWindowMediator::GetMostRecentBrowserWindow(mozIDOMWindowProxy** outWindow) {
254   nsresult rv = GetMostRecentWindow(u"navigator:browser", outWindow);
255   NS_ENSURE_SUCCESS(rv, rv);
256 
257 #ifdef MOZ_WIDGET_ANDROID
258   if (!*outWindow) {
259     rv = GetMostRecentWindow(u"navigator:geckoview", outWindow);
260     NS_ENSURE_SUCCESS(rv, rv);
261   }
262 #endif
263 
264 #ifdef MOZ_THUNDERBIRD
265   if (!*outWindow) {
266     rv = GetMostRecentWindow(u"mail:3pane", outWindow);
267     NS_ENSURE_SUCCESS(rv, rv);
268   }
269 #endif
270 
271   return NS_OK;
272 }
273 
274 NS_IMETHODIMP
GetMostRecentNonPBWindow(const char16_t * aType,mozIDOMWindowProxy ** aWindow)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;
280 
281   nsWindowInfo* info = MostRecentWindowInfo(aType, true);
282   nsCOMPtr<nsPIDOMWindowOuter> domWindow;
283   if (info && info->mWindow) {
284     GetDOMWindow(info->mWindow, domWindow);
285   }
286 
287   if (!domWindow) {
288     return NS_ERROR_FAILURE;
289   }
290 
291   domWindow.forget(aWindow);
292   return NS_OK;
293 }
294 
MostRecentWindowInfo(const char16_t * inType,bool aSkipPrivateBrowsingOrClosed)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();
300 
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;
308 
309     if (!allWindows && !searchInfo->TypeEquals(typeString)) {
310       continue;
311     }
312     if (searchInfo->mTimeStamp < lastTimeStamp) {
313       continue;
314     }
315     if (!searchInfo->mWindow) {
316       continue;
317     }
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;
324       }
325 
326       nsCOMPtr<nsPIDOMWindowOuter> piwindow = docShell->GetWindow();
327       if (!piwindow || piwindow->Closed()) {
328         continue;
329       }
330     }
331 
332     foundInfo = searchInfo;
333     lastTimeStamp = searchInfo->mTimeStamp;
334   }
335 
336   return foundInfo;
337 }
338 
339 NS_IMETHODIMP
GetOuterWindowWithId(uint64_t aWindowID,mozIDOMWindowProxy ** aWindow)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;
346 }
347 
348 NS_IMETHODIMP
GetCurrentInnerWindowWithId(uint64_t aWindowID,mozIDOMWindow ** aWindow)349 nsWindowMediator::GetCurrentInnerWindowWithId(uint64_t aWindowID,
350                                               mozIDOMWindow** aWindow) {
351   RefPtr<nsGlobalWindowInner> window =
352       nsGlobalWindowInner::GetInnerWindowWithId(aWindowID);
353 
354   // not found
355   if (!window) return NS_OK;
356 
357   nsCOMPtr<nsPIDOMWindowOuter> outer = window->GetOuterWindow();
358   NS_ENSURE_TRUE(outer, NS_ERROR_UNEXPECTED);
359 
360   // outer is already using another inner, so it's same as not found
361   if (outer->GetCurrentInnerWindow() != window) return NS_OK;
362 
363   window.forget(aWindow);
364   return NS_OK;
365 }
366 
367 NS_IMETHODIMP
UpdateWindowTimeStamp(nsIAppWindow * inWindow)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;
377   }
378   return NS_ERROR_FAILURE;
379 }
380 
381 /* This method's plan is to intervene only when absolutely necessary.
382    We will get requests to place our windows behind unknown windows.
383    For the most part, we need to leave those alone (turning them into
384    explicit requests to be on top breaks Windows.) So generally we
385    calculate a change as seldom as possible.
386 */
387 NS_IMETHODIMP
CalculateZPosition(nsIAppWindow * inWindow,uint32_t inPosition,nsIWidget * inBelow,uint32_t * outPosition,nsIWidget ** outBelow,bool * outAltered)388 nsWindowMediator::CalculateZPosition(nsIAppWindow* inWindow,
389                                      uint32_t inPosition, nsIWidget* inBelow,
390                                      uint32_t* outPosition,
391                                      nsIWidget** outBelow, bool* outAltered) {
392   MOZ_RELEASE_ASSERT(NS_IsMainThread());
393   NS_ENSURE_ARG_POINTER(outBelow);
394   MOZ_ASSERT(mReady);
395   NS_ENSURE_STATE(mReady);
396 
397   *outBelow = nullptr;
398 
399   if (!inWindow || !outPosition || !outAltered) return NS_ERROR_NULL_POINTER;
400 
401   if (inPosition != nsIWindowMediator::zLevelTop &&
402       inPosition != nsIWindowMediator::zLevelBottom &&
403       inPosition != nsIWindowMediator::zLevelBelow)
404     return NS_ERROR_INVALID_ARG;
405 
406   nsWindowInfo* info = mTopmostWindow;
407   nsIAppWindow* belowWindow = nullptr;
408   bool found = false;
409   nsresult result = NS_OK;
410 
411   *outPosition = inPosition;
412   *outAltered = false;
413 
414   if (mSortingZOrder) {  // don't fight SortZOrder()
415     *outBelow = inBelow;
416     NS_IF_ADDREF(*outBelow);
417     return NS_OK;
418   }
419 
420   uint32_t inZ;
421   GetZLevel(inWindow, &inZ);
422 
423   if (inPosition == nsIWindowMediator::zLevelBelow) {
424     // locate inBelow. use topmost if it can't be found or isn't in the
425     // z-order list
426     info = GetInfoFor(inBelow);
427     if (!info || (info->mYounger != info && info->mLower == info))
428       info = mTopmostWindow;
429     else
430       found = true;
431 
432     if (!found) {
433       /* Treat unknown windows as a request to be on top.
434          Not as it should be, but that's what Windows gives us.
435          Note we change inPosition, but not *outPosition. This forces
436          us to go through the "on top" calculation just below, without
437          necessarily changing the output parameters. */
438       inPosition = nsIWindowMediator::zLevelTop;
439     }
440   }
441 
442   if (inPosition == nsIWindowMediator::zLevelTop) {
443     if (mTopmostWindow && mTopmostWindow->mZLevel > inZ) {
444       // asked for topmost, can't have it. locate highest allowed position.
445       do {
446         if (info->mZLevel <= inZ) break;
447         info = info->mLower;
448       } while (info != mTopmostWindow);
449 
450       *outPosition = nsIWindowMediator::zLevelBelow;
451       belowWindow = info->mHigher->mWindow;
452       *outAltered = true;
453     }
454   } else if (inPosition == nsIWindowMediator::zLevelBottom) {
455     if (mTopmostWindow && mTopmostWindow->mHigher->mZLevel < inZ) {
456       // asked for bottommost, can't have it. locate lowest allowed position.
457       do {
458         info = info->mHigher;
459         if (info->mZLevel >= inZ) break;
460       } while (info != mTopmostWindow);
461 
462       *outPosition = nsIWindowMediator::zLevelBelow;
463       belowWindow = info->mWindow;
464       *outAltered = true;
465     }
466   } else {
467     unsigned long relativeZ;
468 
469     // check that we're in the right z-plane
470     if (found) {
471       belowWindow = info->mWindow;
472       relativeZ = info->mZLevel;
473       if (relativeZ > inZ) {
474         // might be OK. is lower window, if any, lower?
475         if (info->mLower != info && info->mLower->mZLevel > inZ) {
476           do {
477             if (info->mZLevel <= inZ) break;
478             info = info->mLower;
479           } while (info != mTopmostWindow);
480 
481           belowWindow = info->mHigher->mWindow;
482           *outAltered = true;
483         }
484       } else if (relativeZ < inZ) {
485         // nope. look for a higher window to be behind.
486         do {
487           info = info->mHigher;
488           if (info->mZLevel >= inZ) break;
489         } while (info != mTopmostWindow);
490 
491         if (info->mZLevel >= inZ)
492           belowWindow = info->mWindow;
493         else
494           *outPosition = nsIWindowMediator::zLevelTop;
495         *outAltered = true;
496       }  // else they're equal, so it's OK
497     }
498   }
499 
500   if (NS_SUCCEEDED(result) && belowWindow) {
501     nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(belowWindow));
502     if (base)
503       base->GetMainWidget(outBelow);
504     else
505       result = NS_ERROR_NO_INTERFACE;
506   }
507 
508   return result;
509 }
510 
511 NS_IMETHODIMP
SetZPosition(nsIAppWindow * inWindow,uint32_t inPosition,nsIAppWindow * inBelow)512 nsWindowMediator::SetZPosition(nsIAppWindow* inWindow, uint32_t inPosition,
513                                nsIAppWindow* inBelow) {
514   MOZ_RELEASE_ASSERT(NS_IsMainThread());
515   nsWindowInfo *inInfo, *belowInfo;
516 
517   if ((inPosition != nsIWindowMediator::zLevelTop &&
518        inPosition != nsIWindowMediator::zLevelBottom &&
519        inPosition != nsIWindowMediator::zLevelBelow) ||
520       !inWindow) {
521     return NS_ERROR_INVALID_ARG;
522   }
523 
524   if (mSortingZOrder)  // don't fight SortZOrder()
525     return NS_OK;
526 
527   MOZ_ASSERT(mReady);
528   NS_ENSURE_STATE(mReady);
529 
530   /* Locate inWindow and unlink it from the z-order list.
531      It's important we look for it in the age list, not the z-order list.
532      This is because the former is guaranteed complete, while
533      now may be this window's first exposure to the latter. */
534   inInfo = GetInfoFor(inWindow);
535   if (!inInfo) return NS_ERROR_INVALID_ARG;
536 
537   // locate inBelow, place inWindow behind it
538   if (inPosition == nsIWindowMediator::zLevelBelow) {
539     belowInfo = GetInfoFor(inBelow);
540     // it had better also be in the z-order list
541     if (belowInfo && belowInfo->mYounger != belowInfo &&
542         belowInfo->mLower == belowInfo) {
543       belowInfo = nullptr;
544     }
545     if (!belowInfo) {
546       if (inBelow)
547         return NS_ERROR_INVALID_ARG;
548       else
549         inPosition = nsIWindowMediator::zLevelTop;
550     }
551   }
552   if (inPosition == nsIWindowMediator::zLevelTop ||
553       inPosition == nsIWindowMediator::zLevelBottom)
554     belowInfo = mTopmostWindow ? mTopmostWindow->mHigher : nullptr;
555 
556   if (inInfo != belowInfo) {
557     inInfo->Unlink(false, true);
558     inInfo->InsertAfter(nullptr, belowInfo);
559   }
560   if (inPosition == nsIWindowMediator::zLevelTop) mTopmostWindow = inInfo;
561 
562   return NS_OK;
563 }
564 
565 NS_IMETHODIMP
GetZLevel(nsIAppWindow * aWindow,uint32_t * _retval)566 nsWindowMediator::GetZLevel(nsIAppWindow* aWindow, uint32_t* _retval) {
567   NS_ENSURE_ARG_POINTER(_retval);
568   *_retval = nsIAppWindow::normalZ;
569   // This can fail during window destruction.
570   nsWindowInfo* info = GetInfoFor(aWindow);
571   if (info) {
572     *_retval = info->mZLevel;
573   }
574   return NS_OK;
575 }
576 
577 NS_IMETHODIMP
SetZLevel(nsIAppWindow * aWindow,uint32_t aZLevel)578 nsWindowMediator::SetZLevel(nsIAppWindow* aWindow, uint32_t aZLevel) {
579   MOZ_RELEASE_ASSERT(NS_IsMainThread());
580   MOZ_ASSERT(mReady);
581   NS_ENSURE_STATE(mReady);
582 
583   nsWindowInfo* info = GetInfoFor(aWindow);
584   NS_ASSERTION(info, "setting z level of unregistered window");
585   if (!info) return NS_ERROR_FAILURE;
586 
587   if (info->mZLevel != aZLevel) {
588     bool lowered = info->mZLevel > aZLevel;
589     info->mZLevel = aZLevel;
590     if (lowered)
591       SortZOrderFrontToBack();
592     else
593       SortZOrderBackToFront();
594   }
595   return NS_OK;
596 }
597 
598 /*   Fix potentially out-of-order windows by performing an insertion sort
599    on the z-order list. The method will work no matter how broken the
600    list, but its assumed usage is immediately after one window's z level
601    has been changed, so one window is potentially out of place. Such a sort
602    is most efficiently done in a particular direction. Use this one
603    if a window's z level has just been reduced, so the sort is most efficiently
604    done front to back.
605      Note it's hardly worth going to all the trouble to write two versions
606    of this method except that if we choose the inefficient sorting direction,
607    on slow systems windows could visibly bubble around the window that
608    was moved.
609 */
SortZOrderFrontToBack()610 void nsWindowMediator::SortZOrderFrontToBack() {
611   nsWindowInfo *scan,  // scans list looking for problems
612       *search,         // searches for correct placement for scan window
613       *prev,           // previous search element
614       *lowest;         // bottom-most window in list
615   bool finished;
616 
617   if (!mTopmostWindow)  // early during program execution there's no z list yet
618     return;  // there's also only one window, so this is not dangerous
619 
620   mSortingZOrder = true;
621 
622   /* Step through the list from top to bottom. If we find a window which
623      should be moved down in the list, move it to its highest legal position. */
624   do {
625     finished = true;
626     lowest = mTopmostWindow->mHigher;
627     scan = mTopmostWindow;
628     while (scan != lowest) {
629       uint32_t scanZ = scan->mZLevel;
630       if (scanZ < scan->mLower->mZLevel) {  // out of order
631         search = scan->mLower;
632         do {
633           prev = search;
634           search = search->mLower;
635         } while (prev != lowest && scanZ < search->mZLevel);
636 
637         // reposition |scan| within the list
638         if (scan == mTopmostWindow) mTopmostWindow = scan->mLower;
639         scan->Unlink(false, true);
640         scan->InsertAfter(nullptr, prev);
641 
642         // fix actual window order
643         nsCOMPtr<nsIBaseWindow> base;
644         nsCOMPtr<nsIWidget> scanWidget;
645         nsCOMPtr<nsIWidget> prevWidget;
646         base = do_QueryInterface(scan->mWindow);
647         if (base) base->GetMainWidget(getter_AddRefs(scanWidget));
648         base = do_QueryInterface(prev->mWindow);
649         if (base) base->GetMainWidget(getter_AddRefs(prevWidget));
650         if (scanWidget)
651           scanWidget->PlaceBehind(eZPlacementBelow, prevWidget, false);
652 
653         finished = false;
654         break;
655       }
656       scan = scan->mLower;
657     }
658   } while (!finished);
659 
660   mSortingZOrder = false;
661 }
662 
663 // see comment for SortZOrderFrontToBack
SortZOrderBackToFront()664 void nsWindowMediator::SortZOrderBackToFront() {
665   nsWindowInfo *scan,  // scans list looking for problems
666       *search,         // searches for correct placement for scan window
667       *lowest;         // bottom-most window in list
668   bool finished;
669 
670   if (!mTopmostWindow)  // early during program execution there's no z list yet
671     return;  // there's also only one window, so this is not dangerous
672 
673   mSortingZOrder = true;
674 
675   /* Step through the list from bottom to top. If we find a window which
676      should be moved up in the list, move it to its lowest legal position. */
677   do {
678     finished = true;
679     lowest = mTopmostWindow->mHigher;
680     scan = lowest;
681     while (scan != mTopmostWindow) {
682       uint32_t scanZ = scan->mZLevel;
683       if (scanZ > scan->mHigher->mZLevel) {  // out of order
684         search = scan;
685         do {
686           search = search->mHigher;
687         } while (search != lowest && scanZ > search->mZLevel);
688 
689         // reposition |scan| within the list
690         if (scan != search && scan != search->mLower) {
691           scan->Unlink(false, true);
692           scan->InsertAfter(nullptr, search);
693         }
694         if (search == lowest) mTopmostWindow = scan;
695 
696         // fix actual window order
697         nsCOMPtr<nsIBaseWindow> base;
698         nsCOMPtr<nsIWidget> scanWidget;
699         nsCOMPtr<nsIWidget> searchWidget;
700         base = do_QueryInterface(scan->mWindow);
701         if (base) base->GetMainWidget(getter_AddRefs(scanWidget));
702         if (mTopmostWindow != scan) {
703           base = do_QueryInterface(search->mWindow);
704           if (base) base->GetMainWidget(getter_AddRefs(searchWidget));
705         }
706         if (scanWidget)
707           scanWidget->PlaceBehind(eZPlacementBelow, searchWidget, false);
708         finished = false;
709         break;
710       }
711       scan = scan->mHigher;
712     }
713   } while (!finished);
714 
715   mSortingZOrder = false;
716 }
717 
NS_IMPL_ISUPPORTS(nsWindowMediator,nsIWindowMediator,nsIObserver,nsISupportsWeakReference)718 NS_IMPL_ISUPPORTS(nsWindowMediator, nsIWindowMediator, nsIObserver,
719                   nsISupportsWeakReference)
720 
721 NS_IMETHODIMP
722 nsWindowMediator::AddListener(nsIWindowMediatorListener* aListener) {
723   NS_ENSURE_ARG_POINTER(aListener);
724 
725   mListeners.AppendElement(aListener);
726 
727   return NS_OK;
728 }
729 
730 NS_IMETHODIMP
RemoveListener(nsIWindowMediatorListener * aListener)731 nsWindowMediator::RemoveListener(nsIWindowMediatorListener* aListener) {
732   NS_ENSURE_ARG_POINTER(aListener);
733 
734   mListeners.RemoveElement(aListener);
735 
736   return NS_OK;
737 }
738 
739 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)740 nsWindowMediator::Observe(nsISupports* aSubject, const char* aTopic,
741                           const char16_t* aData) {
742   if (!strcmp(aTopic, "xpcom-shutdown") && mReady) {
743     MOZ_RELEASE_ASSERT(NS_IsMainThread());
744     while (mOldestWindow) UnregisterWindow(mOldestWindow);
745     mReady = false;
746   }
747   return NS_OK;
748 }
749