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