Back to index

lightning-sunbird  0.9+nobinonly
nsWindowWatcher.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* vim: set ts=2 sw=2 et tw=78: */
00003 /* ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 2001
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Harshal Pradhan <keeda@hotpop.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 //#define USEWEAKREFS // (haven't quite figured that out yet)
00041 
00042 #include "nsWindowWatcher.h"
00043 
00044 #include "nsAutoLock.h"
00045 #include "nsCRT.h"
00046 #include "nsNetUtil.h"
00047 #include "nsPrompt.h"
00048 #include "nsWWJSUtils.h"
00049 #include "plstr.h"
00050 
00051 #include "nsIBaseWindow.h"
00052 #include "nsIDocShell.h"
00053 #include "nsIDocShellLoadInfo.h"
00054 #include "nsIDocShellTreeItem.h"
00055 #include "nsIDocShellTreeOwner.h"
00056 #include "nsIDocument.h"
00057 #include "nsIDOMDocument.h"
00058 #include "nsIDOMWindow.h"
00059 #include "nsIDOMChromeWindow.h"
00060 #include "nsIDOMWindowInternal.h"
00061 #include "nsIScriptObjectPrincipal.h"
00062 #include "nsIScreen.h"
00063 #include "nsIScreenManager.h"
00064 #include "nsIScriptContext.h"
00065 #include "nsIEventQueue.h"
00066 #include "nsIEventQueueService.h"
00067 #include "nsIGenericFactory.h"
00068 #include "nsIJSContextStack.h"
00069 #include "nsIObserverService.h"
00070 #include "nsIScriptGlobalObject.h"
00071 #include "nsIScriptSecurityManager.h"
00072 #include "nsISupportsArray.h"
00073 #include "nsXPCOM.h"
00074 #include "nsISupportsPrimitives.h"
00075 #include "nsIURI.h"
00076 #include "nsIWebBrowser.h"
00077 #include "nsIWebBrowserChrome.h"
00078 #include "nsIWebNavigation.h"
00079 #include "nsIWindowCreator.h"
00080 #include "nsIWindowCreator2.h"
00081 #include "nsIXPConnect.h"
00082 #include "nsPIDOMWindow.h"
00083 #include "nsIMarkupDocumentViewer.h"
00084 #include "nsIContentViewer.h"
00085 #include "nsIDocumentViewer.h"
00086 #include "nsIWindowProvider.h"
00087 
00088 #include "nsIPrefBranch.h"
00089 #include "nsIPrefService.h"
00090 
00091 #include "jsinterp.h" // for js_AllocStack() and js_FreeStack()
00092 
00093 #ifdef XP_UNIX
00094 // please see bug 78421 for the eventual "right" fix for this
00095 #define HAVE_LAME_APPSHELL
00096 #endif
00097 
00098 #ifdef HAVE_LAME_APPSHELL
00099 #include "nsIAppShell.h"
00100 // for NS_APPSHELL_CID
00101 #include <nsWidgetsCID.h>
00102 #endif
00103 
00104 #ifdef USEWEAKREFS
00105 #include "nsIWeakReference.h"
00106 #endif
00107 
00108 #ifdef HAVE_LAME_APPSHELL
00109 static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
00110 #endif
00111 
00112 static const char *sJSStackContractID="@mozilla.org/js/xpc/ContextStack;1";
00113 
00114 /****************************************************************
00115  ******************** nsWatcherWindowEntry **********************
00116  ****************************************************************/
00117 
00118 class nsWindowWatcher;
00119 
00120 struct nsWatcherWindowEntry {
00121 
00122   nsWatcherWindowEntry(nsIDOMWindow *inWindow, nsIWebBrowserChrome *inChrome) {
00123 #ifdef USEWEAKREFS
00124     mWindow = do_GetWeakReference(inWindow);
00125 #else
00126     mWindow = inWindow;
00127 #endif
00128     nsCOMPtr<nsISupportsWeakReference> supportsweak(do_QueryInterface(inChrome));
00129     if (supportsweak) {
00130       supportsweak->GetWeakReference(getter_AddRefs(mChromeWeak));
00131     } else {
00132       mChrome = inChrome;
00133       mChromeWeak = 0;
00134     }
00135     ReferenceSelf();
00136   }
00137   ~nsWatcherWindowEntry() {}
00138 
00139   void InsertAfter(nsWatcherWindowEntry *inOlder);
00140   void Unlink();
00141   void ReferenceSelf();
00142 
00143 #ifdef USEWEAKREFS
00144   nsCOMPtr<nsIWeakReference> mWindow;
00145 #else // still not an owning ref
00146   nsIDOMWindow              *mWindow;
00147 #endif
00148   nsIWebBrowserChrome       *mChrome;
00149   nsWeakPtr                  mChromeWeak;
00150   // each struct is in a circular, doubly-linked list
00151   nsWatcherWindowEntry      *mYounger, // next younger in sequence
00152                             *mOlder;
00153 };
00154 
00155 void nsWatcherWindowEntry::InsertAfter(nsWatcherWindowEntry *inOlder)
00156 {
00157   if (inOlder) {
00158     mOlder = inOlder;
00159     mYounger = inOlder->mYounger;
00160     mOlder->mYounger = this;
00161     if (mOlder->mOlder == mOlder)
00162       mOlder->mOlder = this;
00163     mYounger->mOlder = this;
00164     if (mYounger->mYounger == mYounger)
00165       mYounger->mYounger = this;
00166   }
00167 }
00168 
00169 void nsWatcherWindowEntry::Unlink() {
00170 
00171   mOlder->mYounger = mYounger;
00172   mYounger->mOlder = mOlder;
00173   ReferenceSelf();
00174 }
00175 
00176 void nsWatcherWindowEntry::ReferenceSelf() {
00177 
00178   mYounger = this;
00179   mOlder = this;
00180 }
00181 
00182 /****************************************************************
00183  ****************** nsWatcherWindowEnumerator *******************
00184  ****************************************************************/
00185 
00186 class nsWatcherWindowEnumerator : public nsISimpleEnumerator {
00187 
00188 public:
00189   nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher);
00190   virtual ~nsWatcherWindowEnumerator();
00191   NS_IMETHOD HasMoreElements(PRBool *retval);
00192   NS_IMETHOD GetNext(nsISupports **retval);
00193 
00194   NS_DECL_ISUPPORTS
00195 
00196 private:
00197   friend class nsWindowWatcher;
00198 
00199   nsWatcherWindowEntry *FindNext();
00200   void WindowRemoved(nsWatcherWindowEntry *inInfo);
00201 
00202   nsWindowWatcher      *mWindowWatcher;
00203   nsWatcherWindowEntry *mCurrentPosition;
00204 };
00205 
00206 NS_IMPL_ADDREF(nsWatcherWindowEnumerator)
00207 NS_IMPL_RELEASE(nsWatcherWindowEnumerator)
00208 NS_IMPL_QUERY_INTERFACE1(nsWatcherWindowEnumerator, nsISimpleEnumerator)
00209 
00210 nsWatcherWindowEnumerator::nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher)
00211   : mWindowWatcher(inWatcher),
00212     mCurrentPosition(inWatcher->mOldestWindow)
00213 {
00214   mWindowWatcher->AddEnumerator(this);
00215   mWindowWatcher->AddRef();
00216 }
00217 
00218 nsWatcherWindowEnumerator::~nsWatcherWindowEnumerator()
00219 {
00220   mWindowWatcher->RemoveEnumerator(this);
00221   mWindowWatcher->Release();
00222 }
00223 
00224 NS_IMETHODIMP
00225 nsWatcherWindowEnumerator::HasMoreElements(PRBool *retval)
00226 {
00227   if (!retval)
00228     return NS_ERROR_INVALID_ARG;
00229 
00230   *retval = mCurrentPosition? PR_TRUE : PR_FALSE;
00231   return NS_OK;
00232 }
00233     
00234 NS_IMETHODIMP
00235 nsWatcherWindowEnumerator::GetNext(nsISupports **retval)
00236 {
00237   if (!retval)
00238     return NS_ERROR_INVALID_ARG;
00239 
00240   *retval = NULL;
00241 
00242 #ifdef USEWEAKREFS
00243   while (mCurrentPosition) {
00244     CallQueryReferent(mCurrentPosition->mWindow, retval);
00245     if (*retval) {
00246       mCurrentPosition = FindNext();
00247       break;
00248     } else // window is gone!
00249       mWindowWatcher->RemoveWindow(mCurrentPosition);
00250   }
00251   NS_IF_ADDREF(*retval);
00252 #else
00253   if (mCurrentPosition) {
00254     CallQueryInterface(mCurrentPosition->mWindow, retval);
00255     mCurrentPosition = FindNext();
00256   }
00257 #endif
00258   return NS_OK;
00259 }
00260 
00261 nsWatcherWindowEntry *
00262 nsWatcherWindowEnumerator::FindNext()
00263 {
00264   nsWatcherWindowEntry *info;
00265 
00266   if (!mCurrentPosition)
00267     return 0;
00268 
00269   info = mCurrentPosition->mYounger;
00270   return info == mWindowWatcher->mOldestWindow ? 0 : info;
00271 }
00272 
00273 // if a window is being removed adjust the iterator's current position
00274 void nsWatcherWindowEnumerator::WindowRemoved(nsWatcherWindowEntry *inInfo) {
00275 
00276   if (mCurrentPosition == inInfo)
00277     mCurrentPosition = mCurrentPosition != inInfo->mYounger ?
00278                        inInfo->mYounger : 0;
00279 }
00280 
00281 /****************************************************************
00282  ********************* EventQueueAutoPopper *********************
00283  ****************************************************************/
00284 
00285 class EventQueueAutoPopper {
00286 public:
00287   EventQueueAutoPopper();
00288   ~EventQueueAutoPopper();
00289 
00290   nsresult Push();
00291 
00292 protected:
00293   nsCOMPtr<nsIEventQueueService> mService;
00294   nsCOMPtr<nsIEventQueue>        mQueue;
00295 #ifdef HAVE_LAME_APPSHELL
00296   nsCOMPtr<nsIAppShell>          mAppShell;
00297 #endif
00298 };
00299 
00300 EventQueueAutoPopper::EventQueueAutoPopper() : mQueue(nsnull)
00301 {
00302 }
00303 
00304 EventQueueAutoPopper::~EventQueueAutoPopper()
00305 {
00306 #ifdef HAVE_LAME_APPSHELL
00307   if (mAppShell) {
00308     if (mQueue)
00309       mAppShell->ListenToEventQueue(mQueue, PR_FALSE);
00310     mAppShell->Spindown();
00311     mAppShell = nsnull;
00312   }
00313 #endif
00314 
00315   if(mQueue)
00316     mService->PopThreadEventQueue(mQueue);
00317 }
00318 
00319 nsresult EventQueueAutoPopper::Push()
00320 {
00321   if (mQueue) // only once
00322     return NS_ERROR_FAILURE;
00323 
00324   mService = do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID);
00325   if (!mService)
00326     return NS_ERROR_FAILURE;
00327 
00328   // push a new queue onto it
00329   mService->PushThreadEventQueue(getter_AddRefs(mQueue));
00330   if (!mQueue)
00331     return NS_ERROR_FAILURE;
00332 
00333 #ifdef HAVE_LAME_APPSHELL
00334   // listen to the event queue
00335   mAppShell = do_CreateInstance(kAppShellCID);
00336   if (!mAppShell)
00337     return NS_ERROR_FAILURE;
00338 
00339   mAppShell->Create(0, nsnull);
00340   mAppShell->Spinup();
00341 
00342   // listen to the new queue
00343   mAppShell->ListenToEventQueue(mQueue, PR_TRUE);
00344 #endif
00345 
00346   return NS_OK;
00347 }
00348 
00349 /****************************************************************
00350  ********************** JSContextAutoPopper *********************
00351  ****************************************************************/
00352 
00353 class JSContextAutoPopper {
00354 public:
00355   JSContextAutoPopper();
00356   ~JSContextAutoPopper();
00357 
00358   nsresult   Push(JSContext *cx = nsnull);
00359   JSContext *get() { return mContext; }
00360 
00361 protected:
00362   nsCOMPtr<nsIThreadJSContextStack>  mService;
00363   JSContext                         *mContext;
00364 };
00365 
00366 JSContextAutoPopper::JSContextAutoPopper() : mContext(nsnull)
00367 {
00368 }
00369 
00370 JSContextAutoPopper::~JSContextAutoPopper()
00371 {
00372   JSContext *cx;
00373   nsresult   rv;
00374 
00375   if(mContext) {
00376     rv = mService->Pop(&cx);
00377     NS_ASSERTION(NS_SUCCEEDED(rv) && cx == mContext, "JSContext push/pop mismatch");
00378   }
00379 }
00380 
00381 nsresult JSContextAutoPopper::Push(JSContext *cx)
00382 {
00383   if (mContext) // only once
00384     return NS_ERROR_FAILURE;
00385 
00386   mService = do_GetService(sJSStackContractID);
00387   if(mService) {
00388     // Get the safe context if we're not provided one.
00389     if (!cx && NS_FAILED(mService->GetSafeJSContext(&cx))) {
00390       cx = nsnull;
00391     }
00392 
00393     // Save cx in mContext to indicate need to pop.
00394     if (cx && NS_SUCCEEDED(mService->Push(cx))) {
00395       mContext = cx;
00396     }
00397   }
00398   return mContext ? NS_OK : NS_ERROR_FAILURE;
00399 }
00400 
00401 /****************************************************************
00402  ************************** AutoFree ****************************
00403  ****************************************************************/
00404 
00405 class AutoFree {
00406 public:
00407   AutoFree(void *aPtr) : mPtr(aPtr) {
00408   }
00409   ~AutoFree() {
00410     if (mPtr)
00411       nsMemory::Free(mPtr);
00412   }
00413   void Invalidate() {
00414     mPtr = 0;
00415   }
00416 private:
00417   void *mPtr;
00418 };
00419 
00420 /****************************************************************
00421  *********************** nsWindowWatcher ************************
00422  ****************************************************************/
00423 
00424 NS_IMPL_ADDREF(nsWindowWatcher)
00425 NS_IMPL_RELEASE(nsWindowWatcher)
00426 NS_IMPL_QUERY_INTERFACE2(nsWindowWatcher, nsIWindowWatcher, nsPIWindowWatcher)
00427 
00428 nsWindowWatcher::nsWindowWatcher() :
00429         mEnumeratorList(),
00430         mOldestWindow(0),
00431         mActiveWindow(0),
00432         mListLock(0)
00433 {
00434 }
00435 
00436 nsWindowWatcher::~nsWindowWatcher()
00437 {
00438   // delete data
00439   while (mOldestWindow)
00440     RemoveWindow(mOldestWindow);
00441 
00442   if (mListLock)
00443     PR_DestroyLock(mListLock);
00444 }
00445 
00446 nsresult
00447 nsWindowWatcher::Init()
00448 {
00449   mListLock = PR_NewLock();
00450   if (!mListLock)
00451     return NS_ERROR_OUT_OF_MEMORY;
00452   return NS_OK;
00453 }
00454 
00455 NS_IMETHODIMP
00456 nsWindowWatcher::OpenWindow(nsIDOMWindow *aParent,
00457                             const char *aUrl,
00458                             const char *aName,
00459                             const char *aFeatures,
00460                             nsISupports *aArguments,
00461                             nsIDOMWindow **_retval)
00462 {
00463   PRUint32  argc;
00464   jsval    *argv = nsnull;
00465   JSContext *cx;
00466   void *mark;
00467 
00468   // This kungFuDeathGrip is filled when we are using aParent's context. It
00469   // prevents the context from being destroyed before we're truly done with
00470   // it.
00471   nsCOMPtr<nsIScriptContext> kungFuDeathGrip;
00472 
00473   nsresult rv = ConvertSupportsTojsvals(aParent, aArguments, &argc, &argv, &cx,
00474                                         &mark, getter_AddRefs(kungFuDeathGrip));
00475   if (NS_SUCCEEDED(rv)) {
00476     PRBool dialog = argc == 0 ? PR_FALSE : PR_TRUE;
00477     rv = OpenWindowJSInternal(aParent, aUrl, aName, aFeatures, dialog, argc,
00478                               argv, PR_FALSE, _retval);
00479 
00480     if (argv) {
00481       js_FreeStack(cx, mark);
00482     }
00483   }
00484 
00485   return rv;
00486 }
00487 
00488 struct SizeSpec {
00489   SizeSpec() :
00490     mLeftSpecified(PR_FALSE),
00491     mTopSpecified(PR_FALSE),
00492     mOuterWidthSpecified(PR_FALSE),
00493     mOuterHeightSpecified(PR_FALSE),
00494     mInnerWidthSpecified(PR_FALSE),
00495     mInnerHeightSpecified(PR_FALSE),
00496     mUseDefaultWidth(PR_FALSE),
00497     mUseDefaultHeight(PR_FALSE)
00498   {}
00499   
00500   PRInt32 mLeft;
00501   PRInt32 mTop;
00502   PRInt32 mOuterWidth;  // Total window width
00503   PRInt32 mOuterHeight; // Total window height
00504   PRInt32 mInnerWidth;  // Content area width
00505   PRInt32 mInnerHeight; // Content area height
00506 
00507   PRPackedBool mLeftSpecified;
00508   PRPackedBool mTopSpecified;
00509   PRPackedBool mOuterWidthSpecified;
00510   PRPackedBool mOuterHeightSpecified;
00511   PRPackedBool mInnerWidthSpecified;
00512   PRPackedBool mInnerHeightSpecified;
00513 
00514   // If these booleans are true, don't look at the corresponding width values
00515   // even if they're specified -- they'll be bogus
00516   PRPackedBool mUseDefaultWidth;
00517   PRPackedBool mUseDefaultHeight;
00518 
00519   PRBool PositionSpecified() const {
00520     return mLeftSpecified || mTopSpecified;
00521   }
00522   
00523   PRBool SizeSpecified() const {
00524     return mOuterWidthSpecified || mOuterHeightSpecified ||
00525       mInnerWidthSpecified || mInnerHeightSpecified;
00526   }
00527 };
00528 
00529 NS_IMETHODIMP
00530 nsWindowWatcher::OpenWindowJS(nsIDOMWindow *aParent,
00531                               const char *aUrl,
00532                               const char *aName,
00533                               const char *aFeatures,
00534                               PRBool aDialog,
00535                               PRUint32 argc,
00536                               jsval *argv,
00537                               nsIDOMWindow **_retval)
00538 {
00539   return OpenWindowJSInternal(aParent, aUrl, aName, aFeatures, aDialog, argc,
00540                               argv, PR_TRUE, _retval);
00541 }
00542 
00543 nsresult
00544 nsWindowWatcher::OpenWindowJSInternal(nsIDOMWindow *aParent,
00545                                       const char *aUrl,
00546                                       const char *aName,
00547                                       const char *aFeatures,
00548                                       PRBool aDialog,
00549                                       PRUint32 argc,
00550                                       jsval *argv,
00551                                       PRBool aCalledFromJS,
00552                                       nsIDOMWindow **_retval)
00553 {
00554   nsresult                        rv = NS_OK;
00555   PRBool                          nameSpecified,
00556                                   featuresSpecified,
00557                                   isNewToplevelWindow = PR_FALSE,
00558                                   windowIsNew = PR_FALSE,
00559                                   windowNeedsName = PR_FALSE,
00560                                   windowIsModal = PR_FALSE,
00561                                   uriToLoadIsChrome = PR_FALSE;
00562   PRUint32                        chromeFlags;
00563   nsAutoString                    name;             // string version of aName
00564   nsCAutoString                   features;         // string version of aFeatures
00565   nsCOMPtr<nsIURI>                uriToLoad;        // from aUrl, if any
00566   nsCOMPtr<nsIDocShellTreeOwner>  parentTreeOwner;  // from the parent window, if any
00567   nsCOMPtr<nsIDocShellTreeItem>   newDocShellItem;  // from the new window
00568   EventQueueAutoPopper            queueGuard;
00569   JSContextAutoPopper             callerContextGuard;
00570 
00571   NS_ENSURE_ARG_POINTER(_retval);
00572   *_retval = 0;
00573 
00574   GetWindowTreeOwner(aParent, getter_AddRefs(parentTreeOwner));
00575 
00576   if (aUrl) {
00577     rv = URIfromURL(aUrl, aParent, getter_AddRefs(uriToLoad));
00578     if (NS_FAILED(rv))
00579       return rv;
00580     uriToLoad->SchemeIs("chrome", &uriToLoadIsChrome);
00581   }
00582 
00583   nameSpecified = PR_FALSE;
00584   if (aName) {
00585     CopyUTF8toUTF16(aName, name);
00586 #ifdef DEBUG
00587     CheckWindowName(name);
00588 #endif
00589     nameSpecified = PR_TRUE;
00590   }
00591 
00592   featuresSpecified = PR_FALSE;
00593   if (aFeatures) {
00594     features.Assign(aFeatures);
00595     featuresSpecified = PR_TRUE;
00596     features.StripWhitespace();
00597   }
00598 
00599   // try to find an extant window with the given name
00600   nsCOMPtr<nsIDOMWindow> foundWindow;
00601   SafeGetWindowByName(name, aParent, getter_AddRefs(foundWindow));
00602   GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
00603 
00604   // no extant window? make a new one.
00605 
00606   nsCOMPtr<nsIDOMChromeWindow> chromeParent(do_QueryInterface(aParent));
00607 
00608   // Make sure we call CalculateChromeFlags() *before* we push the
00609   // callee context onto the context stack so that
00610   // CalculateChromeFlags() sees the actual caller when doing it's
00611   // security checks.
00612   chromeFlags = CalculateChromeFlags(features.get(), featuresSpecified,
00613                                      aDialog, uriToLoadIsChrome,
00614                                      !aParent || chromeParent);
00615 
00616   SizeSpec sizeSpec;
00617   CalcSizeSpec(features.get(), sizeSpec);
00618 
00619   PRBool isCallerChrome = PR_FALSE;
00620   nsCOMPtr<nsIScriptSecurityManager>
00621     sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
00622   if (sm)
00623     sm->SubjectPrincipalIsSystem(&isCallerChrome);
00624 
00625   JSContext *cx = GetJSContextFromWindow(aParent);
00626 
00627   if (isCallerChrome && !chromeParent && cx) {
00628     // open() is called from chrome on a non-chrome window, push
00629     // the context of the callee onto the context stack to
00630     // prevent the caller's priveleges from leaking into code
00631     // that runs while opening the new window.
00632 
00633     callerContextGuard.Push(cx);
00634   }
00635 
00636   if (!newDocShellItem) {
00637     // We're going to either open up a new window ourselves or ask a
00638     // nsIWindowProvider for one.  In either case, we'll want to set the right
00639     // name on it.
00640     windowNeedsName = PR_TRUE;
00641 
00642     // Now check whether it's ok to ask a window provider for a window.  Don't
00643     // do it if we're opening a dialog or if our parent is a chrome window or
00644     // if we're opening something that has modal, dialog, or chrome flags set.
00645     nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(aParent);
00646     if (!aDialog && !chromeWin &&
00647         !(chromeFlags & (nsIWebBrowserChrome::CHROME_MODAL         |
00648                          nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | 
00649                          nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
00650       nsCOMPtr<nsIWindowProvider> provider = do_GetInterface(parentTreeOwner);
00651       if (provider) {
00652         NS_ASSERTION(aParent, "We've _got_ to have a parent here!");
00653 
00654         nsCOMPtr<nsIDOMWindow> newWindow;
00655         rv = provider->ProvideWindow(aParent, chromeFlags,
00656                                      sizeSpec.PositionSpecified(),
00657                                      sizeSpec.SizeSpecified(),
00658                                      uriToLoad, name, features, &windowIsNew,
00659                                      getter_AddRefs(newWindow));
00660         if (NS_SUCCEEDED(rv)) {
00661           GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
00662           if (windowIsNew && newDocShellItem) {
00663             // Make sure to stop any loads happening in this window that the
00664             // window provider might have started.  Otherwise if our caller
00665             // manipulates the window it just opened and then the load
00666             // completes their stuff will get blown away.
00667             nsCOMPtr<nsIWebNavigation> webNav =
00668               do_QueryInterface(newDocShellItem);
00669             webNav->Stop(nsIWebNavigation::STOP_NETWORK);
00670           }
00671         }
00672       }
00673     }
00674   }
00675   
00676   if (!newDocShellItem) {
00677     windowIsNew = PR_TRUE;
00678     isNewToplevelWindow = PR_TRUE;
00679 
00680     nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
00681 
00682     // is the parent (if any) modal? if so, we must be, too.
00683     PRBool weAreModal = (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) != 0;
00684     if (!weAreModal && parentChrome)
00685       parentChrome->IsWindowModal(&weAreModal);
00686 
00687     if (weAreModal) {
00688       rv = queueGuard.Push();
00689       if (NS_SUCCEEDED(rv)) {
00690         windowIsModal = PR_TRUE;
00691         // in case we added this because weAreModal
00692         chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL |
00693           nsIWebBrowserChrome::CHROME_DEPENDENT;
00694       }
00695     }
00696 
00697     NS_ASSERTION(mWindowCreator,
00698                  "attempted to open a new window with no WindowCreator");
00699     rv = NS_ERROR_FAILURE;
00700     if (mWindowCreator) {
00701       nsCOMPtr<nsIWebBrowserChrome> newChrome;
00702 
00703       /* If the window creator is an nsIWindowCreator2, we can give it
00704          some hints. The only hint at this time is whether the opening window
00705          is in a situation that's likely to mean this is an unrequested
00706          popup window we're creating. However we're not completely honest:
00707          we clear that indicator if the opener is chrome, so that the
00708          downstream consumer can treat the indicator to mean simply
00709          that the new window is subject to popup control. */
00710       nsCOMPtr<nsIWindowCreator2> windowCreator2(do_QueryInterface(mWindowCreator));
00711       if (windowCreator2) {
00712         PRUint32 contextFlags = 0;
00713         PRBool popupConditions = PR_FALSE;
00714 
00715         // is the parent under popup conditions?
00716         nsCOMPtr<nsPIDOMWindow> piWindow(do_QueryInterface(aParent));
00717         if (piWindow)
00718           popupConditions = piWindow->IsLoadingOrRunningTimeout();
00719 
00720         // chrome is always allowed, so clear the flag if the opener is chrome
00721         if (popupConditions) {
00722           PRBool isChrome = PR_FALSE;
00723           if (sm)
00724             sm->SubjectPrincipalIsSystem(&isChrome);
00725           popupConditions = !isChrome;
00726         }
00727 
00728         if (popupConditions)
00729           contextFlags |= nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT;
00730 
00731         PRBool cancel = PR_FALSE;
00732         rv = windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags,
00733                                contextFlags, uriToLoad, &cancel,
00734                                getter_AddRefs(newChrome));
00735         if (NS_SUCCEEDED(rv) && cancel) {
00736           newChrome = 0; // just in case
00737           rv = NS_ERROR_ABORT;
00738         }
00739       }
00740       else
00741         rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags,
00742                                getter_AddRefs(newChrome));
00743       if (newChrome) {
00744         /* It might be a chrome nsXULWindow, in which case it won't have
00745             an nsIDOMWindow (primary content shell). But in that case, it'll
00746             be able to hand over an nsIDocShellTreeItem directly. */
00747         nsCOMPtr<nsIDOMWindow> newWindow(do_GetInterface(newChrome));
00748         if (newWindow)
00749           GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
00750         if (!newDocShellItem)
00751           newDocShellItem = do_GetInterface(newChrome);
00752         if (!newDocShellItem)
00753           rv = NS_ERROR_FAILURE;
00754       }
00755     }
00756   }
00757 
00758   // better have a window to use by this point
00759   if (!newDocShellItem)
00760     return rv;
00761 
00762   nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem));
00763   NS_ENSURE_TRUE(newDocShell, NS_ERROR_UNEXPECTED);
00764   
00765   rv = ReadyOpenedDocShellItem(newDocShellItem, aParent, windowIsNew, _retval);
00766   if (NS_FAILED(rv))
00767     return rv;
00768 
00769   /* disable persistence of size/position in popups (determined by
00770      determining whether the features parameter specifies width or height
00771      in any way). We consider any overriding of the window's size or position
00772      in the open call as disabling persistence of those attributes.
00773      Popup windows (which should not persist size or position) generally set
00774      the size. */
00775   if (isNewToplevelWindow) {
00776     /* at the moment, the strings "height=" or "width=" never happen
00777        outside a size specification, so we can do this the Q&D way. */
00778 
00779     if (PL_strcasestr(features.get(), "width=") || PL_strcasestr(features.get(), "height=")) {
00780 
00781       nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
00782       newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
00783       if (newTreeOwner)
00784         newTreeOwner->SetPersistence(PR_FALSE, PR_FALSE, PR_FALSE);
00785     }
00786   }
00787 
00788   if (aDialog && argc > 0) {
00789     rv = AttachArguments(*_retval, argc, argv);
00790 
00791     if (NS_FAILED(rv)) {
00792       return rv;
00793     }
00794   }
00795 
00796   /* allow a window that we found by name to keep its name (important for cases
00797      like _self where the given name is different (and invalid)).  Also, _blank
00798      and _new are not window names. */
00799   if (windowNeedsName)
00800     newDocShellItem->SetName(nameSpecified &&
00801                              !name.LowerCaseEqualsLiteral("_blank") &&
00802                              !name.LowerCaseEqualsLiteral("_new") ?
00803                              name.get() : nsnull);
00804 
00805 
00806   // Inherit the right character set into the new window to use as a fallback
00807   // in the event the document being loaded does not specify a charset.  When
00808   // aCalledFromJS is true, we want to use the character set of the document in
00809   // the caller; otherwise we want to use the character set of aParent's
00810   // docshell. Failing to set this charset is not fatal, so we want to continue
00811   // in the face of errors.
00812   nsCOMPtr<nsIContentViewer> newCV;
00813   newDocShell->GetContentViewer(getter_AddRefs(newCV));
00814   nsCOMPtr<nsIMarkupDocumentViewer> newMuCV = do_QueryInterface(newCV);
00815   if (newMuCV) {
00816     nsCOMPtr<nsIDocShellTreeItem> parentItem;
00817     GetWindowTreeItem(aParent, getter_AddRefs(parentItem));
00818 
00819     if (aCalledFromJS) {
00820       nsCOMPtr<nsIDocShellTreeItem> callerItem = GetCallerTreeItem(parentItem);
00821       nsCOMPtr<nsPIDOMWindow> callerWin = do_GetInterface(callerItem);
00822       if (callerWin) {
00823         nsCOMPtr<nsIDocument> doc =
00824           do_QueryInterface(callerWin->GetExtantDocument());
00825         if (doc) {
00826           newMuCV->SetDefaultCharacterSet(doc->GetDocumentCharacterSet());
00827         }
00828       }
00829     }
00830     else {
00831       nsCOMPtr<nsIDocShell> parentDocshell = do_QueryInterface(parentItem);
00832       // parentDocshell may be null if the parent got closed in the meantime
00833       if (parentDocshell) {
00834         nsCOMPtr<nsIContentViewer> parentCV;
00835         parentDocshell->GetContentViewer(getter_AddRefs(parentCV));
00836         nsCOMPtr<nsIMarkupDocumentViewer> parentMuCV =
00837           do_QueryInterface(parentCV);
00838         if (parentMuCV) {
00839           nsCAutoString charset;
00840           nsresult res = parentMuCV->GetDefaultCharacterSet(charset);
00841           if (NS_SUCCEEDED(res)) {
00842             newMuCV->SetDefaultCharacterSet(charset);
00843           }
00844           res = parentMuCV->GetPrevDocCharacterSet(charset);
00845           if (NS_SUCCEEDED(res)) {
00846             newMuCV->SetPrevDocCharacterSet(charset);
00847           }
00848         }
00849       }
00850     }
00851   }
00852 
00853   if (isNewToplevelWindow) {
00854     // Notify observers that the window is open and ready.
00855     // The window has not yet started to load a document.
00856     nsCOMPtr<nsIObserverService> obsSvc =
00857       do_GetService("@mozilla.org/observer-service;1");
00858     if (obsSvc) {
00859       obsSvc->NotifyObservers(*_retval, "toplevel-window-ready", nsnull);
00860     }
00861   }
00862 
00863   // Now we have to set the right opener principal on the new window.  Note
00864   // that we have to do this _before_ starting any URI loads, thanks to the
00865   // sync nature of javascript: loads.  Since this is the only place where we
00866   // set said opener principal, we need to do it for all URIs, including
00867   // chrome ones.  So to deal with the mess that is bug 79775, just press on in
00868   // a reasonable way even if GetSubjectPrincipal fails.  In that case, just
00869   // use a null subjectPrincipal.
00870   nsCOMPtr<nsIPrincipal> subjectPrincipal;
00871   if (NS_FAILED(sm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)))) {
00872     subjectPrincipal = nsnull;
00873   }
00874 
00875   if (windowIsNew) {
00876     // Now set the opener principal on the new window.  Note that we need to do
00877     // this no matter whether we were opened from JS; if there is nothing on
00878     // the JS stack, just use the principal of our parent window.  In those
00879     // cases we do _not_ set the parent window principal as the owner of the
00880     // load--since we really don't know who the owner is, just leave it null.
00881     nsIPrincipal* newWindowPrincipal = subjectPrincipal;
00882     if (!newWindowPrincipal && aParent) {
00883       nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(aParent));
00884       if (sop) {
00885         newWindowPrincipal = sop->GetPrincipal();
00886       }
00887     }
00888 
00889     nsCOMPtr<nsIPrincipal> systemPrincipal;
00890     sm->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
00891     if (newWindowPrincipal == systemPrincipal) {
00892       // Don't pass this principal along to content windows
00893       PRInt32 itemType;
00894       rv = newDocShellItem->GetItemType(&itemType);
00895       if (NS_FAILED(rv) || itemType != nsIDocShellTreeItem::typeChrome) {
00896         newWindowPrincipal = nsnull;        
00897       }
00898     }
00899 
00900     nsCOMPtr<nsPIDOMWindow_MOZILLA_1_8_BRANCH2> newWindow =
00901       do_QueryInterface(*_retval);
00902 #ifdef DEBUG
00903     nsCOMPtr<nsPIDOMWindow> newDebugWindow = do_GetInterface(newDocShell);
00904     NS_ASSERTION(newWindow == newDebugWindow, "Different windows??");
00905 #endif
00906     if (newWindow) {
00907       newWindow->SetOpenerScriptPrincipal(newWindowPrincipal);
00908     }
00909   }
00910 
00911   if (uriToLoad) { // get the script principal and pass it to docshell
00912     JSContextAutoPopper contextGuard;
00913 
00914     cx = GetJSContextFromCallStack();
00915 
00916     // get the security manager
00917     if (!cx)
00918       cx = GetJSContextFromWindow(aParent);
00919     if (!cx) {
00920       rv = contextGuard.Push();
00921       if (NS_FAILED(rv))
00922         return rv;
00923       cx = contextGuard.get();
00924     }
00925 
00926     nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
00927     newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
00928     NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
00929 
00930     if (subjectPrincipal) {
00931       loadInfo->SetOwner(subjectPrincipal);
00932     }
00933 
00934     // Set the new window's referrer from the calling context's document:
00935 
00936     // get the calling context off the JS context stack
00937     nsCOMPtr<nsIJSContextStack> stack = do_GetService(sJSStackContractID);
00938 
00939     JSContext* ccx = nsnull;
00940 
00941     // get its document, if any
00942     if (stack && NS_SUCCEEDED(stack->Peek(&ccx)) && ccx) {
00943       nsIScriptGlobalObject *sgo = nsWWJSUtils::GetDynamicScriptGlobal(ccx);
00944 
00945       nsCOMPtr<nsPIDOMWindow> w(do_QueryInterface(sgo));
00946       if (w) {
00947         /* use the URL from the *extant* document, if any. The usual accessor
00948            GetDocument will synchronously create an about:blank document if
00949            it has no better answer, and we only care about a real document.
00950            Also using GetDocument to force document creation seems to
00951            screw up focus in the hidden window; see bug 36016.
00952         */
00953         nsCOMPtr<nsIDocument> doc(do_QueryInterface(w->GetExtantDocument()));
00954         if (doc) { 
00955           // Set the referrer
00956           loadInfo->SetReferrer(doc->GetDocumentURI());
00957         }
00958       }
00959     }
00960 
00961     newDocShell->LoadURI(uriToLoad, loadInfo,
00962       windowIsNew ? nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD :
00963                     nsIWebNavigation::LOAD_FLAGS_NONE, PR_TRUE);
00964   }
00965 
00966   if (isNewToplevelWindow)
00967     SizeOpenedDocShellItem(newDocShellItem, aParent, sizeSpec);
00968 
00969   if (windowIsModal) {
00970     nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
00971     newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
00972     nsCOMPtr<nsIWebBrowserChrome> newChrome(do_GetInterface(newTreeOwner));
00973     if (newChrome)
00974       newChrome->ShowAsModal();
00975     NS_ASSERTION(newChrome, "show modal window failed: no available chrome");
00976   }
00977 
00978   return NS_OK;
00979 }
00980 
00981 NS_IMETHODIMP
00982 nsWindowWatcher::RegisterNotification(nsIObserver *aObserver)
00983 {
00984   // just a convenience method; it delegates to nsIObserverService
00985   nsresult rv;
00986 
00987   if (!aObserver)
00988     return NS_ERROR_INVALID_ARG;
00989   
00990   nsCOMPtr<nsIObserverService> os(do_GetService("@mozilla.org/observer-service;1", &rv));
00991   if (os) {
00992     rv = os->AddObserver(aObserver, "domwindowopened", PR_FALSE);
00993     if (NS_SUCCEEDED(rv))
00994       rv = os->AddObserver(aObserver, "domwindowclosed", PR_FALSE);
00995   }
00996   return rv;
00997 }
00998 
00999 NS_IMETHODIMP
01000 nsWindowWatcher::UnregisterNotification(nsIObserver *aObserver)
01001 {
01002   // just a convenience method; it delegates to nsIObserverService
01003   nsresult rv;
01004 
01005   if (!aObserver)
01006     return NS_ERROR_INVALID_ARG;
01007   
01008   nsCOMPtr<nsIObserverService> os(do_GetService("@mozilla.org/observer-service;1", &rv));
01009   if (os) {
01010     os->RemoveObserver(aObserver, "domwindowopened");
01011     os->RemoveObserver(aObserver, "domwindowclosed");
01012   }
01013   return rv;
01014 }
01015 
01016 NS_IMETHODIMP
01017 nsWindowWatcher::GetWindowEnumerator(nsISimpleEnumerator** _retval)
01018 {
01019   if (!_retval)
01020     return NS_ERROR_INVALID_ARG;
01021 
01022   nsAutoLock lock(mListLock);
01023   nsWatcherWindowEnumerator *enumerator = new nsWatcherWindowEnumerator(this);
01024   if (enumerator)
01025     return CallQueryInterface(enumerator, _retval);
01026 
01027   return NS_ERROR_OUT_OF_MEMORY;
01028 }
01029     
01030 NS_IMETHODIMP
01031 nsWindowWatcher::GetNewPrompter(nsIDOMWindow *aParent, nsIPrompt **_retval)
01032 {
01033   return NS_NewPrompter(_retval, aParent);
01034 }
01035 
01036 NS_IMETHODIMP
01037 nsWindowWatcher::GetNewAuthPrompter(nsIDOMWindow *aParent, nsIAuthPrompt **_retval)
01038 {
01039   return NS_NewAuthPrompter(_retval, aParent);
01040 }
01041 
01042 NS_IMETHODIMP
01043 nsWindowWatcher::SetWindowCreator(nsIWindowCreator *creator)
01044 {
01045   mWindowCreator = creator; // it's an nsCOMPtr, so this is an ownership ref
01046   return NS_OK;
01047 }
01048 
01049 NS_IMETHODIMP
01050 nsWindowWatcher::GetActiveWindow(nsIDOMWindow **aActiveWindow)
01051 {
01052   if (!aActiveWindow)
01053     return NS_ERROR_INVALID_ARG;
01054 
01055   *aActiveWindow = mActiveWindow;
01056   NS_IF_ADDREF(mActiveWindow);
01057   return NS_OK;
01058 }
01059 
01060 NS_IMETHODIMP
01061 nsWindowWatcher::SetActiveWindow(nsIDOMWindow *aActiveWindow)
01062 {
01063 #ifdef DEBUG
01064   {
01065     nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aActiveWindow));
01066 
01067     NS_ASSERTION(!win || win->IsOuterWindow(),
01068                  "Uh, the active window must be an outer window!");
01069   }
01070 #endif
01071 
01072   if (FindWindowEntry(aActiveWindow)) {
01073     mActiveWindow = aActiveWindow;
01074     return NS_OK;
01075   }
01076   NS_ERROR("invalid active window");
01077   return NS_ERROR_FAILURE;
01078 }
01079 
01080 NS_IMETHODIMP
01081 nsWindowWatcher::AddWindow(nsIDOMWindow *aWindow, nsIWebBrowserChrome *aChrome)
01082 {
01083   nsresult rv;
01084 
01085   if (!aWindow)
01086     return NS_ERROR_INVALID_ARG;
01087 
01088 #ifdef DEBUG
01089   {
01090     nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aWindow));
01091 
01092     NS_ASSERTION(win->IsOuterWindow(),
01093                  "Uh, the active window must be an outer window!");
01094   }
01095 #endif
01096 
01097   {
01098     nsWatcherWindowEntry *info;
01099     nsAutoLock lock(mListLock);
01100 
01101     // if we already have an entry for this window, adjust
01102     // its chrome mapping and return
01103     info = FindWindowEntry(aWindow);
01104     if (info) {
01105       nsCOMPtr<nsISupportsWeakReference> supportsweak(do_QueryInterface(aChrome));
01106       if (supportsweak) {
01107         supportsweak->GetWeakReference(getter_AddRefs(info->mChromeWeak));
01108       } else {
01109         info->mChrome = aChrome;
01110         info->mChromeWeak = 0;
01111       }
01112       return NS_OK;
01113     }
01114   
01115     // create a window info struct and add it to the list of windows
01116     info = new nsWatcherWindowEntry(aWindow, aChrome);
01117     if (!info)
01118       return NS_ERROR_OUT_OF_MEMORY;
01119 
01120     if (mOldestWindow)
01121       info->InsertAfter(mOldestWindow->mOlder);
01122     else
01123       mOldestWindow = info;
01124   } // leave the mListLock
01125 
01126   // a window being added to us signifies a newly opened window.
01127   // send notifications.
01128   nsCOMPtr<nsIObserverService> os(do_GetService("@mozilla.org/observer-service;1", &rv));
01129   if (os) {
01130     nsCOMPtr<nsISupports> domwin(do_QueryInterface(aWindow));
01131     rv = os->NotifyObservers(domwin, "domwindowopened", 0);
01132   }
01133 
01134   return rv;
01135 }
01136 
01137 NS_IMETHODIMP
01138 nsWindowWatcher::RemoveWindow(nsIDOMWindow *aWindow)
01139 {
01140   // find the corresponding nsWatcherWindowEntry, remove it
01141 
01142   if (!aWindow)
01143     return NS_ERROR_INVALID_ARG;
01144 
01145   nsWatcherWindowEntry *info = FindWindowEntry(aWindow);
01146   if (info) {
01147     RemoveWindow(info);
01148     return NS_OK;
01149   }
01150   NS_WARNING("requested removal of nonexistent window\n");
01151   return NS_ERROR_INVALID_ARG;
01152 }
01153 
01154 nsWatcherWindowEntry *
01155 nsWindowWatcher::FindWindowEntry(nsIDOMWindow *aWindow)
01156 {
01157   // find the corresponding nsWatcherWindowEntry
01158   nsWatcherWindowEntry *info,
01159                        *listEnd;
01160 #ifdef USEWEAKREFS
01161   nsresult    rv;
01162   PRBool      found;
01163 #endif
01164 
01165   info = mOldestWindow;
01166   listEnd = 0;
01167 #ifdef USEWEAKREFS
01168   rv = NS_OK;
01169   found = PR_FALSE;
01170   while (info != listEnd && NS_SUCCEEDED(rv)) {
01171     nsCOMPtr<nsIDOMWindow> infoWindow(do_QueryReferent(info->mWindow));
01172     if (!infoWindow) { // clean up dangling reference, while we're here
01173       rv = RemoveWindow(info);
01174     }
01175     else if (infoWindow.get() == aWindow)
01176       return info;
01177 
01178     info = info->mYounger;
01179     listEnd = mOldestWindow;
01180   }
01181   return 0;
01182 #else
01183   while (info != listEnd) {
01184     if (info->mWindow == aWindow)
01185       return info;
01186     info = info->mYounger;
01187     listEnd = mOldestWindow;
01188   }
01189   return 0;
01190 #endif
01191 }
01192 
01193 nsresult nsWindowWatcher::RemoveWindow(nsWatcherWindowEntry *inInfo)
01194 {
01195   PRInt32  ctr,
01196            count = mEnumeratorList.Count();
01197   nsresult rv;
01198 
01199   {
01200     // notify the enumerators
01201     nsAutoLock lock(mListLock);
01202     for (ctr = 0; ctr < count; ++ctr) 
01203       ((nsWatcherWindowEnumerator*)mEnumeratorList[ctr])->WindowRemoved(inInfo);
01204 
01205     // remove the element from the list
01206     if (inInfo == mOldestWindow)
01207       mOldestWindow = inInfo->mYounger == mOldestWindow ? 0 : inInfo->mYounger;
01208     inInfo->Unlink();
01209 
01210     // clear the active window, if they're the same
01211     if (mActiveWindow == inInfo->mWindow)
01212       mActiveWindow = 0;
01213   }
01214 
01215   // a window being removed from us signifies a newly closed window.
01216   // send notifications.
01217   nsCOMPtr<nsIObserverService> os(do_GetService("@mozilla.org/observer-service;1", &rv));
01218   if (os) {
01219 #ifdef USEWEAKREFS
01220     nsCOMPtr<nsISupports> domwin(do_QueryReferent(inInfo->mWindow));
01221     if (domwin)
01222       rv = os->NotifyObservers(domwin, "domwindowclosed", 0);
01223     // else bummer. since the window is gone, there's nothing to notify with.
01224 #else
01225     nsCOMPtr<nsISupports> domwin(do_QueryInterface(inInfo->mWindow));
01226     rv = os->NotifyObservers(domwin, "domwindowclosed", 0);
01227 #endif
01228   }
01229 
01230   delete inInfo;
01231   return NS_OK;
01232 }
01233 
01234 NS_IMETHODIMP
01235 nsWindowWatcher::GetChromeForWindow(nsIDOMWindow *aWindow, nsIWebBrowserChrome **_retval)
01236 {
01237   if (!aWindow || !_retval)
01238     return NS_ERROR_INVALID_ARG;
01239   *_retval = 0;
01240 
01241   nsAutoLock lock(mListLock);
01242   nsWatcherWindowEntry *info = FindWindowEntry(aWindow);
01243   if (info) {
01244     if (info->mChromeWeak != nsnull) {
01245       return info->mChromeWeak->
01246                             QueryReferent(NS_GET_IID(nsIWebBrowserChrome),
01247                                           NS_REINTERPRET_CAST(void**, _retval));
01248     }
01249     *_retval = info->mChrome;
01250     NS_IF_ADDREF(*_retval);
01251   }
01252   return NS_OK;
01253 }
01254 
01255 NS_IMETHODIMP
01256 nsWindowWatcher::GetWindowByName(const PRUnichar *aTargetName, 
01257                                  nsIDOMWindow *aCurrentWindow,
01258                                  nsIDOMWindow **aResult)
01259 {
01260   if (!aResult) {
01261     return NS_ERROR_INVALID_ARG;
01262   }
01263 
01264   *aResult = nsnull;
01265 
01266   nsCOMPtr<nsIDocShellTreeItem> treeItem;
01267 
01268   nsCOMPtr<nsIDocShellTreeItem> startItem;
01269   GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
01270   if (startItem) {
01271     // Note: original requestor is null here, per idl comments
01272     startItem->FindItemWithName(aTargetName, nsnull, nsnull,
01273                                 getter_AddRefs(treeItem));
01274   }
01275   else {
01276     // Note: original requestor is null here, per idl comments
01277     FindItemWithName(aTargetName, nsnull, nsnull, getter_AddRefs(treeItem));
01278   }
01279 
01280   nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(treeItem);
01281   domWindow.swap(*aResult);
01282 
01283   return NS_OK;
01284 }
01285 
01286 PRBool
01287 nsWindowWatcher::AddEnumerator(nsWatcherWindowEnumerator* inEnumerator)
01288 {
01289   // (requires a lock; assumes it's called by someone holding the lock)
01290   return mEnumeratorList.AppendElement(inEnumerator);
01291 }
01292 
01293 PRBool
01294 nsWindowWatcher::RemoveEnumerator(nsWatcherWindowEnumerator* inEnumerator)
01295 {
01296   // (requires a lock; assumes it's called by someone holding the lock)
01297   return mEnumeratorList.RemoveElement(inEnumerator);
01298 }
01299 
01300 nsresult
01301 nsWindowWatcher::URIfromURL(const char *aURL,
01302                             nsIDOMWindow *aParent,
01303                             nsIURI **aURI)
01304 {
01305   nsCOMPtr<nsIDOMWindow> baseWindow;
01306 
01307   /* build the URI relative to the calling JS Context, if any.
01308      (note this is the same context used to make the security check
01309      in nsGlobalWindow.cpp.) */
01310   JSContext *cx = GetJSContextFromCallStack();
01311   if (cx) {
01312     nsIScriptContext *scriptcx = nsWWJSUtils::GetDynamicScriptContext(cx);
01313     if (scriptcx) {
01314       baseWindow = do_QueryInterface(scriptcx->GetGlobalObject());
01315     }
01316   }
01317 
01318   // failing that, build it relative to the parent window, if possible
01319   if (!baseWindow)
01320     baseWindow = aParent;
01321 
01322   // failing that, use the given URL unmodified. It had better not be relative.
01323 
01324   nsIURI *baseURI = nsnull;
01325 
01326   // get baseWindow's document URI
01327   if (baseWindow) {
01328     nsCOMPtr<nsIDOMDocument> domDoc;
01329     baseWindow->GetDocument(getter_AddRefs(domDoc));
01330     if (domDoc) {
01331       nsCOMPtr<nsIDocument> doc;
01332       doc = do_QueryInterface(domDoc);
01333       if (doc) {
01334         baseURI = doc->GetBaseURI();
01335       }
01336     }
01337   }
01338 
01339   // build and return the absolute URI
01340   return NS_NewURI(aURI, aURL, baseURI);
01341 }
01342 
01343 #ifdef DEBUG
01344 /* Check for an illegal name e.g. frame3.1
01345    This just prints a warning message an continues; we open the window anyway,
01346    (see bug 32898). */
01347 void nsWindowWatcher::CheckWindowName(nsString& aName)
01348 {
01349   nsReadingIterator<PRUnichar> scan;
01350   nsReadingIterator<PRUnichar> endScan;
01351 
01352   aName.EndReading(endScan);
01353   for (aName.BeginReading(scan); scan != endScan; ++scan)
01354     if (!nsCRT::IsAsciiAlpha(*scan) && !nsCRT::IsAsciiDigit(*scan) &&
01355         *scan != '_') {
01356 
01357       // Don't use js_ReportError as this will cause the application
01358       // to shut down (JS_ASSERT calls abort())  See bug 32898
01359       nsCAutoString warn;
01360       warn.AssignLiteral("Illegal character in window name ");
01361       AppendUTF16toUTF8(aName, warn);
01362       NS_WARNING(warn.get());
01363       break;
01364     }
01365 }
01366 #endif // DEBUG
01367 
01368 #define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag)               \
01369     prefBranch->GetBoolPref(feature, &forceEnable);               \
01370     if (forceEnable && !(isChrome && aHasChromeParent)) {         \
01371       chromeFlags |= flag;                                        \
01372     } else {                                                      \
01373       chromeFlags |= WinHasOption(aFeatures, feature,             \
01374                                   0, &presenceFlag)               \
01375                      ? flag : 0;                                  \
01376     }
01377 
01386 PRUint32 nsWindowWatcher::CalculateChromeFlags(const char *aFeatures,
01387                                                PRBool aFeaturesSpecified,
01388                                                PRBool aDialog,
01389                                                PRBool aChromeURL,
01390                                                PRBool aHasChromeParent)
01391 {
01392    if(!aFeaturesSpecified || !aFeatures) {
01393       if(aDialog)
01394          return   nsIWebBrowserChrome::CHROME_ALL | 
01395                   nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | 
01396                   nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
01397       else
01398          return nsIWebBrowserChrome::CHROME_ALL;
01399    }
01400 
01401   /* This function has become complicated since browser windows and
01402      dialogs diverged. The difference is, browser windows assume all
01403      chrome not explicitly mentioned is off, if the features string
01404      is not null. Exceptions are some OS border chrome new with Mozilla.
01405      Dialogs interpret a (mostly) empty features string to mean
01406      "OS's choice," and also support an "all" flag explicitly disallowed
01407      in the standards-compliant window.(normal)open. */
01408 
01409   PRUint32 chromeFlags = 0;
01410   PRBool presenceFlag = PR_FALSE;
01411 
01412   chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS;
01413   if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag))
01414     chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
01415 
01416   /* Next, allow explicitly named options to override the initial settings */
01417 
01418   nsCOMPtr<nsIScriptSecurityManager>
01419     securityManager(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
01420   NS_ENSURE_TRUE(securityManager, NS_ERROR_FAILURE);
01421 
01422   PRBool isChrome = PR_FALSE;
01423   securityManager->SubjectPrincipalIsSystem(&isChrome);
01424 
01425   nsCOMPtr<nsIPrefBranch> prefBranch;
01426   nsresult rv;
01427   nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
01428   NS_ENSURE_SUCCESS(rv, PR_TRUE);
01429 
01430   rv = prefs->GetBranch("dom.disable_window_open_feature.", getter_AddRefs(prefBranch));
01431   NS_ENSURE_SUCCESS(rv, PR_TRUE);
01432 
01433   PRBool forceEnable = PR_FALSE;
01434 
01435   NS_CALCULATE_CHROME_FLAG_FOR("titlebar",
01436                                nsIWebBrowserChrome::CHROME_TITLEBAR);
01437   NS_CALCULATE_CHROME_FLAG_FOR("close",
01438                                nsIWebBrowserChrome::CHROME_WINDOW_CLOSE);
01439   NS_CALCULATE_CHROME_FLAG_FOR("toolbar",
01440                                nsIWebBrowserChrome::CHROME_TOOLBAR);
01441   NS_CALCULATE_CHROME_FLAG_FOR("location",
01442                                nsIWebBrowserChrome::CHROME_LOCATIONBAR);
01443   NS_CALCULATE_CHROME_FLAG_FOR("directories",
01444                                nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
01445   NS_CALCULATE_CHROME_FLAG_FOR("personalbar",
01446                                nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
01447   NS_CALCULATE_CHROME_FLAG_FOR("status",
01448                                nsIWebBrowserChrome::CHROME_STATUSBAR);
01449   NS_CALCULATE_CHROME_FLAG_FOR("menubar",
01450                                nsIWebBrowserChrome::CHROME_MENUBAR);
01451   NS_CALCULATE_CHROME_FLAG_FOR("scrollbars",
01452                                nsIWebBrowserChrome::CHROME_SCROLLBARS);
01453   NS_CALCULATE_CHROME_FLAG_FOR("resizable",
01454                                nsIWebBrowserChrome::CHROME_WINDOW_RESIZE);
01455   NS_CALCULATE_CHROME_FLAG_FOR("minimizable",
01456                                nsIWebBrowserChrome::CHROME_WINDOW_MIN);
01457 
01458   chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag)
01459                  ? nsIWebBrowserChrome::CHROME_WINDOW_POPUP : 0; 
01460 
01461   /* OK.
01462      Normal browser windows, in spite of a stated pattern of turning off
01463      all chrome not mentioned explicitly, will want the new OS chrome (window
01464      borders, titlebars, closebox) on, unless explicitly turned off.
01465      Dialogs, on the other hand, take the absence of any explicit settings
01466      to mean "OS' choice." */
01467 
01468   // default titlebar and closebox to "on," if not mentioned at all
01469   if (!PL_strcasestr(aFeatures, "titlebar"))
01470     chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
01471   if (!PL_strcasestr(aFeatures, "close"))
01472     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
01473 
01474   if (aDialog && !presenceFlag)
01475     chromeFlags = nsIWebBrowserChrome::CHROME_DEFAULT;
01476 
01477   /* Finally, once all the above normal chrome has been divined, deal
01478      with the features that are more operating hints than appearance
01479      instructions. (Note modality implies dependence.) */
01480 
01481   if (WinHasOption(aFeatures, "alwaysLowered", 0, nsnull) ||
01482       WinHasOption(aFeatures, "z-lock", 0, nsnull))
01483     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
01484   else if (WinHasOption(aFeatures, "alwaysRaised", 0, nsnull))
01485     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
01486 
01487   chromeFlags |= WinHasOption(aFeatures, "chrome", 0, nsnull) ?
01488     nsIWebBrowserChrome::CHROME_OPENAS_CHROME : 0;
01489   chromeFlags |= WinHasOption(aFeatures, "extrachrome", 0, nsnull) ?
01490     nsIWebBrowserChrome::CHROME_EXTRA : 0;
01491   chromeFlags |= WinHasOption(aFeatures, "centerscreen", 0, nsnull) ?
01492     nsIWebBrowserChrome::CHROME_CENTER_SCREEN : 0;
01493   chromeFlags |= WinHasOption(aFeatures, "dependent", 0, nsnull) ?
01494     nsIWebBrowserChrome::CHROME_DEPENDENT : 0;
01495   chromeFlags |= WinHasOption(aFeatures, "modal", 0, nsnull) ?
01496     (nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT) : 0;
01497   chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nsnull) ?
01498     nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0;
01499 
01500   /* and dialogs need to have the last word. assume dialogs are dialogs,
01501      and opened as chrome, unless explicitly told otherwise. */
01502   if (aDialog) {
01503     if (!PL_strcasestr(aFeatures, "dialog"))
01504       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
01505     if (!PL_strcasestr(aFeatures, "chrome"))
01506       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
01507   }
01508 
01509   /* missing
01510      chromeFlags->copy_history
01511    */
01512 
01513   // Check security state for use in determing window dimensions
01514   PRBool enabled;
01515   nsresult res =
01516     securityManager->IsCapabilityEnabled("UniversalBrowserWrite", &enabled);
01517 
01518   if (NS_FAILED(res) || !enabled || (isChrome && !aHasChromeParent)) {
01519     // If priv check fails (or if we're called from chrome, but the
01520     // parent is not a chrome window), set all elements to minimum
01521     // reqs., else leave them alone.
01522     chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
01523     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
01524     chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
01525     chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
01526     chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_POPUP;
01527     /* Untrusted script is allowed to pose modal windows with a chrome
01528        scheme. This check could stand to be better. But it effectively
01529        prevents untrusted script from opening modal windows in general
01530        while still allowing alerts and the like. */
01531     if (!aChromeURL)
01532       chromeFlags &= ~(nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
01533   }
01534 
01535   if (!(chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)) {
01536     // Remove the dependent flag if we're not opening as chrome
01537     chromeFlags &= ~nsIWebBrowserChrome::CHROME_DEPENDENT;
01538   }
01539 
01540   return chromeFlags;
01541 }
01542 
01543 PRInt32
01544 nsWindowWatcher::WinHasOption(const char *aOptions, const char *aName,
01545                               PRInt32 aDefault, PRBool *aPresenceFlag)
01546 {
01547   if (!aOptions)
01548     return 0;
01549 
01550   char *comma, *equal;
01551   PRInt32 found = 0;
01552 
01553 #ifdef DEBUG
01554     nsCAutoString options(aOptions);
01555     NS_ASSERTION(options.FindCharInSet(" \n\r\t") == kNotFound, 
01556                   "There should be no whitespace in this string!");
01557 #endif
01558 
01559   while (PR_TRUE) {
01560     comma = PL_strchr(aOptions, ',');
01561     if (comma)
01562       *comma = '\0';
01563     equal = PL_strchr(aOptions, '=');
01564     if (equal)
01565       *equal = '\0';
01566     if (nsCRT::strcasecmp(aOptions, aName) == 0) {
01567       if (aPresenceFlag)
01568         *aPresenceFlag = PR_TRUE;
01569       if (equal)
01570         if (*(equal + 1) == '*')
01571           found = aDefault;
01572         else if (nsCRT::strcasecmp(equal + 1, "yes") == 0)
01573           found = 1;
01574         else
01575           found = atoi(equal + 1);
01576       else
01577         found = 1;
01578     }
01579     if (equal)
01580       *equal = '=';
01581     if (comma)
01582       *comma = ',';
01583     if (found || !comma)
01584       break;
01585     aOptions = comma + 1;
01586   }
01587   return found;
01588 }
01589 
01590 /* try to find an nsIDocShellTreeItem with the given name in any
01591    known open window. a failure to find the item will not
01592    necessarily return a failure method value. check aFoundItem.
01593 */
01594 NS_IMETHODIMP
01595 nsWindowWatcher::FindItemWithName(const PRUnichar* aName,
01596                                   nsIDocShellTreeItem* aRequestor,
01597                                   nsIDocShellTreeItem* aOriginalRequestor,
01598                                   nsIDocShellTreeItem** aFoundItem)
01599 {
01600   *aFoundItem = 0;
01601 
01602   /* special cases */
01603   if(!aName || !*aName)
01604     return NS_OK;
01605 
01606   nsDependentString name(aName);
01607   
01608   nsCOMPtr<nsISimpleEnumerator> windows;
01609   GetWindowEnumerator(getter_AddRefs(windows));
01610   if (!windows)
01611     return NS_ERROR_FAILURE;
01612 
01613   PRBool   more;
01614   nsresult rv = NS_OK;
01615 
01616   do {
01617     windows->HasMoreElements(&more);
01618     if (!more)
01619       break;
01620     nsCOMPtr<nsISupports> nextSupWindow;
01621     windows->GetNext(getter_AddRefs(nextSupWindow));
01622     nsCOMPtr<nsIDOMWindow> nextWindow(do_QueryInterface(nextSupWindow));
01623     if (nextWindow) {
01624       nsCOMPtr<nsIDocShellTreeItem> treeItem;
01625       GetWindowTreeItem(nextWindow, getter_AddRefs(treeItem));
01626       if (treeItem) {
01627         // Get the root tree item of same type, since roots are the only
01628         // things that call into the treeowner to look for named items.
01629         nsCOMPtr<nsIDocShellTreeItem> root;
01630         treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
01631         NS_ASSERTION(root, "Must have root tree item of same type");
01632         // Make sure not to call back into aRequestor
01633         if (root != aRequestor) {
01634           // Get the tree owner so we can pass it in as the requestor so
01635           // the child knows not to call back up, since we're walking
01636           // all windows already.
01637           nsCOMPtr<nsIDocShellTreeOwner> rootOwner;
01638           // Note: if we have no aRequestor, then we want to also look for
01639           // "special" window names, so pass a null requestor.  This will mean
01640           // that the treeitem calls back up to us, effectively (with a
01641           // non-null aRequestor), so break the loop immediately after the
01642           // call in that case.
01643           if (aRequestor) {
01644             root->GetTreeOwner(getter_AddRefs(rootOwner));
01645           }
01646           rv = root->FindItemWithName(aName, rootOwner, aOriginalRequestor,
01647                                       aFoundItem);
01648           if (NS_FAILED(rv) || *aFoundItem || !aRequestor)
01649             break;
01650         }
01651       }
01652     }
01653   } while(1);
01654 
01655   return rv;
01656 }
01657 
01658 already_AddRefed<nsIDocShellTreeItem>
01659 nsWindowWatcher::GetCallerTreeItem(nsIDocShellTreeItem* aParentItem)
01660 {
01661   nsCOMPtr<nsIJSContextStack> stack =
01662     do_GetService(sJSStackContractID);
01663 
01664   JSContext *cx = nsnull;
01665 
01666   if (stack) {
01667     stack->Peek(&cx);
01668   }
01669 
01670   nsIDocShellTreeItem* callerItem = nsnull;
01671 
01672   if (cx) {
01673     nsCOMPtr<nsIWebNavigation> callerWebNav =
01674       do_GetInterface(nsWWJSUtils::GetDynamicScriptGlobal(cx));
01675 
01676     if (callerWebNav) {
01677       CallQueryInterface(callerWebNav, &callerItem);
01678     }
01679   }
01680 
01681   if (!callerItem) {
01682     NS_IF_ADDREF(callerItem = aParentItem);
01683   }
01684 
01685   return callerItem;
01686 }
01687 
01688 nsresult
01689 nsWindowWatcher::SafeGetWindowByName(const nsAString& aName,
01690                                      nsIDOMWindow* aCurrentWindow,
01691                                      nsIDOMWindow** aResult)
01692 {
01693   *aResult = nsnull;
01694   
01695   nsCOMPtr<nsIDocShellTreeItem> startItem;
01696   GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
01697 
01698   nsCOMPtr<nsIDocShellTreeItem> callerItem = GetCallerTreeItem(startItem);
01699 
01700   const nsAFlatString& flatName = PromiseFlatString(aName);
01701 
01702   nsCOMPtr<nsIDocShellTreeItem> foundItem;
01703   if (startItem) {
01704     startItem->FindItemWithName(flatName.get(), nsnull, callerItem,
01705                                 getter_AddRefs(foundItem));
01706   }
01707   else {
01708     FindItemWithName(flatName.get(), nsnull, callerItem,
01709                      getter_AddRefs(foundItem));
01710   }
01711 
01712   nsCOMPtr<nsIDOMWindow> foundWin = do_GetInterface(foundItem);
01713   foundWin.swap(*aResult);
01714   return NS_OK;
01715 }
01716 
01717 /* Fetch the nsIDOMWindow corresponding to the given nsIDocShellTreeItem.
01718    This forces the creation of a script context, if one has not already
01719    been created. Note it also sets the window's opener to the parent,
01720    if applicable -- because it's just convenient, that's all. null aParent
01721    is acceptable. */
01722 nsresult
01723 nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem *aOpenedItem,
01724                                          nsIDOMWindow        *aParent,
01725                                          PRBool              aWindowIsNew,
01726                                          nsIDOMWindow        **aOpenedWindow)
01727 {
01728   nsresult rv = NS_ERROR_FAILURE;
01729 
01730   *aOpenedWindow = 0;
01731   nsCOMPtr<nsIScriptGlobalObject> globalObject(do_GetInterface(aOpenedItem));
01732   if (globalObject) {
01733     if (aParent) {
01734       nsCOMPtr<nsIDOMWindowInternal> internalParent(do_QueryInterface(aParent));
01735       nsCOMPtr<nsPIDOMWindow_MOZILLA_1_8_BRANCH> branchWindow =
01736         do_QueryInterface(globalObject);
01737       branchWindow->SetOpenerWindow(internalParent, aWindowIsNew); // damnit
01738 
01739       if (aWindowIsNew) {
01740         nsCOMPtr<nsIDocument_MOZILLA_1_8_BRANCH2> doc =
01741           do_QueryInterface(branchWindow->GetExtantDocument());
01742         if (doc) {
01743           doc->SetIsInitialDocument(PR_TRUE);
01744         }
01745       }
01746     }
01747     rv = CallQueryInterface(globalObject, aOpenedWindow);
01748   }
01749   return rv;
01750 }
01751 
01752 void
01753 nsWindowWatcher::CalcSizeSpec(const char* aFeatures, SizeSpec& aResult)
01754 {
01755   // Parse position spec, if any, from aFeatures
01756   PRBool  present;
01757   PRInt32 temp;
01758 
01759   present = PR_FALSE;
01760   if ((temp = WinHasOption(aFeatures, "left", 0, &present)) || present)
01761     aResult.mLeft = temp;
01762   else if ((temp = WinHasOption(aFeatures, "screenX", 0, &present)) || present)
01763     aResult.mLeft = temp;
01764   aResult.mLeftSpecified = present;
01765 
01766   present = PR_FALSE;
01767   if ((temp = WinHasOption(aFeatures, "top", 0, &present)) || present)
01768     aResult.mTop = temp;
01769   else if ((temp = WinHasOption(aFeatures, "screenY", 0, &present)) || present)
01770     aResult.mTop = temp;
01771   aResult.mTopSpecified = present;
01772 
01773   // Parse size spec, if any. Chrome size overrides content size.
01774   if ((temp = WinHasOption(aFeatures, "outerWidth", PR_INT32_MIN, nsnull))) {
01775     if (temp == PR_INT32_MIN) {
01776       aResult.mUseDefaultWidth = PR_TRUE;
01777     }
01778     else {
01779       aResult.mOuterWidth = temp;
01780     }
01781     aResult.mOuterWidthSpecified = PR_TRUE;
01782   } else if ((temp = WinHasOption(aFeatures, "width", PR_INT32_MIN, nsnull)) ||
01783              (temp = WinHasOption(aFeatures, "innerWidth", PR_INT32_MIN,
01784                                   nsnull))) {
01785     if (temp == PR_INT32_MIN) {
01786       aResult.mUseDefaultWidth = PR_TRUE;
01787     } else {
01788       aResult.mInnerWidth = temp;
01789     }
01790     aResult.mInnerWidthSpecified = PR_TRUE;
01791   }
01792 
01793   if ((temp = WinHasOption(aFeatures, "outerHeight", PR_INT32_MIN, nsnull))) {
01794     if (temp == PR_INT32_MIN) {
01795       aResult.mUseDefaultHeight = PR_TRUE;
01796     }
01797     else {
01798       aResult.mOuterHeight = temp;
01799     }
01800     aResult.mOuterHeightSpecified = PR_TRUE;
01801   } else if ((temp = WinHasOption(aFeatures, "height", PR_INT32_MIN,
01802                                   nsnull)) ||
01803              (temp = WinHasOption(aFeatures, "innerHeight", PR_INT32_MIN,
01804                                   nsnull))) {
01805     if (temp == PR_INT32_MIN) {
01806       aResult.mUseDefaultHeight = PR_TRUE;
01807     } else {
01808       aResult.mInnerHeight = temp;
01809     }
01810     aResult.mInnerHeightSpecified = PR_TRUE;
01811   }
01812 }
01813 
01814 /* Size and position the new window according to aSizeSpec. This method
01815    is assumed to be called after the window has already been given
01816    a default position and size; thus its current position and size are
01817    accurate defaults. The new window is made visible at method end.
01818 */
01819 void
01820 nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem *aDocShellItem,
01821                                         nsIDOMWindow *aParent,
01822                                         const SizeSpec & aSizeSpec)
01823 {
01824   // position and size of window
01825   PRInt32 left = 0,
01826           top = 0,
01827           width = 100,
01828           height = 100;
01829   // difference between chrome and content size
01830   PRInt32 chromeWidth = 0,
01831           chromeHeight = 0;
01832   // whether the window size spec refers to chrome or content
01833   PRBool  sizeChromeWidth = PR_TRUE,
01834           sizeChromeHeight = PR_TRUE;
01835 
01836   // get various interfaces for aDocShellItem, used throughout this method
01837   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
01838   aDocShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
01839   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(treeOwner));
01840   if (!treeOwnerAsWin) // we'll need this to actually size the docshell
01841     return;
01842 
01843   /* The current position and size will be unchanged if not specified
01844      (and they fit entirely onscreen). Also, calculate the difference
01845      between chrome and content sizes on aDocShellItem's window.
01846      This latter point becomes important if chrome and content
01847      specifications are mixed in aFeatures, and when bringing the window
01848      back from too far off the right or bottom edges of the screen. */
01849 
01850   treeOwnerAsWin->GetPositionAndSize(&left, &top, &width, &height);
01851   { // scope shellWindow why not
01852     nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(aDocShellItem));
01853     if (shellWindow) {
01854       PRInt32 cox, coy;
01855       shellWindow->GetSize(&cox, &coy);
01856       chromeWidth = width - cox;
01857       chromeHeight = height - coy;
01858     }
01859   }
01860 
01861   // Set up left/top
01862   if (aSizeSpec.mLeftSpecified) {
01863     left = aSizeSpec.mLeft;
01864   }
01865 
01866   if (aSizeSpec.mTopSpecified) {
01867     top = aSizeSpec.mTop;
01868   }
01869 
01870   // Set up width
01871   if (aSizeSpec.mOuterWidthSpecified) {
01872     if (!aSizeSpec.mUseDefaultWidth) {
01873       width = aSizeSpec.mOuterWidth;
01874     } // Else specified to default; just use our existing width
01875   }
01876   else if (aSizeSpec.mInnerWidthSpecified) {
01877     sizeChromeWidth = PR_FALSE;
01878     if (aSizeSpec.mUseDefaultWidth) {
01879       width = width - chromeWidth;
01880     } else {
01881       width = aSizeSpec.mInnerWidth;
01882     }
01883   }
01884 
01885   // Set up height
01886   if (aSizeSpec.mOuterHeightSpecified) {
01887     if (!aSizeSpec.mUseDefaultHeight) {
01888       height = aSizeSpec.mOuterHeight;
01889     } // Else specified to default; just use our existing height
01890   }
01891   else if (aSizeSpec.mInnerHeightSpecified) {
01892     sizeChromeHeight = PR_FALSE;
01893     if (aSizeSpec.mUseDefaultHeight) {
01894       height = height - chromeHeight;
01895     } else {
01896       height = aSizeSpec.mInnerHeight;
01897     }
01898   }
01899 
01900   PRBool positionSpecified = aSizeSpec.PositionSpecified();
01901   
01902   nsresult res;
01903   PRBool enabled = PR_FALSE;
01904 
01905   // Check security state for use in determing window dimensions
01906   nsCOMPtr<nsIScriptSecurityManager>
01907     securityManager(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
01908   if (securityManager) {
01909     res = securityManager->IsCapabilityEnabled("UniversalBrowserWrite",
01910                                                &enabled);
01911     if (NS_FAILED(res))
01912       enabled = PR_FALSE;
01913     else if (enabled && aParent) {
01914       nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(aParent));
01915 
01916       PRBool isChrome = PR_FALSE;
01917       securityManager->SubjectPrincipalIsSystem(&isChrome);
01918 
01919       // Only enable special priveleges for chrome when chrome calls
01920       // open() on a chrome window
01921       enabled = !(isChrome && chromeWin == nsnull);
01922     }
01923   }
01924 
01925   if (!enabled) {
01926 
01927     // Security check failed.  Ensure all args meet minimum reqs.
01928 
01929     PRInt32 oldTop = top,
01930             oldLeft = left;
01931 
01932     // We'll also need the screen dimensions
01933     nsCOMPtr<nsIScreen> screen;
01934     nsCOMPtr<nsIScreenManager> screenMgr(do_GetService(
01935                                          "@mozilla.org/gfx/screenmanager;1"));
01936     if (screenMgr)
01937       screenMgr->ScreenForRect(left, top, width, height,
01938                                getter_AddRefs(screen));
01939     if (screen) {
01940       PRInt32 screenLeft, screenTop, screenWidth, screenHeight;
01941       PRInt32 winWidth = width + (sizeChromeWidth ? 0 : chromeWidth),
01942               winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
01943 
01944       screen->GetAvailRect(&screenLeft, &screenTop,
01945                            &screenWidth, &screenHeight);
01946 
01947       if (aSizeSpec.SizeSpecified()) {
01948         /* Unlike position, force size out-of-bounds check only if
01949            size actually was specified. Otherwise, intrinsically sized
01950            windows are broken. */
01951         if (height < 100)
01952           height = 100;
01953         if (winHeight > screenHeight)
01954           height = screenHeight - (sizeChromeHeight ? 0 : chromeHeight);
01955         if (width < 100)
01956           width = 100;
01957         if (winWidth > screenWidth)
01958           width = screenWidth - (sizeChromeWidth ? 0 : chromeWidth);
01959       }
01960 
01961       if (left+winWidth > screenLeft+screenWidth)
01962         left = screenLeft+screenWidth - winWidth;
01963       if (left < screenLeft)
01964         left = screenLeft;
01965       if (top+winHeight > screenTop+screenHeight)
01966         top = screenTop+screenHeight - winHeight;
01967       if (top < screenTop)
01968         top = screenTop;
01969       if (top != oldTop || left != oldLeft)
01970         positionSpecified = PR_TRUE;
01971     }
01972   }
01973 
01974   // size and position the window
01975 
01976   if (positionSpecified)
01977     treeOwnerAsWin->SetPosition(left, top);
01978   if (aSizeSpec.SizeSpecified()) {
01979     /* Prefer to trust the interfaces, which think in terms of pure
01980        chrome or content sizes. If we have a mix, use the chrome size
01981        adjusted by the chrome/content differences calculated earlier. */
01982     if (!sizeChromeWidth && !sizeChromeHeight)
01983       treeOwner->SizeShellTo(aDocShellItem, width, height);
01984     else {
01985       if (!sizeChromeWidth)
01986         width += chromeWidth;
01987       if (!sizeChromeHeight)
01988         height += chromeHeight;
01989       treeOwnerAsWin->SetSize(width, height, PR_FALSE);
01990     }
01991   }
01992   treeOwnerAsWin->SetVisibility(PR_TRUE);
01993 }
01994 
01995 // attach the given array of JS values to the given window, as a property array
01996 // named "arguments"
01997 nsresult
01998 nsWindowWatcher::AttachArguments(nsIDOMWindow *aWindow,
01999                                  PRUint32 argc, jsval *argv)
02000 {
02001   if (argc == 0)
02002     return NS_OK;
02003 
02004   nsCOMPtr<nsIScriptGlobalObject> scriptGlobal(do_QueryInterface(aWindow));
02005   NS_ENSURE_TRUE(scriptGlobal, NS_ERROR_UNEXPECTED);
02006 
02007   // Just ask the global to attach the args for us
02008   return scriptGlobal->SetNewArguments(argc, NS_STATIC_CAST(void*, argv));
02009 }
02010 
02011 nsresult
02012 nsWindowWatcher::ConvertSupportsTojsvals(nsIDOMWindow *aWindow,
02013                                          nsISupports *aArgs,
02014                                          PRUint32 *aArgc, jsval **aArgv,
02015                                          JSContext **aUsedContext,
02016                                          void **aMarkp,
02017                                          nsIScriptContext **aScriptContext)
02018 {
02019   nsresult rv = NS_OK;
02020 
02021   *aArgv = nsnull;
02022   *aArgc = 0;
02023 
02024   // copy the elements in aArgsArray into the JS array
02025   // window.arguments in the new window
02026 
02027   if (!aArgs)
02028     return NS_OK;
02029 
02030   PRUint32 argCtr, argCount;
02031   nsCOMPtr<nsISupportsArray> argsArray(do_QueryInterface(aArgs));
02032 
02033   if (argsArray) {
02034     argsArray->Count(&argCount);
02035     if (argCount == 0)
02036       return NS_OK;
02037   } else
02038     argCount = 1; // the nsISupports which is not an array
02039 
02040   JSContext           *cx;
02041   JSContextAutoPopper  contextGuard;
02042 
02043   cx = GetJSContextFromWindow(aWindow);
02044   if (cx) {
02045     // Our caller needs to hold a strong ref to keep this context alive.
02046     *aScriptContext = GetScriptContextFromJSContext(cx);
02047     NS_ASSERTION(*aScriptContext,
02048                  "The window's context doesn't have a script context?");
02049     NS_ADDREF(*aScriptContext);
02050   } else {
02051     *aScriptContext = nsnull;
02052   }
02053   if (!cx)
02054     cx = GetJSContextFromCallStack();
02055   if (!cx) {
02056     rv = contextGuard.Push();
02057     if (NS_FAILED(rv))
02058       return rv;
02059     cx = contextGuard.get();
02060   }
02061 
02062   jsval *argv = js_AllocStack(cx, argCount, aMarkp);
02063   NS_ENSURE_TRUE(argv, NS_ERROR_OUT_OF_MEMORY);
02064 
02065   if (argsArray)
02066     for (argCtr = 0; argCtr < argCount && NS_SUCCEEDED(rv); argCtr++) {
02067       nsCOMPtr<nsISupports> s(dont_AddRef(argsArray->ElementAt(argCtr)));
02068       rv = AddSupportsTojsvals(s, cx, argv + argCtr);
02069     }
02070   else
02071     rv = AddSupportsTojsvals(aArgs, cx, argv);
02072 
02073   if (NS_FAILED(rv)) {
02074     js_FreeStack(cx, *aMarkp);
02075 
02076     return rv;
02077   }
02078 
02079   *aUsedContext = cx;
02080   *aArgv = argv;
02081   *aArgc = argCount;
02082   return NS_OK;
02083 }
02084 
02085 nsresult
02086 nsWindowWatcher::AddInterfaceTojsvals(nsISupports *aArg,
02087                                       JSContext *cx,
02088                                       jsval *aArgv)
02089 {
02090   nsresult rv;
02091   nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
02092   NS_ENSURE_SUCCESS(rv, rv);
02093 
02094   nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
02095   rv = xpc->WrapNative(cx, ::JS_GetGlobalObject(cx), aArg,
02096               NS_GET_IID(nsISupports), getter_AddRefs(wrapper));
02097   NS_ENSURE_SUCCESS(rv, rv);
02098 
02099   JSObject *obj;
02100   rv = wrapper->GetJSObject(&obj);
02101   NS_ENSURE_SUCCESS(rv, rv);
02102 
02103   *aArgv = OBJECT_TO_JSVAL(obj);
02104   return NS_OK;
02105 }
02106 
02107 nsresult
02108 nsWindowWatcher::AddSupportsTojsvals(nsISupports *aArg,
02109                                      JSContext *cx, jsval *aArgv)
02110 {
02111   if (!aArg) {
02112     *aArgv = JSVAL_NULL;
02113     return NS_OK;
02114   }
02115 
02116   nsCOMPtr<nsISupportsPrimitive> argPrimitive(do_QueryInterface(aArg));
02117   if (!argPrimitive)
02118     return AddInterfaceTojsvals(aArg, cx, aArgv);
02119 
02120   PRUint16 type;
02121   argPrimitive->GetType(&type);
02122 
02123   switch(type) {
02124     case nsISupportsPrimitive::TYPE_CSTRING : {
02125       nsCOMPtr<nsISupportsCString> p(do_QueryInterface(argPrimitive));
02126       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02127 
02128       nsCAutoString data;
02129 
02130       p->GetData(data);
02131 
02132       
02133       JSString *str = ::JS_NewStringCopyN(cx, data.get(), data.Length());
02134       NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
02135 
02136       *aArgv = STRING_TO_JSVAL(str);
02137 
02138       break;
02139     }
02140     case nsISupportsPrimitive::TYPE_STRING : {
02141       nsCOMPtr<nsISupportsString> p(do_QueryInterface(argPrimitive));
02142       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02143 
02144       nsAutoString data;
02145 
02146       p->GetData(data);
02147 
02148       // cast is probably safe since wchar_t and jschar are expected
02149       // to be equivalent; both unsigned 16-bit entities
02150       JSString *str =
02151         ::JS_NewUCStringCopyN(cx,
02152                               NS_REINTERPRET_CAST(const jschar *,data.get()),
02153                               data.Length());
02154       NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
02155 
02156       *aArgv = STRING_TO_JSVAL(str);
02157       break;
02158     }
02159     case nsISupportsPrimitive::TYPE_PRBOOL : {
02160       nsCOMPtr<nsISupportsPRBool> p(do_QueryInterface(argPrimitive));
02161       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02162 
02163       PRBool data;
02164 
02165       p->GetData(&data);
02166 
02167       *aArgv = BOOLEAN_TO_JSVAL(data);
02168 
02169       break;
02170     }
02171     case nsISupportsPrimitive::TYPE_PRUINT8 : {
02172       nsCOMPtr<nsISupportsPRUint8> p(do_QueryInterface(argPrimitive));
02173       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02174 
02175       PRUint8 data;
02176 
02177       p->GetData(&data);
02178 
02179       *aArgv = INT_TO_JSVAL(data);
02180 
02181       break;
02182     }
02183     case nsISupportsPrimitive::TYPE_PRUINT16 : {
02184       nsCOMPtr<nsISupportsPRUint16> p(do_QueryInterface(argPrimitive));
02185       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02186 
02187       PRUint16 data;
02188 
02189       p->GetData(&data);
02190 
02191       *aArgv = INT_TO_JSVAL(data);
02192 
02193       break;
02194     }
02195     case nsISupportsPrimitive::TYPE_PRUINT32 : {
02196       nsCOMPtr<nsISupportsPRUint32> p(do_QueryInterface(argPrimitive));
02197       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02198 
02199       PRUint32 data;
02200 
02201       p->GetData(&data);
02202 
02203       *aArgv = INT_TO_JSVAL(data);
02204 
02205       break;
02206     }
02207     case nsISupportsPrimitive::TYPE_CHAR : {
02208       nsCOMPtr<nsISupportsChar> p(do_QueryInterface(argPrimitive));
02209       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02210 
02211       char data;
02212 
02213       p->GetData(&data);
02214 
02215       JSString *str = ::JS_NewStringCopyN(cx, &data, 1);
02216       NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY);
02217 
02218       *aArgv = STRING_TO_JSVAL(str);
02219 
02220       break;
02221     }
02222     case nsISupportsPrimitive::TYPE_PRINT16 : {
02223       nsCOMPtr<nsISupportsPRInt16> p(do_QueryInterface(argPrimitive));
02224       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02225 
02226       PRInt16 data;
02227 
02228       p->GetData(&data);
02229 
02230       *aArgv = INT_TO_JSVAL(data);
02231 
02232       break;
02233     }
02234     case nsISupportsPrimitive::TYPE_PRINT32 : {
02235       nsCOMPtr<nsISupportsPRInt32> p(do_QueryInterface(argPrimitive));
02236       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02237 
02238       PRInt32 data;
02239 
02240       p->GetData(&data);
02241 
02242       *aArgv = INT_TO_JSVAL(data);
02243 
02244       break;
02245     }
02246     case nsISupportsPrimitive::TYPE_FLOAT : {
02247       nsCOMPtr<nsISupportsFloat> p(do_QueryInterface(argPrimitive));
02248       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02249 
02250       float data;
02251 
02252       p->GetData(&data);
02253 
02254       jsdouble *d = ::JS_NewDouble(cx, data);
02255 
02256       *aArgv = DOUBLE_TO_JSVAL(d);
02257 
02258       break;
02259     }
02260     case nsISupportsPrimitive::TYPE_DOUBLE : {
02261       nsCOMPtr<nsISupportsDouble> p(do_QueryInterface(argPrimitive));
02262       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02263 
02264       double data;
02265 
02266       p->GetData(&data);
02267 
02268       jsdouble *d = ::JS_NewDouble(cx, data);
02269 
02270       *aArgv = DOUBLE_TO_JSVAL(d);
02271 
02272       break;
02273     }
02274     case nsISupportsPrimitive::TYPE_INTERFACE_POINTER : {
02275       nsCOMPtr<nsISupportsInterfacePointer> p(do_QueryInterface(argPrimitive));
02276       NS_ENSURE_TRUE(p, NS_ERROR_UNEXPECTED);
02277 
02278       nsCOMPtr<nsISupports> data;
02279       nsIID *iid = nsnull;
02280 
02281       p->GetData(getter_AddRefs(data));
02282       p->GetDataIID(&iid);
02283       NS_ENSURE_TRUE(iid, NS_ERROR_UNEXPECTED);
02284 
02285       AutoFree iidGuard(iid); // Free iid upon destruction.
02286 
02287       nsresult rv;
02288       nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
02289       NS_ENSURE_SUCCESS(rv, rv);
02290 
02291       nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
02292       rv = xpc->WrapNative(cx, ::JS_GetGlobalObject(cx), data,
02293                            *iid, getter_AddRefs(wrapper));
02294       NS_ENSURE_SUCCESS(rv, rv);
02295 
02296       JSObject *obj;
02297       rv = wrapper->GetJSObject(&obj);
02298       NS_ENSURE_SUCCESS(rv, rv);
02299 
02300       *aArgv = OBJECT_TO_JSVAL(obj);
02301 
02302       break;
02303     }
02304     case nsISupportsPrimitive::TYPE_ID :
02305     case nsISupportsPrimitive::TYPE_PRUINT64 :
02306     case nsISupportsPrimitive::TYPE_PRINT64 :
02307     case nsISupportsPrimitive::TYPE_PRTIME :
02308     case nsISupportsPrimitive::TYPE_VOID : {
02309       NS_WARNING("Unsupported primitive type used");
02310       *aArgv = JSVAL_NULL;
02311       break;
02312     }
02313     default : {
02314       NS_WARNING("Unknown primitive type used");
02315       *aArgv = JSVAL_NULL;
02316       break;
02317     }
02318   }
02319   return NS_OK;
02320 }
02321 
02322 void
02323 nsWindowWatcher::GetWindowTreeItem(nsIDOMWindow *inWindow,
02324                                    nsIDocShellTreeItem **outTreeItem)
02325 {
02326   *outTreeItem = 0;
02327 
02328   nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(inWindow));
02329   if (sgo) {
02330     nsIDocShell *docshell = sgo->GetDocShell();
02331     if (docshell)
02332       CallQueryInterface(docshell, outTreeItem);
02333   }
02334 }
02335 
02336 void
02337 nsWindowWatcher::GetWindowTreeOwner(nsIDOMWindow *inWindow,
02338                                     nsIDocShellTreeOwner **outTreeOwner)
02339 {
02340   *outTreeOwner = 0;
02341 
02342   nsCOMPtr<nsIDocShellTreeItem> treeItem;
02343   GetWindowTreeItem(inWindow, getter_AddRefs(treeItem));
02344   if (treeItem)
02345     treeItem->GetTreeOwner(outTreeOwner);
02346 }
02347 
02348 JSContext *
02349 nsWindowWatcher::GetJSContextFromCallStack()
02350 {
02351   JSContext *cx = 0;
02352 
02353   nsCOMPtr<nsIThreadJSContextStack> cxStack(do_GetService(sJSStackContractID));
02354   if (cxStack)
02355     cxStack->Peek(&cx);
02356 
02357   return cx;
02358 }
02359 
02360 JSContext *
02361 nsWindowWatcher::GetJSContextFromWindow(nsIDOMWindow *aWindow)
02362 {
02363   JSContext *cx = 0;
02364 
02365   if (aWindow) {
02366     nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aWindow));
02367     if (sgo) {
02368       nsIScriptContext *scx = sgo->GetContext();
02369       if (scx)
02370         cx = (JSContext *) scx->GetNativeContext();
02371     }
02372     /* (off-topic note:) the nsIScriptContext can be retrieved by
02373     nsCOMPtr<nsIScriptContext> scx;
02374     nsJSUtils::GetDynamicScriptContext(cx, getter_AddRefs(scx));
02375     */
02376   }
02377 
02378   return cx;
02379 }