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