michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsTArray.h" michael@0: #include "nsIBaseWindow.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsISimpleEnumerator.h" michael@0: #include "nsAppShellWindowEnumerator.h" michael@0: #include "nsWindowMediator.h" michael@0: #include "nsIWindowMediatorListener.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsGlobalWindow.h" michael@0: michael@0: #include "nsIDocShell.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIXULWindow.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: static bool notifyOpenWindow(nsIWindowMediatorListener *aElement, void* aData); michael@0: static bool notifyCloseWindow(nsIWindowMediatorListener *aElement, void* aData); michael@0: static bool notifyWindowTitleChange(nsIWindowMediatorListener *aElement, void* aData); michael@0: michael@0: // for notifyWindowTitleChange michael@0: struct WindowTitleData { michael@0: nsIXULWindow* mWindow; michael@0: const char16_t *mTitle; michael@0: }; michael@0: michael@0: nsresult michael@0: nsWindowMediator::GetDOMWindow(nsIXULWindow* inWindow, michael@0: nsCOMPtr& outDOMWindow) michael@0: { michael@0: nsCOMPtr docShell; michael@0: michael@0: inWindow->GetDocShell(getter_AddRefs(docShell)); michael@0: outDOMWindow = do_GetInterface(docShell); michael@0: return outDOMWindow ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsWindowMediator::nsWindowMediator() : michael@0: mEnumeratorList(), mOldestWindow(nullptr), mTopmostWindow(nullptr), michael@0: mTimeStamp(0), mSortingZOrder(false), mReady(false), michael@0: mListLock("nsWindowMediator.mListLock") michael@0: { michael@0: } michael@0: michael@0: nsWindowMediator::~nsWindowMediator() michael@0: { michael@0: while (mOldestWindow) michael@0: UnregisterWindow(mOldestWindow); michael@0: } michael@0: michael@0: nsresult nsWindowMediator::Init() michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr obsSvc = michael@0: do_GetService("@mozilla.org/observer-service;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = obsSvc->AddObserver(this, "xpcom-shutdown", true); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mReady = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsWindowMediator::RegisterWindow(nsIXULWindow* inWindow) michael@0: { michael@0: NS_ENSURE_STATE(mReady); michael@0: michael@0: if (GetInfoFor(inWindow)) { michael@0: NS_ERROR("multiple window registration"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mTimeStamp++; michael@0: michael@0: // Create window info struct and add to list of windows michael@0: nsWindowInfo* windowInfo = new nsWindowInfo(inWindow, mTimeStamp); michael@0: if (!windowInfo) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: WindowTitleData winData = { inWindow, nullptr }; michael@0: mListeners.EnumerateForwards(notifyOpenWindow, &winData); michael@0: michael@0: MutexAutoLock lock(mListLock); michael@0: if (mOldestWindow) michael@0: windowInfo->InsertAfter(mOldestWindow->mOlder, nullptr); michael@0: else michael@0: mOldestWindow = windowInfo; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::UnregisterWindow(nsIXULWindow* inWindow) michael@0: { michael@0: NS_ENSURE_STATE(mReady); michael@0: MutexAutoLock lock(mListLock); michael@0: nsWindowInfo *info = GetInfoFor(inWindow); michael@0: if (info) michael@0: return UnregisterWindow(info); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: nsresult michael@0: nsWindowMediator::UnregisterWindow(nsWindowInfo *inInfo) michael@0: { michael@0: // Inform the iterators michael@0: uint32_t index = 0; michael@0: while (index < mEnumeratorList.Length()) { michael@0: mEnumeratorList[index]->WindowRemoved(inInfo); michael@0: index++; michael@0: } michael@0: michael@0: WindowTitleData winData = { inInfo->mWindow.get(), nullptr }; michael@0: mListeners.EnumerateForwards(notifyCloseWindow, &winData); michael@0: michael@0: // Remove from the lists and free up michael@0: if (inInfo == mOldestWindow) michael@0: mOldestWindow = inInfo->mYounger; michael@0: if (inInfo == mTopmostWindow) michael@0: mTopmostWindow = inInfo->mLower; michael@0: inInfo->Unlink(true, true); michael@0: if (inInfo == mOldestWindow) michael@0: mOldestWindow = nullptr; michael@0: if (inInfo == mTopmostWindow) michael@0: mTopmostWindow = nullptr; michael@0: delete inInfo; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsWindowInfo* michael@0: nsWindowMediator::GetInfoFor(nsIXULWindow *aWindow) michael@0: { michael@0: nsWindowInfo *info, michael@0: *listEnd; michael@0: michael@0: if (!aWindow) michael@0: return nullptr; michael@0: michael@0: info = mOldestWindow; michael@0: listEnd = nullptr; michael@0: while (info != listEnd) { michael@0: if (info->mWindow.get() == aWindow) michael@0: return info; michael@0: info = info->mYounger; michael@0: listEnd = mOldestWindow; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: nsWindowInfo* michael@0: nsWindowMediator::GetInfoFor(nsIWidget *aWindow) michael@0: { michael@0: nsWindowInfo *info, michael@0: *listEnd; michael@0: michael@0: if (!aWindow) michael@0: return nullptr; michael@0: michael@0: info = mOldestWindow; michael@0: listEnd = nullptr; michael@0: michael@0: nsCOMPtr scanWidget; michael@0: while (info != listEnd) { michael@0: nsCOMPtr base(do_QueryInterface(info->mWindow)); michael@0: if (base) michael@0: base->GetMainWidget(getter_AddRefs(scanWidget)); michael@0: if (aWindow == scanWidget.get()) michael@0: return info; michael@0: info = info->mYounger; michael@0: listEnd = mOldestWindow; michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::GetEnumerator(const char16_t* inType, nsISimpleEnumerator** outEnumerator) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(outEnumerator); michael@0: NS_ENSURE_STATE(mReady); michael@0: MutexAutoLock lock(mListLock); michael@0: nsAppShellWindowEnumerator *enumerator = new nsASDOMWindowEarlyToLateEnumerator(inType, *this); michael@0: if (enumerator) michael@0: return enumerator->QueryInterface(NS_GET_IID(nsISimpleEnumerator) , (void**)outEnumerator); michael@0: michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::GetXULWindowEnumerator(const char16_t* inType, nsISimpleEnumerator** outEnumerator) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(outEnumerator); michael@0: NS_ENSURE_STATE(mReady); michael@0: MutexAutoLock lock(mListLock); michael@0: nsAppShellWindowEnumerator *enumerator = new nsASXULWindowEarlyToLateEnumerator(inType, *this); michael@0: if (enumerator) michael@0: return enumerator->QueryInterface(NS_GET_IID(nsISimpleEnumerator) , (void**)outEnumerator); michael@0: michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::GetZOrderDOMWindowEnumerator( michael@0: const char16_t *aWindowType, bool aFrontToBack, michael@0: nsISimpleEnumerator **_retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: NS_ENSURE_STATE(mReady); michael@0: MutexAutoLock lock(mListLock); michael@0: nsAppShellWindowEnumerator *enumerator; michael@0: if (aFrontToBack) michael@0: enumerator = new nsASDOMWindowFrontToBackEnumerator(aWindowType, *this); michael@0: else michael@0: enumerator = new nsASDOMWindowBackToFrontEnumerator(aWindowType, *this); michael@0: if (enumerator) michael@0: return CallQueryInterface(enumerator, _retval); michael@0: michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::GetZOrderXULWindowEnumerator( michael@0: const char16_t *aWindowType, bool aFrontToBack, michael@0: nsISimpleEnumerator **_retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: NS_ENSURE_STATE(mReady); michael@0: MutexAutoLock lock(mListLock); michael@0: nsAppShellWindowEnumerator *enumerator; michael@0: if (aFrontToBack) michael@0: enumerator = new nsASXULWindowFrontToBackEnumerator(aWindowType, *this); michael@0: else michael@0: enumerator = new nsASXULWindowBackToFrontEnumerator(aWindowType, *this); michael@0: if (enumerator) michael@0: return CallQueryInterface(enumerator, _retval); michael@0: michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: int32_t michael@0: nsWindowMediator::AddEnumerator(nsAppShellWindowEnumerator * inEnumerator) michael@0: { michael@0: return mEnumeratorList.AppendElement(inEnumerator) != nullptr; michael@0: } michael@0: michael@0: int32_t michael@0: nsWindowMediator::RemoveEnumerator(nsAppShellWindowEnumerator * inEnumerator) michael@0: { michael@0: return mEnumeratorList.RemoveElement(inEnumerator); michael@0: } michael@0: michael@0: // Returns the window of type inType ( if null return any window type ) which has the most recent michael@0: // time stamp michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::GetMostRecentWindow(const char16_t* inType, nsIDOMWindow** outWindow) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(outWindow); michael@0: *outWindow = nullptr; michael@0: if (!mReady) michael@0: return NS_OK; michael@0: michael@0: // Find the most window with the highest time stamp that matches michael@0: // the requested type michael@0: michael@0: MutexAutoLock lock(mListLock); michael@0: nsWindowInfo *info = MostRecentWindowInfo(inType); michael@0: michael@0: if (info && info->mWindow) { michael@0: nsCOMPtr DOMWindow; michael@0: if (NS_SUCCEEDED(GetDOMWindow(info->mWindow, DOMWindow))) { michael@0: *outWindow = DOMWindow; michael@0: NS_ADDREF(*outWindow); michael@0: return NS_OK; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsWindowInfo* michael@0: nsWindowMediator::MostRecentWindowInfo(const char16_t* inType) michael@0: { michael@0: int32_t lastTimeStamp = -1; michael@0: nsAutoString typeString(inType); michael@0: bool allWindows = !inType || typeString.IsEmpty(); michael@0: michael@0: // Find the most window with the highest time stamp that matches michael@0: // the requested type michael@0: nsWindowInfo *searchInfo, michael@0: *listEnd, michael@0: *foundInfo = nullptr; michael@0: michael@0: searchInfo = mOldestWindow; michael@0: listEnd = nullptr; michael@0: while (searchInfo != listEnd) { michael@0: if ((allWindows || searchInfo->TypeEquals(typeString)) && michael@0: searchInfo->mTimeStamp >= lastTimeStamp) { michael@0: michael@0: foundInfo = searchInfo; michael@0: lastTimeStamp = searchInfo->mTimeStamp; michael@0: } michael@0: searchInfo = searchInfo->mYounger; michael@0: listEnd = mOldestWindow; michael@0: } michael@0: return foundInfo; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::GetOuterWindowWithId(uint64_t aWindowID, michael@0: nsIDOMWindow** aWindow) michael@0: { michael@0: *aWindow = nsGlobalWindow::GetOuterWindowWithId(aWindowID); michael@0: NS_IF_ADDREF(*aWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::GetCurrentInnerWindowWithId(uint64_t aWindowID, michael@0: nsIDOMWindow** aWindow) michael@0: { michael@0: nsCOMPtr inner = nsGlobalWindow::GetInnerWindowWithId(aWindowID); michael@0: michael@0: // not found michael@0: if (!inner) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr outer = inner->GetOuterWindow(); michael@0: NS_ENSURE_TRUE(outer, NS_ERROR_UNEXPECTED); michael@0: michael@0: // outer is already using another inner, so it's same as not found michael@0: if (outer->GetCurrentInnerWindow() != inner) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr ret = do_QueryInterface(outer); michael@0: ret.forget(aWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::UpdateWindowTimeStamp(nsIXULWindow* inWindow) michael@0: { michael@0: NS_ENSURE_STATE(mReady); michael@0: MutexAutoLock lock(mListLock); michael@0: nsWindowInfo *info = GetInfoFor(inWindow); michael@0: if (info) { michael@0: // increment the window's time stamp michael@0: info->mTimeStamp = ++mTimeStamp; michael@0: return NS_OK; michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::UpdateWindowTitle(nsIXULWindow* inWindow, michael@0: const char16_t* inTitle) michael@0: { michael@0: NS_ENSURE_STATE(mReady); michael@0: MutexAutoLock lock(mListLock); michael@0: if (GetInfoFor(inWindow)) { michael@0: WindowTitleData winData = { inWindow, inTitle }; michael@0: mListeners.EnumerateForwards(notifyWindowTitleChange, &winData); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* This method's plan is to intervene only when absolutely necessary. michael@0: We will get requests to place our windows behind unknown windows. michael@0: For the most part, we need to leave those alone (turning them into michael@0: explicit requests to be on top breaks Windows.) So generally we michael@0: calculate a change as seldom as possible. michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::CalculateZPosition( michael@0: nsIXULWindow *inWindow, michael@0: uint32_t inPosition, michael@0: nsIWidget *inBelow, michael@0: uint32_t *outPosition, michael@0: nsIWidget **outBelow, michael@0: bool *outAltered) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(outBelow); michael@0: NS_ENSURE_STATE(mReady); michael@0: michael@0: *outBelow = nullptr; michael@0: michael@0: if (!inWindow || !outPosition || !outAltered) michael@0: return NS_ERROR_NULL_POINTER; michael@0: michael@0: if (inPosition != nsIWindowMediator::zLevelTop && michael@0: inPosition != nsIWindowMediator::zLevelBottom && michael@0: inPosition != nsIWindowMediator::zLevelBelow) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsWindowInfo *info = mTopmostWindow; michael@0: nsIXULWindow *belowWindow = nullptr; michael@0: bool found = false; michael@0: nsresult result = NS_OK; michael@0: michael@0: *outPosition = inPosition; michael@0: *outAltered = false; michael@0: michael@0: if (mSortingZOrder) { // don't fight SortZOrder() michael@0: *outBelow = inBelow; michael@0: NS_IF_ADDREF(*outBelow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint32_t inZ; michael@0: GetZLevel(inWindow, &inZ); michael@0: michael@0: MutexAutoLock lock(mListLock); michael@0: michael@0: if (inPosition == nsIWindowMediator::zLevelBelow) { michael@0: // locate inBelow. use topmost if it can't be found or isn't in the michael@0: // z-order list michael@0: info = GetInfoFor(inBelow); michael@0: if (!info || (info->mYounger != info && info->mLower == info)) michael@0: info = mTopmostWindow; michael@0: else michael@0: found = true; michael@0: michael@0: if (!found) { michael@0: /* Treat unknown windows as a request to be on top. michael@0: Not as it should be, but that's what Windows gives us. michael@0: Note we change inPosition, but not *outPosition. This forces michael@0: us to go through the "on top" calculation just below, without michael@0: necessarily changing the output parameters. */ michael@0: inPosition = nsIWindowMediator::zLevelTop; michael@0: } michael@0: } michael@0: michael@0: if (inPosition == nsIWindowMediator::zLevelTop) { michael@0: if (mTopmostWindow && mTopmostWindow->mZLevel > inZ) { michael@0: // asked for topmost, can't have it. locate highest allowed position. michael@0: do { michael@0: if (info->mZLevel <= inZ) michael@0: break; michael@0: info = info->mLower; michael@0: } while (info != mTopmostWindow); michael@0: michael@0: *outPosition = nsIWindowMediator::zLevelBelow; michael@0: belowWindow = info->mHigher->mWindow; michael@0: *outAltered = true; michael@0: } michael@0: } else if (inPosition == nsIWindowMediator::zLevelBottom) { michael@0: if (mTopmostWindow && mTopmostWindow->mHigher->mZLevel < inZ) { michael@0: // asked for bottommost, can't have it. locate lowest allowed position. michael@0: do { michael@0: info = info->mHigher; michael@0: if (info->mZLevel >= inZ) michael@0: break; michael@0: } while (info != mTopmostWindow); michael@0: michael@0: *outPosition = nsIWindowMediator::zLevelBelow; michael@0: belowWindow = info->mWindow; michael@0: *outAltered = true; michael@0: } michael@0: } else { michael@0: unsigned long relativeZ; michael@0: michael@0: // check that we're in the right z-plane michael@0: if (found) { michael@0: belowWindow = info->mWindow; michael@0: relativeZ = info->mZLevel; michael@0: if (relativeZ > inZ) { michael@0: // might be OK. is lower window, if any, lower? michael@0: if (info->mLower != info && info->mLower->mZLevel > inZ) { michael@0: do { michael@0: if (info->mZLevel <= inZ) michael@0: break; michael@0: info = info->mLower; michael@0: } while (info != mTopmostWindow); michael@0: michael@0: belowWindow = info->mHigher->mWindow; michael@0: *outAltered = true; michael@0: } michael@0: } else if (relativeZ < inZ) { michael@0: // nope. look for a higher window to be behind. michael@0: do { michael@0: info = info->mHigher; michael@0: if (info->mZLevel >= inZ) michael@0: break; michael@0: } while (info != mTopmostWindow); michael@0: michael@0: if (info->mZLevel >= inZ) michael@0: belowWindow = info->mWindow; michael@0: else michael@0: *outPosition = nsIWindowMediator::zLevelTop; michael@0: *outAltered = true; michael@0: } // else they're equal, so it's OK michael@0: } michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(result) && belowWindow) { michael@0: nsCOMPtr base(do_QueryInterface(belowWindow)); michael@0: if (base) michael@0: base->GetMainWidget(outBelow); michael@0: else michael@0: result = NS_ERROR_NO_INTERFACE; michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::SetZPosition( michael@0: nsIXULWindow *inWindow, michael@0: uint32_t inPosition, michael@0: nsIXULWindow *inBelow) michael@0: { michael@0: nsWindowInfo *inInfo, michael@0: *belowInfo; michael@0: michael@0: if ((inPosition != nsIWindowMediator::zLevelTop && michael@0: inPosition != nsIWindowMediator::zLevelBottom && michael@0: inPosition != nsIWindowMediator::zLevelBelow) || michael@0: !inWindow) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: if (mSortingZOrder) // don't fight SortZOrder() michael@0: return NS_OK; michael@0: michael@0: NS_ENSURE_STATE(mReady); michael@0: MutexAutoLock lock(mListLock); michael@0: michael@0: /* Locate inWindow and unlink it from the z-order list. michael@0: It's important we look for it in the age list, not the z-order list. michael@0: This is because the former is guaranteed complete, while michael@0: now may be this window's first exposure to the latter. */ michael@0: inInfo = GetInfoFor(inWindow); michael@0: if (!inInfo) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: // locate inBelow, place inWindow behind it michael@0: if (inPosition == nsIWindowMediator::zLevelBelow) { michael@0: belowInfo = GetInfoFor(inBelow); michael@0: // it had better also be in the z-order list michael@0: if (belowInfo && michael@0: belowInfo->mYounger != belowInfo && belowInfo->mLower == belowInfo) { michael@0: belowInfo = nullptr; michael@0: } michael@0: if (!belowInfo) { michael@0: if (inBelow) michael@0: return NS_ERROR_INVALID_ARG; michael@0: else michael@0: inPosition = nsIWindowMediator::zLevelTop; michael@0: } michael@0: } michael@0: if (inPosition == nsIWindowMediator::zLevelTop || michael@0: inPosition == nsIWindowMediator::zLevelBottom) michael@0: belowInfo = mTopmostWindow ? mTopmostWindow->mHigher : nullptr; michael@0: michael@0: if (inInfo != belowInfo) { michael@0: inInfo->Unlink(false, true); michael@0: inInfo->InsertAfter(nullptr, belowInfo); michael@0: } michael@0: if (inPosition == nsIWindowMediator::zLevelTop) michael@0: mTopmostWindow = inInfo; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::GetZLevel(nsIXULWindow *aWindow, uint32_t *_retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: *_retval = nsIXULWindow::normalZ; michael@0: nsWindowInfo *info = GetInfoFor(aWindow); michael@0: if (info) { michael@0: *_retval = info->mZLevel; michael@0: } else { michael@0: NS_WARNING("getting z level of unregistered window"); michael@0: // this goes off during window destruction michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::SetZLevel(nsIXULWindow *aWindow, uint32_t aZLevel) michael@0: { michael@0: NS_ENSURE_STATE(mReady); michael@0: MutexAutoLock lock(mListLock); michael@0: michael@0: nsWindowInfo *info = GetInfoFor(aWindow); michael@0: NS_ASSERTION(info, "setting z level of unregistered window"); michael@0: if (!info) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (info->mZLevel != aZLevel) { michael@0: bool lowered = info->mZLevel > aZLevel; michael@0: info->mZLevel = aZLevel; michael@0: if (lowered) michael@0: SortZOrderFrontToBack(); michael@0: else michael@0: SortZOrderBackToFront(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* Fix potentially out-of-order windows by performing an insertion sort michael@0: on the z-order list. The method will work no matter how broken the michael@0: list, but its assumed usage is immediately after one window's z level michael@0: has been changed, so one window is potentially out of place. Such a sort michael@0: is most efficiently done in a particular direction. Use this one michael@0: if a window's z level has just been reduced, so the sort is most efficiently michael@0: done front to back. Assumes caller has locked mListLock. michael@0: Note it's hardly worth going to all the trouble to write two versions michael@0: of this method except that if we choose the inefficient sorting direction, michael@0: on slow systems windows could visibly bubble around the window that michael@0: was moved. michael@0: */ michael@0: void michael@0: nsWindowMediator::SortZOrderFrontToBack() michael@0: { michael@0: nsWindowInfo *scan, // scans list looking for problems michael@0: *search, // searches for correct placement for scan window michael@0: *prev, // previous search element michael@0: *lowest; // bottom-most window in list michael@0: bool finished; michael@0: michael@0: if (!mTopmostWindow) // early during program execution there's no z list yet michael@0: return; // there's also only one window, so this is not dangerous michael@0: michael@0: mSortingZOrder = true; michael@0: michael@0: /* Step through the list from top to bottom. If we find a window which michael@0: should be moved down in the list, move it to its highest legal position. */ michael@0: do { michael@0: finished = true; michael@0: lowest = mTopmostWindow->mHigher; michael@0: scan = mTopmostWindow; michael@0: while (scan != lowest) { michael@0: uint32_t scanZ = scan->mZLevel; michael@0: if (scanZ < scan->mLower->mZLevel) { // out of order michael@0: search = scan->mLower; michael@0: do { michael@0: prev = search; michael@0: search = search->mLower; michael@0: } while (prev != lowest && scanZ < search->mZLevel); michael@0: michael@0: // reposition |scan| within the list michael@0: if (scan == mTopmostWindow) michael@0: mTopmostWindow = scan->mLower; michael@0: scan->Unlink(false, true); michael@0: scan->InsertAfter(nullptr, prev); michael@0: michael@0: // fix actual window order michael@0: nsCOMPtr base; michael@0: nsCOMPtr scanWidget; michael@0: nsCOMPtr prevWidget; michael@0: base = do_QueryInterface(scan->mWindow); michael@0: if (base) michael@0: base->GetMainWidget(getter_AddRefs(scanWidget)); michael@0: base = do_QueryInterface(prev->mWindow); michael@0: if (base) michael@0: base->GetMainWidget(getter_AddRefs(prevWidget)); michael@0: if (scanWidget) michael@0: scanWidget->PlaceBehind(eZPlacementBelow, prevWidget, false); michael@0: michael@0: finished = false; michael@0: break; michael@0: } michael@0: scan = scan->mLower; michael@0: } michael@0: } while (!finished); michael@0: michael@0: mSortingZOrder = false; michael@0: } michael@0: michael@0: // see comment for SortZOrderFrontToBack michael@0: void michael@0: nsWindowMediator::SortZOrderBackToFront() michael@0: { michael@0: nsWindowInfo *scan, // scans list looking for problems michael@0: *search, // searches for correct placement for scan window michael@0: *lowest; // bottom-most window in list michael@0: bool finished; michael@0: michael@0: if (!mTopmostWindow) // early during program execution there's no z list yet michael@0: return; // there's also only one window, so this is not dangerous michael@0: michael@0: mSortingZOrder = true; michael@0: michael@0: /* Step through the list from bottom to top. If we find a window which michael@0: should be moved up in the list, move it to its lowest legal position. */ michael@0: do { michael@0: finished = true; michael@0: lowest = mTopmostWindow->mHigher; michael@0: scan = lowest; michael@0: while (scan != mTopmostWindow) { michael@0: uint32_t scanZ = scan->mZLevel; michael@0: if (scanZ > scan->mHigher->mZLevel) { // out of order michael@0: search = scan; michael@0: do { michael@0: search = search->mHigher; michael@0: } while (search != lowest && scanZ > search->mZLevel); michael@0: michael@0: // reposition |scan| within the list michael@0: if (scan != search && scan != search->mLower) { michael@0: scan->Unlink(false, true); michael@0: scan->InsertAfter(nullptr, search); michael@0: } michael@0: if (search == lowest) michael@0: mTopmostWindow = scan; michael@0: michael@0: // fix actual window order michael@0: nsCOMPtr base; michael@0: nsCOMPtr scanWidget; michael@0: nsCOMPtr searchWidget; michael@0: base = do_QueryInterface(scan->mWindow); michael@0: if (base) michael@0: base->GetMainWidget(getter_AddRefs(scanWidget)); michael@0: if (mTopmostWindow != scan) { michael@0: base = do_QueryInterface(search->mWindow); michael@0: if (base) michael@0: base->GetMainWidget(getter_AddRefs(searchWidget)); michael@0: } michael@0: if (scanWidget) michael@0: scanWidget->PlaceBehind(eZPlacementBelow, searchWidget, false); michael@0: finished = false; michael@0: break; michael@0: } michael@0: scan = scan->mHigher; michael@0: } michael@0: } while (!finished); michael@0: michael@0: mSortingZOrder = false; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsWindowMediator, michael@0: nsIWindowMediator, michael@0: nsIObserver, michael@0: nsISupportsWeakReference) michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::AddListener(nsIWindowMediatorListener* aListener) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aListener); michael@0: michael@0: mListeners.AppendObject(aListener); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::RemoveListener(nsIWindowMediatorListener* aListener) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aListener); michael@0: michael@0: mListeners.RemoveObject(aListener); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowMediator::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: if (!strcmp(aTopic, "xpcom-shutdown") && mReady) { michael@0: // Unregistering a window may cause its destructor to run, causing it to michael@0: // call into the window mediator, try to acquire mListLock, and deadlock. michael@0: // Our solution is to hold strong refs to all windows until we release michael@0: // mListLock. michael@0: nsTArray > windows; michael@0: michael@0: { michael@0: MutexAutoLock lock(mListLock); michael@0: while (mOldestWindow) { michael@0: windows.AppendElement(mOldestWindow->mWindow); michael@0: UnregisterWindow(mOldestWindow); michael@0: } michael@0: } michael@0: mReady = false; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: notifyOpenWindow(nsIWindowMediatorListener *aListener, void* aData) michael@0: { michael@0: WindowTitleData* winData = static_cast(aData); michael@0: aListener->OnOpenWindow(winData->mWindow); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: notifyCloseWindow(nsIWindowMediatorListener *aListener, void* aData) michael@0: { michael@0: WindowTitleData* winData = static_cast(aData); michael@0: aListener->OnCloseWindow(winData->mWindow); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: notifyWindowTitleChange(nsIWindowMediatorListener *aListener, void* aData) michael@0: { michael@0: WindowTitleData* titleData = reinterpret_cast(aData); michael@0: aListener->OnWindowTitleChange(titleData->mWindow, titleData->mTitle); michael@0: michael@0: return true; michael@0: }