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