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