xpfe/appshell/src/nsWindowMediator.cpp

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

mercurial