xpfe/appshell/src/nsWindowMediator.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial