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