embedding/components/windowwatcher/src/nsWindowWatcher.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 sw=2 et tw=78: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 //#define USEWEAKREFS // (haven't quite figured that out yet)
     9 #include "nsWindowWatcher.h"
    10 #include "nsAutoWindowStateHelper.h"
    12 #include "nsCRT.h"
    13 #include "nsNetUtil.h"
    14 #include "nsJSUtils.h"
    15 #include "plstr.h"
    17 #include "nsIBaseWindow.h"
    18 #include "nsIDocShell.h"
    19 #include "nsIDocShellLoadInfo.h"
    20 #include "nsIDocShellTreeItem.h"
    21 #include "nsIDocShellTreeOwner.h"
    22 #include "nsIDocumentLoader.h"
    23 #include "nsIDocument.h"
    24 #include "nsIDOMDocument.h"
    25 #include "nsIDOMWindow.h"
    26 #include "nsIDOMChromeWindow.h"
    27 #include "nsIDOMModalContentWindow.h"
    28 #include "nsIPrompt.h"
    29 #include "nsIScriptObjectPrincipal.h"
    30 #include "nsIScreen.h"
    31 #include "nsIScreenManager.h"
    32 #include "nsIScriptContext.h"
    33 #include "nsIObserverService.h"
    34 #include "nsIScriptGlobalObject.h"
    35 #include "nsIScriptSecurityManager.h"
    36 #include "nsXPCOM.h"
    37 #include "nsIURI.h"
    38 #include "nsIWebBrowser.h"
    39 #include "nsIWebBrowserChrome.h"
    40 #include "nsIWebNavigation.h"
    41 #include "nsIWindowCreator.h"
    42 #include "nsIWindowCreator2.h"
    43 #include "nsIXPConnect.h"
    44 #include "nsIXULRuntime.h"
    45 #include "nsPIDOMWindow.h"
    46 #include "nsIMarkupDocumentViewer.h"
    47 #include "nsIContentViewer.h"
    48 #include "nsIWindowProvider.h"
    49 #include "nsIMutableArray.h"
    50 #include "nsISupportsArray.h"
    51 #include "nsIDOMStorage.h"
    52 #include "nsIDOMStorageManager.h"
    53 #include "nsIWidget.h"
    54 #include "nsFocusManager.h"
    55 #include "nsIPresShell.h"
    56 #include "nsPresContext.h"
    57 #include "nsContentUtils.h"
    58 #include "nsCxPusher.h"
    59 #include "nsIPrefBranch.h"
    60 #include "nsIPrefService.h"
    61 #include "nsSandboxFlags.h"
    62 #include "mozilla/Preferences.h"
    64 #ifdef USEWEAKREFS
    65 #include "nsIWeakReference.h"
    66 #endif
    68 using namespace mozilla;
    70 /****************************************************************
    71  ******************** nsWatcherWindowEntry **********************
    72  ****************************************************************/
    74 class nsWindowWatcher;
    76 struct nsWatcherWindowEntry {
    78   nsWatcherWindowEntry(nsIDOMWindow *inWindow, nsIWebBrowserChrome *inChrome) {
    79 #ifdef USEWEAKREFS
    80     mWindow = do_GetWeakReference(inWindow);
    81 #else
    82     mWindow = inWindow;
    83 #endif
    84     nsCOMPtr<nsISupportsWeakReference> supportsweak(do_QueryInterface(inChrome));
    85     if (supportsweak) {
    86       supportsweak->GetWeakReference(getter_AddRefs(mChromeWeak));
    87     } else {
    88       mChrome = inChrome;
    89       mChromeWeak = 0;
    90     }
    91     ReferenceSelf();
    92   }
    93   ~nsWatcherWindowEntry() {}
    95   void InsertAfter(nsWatcherWindowEntry *inOlder);
    96   void Unlink();
    97   void ReferenceSelf();
    99 #ifdef USEWEAKREFS
   100   nsCOMPtr<nsIWeakReference> mWindow;
   101 #else // still not an owning ref
   102   nsIDOMWindow              *mWindow;
   103 #endif
   104   nsIWebBrowserChrome       *mChrome;
   105   nsWeakPtr                  mChromeWeak;
   106   // each struct is in a circular, doubly-linked list
   107   nsWatcherWindowEntry      *mYounger, // next younger in sequence
   108                             *mOlder;
   109 };
   111 void nsWatcherWindowEntry::InsertAfter(nsWatcherWindowEntry *inOlder)
   112 {
   113   if (inOlder) {
   114     mOlder = inOlder;
   115     mYounger = inOlder->mYounger;
   116     mOlder->mYounger = this;
   117     if (mOlder->mOlder == mOlder)
   118       mOlder->mOlder = this;
   119     mYounger->mOlder = this;
   120     if (mYounger->mYounger == mYounger)
   121       mYounger->mYounger = this;
   122   }
   123 }
   125 void nsWatcherWindowEntry::Unlink() {
   127   mOlder->mYounger = mYounger;
   128   mYounger->mOlder = mOlder;
   129   ReferenceSelf();
   130 }
   132 void nsWatcherWindowEntry::ReferenceSelf() {
   134   mYounger = this;
   135   mOlder = this;
   136 }
   138 /****************************************************************
   139  ****************** nsWatcherWindowEnumerator *******************
   140  ****************************************************************/
   142 class nsWatcherWindowEnumerator : public nsISimpleEnumerator {
   144 public:
   145   nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher);
   146   virtual ~nsWatcherWindowEnumerator();
   147   NS_IMETHOD HasMoreElements(bool *retval);
   148   NS_IMETHOD GetNext(nsISupports **retval);
   150   NS_DECL_ISUPPORTS
   152 private:
   153   friend class nsWindowWatcher;
   155   nsWatcherWindowEntry *FindNext();
   156   void WindowRemoved(nsWatcherWindowEntry *inInfo);
   158   nsWindowWatcher      *mWindowWatcher;
   159   nsWatcherWindowEntry *mCurrentPosition;
   160 };
   162 NS_IMPL_ADDREF(nsWatcherWindowEnumerator)
   163 NS_IMPL_RELEASE(nsWatcherWindowEnumerator)
   164 NS_IMPL_QUERY_INTERFACE(nsWatcherWindowEnumerator, nsISimpleEnumerator)
   166 nsWatcherWindowEnumerator::nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher)
   167   : mWindowWatcher(inWatcher),
   168     mCurrentPosition(inWatcher->mOldestWindow)
   169 {
   170   mWindowWatcher->AddEnumerator(this);
   171   mWindowWatcher->AddRef();
   172 }
   174 nsWatcherWindowEnumerator::~nsWatcherWindowEnumerator()
   175 {
   176   mWindowWatcher->RemoveEnumerator(this);
   177   mWindowWatcher->Release();
   178 }
   180 NS_IMETHODIMP
   181 nsWatcherWindowEnumerator::HasMoreElements(bool *retval)
   182 {
   183   if (!retval)
   184     return NS_ERROR_INVALID_ARG;
   186   *retval = mCurrentPosition? true : false;
   187   return NS_OK;
   188 }
   190 NS_IMETHODIMP
   191 nsWatcherWindowEnumerator::GetNext(nsISupports **retval)
   192 {
   193   if (!retval)
   194     return NS_ERROR_INVALID_ARG;
   196   *retval = nullptr;
   198 #ifdef USEWEAKREFS
   199   while (mCurrentPosition) {
   200     CallQueryReferent(mCurrentPosition->mWindow, retval);
   201     if (*retval) {
   202       mCurrentPosition = FindNext();
   203       break;
   204     } else // window is gone!
   205       mWindowWatcher->RemoveWindow(mCurrentPosition);
   206   }
   207   NS_IF_ADDREF(*retval);
   208 #else
   209   if (mCurrentPosition) {
   210     CallQueryInterface(mCurrentPosition->mWindow, retval);
   211     mCurrentPosition = FindNext();
   212   }
   213 #endif
   214   return NS_OK;
   215 }
   217 nsWatcherWindowEntry *
   218 nsWatcherWindowEnumerator::FindNext()
   219 {
   220   nsWatcherWindowEntry *info;
   222   if (!mCurrentPosition)
   223     return 0;
   225   info = mCurrentPosition->mYounger;
   226   return info == mWindowWatcher->mOldestWindow ? 0 : info;
   227 }
   229 // if a window is being removed adjust the iterator's current position
   230 void nsWatcherWindowEnumerator::WindowRemoved(nsWatcherWindowEntry *inInfo) {
   232   if (mCurrentPosition == inInfo)
   233     mCurrentPosition = mCurrentPosition != inInfo->mYounger ?
   234                        inInfo->mYounger : 0;
   235 }
   237 /****************************************************************
   238  *********************** nsWindowWatcher ************************
   239  ****************************************************************/
   241 NS_IMPL_ADDREF(nsWindowWatcher)
   242 NS_IMPL_RELEASE(nsWindowWatcher)
   243 NS_IMPL_QUERY_INTERFACE(nsWindowWatcher,
   244                         nsIWindowWatcher,
   245                         nsIPromptFactory,
   246                         nsPIWindowWatcher)
   248 nsWindowWatcher::nsWindowWatcher() :
   249         mEnumeratorList(),
   250         mOldestWindow(0),
   251         mListLock("nsWindowWatcher.mListLock")
   252 {
   253 }
   255 nsWindowWatcher::~nsWindowWatcher()
   256 {
   257   // delete data
   258   while (mOldestWindow)
   259     RemoveWindow(mOldestWindow);
   260 }
   262 nsresult
   263 nsWindowWatcher::Init()
   264 {
   265   return NS_OK;
   266 }
   268 /**
   269  * Convert aArguments into either an nsIArray or nullptr.
   270  *
   271  *  - If aArguments is nullptr, return nullptr.
   272  *  - If aArguments is an nsArray, return nullptr if it's empty, or otherwise
   273  *    return the array.
   274  *  - If aArguments is an nsISupportsArray, return nullptr if it's empty, or
   275  *    otherwise add its elements to an nsArray and return the new array.
   276  *  - Otherwise, return an nsIArray with one element: aArguments.
   277  */
   278 static already_AddRefed<nsIArray>
   279 ConvertArgsToArray(nsISupports* aArguments)
   280 {
   281   if (!aArguments) {
   282     return nullptr;
   283   }
   285   nsCOMPtr<nsIArray> array = do_QueryInterface(aArguments);
   286   if (array) {
   287     uint32_t argc = 0;
   288     array->GetLength(&argc);
   289     if (argc == 0)
   290       return nullptr;
   292     return array.forget();
   293   }
   295   nsCOMPtr<nsISupportsArray> supArray = do_QueryInterface(aArguments);
   296   if (supArray) {
   297     uint32_t argc = 0;
   298     supArray->Count(&argc);
   299     if (argc == 0) {
   300       return nullptr;
   301     }
   303     nsCOMPtr<nsIMutableArray> mutableArray =
   304       do_CreateInstance(NS_ARRAY_CONTRACTID);
   305     NS_ENSURE_TRUE(mutableArray, nullptr);
   307     for (uint32_t i = 0; i < argc; i++) {
   308       nsCOMPtr<nsISupports> elt;
   309       supArray->GetElementAt(i, getter_AddRefs(elt));
   310       nsresult rv = mutableArray->AppendElement(elt, /* aWeak = */ false);
   311       NS_ENSURE_SUCCESS(rv, nullptr);
   312     }
   314     return mutableArray.forget();
   315   }
   317   nsCOMPtr<nsIMutableArray> singletonArray =
   318     do_CreateInstance(NS_ARRAY_CONTRACTID);
   319   NS_ENSURE_TRUE(singletonArray, nullptr);
   321   nsresult rv = singletonArray->AppendElement(aArguments, /* aWeak = */ false);
   322   NS_ENSURE_SUCCESS(rv, nullptr);
   324   return singletonArray.forget();
   325 }
   327 NS_IMETHODIMP
   328 nsWindowWatcher::OpenWindow(nsIDOMWindow *aParent,
   329                             const char *aUrl,
   330                             const char *aName,
   331                             const char *aFeatures,
   332                             nsISupports *aArguments,
   333                             nsIDOMWindow **_retval)
   334 {
   335   nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
   337   uint32_t argc = 0;
   338   if (argv) {
   339     argv->GetLength(&argc);
   340   }
   341   bool dialog = (argc != 0);
   343   return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
   344                             /* calledFromJS = */ false, dialog,
   345                             /* navigate = */ true, argv, _retval);
   346 }
   348 struct SizeSpec {
   349   SizeSpec() :
   350     mLeftSpecified(false),
   351     mTopSpecified(false),
   352     mOuterWidthSpecified(false),
   353     mOuterHeightSpecified(false),
   354     mInnerWidthSpecified(false),
   355     mInnerHeightSpecified(false),
   356     mUseDefaultWidth(false),
   357     mUseDefaultHeight(false)
   358   {}
   360   int32_t mLeft;
   361   int32_t mTop;
   362   int32_t mOuterWidth;  // Total window width
   363   int32_t mOuterHeight; // Total window height
   364   int32_t mInnerWidth;  // Content area width
   365   int32_t mInnerHeight; // Content area height
   367   bool mLeftSpecified;
   368   bool mTopSpecified;
   369   bool mOuterWidthSpecified;
   370   bool mOuterHeightSpecified;
   371   bool mInnerWidthSpecified;
   372   bool mInnerHeightSpecified;
   374   // If these booleans are true, don't look at the corresponding width values
   375   // even if they're specified -- they'll be bogus
   376   bool mUseDefaultWidth;
   377   bool mUseDefaultHeight;
   379   bool PositionSpecified() const {
   380     return mLeftSpecified || mTopSpecified;
   381   }
   383   bool SizeSpecified() const {
   384     return mOuterWidthSpecified || mOuterHeightSpecified ||
   385       mInnerWidthSpecified || mInnerHeightSpecified;
   386   }
   387 };
   389 NS_IMETHODIMP
   390 nsWindowWatcher::OpenWindow2(nsIDOMWindow *aParent,
   391                               const char *aUrl,
   392                               const char *aName,
   393                               const char *aFeatures,
   394                               bool aCalledFromScript,
   395                               bool aDialog,
   396                               bool aNavigate,
   397                               nsISupports *aArguments,
   398                               nsIDOMWindow **_retval)
   399 {
   400   nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
   402   uint32_t argc = 0;
   403   if (argv) {
   404     argv->GetLength(&argc);
   405   }
   407   // This is extremely messed up, but this behavior is necessary because
   408   // callers lie about whether they're a dialog window and whether they're
   409   // called from script.  Fixing this is bug 779939.
   410   bool dialog = aDialog;
   411   if (!aCalledFromScript) {
   412     dialog = argc > 0;
   413   }
   415   return OpenWindowInternal(aParent, aUrl, aName, aFeatures,
   416                             aCalledFromScript, dialog,
   417                             aNavigate, argv, _retval);
   418 }
   420 nsresult
   421 nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent,
   422                                     const char *aUrl,
   423                                     const char *aName,
   424                                     const char *aFeatures,
   425                                     bool aCalledFromJS,
   426                                     bool aDialog,
   427                                     bool aNavigate,
   428                                     nsIArray *argv,
   429                                     nsIDOMWindow **_retval)
   430 {
   431   nsresult                        rv = NS_OK;
   432   bool                            nameSpecified,
   433                                   featuresSpecified,
   434                                   isNewToplevelWindow = false,
   435                                   windowIsNew = false,
   436                                   windowNeedsName = false,
   437                                   windowIsModal = false,
   438                                   uriToLoadIsChrome = false,
   439                                   windowIsModalContentDialog = false;
   440   uint32_t                        chromeFlags;
   441   nsAutoString                    name;             // string version of aName
   442   nsAutoCString                   features;         // string version of aFeatures
   443   nsCOMPtr<nsIURI>                uriToLoad;        // from aUrl, if any
   444   nsCOMPtr<nsIDocShellTreeOwner>  parentTreeOwner;  // from the parent window, if any
   445   nsCOMPtr<nsIDocShellTreeItem>   newDocShellItem;  // from the new window
   446   nsCxPusher                      callerContextGuard;
   448   NS_ENSURE_ARG_POINTER(_retval);
   449   *_retval = 0;
   451   if (!nsContentUtils::IsSafeToRunScript()) {
   452     return NS_ERROR_FAILURE;
   453   }
   455   GetWindowTreeOwner(aParent, getter_AddRefs(parentTreeOwner));
   457   if (aUrl) {
   458     rv = URIfromURL(aUrl, aParent, getter_AddRefs(uriToLoad));
   459     if (NS_FAILED(rv))
   460       return rv;
   461     uriToLoad->SchemeIs("chrome", &uriToLoadIsChrome);
   462   }
   464   nameSpecified = false;
   465   if (aName) {
   466     CopyUTF8toUTF16(aName, name);
   467     nameSpecified = true;
   468   }
   470   featuresSpecified = false;
   471   if (aFeatures) {
   472     features.Assign(aFeatures);
   473     featuresSpecified = true;
   474     features.StripWhitespace();
   475   }
   477   // try to find an extant window with the given name
   478   nsCOMPtr<nsIDOMWindow> foundWindow = SafeGetWindowByName(name, aParent);
   479   GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
   481   // Do sandbox checks here, instead of waiting until nsIDocShell::LoadURI.
   482   // The state of the window can change before this call and if we are blocked
   483   // because of sandboxing, we wouldn't want that to happen.
   484   nsCOMPtr<nsPIDOMWindow> parentWindow = do_QueryInterface(aParent);
   485   nsCOMPtr<nsIDocShell> parentDocShell;
   486   if (parentWindow) {
   487     parentDocShell = parentWindow->GetDocShell();
   488     if (parentDocShell) {
   489       nsCOMPtr<nsIDocShell> foundDocShell = do_QueryInterface(newDocShellItem);
   490       if (parentDocShell->IsSandboxedFrom(foundDocShell)) {
   491         return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   492       }
   493     }
   494   }
   496   // no extant window? make a new one.
   498   // If no parent, consider it chrome.
   499   bool hasChromeParent = true;
   500   if (aParent) {
   501     // Check if the parent document has chrome privileges.
   502     nsCOMPtr<nsIDOMDocument> domdoc;
   503     aParent->GetDocument(getter_AddRefs(domdoc));
   504     nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
   505     hasChromeParent = doc && nsContentUtils::IsChromeDoc(doc);
   506   }
   508   // Make sure we call CalculateChromeFlags() *before* we push the
   509   // callee context onto the context stack so that
   510   // CalculateChromeFlags() sees the actual caller when doing its
   511   // security checks.
   512   chromeFlags = CalculateChromeFlags(aParent, features.get(), featuresSpecified,
   513                                      aDialog, uriToLoadIsChrome,
   514                                      hasChromeParent);
   516   // If we're not called through our JS version of the API, and we got
   517   // our internal modal option, treat the window we're opening as a
   518   // modal content window (and set the modal chrome flag).
   519   if (!aCalledFromJS && argv &&
   520       WinHasOption(features.get(), "-moz-internal-modal", 0, nullptr)) {
   521     windowIsModalContentDialog = true;
   523     // CHROME_MODAL gets inherited by dependent windows, which affects various
   524     // platform-specific window state (especially on OSX). So we need some way
   525     // to determine that this window was actually opened by nsGlobalWindow::
   526     // ShowModalDialog(), and that somebody is actually going to be watching
   527     // for return values and all that.
   528     chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL_CONTENT_WINDOW;
   529     chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL;
   530   }
   532   SizeSpec sizeSpec;
   533   CalcSizeSpec(features.get(), sizeSpec);
   535   nsCOMPtr<nsIScriptSecurityManager>
   536     sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID));
   538   bool isCallerChrome = nsContentUtils::IsCallerChrome();
   540   JSContext *cx = GetJSContextFromWindow(aParent);
   542   bool windowTypeIsChrome = chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
   543   if (isCallerChrome && !hasChromeParent && !windowTypeIsChrome && cx) {
   544     // open() is called from chrome on a non-chrome window, push the context of the
   545     // callee onto the context stack to prevent the caller's priveleges from leaking
   546     // into code that runs while opening the new window.
   547     //
   548     // The reasoning for this is in bug 289204. Basically, chrome sometimes does
   549     // someContentWindow.open(untrustedURL), and wants to be insulated from nasty
   550     // javascript: URLs and such. But there are also cases where we create a
   551     // window parented to a content window (such as a download dialog), usually
   552     // directly with nsIWindowWatcher. In those cases, we want the principal of
   553     // the initial about:blank document to be system, so that the subsequent XUL
   554     // load can reuse the inner window and avoid blowing away expandos. As such,
   555     // we decide whether to load with the principal of the caller or of the parent
   556     // based on whether the docshell type is chrome or content.
   558     callerContextGuard.Push(cx);
   559   }
   561   uint32_t activeDocsSandboxFlags = 0;
   562   if (!newDocShellItem) {
   563     // We're going to either open up a new window ourselves or ask a
   564     // nsIWindowProvider for one.  In either case, we'll want to set the right
   565     // name on it.
   566     windowNeedsName = true;
   568     // If the parent trying to open a new window is sandboxed
   569     // without 'allow-popups', this is not allowed and we fail here.
   570     if (aParent) {
   571       nsCOMPtr<nsIDOMDocument> domdoc;
   572       aParent->GetDocument(getter_AddRefs(domdoc));
   573       nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
   575       if (doc) {
   576         // Save sandbox flags for copying to new browsing context (docShell).
   577         activeDocsSandboxFlags = doc->GetSandboxFlags();
   578         if (activeDocsSandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) {
   579           return NS_ERROR_DOM_INVALID_ACCESS_ERR;
   580         }
   581       }
   582     }
   584     // Now check whether it's ok to ask a window provider for a window.  Don't
   585     // do it if we're opening a dialog or if our parent is a chrome window or
   586     // if we're opening something that has modal, dialog, or chrome flags set.
   587     nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(aParent);
   588     if (!aDialog && !chromeWin &&
   589         !(chromeFlags & (nsIWebBrowserChrome::CHROME_MODAL         |
   590                          nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | 
   591                          nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
   592       nsCOMPtr<nsIWindowProvider> provider = do_GetInterface(parentTreeOwner);
   593       if (provider) {
   594         NS_ASSERTION(aParent, "We've _got_ to have a parent here!");
   596         nsCOMPtr<nsIDOMWindow> newWindow;
   597         rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS,
   598                                      sizeSpec.PositionSpecified(),
   599                                      sizeSpec.SizeSpecified(),
   600                                      uriToLoad, name, features, &windowIsNew,
   601                                      getter_AddRefs(newWindow));
   603         if (NS_SUCCEEDED(rv)) {
   604           GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
   605           if (windowIsNew && newDocShellItem) {
   606             // Make sure to stop any loads happening in this window that the
   607             // window provider might have started.  Otherwise if our caller
   608             // manipulates the window it just opened and then the load
   609             // completes their stuff will get blown away.
   610             nsCOMPtr<nsIWebNavigation> webNav =
   611               do_QueryInterface(newDocShellItem);
   612             webNav->Stop(nsIWebNavigation::STOP_NETWORK);
   613           }
   614         }
   615         else if (rv == NS_ERROR_ABORT) {
   616           // NS_ERROR_ABORT means the window provider has flat-out rejected
   617           // the open-window call and we should bail.  Don't return an error
   618           // here, because our caller may propagate that error, which might
   619           // cause e.g. window.open to throw!  Just return null for our out
   620           // param.
   621           return NS_OK;
   622         }
   623       }
   624     }
   625   }
   627   bool newWindowShouldBeModal = false;
   628   bool parentIsModal = false;
   629   if (!newDocShellItem) {
   630     windowIsNew = true;
   631     isNewToplevelWindow = true;
   633     nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
   635     // is the parent (if any) modal? if so, we must be, too.
   636     bool weAreModal = (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) != 0;
   637     newWindowShouldBeModal = weAreModal;
   638     if (!weAreModal && parentChrome) {
   639       parentChrome->IsWindowModal(&weAreModal);
   640       parentIsModal = weAreModal;
   641     }
   643     if (weAreModal) {
   644       windowIsModal = true;
   645       // in case we added this because weAreModal
   646       chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL |
   647         nsIWebBrowserChrome::CHROME_DEPENDENT;
   648     }
   650     // Make sure to not create modal windows if our parent is invisible and
   651     // isn't a chrome window.  Otherwise we can end up in a bizarre situation
   652     // where we can't shut down because an invisible window is open.  If
   653     // someone tries to do this, throw.
   654     if (!hasChromeParent && (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)) {
   655       nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(parentTreeOwner));
   656       nsCOMPtr<nsIWidget> parentWidget;
   657       if (parentWindow)
   658         parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
   659       // NOTE: the logic for this visibility check is duplicated in
   660       // nsIDOMWindowUtils::isParentWindowMainWidgetVisible - if we change
   661       // how a window is determined "visible" in this context then we should
   662       // also adjust that attribute and/or any consumers of it...
   663       if (parentWidget && !parentWidget->IsVisible())
   664         return NS_ERROR_NOT_AVAILABLE;
   665     }
   667     NS_ASSERTION(mWindowCreator,
   668                  "attempted to open a new window with no WindowCreator");
   669     rv = NS_ERROR_FAILURE;
   670     if (mWindowCreator) {
   671       nsCOMPtr<nsIWebBrowserChrome> newChrome;
   673       /* If the window creator is an nsIWindowCreator2, we can give it
   674          some hints. The only hint at this time is whether the opening window
   675          is in a situation that's likely to mean this is an unrequested
   676          popup window we're creating. However we're not completely honest:
   677          we clear that indicator if the opener is chrome, so that the
   678          downstream consumer can treat the indicator to mean simply
   679          that the new window is subject to popup control. */
   680       nsCOMPtr<nsIWindowCreator2> windowCreator2(do_QueryInterface(mWindowCreator));
   681       if (windowCreator2) {
   682         uint32_t contextFlags = 0;
   683         bool popupConditions = false;
   685         // is the parent under popup conditions?
   686         if (parentWindow) {
   687           popupConditions = parentWindow->IsLoadingOrRunningTimeout();
   688         }
   690         // chrome is always allowed, so clear the flag if the opener is chrome
   691         if (popupConditions) {
   692           popupConditions = !isCallerChrome;
   693         }
   695         if (popupConditions)
   696           contextFlags |= nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT;
   698         bool cancel = false;
   699         rv = windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags,
   700                                                  contextFlags, uriToLoad,
   701                                                  &cancel,
   702                                                  getter_AddRefs(newChrome));
   703         if (NS_SUCCEEDED(rv) && cancel) {
   704           newChrome = 0; // just in case
   705           rv = NS_ERROR_ABORT;
   706         }
   707       }
   708       else
   709         rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags,
   710                                                 getter_AddRefs(newChrome));
   711       if (newChrome) {
   712         /* It might be a chrome nsXULWindow, in which case it won't have
   713             an nsIDOMWindow (primary content shell). But in that case, it'll
   714             be able to hand over an nsIDocShellTreeItem directly. */
   715         nsCOMPtr<nsIDOMWindow> newWindow(do_GetInterface(newChrome));
   716         if (newWindow)
   717           GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem));
   718         if (!newDocShellItem)
   719           newDocShellItem = do_GetInterface(newChrome);
   720         if (!newDocShellItem)
   721           rv = NS_ERROR_FAILURE;
   722       }
   723     }
   724   }
   726   // better have a window to use by this point
   727   if (!newDocShellItem)
   728     return rv;
   730   nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem));
   731   NS_ENSURE_TRUE(newDocShell, NS_ERROR_UNEXPECTED);
   733   // Set up sandboxing attributes if the window is new.
   734   // The flags can only be non-zero for new windows.
   735   if (activeDocsSandboxFlags != 0) {
   736     newDocShell->SetSandboxFlags(activeDocsSandboxFlags);
   737     if (parentWindow) {
   738       newDocShell->
   739         SetOnePermittedSandboxedNavigator(parentWindow->GetDocShell());
   740     }
   741   }
   743   rv = ReadyOpenedDocShellItem(newDocShellItem, aParent, windowIsNew, _retval);
   744   if (NS_FAILED(rv))
   745     return rv;
   747   /* disable persistence of size/position in popups (determined by
   748      determining whether the features parameter specifies width or height
   749      in any way). We consider any overriding of the window's size or position
   750      in the open call as disabling persistence of those attributes.
   751      Popup windows (which should not persist size or position) generally set
   752      the size. */
   753   if (isNewToplevelWindow) {
   754     /* at the moment, the strings "height=" or "width=" never happen
   755        outside a size specification, so we can do this the Q&D way. */
   757     if (PL_strcasestr(features.get(), "width=") || PL_strcasestr(features.get(), "height=")) {
   759       nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
   760       newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
   761       if (newTreeOwner)
   762         newTreeOwner->SetPersistence(false, false, false);
   763     }
   764   }
   766   if ((aDialog || windowIsModalContentDialog) && argv) {
   767     // Set the args on the new window.
   768     nsCOMPtr<nsPIDOMWindow> piwin(do_QueryInterface(*_retval));
   769     NS_ENSURE_TRUE(piwin, NS_ERROR_UNEXPECTED);
   771     rv = piwin->SetArguments(argv);
   772     NS_ENSURE_SUCCESS(rv, rv);
   773   }
   775   /* allow a window that we found by name to keep its name (important for cases
   776      like _self where the given name is different (and invalid)).  Also, _blank
   777      is not a window name. */
   778   if (windowNeedsName) {
   779     if (nameSpecified && !name.LowerCaseEqualsLiteral("_blank")) {
   780       newDocShellItem->SetName(name);
   781     } else {
   782       newDocShellItem->SetName(EmptyString());
   783     }
   784   }
   786   // Now we have to set the right opener principal on the new window.  Note
   787   // that we have to do this _before_ starting any URI loads, thanks to the
   788   // sync nature of javascript: loads.  Since this is the only place where we
   789   // set said opener principal, we need to do it for all URIs, including
   790   // chrome ones.  So to deal with the mess that is bug 79775, just press on in
   791   // a reasonable way even if GetSubjectPrincipal fails.  In that case, just
   792   // use a null subjectPrincipal.
   793   nsCOMPtr<nsIPrincipal> subjectPrincipal;
   794   if (NS_FAILED(sm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)))) {
   795     subjectPrincipal = nullptr;
   796   }
   798   if (windowIsNew) {
   799     // Now set the opener principal on the new window.  Note that we need to do
   800     // this no matter whether we were opened from JS; if there is nothing on
   801     // the JS stack, just use the principal of our parent window.  In those
   802     // cases we do _not_ set the parent window principal as the owner of the
   803     // load--since we really don't know who the owner is, just leave it null.
   804     nsCOMPtr<nsPIDOMWindow> newWindow = do_QueryInterface(*_retval);
   805 #ifdef DEBUG
   806     nsCOMPtr<nsPIDOMWindow> newDebugWindow = do_GetInterface(newDocShell);
   807     NS_ASSERTION(newWindow == newDebugWindow, "Different windows??");
   808 #endif
   809     // The principal of the initial about:blank document gets set up in
   810     // nsWindowWatcher::AddWindow. Make sure to call it. In the common case
   811     // this call already happened when the window was created, but
   812     // SetInitialPrincipalToSubject is safe to call multiple times.
   813     if (newWindow) {
   814       newWindow->SetInitialPrincipalToSubject();
   815     }
   816   }
   818   // If all windows should be private, make sure the new window is also
   819   // private.  Otherwise, see if the caller has explicitly requested a
   820   // private or non-private window.
   821   bool isPrivateBrowsingWindow =
   822       Preferences::GetBool("browser.privatebrowsing.autostart") ||
   823       (!!(chromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) &&
   824        !(chromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW));
   826   // Otherwise, propagate the privacy status of the parent window, if
   827   // available, to the child.
   828   if (!isPrivateBrowsingWindow &&
   829       !(chromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW)) {
   830     nsCOMPtr<nsIDocShellTreeItem> parentItem;
   831     GetWindowTreeItem(aParent, getter_AddRefs(parentItem));
   832     nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(parentItem);
   833     if (parentContext) {
   834       isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing();
   835     }
   836   }
   838   // We rely on CalculateChromeFlags to decide whether remote (out-of-process)
   839   // tabs should be used.
   840   bool isRemoteWindow =
   841     !!(chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
   843   if (isNewToplevelWindow) {
   844     nsCOMPtr<nsIDocShellTreeItem> childRoot;
   845     newDocShellItem->GetRootTreeItem(getter_AddRefs(childRoot));
   846     nsCOMPtr<nsILoadContext> childContext = do_QueryInterface(childRoot);
   847     if (childContext) {
   848       childContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
   849       childContext->SetRemoteTabs(isRemoteWindow);
   850     }
   851   } else if (windowIsNew) {
   852     nsCOMPtr<nsILoadContext> childContext = do_QueryInterface(newDocShellItem);
   853     if (childContext) {
   854       childContext->SetPrivateBrowsing(isPrivateBrowsingWindow);
   855       childContext->SetRemoteTabs(isRemoteWindow);
   856     }
   857   }
   859   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
   860   if (uriToLoad && aNavigate) { // get the script principal and pass it to docshell
   862     // The historical ordering of attempts here is:
   863     // (1) Stack-top cx.
   864     // (2) cx associated with aParent.
   865     // (3) Safe JSContext.
   866     //
   867     // We could just use an AutoJSContext here if it weren't for (2), which
   868     // is probably nonsensical anyway. But we preserve the old semantics for
   869     // now, because this is part of a large patch where we don't want to risk
   870     // subtle behavioral modifications.
   871     cx = nsContentUtils::GetCurrentJSContext();
   872     nsCxPusher pusher;
   873     if (!cx) {
   874       cx = GetJSContextFromWindow(aParent);
   875       if (!cx)
   876         cx = nsContentUtils::GetSafeJSContext();
   877       pusher.Push(cx);
   878     }
   880     newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
   881     NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
   883     if (subjectPrincipal) {
   884       loadInfo->SetOwner(subjectPrincipal);
   885     }
   887     // Set the new window's referrer from the calling context's document:
   889     // get its document, if any
   890     JSContext* ccx = nsContentUtils::GetCurrentJSContext();
   891     if (ccx) {
   892       nsIScriptGlobalObject *sgo = nsJSUtils::GetDynamicScriptGlobal(ccx);
   894       nsCOMPtr<nsPIDOMWindow> w(do_QueryInterface(sgo));
   895       if (w) {
   896         /* use the URL from the *extant* document, if any. The usual accessor
   897            GetDocument will synchronously create an about:blank document if
   898            it has no better answer, and we only care about a real document.
   899            Also using GetDocument to force document creation seems to
   900            screw up focus in the hidden window; see bug 36016.
   901         */
   902         nsCOMPtr<nsIDocument> doc = w->GetExtantDoc();
   903         if (doc) {
   904           // Set the referrer
   905           loadInfo->SetReferrer(doc->GetDocumentURI());
   906         }
   907       }
   908     }
   909   }
   911   if (isNewToplevelWindow) {
   912     // Notify observers that the window is open and ready.
   913     // The window has not yet started to load a document.
   914     nsCOMPtr<nsIObserverService> obsSvc =
   915       mozilla::services::GetObserverService();
   916     if (obsSvc)
   917       obsSvc->NotifyObservers(*_retval, "toplevel-window-ready", nullptr);
   918   }
   920   if (uriToLoad && aNavigate) {
   921     newDocShell->LoadURI(uriToLoad,
   922                          loadInfo,
   923                          windowIsNew
   924                            ? static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD)
   925                            : static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_NONE),
   926                          true);
   927   }
   929   // Copy the current session storage for the current domain.
   930   if (subjectPrincipal && parentDocShell) {
   931     nsCOMPtr<nsIDOMStorageManager> parentStorageManager = do_QueryInterface(parentDocShell);
   932     nsCOMPtr<nsIDOMStorageManager> newStorageManager = do_QueryInterface(newDocShell);
   934     if (parentStorageManager && newStorageManager) {
   935       nsCOMPtr<nsIDOMStorage> storage;
   936       parentStorageManager->GetStorageForFirstParty(uriToLoad, subjectPrincipal,
   937                              isPrivateBrowsingWindow, getter_AddRefs(storage));
   938       if (storage)
   939         newStorageManager->CloneStorage(storage);
   940     }
   941   }
   943   if (isNewToplevelWindow)
   944     SizeOpenedDocShellItem(newDocShellItem, aParent, sizeSpec);
   946   // XXXbz isn't windowIsModal always true when windowIsModalContentDialog?
   947   if (windowIsModal || windowIsModalContentDialog) {
   948     nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
   949     newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner));
   950     nsCOMPtr<nsIWebBrowserChrome> newChrome(do_GetInterface(newTreeOwner));
   952     // Throw an exception here if no web browser chrome is available,
   953     // we need that to show a modal window.
   954     NS_ENSURE_TRUE(newChrome, NS_ERROR_NOT_AVAILABLE);
   956     // Dispatch dialog events etc, but we only want to do that if
   957     // we're opening a modal content window (the helper classes are
   958     // no-ops if given no window), for chrome dialogs we don't want to
   959     // do any of that (it's done elsewhere for us).
   960     // Make sure we maintain the state on an outer window, because
   961     // that's where it lives; inner windows assert if you try to
   962     // maintain the state on them.
   963     nsAutoWindowStateHelper windowStateHelper(
   964       parentWindow ? parentWindow->GetOuterWindow() : nullptr);
   966     if (!windowStateHelper.DefaultEnabled()) {
   967       // Default to cancel not opening the modal window.
   968       NS_RELEASE(*_retval);
   970       return NS_OK;
   971     }
   974     if (!newWindowShouldBeModal && parentIsModal) {
   975       nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(newTreeOwner));
   976       if (parentWindow) {
   977         nsCOMPtr<nsIWidget> parentWidget;
   978         parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
   979         if (parentWidget) {
   980           parentWidget->SetModal(true);
   981         }
   982       }
   983     } else { 
   984       // Reset popup state while opening a modal dialog, and firing
   985       // events about the dialog, to prevent the current state from
   986       // being active the whole time a modal dialog is open.
   987       nsAutoPopupStatePusher popupStatePusher(openAbused);
   989       newChrome->ShowAsModal();
   990     }
   991   }
   993   return NS_OK;
   994 }
   996 NS_IMETHODIMP
   997 nsWindowWatcher::RegisterNotification(nsIObserver *aObserver)
   998 {
   999   // just a convenience method; it delegates to nsIObserverService
  1001   if (!aObserver)
  1002     return NS_ERROR_INVALID_ARG;
  1004   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1005   if (!os)
  1006     return NS_ERROR_FAILURE;
  1008   nsresult rv = os->AddObserver(aObserver, "domwindowopened", false);
  1009   if (NS_SUCCEEDED(rv))
  1010     rv = os->AddObserver(aObserver, "domwindowclosed", false);
  1012   return rv;
  1015 NS_IMETHODIMP
  1016 nsWindowWatcher::UnregisterNotification(nsIObserver *aObserver)
  1018   // just a convenience method; it delegates to nsIObserverService
  1020   if (!aObserver)
  1021     return NS_ERROR_INVALID_ARG;
  1023   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1024   if (!os)
  1025     return NS_ERROR_FAILURE;
  1027   os->RemoveObserver(aObserver, "domwindowopened");
  1028   os->RemoveObserver(aObserver, "domwindowclosed");
  1030   return NS_OK;
  1033 NS_IMETHODIMP
  1034 nsWindowWatcher::GetWindowEnumerator(nsISimpleEnumerator** _retval)
  1036   if (!_retval)
  1037     return NS_ERROR_INVALID_ARG;
  1039   MutexAutoLock lock(mListLock);
  1040   nsWatcherWindowEnumerator *enumerator = new nsWatcherWindowEnumerator(this);
  1041   if (enumerator)
  1042     return CallQueryInterface(enumerator, _retval);
  1044   return NS_ERROR_OUT_OF_MEMORY;
  1047 NS_IMETHODIMP
  1048 nsWindowWatcher::GetNewPrompter(nsIDOMWindow *aParent, nsIPrompt **_retval)
  1050   // This is for backwards compat only. Callers should just use the prompt service directly.
  1051   nsresult rv;
  1052   nsCOMPtr<nsIPromptFactory> factory = do_GetService("@mozilla.org/prompter;1", &rv);
  1053   NS_ENSURE_SUCCESS(rv, rv);
  1054   return factory->GetPrompt(aParent, NS_GET_IID(nsIPrompt), reinterpret_cast<void**>(_retval));
  1057 NS_IMETHODIMP
  1058 nsWindowWatcher::GetNewAuthPrompter(nsIDOMWindow *aParent, nsIAuthPrompt **_retval)
  1060   // This is for backwards compat only. Callers should just use the prompt service directly.
  1061   nsresult rv;
  1062   nsCOMPtr<nsIPromptFactory> factory = do_GetService("@mozilla.org/prompter;1", &rv);
  1063   NS_ENSURE_SUCCESS(rv, rv);
  1064   return factory->GetPrompt(aParent, NS_GET_IID(nsIAuthPrompt), reinterpret_cast<void**>(_retval));
  1067 NS_IMETHODIMP
  1068 nsWindowWatcher::GetPrompt(nsIDOMWindow *aParent, const nsIID& aIID,
  1069                            void **_retval)
  1071   // This is for backwards compat only. Callers should just use the prompt service directly.
  1072   nsresult rv;
  1073   nsCOMPtr<nsIPromptFactory> factory = do_GetService("@mozilla.org/prompter;1", &rv);
  1074   NS_ENSURE_SUCCESS(rv, rv);
  1075   rv = factory->GetPrompt(aParent, aIID, _retval);
  1077   // Allow for an embedding implementation to not support nsIAuthPrompt2.
  1078   if (rv == NS_NOINTERFACE && aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
  1079     nsCOMPtr<nsIAuthPrompt> oldPrompt;
  1080     rv = factory->GetPrompt(aParent,
  1081                             NS_GET_IID(nsIAuthPrompt),
  1082                             getter_AddRefs(oldPrompt));
  1083     NS_ENSURE_SUCCESS(rv, rv);
  1085     NS_WrapAuthPrompt(oldPrompt, reinterpret_cast<nsIAuthPrompt2**>(_retval));
  1086     if (!*_retval)
  1087       rv = NS_ERROR_NOT_AVAILABLE;
  1089   return rv;
  1092 NS_IMETHODIMP
  1093 nsWindowWatcher::SetWindowCreator(nsIWindowCreator *creator)
  1095   mWindowCreator = creator; // it's an nsCOMPtr, so this is an ownership ref
  1096   return NS_OK;
  1099 NS_IMETHODIMP
  1100 nsWindowWatcher::HasWindowCreator(bool *result)
  1102   *result = mWindowCreator;
  1103   return NS_OK;
  1106 NS_IMETHODIMP
  1107 nsWindowWatcher::GetActiveWindow(nsIDOMWindow **aActiveWindow)
  1109   *aActiveWindow = nullptr;
  1110   nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
  1111   if (fm)
  1112     return fm->GetActiveWindow(aActiveWindow);
  1113   return NS_OK;
  1116 NS_IMETHODIMP
  1117 nsWindowWatcher::SetActiveWindow(nsIDOMWindow *aActiveWindow)
  1119   nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
  1120   if (fm)
  1121     return fm->SetActiveWindow(aActiveWindow);
  1122   return NS_OK;
  1125 NS_IMETHODIMP
  1126 nsWindowWatcher::AddWindow(nsIDOMWindow *aWindow, nsIWebBrowserChrome *aChrome)
  1128   if (!aWindow)
  1129     return NS_ERROR_INVALID_ARG;
  1131 #ifdef DEBUG
  1133     nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aWindow));
  1135     NS_ASSERTION(win->IsOuterWindow(),
  1136                  "Uh, the active window must be an outer window!");
  1138 #endif
  1141     nsWatcherWindowEntry *info;
  1142     MutexAutoLock lock(mListLock);
  1144     // if we already have an entry for this window, adjust
  1145     // its chrome mapping and return
  1146     info = FindWindowEntry(aWindow);
  1147     if (info) {
  1148       nsCOMPtr<nsISupportsWeakReference> supportsweak(do_QueryInterface(aChrome));
  1149       if (supportsweak) {
  1150         supportsweak->GetWeakReference(getter_AddRefs(info->mChromeWeak));
  1151       } else {
  1152         info->mChrome = aChrome;
  1153         info->mChromeWeak = 0;
  1155       return NS_OK;
  1158     // create a window info struct and add it to the list of windows
  1159     info = new nsWatcherWindowEntry(aWindow, aChrome);
  1160     if (!info)
  1161       return NS_ERROR_OUT_OF_MEMORY;
  1163     if (mOldestWindow)
  1164       info->InsertAfter(mOldestWindow->mOlder);
  1165     else
  1166       mOldestWindow = info;
  1167   } // leave the mListLock
  1169   // a window being added to us signifies a newly opened window.
  1170   // send notifications.
  1171   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1172   if (!os)
  1173     return NS_ERROR_FAILURE;
  1175   nsCOMPtr<nsISupports> domwin(do_QueryInterface(aWindow));
  1176   return os->NotifyObservers(domwin, "domwindowopened", 0);
  1179 NS_IMETHODIMP
  1180 nsWindowWatcher::RemoveWindow(nsIDOMWindow *aWindow)
  1182   // find the corresponding nsWatcherWindowEntry, remove it
  1184   if (!aWindow)
  1185     return NS_ERROR_INVALID_ARG;
  1187   nsWatcherWindowEntry *info = FindWindowEntry(aWindow);
  1188   if (info) {
  1189     RemoveWindow(info);
  1190     return NS_OK;
  1192   NS_WARNING("requested removal of nonexistent window");
  1193   return NS_ERROR_INVALID_ARG;
  1196 nsWatcherWindowEntry *
  1197 nsWindowWatcher::FindWindowEntry(nsIDOMWindow *aWindow)
  1199   // find the corresponding nsWatcherWindowEntry
  1200   nsWatcherWindowEntry *info,
  1201                        *listEnd;
  1202 #ifdef USEWEAKREFS
  1203   nsresult    rv;
  1204   bool        found;
  1205 #endif
  1207   info = mOldestWindow;
  1208   listEnd = 0;
  1209 #ifdef USEWEAKREFS
  1210   rv = NS_OK;
  1211   found = false;
  1212   while (info != listEnd && NS_SUCCEEDED(rv)) {
  1213     nsCOMPtr<nsIDOMWindow> infoWindow(do_QueryReferent(info->mWindow));
  1214     if (!infoWindow) { // clean up dangling reference, while we're here
  1215       rv = RemoveWindow(info);
  1217     else if (infoWindow.get() == aWindow)
  1218       return info;
  1220     info = info->mYounger;
  1221     listEnd = mOldestWindow;
  1223   return 0;
  1224 #else
  1225   while (info != listEnd) {
  1226     if (info->mWindow == aWindow)
  1227       return info;
  1228     info = info->mYounger;
  1229     listEnd = mOldestWindow;
  1231   return 0;
  1232 #endif
  1235 nsresult nsWindowWatcher::RemoveWindow(nsWatcherWindowEntry *inInfo)
  1237   uint32_t  ctr,
  1238             count = mEnumeratorList.Length();
  1241     // notify the enumerators
  1242     MutexAutoLock lock(mListLock);
  1243     for (ctr = 0; ctr < count; ++ctr) 
  1244       mEnumeratorList[ctr]->WindowRemoved(inInfo);
  1246     // remove the element from the list
  1247     if (inInfo == mOldestWindow)
  1248       mOldestWindow = inInfo->mYounger == mOldestWindow ? 0 : inInfo->mYounger;
  1249     inInfo->Unlink();
  1252   // a window being removed from us signifies a newly closed window.
  1253   // send notifications.
  1254   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  1255   if (os) {
  1256 #ifdef USEWEAKREFS
  1257     nsCOMPtr<nsISupports> domwin(do_QueryReferent(inInfo->mWindow));
  1258     if (domwin)
  1259       os->NotifyObservers(domwin, "domwindowclosed", 0);
  1260     // else bummer. since the window is gone, there's nothing to notify with.
  1261 #else
  1262     nsCOMPtr<nsISupports> domwin(do_QueryInterface(inInfo->mWindow));
  1263     os->NotifyObservers(domwin, "domwindowclosed", 0);
  1264 #endif
  1267   delete inInfo;
  1268   return NS_OK;
  1271 NS_IMETHODIMP
  1272 nsWindowWatcher::GetChromeForWindow(nsIDOMWindow *aWindow, nsIWebBrowserChrome **_retval)
  1274   if (!aWindow || !_retval)
  1275     return NS_ERROR_INVALID_ARG;
  1276   *_retval = 0;
  1278   MutexAutoLock lock(mListLock);
  1279   nsWatcherWindowEntry *info = FindWindowEntry(aWindow);
  1280   if (info) {
  1281     if (info->mChromeWeak != nullptr) {
  1282       return info->mChromeWeak->
  1283                             QueryReferent(NS_GET_IID(nsIWebBrowserChrome),
  1284                                           reinterpret_cast<void**>(_retval));
  1286     *_retval = info->mChrome;
  1287     NS_IF_ADDREF(*_retval);
  1289   return NS_OK;
  1292 NS_IMETHODIMP
  1293 nsWindowWatcher::GetWindowByName(const char16_t *aTargetName, 
  1294                                  nsIDOMWindow *aCurrentWindow,
  1295                                  nsIDOMWindow **aResult)
  1297   if (!aResult) {
  1298     return NS_ERROR_INVALID_ARG;
  1301   *aResult = nullptr;
  1303   nsCOMPtr<nsIDocShellTreeItem> treeItem;
  1305   nsCOMPtr<nsIDocShellTreeItem> startItem;
  1306   GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
  1307   if (startItem) {
  1308     // Note: original requestor is null here, per idl comments
  1309     startItem->FindItemWithName(aTargetName, nullptr, nullptr,
  1310                                 getter_AddRefs(treeItem));
  1312   else {
  1313     // Note: original requestor is null here, per idl comments
  1314     FindItemWithName(aTargetName, nullptr, nullptr, getter_AddRefs(treeItem));
  1317   nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(treeItem);
  1318   domWindow.swap(*aResult);
  1320   return NS_OK;
  1323 bool
  1324 nsWindowWatcher::AddEnumerator(nsWatcherWindowEnumerator* inEnumerator)
  1326   // (requires a lock; assumes it's called by someone holding the lock)
  1327   return mEnumeratorList.AppendElement(inEnumerator) != nullptr;
  1330 bool
  1331 nsWindowWatcher::RemoveEnumerator(nsWatcherWindowEnumerator* inEnumerator)
  1333   // (requires a lock; assumes it's called by someone holding the lock)
  1334   return mEnumeratorList.RemoveElement(inEnumerator);
  1337 nsresult
  1338 nsWindowWatcher::URIfromURL(const char *aURL,
  1339                             nsIDOMWindow *aParent,
  1340                             nsIURI **aURI)
  1342   nsCOMPtr<nsIDOMWindow> baseWindow;
  1344   /* build the URI relative to the calling JS Context, if any.
  1345      (note this is the same context used to make the security check
  1346      in nsGlobalWindow.cpp.) */
  1347   JSContext *cx = nsContentUtils::GetCurrentJSContext();
  1348   if (cx) {
  1349     nsIScriptContext *scriptcx = nsJSUtils::GetDynamicScriptContext(cx);
  1350     if (scriptcx) {
  1351       baseWindow = do_QueryInterface(scriptcx->GetGlobalObject());
  1355   // failing that, build it relative to the parent window, if possible
  1356   if (!baseWindow)
  1357     baseWindow = aParent;
  1359   // failing that, use the given URL unmodified. It had better not be relative.
  1361   nsIURI *baseURI = nullptr;
  1363   // get baseWindow's document URI
  1364   if (baseWindow) {
  1365     nsCOMPtr<nsIDOMDocument> domDoc;
  1366     baseWindow->GetDocument(getter_AddRefs(domDoc));
  1367     if (domDoc) {
  1368       nsCOMPtr<nsIDocument> doc;
  1369       doc = do_QueryInterface(domDoc);
  1370       if (doc) {
  1371         baseURI = doc->GetDocBaseURI();
  1376   // build and return the absolute URI
  1377   return NS_NewURI(aURI, aURL, baseURI);
  1380 #define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag)               \
  1381     prefBranch->GetBoolPref(feature, &forceEnable);               \
  1382     if (forceEnable && !(aDialog && isCallerChrome) &&            \
  1383         !(isCallerChrome && aHasChromeParent) && !aChromeURL) {   \
  1384       chromeFlags |= flag;                                        \
  1385     } else {                                                      \
  1386       chromeFlags |= WinHasOption(aFeatures, feature,             \
  1387                                   0, &presenceFlag)               \
  1388                      ? flag : 0;                                  \
  1391 /**
  1392  * Calculate the chrome bitmask from a string list of features.
  1393  * @param aParent the opener window
  1394  * @param aFeatures a string containing a list of named chrome features
  1395  * @param aNullFeatures true if aFeatures was a null pointer (which fact
  1396  *                      is lost by its conversion to a string in the caller)
  1397  * @param aDialog affects the assumptions made about unnamed features
  1398  * @return the chrome bitmask
  1399  */
  1400 // static
  1401 uint32_t nsWindowWatcher::CalculateChromeFlags(nsIDOMWindow *aParent,
  1402                                                const char *aFeatures,
  1403                                                bool aFeaturesSpecified,
  1404                                                bool aDialog,
  1405                                                bool aChromeURL,
  1406                                                bool aHasChromeParent)
  1408    if(!aFeaturesSpecified || !aFeatures) {
  1409       if(aDialog)
  1410          return nsIWebBrowserChrome::CHROME_ALL | 
  1411                 nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | 
  1412                 nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
  1413       else
  1414          return nsIWebBrowserChrome::CHROME_ALL;
  1417   /* This function has become complicated since browser windows and
  1418      dialogs diverged. The difference is, browser windows assume all
  1419      chrome not explicitly mentioned is off, if the features string
  1420      is not null. Exceptions are some OS border chrome new with Mozilla.
  1421      Dialogs interpret a (mostly) empty features string to mean
  1422      "OS's choice," and also support an "all" flag explicitly disallowed
  1423      in the standards-compliant window.(normal)open. */
  1425   uint32_t chromeFlags = 0;
  1426   bool presenceFlag = false;
  1428   chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS;
  1429   if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag))
  1430     chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
  1432   /* Next, allow explicitly named options to override the initial settings */
  1434   bool isCallerChrome = nsContentUtils::IsCallerChrome();
  1436   // Determine whether the window is a private browsing window
  1437   if (isCallerChrome) {
  1438     chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag) ?
  1439       nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW : 0;
  1440     chromeFlags |= WinHasOption(aFeatures, "non-private", 0, &presenceFlag) ?
  1441       nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW : 0;
  1444   // Determine whether the window should have remote tabs.
  1445   if (isCallerChrome) {
  1446     bool remote;
  1447     if (Preferences::GetBool("browser.tabs.remote.autostart")) {
  1448       remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag);
  1449     } else {
  1450       remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag);
  1452     if (remote) {
  1453       chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
  1457   nsresult rv;
  1459   nsCOMPtr<nsIPrefBranch> prefBranch;
  1460   nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
  1461   NS_ENSURE_SUCCESS(rv, true);
  1463   rv = prefs->GetBranch("dom.disable_window_open_feature.", getter_AddRefs(prefBranch));
  1464   NS_ENSURE_SUCCESS(rv, true);
  1466   bool forceEnable = false;
  1468   NS_CALCULATE_CHROME_FLAG_FOR("titlebar",
  1469                                nsIWebBrowserChrome::CHROME_TITLEBAR);
  1470   NS_CALCULATE_CHROME_FLAG_FOR("close",
  1471                                nsIWebBrowserChrome::CHROME_WINDOW_CLOSE);
  1472   NS_CALCULATE_CHROME_FLAG_FOR("toolbar",
  1473                                nsIWebBrowserChrome::CHROME_TOOLBAR);
  1474   NS_CALCULATE_CHROME_FLAG_FOR("location",
  1475                                nsIWebBrowserChrome::CHROME_LOCATIONBAR);
  1476   NS_CALCULATE_CHROME_FLAG_FOR("personalbar",
  1477                                nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
  1478   NS_CALCULATE_CHROME_FLAG_FOR("status",
  1479                                nsIWebBrowserChrome::CHROME_STATUSBAR);
  1480   NS_CALCULATE_CHROME_FLAG_FOR("menubar",
  1481                                nsIWebBrowserChrome::CHROME_MENUBAR);
  1482   NS_CALCULATE_CHROME_FLAG_FOR("scrollbars",
  1483                                nsIWebBrowserChrome::CHROME_SCROLLBARS);
  1484   NS_CALCULATE_CHROME_FLAG_FOR("resizable",
  1485                                nsIWebBrowserChrome::CHROME_WINDOW_RESIZE);
  1486   NS_CALCULATE_CHROME_FLAG_FOR("minimizable",
  1487                                nsIWebBrowserChrome::CHROME_WINDOW_MIN);
  1489   chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag)
  1490                  ? nsIWebBrowserChrome::CHROME_WINDOW_POPUP : 0; 
  1492   /* OK.
  1493      Normal browser windows, in spite of a stated pattern of turning off
  1494      all chrome not mentioned explicitly, will want the new OS chrome (window
  1495      borders, titlebars, closebox) on, unless explicitly turned off.
  1496      Dialogs, on the other hand, take the absence of any explicit settings
  1497      to mean "OS' choice." */
  1499   // default titlebar and closebox to "on," if not mentioned at all
  1500   if (!(chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)) {
  1501     if (!PL_strcasestr(aFeatures, "titlebar"))
  1502       chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
  1503     if (!PL_strcasestr(aFeatures, "close"))
  1504       chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
  1507   if (aDialog && !presenceFlag)
  1508     chromeFlags = nsIWebBrowserChrome::CHROME_DEFAULT;
  1510   /* Finally, once all the above normal chrome has been divined, deal
  1511      with the features that are more operating hints than appearance
  1512      instructions. (Note modality implies dependence.) */
  1514   if (WinHasOption(aFeatures, "alwaysLowered", 0, nullptr) ||
  1515       WinHasOption(aFeatures, "z-lock", 0, nullptr))
  1516     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
  1517   else if (WinHasOption(aFeatures, "alwaysRaised", 0, nullptr))
  1518     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
  1520   chromeFlags |= WinHasOption(aFeatures, "macsuppressanimation", 0, nullptr) ?
  1521     nsIWebBrowserChrome::CHROME_MAC_SUPPRESS_ANIMATION : 0;
  1523   chromeFlags |= WinHasOption(aFeatures, "chrome", 0, nullptr) ?
  1524     nsIWebBrowserChrome::CHROME_OPENAS_CHROME : 0;
  1525   chromeFlags |= WinHasOption(aFeatures, "extrachrome", 0, nullptr) ?
  1526     nsIWebBrowserChrome::CHROME_EXTRA : 0;
  1527   chromeFlags |= WinHasOption(aFeatures, "centerscreen", 0, nullptr) ?
  1528     nsIWebBrowserChrome::CHROME_CENTER_SCREEN : 0;
  1529   chromeFlags |= WinHasOption(aFeatures, "dependent", 0, nullptr) ?
  1530     nsIWebBrowserChrome::CHROME_DEPENDENT : 0;
  1531   chromeFlags |= WinHasOption(aFeatures, "modal", 0, nullptr) ?
  1532     (nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT) : 0;
  1534   /* On mobile we want to ignore the dialog window feature, since the mobile UI
  1535      does not provide any affordance for dialog windows. This does not interfere
  1536      with dialog windows created through openDialog. */
  1537   bool disableDialogFeature = false;
  1538   nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
  1539   branch->GetBoolPref("dom.disable_window_open_dialog_feature", &disableDialogFeature);
  1541   bool isFullScreen = false;
  1542   if (aParent) {
  1543     aParent->GetFullScreen(&isFullScreen);
  1545   if (isFullScreen && !isCallerChrome) {
  1546     // If the parent window is in fullscreen & the caller context is content,
  1547     // dialog feature is disabled. (see bug 803675)
  1548     disableDialogFeature = true;
  1551   if (!disableDialogFeature) {
  1552     chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nullptr) ?
  1553       nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0;
  1556   /* and dialogs need to have the last word. assume dialogs are dialogs,
  1557      and opened as chrome, unless explicitly told otherwise. */
  1558   if (aDialog) {
  1559     if (!PL_strcasestr(aFeatures, "dialog"))
  1560       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
  1561     if (!PL_strcasestr(aFeatures, "chrome"))
  1562       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
  1565   /* missing
  1566      chromeFlags->copy_history
  1567    */
  1569   // Check security state for use in determing window dimensions
  1570   if (!isCallerChrome || !aHasChromeParent) {
  1571     // If priv check fails (or if we're called from chrome, but the
  1572     // parent is not a chrome window), set all elements to minimum
  1573     // reqs., else leave them alone.
  1574     chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
  1575     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
  1576     chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
  1577     chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
  1578     chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_POPUP;
  1579     /* Untrusted script is allowed to pose modal windows with a chrome
  1580        scheme. This check could stand to be better. But it effectively
  1581        prevents untrusted script from opening modal windows in general
  1582        while still allowing alerts and the like. */
  1583     if (!aChromeURL)
  1584       chromeFlags &= ~(nsIWebBrowserChrome::CHROME_MODAL |
  1585                        nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
  1588   if (!(chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)) {
  1589     // Remove the dependent flag if we're not opening as chrome
  1590     chromeFlags &= ~nsIWebBrowserChrome::CHROME_DEPENDENT;
  1593   // Disable CHROME_OPENAS_DIALOG if the window is inside <iframe mozbrowser>.
  1594   // It's up to the embedder to interpret what dialog=1 means.
  1595   nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
  1596   if (docshell && docshell->GetIsInBrowserOrApp()) {
  1597     chromeFlags &= ~nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
  1600   return chromeFlags;
  1603 // static
  1604 int32_t
  1605 nsWindowWatcher::WinHasOption(const char *aOptions, const char *aName,
  1606                               int32_t aDefault, bool *aPresenceFlag)
  1608   if (!aOptions)
  1609     return 0;
  1611   char *comma, *equal;
  1612   int32_t found = 0;
  1614 #ifdef DEBUG
  1615     nsAutoCString options(aOptions);
  1616     NS_ASSERTION(options.FindCharInSet(" \n\r\t") == kNotFound, 
  1617                   "There should be no whitespace in this string!");
  1618 #endif
  1620   while (true) {
  1621     comma = PL_strchr(aOptions, ',');
  1622     if (comma)
  1623       *comma = '\0';
  1624     equal = PL_strchr(aOptions, '=');
  1625     if (equal)
  1626       *equal = '\0';
  1627     if (nsCRT::strcasecmp(aOptions, aName) == 0) {
  1628       if (aPresenceFlag)
  1629         *aPresenceFlag = true;
  1630       if (equal)
  1631         if (*(equal + 1) == '*')
  1632           found = aDefault;
  1633         else if (nsCRT::strcasecmp(equal + 1, "yes") == 0)
  1634           found = 1;
  1635         else
  1636           found = atoi(equal + 1);
  1637       else
  1638         found = 1;
  1640     if (equal)
  1641       *equal = '=';
  1642     if (comma)
  1643       *comma = ',';
  1644     if (found || !comma)
  1645       break;
  1646     aOptions = comma + 1;
  1648   return found;
  1651 /* try to find an nsIDocShellTreeItem with the given name in any
  1652    known open window. a failure to find the item will not
  1653    necessarily return a failure method value. check aFoundItem.
  1654 */
  1655 NS_IMETHODIMP
  1656 nsWindowWatcher::FindItemWithName(const char16_t* aName,
  1657                                   nsIDocShellTreeItem* aRequestor,
  1658                                   nsIDocShellTreeItem* aOriginalRequestor,
  1659                                   nsIDocShellTreeItem** aFoundItem)
  1661   *aFoundItem = 0;
  1663   /* special cases */
  1664   if(!aName || !*aName)
  1665     return NS_OK;
  1667   nsDependentString name(aName);
  1669   nsCOMPtr<nsISimpleEnumerator> windows;
  1670   GetWindowEnumerator(getter_AddRefs(windows));
  1671   if (!windows)
  1672     return NS_ERROR_FAILURE;
  1674   bool     more;
  1675   nsresult rv = NS_OK;
  1677   do {
  1678     windows->HasMoreElements(&more);
  1679     if (!more)
  1680       break;
  1681     nsCOMPtr<nsISupports> nextSupWindow;
  1682     windows->GetNext(getter_AddRefs(nextSupWindow));
  1683     nsCOMPtr<nsIDOMWindow> nextWindow(do_QueryInterface(nextSupWindow));
  1684     if (nextWindow) {
  1685       nsCOMPtr<nsIDocShellTreeItem> treeItem;
  1686       GetWindowTreeItem(nextWindow, getter_AddRefs(treeItem));
  1687       if (treeItem) {
  1688         // Get the root tree item of same type, since roots are the only
  1689         // things that call into the treeowner to look for named items.
  1690         nsCOMPtr<nsIDocShellTreeItem> root;
  1691         treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
  1692         NS_ASSERTION(root, "Must have root tree item of same type");
  1693         // Make sure not to call back into aRequestor
  1694         if (root != aRequestor) {
  1695           // Get the tree owner so we can pass it in as the requestor so
  1696           // the child knows not to call back up, since we're walking
  1697           // all windows already.
  1698           nsCOMPtr<nsIDocShellTreeOwner> rootOwner;
  1699           // Note: if we have no aRequestor, then we want to also look for
  1700           // "special" window names, so pass a null requestor.  This will mean
  1701           // that the treeitem calls back up to us, effectively (with a
  1702           // non-null aRequestor), so break the loop immediately after the
  1703           // call in that case.
  1704           if (aRequestor) {
  1705             root->GetTreeOwner(getter_AddRefs(rootOwner));
  1707           rv = root->FindItemWithName(aName, rootOwner, aOriginalRequestor,
  1708                                       aFoundItem);
  1709           if (NS_FAILED(rv) || *aFoundItem || !aRequestor)
  1710             break;
  1714   } while(1);
  1716   return rv;
  1719 already_AddRefed<nsIDocShellTreeItem>
  1720 nsWindowWatcher::GetCallerTreeItem(nsIDocShellTreeItem* aParentItem)
  1722   JSContext *cx = nsContentUtils::GetCurrentJSContext();
  1723   nsCOMPtr<nsIDocShellTreeItem> callerItem;
  1725   if (cx) {
  1726     nsCOMPtr<nsIWebNavigation> callerWebNav =
  1727       do_GetInterface(nsJSUtils::GetDynamicScriptGlobal(cx));
  1729     callerItem = do_QueryInterface(callerWebNav);
  1732   if (!callerItem) {
  1733     callerItem = aParentItem;
  1736   return callerItem.forget();
  1739 already_AddRefed<nsIDOMWindow>
  1740 nsWindowWatcher::SafeGetWindowByName(const nsAString& aName,
  1741                                      nsIDOMWindow* aCurrentWindow)
  1743   nsCOMPtr<nsIDocShellTreeItem> startItem;
  1744   GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
  1746   nsCOMPtr<nsIDocShellTreeItem> callerItem = GetCallerTreeItem(startItem);
  1748   const nsAFlatString& flatName = PromiseFlatString(aName);
  1750   nsCOMPtr<nsIDocShellTreeItem> foundItem;
  1751   if (startItem) {
  1752     startItem->FindItemWithName(flatName.get(), nullptr, callerItem,
  1753                                 getter_AddRefs(foundItem));
  1755   else {
  1756     FindItemWithName(flatName.get(), nullptr, callerItem,
  1757                      getter_AddRefs(foundItem));
  1760   nsCOMPtr<nsIDOMWindow> foundWin = do_GetInterface(foundItem);
  1761   return foundWin.forget();
  1764 /* Fetch the nsIDOMWindow corresponding to the given nsIDocShellTreeItem.
  1765    This forces the creation of a script context, if one has not already
  1766    been created. Note it also sets the window's opener to the parent,
  1767    if applicable -- because it's just convenient, that's all. null aParent
  1768    is acceptable. */
  1769 nsresult
  1770 nsWindowWatcher::ReadyOpenedDocShellItem(nsIDocShellTreeItem *aOpenedItem,
  1771                                          nsIDOMWindow        *aParent,
  1772                                          bool                aWindowIsNew,
  1773                                          nsIDOMWindow        **aOpenedWindow)
  1775   nsresult rv = NS_ERROR_FAILURE;
  1777   *aOpenedWindow = 0;
  1778   nsCOMPtr<nsPIDOMWindow> piOpenedWindow(do_GetInterface(aOpenedItem));
  1779   if (piOpenedWindow) {
  1780     if (aParent) {
  1781       piOpenedWindow->SetOpenerWindow(aParent, aWindowIsNew); // damnit
  1783       if (aWindowIsNew) {
  1784 #ifdef DEBUG
  1785         // Assert that we're not loading things right now.  If we are, when
  1786         // that load completes it will clobber whatever principals we set up
  1787         // on this new window!
  1788         nsCOMPtr<nsIDocumentLoader> docloader =
  1789           do_QueryInterface(aOpenedItem);
  1790         NS_ASSERTION(docloader, "How can we not have a docloader here?");
  1792         nsCOMPtr<nsIChannel> chan;
  1793         docloader->GetDocumentChannel(getter_AddRefs(chan));
  1794         NS_ASSERTION(!chan, "Why is there a document channel?");
  1795 #endif
  1797         nsCOMPtr<nsIDocument> doc = piOpenedWindow->GetExtantDoc();
  1798         if (doc) {
  1799           doc->SetIsInitialDocument(true);
  1803     rv = CallQueryInterface(piOpenedWindow, aOpenedWindow);
  1805   return rv;
  1808 // static
  1809 void
  1810 nsWindowWatcher::CalcSizeSpec(const char* aFeatures, SizeSpec& aResult)
  1812   // Parse position spec, if any, from aFeatures
  1813   bool    present;
  1814   int32_t temp;
  1816   present = false;
  1817   if ((temp = WinHasOption(aFeatures, "left", 0, &present)) || present)
  1818     aResult.mLeft = temp;
  1819   else if ((temp = WinHasOption(aFeatures, "screenX", 0, &present)) || present)
  1820     aResult.mLeft = temp;
  1821   aResult.mLeftSpecified = present;
  1823   present = false;
  1824   if ((temp = WinHasOption(aFeatures, "top", 0, &present)) || present)
  1825     aResult.mTop = temp;
  1826   else if ((temp = WinHasOption(aFeatures, "screenY", 0, &present)) || present)
  1827     aResult.mTop = temp;
  1828   aResult.mTopSpecified = present;
  1830   // Parse size spec, if any. Chrome size overrides content size.
  1831   if ((temp = WinHasOption(aFeatures, "outerWidth", INT32_MIN, nullptr))) {
  1832     if (temp == INT32_MIN) {
  1833       aResult.mUseDefaultWidth = true;
  1835     else {
  1836       aResult.mOuterWidth = temp;
  1838     aResult.mOuterWidthSpecified = true;
  1839   } else if ((temp = WinHasOption(aFeatures, "width", INT32_MIN, nullptr)) ||
  1840              (temp = WinHasOption(aFeatures, "innerWidth", INT32_MIN,
  1841                                   nullptr))) {
  1842     if (temp == INT32_MIN) {
  1843       aResult.mUseDefaultWidth = true;
  1844     } else {
  1845       aResult.mInnerWidth = temp;
  1847     aResult.mInnerWidthSpecified = true;
  1850   if ((temp = WinHasOption(aFeatures, "outerHeight", INT32_MIN, nullptr))) {
  1851     if (temp == INT32_MIN) {
  1852       aResult.mUseDefaultHeight = true;
  1854     else {
  1855       aResult.mOuterHeight = temp;
  1857     aResult.mOuterHeightSpecified = true;
  1858   } else if ((temp = WinHasOption(aFeatures, "height", INT32_MIN,
  1859                                   nullptr)) ||
  1860              (temp = WinHasOption(aFeatures, "innerHeight", INT32_MIN,
  1861                                   nullptr))) {
  1862     if (temp == INT32_MIN) {
  1863       aResult.mUseDefaultHeight = true;
  1864     } else {
  1865       aResult.mInnerHeight = temp;
  1867     aResult.mInnerHeightSpecified = true;
  1871 /* Size and position the new window according to aSizeSpec. This method
  1872    is assumed to be called after the window has already been given
  1873    a default position and size; thus its current position and size are
  1874    accurate defaults. The new window is made visible at method end.
  1875 */
  1876 void
  1877 nsWindowWatcher::SizeOpenedDocShellItem(nsIDocShellTreeItem *aDocShellItem,
  1878                                         nsIDOMWindow *aParent,
  1879                                         const SizeSpec & aSizeSpec)
  1881   // position and size of window
  1882   int32_t left = 0,
  1883           top = 0,
  1884           width = 100,
  1885           height = 100;
  1886   // difference between chrome and content size
  1887   int32_t chromeWidth = 0,
  1888           chromeHeight = 0;
  1889   // whether the window size spec refers to chrome or content
  1890   bool    sizeChromeWidth = true,
  1891           sizeChromeHeight = true;
  1893   // get various interfaces for aDocShellItem, used throughout this method
  1894   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
  1895   aDocShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
  1896   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(treeOwner));
  1897   if (!treeOwnerAsWin) // we'll need this to actually size the docshell
  1898     return;
  1900   double openerZoom = 1.0;
  1901   if (aParent) {
  1902     nsCOMPtr<nsIDOMDocument> openerDoc;
  1903     aParent->GetDocument(getter_AddRefs(openerDoc));
  1904     if (openerDoc) {
  1905       nsCOMPtr<nsIDocument> doc = do_QueryInterface(openerDoc);
  1906       nsIPresShell* shell = doc->GetShell();
  1907       if (shell) {
  1908         nsPresContext* presContext = shell->GetPresContext();
  1909         if (presContext) {
  1910           openerZoom = presContext->GetFullZoom();
  1916   double scale;
  1917   treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
  1919   /* The current position and size will be unchanged if not specified
  1920      (and they fit entirely onscreen). Also, calculate the difference
  1921      between chrome and content sizes on aDocShellItem's window.
  1922      This latter point becomes important if chrome and content
  1923      specifications are mixed in aFeatures, and when bringing the window
  1924      back from too far off the right or bottom edges of the screen. */
  1926   treeOwnerAsWin->GetPositionAndSize(&left, &top, &width, &height);
  1927   left = NSToIntRound(left / scale);
  1928   top = NSToIntRound(top / scale);
  1929   width = NSToIntRound(width / scale);
  1930   height = NSToIntRound(height / scale);
  1931   { // scope shellWindow why not
  1932     nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(aDocShellItem));
  1933     if (shellWindow) {
  1934       int32_t cox, coy;
  1935       double shellScale;
  1936       shellWindow->GetSize(&cox, &coy);
  1937       shellWindow->GetUnscaledDevicePixelsPerCSSPixel(&shellScale);
  1938       chromeWidth = width - NSToIntRound(cox / shellScale);
  1939       chromeHeight = height - NSToIntRound(coy / shellScale);
  1943   // Set up left/top
  1944   if (aSizeSpec.mLeftSpecified) {
  1945     left = NSToIntRound(aSizeSpec.mLeft * openerZoom);
  1948   if (aSizeSpec.mTopSpecified) {
  1949     top = NSToIntRound(aSizeSpec.mTop * openerZoom);
  1952   // Set up width
  1953   if (aSizeSpec.mOuterWidthSpecified) {
  1954     if (!aSizeSpec.mUseDefaultWidth) {
  1955       width = NSToIntRound(aSizeSpec.mOuterWidth * openerZoom);
  1956     } // Else specified to default; just use our existing width
  1958   else if (aSizeSpec.mInnerWidthSpecified) {
  1959     sizeChromeWidth = false;
  1960     if (aSizeSpec.mUseDefaultWidth) {
  1961       width = width - chromeWidth;
  1962     } else {
  1963       width = NSToIntRound(aSizeSpec.mInnerWidth * openerZoom);
  1967   // Set up height
  1968   if (aSizeSpec.mOuterHeightSpecified) {
  1969     if (!aSizeSpec.mUseDefaultHeight) {
  1970       height = NSToIntRound(aSizeSpec.mOuterHeight * openerZoom);
  1971     } // Else specified to default; just use our existing height
  1973   else if (aSizeSpec.mInnerHeightSpecified) {
  1974     sizeChromeHeight = false;
  1975     if (aSizeSpec.mUseDefaultHeight) {
  1976       height = height - chromeHeight;
  1977     } else {
  1978       height = NSToIntRound(aSizeSpec.mInnerHeight * openerZoom);
  1982   bool positionSpecified = aSizeSpec.PositionSpecified();
  1984   // Check security state for use in determing window dimensions
  1985   bool enabled = false;
  1986   if (nsContentUtils::IsCallerChrome()) {
  1987     // Only enable special priveleges for chrome when chrome calls
  1988     // open() on a chrome window
  1989     nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(aParent));
  1990     enabled = !aParent || chromeWin;
  1993   if (!enabled) {
  1995     // Security check failed.  Ensure all args meet minimum reqs.
  1997     int32_t oldTop = top,
  1998             oldLeft = left;
  2000     // We'll also need the screen dimensions
  2001     nsCOMPtr<nsIScreen> screen;
  2002     nsCOMPtr<nsIScreenManager> screenMgr(do_GetService(
  2003                                          "@mozilla.org/gfx/screenmanager;1"));
  2004     if (screenMgr)
  2005       screenMgr->ScreenForRect(left, top, width, height,
  2006                                getter_AddRefs(screen));
  2007     if (screen) {
  2008       int32_t screenLeft, screenTop, screenWidth, screenHeight;
  2009       int32_t winWidth = width + (sizeChromeWidth ? 0 : chromeWidth),
  2010               winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
  2012       screen->GetAvailRectDisplayPix(&screenLeft, &screenTop,
  2013                                      &screenWidth, &screenHeight);
  2015       if (aSizeSpec.SizeSpecified()) {
  2016         /* Unlike position, force size out-of-bounds check only if
  2017            size actually was specified. Otherwise, intrinsically sized
  2018            windows are broken. */
  2019         if (height < 100) {
  2020           height = 100;
  2021           winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
  2023         if (winHeight > screenHeight) {
  2024           height = screenHeight - (sizeChromeHeight ? 0 : chromeHeight);
  2026         if (width < 100) {
  2027           width = 100;
  2028           winWidth = width + (sizeChromeWidth ? 0 : chromeWidth);
  2030         if (winWidth > screenWidth) {
  2031           width = screenWidth - (sizeChromeWidth ? 0 : chromeWidth);
  2035       if (left + winWidth > screenLeft + screenWidth || left + winWidth < left) {
  2036         left = screenLeft + screenWidth - winWidth;
  2038       if (left < screenLeft) {
  2039         left = screenLeft;
  2041       if (top + winHeight > screenTop + screenHeight || top + winHeight < top) {
  2042         top = screenTop + screenHeight - winHeight;
  2044       if (top < screenTop) {
  2045         top = screenTop;
  2047       if (top != oldTop || left != oldLeft) {
  2048         positionSpecified = true;
  2053   // size and position the window
  2055   if (positionSpecified) {
  2056     treeOwnerAsWin->SetPosition(left * scale, top * scale);
  2057     // moving the window may have changed its scale factor
  2058     treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
  2060   if (aSizeSpec.SizeSpecified()) {
  2061     /* Prefer to trust the interfaces, which think in terms of pure
  2062        chrome or content sizes. If we have a mix, use the chrome size
  2063        adjusted by the chrome/content differences calculated earlier. */
  2064     if (!sizeChromeWidth && !sizeChromeHeight) {
  2065       treeOwner->SizeShellTo(aDocShellItem, width * scale, height * scale);
  2067     else {
  2068       if (!sizeChromeWidth)
  2069         width += chromeWidth;
  2070       if (!sizeChromeHeight)
  2071         height += chromeHeight;
  2072       treeOwnerAsWin->SetSize(width * scale, height * scale, false);
  2075   treeOwnerAsWin->SetVisibility(true);
  2078 void
  2079 nsWindowWatcher::GetWindowTreeItem(nsIDOMWindow *inWindow,
  2080                                    nsIDocShellTreeItem **outTreeItem)
  2082   *outTreeItem = 0;
  2084   nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(inWindow));
  2085   if (window) {
  2086     nsIDocShell *docshell = window->GetDocShell();
  2087     if (docshell)
  2088       CallQueryInterface(docshell, outTreeItem);
  2092 void
  2093 nsWindowWatcher::GetWindowTreeOwner(nsIDOMWindow *inWindow,
  2094                                     nsIDocShellTreeOwner **outTreeOwner)
  2096   *outTreeOwner = 0;
  2098   nsCOMPtr<nsIDocShellTreeItem> treeItem;
  2099   GetWindowTreeItem(inWindow, getter_AddRefs(treeItem));
  2100   if (treeItem)
  2101     treeItem->GetTreeOwner(outTreeOwner);
  2104 JSContext *
  2105 nsWindowWatcher::GetJSContextFromWindow(nsIDOMWindow *aWindow)
  2107   JSContext *cx = 0;
  2109   if (aWindow) {
  2110     nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aWindow));
  2111     if (sgo) {
  2112       nsIScriptContext *scx = sgo->GetContext();
  2113       if (scx)
  2114         cx = scx->GetNativeContext();
  2116     /* (off-topic note:) the nsIScriptContext can be retrieved by
  2117     nsCOMPtr<nsIScriptContext> scx;
  2118     nsJSUtils::GetDynamicScriptContext(cx, getter_AddRefs(scx));
  2119     */
  2122   return cx;

mercurial