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 "nsAppShellWindowEnumerator.h"
7 
8 #include "nsIContentViewer.h"
9 #include "nsIDocShell.h"
10 #include "mozilla/dom/Document.h"
11 #include "nsIInterfaceRequestor.h"
12 #include "nsIInterfaceRequestorUtils.h"
13 #include "nsIAppWindow.h"
14 #include "mozilla/dom/Element.h"
15 
16 #include "nsWindowMediator.h"
17 
18 using mozilla::dom::Document;
19 using mozilla::dom::Element;
20 
21 //
22 // static helper functions
23 //
24 
25 static void GetAttribute(nsIAppWindow* inWindow, const nsAString& inAttribute,
26                          nsAString& outValue);
27 static void GetWindowType(nsIAppWindow* inWindow, nsString& outType);
28 
GetElementFromDocShell(nsIDocShell * aShell)29 static Element* GetElementFromDocShell(nsIDocShell* aShell) {
30   nsCOMPtr<nsIContentViewer> cv;
31   aShell->GetContentViewer(getter_AddRefs(cv));
32   if (cv) {
33     RefPtr<Document> doc = cv->GetDocument();
34     if (doc) {
35       return doc->GetDocumentElement();
36     }
37   }
38 
39   return nullptr;
40 }
41 
42 // generic "retrieve the value of a XUL attribute" function
GetAttribute(nsIAppWindow * inWindow,const nsAString & inAttribute,nsAString & outValue)43 void GetAttribute(nsIAppWindow* inWindow, const nsAString& inAttribute,
44                   nsAString& outValue) {
45   nsCOMPtr<nsIDocShell> shell;
46   if (inWindow && NS_SUCCEEDED(inWindow->GetDocShell(getter_AddRefs(shell)))) {
47     RefPtr<Element> webshellElement = GetElementFromDocShell(shell);
48     if (webshellElement) {
49       webshellElement->GetAttribute(inAttribute, outValue);
50     }
51   }
52 }
53 
54 // retrieve the window type, stored as the value of a particular
55 // attribute in its XUL window tag
GetWindowType(nsIAppWindow * aWindow,nsString & outType)56 void GetWindowType(nsIAppWindow* aWindow, nsString& outType) {
57   GetAttribute(aWindow, u"windowtype"_ns, outType);
58 }
59 
60 //
61 // nsWindowInfo
62 //
63 
nsWindowInfo(nsIAppWindow * inWindow,int32_t inTimeStamp)64 nsWindowInfo::nsWindowInfo(nsIAppWindow* inWindow, int32_t inTimeStamp)
65     : mWindow(inWindow),
66       mTimeStamp(inTimeStamp),
67       mZLevel(nsIAppWindow::normalZ) {
68   ReferenceSelf(true, true);
69 }
70 
~nsWindowInfo()71 nsWindowInfo::~nsWindowInfo() {}
72 
73 // return true if the window described by this WindowInfo has a type
74 // equal to the given type
TypeEquals(const nsAString & aType)75 bool nsWindowInfo::TypeEquals(const nsAString& aType) {
76   nsAutoString rtnString;
77   GetWindowType(mWindow, rtnString);
78   return rtnString == aType;
79 }
80 
81 // insert the struct into their two linked lists, in position after the
82 // given (independent) method arguments
InsertAfter(nsWindowInfo * inOlder,nsWindowInfo * inHigher)83 void nsWindowInfo::InsertAfter(nsWindowInfo* inOlder, nsWindowInfo* inHigher) {
84   if (inOlder) {
85     mOlder = inOlder;
86     mYounger = inOlder->mYounger;
87     mOlder->mYounger = this;
88     if (mOlder->mOlder == mOlder) mOlder->mOlder = this;
89     mYounger->mOlder = this;
90     if (mYounger->mYounger == mYounger) mYounger->mYounger = this;
91   }
92   if (inHigher) {
93     mHigher = inHigher;
94     mLower = inHigher->mLower;
95     mHigher->mLower = this;
96     if (mHigher->mHigher == mHigher) mHigher->mHigher = this;
97     mLower->mHigher = this;
98     if (mLower->mLower == mLower) mLower->mLower = this;
99   }
100 }
101 
102 // remove the struct from its linked lists
Unlink(bool inAge,bool inZ)103 void nsWindowInfo::Unlink(bool inAge, bool inZ) {
104   if (inAge) {
105     mOlder->mYounger = mYounger;
106     mYounger->mOlder = mOlder;
107   }
108   if (inZ) {
109     mLower->mHigher = mHigher;
110     mHigher->mLower = mLower;
111   }
112   ReferenceSelf(inAge, inZ);
113 }
114 
115 // initialize the struct to be a valid linked list of one element
ReferenceSelf(bool inAge,bool inZ)116 void nsWindowInfo::ReferenceSelf(bool inAge, bool inZ) {
117   if (inAge) {
118     mYounger = this;
119     mOlder = this;
120   }
121   if (inZ) {
122     mLower = this;
123     mHigher = this;
124   }
125 }
126 
127 //
128 // nsAppShellWindowEnumerator
129 //
130 
nsAppShellWindowEnumerator(const char16_t * aTypeString,nsWindowMediator & aMediator)131 nsAppShellWindowEnumerator::nsAppShellWindowEnumerator(
132     const char16_t* aTypeString, nsWindowMediator& aMediator)
133     : mWindowMediator(&aMediator),
134       mType(aTypeString),
135       mCurrentPosition(nullptr) {
136   mWindowMediator->AddEnumerator(this);
137   NS_ADDREF(mWindowMediator);
138 }
139 
~nsAppShellWindowEnumerator()140 nsAppShellWindowEnumerator::~nsAppShellWindowEnumerator() {
141   mWindowMediator->RemoveEnumerator(this);
142   NS_RELEASE(mWindowMediator);
143 }
144 
145 // after mCurrentPosition has been initialized to point to the beginning
146 // of the appropriate list, adjust it if necessary
AdjustInitialPosition()147 void nsAppShellWindowEnumerator::AdjustInitialPosition() {
148   if (!mType.IsEmpty() && mCurrentPosition &&
149       !mCurrentPosition->TypeEquals(mType))
150     mCurrentPosition = FindNext();
151 }
152 
HasMoreElements(bool * retval)153 NS_IMETHODIMP nsAppShellWindowEnumerator::HasMoreElements(bool* retval) {
154   if (!retval) return NS_ERROR_INVALID_ARG;
155 
156   *retval = mCurrentPosition ? true : false;
157   return NS_OK;
158 }
159 
160 // if a window is being removed adjust the iterator's current position
WindowRemoved(nsWindowInfo * inInfo)161 void nsAppShellWindowEnumerator::WindowRemoved(nsWindowInfo* inInfo) {
162   if (mCurrentPosition == inInfo) mCurrentPosition = FindNext();
163 }
164 
165 //
166 // nsASDOMWindowEnumerator
167 //
168 
nsASDOMWindowEnumerator(const char16_t * aTypeString,nsWindowMediator & aMediator)169 nsASDOMWindowEnumerator::nsASDOMWindowEnumerator(const char16_t* aTypeString,
170                                                  nsWindowMediator& aMediator)
171     : nsAppShellWindowEnumerator(aTypeString, aMediator) {}
172 
~nsASDOMWindowEnumerator()173 nsASDOMWindowEnumerator::~nsASDOMWindowEnumerator() {}
174 
GetNext(nsISupports ** retval)175 NS_IMETHODIMP nsASDOMWindowEnumerator::GetNext(nsISupports** retval) {
176   if (!retval) return NS_ERROR_INVALID_ARG;
177 
178   *retval = nullptr;
179   while (mCurrentPosition) {
180     nsCOMPtr<nsPIDOMWindowOuter> domWindow;
181     nsWindowMediator::GetDOMWindow(mCurrentPosition->mWindow, domWindow);
182     mCurrentPosition = FindNext();
183     if (domWindow) return CallQueryInterface(domWindow, retval);
184   }
185   return NS_ERROR_FAILURE;
186 }
187 
188 //
189 // nsASAppWindowEnumerator
190 //
191 
nsASAppWindowEnumerator(const char16_t * aTypeString,nsWindowMediator & aMediator)192 nsASAppWindowEnumerator::nsASAppWindowEnumerator(const char16_t* aTypeString,
193                                                  nsWindowMediator& aMediator)
194     : nsAppShellWindowEnumerator(aTypeString, aMediator) {}
195 
~nsASAppWindowEnumerator()196 nsASAppWindowEnumerator::~nsASAppWindowEnumerator() {}
197 
GetNext(nsISupports ** retval)198 NS_IMETHODIMP nsASAppWindowEnumerator::GetNext(nsISupports** retval) {
199   if (!retval) return NS_ERROR_INVALID_ARG;
200 
201   *retval = nullptr;
202   if (mCurrentPosition) {
203     CallQueryInterface(mCurrentPosition->mWindow, retval);
204     mCurrentPosition = FindNext();
205     return NS_OK;
206   }
207   return NS_ERROR_FAILURE;
208 }
209 
210 //
211 // nsASDOMWindowEarlyToLateEnumerator
212 //
213 
nsASDOMWindowEarlyToLateEnumerator(const char16_t * aTypeString,nsWindowMediator & aMediator)214 nsASDOMWindowEarlyToLateEnumerator::nsASDOMWindowEarlyToLateEnumerator(
215     const char16_t* aTypeString, nsWindowMediator& aMediator)
216     : nsASDOMWindowEnumerator(aTypeString, aMediator) {
217   mCurrentPosition = aMediator.mOldestWindow;
218   AdjustInitialPosition();
219 }
220 
~nsASDOMWindowEarlyToLateEnumerator()221 nsASDOMWindowEarlyToLateEnumerator::~nsASDOMWindowEarlyToLateEnumerator() {}
222 
FindNext()223 nsWindowInfo* nsASDOMWindowEarlyToLateEnumerator::FindNext() {
224   nsWindowInfo *info, *listEnd;
225   bool allWindows = mType.IsEmpty();
226 
227   // see AppWindowEarlyToLateEnumerator::FindNext
228   if (!mCurrentPosition) return nullptr;
229 
230   info = mCurrentPosition->mYounger;
231   listEnd = mWindowMediator->mOldestWindow;
232 
233   while (info != listEnd) {
234     if (allWindows || info->TypeEquals(mType)) return info;
235     info = info->mYounger;
236   }
237 
238   return nullptr;
239 }
240 
241 //
242 // nsASAppWindowEarlyToLateEnumerator
243 //
244 
nsASAppWindowEarlyToLateEnumerator(const char16_t * aTypeString,nsWindowMediator & aMediator)245 nsASAppWindowEarlyToLateEnumerator::nsASAppWindowEarlyToLateEnumerator(
246     const char16_t* aTypeString, nsWindowMediator& aMediator)
247     : nsASAppWindowEnumerator(aTypeString, aMediator) {
248   mCurrentPosition = aMediator.mOldestWindow;
249   AdjustInitialPosition();
250 }
251 
~nsASAppWindowEarlyToLateEnumerator()252 nsASAppWindowEarlyToLateEnumerator::~nsASAppWindowEarlyToLateEnumerator() {}
253 
FindNext()254 nsWindowInfo* nsASAppWindowEarlyToLateEnumerator::FindNext() {
255   nsWindowInfo *info, *listEnd;
256   bool allWindows = mType.IsEmpty();
257 
258   /* mCurrentPosition null is assumed to mean that the enumerator has run
259      its course and is now basically useless. It could also be interpreted
260      to mean that it was created at a time when there were no windows. In
261      that case it would probably be more appropriate to check to see whether
262      windows have subsequently been added. But it's not guaranteed that we'll
263      pick up newly added windows anyway (if they occurred previous to our
264      current position) so we just don't worry about that. */
265   if (!mCurrentPosition) return nullptr;
266 
267   info = mCurrentPosition->mYounger;
268   listEnd = mWindowMediator->mOldestWindow;
269 
270   while (info != listEnd) {
271     if (allWindows || info->TypeEquals(mType)) return info;
272     info = info->mYounger;
273   }
274 
275   return nullptr;
276 }
277 
278 //
279 // nsASAppWindowFrontToBackEnumerator
280 //
281 
nsASAppWindowFrontToBackEnumerator(const char16_t * aTypeString,nsWindowMediator & aMediator)282 nsASAppWindowFrontToBackEnumerator::nsASAppWindowFrontToBackEnumerator(
283     const char16_t* aTypeString, nsWindowMediator& aMediator)
284     : nsASAppWindowEnumerator(aTypeString, aMediator) {
285   mCurrentPosition = aMediator.mTopmostWindow;
286   AdjustInitialPosition();
287 }
288 
~nsASAppWindowFrontToBackEnumerator()289 nsASAppWindowFrontToBackEnumerator::~nsASAppWindowFrontToBackEnumerator() {}
290 
FindNext()291 nsWindowInfo* nsASAppWindowFrontToBackEnumerator::FindNext() {
292   nsWindowInfo *info, *listEnd;
293   bool allWindows = mType.IsEmpty();
294 
295   // see AppWindowEarlyToLateEnumerator::FindNext
296   if (!mCurrentPosition) return nullptr;
297 
298   info = mCurrentPosition->mLower;
299   listEnd = mWindowMediator->mTopmostWindow;
300 
301   while (info != listEnd) {
302     if (allWindows || info->TypeEquals(mType)) return info;
303     info = info->mLower;
304   }
305 
306   return nullptr;
307 }
308 
309 //
310 // nsASAppWindowBackToFrontEnumerator
311 //
312 
nsASAppWindowBackToFrontEnumerator(const char16_t * aTypeString,nsWindowMediator & aMediator)313 nsASAppWindowBackToFrontEnumerator::nsASAppWindowBackToFrontEnumerator(
314     const char16_t* aTypeString, nsWindowMediator& aMediator)
315     : nsASAppWindowEnumerator(aTypeString, aMediator) {
316   mCurrentPosition =
317       aMediator.mTopmostWindow ? aMediator.mTopmostWindow->mHigher : nullptr;
318   AdjustInitialPosition();
319 }
320 
~nsASAppWindowBackToFrontEnumerator()321 nsASAppWindowBackToFrontEnumerator::~nsASAppWindowBackToFrontEnumerator() {}
322 
FindNext()323 nsWindowInfo* nsASAppWindowBackToFrontEnumerator::FindNext() {
324   nsWindowInfo *info, *listEnd;
325   bool allWindows = mType.IsEmpty();
326 
327   // see AppWindowEarlyToLateEnumerator::FindNext
328   if (!mCurrentPosition) return nullptr;
329 
330   info = mCurrentPosition->mHigher;
331   listEnd = mWindowMediator->mTopmostWindow;
332   if (listEnd) listEnd = listEnd->mHigher;
333 
334   while (info != listEnd) {
335     if (allWindows || info->TypeEquals(mType)) return info;
336     info = info->mHigher;
337   }
338 
339   return nullptr;
340 }
341