Back to index

lightning-sunbird  0.9+nobinonly
nsWindowMediator.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsCOMPtr.h"
00040 #include "nsAutoLock.h"
00041 #include "nsString.h"
00042 #include "nsReadableUtils.h"
00043 #include "nsUnicharUtils.h"
00044 #include "nsVoidArray.h"
00045 #include "nsIBaseWindow.h"
00046 #include "nsIWidget.h"
00047 #include "nsIDOMWindow.h"
00048 #include "nsIDOMWindowInternal.h"
00049 #include "nsIDOMElement.h"
00050 #include "nsIDocumentViewer.h"
00051 #include "nsIDocument.h"
00052 #include "nsIDOMDocument.h"
00053 #include "nsIServiceManager.h"
00054 #include "nsISimpleEnumerator.h"
00055 #include "nsAppShellWindowEnumerator.h"
00056 #include "nsWindowMediator.h"
00057 #include "nsIWindowMediatorListener.h"
00058 #include "nsXPIDLString.h"
00059 
00060 // Interfaces Needed
00061 #include "nsIDocShell.h"
00062 #include "nsIInterfaceRequestor.h"
00063 #include "nsIInterfaceRequestorUtils.h"
00064 #include "nsIXULWindow.h"
00065 
00066 
00067 static nsresult GetDOMWindow( nsIXULWindow* inWindow,
00068                   nsCOMPtr< nsIDOMWindowInternal>& outDOMWindow);
00069 
00070 static PRBool notifyOpenWindow(nsISupports *aElement, void* aData);
00071 static PRBool notifyCloseWindow(nsISupports *aElement, void* aData);
00072 static PRBool notifyWindowTitleChange(nsISupports *aElement, void* aData);
00073 
00074 // for notifyWindowTitleChange
00075 struct windowData {
00076   nsIXULWindow* mWindow;
00077   const PRUnichar *mTitle;
00078 };
00079 
00080 
00081 nsresult
00082 GetDOMWindow( nsIXULWindow* inWindow, nsCOMPtr< nsIDOMWindowInternal>& outDOMWindow)
00083 {
00084   nsCOMPtr<nsIDocShell> docShell;
00085 
00086   inWindow->GetDocShell(getter_AddRefs(docShell));
00087    outDOMWindow = do_GetInterface(docShell);
00088    return outDOMWindow ? NS_OK : NS_ERROR_FAILURE;
00089 }
00090 
00091 PRInt32   nsWindowMediator::gRefCnt = 0;
00092 
00093 nsWindowMediator::nsWindowMediator() :
00094   mEnumeratorList(), mOldestWindow(0), mTopmostWindow(0),
00095   mTimeStamp(0), mSortingZOrder(PR_FALSE), mListLock(0)
00096 {
00097    // This should really be done in the static constructor fn.
00098    nsresult rv;
00099    rv = Init();
00100    NS_ASSERTION(NS_SUCCEEDED(rv), "uh oh, couldn't Init() for some reason");
00101 }
00102 
00103 nsWindowMediator::~nsWindowMediator()
00104 {
00105   if (--gRefCnt == 0) {
00106 
00107     // Delete data
00108     while (mOldestWindow)
00109       UnregisterWindow(mOldestWindow);
00110 
00111     if (mListLock)
00112       PR_DestroyLock(mListLock);
00113   }
00114 }
00115 
00116 
00117 
00118 NS_IMETHODIMP nsWindowMediator::RegisterWindow(nsIXULWindow* inWindow)
00119 {
00120   if (GetInfoFor(inWindow)) {
00121     NS_ERROR("multiple window registration");
00122     return NS_ERROR_FAILURE;
00123   }
00124 
00125   mTimeStamp++;
00126 
00127   // Create window info struct and add to list of windows
00128   nsWindowInfo* windowInfo = new nsWindowInfo (inWindow, mTimeStamp);
00129   if (windowInfo == NULL)
00130     return NS_ERROR_OUT_OF_MEMORY;
00131 
00132   if (mListeners) {
00133     windowData winData = { inWindow, nsnull };
00134     mListeners->EnumerateForwards(notifyOpenWindow, (void*)&winData);
00135   }
00136   
00137   nsAutoLock lock(mListLock);
00138   if (mOldestWindow)
00139     windowInfo->InsertAfter(mOldestWindow->mOlder, 0);
00140   else
00141     mOldestWindow = windowInfo;
00142 
00143   return NS_OK;
00144 }
00145 
00146 
00147 
00148 NS_IMETHODIMP
00149 nsWindowMediator::UnregisterWindow(nsIXULWindow* inWindow)
00150 {
00151   nsAutoLock lock(mListLock);
00152   nsWindowInfo *info = GetInfoFor(inWindow);
00153   if (info)
00154     return UnregisterWindow(info);
00155   return NS_ERROR_INVALID_ARG;
00156 }
00157 
00158 
00159 
00160 NS_IMETHODIMP
00161 nsWindowMediator::UnregisterWindow(nsWindowInfo *inInfo)
00162 {
00163   // Inform the iterators
00164   PRInt32 index = -1;
00165   while (++index < mEnumeratorList.Count()) 
00166     ((nsAppShellWindowEnumerator*)mEnumeratorList[index])->WindowRemoved(inInfo);
00167   
00168   if (mListeners) {
00169     windowData winData = {inInfo->mWindow.get(), nsnull };
00170     mListeners->EnumerateForwards(notifyCloseWindow, (void*)&winData);
00171   }
00172 
00173   // Remove from the lists and free up 
00174   if (inInfo == mOldestWindow)
00175     mOldestWindow = inInfo->mYounger;
00176   if (inInfo == mTopmostWindow)
00177     mTopmostWindow = inInfo->mLower;
00178   inInfo->Unlink(PR_TRUE, PR_TRUE);
00179   if (inInfo == mOldestWindow)
00180     mOldestWindow = 0;
00181   if (inInfo == mTopmostWindow)
00182     mTopmostWindow = 0;
00183   delete inInfo;  
00184 
00185   return NS_OK;
00186 }
00187 
00188 nsWindowInfo *
00189 nsWindowMediator::GetInfoFor(nsIXULWindow *aWindow)
00190 {
00191   nsWindowInfo *info,
00192                *listEnd;
00193 
00194   if (!aWindow)
00195     return 0;
00196 
00197   info = mOldestWindow;
00198   listEnd = 0;
00199   while (info != listEnd) {
00200     if (info->mWindow.get() == aWindow)
00201       return info;
00202     info = info->mYounger;
00203     listEnd = mOldestWindow;
00204   }
00205   return 0;
00206 }
00207 
00208 nsWindowInfo *
00209 nsWindowMediator::GetInfoFor(nsIWidget *aWindow)
00210 {
00211   nsWindowInfo *info,
00212                *listEnd;
00213 
00214   if (!aWindow)
00215     return 0;
00216 
00217   info = mOldestWindow;
00218   listEnd = 0;
00219 
00220   nsCOMPtr<nsIWidget> scanWidget;
00221   while (info != listEnd) {
00222     nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(info->mWindow));
00223     if (base)
00224       base->GetMainWidget(getter_AddRefs(scanWidget));
00225     if (aWindow == scanWidget.get())
00226       return info;
00227     info = info->mYounger;
00228     listEnd = mOldestWindow;
00229   }
00230   return 0;
00231 }
00232 
00233 NS_METHOD
00234 nsWindowMediator::GetEnumerator( const PRUnichar* inType, nsISimpleEnumerator** outEnumerator)
00235 {
00236   if (outEnumerator == NULL)
00237     return NS_ERROR_INVALID_ARG;
00238 
00239   nsAutoLock lock(mListLock);
00240   nsAppShellWindowEnumerator *enumerator = new nsASDOMWindowEarlyToLateEnumerator(inType, *this);
00241   if (enumerator)
00242     return enumerator->QueryInterface(NS_GET_IID(nsISimpleEnumerator) , (void**)outEnumerator);
00243 
00244   return NS_ERROR_OUT_OF_MEMORY;
00245 }
00246 
00247 
00248 
00249 NS_METHOD
00250 nsWindowMediator::GetXULWindowEnumerator(const PRUnichar* inType, nsISimpleEnumerator** outEnumerator)
00251 {
00252   if (outEnumerator == NULL)
00253     return NS_ERROR_INVALID_ARG;
00254 
00255   nsAutoLock lock(mListLock);
00256   nsAppShellWindowEnumerator *enumerator = new nsASXULWindowEarlyToLateEnumerator(inType, *this);
00257   if (enumerator)
00258     return enumerator->QueryInterface( NS_GET_IID(nsISimpleEnumerator) , (void**)outEnumerator);
00259 
00260   return NS_ERROR_OUT_OF_MEMORY;
00261 } 
00262 
00263 
00264 
00265 NS_METHOD
00266 nsWindowMediator::GetZOrderDOMWindowEnumerator(
00267             const PRUnichar *aWindowType, PRBool aFrontToBack,
00268             nsISimpleEnumerator **_retval)
00269 {
00270   if (!_retval)
00271     return NS_ERROR_INVALID_ARG;
00272 
00273   nsAutoLock lock(mListLock);
00274   nsAppShellWindowEnumerator *enumerator;
00275   if (aFrontToBack)
00276     enumerator = new nsASDOMWindowFrontToBackEnumerator(aWindowType, *this);
00277   else
00278     enumerator = new nsASDOMWindowBackToFrontEnumerator(aWindowType, *this);
00279   if (enumerator)
00280     return enumerator->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void**) _retval);
00281 
00282   return NS_ERROR_OUT_OF_MEMORY;
00283 }
00284 
00285 
00286 
00287 NS_METHOD
00288 nsWindowMediator::GetZOrderXULWindowEnumerator(
00289             const PRUnichar *aWindowType, PRBool aFrontToBack,
00290             nsISimpleEnumerator **_retval)
00291 {
00292   if (!_retval)
00293     return NS_ERROR_INVALID_ARG;
00294 
00295   nsAutoLock lock(mListLock);
00296   nsAppShellWindowEnumerator *enumerator;
00297   if (aFrontToBack)
00298     enumerator = new nsASXULWindowFrontToBackEnumerator(aWindowType, *this);
00299   else
00300     enumerator = new nsASXULWindowBackToFrontEnumerator(aWindowType, *this);
00301   if (enumerator)
00302     return enumerator->QueryInterface(NS_GET_IID(nsISimpleEnumerator), (void**) _retval);
00303 
00304   return NS_ERROR_OUT_OF_MEMORY;
00305 }
00306 
00307 
00308 
00309 PRInt32
00310 nsWindowMediator::AddEnumerator(nsAppShellWindowEnumerator * inEnumerator)
00311 {
00312   return mEnumeratorList.AppendElement(inEnumerator);
00313 }
00314 
00315 
00316 
00317 PRInt32
00318 nsWindowMediator::RemoveEnumerator( nsAppShellWindowEnumerator * inEnumerator)
00319 {
00320   return mEnumeratorList.RemoveElement(inEnumerator);
00321 }
00322 
00323 
00324 
00325 /*
00326   Returns the window of type inType ( if null return any window type ) which has the most recent
00327   time stamp
00328 */
00329 NS_IMETHODIMP
00330 nsWindowMediator::GetMostRecentWindow( const PRUnichar* inType, nsIDOMWindowInternal** outWindow)
00331 {
00332   NS_ENSURE_ARG_POINTER(outWindow);
00333   *outWindow = NULL;
00334 
00335   // Find the most window with the highest time stamp that matches
00336   // the requested type
00337 
00338   nsAutoLock lock(mListLock);
00339   nsWindowInfo *info = MostRecentWindowInfo(inType);
00340 
00341   if (info && info->mWindow) {
00342     nsCOMPtr <nsIDOMWindowInternal> DOMWindow;
00343     if(NS_SUCCEEDED(GetDOMWindow(info->mWindow, DOMWindow))) {  
00344       *outWindow = DOMWindow;
00345       NS_ADDREF(*outWindow);
00346       return NS_OK;
00347     }
00348     return NS_ERROR_FAILURE;
00349   }
00350 
00351   return NS_OK;
00352 }
00353 
00354 
00355 
00356 nsWindowInfo *
00357 nsWindowMediator::MostRecentWindowInfo(const PRUnichar* inType)
00358 {
00359   PRInt32       lastTimeStamp = -1;
00360   nsAutoString  typeString(inType);
00361   PRBool        allWindows = !inType || typeString.IsEmpty();
00362 
00363   // Find the most window with the highest time stamp that matches
00364   // the requested type
00365   nsWindowInfo *searchInfo,
00366                *listEnd,
00367                *foundInfo = 0;
00368 
00369   searchInfo = mOldestWindow;
00370   listEnd = 0;
00371   while (searchInfo != listEnd) {
00372     if ((allWindows || searchInfo->TypeEquals(typeString)) &&
00373         searchInfo->mTimeStamp >= lastTimeStamp) {
00374 
00375       foundInfo = searchInfo;
00376       lastTimeStamp = searchInfo->mTimeStamp;
00377     }
00378     searchInfo = searchInfo->mYounger;
00379     listEnd = mOldestWindow;
00380   }
00381   return foundInfo;
00382 }
00383 
00384 
00385 
00386 NS_IMETHODIMP
00387 nsWindowMediator::UpdateWindowTimeStamp( nsIXULWindow* inWindow)
00388 {
00389   nsAutoLock lock(mListLock);
00390   nsWindowInfo *info = GetInfoFor(inWindow);
00391   if (info) {
00392     // increment the window's time stamp
00393     info->mTimeStamp = ++mTimeStamp;
00394     return NS_OK;
00395   }
00396   return NS_ERROR_FAILURE; 
00397 }
00398 
00399 
00400 
00401 NS_IMETHODIMP
00402 nsWindowMediator::UpdateWindowTitle( nsIXULWindow* inWindow,
00403                                      const PRUnichar* inTitle )
00404 {
00405   nsAutoLock lock(mListLock);
00406   if (mListeners && GetInfoFor(inWindow)) {
00407     windowData winData = { inWindow, inTitle };
00408     mListeners->EnumerateForwards(notifyWindowTitleChange, (void *)&winData);
00409   }
00410 
00411   return NS_OK;
00412 }
00413 
00414 
00415 
00416 /* This method's plan is to intervene only when absolutely necessary.
00417    We will get requests to place our windows behind unknown windows.
00418    For the most part, we need to leave those alone (turning them into
00419    explicit requests to be on top breaks Windows.) So generally we
00420    calculate a change as seldom as possible.
00421 */
00422 NS_IMETHODIMP
00423 nsWindowMediator::CalculateZPosition(
00424                 nsIXULWindow   *inWindow,
00425                 PRUint32        inPosition,
00426                 nsIWidget      *inBelow,
00427                 PRUint32       *outPosition,
00428                 nsIWidget     **outBelow,
00429                 PRBool         *outAltered) {
00430 
00431   if (!outBelow)
00432     return NS_ERROR_NULL_POINTER;
00433 
00434   *outBelow = 0;
00435 
00436   if (!inWindow || !outPosition || !outAltered)
00437     return NS_ERROR_NULL_POINTER;
00438 
00439   if (inPosition != nsIWindowMediator::zLevelTop &&
00440       inPosition != nsIWindowMediator::zLevelBottom &&
00441       inPosition != nsIWindowMediator::zLevelBelow)
00442 
00443     return NS_ERROR_INVALID_ARG;
00444 
00445   nsWindowInfo *info = mTopmostWindow;
00446   nsIXULWindow *belowWindow = 0;
00447   PRBool        found = PR_FALSE;
00448   nsresult      result = NS_OK;
00449   
00450   *outPosition = inPosition;
00451   *outAltered = PR_FALSE;
00452 
00453   if (mSortingZOrder) { // don't fight SortZOrder()
00454     *outBelow = inBelow;
00455     NS_IF_ADDREF(*outBelow);
00456     return NS_OK;
00457   }
00458 
00459   PRUint32 inZ;
00460   GetZLevel(inWindow, &inZ);
00461 
00462   nsAutoLock lock(mListLock);
00463 
00464   if (inPosition == nsIWindowMediator::zLevelBelow) {
00465 
00466     // locate inBelow. use topmost if it can't be found or isn't in the
00467     // z-order list
00468     info = GetInfoFor(inBelow);
00469     if (!info || info->mYounger != info && info->mLower == info)
00470       info = mTopmostWindow;
00471     else
00472       found = PR_TRUE;
00473 
00474     if (!found) {
00475       /* Treat unknown windows as a request to be on top.
00476          Not as it should be, but that's what Windows gives us.
00477          Note we change inPosition, but not *outPosition. This forces
00478          us to go through the "on top" calculation just below, without
00479          necessarily changing the output parameters. */
00480       inPosition = nsIWindowMediator::zLevelTop;
00481     }
00482   }
00483 
00484   if (inPosition == nsIWindowMediator::zLevelTop) {
00485     if (mTopmostWindow && mTopmostWindow->mZLevel > inZ) {
00486 
00487       // asked for topmost, can't have it. locate highest allowed position.
00488       do {
00489         if (info->mZLevel <= inZ)
00490           break;
00491         info = info->mLower;
00492       } while (info != mTopmostWindow);
00493 
00494       *outPosition = nsIWindowMediator::zLevelBelow;
00495       belowWindow = info->mHigher->mWindow;
00496       *outAltered = PR_TRUE;
00497 
00498     }
00499   } else if (inPosition == nsIWindowMediator::zLevelBottom) {
00500     if (mTopmostWindow && mTopmostWindow->mHigher->mZLevel < inZ) {
00501 
00502       // asked for bottommost, can't have it. locate lowest allowed position.
00503       do {
00504         info = info->mHigher;
00505         if (info->mZLevel >= inZ)
00506           break;
00507       } while (info != mTopmostWindow);
00508 
00509       *outPosition = nsIWindowMediator::zLevelBelow;
00510       belowWindow = info->mWindow;
00511       *outAltered = PR_TRUE;
00512     }
00513   } else {
00514 
00515     unsigned long relativeZ;
00516 
00517     // check that we're in the right z-plane
00518     if (found) {
00519       belowWindow = info->mWindow;
00520       relativeZ = info->mZLevel;
00521       if (relativeZ > inZ) {
00522 
00523         // might be OK. is lower window, if any, lower?
00524         if (info->mLower != info && info->mLower->mZLevel > inZ) {
00525 
00526           do {
00527             if (info->mZLevel <= inZ)
00528               break;
00529             info = info->mLower;
00530           } while (info != mTopmostWindow);
00531 
00532           belowWindow = info->mHigher->mWindow;
00533           *outAltered = PR_TRUE;
00534         }
00535       } else if (relativeZ < inZ) {
00536 
00537         // nope. look for a higher window to be behind.
00538         do {
00539           info = info->mHigher;
00540           if (info->mZLevel >= inZ)
00541             break;
00542         } while (info != mTopmostWindow);
00543 
00544         if (info->mZLevel >= inZ)
00545           belowWindow = info->mWindow;
00546         else
00547           *outPosition = nsIWindowMediator::zLevelTop;
00548         *outAltered = PR_TRUE;
00549 
00550       } // else they're equal, so it's OK
00551     }
00552   }
00553 
00554   if (NS_SUCCEEDED(result) && belowWindow) {
00555     nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(belowWindow));
00556     if (base)
00557       base->GetMainWidget(outBelow);
00558     else
00559       result = NS_ERROR_NO_INTERFACE;
00560   }
00561 
00562   return result;
00563 }
00564 
00565 
00566 
00567 NS_IMETHODIMP
00568 nsWindowMediator::SetZPosition(
00569                 nsIXULWindow *inWindow,
00570                 PRUint32      inPosition,
00571                 nsIXULWindow *inBelow) {
00572 
00573   nsWindowInfo *inInfo,
00574                *belowInfo;
00575 
00576   if (inPosition != nsIWindowMediator::zLevelTop &&
00577       inPosition != nsIWindowMediator::zLevelBottom &&
00578       inPosition != nsIWindowMediator::zLevelBelow ||
00579 
00580       !inWindow)
00581 
00582 //    inPosition == nsIWindowMediator::zLevelBelow && !inBelow)
00583 
00584     return NS_ERROR_INVALID_ARG;
00585 
00586   if (mSortingZOrder) // don't fight SortZOrder()
00587     return NS_OK;
00588 
00589   nsAutoLock lock(mListLock);
00590 
00591   /* Locate inWindow and unlink it from the z-order list.
00592      It's important we look for it in the age list, not the z-order list.
00593      This is because the former is guaranteed complete, while
00594      now may be this window's first exposure to the latter. */
00595   inInfo = GetInfoFor(inWindow);
00596   if (!inInfo)
00597     return NS_ERROR_INVALID_ARG;
00598 
00599   // locate inBelow, place inWindow behind it
00600   if (inPosition == nsIWindowMediator::zLevelBelow) {
00601     belowInfo = GetInfoFor(inBelow);
00602     // it had better also be in the z-order list
00603     if (belowInfo &&
00604         belowInfo->mYounger != belowInfo && belowInfo->mLower == belowInfo)
00605       belowInfo = 0;
00606     if (!belowInfo)
00607       if (inBelow)
00608         return NS_ERROR_INVALID_ARG;
00609       else
00610         inPosition = nsIWindowMediator::zLevelTop;
00611   }
00612   if (inPosition == nsIWindowMediator::zLevelTop ||
00613       inPosition == nsIWindowMediator::zLevelBottom)
00614     belowInfo = mTopmostWindow ? mTopmostWindow->mHigher : 0;
00615 
00616   if (inInfo != belowInfo) {
00617     inInfo->Unlink(PR_FALSE, PR_TRUE);
00618     inInfo->InsertAfter(0, belowInfo);
00619   }
00620   if (inPosition == nsIWindowMediator::zLevelTop)
00621     mTopmostWindow = inInfo;
00622 
00623   return NS_OK;
00624 }
00625 
00626 NS_IMETHODIMP
00627 nsWindowMediator::GetZLevel(nsIXULWindow *aWindow, PRUint32 *_retval) {
00628 
00629   NS_ENSURE_ARG_POINTER(_retval);
00630   *_retval = nsIXULWindow::normalZ;
00631   nsWindowInfo *info = GetInfoFor(aWindow);
00632   if (info)
00633     *_retval = info->mZLevel;
00634   else {
00635     NS_WARNING("getting z level of unregistered window");
00636     // this goes off during window destruction
00637   }
00638   return NS_OK;
00639 }
00640 
00641 NS_IMETHODIMP
00642 nsWindowMediator::SetZLevel(nsIXULWindow *aWindow, PRUint32 aZLevel) {
00643 
00644   nsAutoLock lock(mListLock);
00645 
00646   nsWindowInfo *info = GetInfoFor(aWindow);
00647   NS_ASSERTION(info, "setting z level of unregistered window");
00648   if (!info)
00649     return NS_ERROR_FAILURE;
00650 
00651   if (info->mZLevel != aZLevel) {
00652     PRBool lowered = info->mZLevel > aZLevel;
00653     info->mZLevel = aZLevel;
00654     if (lowered)
00655       SortZOrderFrontToBack();
00656     else
00657       SortZOrderBackToFront();
00658   }
00659   return NS_OK;
00660 }
00661 
00662 /*   Fix potentially out-of-order windows by performing an insertion sort
00663    on the z-order list. The method will work no matter how broken the
00664    list, but its assumed usage is immediately after one window's z level
00665    has been changed, so one window is potentially out of place. Such a sort
00666    is most efficiently done in a particular direction. Use this one
00667    if a window's z level has just been reduced, so the sort is most efficiently
00668    done front to back. Assumes caller has locked mListLock.
00669      Note it's hardly worth going to all the trouble to write two versions
00670    of this method except that if we choose the inefficient sorting direction,
00671    on slow systems windows could visibly bubble around the window that
00672    was moved.
00673 */
00674 void
00675 nsWindowMediator::SortZOrderFrontToBack() {
00676 
00677   nsWindowInfo *scan,   // scans list looking for problems
00678                *search, // searches for correct placement for scan window
00679                *prev,   // previous search element
00680                *lowest; // bottom-most window in list
00681   PRBool       finished;
00682 
00683   if (!mTopmostWindow) // early during program execution there's no z list yet
00684     return;            // there's also only one window, so this is not dangerous
00685 
00686   mSortingZOrder = PR_TRUE;
00687 
00688   /* Step through the list from top to bottom. If we find a window which
00689      should be moved down in the list, move it to its highest legal position. */
00690   do {
00691     finished = PR_TRUE;
00692     lowest = mTopmostWindow->mHigher;
00693     scan = mTopmostWindow;
00694     while (scan != lowest) {
00695       PRUint32 scanZ = scan->mZLevel;
00696       if (scanZ < scan->mLower->mZLevel) { // out of order
00697         search = scan->mLower;
00698         do {
00699           prev = search;
00700           search = search->mLower;
00701         } while (prev != lowest && scanZ < search->mZLevel);
00702 
00703         // reposition |scan| within the list
00704         if (scan == mTopmostWindow)
00705           mTopmostWindow = scan->mLower;
00706         scan->Unlink(PR_FALSE, PR_TRUE);
00707         scan->InsertAfter(0, prev);
00708 
00709         // fix actual window order
00710         nsCOMPtr<nsIBaseWindow> base;
00711         nsCOMPtr<nsIWidget> scanWidget;
00712         nsCOMPtr<nsIWidget> prevWidget;
00713         base = do_QueryInterface(scan->mWindow);
00714         if (base)
00715           base->GetMainWidget(getter_AddRefs(scanWidget));
00716         base = do_QueryInterface(prev->mWindow);
00717         if (base)
00718           base->GetMainWidget(getter_AddRefs(prevWidget));
00719         if (scanWidget)
00720           scanWidget->PlaceBehind(eZPlacementBelow, prevWidget, PR_FALSE);
00721 
00722         finished = PR_FALSE;
00723         break;
00724       }
00725       scan = scan->mLower;
00726     }
00727   } while (!finished);
00728 
00729   mSortingZOrder = PR_FALSE;
00730 }
00731 
00732 // see comment for SortZOrderFrontToBack
00733 void
00734 nsWindowMediator::SortZOrderBackToFront() {
00735 
00736   nsWindowInfo *scan,   // scans list looking for problems
00737                *search, // searches for correct placement for scan window
00738                *lowest; // bottom-most window in list
00739   PRBool       finished;
00740 
00741   if (!mTopmostWindow) // early during program execution there's no z list yet
00742     return;            // there's also only one window, so this is not dangerous
00743 
00744   mSortingZOrder = PR_TRUE;
00745 
00746   /* Step through the list from bottom to top. If we find a window which
00747      should be moved up in the list, move it to its lowest legal position. */
00748   do {
00749     finished = PR_TRUE;
00750     lowest = mTopmostWindow->mHigher;
00751     scan = lowest;
00752     while (scan != mTopmostWindow) {
00753       PRUint32 scanZ = scan->mZLevel;
00754       if (scanZ > scan->mHigher->mZLevel) { // out of order
00755         search = scan;
00756         do
00757           search = search->mHigher;
00758         while (search != lowest && scanZ > search->mZLevel);
00759 
00760         // reposition |scan| within the list
00761         if (scan != search && scan != search->mLower) {
00762           scan->Unlink(PR_FALSE, PR_TRUE);
00763           scan->InsertAfter(0, search);
00764         }
00765         if (search == lowest)
00766           mTopmostWindow = scan;
00767 
00768         // fix actual window order
00769         nsCOMPtr<nsIBaseWindow> base;
00770         nsCOMPtr<nsIWidget> scanWidget;
00771         nsCOMPtr<nsIWidget> searchWidget;
00772         base = do_QueryInterface(scan->mWindow);
00773         if (base)
00774           base->GetMainWidget(getter_AddRefs(scanWidget));
00775         if (mTopmostWindow != scan) {
00776           base = do_QueryInterface(search->mWindow);
00777           if (base)
00778             base->GetMainWidget(getter_AddRefs(searchWidget));
00779         }
00780         if (scanWidget)
00781           scanWidget->PlaceBehind(eZPlacementBelow, searchWidget, PR_FALSE);
00782 
00783         finished = PR_FALSE;
00784         break;
00785       }
00786       scan = scan->mHigher;
00787     }
00788   } while (!finished);
00789 
00790   mSortingZOrder = PR_FALSE;
00791 }
00792 
00793 // COM
00794 NS_IMPL_ADDREF( nsWindowMediator )
00795 NS_IMPL_QUERY_INTERFACE1(nsWindowMediator, nsIWindowMediator)
00796 NS_IMPL_RELEASE(nsWindowMediator)
00797 
00798 nsresult
00799 nsWindowMediator::Init()
00800 {
00801   if (gRefCnt++ == 0)
00802   {
00803     mListLock = PR_NewLock();
00804     if (!mListLock)
00805         return NS_ERROR_OUT_OF_MEMORY;
00806   }
00807 
00808   return NS_OK;
00809 }
00810 
00811 NS_IMETHODIMP
00812 nsWindowMediator::AddListener(nsIWindowMediatorListener* aListener)
00813 {
00814   NS_ENSURE_ARG_POINTER(aListener);
00815   
00816   nsresult rv;
00817   if (!mListeners) {
00818     rv = NS_NewISupportsArray(getter_AddRefs(mListeners));
00819     if (NS_FAILED(rv)) return rv;
00820   }
00821 
00822   mListeners->AppendElement(aListener);
00823   
00824   return NS_OK;
00825 }
00826 
00827 NS_IMETHODIMP
00828 nsWindowMediator::RemoveListener(nsIWindowMediatorListener* aListener)
00829 {
00830   NS_ENSURE_ARG_POINTER(aListener);
00831 
00832   if (!mListeners) return NS_OK;
00833 
00834   mListeners->RemoveElement(aListener);
00835   
00836   return NS_OK;
00837 }
00838 
00839 PRBool notifyOpenWindow(nsISupports *aElement, void* aData)
00840 {
00841   nsIWindowMediatorListener* listener =
00842     NS_REINTERPRET_CAST(nsIWindowMediatorListener*, aElement);
00843   windowData* winData = (windowData*) aData;
00844 
00845   listener->OnOpenWindow(winData->mWindow);
00846   return PR_TRUE;
00847 }
00848 
00849 PRBool notifyCloseWindow(nsISupports *aElement, void* aData)
00850 {
00851   nsIWindowMediatorListener* listener =
00852     NS_REINTERPRET_CAST(nsIWindowMediatorListener*, aElement);
00853   windowData* winData = (windowData*) aData;
00854   
00855   listener->OnCloseWindow(winData->mWindow);
00856   return PR_TRUE;
00857 }
00858 
00859 PRBool notifyWindowTitleChange(nsISupports *aElement, void* aData)
00860 {
00861   nsIWindowMediatorListener* listener =
00862     NS_REINTERPRET_CAST(nsIWindowMediatorListener*, aElement);
00863 
00864   windowData* titleData =
00865     NS_REINTERPRET_CAST(windowData*, aData);
00866   listener->OnWindowTitleChange(titleData->mWindow,
00867                                 titleData->mTitle);
00868 
00869   return PR_TRUE;
00870 }
00871