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