summaryrefslogtreecommitdiffstats
path: root/xpfe/appshell/nsWindowMediator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpfe/appshell/nsWindowMediator.cpp')
-rw-r--r--xpfe/appshell/nsWindowMediator.cpp749
1 files changed, 749 insertions, 0 deletions
diff --git a/xpfe/appshell/nsWindowMediator.cpp b/xpfe/appshell/nsWindowMediator.cpp
new file mode 100644
index 0000000000..82ebe6f00d
--- /dev/null
+++ b/xpfe/appshell/nsWindowMediator.cpp
@@ -0,0 +1,749 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCOMPtr.h"
+#include "nsEnumeratorUtils.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsTArray.h"
+#include "nsIBaseWindow.h"
+#include "nsIWidget.h"
+#include "nsIObserverService.h"
+#include "nsISimpleEnumerator.h"
+#include "nsAppShellWindowEnumerator.h"
+#include "nsWindowMediator.h"
+#include "nsIWindowMediatorListener.h"
+#include "nsGlobalWindow.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nsIDocShell.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIAppWindow.h"
+
+using namespace mozilla;
+
+nsresult nsWindowMediator::GetDOMWindow(
+ nsIAppWindow* inWindow, nsCOMPtr<nsPIDOMWindowOuter>& outDOMWindow) {
+ nsCOMPtr<nsIDocShell> docShell;
+
+ outDOMWindow = nullptr;
+ inWindow->GetDocShell(getter_AddRefs(docShell));
+ NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
+
+ outDOMWindow = docShell->GetWindow();
+ return outDOMWindow ? NS_OK : NS_ERROR_FAILURE;
+}
+
+nsWindowMediator::nsWindowMediator()
+ : mEnumeratorList(),
+ mOldestWindow(nullptr),
+ mTopmostWindow(nullptr),
+ mTimeStamp(0),
+ mSortingZOrder(false),
+ mReady(false) {}
+
+nsWindowMediator::~nsWindowMediator() {
+ while (mOldestWindow) UnregisterWindow(mOldestWindow);
+}
+
+nsresult nsWindowMediator::Init() {
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> obsSvc =
+ do_GetService("@mozilla.org/observer-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = obsSvc->AddObserver(this, "xpcom-shutdown", true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mReady = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsWindowMediator::RegisterWindow(nsIAppWindow* inWindow) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ if (!mReady) {
+ NS_ERROR("Mediator is not initialized or about to die.");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (GetInfoFor(inWindow)) {
+ NS_ERROR("multiple window registration");
+ return NS_ERROR_FAILURE;
+ }
+
+ mTimeStamp++;
+
+ // Create window info struct and add to list of windows
+ nsWindowInfo* windowInfo = new nsWindowInfo(inWindow, mTimeStamp);
+
+ for (const auto& listener : mListeners.ForwardRange()) {
+ listener->OnOpenWindow(inWindow);
+ }
+
+ if (mOldestWindow)
+ windowInfo->InsertAfter(mOldestWindow->mOlder, nullptr);
+ else
+ mOldestWindow = windowInfo;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::UnregisterWindow(nsIAppWindow* inWindow) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mReady);
+ NS_ENSURE_STATE(mReady);
+ nsWindowInfo* info = GetInfoFor(inWindow);
+ if (info) return UnregisterWindow(info);
+ return NS_ERROR_INVALID_ARG;
+}
+
+nsresult nsWindowMediator::UnregisterWindow(nsWindowInfo* inInfo) {
+ // Inform the iterators
+ uint32_t index = 0;
+ while (index < mEnumeratorList.Length()) {
+ mEnumeratorList[index]->WindowRemoved(inInfo);
+ index++;
+ }
+
+ nsIAppWindow* window = inInfo->mWindow.get();
+ for (const auto& listener : mListeners.ForwardRange()) {
+ listener->OnCloseWindow(window);
+ }
+
+ // Remove from the lists and free up
+ if (inInfo == mOldestWindow) mOldestWindow = inInfo->mYounger;
+ if (inInfo == mTopmostWindow) mTopmostWindow = inInfo->mLower;
+ inInfo->Unlink(true, true);
+ if (inInfo == mOldestWindow) mOldestWindow = nullptr;
+ if (inInfo == mTopmostWindow) mTopmostWindow = nullptr;
+ delete inInfo;
+
+ return NS_OK;
+}
+
+nsWindowInfo* nsWindowMediator::GetInfoFor(nsIAppWindow* aWindow) {
+ nsWindowInfo *info, *listEnd;
+
+ if (!aWindow) return nullptr;
+
+ info = mOldestWindow;
+ listEnd = nullptr;
+ while (info != listEnd) {
+ if (info->mWindow.get() == aWindow) return info;
+ info = info->mYounger;
+ listEnd = mOldestWindow;
+ }
+ return nullptr;
+}
+
+nsWindowInfo* nsWindowMediator::GetInfoFor(nsIWidget* aWindow) {
+ nsWindowInfo *info, *listEnd;
+
+ if (!aWindow) return nullptr;
+
+ info = mOldestWindow;
+ listEnd = nullptr;
+
+ nsCOMPtr<nsIWidget> scanWidget;
+ while (info != listEnd) {
+ nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(info->mWindow));
+ if (base) base->GetMainWidget(getter_AddRefs(scanWidget));
+ if (aWindow == scanWidget.get()) return info;
+ info = info->mYounger;
+ listEnd = mOldestWindow;
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetEnumerator(const char16_t* inType,
+ nsISimpleEnumerator** outEnumerator) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(outEnumerator);
+ if (!mReady) {
+ // If we get here with mReady false, we most likely did observe
+ // xpcom-shutdown. We will return an empty enumerator such that
+ // we make happy Javascripts calling late without throwing.
+ return NS_NewEmptyEnumerator(outEnumerator);
+ }
+ RefPtr<nsAppShellWindowEnumerator> enumerator =
+ new nsASDOMWindowEarlyToLateEnumerator(inType, *this);
+ enumerator.forget(outEnumerator);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetAppWindowEnumerator(const char16_t* inType,
+ nsISimpleEnumerator** outEnumerator) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(outEnumerator);
+ if (!mReady) {
+ // If we get here with mReady false, we most likely did observe
+ // xpcom-shutdown. We will return an empty enumerator such that
+ // we make happy Javascripts calling late without throwing.
+ return NS_NewEmptyEnumerator(outEnumerator);
+ }
+ RefPtr<nsAppShellWindowEnumerator> enumerator =
+ new nsASAppWindowEarlyToLateEnumerator(inType, *this);
+ enumerator.forget(outEnumerator);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetZOrderAppWindowEnumerator(const char16_t* aWindowType,
+ bool aFrontToBack,
+ nsISimpleEnumerator** _retval) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(_retval);
+ if (!mReady) {
+ // If we get here with mReady false, we most likely did observe
+ // xpcom-shutdown. We will return an empty enumerator such that
+ // we make happy Javascripts calling late without throwing.
+ return NS_NewEmptyEnumerator(_retval);
+ }
+ RefPtr<nsAppShellWindowEnumerator> enumerator;
+ if (aFrontToBack)
+ enumerator = new nsASAppWindowFrontToBackEnumerator(aWindowType, *this);
+ else
+ enumerator = new nsASAppWindowBackToFrontEnumerator(aWindowType, *this);
+
+ enumerator.forget(_retval);
+ return NS_OK;
+}
+
+void nsWindowMediator::AddEnumerator(nsAppShellWindowEnumerator* inEnumerator) {
+ mEnumeratorList.AppendElement(inEnumerator);
+}
+
+int32_t nsWindowMediator::RemoveEnumerator(
+ nsAppShellWindowEnumerator* inEnumerator) {
+ return mEnumeratorList.RemoveElement(inEnumerator);
+}
+
+// Returns the window of type inType ( if null return any window type ) which
+// has the most recent time stamp
+NS_IMETHODIMP
+nsWindowMediator::GetMostRecentWindow(const char16_t* inType,
+ mozIDOMWindowProxy** outWindow) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(outWindow);
+ *outWindow = nullptr;
+ if (!mReady) return NS_OK;
+
+ // Find the most window with the highest time stamp that matches
+ // the requested type
+ nsWindowInfo* info = MostRecentWindowInfo(inType, false);
+ if (info && info->mWindow) {
+ nsCOMPtr<nsPIDOMWindowOuter> DOMWindow;
+ if (NS_SUCCEEDED(GetDOMWindow(info->mWindow, DOMWindow))) {
+ DOMWindow.forget(outWindow);
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetMostRecentBrowserWindow(mozIDOMWindowProxy** outWindow) {
+ nsresult rv = GetMostRecentWindow(u"navigator:browser", outWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef MOZ_WIDGET_ANDROID
+ if (!*outWindow) {
+ rv = GetMostRecentWindow(u"navigator:geckoview", outWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+#endif
+
+#ifdef MOZ_THUNDERBIRD
+ if (!*outWindow) {
+ rv = GetMostRecentWindow(u"mail:3pane", outWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+#endif
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetMostRecentNonPBWindow(const char16_t* aType,
+ mozIDOMWindowProxy** aWindow) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(aWindow);
+ *aWindow = nullptr;
+
+ nsWindowInfo* info = MostRecentWindowInfo(aType, true);
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow;
+ if (info && info->mWindow) {
+ GetDOMWindow(info->mWindow, domWindow);
+ }
+
+ if (!domWindow) {
+ return NS_ERROR_FAILURE;
+ }
+
+ domWindow.forget(aWindow);
+ return NS_OK;
+}
+
+nsWindowInfo* nsWindowMediator::MostRecentWindowInfo(
+ const char16_t* inType, bool aSkipPrivateBrowsingOrClosed) {
+ int32_t lastTimeStamp = -1;
+ nsAutoString typeString(inType);
+ bool allWindows = !inType || typeString.IsEmpty();
+
+ // Find the most recent window with the highest time stamp that matches
+ // the requested type and has the correct browsing mode.
+ nsWindowInfo* searchInfo = mOldestWindow;
+ nsWindowInfo* listEnd = nullptr;
+ nsWindowInfo* foundInfo = nullptr;
+ for (; searchInfo != listEnd; searchInfo = searchInfo->mYounger) {
+ listEnd = mOldestWindow;
+
+ if (!allWindows && !searchInfo->TypeEquals(typeString)) {
+ continue;
+ }
+ if (searchInfo->mTimeStamp < lastTimeStamp) {
+ continue;
+ }
+ if (!searchInfo->mWindow) {
+ continue;
+ }
+ if (aSkipPrivateBrowsingOrClosed) {
+ nsCOMPtr<nsIDocShell> docShell;
+ searchInfo->mWindow->GetDocShell(getter_AddRefs(docShell));
+ nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
+ if (!loadContext || loadContext->UsePrivateBrowsing()) {
+ continue;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> piwindow = docShell->GetWindow();
+ if (!piwindow || piwindow->Closed()) {
+ continue;
+ }
+ }
+
+ foundInfo = searchInfo;
+ lastTimeStamp = searchInfo->mTimeStamp;
+ }
+
+ return foundInfo;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetOuterWindowWithId(uint64_t aWindowID,
+ mozIDOMWindowProxy** aWindow) {
+ RefPtr<nsGlobalWindowOuter> window =
+ nsGlobalWindowOuter::GetOuterWindowWithId(aWindowID);
+ window.forget(aWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetCurrentInnerWindowWithId(uint64_t aWindowID,
+ mozIDOMWindow** aWindow) {
+ RefPtr<nsGlobalWindowInner> window =
+ nsGlobalWindowInner::GetInnerWindowWithId(aWindowID);
+
+ // not found
+ if (!window) return NS_OK;
+
+ nsCOMPtr<nsPIDOMWindowOuter> outer = window->GetOuterWindow();
+ NS_ENSURE_TRUE(outer, NS_ERROR_UNEXPECTED);
+
+ // outer is already using another inner, so it's same as not found
+ if (outer->GetCurrentInnerWindow() != window) return NS_OK;
+
+ window.forget(aWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::UpdateWindowTimeStamp(nsIAppWindow* inWindow) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mReady);
+ NS_ENSURE_STATE(mReady);
+ nsWindowInfo* info = GetInfoFor(inWindow);
+ if (info) {
+ // increment the window's time stamp
+ info->mTimeStamp = ++mTimeStamp;
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+/* This method's plan is to intervene only when absolutely necessary.
+ We will get requests to place our windows behind unknown windows.
+ For the most part, we need to leave those alone (turning them into
+ explicit requests to be on top breaks Windows.) So generally we
+ calculate a change as seldom as possible.
+*/
+NS_IMETHODIMP
+nsWindowMediator::CalculateZPosition(nsIAppWindow* inWindow,
+ uint32_t inPosition, nsIWidget* inBelow,
+ uint32_t* outPosition,
+ nsIWidget** outBelow, bool* outAltered) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(outBelow);
+ MOZ_ASSERT(mReady);
+ NS_ENSURE_STATE(mReady);
+
+ *outBelow = nullptr;
+
+ if (!inWindow || !outPosition || !outAltered) return NS_ERROR_NULL_POINTER;
+
+ if (inPosition != nsIWindowMediator::zLevelTop &&
+ inPosition != nsIWindowMediator::zLevelBottom &&
+ inPosition != nsIWindowMediator::zLevelBelow)
+ return NS_ERROR_INVALID_ARG;
+
+ nsWindowInfo* info = mTopmostWindow;
+ nsIAppWindow* belowWindow = nullptr;
+ bool found = false;
+ nsresult result = NS_OK;
+
+ *outPosition = inPosition;
+ *outAltered = false;
+
+ if (mSortingZOrder) { // don't fight SortZOrder()
+ *outBelow = inBelow;
+ NS_IF_ADDREF(*outBelow);
+ return NS_OK;
+ }
+
+ uint32_t inZ;
+ GetZLevel(inWindow, &inZ);
+
+ if (inPosition == nsIWindowMediator::zLevelBelow) {
+ // locate inBelow. use topmost if it can't be found or isn't in the
+ // z-order list
+ info = GetInfoFor(inBelow);
+ if (!info || (info->mYounger != info && info->mLower == info))
+ info = mTopmostWindow;
+ else
+ found = true;
+
+ if (!found) {
+ /* Treat unknown windows as a request to be on top.
+ Not as it should be, but that's what Windows gives us.
+ Note we change inPosition, but not *outPosition. This forces
+ us to go through the "on top" calculation just below, without
+ necessarily changing the output parameters. */
+ inPosition = nsIWindowMediator::zLevelTop;
+ }
+ }
+
+ if (inPosition == nsIWindowMediator::zLevelTop) {
+ if (mTopmostWindow && mTopmostWindow->mZLevel > inZ) {
+ // asked for topmost, can't have it. locate highest allowed position.
+ do {
+ if (info->mZLevel <= inZ) break;
+ info = info->mLower;
+ } while (info != mTopmostWindow);
+
+ *outPosition = nsIWindowMediator::zLevelBelow;
+ belowWindow = info->mHigher->mWindow;
+ *outAltered = true;
+ }
+ } else if (inPosition == nsIWindowMediator::zLevelBottom) {
+ if (mTopmostWindow && mTopmostWindow->mHigher->mZLevel < inZ) {
+ // asked for bottommost, can't have it. locate lowest allowed position.
+ do {
+ info = info->mHigher;
+ if (info->mZLevel >= inZ) break;
+ } while (info != mTopmostWindow);
+
+ *outPosition = nsIWindowMediator::zLevelBelow;
+ belowWindow = info->mWindow;
+ *outAltered = true;
+ }
+ } else {
+ unsigned long relativeZ;
+
+ // check that we're in the right z-plane
+ if (found) {
+ belowWindow = info->mWindow;
+ relativeZ = info->mZLevel;
+ if (relativeZ > inZ) {
+ // might be OK. is lower window, if any, lower?
+ if (info->mLower != info && info->mLower->mZLevel > inZ) {
+ do {
+ if (info->mZLevel <= inZ) break;
+ info = info->mLower;
+ } while (info != mTopmostWindow);
+
+ belowWindow = info->mHigher->mWindow;
+ *outAltered = true;
+ }
+ } else if (relativeZ < inZ) {
+ // nope. look for a higher window to be behind.
+ do {
+ info = info->mHigher;
+ if (info->mZLevel >= inZ) break;
+ } while (info != mTopmostWindow);
+
+ if (info->mZLevel >= inZ)
+ belowWindow = info->mWindow;
+ else
+ *outPosition = nsIWindowMediator::zLevelTop;
+ *outAltered = true;
+ } // else they're equal, so it's OK
+ }
+ }
+
+ if (NS_SUCCEEDED(result) && belowWindow) {
+ nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(belowWindow));
+ if (base)
+ base->GetMainWidget(outBelow);
+ else
+ result = NS_ERROR_NO_INTERFACE;
+ }
+
+ return result;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::SetZPosition(nsIAppWindow* inWindow, uint32_t inPosition,
+ nsIAppWindow* inBelow) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ nsWindowInfo *inInfo, *belowInfo;
+
+ if ((inPosition != nsIWindowMediator::zLevelTop &&
+ inPosition != nsIWindowMediator::zLevelBottom &&
+ inPosition != nsIWindowMediator::zLevelBelow) ||
+ !inWindow) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (mSortingZOrder) // don't fight SortZOrder()
+ return NS_OK;
+
+ MOZ_ASSERT(mReady);
+ NS_ENSURE_STATE(mReady);
+
+ /* Locate inWindow and unlink it from the z-order list.
+ It's important we look for it in the age list, not the z-order list.
+ This is because the former is guaranteed complete, while
+ now may be this window's first exposure to the latter. */
+ inInfo = GetInfoFor(inWindow);
+ if (!inInfo) return NS_ERROR_INVALID_ARG;
+
+ // locate inBelow, place inWindow behind it
+ if (inPosition == nsIWindowMediator::zLevelBelow) {
+ belowInfo = GetInfoFor(inBelow);
+ // it had better also be in the z-order list
+ if (belowInfo && belowInfo->mYounger != belowInfo &&
+ belowInfo->mLower == belowInfo) {
+ belowInfo = nullptr;
+ }
+ if (!belowInfo) {
+ if (inBelow)
+ return NS_ERROR_INVALID_ARG;
+ else
+ inPosition = nsIWindowMediator::zLevelTop;
+ }
+ }
+ if (inPosition == nsIWindowMediator::zLevelTop ||
+ inPosition == nsIWindowMediator::zLevelBottom)
+ belowInfo = mTopmostWindow ? mTopmostWindow->mHigher : nullptr;
+
+ if (inInfo != belowInfo) {
+ inInfo->Unlink(false, true);
+ inInfo->InsertAfter(nullptr, belowInfo);
+ }
+ if (inPosition == nsIWindowMediator::zLevelTop) mTopmostWindow = inInfo;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetZLevel(nsIAppWindow* aWindow, uint32_t* _retval) {
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nsIAppWindow::normalZ;
+ // This can fail during window destruction.
+ nsWindowInfo* info = GetInfoFor(aWindow);
+ if (info) {
+ *_retval = info->mZLevel;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::SetZLevel(nsIAppWindow* aWindow, uint32_t aZLevel) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mReady);
+ NS_ENSURE_STATE(mReady);
+
+ nsWindowInfo* info = GetInfoFor(aWindow);
+ NS_ASSERTION(info, "setting z level of unregistered window");
+ if (!info) return NS_ERROR_FAILURE;
+
+ if (info->mZLevel != aZLevel) {
+ bool lowered = info->mZLevel > aZLevel;
+ info->mZLevel = aZLevel;
+ if (lowered)
+ SortZOrderFrontToBack();
+ else
+ SortZOrderBackToFront();
+ }
+ return NS_OK;
+}
+
+/* Fix potentially out-of-order windows by performing an insertion sort
+ on the z-order list. The method will work no matter how broken the
+ list, but its assumed usage is immediately after one window's z level
+ has been changed, so one window is potentially out of place. Such a sort
+ is most efficiently done in a particular direction. Use this one
+ if a window's z level has just been reduced, so the sort is most efficiently
+ done front to back.
+ Note it's hardly worth going to all the trouble to write two versions
+ of this method except that if we choose the inefficient sorting direction,
+ on slow systems windows could visibly bubble around the window that
+ was moved.
+*/
+void nsWindowMediator::SortZOrderFrontToBack() {
+ nsWindowInfo *scan, // scans list looking for problems
+ *search, // searches for correct placement for scan window
+ *prev, // previous search element
+ *lowest; // bottom-most window in list
+ bool finished;
+
+ if (!mTopmostWindow) // early during program execution there's no z list yet
+ return; // there's also only one window, so this is not dangerous
+
+ mSortingZOrder = true;
+
+ /* Step through the list from top to bottom. If we find a window which
+ should be moved down in the list, move it to its highest legal position. */
+ do {
+ finished = true;
+ lowest = mTopmostWindow->mHigher;
+ scan = mTopmostWindow;
+ while (scan != lowest) {
+ uint32_t scanZ = scan->mZLevel;
+ if (scanZ < scan->mLower->mZLevel) { // out of order
+ search = scan->mLower;
+ do {
+ prev = search;
+ search = search->mLower;
+ } while (prev != lowest && scanZ < search->mZLevel);
+
+ // reposition |scan| within the list
+ if (scan == mTopmostWindow) mTopmostWindow = scan->mLower;
+ scan->Unlink(false, true);
+ scan->InsertAfter(nullptr, prev);
+
+ // fix actual window order
+ nsCOMPtr<nsIBaseWindow> base;
+ nsCOMPtr<nsIWidget> scanWidget;
+ nsCOMPtr<nsIWidget> prevWidget;
+ base = do_QueryInterface(scan->mWindow);
+ if (base) base->GetMainWidget(getter_AddRefs(scanWidget));
+ base = do_QueryInterface(prev->mWindow);
+ if (base) base->GetMainWidget(getter_AddRefs(prevWidget));
+ if (scanWidget)
+ scanWidget->PlaceBehind(eZPlacementBelow, prevWidget, false);
+
+ finished = false;
+ break;
+ }
+ scan = scan->mLower;
+ }
+ } while (!finished);
+
+ mSortingZOrder = false;
+}
+
+// see comment for SortZOrderFrontToBack
+void nsWindowMediator::SortZOrderBackToFront() {
+ nsWindowInfo *scan, // scans list looking for problems
+ *search, // searches for correct placement for scan window
+ *lowest; // bottom-most window in list
+ bool finished;
+
+ if (!mTopmostWindow) // early during program execution there's no z list yet
+ return; // there's also only one window, so this is not dangerous
+
+ mSortingZOrder = true;
+
+ /* Step through the list from bottom to top. If we find a window which
+ should be moved up in the list, move it to its lowest legal position. */
+ do {
+ finished = true;
+ lowest = mTopmostWindow->mHigher;
+ scan = lowest;
+ while (scan != mTopmostWindow) {
+ uint32_t scanZ = scan->mZLevel;
+ if (scanZ > scan->mHigher->mZLevel) { // out of order
+ search = scan;
+ do {
+ search = search->mHigher;
+ } while (search != lowest && scanZ > search->mZLevel);
+
+ // reposition |scan| within the list
+ if (scan != search && scan != search->mLower) {
+ scan->Unlink(false, true);
+ scan->InsertAfter(nullptr, search);
+ }
+ if (search == lowest) mTopmostWindow = scan;
+
+ // fix actual window order
+ nsCOMPtr<nsIBaseWindow> base;
+ nsCOMPtr<nsIWidget> scanWidget;
+ nsCOMPtr<nsIWidget> searchWidget;
+ base = do_QueryInterface(scan->mWindow);
+ if (base) base->GetMainWidget(getter_AddRefs(scanWidget));
+ if (mTopmostWindow != scan) {
+ base = do_QueryInterface(search->mWindow);
+ if (base) base->GetMainWidget(getter_AddRefs(searchWidget));
+ }
+ if (scanWidget)
+ scanWidget->PlaceBehind(eZPlacementBelow, searchWidget, false);
+ finished = false;
+ break;
+ }
+ scan = scan->mHigher;
+ }
+ } while (!finished);
+
+ mSortingZOrder = false;
+}
+
+NS_IMPL_ISUPPORTS(nsWindowMediator, nsIWindowMediator, nsIObserver,
+ nsISupportsWeakReference)
+
+NS_IMETHODIMP
+nsWindowMediator::AddListener(nsIWindowMediatorListener* aListener) {
+ NS_ENSURE_ARG_POINTER(aListener);
+
+ mListeners.AppendElement(aListener);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::RemoveListener(nsIWindowMediatorListener* aListener) {
+ NS_ENSURE_ARG_POINTER(aListener);
+
+ mListeners.RemoveElement(aListener);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (!strcmp(aTopic, "xpcom-shutdown") && mReady) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ while (mOldestWindow) UnregisterWindow(mOldestWindow);
+ mReady = false;
+ }
+ return NS_OK;
+}