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