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