forked from mirrors/gecko-dev
		
	 7508fffd89
			
		
	
	
		7508fffd89
		
	
	
	
	
		
			
			The check was a bit too general it seems. Explicitly allow moving focus for link clicks and window.open(), which are the things we have tests for and care about moving focus. Differential Revision: https://phabricator.services.mozilla.com/D107039
		
			
				
	
	
		
			2527 lines
		
	
	
	
		
			87 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			2527 lines
		
	
	
	
		
			87 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | ||
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | ||
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | ||
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | ||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | ||
| 
 | ||
| #include "nsWindowWatcher.h"
 | ||
| #include "nsAutoWindowStateHelper.h"
 | ||
| 
 | ||
| #include "nsCRT.h"
 | ||
| #include "nsNetUtil.h"
 | ||
| #include "nsIAuthPrompt.h"
 | ||
| #include "nsIAuthPrompt2.h"
 | ||
| #include "nsISimpleEnumerator.h"
 | ||
| #include "nsIInterfaceRequestorUtils.h"
 | ||
| #include "nsJSUtils.h"
 | ||
| #include "plstr.h"
 | ||
| 
 | ||
| #include "nsDocShell.h"
 | ||
| #include "nsGlobalWindow.h"
 | ||
| #include "nsHashPropertyBag.h"
 | ||
| #include "nsIBaseWindow.h"
 | ||
| #include "nsIBrowserDOMWindow.h"
 | ||
| #include "nsIDocShell.h"
 | ||
| #include "nsDocShellLoadState.h"
 | ||
| #include "nsIDocShellTreeItem.h"
 | ||
| #include "nsIDocShellTreeOwner.h"
 | ||
| #include "mozilla/dom/BrowsingContext.h"
 | ||
| #include "mozilla/dom/BrowsingContextGroup.h"
 | ||
| #include "mozilla/dom/Document.h"
 | ||
| #include "mozilla/dom/DocumentInlines.h"
 | ||
| #include "nsIDOMChromeWindow.h"
 | ||
| #include "nsIPrompt.h"
 | ||
| #include "nsIScriptObjectPrincipal.h"
 | ||
| #include "nsIScreen.h"
 | ||
| #include "nsIScreenManager.h"
 | ||
| #include "nsIScriptContext.h"
 | ||
| #include "nsIObserverService.h"
 | ||
| #include "nsXPCOM.h"
 | ||
| #include "nsIURI.h"
 | ||
| #include "nsIWebBrowser.h"
 | ||
| #include "nsIWebBrowserChrome.h"
 | ||
| #include "nsIWebNavigation.h"
 | ||
| #include "nsIWindowCreator.h"
 | ||
| #include "nsIXULRuntime.h"
 | ||
| #include "nsPIDOMWindow.h"
 | ||
| #include "nsIWindowProvider.h"
 | ||
| #include "nsIMutableArray.h"
 | ||
| #include "nsIDOMStorageManager.h"
 | ||
| #include "nsIWidget.h"
 | ||
| #include "nsFocusManager.h"
 | ||
| #include "nsOpenWindowInfo.h"
 | ||
| #include "nsPresContext.h"
 | ||
| #include "nsContentUtils.h"
 | ||
| #include "nsIPrefBranch.h"
 | ||
| #include "nsIPrefService.h"
 | ||
| #include "nsSandboxFlags.h"
 | ||
| #include "nsSimpleEnumerator.h"
 | ||
| #include "mozilla/BasePrincipal.h"
 | ||
| #include "mozilla/CheckedInt.h"
 | ||
| #include "mozilla/NullPrincipal.h"
 | ||
| #include "mozilla/Preferences.h"
 | ||
| #include "mozilla/ResultExtensions.h"
 | ||
| #include "mozilla/StaticPrefs_full_screen_api.h"
 | ||
| #include "mozilla/dom/Element.h"
 | ||
| #include "mozilla/dom/Storage.h"
 | ||
| #include "mozilla/dom/ScriptSettings.h"
 | ||
| #include "mozilla/dom/BrowserParent.h"
 | ||
| #include "mozilla/dom/BrowserHost.h"
 | ||
| #include "mozilla/dom/DocGroup.h"
 | ||
| #include "mozilla/dom/SessionStorageManager.h"
 | ||
| #include "nsIAppWindow.h"
 | ||
| #include "nsIXULBrowserWindow.h"
 | ||
| #include "nsGlobalWindow.h"
 | ||
| #include "ReferrerInfo.h"
 | ||
| 
 | ||
| using namespace mozilla;
 | ||
| using namespace mozilla::dom;
 | ||
| 
 | ||
| /****************************************************************
 | ||
|  ******************** nsWatcherWindowEntry **********************
 | ||
|  ****************************************************************/
 | ||
| 
 | ||
| class nsWindowWatcher;
 | ||
| 
 | ||
| struct nsWatcherWindowEntry {
 | ||
|   nsWatcherWindowEntry(mozIDOMWindowProxy* aWindow,
 | ||
|                        nsIWebBrowserChrome* aChrome)
 | ||
|       : mChrome(nullptr) {
 | ||
|     mWindow = aWindow;
 | ||
|     nsCOMPtr<nsISupportsWeakReference> supportsweak(do_QueryInterface(aChrome));
 | ||
|     if (supportsweak) {
 | ||
|       supportsweak->GetWeakReference(getter_AddRefs(mChromeWeak));
 | ||
|     } else {
 | ||
|       mChrome = aChrome;
 | ||
|       mChromeWeak = nullptr;
 | ||
|     }
 | ||
|     ReferenceSelf();
 | ||
|   }
 | ||
|   ~nsWatcherWindowEntry() = default;
 | ||
| 
 | ||
|   void InsertAfter(nsWatcherWindowEntry* aOlder);
 | ||
|   void Unlink();
 | ||
|   void ReferenceSelf();
 | ||
| 
 | ||
|   mozIDOMWindowProxy* mWindow;
 | ||
|   nsIWebBrowserChrome* mChrome;
 | ||
|   nsWeakPtr mChromeWeak;
 | ||
|   // each struct is in a circular, doubly-linked list
 | ||
|   nsWatcherWindowEntry* mYounger;  // next younger in sequence
 | ||
|   nsWatcherWindowEntry* mOlder;
 | ||
| };
 | ||
| 
 | ||
| void nsWatcherWindowEntry::InsertAfter(nsWatcherWindowEntry* aOlder) {
 | ||
|   if (aOlder) {
 | ||
|     mOlder = aOlder;
 | ||
|     mYounger = aOlder->mYounger;
 | ||
|     mOlder->mYounger = this;
 | ||
|     if (mOlder->mOlder == mOlder) {
 | ||
|       mOlder->mOlder = this;
 | ||
|     }
 | ||
|     mYounger->mOlder = this;
 | ||
|     if (mYounger->mYounger == mYounger) {
 | ||
|       mYounger->mYounger = this;
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| void nsWatcherWindowEntry::Unlink() {
 | ||
|   mOlder->mYounger = mYounger;
 | ||
|   mYounger->mOlder = mOlder;
 | ||
|   ReferenceSelf();
 | ||
| }
 | ||
| 
 | ||
| void nsWatcherWindowEntry::ReferenceSelf() {
 | ||
|   mYounger = this;
 | ||
|   mOlder = this;
 | ||
| }
 | ||
| 
 | ||
| /****************************************************************
 | ||
|  ****************** nsWatcherWindowEnumerator *******************
 | ||
|  ****************************************************************/
 | ||
| 
 | ||
| class nsWatcherWindowEnumerator : public nsSimpleEnumerator {
 | ||
|  public:
 | ||
|   explicit nsWatcherWindowEnumerator(nsWindowWatcher* aWatcher);
 | ||
|   NS_IMETHOD HasMoreElements(bool* aResult) override;
 | ||
|   NS_IMETHOD GetNext(nsISupports** aResult) override;
 | ||
| 
 | ||
|  protected:
 | ||
|   ~nsWatcherWindowEnumerator() override;
 | ||
| 
 | ||
|  private:
 | ||
|   friend class nsWindowWatcher;
 | ||
| 
 | ||
|   nsWatcherWindowEntry* FindNext();
 | ||
|   void WindowRemoved(nsWatcherWindowEntry* aInfo);
 | ||
| 
 | ||
|   nsWindowWatcher* mWindowWatcher;
 | ||
|   nsWatcherWindowEntry* mCurrentPosition;
 | ||
| };
 | ||
| 
 | ||
| nsWatcherWindowEnumerator::nsWatcherWindowEnumerator(nsWindowWatcher* aWatcher)
 | ||
|     : mWindowWatcher(aWatcher), mCurrentPosition(aWatcher->mOldestWindow) {
 | ||
|   mWindowWatcher->AddEnumerator(this);
 | ||
|   mWindowWatcher->AddRef();
 | ||
| }
 | ||
| 
 | ||
| nsWatcherWindowEnumerator::~nsWatcherWindowEnumerator() {
 | ||
|   mWindowWatcher->RemoveEnumerator(this);
 | ||
|   mWindowWatcher->Release();
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWatcherWindowEnumerator::HasMoreElements(bool* aResult) {
 | ||
|   if (!aResult) {
 | ||
|     return NS_ERROR_INVALID_ARG;
 | ||
|   }
 | ||
| 
 | ||
|   *aResult = !!mCurrentPosition;
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWatcherWindowEnumerator::GetNext(nsISupports** aResult) {
 | ||
|   if (!aResult) {
 | ||
|     return NS_ERROR_INVALID_ARG;
 | ||
|   }
 | ||
| 
 | ||
|   *aResult = nullptr;
 | ||
| 
 | ||
|   if (mCurrentPosition) {
 | ||
|     CallQueryInterface(mCurrentPosition->mWindow, aResult);
 | ||
|     mCurrentPosition = FindNext();
 | ||
|     return NS_OK;
 | ||
|   }
 | ||
|   return NS_ERROR_FAILURE;
 | ||
| }
 | ||
| 
 | ||
| nsWatcherWindowEntry* nsWatcherWindowEnumerator::FindNext() {
 | ||
|   nsWatcherWindowEntry* info;
 | ||
| 
 | ||
|   if (!mCurrentPosition) {
 | ||
|     return 0;
 | ||
|   }
 | ||
| 
 | ||
|   info = mCurrentPosition->mYounger;
 | ||
|   return info == mWindowWatcher->mOldestWindow ? 0 : info;
 | ||
| }
 | ||
| 
 | ||
| // if a window is being removed adjust the iterator's current position
 | ||
| void nsWatcherWindowEnumerator::WindowRemoved(nsWatcherWindowEntry* aInfo) {
 | ||
|   if (mCurrentPosition == aInfo) {
 | ||
|     mCurrentPosition =
 | ||
|         mCurrentPosition != aInfo->mYounger ? aInfo->mYounger : 0;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| /****************************************************************
 | ||
|  *********************** nsWindowWatcher ************************
 | ||
|  ****************************************************************/
 | ||
| 
 | ||
| NS_IMPL_ADDREF(nsWindowWatcher)
 | ||
| NS_IMPL_RELEASE(nsWindowWatcher)
 | ||
| NS_IMPL_QUERY_INTERFACE(nsWindowWatcher, nsIWindowWatcher, nsIPromptFactory,
 | ||
|                         nsPIWindowWatcher)
 | ||
| 
 | ||
| nsWindowWatcher::nsWindowWatcher()
 | ||
|     : mEnumeratorList(),
 | ||
|       mOldestWindow(0),
 | ||
|       mListLock("nsWindowWatcher.mListLock") {}
 | ||
| 
 | ||
| nsWindowWatcher::~nsWindowWatcher() {
 | ||
|   // delete data
 | ||
|   while (mOldestWindow) {
 | ||
|     RemoveWindow(mOldestWindow);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| nsresult nsWindowWatcher::Init() { return NS_OK; }
 | ||
| 
 | ||
| /**
 | ||
|  * Convert aArguments into either an nsIArray or nullptr.
 | ||
|  *
 | ||
|  *  - If aArguments is nullptr, return nullptr.
 | ||
|  *  - If aArguments is an nsArray, return nullptr if it's empty, or otherwise
 | ||
|  *    return the array.
 | ||
|  *  - If aArguments is an nsIArray, return nullptr if it's empty, or
 | ||
|  *    otherwise just return the array.
 | ||
|  *  - Otherwise, return an nsIArray with one element: aArguments.
 | ||
|  */
 | ||
| static already_AddRefed<nsIArray> ConvertArgsToArray(nsISupports* aArguments) {
 | ||
|   if (!aArguments) {
 | ||
|     return nullptr;
 | ||
|   }
 | ||
| 
 | ||
|   nsCOMPtr<nsIArray> array = do_QueryInterface(aArguments);
 | ||
|   if (array) {
 | ||
|     uint32_t argc = 0;
 | ||
|     array->GetLength(&argc);
 | ||
|     if (argc == 0) {
 | ||
|       return nullptr;
 | ||
|     }
 | ||
| 
 | ||
|     return array.forget();
 | ||
|   }
 | ||
| 
 | ||
|   nsCOMPtr<nsIMutableArray> singletonArray =
 | ||
|       do_CreateInstance(NS_ARRAY_CONTRACTID);
 | ||
|   NS_ENSURE_TRUE(singletonArray, nullptr);
 | ||
| 
 | ||
|   nsresult rv = singletonArray->AppendElement(aArguments);
 | ||
|   NS_ENSURE_SUCCESS(rv, nullptr);
 | ||
| 
 | ||
|   return singletonArray.forget();
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::OpenWindow(mozIDOMWindowProxy* aParent, const nsACString& aUrl,
 | ||
|                             const nsACString& aName,
 | ||
|                             const nsACString& aFeatures,
 | ||
|                             nsISupports* aArguments,
 | ||
|                             mozIDOMWindowProxy** aResult) {
 | ||
|   nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
 | ||
| 
 | ||
|   uint32_t argc = 0;
 | ||
|   if (argv) {
 | ||
|     argv->GetLength(&argc);
 | ||
|   }
 | ||
|   bool dialog = (argc != 0);
 | ||
| 
 | ||
|   RefPtr<BrowsingContext> bc;
 | ||
|   MOZ_TRY(OpenWindowInternal(aParent, aUrl, aName, aFeatures,
 | ||
|                              /* calledFromJS = */ false, dialog,
 | ||
|                              /* navigate = */ true, argv,
 | ||
|                              /* aIsPopupSpam = */ false,
 | ||
|                              /* aForceNoOpener = */ false,
 | ||
|                              /* aForceNoReferrer = */ false, PRINT_NONE,
 | ||
|                              /* aLoadState = */ nullptr, getter_AddRefs(bc)));
 | ||
|   if (bc) {
 | ||
|     nsCOMPtr<mozIDOMWindowProxy> win(bc->GetDOMWindow());
 | ||
|     win.forget(aResult);
 | ||
|   }
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| struct SizeSpec {
 | ||
|   SizeSpec()
 | ||
|       : mLeft(0),
 | ||
|         mTop(0),
 | ||
|         mOuterWidth(0),
 | ||
|         mOuterHeight(0),
 | ||
|         mInnerWidth(0),
 | ||
|         mInnerHeight(0),
 | ||
|         mLeftSpecified(false),
 | ||
|         mTopSpecified(false),
 | ||
|         mOuterWidthSpecified(false),
 | ||
|         mOuterHeightSpecified(false),
 | ||
|         mInnerWidthSpecified(false),
 | ||
|         mInnerHeightSpecified(false),
 | ||
|         mLockAspectRatio(false) {}
 | ||
| 
 | ||
|   int32_t mLeft;
 | ||
|   int32_t mTop;
 | ||
|   int32_t mOuterWidth;   // Total window width
 | ||
|   int32_t mOuterHeight;  // Total window height
 | ||
|   int32_t mInnerWidth;   // Content area width
 | ||
|   int32_t mInnerHeight;  // Content area height
 | ||
| 
 | ||
|   bool mLeftSpecified;
 | ||
|   bool mTopSpecified;
 | ||
|   bool mOuterWidthSpecified;
 | ||
|   bool mOuterHeightSpecified;
 | ||
|   bool mInnerWidthSpecified;
 | ||
|   bool mInnerHeightSpecified;
 | ||
|   bool mLockAspectRatio;
 | ||
| 
 | ||
|   bool PositionSpecified() const { return mLeftSpecified || mTopSpecified; }
 | ||
| 
 | ||
|   bool SizeSpecified() const { return WidthSpecified() || HeightSpecified(); }
 | ||
| 
 | ||
|   bool WidthSpecified() const {
 | ||
|     return mOuterWidthSpecified || mInnerWidthSpecified;
 | ||
|   }
 | ||
| 
 | ||
|   bool HeightSpecified() const {
 | ||
|     return mOuterHeightSpecified || mInnerHeightSpecified;
 | ||
|   }
 | ||
| };
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::OpenWindow2(mozIDOMWindowProxy* aParent,
 | ||
|                              const nsACString& aUrl, const nsACString& aName,
 | ||
|                              const nsACString& aFeatures,
 | ||
|                              bool aCalledFromScript, bool aDialog,
 | ||
|                              bool aNavigate, nsISupports* aArguments,
 | ||
|                              bool aIsPopupSpam, bool aForceNoOpener,
 | ||
|                              bool aForceNoReferrer, PrintKind aPrintKind,
 | ||
|                              nsDocShellLoadState* aLoadState,
 | ||
|                              BrowsingContext** aResult) {
 | ||
|   nsCOMPtr<nsIArray> argv = ConvertArgsToArray(aArguments);
 | ||
| 
 | ||
|   uint32_t argc = 0;
 | ||
|   if (argv) {
 | ||
|     argv->GetLength(&argc);
 | ||
|   }
 | ||
| 
 | ||
|   // This is extremely messed up, but this behavior is necessary because
 | ||
|   // callers lie about whether they're a dialog window and whether they're
 | ||
|   // called from script.  Fixing this is bug 779939.
 | ||
|   bool dialog = aDialog;
 | ||
|   if (!aCalledFromScript) {
 | ||
|     dialog = argc > 0;
 | ||
|   }
 | ||
| 
 | ||
|   return OpenWindowInternal(aParent, aUrl, aName, aFeatures, aCalledFromScript,
 | ||
|                             dialog, aNavigate, argv, aIsPopupSpam,
 | ||
|                             aForceNoOpener, aForceNoReferrer, aPrintKind,
 | ||
|                             aLoadState, aResult);
 | ||
| }
 | ||
| 
 | ||
| // This static function checks if the aDocShell uses an UserContextId equal to
 | ||
| // the userContextId of subjectPrincipal, if not null.
 | ||
| static bool CheckUserContextCompatibility(nsIDocShell* aDocShell) {
 | ||
|   MOZ_ASSERT(aDocShell);
 | ||
| 
 | ||
|   uint32_t userContextId =
 | ||
|       static_cast<nsDocShell*>(aDocShell)->GetOriginAttributes().mUserContextId;
 | ||
| 
 | ||
|   nsCOMPtr<nsIPrincipal> subjectPrincipal =
 | ||
|       nsContentUtils::GetCurrentJSContext() ? nsContentUtils::SubjectPrincipal()
 | ||
|                                             : nullptr;
 | ||
| 
 | ||
|   // If we don't have a valid principal, probably we are in e10s mode, parent
 | ||
|   // side.
 | ||
|   if (!subjectPrincipal) {
 | ||
|     return true;
 | ||
|   }
 | ||
| 
 | ||
|   // DocShell can have UsercontextID set but loading a document with system
 | ||
|   // principal. In this case, we consider everything ok.
 | ||
|   if (subjectPrincipal->IsSystemPrincipal()) {
 | ||
|     return true;
 | ||
|   }
 | ||
| 
 | ||
|   return subjectPrincipal->GetUserContextId() == userContextId;
 | ||
| }
 | ||
| 
 | ||
| nsresult nsWindowWatcher::CreateChromeWindow(nsIWebBrowserChrome* aParentChrome,
 | ||
|                                              uint32_t aChromeFlags,
 | ||
|                                              nsIOpenWindowInfo* aOpenWindowInfo,
 | ||
|                                              nsIWebBrowserChrome** aResult) {
 | ||
|   if (NS_WARN_IF(!mWindowCreator)) {
 | ||
|     return NS_ERROR_UNEXPECTED;
 | ||
|   }
 | ||
| 
 | ||
|   bool cancel = false;
 | ||
|   nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
 | ||
|   nsresult rv = mWindowCreator->CreateChromeWindow(
 | ||
|       aParentChrome, aChromeFlags, aOpenWindowInfo, &cancel,
 | ||
|       getter_AddRefs(newWindowChrome));
 | ||
| 
 | ||
|   if (NS_SUCCEEDED(rv) && cancel) {
 | ||
|     newWindowChrome = nullptr;
 | ||
|     return NS_ERROR_ABORT;
 | ||
|   }
 | ||
| 
 | ||
|   newWindowChrome.forget(aResult);
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * Disable persistence of size/position in popups (determined by
 | ||
|  * determining whether the features parameter specifies width or height
 | ||
|  * in any way). We consider any overriding of the window's size or position
 | ||
|  * in the open call as disabling persistence of those attributes.
 | ||
|  * Popup windows (which should not persist size or position) generally set
 | ||
|  * the size.
 | ||
|  *
 | ||
|  * @param aFeatures
 | ||
|  *        The features that was used to open the window.
 | ||
|  * @param aTreeOwner
 | ||
|  *        The nsIDocShellTreeOwner of the newly opened window. If null,
 | ||
|  *        this function is a no-op.
 | ||
|  */
 | ||
| void nsWindowWatcher::MaybeDisablePersistence(
 | ||
|     const SizeSpec& sizeSpec, nsIDocShellTreeOwner* aTreeOwner) {
 | ||
|   if (!aTreeOwner) {
 | ||
|     return;
 | ||
|   }
 | ||
| 
 | ||
|   if (sizeSpec.SizeSpecified()) {
 | ||
|     aTreeOwner->SetPersistence(false, false, false);
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::OpenWindowWithRemoteTab(nsIRemoteTab* aRemoteTab,
 | ||
|                                          const nsACString& aFeatures,
 | ||
|                                          bool aCalledFromJS,
 | ||
|                                          float aOpenerFullZoom,
 | ||
|                                          nsIOpenWindowInfo* aOpenWindowInfo,
 | ||
|                                          nsIRemoteTab** aResult) {
 | ||
|   MOZ_ASSERT(XRE_IsParentProcess());
 | ||
|   MOZ_ASSERT(mWindowCreator);
 | ||
| 
 | ||
|   if (!nsContentUtils::IsSafeToRunScript()) {
 | ||
|     nsContentUtils::WarnScriptWasIgnored(nullptr);
 | ||
|     return NS_ERROR_FAILURE;
 | ||
|   }
 | ||
| 
 | ||
|   if (NS_WARN_IF(!mWindowCreator)) {
 | ||
|     return NS_ERROR_UNEXPECTED;
 | ||
|   }
 | ||
| 
 | ||
|   bool isFissionWindow = FissionAutostart();
 | ||
|   bool isPrivateBrowsingWindow =
 | ||
|       Preferences::GetBool("browser.privatebrowsing.autostart");
 | ||
| 
 | ||
|   nsCOMPtr<nsPIDOMWindowOuter> parentWindowOuter;
 | ||
|   RefPtr<BrowsingContext> parentBC = aOpenWindowInfo->GetParent();
 | ||
|   if (parentBC) {
 | ||
|     RefPtr<Element> browserElement = parentBC->Top()->GetEmbedderElement();
 | ||
|     if (browserElement && browserElement->GetOwnerGlobal() &&
 | ||
|         browserElement->GetOwnerGlobal()->AsInnerWindow()) {
 | ||
|       parentWindowOuter =
 | ||
|           browserElement->GetOwnerGlobal()->AsInnerWindow()->GetOuterWindow();
 | ||
|     }
 | ||
| 
 | ||
|     isFissionWindow = parentBC->UseRemoteSubframes();
 | ||
|     isPrivateBrowsingWindow =
 | ||
|         isPrivateBrowsingWindow || parentBC->UsePrivateBrowsing();
 | ||
|   }
 | ||
| 
 | ||
|   if (!parentWindowOuter) {
 | ||
|     // We couldn't find a browser window for the opener, so either we
 | ||
|     // never were passed aRemoteTab, the window is closed,
 | ||
|     // or it's in the process of closing. Either way, we'll use
 | ||
|     // the most recently opened browser window instead.
 | ||
|     parentWindowOuter = nsContentUtils::GetMostRecentNonPBWindow();
 | ||
|   }
 | ||
| 
 | ||
|   if (NS_WARN_IF(!parentWindowOuter)) {
 | ||
|     return NS_ERROR_UNEXPECTED;
 | ||
|   }
 | ||
| 
 | ||
|   nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner =
 | ||
|       parentWindowOuter->GetTreeOwner();
 | ||
|   if (NS_WARN_IF(!parentTreeOwner)) {
 | ||
|     return NS_ERROR_UNEXPECTED;
 | ||
|   }
 | ||
| 
 | ||
|   if (NS_WARN_IF(!mWindowCreator)) {
 | ||
|     return NS_ERROR_UNEXPECTED;
 | ||
|   }
 | ||
| 
 | ||
|   WindowFeatures features;
 | ||
|   features.Tokenize(aFeatures);
 | ||
| 
 | ||
|   SizeSpec sizeSpec;
 | ||
|   CalcSizeSpec(features, false, sizeSpec);
 | ||
| 
 | ||
|   uint32_t chromeFlags = CalculateChromeFlagsForContent(features, sizeSpec);
 | ||
| 
 | ||
|   if (isPrivateBrowsingWindow) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
 | ||
|   }
 | ||
| 
 | ||
|   // A content process has asked for a new window, which implies
 | ||
|   // that the new window will need to be remote.
 | ||
|   chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
 | ||
| 
 | ||
|   if (isFissionWindow) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
 | ||
|   }
 | ||
| 
 | ||
|   nsCOMPtr<nsIWebBrowserChrome> parentChrome(do_GetInterface(parentTreeOwner));
 | ||
|   nsCOMPtr<nsIWebBrowserChrome> newWindowChrome;
 | ||
| 
 | ||
|   CreateChromeWindow(parentChrome, chromeFlags, aOpenWindowInfo,
 | ||
|                      getter_AddRefs(newWindowChrome));
 | ||
| 
 | ||
|   if (NS_WARN_IF(!newWindowChrome)) {
 | ||
|     return NS_ERROR_UNEXPECTED;
 | ||
|   }
 | ||
| 
 | ||
|   nsCOMPtr<nsIDocShellTreeItem> chromeTreeItem =
 | ||
|       do_GetInterface(newWindowChrome);
 | ||
|   if (NS_WARN_IF(!chromeTreeItem)) {
 | ||
|     return NS_ERROR_UNEXPECTED;
 | ||
|   }
 | ||
| 
 | ||
|   nsCOMPtr<nsIDocShellTreeOwner> chromeTreeOwner;
 | ||
|   chromeTreeItem->GetTreeOwner(getter_AddRefs(chromeTreeOwner));
 | ||
|   if (NS_WARN_IF(!chromeTreeOwner)) {
 | ||
|     return NS_ERROR_UNEXPECTED;
 | ||
|   }
 | ||
| 
 | ||
|   nsCOMPtr<nsILoadContext> chromeContext = do_QueryInterface(chromeTreeItem);
 | ||
|   if (NS_WARN_IF(!chromeContext)) {
 | ||
|     return NS_ERROR_UNEXPECTED;
 | ||
|   }
 | ||
| 
 | ||
|   MOZ_ASSERT(chromeContext->UsePrivateBrowsing() == isPrivateBrowsingWindow);
 | ||
|   MOZ_ASSERT(chromeContext->UseRemoteSubframes() == isFissionWindow);
 | ||
| 
 | ||
|   // Tabs opened from a content process can only open new windows
 | ||
|   // that will also run with out-of-process tabs.
 | ||
|   MOZ_ASSERT(chromeContext->UseRemoteTabs());
 | ||
| 
 | ||
|   MaybeDisablePersistence(sizeSpec, chromeTreeOwner);
 | ||
| 
 | ||
|   SizeOpenedWindow(chromeTreeOwner, parentWindowOuter, false, sizeSpec,
 | ||
|                    Some(aOpenerFullZoom));
 | ||
| 
 | ||
|   nsCOMPtr<nsIRemoteTab> newBrowserParent;
 | ||
|   chromeTreeOwner->GetPrimaryRemoteTab(getter_AddRefs(newBrowserParent));
 | ||
|   if (NS_WARN_IF(!newBrowserParent)) {
 | ||
|     return NS_ERROR_UNEXPECTED;
 | ||
|   }
 | ||
| 
 | ||
|   newBrowserParent.forget(aResult);
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| nsresult nsWindowWatcher::OpenWindowInternal(
 | ||
|     mozIDOMWindowProxy* aParent, const nsACString& aUrl,
 | ||
|     const nsACString& aName, const nsACString& aFeatures, bool aCalledFromJS,
 | ||
|     bool aDialog, bool aNavigate, nsIArray* aArgv, bool aIsPopupSpam,
 | ||
|     bool aForceNoOpener, bool aForceNoReferrer, PrintKind aPrintKind,
 | ||
|     nsDocShellLoadState* aLoadState, BrowsingContext** aResult) {
 | ||
|   MOZ_ASSERT_IF(aForceNoReferrer, aForceNoOpener);
 | ||
| 
 | ||
|   nsresult rv = NS_OK;
 | ||
|   bool isNewToplevelWindow = false;
 | ||
|   bool windowIsNew = false;
 | ||
|   bool windowNeedsName = false;
 | ||
|   bool windowIsModal = false;
 | ||
|   bool uriToLoadIsChrome = false;
 | ||
| 
 | ||
|   uint32_t chromeFlags;
 | ||
|   nsAutoString name;           // string version of aName
 | ||
|   nsCOMPtr<nsIURI> uriToLoad;  // from aUrl, if any
 | ||
|   nsCOMPtr<nsIDocShellTreeOwner>
 | ||
|       parentTreeOwner;            // from the parent window, if any
 | ||
|   RefPtr<BrowsingContext> newBC;  // from the new window
 | ||
| 
 | ||
|   nsCOMPtr<nsPIDOMWindowOuter> parentWindow =
 | ||
|       aParent ? nsPIDOMWindowOuter::From(aParent) : nullptr;
 | ||
| 
 | ||
|   NS_ENSURE_ARG_POINTER(aResult);
 | ||
|   *aResult = 0;
 | ||
| 
 | ||
|   if (!nsContentUtils::IsSafeToRunScript()) {
 | ||
|     nsContentUtils::WarnScriptWasIgnored(nullptr);
 | ||
|     return NS_ERROR_FAILURE;
 | ||
|   }
 | ||
| 
 | ||
|   if (parentWindow) {
 | ||
|     parentTreeOwner = parentWindow->GetTreeOwner();
 | ||
|   }
 | ||
| 
 | ||
|   // We expect BrowserParent to have provided us the absolute URI of the window
 | ||
|   // we're to open, so there's no need to call URIfromURL (or more importantly,
 | ||
|   // to check for a chrome URI, which cannot be opened from a remote tab).
 | ||
|   if (!aUrl.IsVoid()) {
 | ||
|     rv = URIfromURL(aUrl, aParent, getter_AddRefs(uriToLoad));
 | ||
|     if (NS_FAILED(rv)) {
 | ||
|       return rv;
 | ||
|     }
 | ||
|     uriToLoadIsChrome = uriToLoad->SchemeIs("chrome");
 | ||
|   }
 | ||
| 
 | ||
|   bool nameSpecified = false;
 | ||
|   if (!aName.IsEmpty()) {
 | ||
|     CopyUTF8toUTF16(aName, name);
 | ||
|     nameSpecified = true;
 | ||
|   } else {
 | ||
|     name.SetIsVoid(true);
 | ||
|   }
 | ||
| 
 | ||
|   WindowFeatures features;
 | ||
|   nsAutoCString featuresStr;
 | ||
|   if (!aFeatures.IsEmpty()) {
 | ||
|     featuresStr.Assign(aFeatures);
 | ||
|     features.Tokenize(featuresStr);
 | ||
|   } else {
 | ||
|     featuresStr.SetIsVoid(true);
 | ||
|   }
 | ||
| 
 | ||
|   RefPtr<BrowsingContext> parentBC(
 | ||
|       parentWindow ? parentWindow->GetBrowsingContext() : nullptr);
 | ||
|   nsCOMPtr<nsIDocShell> parentDocShell(parentBC ? parentBC->GetDocShell()
 | ||
|                                                 : nullptr);
 | ||
| 
 | ||
|   // Return null for any attempt to trigger a load from a discarded browsing
 | ||
|   // context. The spec is non-normative, and doesn't specify what should happen
 | ||
|   // when window.open is called on a window with a null browsing context, but it
 | ||
|   // does give us broad discretion over when we can decide to ignore an open
 | ||
|   // request and return null.
 | ||
|   //
 | ||
|   // Regardless, we cannot trigger a cross-process load from a discarded
 | ||
|   // browsing context, and ideally we should behave consistently whether a load
 | ||
|   // is same-process or cross-process.
 | ||
|   if (parentBC && parentBC->IsDiscarded()) {
 | ||
|     return NS_ERROR_ABORT;
 | ||
|   }
 | ||
| 
 | ||
|   // try to find an extant browsing context with the given name
 | ||
|   newBC = GetBrowsingContextByName(name, aForceNoOpener, parentBC);
 | ||
| 
 | ||
|   // Do sandbox checks here, instead of waiting until nsIDocShell::LoadURI.
 | ||
|   // The state of the window can change before this call and if we are blocked
 | ||
|   // because of sandboxing, we wouldn't want that to happen.
 | ||
|   if (parentBC && parentBC->IsSandboxedFrom(newBC)) {
 | ||
|     return NS_ERROR_DOM_INVALID_ACCESS_ERR;
 | ||
|   }
 | ||
| 
 | ||
|   // If our target BrowsingContext is still pending initialization, ignore the
 | ||
|   // navigation request targeting it.
 | ||
|   if (newBC && NS_WARN_IF(newBC->GetPendingInitialization())) {
 | ||
|     return NS_ERROR_ABORT;
 | ||
|   }
 | ||
| 
 | ||
|   // no extant window? make a new one.
 | ||
| 
 | ||
|   // If no parent, consider it chrome when running in the parent process.
 | ||
|   bool hasChromeParent = XRE_IsContentProcess() ? false : true;
 | ||
|   if (aParent) {
 | ||
|     // Check if the parent document has chrome privileges.
 | ||
|     Document* doc = parentWindow->GetDoc();
 | ||
|     hasChromeParent = doc && nsContentUtils::IsChromeDoc(doc);
 | ||
|   }
 | ||
| 
 | ||
|   bool isCallerChrome = nsContentUtils::LegacyIsCallerChromeOrNativeCode();
 | ||
| 
 | ||
|   SizeSpec sizeSpec;
 | ||
|   CalcSizeSpec(features, hasChromeParent, sizeSpec);
 | ||
| 
 | ||
|   // Make sure we calculate the chromeFlags *before* we push the
 | ||
|   // callee context onto the context stack so that
 | ||
|   // the calculation sees the actual caller when doing its
 | ||
|   // security checks.
 | ||
|   if (isCallerChrome && XRE_IsParentProcess()) {
 | ||
|     chromeFlags = CalculateChromeFlagsForSystem(
 | ||
|         features, sizeSpec, aDialog, uriToLoadIsChrome, hasChromeParent);
 | ||
|   } else {
 | ||
|     MOZ_DIAGNOSTIC_ASSERT(parentBC && parentBC->IsContent(),
 | ||
|                           "content caller must provide content parent");
 | ||
|     chromeFlags = CalculateChromeFlagsForContent(features, sizeSpec);
 | ||
| 
 | ||
|     if (aDialog) {
 | ||
|       MOZ_ASSERT(XRE_IsParentProcess());
 | ||
|       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   bool windowTypeIsChrome =
 | ||
|       chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
 | ||
| 
 | ||
|   if (parentBC && !aForceNoOpener) {
 | ||
|     if (parentBC->IsChrome() && !windowTypeIsChrome) {
 | ||
|       NS_WARNING(
 | ||
|           "Content windows may never have chrome windows as their openers.");
 | ||
|       return NS_ERROR_INVALID_ARG;
 | ||
|     }
 | ||
|     if (parentBC->IsContent() && windowTypeIsChrome) {
 | ||
|       NS_WARNING(
 | ||
|           "Chrome windows may never have content windows as their openers.");
 | ||
|       return NS_ERROR_INVALID_ARG;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // If we're opening a content window from a content window, we need to exactly
 | ||
|   // inherit fission and e10s status flags from parentBC. Only new toplevel
 | ||
|   // windows may change these options.
 | ||
|   if (parentBC && parentBC->IsContent() && !windowTypeIsChrome) {
 | ||
|     chromeFlags &= ~(nsIWebBrowserChrome::CHROME_REMOTE_WINDOW |
 | ||
|                      nsIWebBrowserChrome::CHROME_FISSION_WINDOW);
 | ||
|     if (parentBC->UseRemoteTabs()) {
 | ||
|       chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
 | ||
|     }
 | ||
|     if (parentBC->UseRemoteSubframes()) {
 | ||
|       chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // XXXbz Why is an AutoJSAPI good enough here?  Wouldn't AutoEntryScript (so
 | ||
|   // we affect the entry global) make more sense?  Or do we just want to affect
 | ||
|   // GetSubjectPrincipal()?
 | ||
|   dom::AutoJSAPI jsapiChromeGuard;
 | ||
| 
 | ||
|   if (isCallerChrome && !hasChromeParent && !windowTypeIsChrome) {
 | ||
|     // open() is called from chrome on a non-chrome window, initialize an
 | ||
|     // AutoJSAPI with the callee to prevent the caller's privileges from leaking
 | ||
|     // into code that runs while opening the new window.
 | ||
|     //
 | ||
|     // The reasoning for this is in bug 289204. Basically, chrome sometimes does
 | ||
|     // someContentWindow.open(untrustedURL), and wants to be insulated from
 | ||
|     // nasty javascript: URLs and such. But there are also cases where we create
 | ||
|     // a window parented to a content window (such as a download dialog),
 | ||
|     // usually directly with nsIWindowWatcher. In those cases, we want the
 | ||
|     // principal of the initial about:blank document to be system, so that the
 | ||
|     // subsequent XUL load can reuse the inner window and avoid blowing away
 | ||
|     // expandos. As such, we decide whether to load with the principal of the
 | ||
|     // caller or of the parent based on whether the docshell type is chrome or
 | ||
|     // content.
 | ||
| 
 | ||
|     nsCOMPtr<nsIGlobalObject> parentGlobalObject = do_QueryInterface(aParent);
 | ||
|     if (!aParent) {
 | ||
|       jsapiChromeGuard.Init();
 | ||
|     } else if (NS_WARN_IF(!jsapiChromeGuard.Init(parentGlobalObject))) {
 | ||
|       return NS_ERROR_UNEXPECTED;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // Information used when opening new content windows. This object will be
 | ||
|   // passed through to the inner nsFrameLoader.
 | ||
|   RefPtr<nsOpenWindowInfo> openWindowInfo;
 | ||
|   if (!newBC && !windowTypeIsChrome) {
 | ||
|     openWindowInfo = new nsOpenWindowInfo();
 | ||
|     openWindowInfo->mForceNoOpener = aForceNoOpener;
 | ||
|     openWindowInfo->mParent = parentBC;
 | ||
|     openWindowInfo->mIsForPrinting = aPrintKind != PRINT_NONE;
 | ||
|     openWindowInfo->mIsForWindowDotPrint = aPrintKind == PRINT_WINDOW_DOT_PRINT;
 | ||
| 
 | ||
|     // We're going to want the window to be immediately available, meaning we
 | ||
|     // want it to match the current remoteness.
 | ||
|     openWindowInfo->mIsRemote = XRE_IsContentProcess();
 | ||
| 
 | ||
|     // If we have a non-system non-expanded subject principal, we can inherit
 | ||
|     // our OriginAttributes from it.
 | ||
|     nsCOMPtr<nsIPrincipal> subjectPrincipal =
 | ||
|         nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
 | ||
|     if (subjectPrincipal &&
 | ||
|         !nsContentUtils::IsSystemOrExpandedPrincipal(subjectPrincipal)) {
 | ||
|       openWindowInfo->mOriginAttributes =
 | ||
|           subjectPrincipal->OriginAttributesRef();
 | ||
|     } else if (parentBC) {
 | ||
|       openWindowInfo->mOriginAttributes = parentBC->OriginAttributesRef();
 | ||
|     }
 | ||
| 
 | ||
|     MOZ_DIAGNOSTIC_ASSERT(
 | ||
|         !parentBC || openWindowInfo->mOriginAttributes.EqualsIgnoringFPD(
 | ||
|                          parentBC->OriginAttributesRef()),
 | ||
|         "subject principal origin attributes doesn't match opener");
 | ||
|   }
 | ||
| 
 | ||
|   uint32_t activeDocsSandboxFlags = 0;
 | ||
|   if (!newBC) {
 | ||
|     // We're going to either open up a new window ourselves or ask a
 | ||
|     // nsIWindowProvider for one.  In either case, we'll want to set the right
 | ||
|     // name on it.
 | ||
|     windowNeedsName = true;
 | ||
| 
 | ||
|     // If the parent trying to open a new window is sandboxed
 | ||
|     // without 'allow-popups', this is not allowed and we fail here.
 | ||
|     if (aParent) {
 | ||
|       if (Document* doc = parentWindow->GetDoc()) {
 | ||
|         // Save sandbox flags for copying to new browsing context (docShell).
 | ||
|         activeDocsSandboxFlags = doc->GetSandboxFlags();
 | ||
|         // Check to see if this frame is allowed to navigate, but don't check if
 | ||
|         // we're printing, as that's not a real navigation.
 | ||
|         if (aPrintKind == PRINT_NONE &&
 | ||
|             (activeDocsSandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION)) {
 | ||
|           return NS_ERROR_DOM_INVALID_ACCESS_ERR;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     // Now check whether it's ok to ask a window provider for a window.  Don't
 | ||
|     // do it if we're opening a dialog or if our parent is a chrome window or
 | ||
|     // if we're opening something that has modal, dialog, or chrome flags set.
 | ||
|     if (parentTreeOwner && !aDialog && parentBC->IsContent() &&
 | ||
|         !(chromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
 | ||
|                          nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
 | ||
|                          nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) {
 | ||
|       MOZ_ASSERT(openWindowInfo);
 | ||
| 
 | ||
|       nsCOMPtr<nsIWindowProvider> provider = do_GetInterface(parentTreeOwner);
 | ||
|       if (provider) {
 | ||
|         rv = provider->ProvideWindow(openWindowInfo, chromeFlags, aCalledFromJS,
 | ||
|                                      sizeSpec.WidthSpecified(), uriToLoad, name,
 | ||
|                                      featuresStr, aForceNoOpener,
 | ||
|                                      aForceNoReferrer, aLoadState, &windowIsNew,
 | ||
|                                      getter_AddRefs(newBC));
 | ||
| 
 | ||
|         if (NS_SUCCEEDED(rv) && newBC) {
 | ||
|           nsCOMPtr<nsIDocShell> newDocShell = newBC->GetDocShell();
 | ||
| 
 | ||
|           // If this is a new window, but it's incompatible with the current
 | ||
|           // userContextId, we ignore it and we pretend that nothing has been
 | ||
|           // returned by ProvideWindow.
 | ||
|           if (!windowIsNew && newDocShell) {
 | ||
|             if (!CheckUserContextCompatibility(newDocShell)) {
 | ||
|               newBC = nullptr;
 | ||
|               windowIsNew = false;
 | ||
|             }
 | ||
|           }
 | ||
| 
 | ||
|         } else if (rv == NS_ERROR_ABORT) {
 | ||
|           // NS_ERROR_ABORT means the window provider has flat-out rejected
 | ||
|           // the open-window call and we should bail.  Don't return an error
 | ||
|           // here, because our caller may propagate that error, which might
 | ||
|           // cause e.g. window.open to throw!  Just return null for our out
 | ||
|           // param.
 | ||
|           return NS_OK;
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   bool newWindowShouldBeModal = false;
 | ||
|   bool parentIsModal = false;
 | ||
|   if (!newBC) {
 | ||
|     if (XRE_IsContentProcess()) {
 | ||
|       // If our window provider failed to provide a window in the content
 | ||
|       // process, we cannot recover. Reject the window open request and bail.
 | ||
|       return NS_OK;
 | ||
|     }
 | ||
| 
 | ||
|     windowIsNew = true;
 | ||
|     isNewToplevelWindow = true;
 | ||
| 
 | ||
|     nsCOMPtr<nsIWebBrowserChrome> parentChrome(
 | ||
|         do_GetInterface(parentTreeOwner));
 | ||
| 
 | ||
|     // is the parent (if any) modal? if so, we must be, too.
 | ||
|     bool weAreModal = (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) != 0;
 | ||
|     newWindowShouldBeModal = weAreModal;
 | ||
|     if (!weAreModal && parentChrome) {
 | ||
|       parentChrome->IsWindowModal(&weAreModal);
 | ||
|       parentIsModal = weAreModal;
 | ||
|     }
 | ||
| 
 | ||
|     if (weAreModal) {
 | ||
|       windowIsModal = true;
 | ||
|       // in case we added this because weAreModal
 | ||
|       chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL |
 | ||
|                      nsIWebBrowserChrome::CHROME_DEPENDENT;
 | ||
|     }
 | ||
| 
 | ||
|     // Make sure to not create modal windows if our parent is invisible and
 | ||
|     // isn't a chrome window.  Otherwise we can end up in a bizarre situation
 | ||
|     // where we can't shut down because an invisible window is open.  If
 | ||
|     // someone tries to do this, throw.
 | ||
|     if (!hasChromeParent && (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)) {
 | ||
|       nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(parentTreeOwner));
 | ||
|       nsCOMPtr<nsIWidget> parentWidget;
 | ||
|       if (parentWindow) {
 | ||
|         parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
 | ||
|       }
 | ||
|       // NOTE: the logic for this visibility check is duplicated in
 | ||
|       // nsIDOMWindowUtils::isParentWindowMainWidgetVisible - if we change
 | ||
|       // how a window is determined "visible" in this context then we should
 | ||
|       // also adjust that attribute and/or any consumers of it...
 | ||
|       if (parentWidget && !parentWidget->IsVisible()) {
 | ||
|         return NS_ERROR_NOT_AVAILABLE;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     NS_ASSERTION(mWindowCreator,
 | ||
|                  "attempted to open a new window with no WindowCreator");
 | ||
|     rv = NS_ERROR_FAILURE;
 | ||
|     if (mWindowCreator) {
 | ||
|       nsCOMPtr<nsIWebBrowserChrome> newChrome;
 | ||
| 
 | ||
|       nsCOMPtr<nsPIDOMWindowInner> parentTopInnerWindow;
 | ||
|       if (parentWindow) {
 | ||
|         nsCOMPtr<nsPIDOMWindowOuter> parentTopWindow =
 | ||
|             parentWindow->GetInProcessTop();
 | ||
|         if (parentTopWindow) {
 | ||
|           parentTopInnerWindow = parentTopWindow->GetCurrentInnerWindow();
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       if (parentTopInnerWindow) {
 | ||
|         parentTopInnerWindow->Suspend();
 | ||
|       }
 | ||
| 
 | ||
|       /* We can give the window creator some hints. The only hint at this time
 | ||
|          is whether the opening window is in a situation that's likely to mean
 | ||
|          this is an unrequested popup window we're creating. However we're not
 | ||
|          completely honest: we clear that indicator if the opener is chrome, so
 | ||
|          that the downstream consumer can treat the indicator to mean simply
 | ||
|          that the new window is subject to popup control. */
 | ||
|       rv = CreateChromeWindow(parentChrome, chromeFlags, openWindowInfo,
 | ||
|                               getter_AddRefs(newChrome));
 | ||
|       if (parentTopInnerWindow) {
 | ||
|         parentTopInnerWindow->Resume();
 | ||
|       }
 | ||
| 
 | ||
|       if (newChrome) {
 | ||
|         /* It might be a chrome AppWindow, in which case it won't have
 | ||
|             an nsIDOMWindow (primary content shell). But in that case, it'll
 | ||
|             be able to hand over an nsIDocShellTreeItem directly. */
 | ||
|         nsCOMPtr<nsPIDOMWindowOuter> newWindow(do_GetInterface(newChrome));
 | ||
|         nsCOMPtr<nsIDocShellTreeItem> newDocShellItem;
 | ||
|         if (newWindow) {
 | ||
|           newDocShellItem = newWindow->GetDocShell();
 | ||
|         }
 | ||
|         if (!newDocShellItem) {
 | ||
|           newDocShellItem = do_GetInterface(newChrome);
 | ||
|         }
 | ||
|         if (!newDocShellItem) {
 | ||
|           rv = NS_ERROR_FAILURE;
 | ||
|         }
 | ||
|         newBC = newDocShellItem->GetBrowsingContext();
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // better have a window to use by this point
 | ||
|   if (!newBC) {
 | ||
|     return rv;
 | ||
|   }
 | ||
| 
 | ||
|   // If our parent is sandboxed, set it as the one permitted sandboxed navigator
 | ||
|   // on the new window we're opening.
 | ||
|   if (activeDocsSandboxFlags && parentBC) {
 | ||
|     MOZ_ALWAYS_SUCCEEDS(newBC->SetOnePermittedSandboxedNavigator(parentBC));
 | ||
|   }
 | ||
| 
 | ||
|   if (!aForceNoOpener && parentBC) {
 | ||
|     // If we've created a new content window, its opener should have been set
 | ||
|     // when its BrowsingContext was created, in order to ensure that the context
 | ||
|     // is loaded within the correct BrowsingContextGroup.
 | ||
|     if (windowIsNew && newBC->IsContent()) {
 | ||
|       MOZ_RELEASE_ASSERT(newBC->GetOpenerId() == parentBC->Id());
 | ||
|       MOZ_RELEASE_ASSERT(!!parentBC == newBC->HadOriginalOpener());
 | ||
|     } else {
 | ||
|       // Update the opener for an existing or chrome BC.
 | ||
|       newBC->SetOpener(parentBC);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   RefPtr<nsDocShell> newDocShell(nsDocShell::Cast(newBC->GetDocShell()));
 | ||
| 
 | ||
|   // As required by spec, new windows always start out same-process, even if the
 | ||
|   // URL being loaded will eventually load in a new process.
 | ||
|   MOZ_DIAGNOSTIC_ASSERT(!windowIsNew || newDocShell);
 | ||
|   // New top-level windows are only opened in the parent process and are, by
 | ||
|   // definition, always in-process.
 | ||
|   MOZ_DIAGNOSTIC_ASSERT(!isNewToplevelWindow || newDocShell);
 | ||
| 
 | ||
|   // Copy sandbox flags to the new window if activeDocsSandboxFlags says to do
 | ||
|   // so.  Note that it's only nonzero if the window is new, so clobbering
 | ||
|   // sandbox flags on the window makes sense in that case.
 | ||
|   if (activeDocsSandboxFlags &
 | ||
|       SANDBOX_PROPAGATES_TO_AUXILIARY_BROWSING_CONTEXTS) {
 | ||
|     MOZ_ASSERT(windowIsNew, "Should only get here for new windows");
 | ||
|     MOZ_ALWAYS_SUCCEEDS(newBC->SetSandboxFlags(activeDocsSandboxFlags));
 | ||
|   }
 | ||
| 
 | ||
|   RefPtr<nsGlobalWindowOuter> win(
 | ||
|       nsGlobalWindowOuter::Cast(newBC->GetDOMWindow()));
 | ||
|   if (win) {
 | ||
|     if (windowIsNew) {
 | ||
| #ifdef DEBUG
 | ||
|       // Assert that we're not loading things right now.  If we are, when
 | ||
|       // that load completes it will clobber whatever principals we set up
 | ||
|       // on this new window!
 | ||
|       nsCOMPtr<nsIChannel> chan;
 | ||
|       newDocShell->GetDocumentChannel(getter_AddRefs(chan));
 | ||
|       MOZ_ASSERT(!chan, "Why is there a document channel?");
 | ||
| #endif
 | ||
| 
 | ||
|       if (RefPtr<Document> doc = win->GetExtantDoc()) {
 | ||
|         doc->SetIsInitialDocument(true);
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   MOZ_ASSERT(win || !windowIsNew, "New windows are always created in-process");
 | ||
| 
 | ||
|   *aResult = do_AddRef(newBC).take();
 | ||
| 
 | ||
|   if (isNewToplevelWindow) {
 | ||
|     nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
 | ||
|     newDocShell->GetTreeOwner(getter_AddRefs(newTreeOwner));
 | ||
|     MaybeDisablePersistence(sizeSpec, newTreeOwner);
 | ||
|   }
 | ||
| 
 | ||
|   if (aDialog && aArgv) {
 | ||
|     MOZ_ASSERT(win);
 | ||
|     NS_ENSURE_TRUE(win, NS_ERROR_UNEXPECTED);
 | ||
| 
 | ||
|     // Set the args on the new window.
 | ||
|     MOZ_TRY(win->SetArguments(aArgv));
 | ||
|   }
 | ||
| 
 | ||
|   /* allow a window that we found by name to keep its name (important for cases
 | ||
|      like _self where the given name is different (and invalid)).  Also, _blank
 | ||
|      is not a window name. */
 | ||
|   if (windowNeedsName) {
 | ||
|     if (nameSpecified && !name.LowerCaseEqualsLiteral("_blank")) {
 | ||
|       MOZ_ALWAYS_SUCCEEDS(newBC->SetName(name));
 | ||
|     } else {
 | ||
|       MOZ_ALWAYS_SUCCEEDS(newBC->SetName(u""_ns));
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // Now we have to set the right opener principal on the new window.  Note
 | ||
|   // that we have to do this _before_ starting any URI loads, thanks to the
 | ||
|   // sync nature of javascript: loads.
 | ||
|   //
 | ||
|   // Note: The check for the current JSContext isn't necessarily sensical.
 | ||
|   // It's just designed to preserve old semantics during a mass-conversion
 | ||
|   // patch.
 | ||
|   // Bug 1498605 verify usages of systemPrincipal here
 | ||
|   JSContext* cx = nsContentUtils::GetCurrentJSContext();
 | ||
|   nsCOMPtr<nsIPrincipal> subjectPrincipal =
 | ||
|       cx ? nsContentUtils::SubjectPrincipal()
 | ||
|          : nsContentUtils::GetSystemPrincipal();
 | ||
| 
 | ||
|   if (windowIsNew) {
 | ||
|     if (subjectPrincipal &&
 | ||
|         !nsContentUtils::IsSystemOrExpandedPrincipal(subjectPrincipal) &&
 | ||
|         newBC->IsContent()) {
 | ||
|       MOZ_DIAGNOSTIC_ASSERT(
 | ||
|           subjectPrincipal->OriginAttributesRef().EqualsIgnoringFPD(
 | ||
|               newBC->OriginAttributesRef()));
 | ||
|     }
 | ||
| 
 | ||
|     bool autoPrivateBrowsing =
 | ||
|         Preferences::GetBool("browser.privatebrowsing.autostart");
 | ||
| 
 | ||
|     if (!autoPrivateBrowsing &&
 | ||
|         (chromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW)) {
 | ||
|       if (newBC->IsChrome()) {
 | ||
|         newBC->SetUsePrivateBrowsing(false);
 | ||
|       }
 | ||
|       MOZ_DIAGNOSTIC_ASSERT(
 | ||
|           !newBC->UsePrivateBrowsing(),
 | ||
|           "CHROME_NON_PRIVATE_WINDOW passed, but got private window");
 | ||
|     } else if (autoPrivateBrowsing ||
 | ||
|                (chromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW)) {
 | ||
|       if (newBC->IsChrome()) {
 | ||
|         newBC->SetUsePrivateBrowsing(true);
 | ||
|       }
 | ||
|       MOZ_DIAGNOSTIC_ASSERT(
 | ||
|           newBC->UsePrivateBrowsing(),
 | ||
|           "CHROME_PRIVATE_WINDOW passed, but got non-private window");
 | ||
|     }
 | ||
| 
 | ||
|     // Now set the opener principal on the new window.  Note that we need to do
 | ||
|     // this no matter whether we were opened from JS; if there is nothing on
 | ||
|     // the JS stack, just use the principal of our parent window.  In those
 | ||
|     // cases we do _not_ set the parent window principal as the owner of the
 | ||
|     // load--since we really don't know who the owner is, just leave it null.
 | ||
|     NS_ASSERTION(win == newDocShell->GetWindow(), "Different windows??");
 | ||
| 
 | ||
|     // The principal of the initial about:blank document gets set up in
 | ||
|     // nsWindowWatcher::AddWindow. Make sure to call it. In the common case
 | ||
|     // this call already happened when the window was created, but
 | ||
|     // SetInitialPrincipalToSubject is safe to call multiple times.
 | ||
|     if (win) {
 | ||
|       nsCOMPtr<nsIContentSecurityPolicy> cspToInheritForAboutBlank;
 | ||
|       Maybe<nsILoadInfo::CrossOriginEmbedderPolicy> coepToInheritForAboutBlank;
 | ||
|       nsCOMPtr<mozIDOMWindowProxy> targetOpener = win->GetSameProcessOpener();
 | ||
|       nsCOMPtr<nsIDocShell> openerDocShell(do_GetInterface(targetOpener));
 | ||
|       if (openerDocShell) {
 | ||
|         RefPtr<Document> openerDoc =
 | ||
|             static_cast<nsDocShell*>(openerDocShell.get())->GetDocument();
 | ||
|         cspToInheritForAboutBlank = openerDoc ? openerDoc->GetCsp() : nullptr;
 | ||
|         coepToInheritForAboutBlank = openerDoc->GetEmbedderPolicy();
 | ||
|       }
 | ||
|       win->SetInitialPrincipalToSubject(cspToInheritForAboutBlank,
 | ||
|                                         coepToInheritForAboutBlank);
 | ||
| 
 | ||
|       if (aIsPopupSpam) {
 | ||
|         MOZ_ASSERT(!newBC->GetIsPopupSpam(),
 | ||
|                    "Who marked it as popup spam already???");
 | ||
|         // Make sure we don't mess up our counter even if the above assert
 | ||
|         // fails.
 | ||
|         if (!newBC->GetIsPopupSpam()) {
 | ||
|           MOZ_ALWAYS_SUCCEEDS(newBC->SetIsPopupSpam(true));
 | ||
|         }
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // We rely on CalculateChromeFlags to decide whether remote (out-of-process)
 | ||
|   // tabs should be used.
 | ||
|   MOZ_DIAGNOSTIC_ASSERT(
 | ||
|       newBC->UseRemoteTabs() ==
 | ||
|       !!(chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW));
 | ||
|   MOZ_DIAGNOSTIC_ASSERT(
 | ||
|       newBC->UseRemoteSubframes() ==
 | ||
|       !!(chromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW));
 | ||
| 
 | ||
|   nsCOMPtr<nsPIDOMWindowInner> pInnerWin =
 | ||
|       parentWindow ? parentWindow->GetCurrentInnerWindow() : nullptr;
 | ||
|   ;
 | ||
|   RefPtr<nsDocShellLoadState> loadState = aLoadState;
 | ||
|   if (uriToLoad && loadState) {
 | ||
|     // If a URI was passed to this function, open that, not what was passed in
 | ||
|     // the original LoadState. See Bug 1515433.
 | ||
|     loadState->SetURI(uriToLoad);
 | ||
|   } else if (uriToLoad && aNavigate && !loadState) {
 | ||
|     RefPtr<WindowContext> context =
 | ||
|         pInnerWin ? pInnerWin->GetWindowContext() : nullptr;
 | ||
|     loadState = new nsDocShellLoadState(uriToLoad);
 | ||
| 
 | ||
|     loadState->SetSourceBrowsingContext(parentBC);
 | ||
|     loadState->SetAllowFocusMove(true);
 | ||
|     loadState->SetHasValidUserGestureActivation(
 | ||
|         context && context->HasValidTransientUserGestureActivation());
 | ||
|     if (parentBC) {
 | ||
|       loadState->SetTriggeringSandboxFlags(parentBC->GetSandboxFlags());
 | ||
|     }
 | ||
| 
 | ||
|     if (subjectPrincipal) {
 | ||
|       loadState->SetTriggeringPrincipal(subjectPrincipal);
 | ||
|     }
 | ||
| #ifndef ANDROID
 | ||
|     MOZ_ASSERT(subjectPrincipal,
 | ||
|                "nsWindowWatcher: triggeringPrincipal required");
 | ||
| #endif
 | ||
| 
 | ||
|     if (!aForceNoReferrer) {
 | ||
|       /* use the URL from the *extant* document, if any. The usual accessor
 | ||
|          GetDocument will synchronously create an about:blank document if
 | ||
|          it has no better answer, and we only care about a real document.
 | ||
|          Also using GetDocument to force document creation seems to
 | ||
|          screw up focus in the hidden window; see bug 36016.
 | ||
|       */
 | ||
|       RefPtr<Document> doc = GetEntryDocument();
 | ||
|       if (!doc && parentWindow) {
 | ||
|         doc = parentWindow->GetExtantDoc();
 | ||
|       }
 | ||
|       if (doc) {
 | ||
|         auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
 | ||
|         loadState->SetReferrerInfo(referrerInfo);
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   if (loadState && cx) {
 | ||
|     nsGlobalWindowInner* win = xpc::CurrentWindowOrNull(cx);
 | ||
|     if (win) {
 | ||
|       nsCOMPtr<nsIContentSecurityPolicy> csp = win->GetCsp();
 | ||
|       loadState->SetCsp(csp);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   if (isNewToplevelWindow) {
 | ||
|     // Notify observers that the window is open and ready.
 | ||
|     // The window has not yet started to load a document.
 | ||
|     nsCOMPtr<nsIObserverService> obsSvc =
 | ||
|         mozilla::services::GetObserverService();
 | ||
|     if (obsSvc) {
 | ||
|       obsSvc->NotifyObservers(ToSupports(win), "toplevel-window-ready",
 | ||
|                               nullptr);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // Before loading the URI we want to be 100% sure that we use the correct
 | ||
|   // userContextId.
 | ||
|   MOZ_ASSERT_IF(newDocShell, CheckUserContextCompatibility(newDocShell));
 | ||
| 
 | ||
|   // If this tab or window has been opened by a window.open call, we have to
 | ||
|   // provide all the data needed to send a
 | ||
|   // webNavigation.onCreatedNavigationTarget event.
 | ||
|   if (parentDocShell && windowIsNew) {
 | ||
|     nsCOMPtr<nsIObserverService> obsSvc =
 | ||
|         mozilla::services::GetObserverService();
 | ||
| 
 | ||
|     if (obsSvc) {
 | ||
|       RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
 | ||
| 
 | ||
|       if (uriToLoad) {
 | ||
|         // The url notified in the webNavigation.onCreatedNavigationTarget
 | ||
|         // event.
 | ||
|         props->SetPropertyAsACString(u"url"_ns, uriToLoad->GetSpecOrDefault());
 | ||
|       }
 | ||
| 
 | ||
|       props->SetPropertyAsInterface(u"sourceTabDocShell"_ns, parentDocShell);
 | ||
|       props->SetPropertyAsInterface(u"createdTabDocShell"_ns,
 | ||
|                                     ToSupports(newDocShell));
 | ||
| 
 | ||
|       obsSvc->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
 | ||
|                               "webNavigation-createdNavigationTarget-from-js",
 | ||
|                               nullptr);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   if (uriToLoad && aNavigate) {
 | ||
|     // XXXBFCache Per spec this should effectively use
 | ||
|     // LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL when noopener is passed to
 | ||
|     // window.open(). Bug 1694993.
 | ||
|     loadState->SetLoadFlags(
 | ||
|         windowIsNew
 | ||
|             ? static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD)
 | ||
|             : static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_NONE));
 | ||
|     loadState->SetFirstParty(true);
 | ||
| 
 | ||
|     // Should this pay attention to errors returned by LoadURI?
 | ||
|     newBC->LoadURI(loadState);
 | ||
|   }
 | ||
| 
 | ||
|   // Copy the current session storage for the current domain. Don't perform the
 | ||
|   // copy if we're forcing noopener, however.
 | ||
|   if (!aForceNoOpener && subjectPrincipal && parentDocShell && newDocShell) {
 | ||
|     const RefPtr<SessionStorageManager> parentStorageManager =
 | ||
|         parentDocShell->GetBrowsingContext()->GetSessionStorageManager();
 | ||
|     const RefPtr<SessionStorageManager> newStorageManager =
 | ||
|         newDocShell->GetBrowsingContext()->GetSessionStorageManager();
 | ||
| 
 | ||
|     if (parentStorageManager && newStorageManager) {
 | ||
|       RefPtr<Storage> storage;
 | ||
|       parentStorageManager->GetStorage(
 | ||
|           pInnerWin, subjectPrincipal, subjectPrincipal,
 | ||
|           newBC->UsePrivateBrowsing(), getter_AddRefs(storage));
 | ||
|       if (storage) {
 | ||
|         newStorageManager->CloneStorage(storage);
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   if (isNewToplevelWindow) {
 | ||
|     nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
 | ||
|     newDocShell->GetTreeOwner(getter_AddRefs(newTreeOwner));
 | ||
|     SizeOpenedWindow(newTreeOwner, aParent, isCallerChrome, sizeSpec);
 | ||
|   }
 | ||
| 
 | ||
|   if (windowIsModal) {
 | ||
|     NS_ENSURE_TRUE(newDocShell, NS_ERROR_NOT_IMPLEMENTED);
 | ||
| 
 | ||
|     nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
 | ||
|     newDocShell->GetTreeOwner(getter_AddRefs(newTreeOwner));
 | ||
|     nsCOMPtr<nsIWebBrowserChrome> newChrome(do_GetInterface(newTreeOwner));
 | ||
| 
 | ||
|     // Throw an exception here if no web browser chrome is available,
 | ||
|     // we need that to show a modal window.
 | ||
|     NS_ENSURE_TRUE(newChrome, NS_ERROR_NOT_AVAILABLE);
 | ||
| 
 | ||
|     // Dispatch dialog events etc, but we only want to do that if
 | ||
|     // we're opening a modal content window (the helper classes are
 | ||
|     // no-ops if given no window), for chrome dialogs we don't want to
 | ||
|     // do any of that (it's done elsewhere for us).
 | ||
|     // Make sure we maintain the state on an outer window, because
 | ||
|     // that's where it lives; inner windows assert if you try to
 | ||
|     // maintain the state on them.
 | ||
|     nsAutoWindowStateHelper windowStateHelper(parentWindow);
 | ||
| 
 | ||
|     if (!windowStateHelper.DefaultEnabled()) {
 | ||
|       // Default to cancel not opening the modal window.
 | ||
|       NS_RELEASE(*aResult);
 | ||
| 
 | ||
|       return NS_OK;
 | ||
|     }
 | ||
| 
 | ||
|     bool isAppModal = false;
 | ||
|     nsCOMPtr<nsIBaseWindow> parentWindow(do_GetInterface(newTreeOwner));
 | ||
|     nsCOMPtr<nsIWidget> parentWidget;
 | ||
|     if (parentWindow) {
 | ||
|       parentWindow->GetMainWidget(getter_AddRefs(parentWidget));
 | ||
|       if (parentWidget) {
 | ||
|         isAppModal = parentWidget->IsRunningAppModal();
 | ||
|       }
 | ||
|     }
 | ||
|     if (parentWidget &&
 | ||
|         ((!newWindowShouldBeModal && parentIsModal) || isAppModal)) {
 | ||
|       parentWidget->SetFakeModal(true);
 | ||
|     } else {
 | ||
|       // Reset popup state while opening a modal dialog, and firing
 | ||
|       // events about the dialog, to prevent the current state from
 | ||
|       // being active the whole time a modal dialog is open.
 | ||
|       AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused);
 | ||
| 
 | ||
|       newChrome->ShowAsModal();
 | ||
|     }
 | ||
|   }
 | ||
|   // If a website opens a popup exit DOM fullscreen
 | ||
|   if (StaticPrefs::full_screen_api_exit_on_windowOpen() && windowIsNew &&
 | ||
|       aCalledFromJS && !hasChromeParent && !isCallerChrome && parentWindow) {
 | ||
|     Document::AsyncExitFullscreen(parentWindow->GetDoc());
 | ||
|   }
 | ||
| 
 | ||
|   if (aForceNoOpener && windowIsNew) {
 | ||
|     NS_RELEASE(*aResult);
 | ||
|   }
 | ||
| 
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::RegisterNotification(nsIObserver* aObserver) {
 | ||
|   // just a convenience method; it delegates to nsIObserverService
 | ||
| 
 | ||
|   if (!aObserver) {
 | ||
|     return NS_ERROR_INVALID_ARG;
 | ||
|   }
 | ||
| 
 | ||
|   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 | ||
|   if (!os) {
 | ||
|     return NS_ERROR_FAILURE;
 | ||
|   }
 | ||
| 
 | ||
|   nsresult rv = os->AddObserver(aObserver, "domwindowopened", false);
 | ||
|   if (NS_SUCCEEDED(rv)) {
 | ||
|     rv = os->AddObserver(aObserver, "domwindowclosed", false);
 | ||
|   }
 | ||
| 
 | ||
|   return rv;
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::UnregisterNotification(nsIObserver* aObserver) {
 | ||
|   // just a convenience method; it delegates to nsIObserverService
 | ||
| 
 | ||
|   if (!aObserver) {
 | ||
|     return NS_ERROR_INVALID_ARG;
 | ||
|   }
 | ||
| 
 | ||
|   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 | ||
|   if (!os) {
 | ||
|     return NS_ERROR_FAILURE;
 | ||
|   }
 | ||
| 
 | ||
|   os->RemoveObserver(aObserver, "domwindowopened");
 | ||
|   os->RemoveObserver(aObserver, "domwindowclosed");
 | ||
| 
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::GetWindowEnumerator(nsISimpleEnumerator** aResult) {
 | ||
|   if (!aResult) {
 | ||
|     return NS_ERROR_INVALID_ARG;
 | ||
|   }
 | ||
| 
 | ||
|   MutexAutoLock lock(mListLock);
 | ||
|   RefPtr<nsWatcherWindowEnumerator> enumerator =
 | ||
|       new nsWatcherWindowEnumerator(this);
 | ||
|   enumerator.forget(aResult);
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::GetNewPrompter(mozIDOMWindowProxy* aParent,
 | ||
|                                 nsIPrompt** aResult) {
 | ||
|   // This is for backwards compat only. Callers should just use the prompt
 | ||
|   // service directly.
 | ||
|   nsresult rv;
 | ||
|   nsCOMPtr<nsIPromptFactory> factory =
 | ||
|       do_GetService("@mozilla.org/prompter;1", &rv);
 | ||
|   NS_ENSURE_SUCCESS(rv, rv);
 | ||
|   return factory->GetPrompt(aParent, NS_GET_IID(nsIPrompt),
 | ||
|                             reinterpret_cast<void**>(aResult));
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::GetNewAuthPrompter(mozIDOMWindowProxy* aParent,
 | ||
|                                     nsIAuthPrompt** aResult) {
 | ||
|   // This is for backwards compat only. Callers should just use the prompt
 | ||
|   // service directly.
 | ||
|   nsresult rv;
 | ||
|   nsCOMPtr<nsIPromptFactory> factory =
 | ||
|       do_GetService("@mozilla.org/prompter;1", &rv);
 | ||
|   NS_ENSURE_SUCCESS(rv, rv);
 | ||
|   return factory->GetPrompt(aParent, NS_GET_IID(nsIAuthPrompt),
 | ||
|                             reinterpret_cast<void**>(aResult));
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::GetPrompt(mozIDOMWindowProxy* aParent, const nsIID& aIID,
 | ||
|                            void** aResult) {
 | ||
|   // This is for backwards compat only. Callers should just use the prompt
 | ||
|   // service directly.
 | ||
|   nsresult rv;
 | ||
|   nsCOMPtr<nsIPromptFactory> factory =
 | ||
|       do_GetService("@mozilla.org/prompter;1", &rv);
 | ||
|   NS_ENSURE_SUCCESS(rv, rv);
 | ||
|   rv = factory->GetPrompt(aParent, aIID, aResult);
 | ||
| 
 | ||
|   // Allow for an embedding implementation to not support nsIAuthPrompt2.
 | ||
|   if (rv == NS_NOINTERFACE && aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
 | ||
|     nsCOMPtr<nsIAuthPrompt> oldPrompt;
 | ||
|     rv = factory->GetPrompt(aParent, NS_GET_IID(nsIAuthPrompt),
 | ||
|                             getter_AddRefs(oldPrompt));
 | ||
|     NS_ENSURE_SUCCESS(rv, rv);
 | ||
| 
 | ||
|     NS_WrapAuthPrompt(oldPrompt, reinterpret_cast<nsIAuthPrompt2**>(aResult));
 | ||
|     if (!*aResult) {
 | ||
|       rv = NS_ERROR_NOT_AVAILABLE;
 | ||
|     }
 | ||
|   }
 | ||
|   return rv;
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::SetWindowCreator(nsIWindowCreator* aCreator) {
 | ||
|   mWindowCreator = aCreator;
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::HasWindowCreator(bool* aResult) {
 | ||
|   *aResult = mWindowCreator;
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::GetActiveWindow(mozIDOMWindowProxy** aActiveWindow) {
 | ||
|   *aActiveWindow = nullptr;
 | ||
|   nsFocusManager* fm = nsFocusManager::GetFocusManager();
 | ||
|   if (fm) {
 | ||
|     return fm->GetActiveWindow(aActiveWindow);
 | ||
|   }
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::AddWindow(mozIDOMWindowProxy* aWindow,
 | ||
|                            nsIWebBrowserChrome* aChrome) {
 | ||
|   if (!aWindow) {
 | ||
|     return NS_ERROR_INVALID_ARG;
 | ||
|   }
 | ||
| 
 | ||
|   {
 | ||
|     nsWatcherWindowEntry* info;
 | ||
|     MutexAutoLock lock(mListLock);
 | ||
| 
 | ||
|     // if we already have an entry for this window, adjust
 | ||
|     // its chrome mapping and return
 | ||
|     info = FindWindowEntry(aWindow);
 | ||
|     if (info) {
 | ||
|       nsCOMPtr<nsISupportsWeakReference> supportsweak(
 | ||
|           do_QueryInterface(aChrome));
 | ||
|       if (supportsweak) {
 | ||
|         supportsweak->GetWeakReference(getter_AddRefs(info->mChromeWeak));
 | ||
|       } else {
 | ||
|         info->mChrome = aChrome;
 | ||
|         info->mChromeWeak = nullptr;
 | ||
|       }
 | ||
|       return NS_OK;
 | ||
|     }
 | ||
| 
 | ||
|     // create a window info struct and add it to the list of windows
 | ||
|     info = new nsWatcherWindowEntry(aWindow, aChrome);
 | ||
|     if (!info) {
 | ||
|       return NS_ERROR_OUT_OF_MEMORY;
 | ||
|     }
 | ||
| 
 | ||
|     if (mOldestWindow) {
 | ||
|       info->InsertAfter(mOldestWindow->mOlder);
 | ||
|     } else {
 | ||
|       mOldestWindow = info;
 | ||
|     }
 | ||
|   }  // leave the mListLock
 | ||
| 
 | ||
|   // a window being added to us signifies a newly opened window.
 | ||
|   // send notifications.
 | ||
|   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 | ||
|   if (!os) {
 | ||
|     return NS_ERROR_FAILURE;
 | ||
|   }
 | ||
| 
 | ||
|   nsCOMPtr<nsISupports> domwin(do_QueryInterface(aWindow));
 | ||
|   return os->NotifyObservers(domwin, "domwindowopened", 0);
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::RemoveWindow(mozIDOMWindowProxy* aWindow) {
 | ||
|   // find the corresponding nsWatcherWindowEntry, remove it
 | ||
| 
 | ||
|   if (!aWindow) {
 | ||
|     return NS_ERROR_INVALID_ARG;
 | ||
|   }
 | ||
| 
 | ||
|   nsWatcherWindowEntry* info = FindWindowEntry(aWindow);
 | ||
|   if (info) {
 | ||
|     RemoveWindow(info);
 | ||
|     return NS_OK;
 | ||
|   }
 | ||
|   NS_WARNING("requested removal of nonexistent window");
 | ||
|   return NS_ERROR_INVALID_ARG;
 | ||
| }
 | ||
| 
 | ||
| nsWatcherWindowEntry* nsWindowWatcher::FindWindowEntry(
 | ||
|     mozIDOMWindowProxy* aWindow) {
 | ||
|   // find the corresponding nsWatcherWindowEntry
 | ||
|   nsWatcherWindowEntry* info;
 | ||
|   nsWatcherWindowEntry* listEnd;
 | ||
| 
 | ||
|   info = mOldestWindow;
 | ||
|   listEnd = 0;
 | ||
|   while (info != listEnd) {
 | ||
|     if (info->mWindow == aWindow) {
 | ||
|       return info;
 | ||
|     }
 | ||
|     info = info->mYounger;
 | ||
|     listEnd = mOldestWindow;
 | ||
|   }
 | ||
|   return 0;
 | ||
| }
 | ||
| 
 | ||
| nsresult nsWindowWatcher::RemoveWindow(nsWatcherWindowEntry* aInfo) {
 | ||
|   uint32_t count = mEnumeratorList.Length();
 | ||
| 
 | ||
|   {
 | ||
|     // notify the enumerators
 | ||
|     MutexAutoLock lock(mListLock);
 | ||
|     for (uint32_t ctr = 0; ctr < count; ++ctr) {
 | ||
|       mEnumeratorList[ctr]->WindowRemoved(aInfo);
 | ||
|     }
 | ||
| 
 | ||
|     // remove the element from the list
 | ||
|     if (aInfo == mOldestWindow) {
 | ||
|       mOldestWindow = aInfo->mYounger == mOldestWindow ? 0 : aInfo->mYounger;
 | ||
|     }
 | ||
|     aInfo->Unlink();
 | ||
|   }
 | ||
| 
 | ||
|   // a window being removed from us signifies a newly closed window.
 | ||
|   // send notifications.
 | ||
|   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
 | ||
|   if (os) {
 | ||
|     nsCOMPtr<nsISupports> domwin(do_QueryInterface(aInfo->mWindow));
 | ||
|     os->NotifyObservers(domwin, "domwindowclosed", 0);
 | ||
|   }
 | ||
| 
 | ||
|   delete aInfo;
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::GetChromeForWindow(mozIDOMWindowProxy* aWindow,
 | ||
|                                     nsIWebBrowserChrome** aResult) {
 | ||
|   if (!aWindow || !aResult) {
 | ||
|     return NS_ERROR_INVALID_ARG;
 | ||
|   }
 | ||
|   *aResult = 0;
 | ||
| 
 | ||
|   MutexAutoLock lock(mListLock);
 | ||
|   nsWatcherWindowEntry* info = FindWindowEntry(aWindow);
 | ||
|   if (info) {
 | ||
|     if (info->mChromeWeak) {
 | ||
|       return info->mChromeWeak->QueryReferent(
 | ||
|           NS_GET_IID(nsIWebBrowserChrome), reinterpret_cast<void**>(aResult));
 | ||
|     }
 | ||
|     *aResult = info->mChrome;
 | ||
|     NS_IF_ADDREF(*aResult);
 | ||
|   }
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| NS_IMETHODIMP
 | ||
| nsWindowWatcher::GetWindowByName(const nsAString& aTargetName,
 | ||
|                                  mozIDOMWindowProxy* aCurrentWindow,
 | ||
|                                  mozIDOMWindowProxy** aResult) {
 | ||
|   if (!aResult) {
 | ||
|     return NS_ERROR_INVALID_ARG;
 | ||
|   }
 | ||
| 
 | ||
|   *aResult = nullptr;
 | ||
| 
 | ||
|   BrowsingContext* currentContext =
 | ||
|       aCurrentWindow
 | ||
|           ? nsPIDOMWindowOuter::From(aCurrentWindow)->GetBrowsingContext()
 | ||
|           : nullptr;
 | ||
| 
 | ||
|   RefPtr<BrowsingContext> context =
 | ||
|       GetBrowsingContextByName(aTargetName, false, currentContext);
 | ||
| 
 | ||
|   if (context) {
 | ||
|     *aResult = do_AddRef(context->GetDOMWindow()).take();
 | ||
|     MOZ_ASSERT(*aResult);
 | ||
|   }
 | ||
| 
 | ||
|   return NS_OK;
 | ||
| }
 | ||
| 
 | ||
| bool nsWindowWatcher::AddEnumerator(nsWatcherWindowEnumerator* aEnumerator) {
 | ||
|   // (requires a lock; assumes it's called by someone holding the lock)
 | ||
|   // XXX(Bug 1631371) Check if this should use a fallible operation as it
 | ||
|   // pretended earlier, or change the return type to void.
 | ||
|   mEnumeratorList.AppendElement(aEnumerator);
 | ||
|   return true;
 | ||
| }
 | ||
| 
 | ||
| bool nsWindowWatcher::RemoveEnumerator(nsWatcherWindowEnumerator* aEnumerator) {
 | ||
|   // (requires a lock; assumes it's called by someone holding the lock)
 | ||
|   return mEnumeratorList.RemoveElement(aEnumerator);
 | ||
| }
 | ||
| 
 | ||
| nsresult nsWindowWatcher::URIfromURL(const nsACString& aURL,
 | ||
|                                      mozIDOMWindowProxy* aParent,
 | ||
|                                      nsIURI** aURI) {
 | ||
|   // Build the URI relative to the entry global.
 | ||
|   nsCOMPtr<nsPIDOMWindowInner> baseWindow = do_QueryInterface(GetEntryGlobal());
 | ||
| 
 | ||
|   // failing that, build it relative to the parent window, if possible
 | ||
|   if (!baseWindow && aParent) {
 | ||
|     baseWindow = nsPIDOMWindowOuter::From(aParent)->GetCurrentInnerWindow();
 | ||
|   }
 | ||
| 
 | ||
|   // failing that, use the given URL unmodified. It had better not be relative.
 | ||
| 
 | ||
|   nsIURI* baseURI = nullptr;
 | ||
| 
 | ||
|   // get baseWindow's document URI
 | ||
|   if (baseWindow) {
 | ||
|     if (Document* doc = baseWindow->GetDoc()) {
 | ||
|       baseURI = doc->GetDocBaseURI();
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // build and return the absolute URI
 | ||
|   return NS_NewURI(aURI, aURL, nullptr, baseURI);
 | ||
| }
 | ||
| 
 | ||
| // static
 | ||
| uint32_t nsWindowWatcher::CalculateChromeFlagsHelper(
 | ||
|     uint32_t aInitialFlags, const WindowFeatures& aFeatures,
 | ||
|     const SizeSpec& aSizeSpec, bool* presenceFlag, bool aHasChromeParent) {
 | ||
|   uint32_t chromeFlags = aInitialFlags;
 | ||
| 
 | ||
|   if (aFeatures.GetBoolWithDefault("titlebar", false, presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("close", false, presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("toolbar", false, presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_TOOLBAR;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("location", false, presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_LOCATIONBAR;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("personalbar", false, presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("status", false, presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_STATUSBAR;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("menubar", false, presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_MENUBAR;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("resizable", false, presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RESIZE;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("minimizable", false, presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_MIN;
 | ||
|   }
 | ||
| 
 | ||
|   if (aFeatures.GetBoolWithDefault("scrollbars", true, presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_SCROLLBARS;
 | ||
|   }
 | ||
| 
 | ||
|   if (aHasChromeParent) {
 | ||
|     return chromeFlags;
 | ||
|   }
 | ||
| 
 | ||
|   // Web content isn't allowed to control UI visibility separately, but only
 | ||
|   // whether to open a popup or not.
 | ||
|   //
 | ||
|   // The above code is still necessary to calculate `presenceFlag`.
 | ||
|   // (`ShouldOpenPopup` early returns and doesn't check all feature)
 | ||
| 
 | ||
|   if (ShouldOpenPopup(aFeatures, aSizeSpec)) {
 | ||
|     // Flags for opening a popup, that doesn't have the following:
 | ||
|     //   * nsIWebBrowserChrome::CHROME_TOOLBAR
 | ||
|     //   * nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR
 | ||
|     //   * nsIWebBrowserChrome::CHROME_MENUBAR
 | ||
|     return aInitialFlags | nsIWebBrowserChrome::CHROME_TITLEBAR |
 | ||
|            nsIWebBrowserChrome::CHROME_WINDOW_CLOSE |
 | ||
|            nsIWebBrowserChrome::CHROME_LOCATIONBAR |
 | ||
|            nsIWebBrowserChrome::CHROME_STATUSBAR |
 | ||
|            nsIWebBrowserChrome::CHROME_WINDOW_RESIZE |
 | ||
|            nsIWebBrowserChrome::CHROME_WINDOW_MIN |
 | ||
|            nsIWebBrowserChrome::CHROME_SCROLLBARS;
 | ||
|   }
 | ||
| 
 | ||
|   // Otherwise open the current/new tab in the current/new window
 | ||
|   // (depends on browser.link.open_newwindow).
 | ||
|   return aInitialFlags | nsIWebBrowserChrome::CHROME_ALL;
 | ||
| }
 | ||
| 
 | ||
| // static
 | ||
| uint32_t nsWindowWatcher::EnsureFlagsSafeForContent(uint32_t aChromeFlags,
 | ||
|                                                     bool aChromeURL) {
 | ||
|   aChromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
 | ||
|   aChromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
 | ||
|   aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
 | ||
|   aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
 | ||
|   aChromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_POPUP;
 | ||
|   /* Untrusted script is allowed to pose modal windows with a chrome
 | ||
|      scheme. This check could stand to be better. But it effectively
 | ||
|      prevents untrusted script from opening modal windows in general
 | ||
|      while still allowing alerts and the like. */
 | ||
|   if (!aChromeURL) {
 | ||
|     aChromeFlags &= ~(nsIWebBrowserChrome::CHROME_MODAL |
 | ||
|                       nsIWebBrowserChrome::CHROME_OPENAS_CHROME);
 | ||
|   }
 | ||
| 
 | ||
|   if (!(aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)) {
 | ||
|     aChromeFlags &= ~nsIWebBrowserChrome::CHROME_DEPENDENT;
 | ||
|   }
 | ||
| 
 | ||
|   return aChromeFlags;
 | ||
| }
 | ||
| 
 | ||
| // static
 | ||
| bool nsWindowWatcher::ShouldOpenPopup(const WindowFeatures& aFeatures,
 | ||
|                                       const SizeSpec& aSizeSpec) {
 | ||
|   if (aFeatures.IsEmpty()) {
 | ||
|     return false;
 | ||
|   }
 | ||
| 
 | ||
|   // Follow Google Chrome's behavior that opens a popup depending on
 | ||
|   // the following features.
 | ||
|   if (!aFeatures.GetBoolWithDefault("location", false) &&
 | ||
|       !aFeatures.GetBoolWithDefault("toolbar", false)) {
 | ||
|     return true;
 | ||
|   }
 | ||
| 
 | ||
|   if (!aFeatures.GetBoolWithDefault("menubar", false)) {
 | ||
|     return true;
 | ||
|   }
 | ||
| 
 | ||
|   if (!aFeatures.GetBoolWithDefault("resizable", true)) {
 | ||
|     return true;
 | ||
|   }
 | ||
| 
 | ||
|   if (!aFeatures.GetBoolWithDefault("scrollbars", false)) {
 | ||
|     return true;
 | ||
|   }
 | ||
| 
 | ||
|   if (!aFeatures.GetBoolWithDefault("status", false)) {
 | ||
|     return true;
 | ||
|   }
 | ||
| 
 | ||
|   // Follow Safari's behavior that opens a popup when width is specified.
 | ||
|   if (aSizeSpec.WidthSpecified()) {
 | ||
|     return true;
 | ||
|   }
 | ||
| 
 | ||
|   return false;
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * Calculate the chrome bitmask from a string list of features requested
 | ||
|  * from a child process. The feature string can only control whether to open a
 | ||
|  * new tab or a new popup.
 | ||
|  * @param aFeatures a string containing a list of named features
 | ||
|  * @param aSizeSpec the result of CalcSizeSpec
 | ||
|  * @return the chrome bitmask
 | ||
|  */
 | ||
| // static
 | ||
| uint32_t nsWindowWatcher::CalculateChromeFlagsForContent(
 | ||
|     const WindowFeatures& aFeatures, const SizeSpec& aSizeSpec) {
 | ||
|   if (aFeatures.IsEmpty()) {
 | ||
|     return nsIWebBrowserChrome::CHROME_ALL;
 | ||
|   }
 | ||
| 
 | ||
|   uint32_t chromeFlags = CalculateChromeFlagsHelper(
 | ||
|       nsIWebBrowserChrome::CHROME_WINDOW_BORDERS, aFeatures, aSizeSpec);
 | ||
| 
 | ||
|   return EnsureFlagsSafeForContent(chromeFlags);
 | ||
| }
 | ||
| 
 | ||
| /**
 | ||
|  * Calculate the chrome bitmask from a string list of features for a new
 | ||
|  * privileged window.
 | ||
|  * @param aFeatures a string containing a list of named chrome features
 | ||
|  * @param aDialog affects the assumptions made about unnamed features
 | ||
|  * @param aChromeURL true if the window is being sent to a chrome:// URL
 | ||
|  * @param aHasChromeParent true if the parent window is privileged
 | ||
|  * @return the chrome bitmask
 | ||
|  */
 | ||
| // static
 | ||
| uint32_t nsWindowWatcher::CalculateChromeFlagsForSystem(
 | ||
|     const WindowFeatures& aFeatures, const SizeSpec& aSizeSpec, bool aDialog,
 | ||
|     bool aChromeURL, bool aHasChromeParent) {
 | ||
|   MOZ_ASSERT(XRE_IsParentProcess());
 | ||
|   MOZ_ASSERT(nsContentUtils::LegacyIsCallerChromeOrNativeCode());
 | ||
| 
 | ||
|   uint32_t chromeFlags = 0;
 | ||
| 
 | ||
|   // The features string is made void by OpenWindowInternal
 | ||
|   // if nullptr was originally passed as the features string.
 | ||
|   if (aFeatures.IsEmpty()) {
 | ||
|     chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
 | ||
|     if (aDialog) {
 | ||
|       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
 | ||
|                      nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
 | ||
|     }
 | ||
|   } else {
 | ||
|     chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS;
 | ||
|   }
 | ||
| 
 | ||
|   /* This function has become complicated since browser windows and
 | ||
|      dialogs diverged. The difference is, browser windows assume all
 | ||
|      chrome not explicitly mentioned is off, if the features string
 | ||
|      is not null. Exceptions are some OS border chrome new with Mozilla.
 | ||
|      Dialogs interpret a (mostly) empty features string to mean
 | ||
|      "OS's choice," and also support an "all" flag explicitly disallowed
 | ||
|      in the standards-compliant window.(normal)open. */
 | ||
| 
 | ||
|   bool presenceFlag = false;
 | ||
|   if (aDialog && aFeatures.GetBoolWithDefault("all", false, &presenceFlag)) {
 | ||
|     chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
 | ||
|   }
 | ||
| 
 | ||
|   /* Next, allow explicitly named options to override the initial settings */
 | ||
|   chromeFlags = CalculateChromeFlagsHelper(chromeFlags, aFeatures, aSizeSpec,
 | ||
|                                            &presenceFlag, aHasChromeParent);
 | ||
| 
 | ||
|   // Determine whether the window is a private browsing window
 | ||
|   if (aFeatures.GetBoolWithDefault("private", false, &presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("non-private", false, &presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW;
 | ||
|   }
 | ||
| 
 | ||
|   // Determine whether the window should have remote tabs.
 | ||
|   bool remote = BrowserTabsRemoteAutostart();
 | ||
| 
 | ||
|   if (remote) {
 | ||
|     remote = !aFeatures.GetBoolWithDefault("non-remote", false, &presenceFlag);
 | ||
|   } else {
 | ||
|     remote = aFeatures.GetBoolWithDefault("remote", false, &presenceFlag);
 | ||
|   }
 | ||
| 
 | ||
|   if (remote) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
 | ||
|   }
 | ||
| 
 | ||
|   // Determine whether the window should have remote subframes
 | ||
|   bool fission = FissionAutostart();
 | ||
| 
 | ||
|   if (fission) {
 | ||
|     fission =
 | ||
|         !aFeatures.GetBoolWithDefault("non-fission", false, &presenceFlag);
 | ||
|   } else {
 | ||
|     fission = aFeatures.GetBoolWithDefault("fission", false, &presenceFlag);
 | ||
|   }
 | ||
| 
 | ||
|   if (fission) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
 | ||
|   }
 | ||
| 
 | ||
|   if (aFeatures.GetBoolWithDefault("popup", false, &presenceFlag)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_POPUP;
 | ||
|   }
 | ||
| 
 | ||
|   /* OK.
 | ||
|      Normal browser windows, in spite of a stated pattern of turning off
 | ||
|      all chrome not mentioned explicitly, will want the new OS chrome (window
 | ||
|      borders, titlebars, closebox) on, unless explicitly turned off.
 | ||
|      Dialogs, on the other hand, take the absence of any explicit settings
 | ||
|      to mean "OS' choice." */
 | ||
| 
 | ||
|   // default titlebar and closebox to "on," if not mentioned at all
 | ||
|   if (!(chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)) {
 | ||
|     if (!aFeatures.Exists("titlebar")) {
 | ||
|       chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
 | ||
|     }
 | ||
|     if (!aFeatures.Exists("close")) {
 | ||
|       chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   if (aDialog && !aFeatures.IsEmpty() && !presenceFlag) {
 | ||
|     chromeFlags = nsIWebBrowserChrome::CHROME_DEFAULT;
 | ||
|   }
 | ||
| 
 | ||
|   /* Finally, once all the above normal chrome has been divined, deal
 | ||
|      with the features that are more operating hints than appearance
 | ||
|      instructions. (Note modality implies dependence.) */
 | ||
| 
 | ||
|   if (aFeatures.GetBoolWithDefault("alwayslowered", false) ||
 | ||
|       aFeatures.GetBoolWithDefault("z-lock", false)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
 | ||
|   } else if (aFeatures.GetBoolWithDefault("alwaysraised", false)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
 | ||
|   }
 | ||
| 
 | ||
|   if (aFeatures.GetBoolWithDefault("suppressanimation", false)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_SUPPRESS_ANIMATION;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("alwaysontop", false)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_ALWAYS_ON_TOP;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("chrome", false)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("extrachrome", false)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_EXTRA;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("centerscreen", false)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_CENTER_SCREEN;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("dependent", false)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_DEPENDENT;
 | ||
|   }
 | ||
|   if (aFeatures.GetBoolWithDefault("modal", false)) {
 | ||
|     chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL |
 | ||
|                    nsIWebBrowserChrome::CHROME_DEPENDENT;
 | ||
|   }
 | ||
| 
 | ||
|   /* On mobile we want to ignore the dialog window feature, since the mobile UI
 | ||
|      does not provide any affordance for dialog windows. This does not interfere
 | ||
|      with dialog windows created through openDialog. */
 | ||
|   bool disableDialogFeature = false;
 | ||
|   nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID);
 | ||
| 
 | ||
|   branch->GetBoolPref("dom.disable_window_open_dialog_feature",
 | ||
|                       &disableDialogFeature);
 | ||
| 
 | ||
|   if (!disableDialogFeature) {
 | ||
|     if (aFeatures.GetBoolWithDefault("dialog", false)) {
 | ||
|       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   /* and dialogs need to have the last word. assume dialogs are dialogs,
 | ||
|      and opened as chrome, unless explicitly told otherwise. */
 | ||
|   if (aDialog) {
 | ||
|     if (!aFeatures.Exists("dialog")) {
 | ||
|       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG;
 | ||
|     }
 | ||
|     if (!aFeatures.Exists("chrome")) {
 | ||
|       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   /* missing
 | ||
|      chromeFlags->copy_history
 | ||
|    */
 | ||
| 
 | ||
|   // Check security state for use in determing window dimensions
 | ||
|   if (!aHasChromeParent) {
 | ||
|     chromeFlags = EnsureFlagsSafeForContent(chromeFlags, aChromeURL);
 | ||
|   }
 | ||
| 
 | ||
|   return chromeFlags;
 | ||
| }
 | ||
| 
 | ||
| already_AddRefed<BrowsingContext> nsWindowWatcher::GetBrowsingContextByName(
 | ||
|     const nsAString& aName, bool aForceNoOpener,
 | ||
|     BrowsingContext* aCurrentContext) {
 | ||
|   if (aName.IsEmpty()) {
 | ||
|     return nullptr;
 | ||
|   }
 | ||
| 
 | ||
|   if (aForceNoOpener && !nsContentUtils::IsSpecialName(aName)) {
 | ||
|     // Ignore all other names in the noopener case.
 | ||
|     return nullptr;
 | ||
|   }
 | ||
| 
 | ||
|   RefPtr<BrowsingContext> foundContext;
 | ||
|   if (aCurrentContext) {
 | ||
|     foundContext = aCurrentContext->FindWithName(aName);
 | ||
|   } else if (!nsContentUtils::IsSpecialName(aName)) {
 | ||
|     // If we are looking for an item and we don't have a docshell we are
 | ||
|     // checking on, let's just look in the chrome browsing context group!
 | ||
|     for (RefPtr<BrowsingContext> toplevel :
 | ||
|          BrowsingContextGroup::GetChromeGroup()->Toplevels()) {
 | ||
|       foundContext = toplevel->FindWithNameInSubtree(aName, *toplevel);
 | ||
|       if (foundContext) {
 | ||
|         break;
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   return foundContext.forget();
 | ||
| }
 | ||
| 
 | ||
| // static
 | ||
| void nsWindowWatcher::CalcSizeSpec(const WindowFeatures& aFeatures,
 | ||
|                                    bool aHasChromeParent, SizeSpec& aResult) {
 | ||
|   // https://drafts.csswg.org/cssom-view/#set-up-browsing-context-features
 | ||
|   // To set up browsing context features for a browsing context `target` given
 | ||
|   // a map `tokenizedFeatures`:
 | ||
| 
 | ||
|   // Step 1. Let `x` be null.
 | ||
|   // (implicit)
 | ||
| 
 | ||
|   // Step 2. Let `y` be null.
 | ||
|   // (implicit)
 | ||
| 
 | ||
|   // Step 3. Let `width` be null.
 | ||
|   // (implicit)
 | ||
| 
 | ||
|   // Step 4. Let `height` be null.
 | ||
|   // (implicit)
 | ||
| 
 | ||
|   // Step 5. If `tokenizedFeatures["left"]` exists:
 | ||
|   if (aFeatures.Exists("left")) {
 | ||
|     // Step 5.1. Set `x` to the result of invoking the rules for parsing
 | ||
|     // integers on `tokenizedFeatures["left"]`.
 | ||
|     //
 | ||
|     // Step 5.2. If `x` is an error, set `x` to 0.
 | ||
|     int32_t x = aFeatures.GetInt("left");
 | ||
| 
 | ||
|     // Step 5.3. Optionally, clamp `x` in a user-agent-defined manner so that
 | ||
|     // the window does not move outside the Web-exposed available screen area.
 | ||
|     // (done later)
 | ||
| 
 | ||
|     // Step 5.4. Optionally, move `target`’s window such that the window’s
 | ||
|     // left edge is at the horizontal coordinate `x` relative to the left edge
 | ||
|     // of the Web-exposed screen area, measured in CSS pixels of target.
 | ||
|     // The positive axis is rightward.
 | ||
|     aResult.mLeft = x;
 | ||
|     aResult.mLeftSpecified = true;
 | ||
|   }
 | ||
| 
 | ||
|   // Step 6. If `tokenizedFeatures["top"]` exists:
 | ||
|   if (aFeatures.Exists("top")) {
 | ||
|     // Step 6.1. Set `y` to the result of invoking the rules for parsing
 | ||
|     // integers on `tokenizedFeatures["top"]`.
 | ||
|     //
 | ||
|     // Step 6.2. If `y` is an error, set `y` to 0.
 | ||
|     int32_t y = aFeatures.GetInt("top");
 | ||
| 
 | ||
|     // Step 6.3. Optionally, clamp `y` in a user-agent-defined manner so that
 | ||
|     // the window does not move outside the Web-exposed available screen area.
 | ||
|     // (done later)
 | ||
| 
 | ||
|     // Step 6.4. Optionally, move `target`’s window such that the window’s top
 | ||
|     // edge is at the vertical coordinate `y` relative to the top edge of the
 | ||
|     // Web-exposed screen area, measured in CSS pixels of target. The positive
 | ||
|     // axis is downward.
 | ||
|     aResult.mTop = y;
 | ||
|     aResult.mTopSpecified = true;
 | ||
|   }
 | ||
| 
 | ||
|   // Non-standard extension.
 | ||
|   // Not exposed to web content.
 | ||
|   if (aHasChromeParent && aFeatures.Exists("outerwidth")) {
 | ||
|     int32_t width = aFeatures.GetInt("outerwidth");
 | ||
|     if (width) {
 | ||
|       aResult.mOuterWidth = width;
 | ||
|       aResult.mOuterWidthSpecified = true;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   if (!aResult.mOuterWidthSpecified) {
 | ||
|     // Step 7. If `tokenizedFeatures["width"]` exists:
 | ||
|     if (aFeatures.Exists("width")) {
 | ||
|       // Step 7.1. Set `width` to the result of invoking the rules for parsing
 | ||
|       // integers on `tokenizedFeatures["width"]`.
 | ||
|       //
 | ||
|       // Step 7.2. If `width` is an error, set `width` to 0.
 | ||
|       int32_t width = aFeatures.GetInt("width");
 | ||
| 
 | ||
|       // Step 7.3. If `width` is not 0:
 | ||
|       if (width) {
 | ||
|         // Step 7.3.1. Optionally, clamp `width` in a user-agent-defined manner
 | ||
|         // so that the window does not get too small or bigger than the
 | ||
|         // Web-exposed available screen area.
 | ||
|         // (done later)
 | ||
| 
 | ||
|         // Step 7.3.2. Optionally, size `target`’s window by moving its right
 | ||
|         // edge such that the distance between the left and right edges of the
 | ||
|         // viewport are `width` CSS pixels of target.
 | ||
|         aResult.mInnerWidth = width;
 | ||
|         aResult.mInnerWidthSpecified = true;
 | ||
| 
 | ||
|         // Step 7.3.3. Optionally, move target’s window in a user-agent-defined
 | ||
|         // manner so that it does not grow outside the Web-exposed available
 | ||
|         // screen area.
 | ||
|         // (done later)
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // Non-standard extension.
 | ||
|   // Not exposed to web content.
 | ||
|   if (aHasChromeParent && aFeatures.Exists("outerheight")) {
 | ||
|     int32_t height = aFeatures.GetInt("outerheight");
 | ||
|     if (height) {
 | ||
|       aResult.mOuterHeight = height;
 | ||
|       aResult.mOuterHeightSpecified = true;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   if (!aResult.mOuterHeightSpecified) {
 | ||
|     // Step 8. If `tokenizedFeatures["height"]` exists:
 | ||
|     if (aFeatures.Exists("height")) {
 | ||
|       // Step 8.1. Set `height` to the result of invoking the rules for parsing
 | ||
|       // integers on `tokenizedFeatures["height"]`.
 | ||
|       //
 | ||
|       // Step 8.2. If `height` is an error, set `height` to 0.
 | ||
|       int32_t height = aFeatures.GetInt("height");
 | ||
| 
 | ||
|       // Step 8.3. If `height` is not 0:
 | ||
|       if (height) {
 | ||
|         // Step 8.3.1. Optionally, clamp `height` in a user-agent-defined manner
 | ||
|         // so that the window does not get too small or bigger than the
 | ||
|         // Web-exposed available screen area.
 | ||
|         // (done later)
 | ||
| 
 | ||
|         // Step 8.3.2. Optionally, size `target`’s window by moving its bottom
 | ||
|         // edge such that the distance between the top and bottom edges of the
 | ||
|         // viewport are `height` CSS pixels of target.
 | ||
|         aResult.mInnerHeight = height;
 | ||
|         aResult.mInnerHeightSpecified = true;
 | ||
| 
 | ||
|         // Step 8.3.3. Optionally, move target’s window in a user-agent-defined
 | ||
|         // manner so that it does not grow outside the Web-exposed available
 | ||
|         // screen area.
 | ||
|         // (done later)
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // NOTE: The value is handled only on chrome-priv code.
 | ||
|   // See nsWindowWatcher::SizeOpenedWindow.
 | ||
|   aResult.mLockAspectRatio =
 | ||
|       aFeatures.GetBoolWithDefault("lockaspectratio", false);
 | ||
| }
 | ||
| 
 | ||
| /* Size and position a new window according to aSizeSpec. This method
 | ||
|    is assumed to be called after the window has already been given
 | ||
|    a default position and size; thus its current position and size are
 | ||
|    accurate defaults. The new window is made visible at method end.
 | ||
|    @param aTreeOwner
 | ||
|           The top-level nsIDocShellTreeOwner of the newly opened window.
 | ||
|    @param aParent (optional)
 | ||
|           The parent window from which to inherit zoom factors from if
 | ||
|           aOpenerFullZoom is none.
 | ||
|    @param aIsCallerChrome
 | ||
|           True if the code requesting the new window is privileged.
 | ||
|    @param aSizeSpec
 | ||
|           The size that the new window should be.
 | ||
|    @param aOpenerFullZoom
 | ||
|           If not nothing, a zoom factor to scale the content to.
 | ||
| */
 | ||
| void nsWindowWatcher::SizeOpenedWindow(nsIDocShellTreeOwner* aTreeOwner,
 | ||
|                                        mozIDOMWindowProxy* aParent,
 | ||
|                                        bool aIsCallerChrome,
 | ||
|                                        const SizeSpec& aSizeSpec,
 | ||
|                                        const Maybe<float>& aOpenerFullZoom) {
 | ||
|   // We should only be sizing top-level windows if we're in the parent
 | ||
|   // process.
 | ||
|   MOZ_ASSERT(XRE_IsParentProcess());
 | ||
| 
 | ||
|   // position and size of window
 | ||
|   int32_t left = 0, top = 0, width = 100, height = 100;
 | ||
|   // difference between chrome and content size
 | ||
|   int32_t chromeWidth = 0, chromeHeight = 0;
 | ||
|   // whether the window size spec refers to chrome or content
 | ||
|   bool sizeChromeWidth = true, sizeChromeHeight = true;
 | ||
| 
 | ||
|   // get various interfaces for aDocShellItem, used throughout this method
 | ||
|   nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(aTreeOwner));
 | ||
|   if (!treeOwnerAsWin) {  // we'll need this to actually size the docshell
 | ||
|     return;
 | ||
|   }
 | ||
| 
 | ||
|   double openerZoom = aOpenerFullZoom.valueOr(1.0);
 | ||
|   if (aParent && aOpenerFullZoom.isNothing()) {
 | ||
|     nsCOMPtr<nsPIDOMWindowOuter> piWindow = nsPIDOMWindowOuter::From(aParent);
 | ||
|     if (Document* doc = piWindow->GetDoc()) {
 | ||
|       if (nsPresContext* presContext = doc->GetPresContext()) {
 | ||
|         openerZoom = presContext->GetFullZoom();
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   double scale = 1.0;
 | ||
|   treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
 | ||
| 
 | ||
|   /* The current position and size will be unchanged if not specified
 | ||
|      (and they fit entirely onscreen). Also, calculate the difference
 | ||
|      between chrome and content sizes on aDocShellItem's window.
 | ||
|      This latter point becomes important if chrome and content
 | ||
|      specifications are mixed in aFeatures, and when bringing the window
 | ||
|      back from too far off the right or bottom edges of the screen. */
 | ||
| 
 | ||
|   treeOwnerAsWin->GetPositionAndSize(&left, &top, &width, &height);
 | ||
|   left = NSToIntRound(left / scale);
 | ||
|   top = NSToIntRound(top / scale);
 | ||
|   width = NSToIntRound(width / scale);
 | ||
|   height = NSToIntRound(height / scale);
 | ||
|   {
 | ||
|     int32_t contentWidth, contentHeight;
 | ||
|     bool hasPrimaryContent = false;
 | ||
|     aTreeOwner->GetHasPrimaryContent(&hasPrimaryContent);
 | ||
|     if (hasPrimaryContent) {
 | ||
|       aTreeOwner->GetPrimaryContentSize(&contentWidth, &contentHeight);
 | ||
|     } else {
 | ||
|       aTreeOwner->GetRootShellSize(&contentWidth, &contentHeight);
 | ||
|     }
 | ||
|     chromeWidth = width - contentWidth;
 | ||
|     chromeHeight = height - contentHeight;
 | ||
|   }
 | ||
| 
 | ||
|   // Set up left/top
 | ||
|   if (aSizeSpec.mLeftSpecified) {
 | ||
|     left = NSToIntRound(aSizeSpec.mLeft * openerZoom);
 | ||
|   }
 | ||
| 
 | ||
|   if (aSizeSpec.mTopSpecified) {
 | ||
|     top = NSToIntRound(aSizeSpec.mTop * openerZoom);
 | ||
|   }
 | ||
| 
 | ||
|   // Set up width
 | ||
|   if (aSizeSpec.mOuterWidthSpecified) {
 | ||
|     width = NSToIntRound(aSizeSpec.mOuterWidth * openerZoom);
 | ||
|   } else if (aSizeSpec.mInnerWidthSpecified) {
 | ||
|     sizeChromeWidth = false;
 | ||
|     width = NSToIntRound(aSizeSpec.mInnerWidth * openerZoom);
 | ||
|   }
 | ||
| 
 | ||
|   // Set up height
 | ||
|   if (aSizeSpec.mOuterHeightSpecified) {
 | ||
|     height = NSToIntRound(aSizeSpec.mOuterHeight * openerZoom);
 | ||
|   } else if (aSizeSpec.mInnerHeightSpecified) {
 | ||
|     sizeChromeHeight = false;
 | ||
|     height = NSToIntRound(aSizeSpec.mInnerHeight * openerZoom);
 | ||
|   }
 | ||
| 
 | ||
|   bool positionSpecified = aSizeSpec.PositionSpecified();
 | ||
| 
 | ||
|   // Check security state for use in determing window dimensions
 | ||
|   bool enabled = false;
 | ||
|   if (aIsCallerChrome) {
 | ||
|     // Only enable special priveleges for chrome when chrome calls
 | ||
|     // open() on a chrome window
 | ||
|     nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(aParent));
 | ||
|     enabled = !aParent || chromeWin;
 | ||
|   }
 | ||
| 
 | ||
|   if (!enabled) {
 | ||
|     // Security check failed.  Ensure all args meet minimum reqs.
 | ||
| 
 | ||
|     int32_t oldTop = top, oldLeft = left;
 | ||
| 
 | ||
|     // We'll also need the screen dimensions
 | ||
|     nsCOMPtr<nsIScreen> screen;
 | ||
|     nsCOMPtr<nsIScreenManager> screenMgr(
 | ||
|         do_GetService("@mozilla.org/gfx/screenmanager;1"));
 | ||
|     if (screenMgr)
 | ||
|       screenMgr->ScreenForRect(left, top, width, height,
 | ||
|                                getter_AddRefs(screen));
 | ||
|     if (screen) {
 | ||
|       int32_t screenLeft, screenTop, screenWidth, screenHeight;
 | ||
|       int32_t winWidth = width + (sizeChromeWidth ? 0 : chromeWidth),
 | ||
|               winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
 | ||
| 
 | ||
|       // Get screen dimensions (in device pixels)
 | ||
|       screen->GetAvailRect(&screenLeft, &screenTop, &screenWidth,
 | ||
|                            &screenHeight);
 | ||
|       // Convert them to CSS pixels
 | ||
|       screenLeft = NSToIntRound(screenLeft / scale);
 | ||
|       screenTop = NSToIntRound(screenTop / scale);
 | ||
|       screenWidth = NSToIntRound(screenWidth / scale);
 | ||
|       screenHeight = NSToIntRound(screenHeight / scale);
 | ||
| 
 | ||
|       if (aSizeSpec.SizeSpecified()) {
 | ||
|         if (!nsContentUtils::ShouldResistFingerprinting()) {
 | ||
|           /* Unlike position, force size out-of-bounds check only if
 | ||
|              size actually was specified. Otherwise, intrinsically sized
 | ||
|              windows are broken. */
 | ||
|           if (height < 100) {
 | ||
|             height = 100;
 | ||
|             winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
 | ||
|           }
 | ||
|           if (winHeight > screenHeight) {
 | ||
|             height = screenHeight - (sizeChromeHeight ? 0 : chromeHeight);
 | ||
|           }
 | ||
|           if (width < 100) {
 | ||
|             width = 100;
 | ||
|             winWidth = width + (sizeChromeWidth ? 0 : chromeWidth);
 | ||
|           }
 | ||
|           if (winWidth > screenWidth) {
 | ||
|             width = screenWidth - (sizeChromeWidth ? 0 : chromeWidth);
 | ||
|           }
 | ||
|         } else {
 | ||
|           int32_t targetContentWidth = 0;
 | ||
|           int32_t targetContentHeight = 0;
 | ||
| 
 | ||
|           nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
 | ||
|               chromeWidth, chromeHeight, screenWidth, screenHeight, width,
 | ||
|               height, sizeChromeWidth, sizeChromeHeight, &targetContentWidth,
 | ||
|               &targetContentHeight);
 | ||
| 
 | ||
|           if (aSizeSpec.mInnerWidthSpecified ||
 | ||
|               aSizeSpec.mOuterWidthSpecified) {
 | ||
|             width = targetContentWidth;
 | ||
|             winWidth = width + (sizeChromeWidth ? 0 : chromeWidth);
 | ||
|           }
 | ||
| 
 | ||
|           if (aSizeSpec.mInnerHeightSpecified ||
 | ||
|               aSizeSpec.mOuterHeightSpecified) {
 | ||
|             height = targetContentHeight;
 | ||
|             winHeight = height + (sizeChromeHeight ? 0 : chromeHeight);
 | ||
|           }
 | ||
|         }
 | ||
|       }
 | ||
| 
 | ||
|       CheckedInt<decltype(left)> leftPlusWinWidth = left;
 | ||
|       leftPlusWinWidth += winWidth;
 | ||
|       if (!leftPlusWinWidth.isValid() ||
 | ||
|           leftPlusWinWidth.value() > screenLeft + screenWidth) {
 | ||
|         left = screenLeft + screenWidth - winWidth;
 | ||
|       }
 | ||
|       if (left < screenLeft) {
 | ||
|         left = screenLeft;
 | ||
|       }
 | ||
| 
 | ||
|       CheckedInt<decltype(top)> topPlusWinHeight = top;
 | ||
|       topPlusWinHeight += winHeight;
 | ||
|       if (!topPlusWinHeight.isValid() ||
 | ||
|           topPlusWinHeight.value() > screenTop + screenHeight) {
 | ||
|         top = screenTop + screenHeight - winHeight;
 | ||
|       }
 | ||
|       if (top < screenTop) {
 | ||
|         top = screenTop;
 | ||
|       }
 | ||
| 
 | ||
|       if (top != oldTop || left != oldLeft) {
 | ||
|         positionSpecified = true;
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // size and position the window
 | ||
| 
 | ||
|   if (positionSpecified) {
 | ||
|     // Get the scale factor appropriate for the screen we're actually
 | ||
|     // positioning on.
 | ||
|     nsCOMPtr<nsIScreen> screen;
 | ||
|     nsCOMPtr<nsIScreenManager> screenMgr(
 | ||
|         do_GetService("@mozilla.org/gfx/screenmanager;1"));
 | ||
|     if (screenMgr) {
 | ||
|       screenMgr->ScreenForRect(left, top, 1, 1, getter_AddRefs(screen));
 | ||
|     }
 | ||
|     if (screen) {
 | ||
|       double cssToDevPixScale, desktopToDevPixScale;
 | ||
|       screen->GetDefaultCSSScaleFactor(&cssToDevPixScale);
 | ||
|       screen->GetContentsScaleFactor(&desktopToDevPixScale);
 | ||
|       double cssToDesktopScale = cssToDevPixScale / desktopToDevPixScale;
 | ||
|       int32_t screenLeft, screenTop, screenWd, screenHt;
 | ||
|       screen->GetRectDisplayPix(&screenLeft, &screenTop, &screenWd, &screenHt);
 | ||
|       // Adjust by desktop-pixel origin of the target screen when scaling
 | ||
|       // to convert from per-screen CSS-px coords to global desktop coords.
 | ||
|       treeOwnerAsWin->SetPositionDesktopPix(
 | ||
|           (left - screenLeft) * cssToDesktopScale + screenLeft,
 | ||
|           (top - screenTop) * cssToDesktopScale + screenTop);
 | ||
|     } else {
 | ||
|       // Couldn't find screen? This shouldn't happen.
 | ||
|       treeOwnerAsWin->SetPosition(left * scale, top * scale);
 | ||
|     }
 | ||
|     // This shouldn't be necessary, given the screen check above, but in case
 | ||
|     // moving the window didn't put it where we expected (e.g. due to issues
 | ||
|     // at the widget level, or whatever), let's re-fetch the scale factor for
 | ||
|     // wherever it really ended up
 | ||
|     treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&scale);
 | ||
|   }
 | ||
|   if (aSizeSpec.SizeSpecified()) {
 | ||
|     /* Prefer to trust the interfaces, which think in terms of pure
 | ||
|        chrome or content sizes. If we have a mix, use the chrome size
 | ||
|        adjusted by the chrome/content differences calculated earlier. */
 | ||
|     if (!sizeChromeWidth && !sizeChromeHeight) {
 | ||
|       bool hasPrimaryContent = false;
 | ||
|       aTreeOwner->GetHasPrimaryContent(&hasPrimaryContent);
 | ||
|       if (hasPrimaryContent) {
 | ||
|         aTreeOwner->SetPrimaryContentSize(width * scale, height * scale);
 | ||
|       } else {
 | ||
|         aTreeOwner->SetRootShellSize(width * scale, height * scale);
 | ||
|       }
 | ||
|     } else {
 | ||
|       if (!sizeChromeWidth) {
 | ||
|         width += chromeWidth;
 | ||
|       }
 | ||
|       if (!sizeChromeHeight) {
 | ||
|         height += chromeHeight;
 | ||
|       }
 | ||
|       treeOwnerAsWin->SetSize(width * scale, height * scale, false);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   if (aIsCallerChrome) {
 | ||
|     nsCOMPtr<nsIAppWindow> appWin = do_GetInterface(treeOwnerAsWin);
 | ||
|     if (appWin && aSizeSpec.mLockAspectRatio) {
 | ||
|       appWin->LockAspectRatio(true);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   treeOwnerAsWin->SetVisibility(true);
 | ||
| }
 | ||
| 
 | ||
| /* static */
 | ||
| int32_t nsWindowWatcher::GetWindowOpenLocation(nsPIDOMWindowOuter* aParent,
 | ||
|                                                uint32_t aChromeFlags,
 | ||
|                                                bool aCalledFromJS,
 | ||
|                                                bool aWidthSpecified,
 | ||
|                                                bool aIsForPrinting) {
 | ||
|   // These windows are not actually visible to the user, so we return the thing
 | ||
|   // that we can always handle.
 | ||
|   if (aIsForPrinting) {
 | ||
|     return nsIBrowserDOMWindow::OPEN_PRINT_BROWSER;
 | ||
|   }
 | ||
| 
 | ||
|   // Where should we open this?
 | ||
|   int32_t containerPref;
 | ||
|   if (NS_FAILED(
 | ||
|           Preferences::GetInt("browser.link.open_newwindow", &containerPref))) {
 | ||
|     // We couldn't read the user preference, so fall back on the default.
 | ||
|     return nsIBrowserDOMWindow::OPEN_NEWTAB;
 | ||
|   }
 | ||
| 
 | ||
|   bool isDisabledOpenNewWindow =
 | ||
|       aParent->GetFullScreen() &&
 | ||
|       Preferences::GetBool(
 | ||
|           "browser.link.open_newwindow.disabled_in_fullscreen");
 | ||
| 
 | ||
|   if (isDisabledOpenNewWindow &&
 | ||
|       (containerPref == nsIBrowserDOMWindow::OPEN_NEWWINDOW)) {
 | ||
|     containerPref = nsIBrowserDOMWindow::OPEN_NEWTAB;
 | ||
|   }
 | ||
| 
 | ||
|   if (containerPref != nsIBrowserDOMWindow::OPEN_NEWTAB &&
 | ||
|       containerPref != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
 | ||
|     // Just open a window normally
 | ||
|     return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
 | ||
|   }
 | ||
| 
 | ||
|   if (aCalledFromJS) {
 | ||
|     /* Now check our restriction pref.  The restriction pref is a power-user's
 | ||
|        fine-tuning pref. values:
 | ||
|        0: no restrictions - divert everything
 | ||
|        1: don't divert window.open at all
 | ||
|        2: don't divert window.open with features
 | ||
|     */
 | ||
|     int32_t restrictionPref =
 | ||
|         Preferences::GetInt("browser.link.open_newwindow.restriction", 2);
 | ||
|     if (restrictionPref < 0 || restrictionPref > 2) {
 | ||
|       restrictionPref = 2;  // Sane default behavior
 | ||
|     }
 | ||
| 
 | ||
|     if (isDisabledOpenNewWindow) {
 | ||
|       // In browser fullscreen, the window should be opened
 | ||
|       // in the current window with no features (see bug 803675)
 | ||
|       restrictionPref = 0;
 | ||
|     }
 | ||
| 
 | ||
|     if (restrictionPref == 1) {
 | ||
|       return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
 | ||
|     }
 | ||
| 
 | ||
|     if (restrictionPref == 2) {
 | ||
|       // Only continue if there are no width feature and no special
 | ||
|       // chrome flags - with the exception of the remoteness and private flags,
 | ||
|       // which might have been automatically flipped by Gecko.
 | ||
|       int32_t uiChromeFlags = aChromeFlags;
 | ||
|       uiChromeFlags &= ~(nsIWebBrowserChrome::CHROME_REMOTE_WINDOW |
 | ||
|                          nsIWebBrowserChrome::CHROME_FISSION_WINDOW |
 | ||
|                          nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
 | ||
|                          nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW |
 | ||
|                          nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME);
 | ||
|       if (uiChromeFlags != nsIWebBrowserChrome::CHROME_ALL || aWidthSpecified) {
 | ||
|         return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   return containerPref;
 | ||
| }
 |