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