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