/* -*- 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/. */ //#define USEWEAKREFS // (haven't quite figured that out yet) #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 "nsGlobalWindow.h" #include "nsIBaseWindow.h" #include "nsIBrowserDOMWindow.h" #include "nsIDocShell.h" #include "nsIDocShellLoadInfo.h" #include "nsIDocShellTreeItem.h" #include "nsIDocShellTreeOwner.h" #include "nsIDocumentLoader.h" #include "nsIDocument.h" #include "nsIDOMDocument.h" #include "nsIDOMWindow.h" #include "nsIDOMChromeWindow.h" #include "nsIDOMModalContentWindow.h" #include "nsIPrompt.h" #include "nsIScriptObjectPrincipal.h" #include "nsIScreen.h" #include "nsIScreenManager.h" #include "nsIScriptContext.h" #include "nsIObserverService.h" #include "nsIScriptSecurityManager.h" #include "nsXPCOM.h" #include "nsIURI.h" #include "nsIWebBrowser.h" #include "nsIWebBrowserChrome.h" #include "nsIWebNavigation.h" #include "nsIWindowCreator.h" #include "nsIWindowCreator2.h" #include "nsIXPConnect.h" #include "nsIXULRuntime.h" #include "nsPIDOMWindow.h" #include "nsIContentViewer.h" #include "nsIWindowProvider.h" #include "nsIMutableArray.h" #include "nsISupportsArray.h" #include "nsIDOMStorageManager.h" #include "nsIWidget.h" #include "nsFocusManager.h" #include "nsIPresShell.h" #include "nsPresContext.h" #include "nsContentUtils.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" #include "nsSandboxFlags.h" #include "mozilla/Preferences.h" #include "mozilla/dom/DOMStorage.h" #include "mozilla/dom/ScriptSettings.h" #ifdef USEWEAKREFS #include "nsIWeakReference.h" #endif using namespace mozilla; using namespace mozilla::dom; /**************************************************************** ******************** nsWatcherWindowEntry ********************** ****************************************************************/ class nsWindowWatcher; struct nsWatcherWindowEntry { nsWatcherWindowEntry(nsIDOMWindow* aWindow, nsIWebBrowserChrome* aChrome) { #ifdef USEWEAKREFS mWindow = do_GetWeakReference(aWindow); #else mWindow = aWindow; #endif nsCOMPtr supportsweak(do_QueryInterface(aChrome)); if (supportsweak) { supportsweak->GetWeakReference(getter_AddRefs(mChromeWeak)); } else { mChrome = aChrome; mChromeWeak = 0; } ReferenceSelf(); } ~nsWatcherWindowEntry() {} void InsertAfter(nsWatcherWindowEntry* aOlder); void Unlink(); void ReferenceSelf(); #ifdef USEWEAKREFS nsCOMPtr mWindow; #else // still not an owning ref nsIDOMWindow* mWindow; #endif 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 nsISimpleEnumerator { public: explicit nsWatcherWindowEnumerator(nsWindowWatcher* aWatcher); NS_IMETHOD HasMoreElements(bool* aResult) override; NS_IMETHOD GetNext(nsISupports** aResult) override; NS_DECL_ISUPPORTS protected: virtual ~nsWatcherWindowEnumerator(); private: friend class nsWindowWatcher; nsWatcherWindowEntry* FindNext(); void WindowRemoved(nsWatcherWindowEntry* aInfo); nsWindowWatcher* mWindowWatcher; nsWatcherWindowEntry* mCurrentPosition; }; NS_IMPL_ADDREF(nsWatcherWindowEnumerator) NS_IMPL_RELEASE(nsWatcherWindowEnumerator) NS_IMPL_QUERY_INTERFACE(nsWatcherWindowEnumerator, nsISimpleEnumerator) 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; #ifdef USEWEAKREFS while (mCurrentPosition) { CallQueryReferent(mCurrentPosition->mWindow, aResult); if (*aResult) { mCurrentPosition = FindNext(); break; } else { // window is gone! mWindowWatcher->RemoveWindow(mCurrentPosition); } } NS_IF_ADDREF(*aResult); #else if (mCurrentPosition) { CallQueryInterface(mCurrentPosition->mWindow, aResult); mCurrentPosition = FindNext(); } #endif return NS_OK; } 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 nsISupportsArray, return nullptr if it's empty, or * otherwise add its elements to an nsArray and return the new array. * - Otherwise, return an nsIArray with one element: aArguments. */ static already_AddRefed ConvertArgsToArray(nsISupports* aArguments) { if (!aArguments) { return nullptr; } nsCOMPtr array = do_QueryInterface(aArguments); if (array) { uint32_t argc = 0; array->GetLength(&argc); if (argc == 0) { return nullptr; } return array.forget(); } nsCOMPtr supArray = do_QueryInterface(aArguments); if (supArray) { uint32_t argc = 0; supArray->Count(&argc); if (argc == 0) { return nullptr; } nsCOMPtr mutableArray = do_CreateInstance(NS_ARRAY_CONTRACTID); NS_ENSURE_TRUE(mutableArray, nullptr); for (uint32_t i = 0; i < argc; i++) { nsCOMPtr elt; supArray->GetElementAt(i, getter_AddRefs(elt)); nsresult rv = mutableArray->AppendElement(elt, /* aWeak = */ false); NS_ENSURE_SUCCESS(rv, nullptr); } return mutableArray.forget(); } nsCOMPtr singletonArray = do_CreateInstance(NS_ARRAY_CONTRACTID); NS_ENSURE_TRUE(singletonArray, nullptr); nsresult rv = singletonArray->AppendElement(aArguments, /* aWeak = */ false); NS_ENSURE_SUCCESS(rv, nullptr); return singletonArray.forget(); } NS_IMETHODIMP nsWindowWatcher::OpenWindow(nsIDOMWindow* aParent, const char* aUrl, const char* aName, const char* aFeatures, nsISupports* aArguments, nsIDOMWindow** aResult) { nsCOMPtr argv = ConvertArgsToArray(aArguments); uint32_t argc = 0; if (argv) { argv->GetLength(&argc); } bool dialog = (argc != 0); return OpenWindowInternal(aParent, aUrl, aName, aFeatures, /* calledFromJS = */ false, dialog, /* navigate = */ true, nullptr, argv, /* aIsPopupSpam = */ false, /* aForceNoOpener = */ false, aResult); } struct SizeSpec { SizeSpec() : mLeftSpecified(false) , mTopSpecified(false) , mOuterWidthSpecified(false) , mOuterHeightSpecified(false) , mInnerWidthSpecified(false) , mInnerHeightSpecified(false) , mUseDefaultWidth(false) , mUseDefaultHeight(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; // If these booleans are true, don't look at the corresponding width values // even if they're specified -- they'll be bogus bool mUseDefaultWidth; bool mUseDefaultHeight; bool PositionSpecified() const { return mLeftSpecified || mTopSpecified; } bool SizeSpecified() const { return mOuterWidthSpecified || mOuterHeightSpecified || mInnerWidthSpecified || mInnerHeightSpecified; } }; NS_IMETHODIMP nsWindowWatcher::OpenWindow2(nsIDOMWindow* aParent, const char* aUrl, const char* aName, const char* aFeatures, bool aCalledFromScript, bool aDialog, bool aNavigate, nsITabParent* aOpeningTab, nsISupports* aArguments, bool aIsPopupSpam, bool aForceNoOpener, nsIDOMWindow** aResult) { nsCOMPtr 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, aOpeningTab, argv, aIsPopupSpam, aForceNoOpener, aResult); } nsresult nsWindowWatcher::OpenWindowInternal(nsIDOMWindow* aParent, const char* aUrl, const char* aName, const char* aFeatures, bool aCalledFromJS, bool aDialog, bool aNavigate, nsITabParent* aOpeningTab, nsIArray* aArgv, bool aIsPopupSpam, bool aForceNoOpener, nsIDOMWindow** aResult) { nsresult rv = NS_OK; bool isNewToplevelWindow = false; bool windowIsNew = false; bool windowNeedsName = false; bool windowIsModal = false; bool uriToLoadIsChrome = false; bool windowIsModalContentDialog = false; // Opening tabs are only ever passed to OpenWindowInternal if we're opening // a window from a remote tab. bool openedFromRemoteTab = !!aOpeningTab; uint32_t chromeFlags; nsAutoString name; // string version of aName nsAutoCString features; // string version of aFeatures nsCOMPtr uriToLoad; // from aUrl, if any nsCOMPtr parentTreeOwner; // from the parent window, if any nsCOMPtr newDocShellItem; // from the new window nsCOMPtr parent = do_QueryInterface(aParent); if (parent && parent->IsInnerWindow()) { NS_ENSURE_STATE(parent->IsCurrentInnerWindow()); aParent = parent->GetOuterWindow(); } MOZ_ASSERT_IF(openedFromRemoteTab, XRE_IsParentProcess()); NS_ENSURE_ARG_POINTER(aResult); *aResult = 0; if (!nsContentUtils::IsSafeToRunScript()) { nsContentUtils::WarnScriptWasIgnored(nullptr); return NS_ERROR_FAILURE; } GetWindowTreeOwner(aParent, getter_AddRefs(parentTreeOwner)); // We expect TabParent 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 && !openedFromRemoteTab) { rv = URIfromURL(aUrl, aParent, getter_AddRefs(uriToLoad)); if (NS_FAILED(rv)) { return rv; } uriToLoad->SchemeIs("chrome", &uriToLoadIsChrome); } bool nameSpecified = false; if (aName) { CopyUTF8toUTF16(aName, name); nameSpecified = true; } else { name.SetIsVoid(true); } bool featuresSpecified = false; if (aFeatures) { features.Assign(aFeatures); featuresSpecified = true; features.StripWhitespace(); } else { features.SetIsVoid(true); } // We only want to check for existing named windows if: // a) We're the child process // b) We're the parent process, and aOpeningTab wasn't passed // in. // This is because when using child processes, the parent process shouldn't // know or care about names - unless we're opening named windows from chrome. if (!aOpeningTab) { // try to find an extant window with the given name nsCOMPtr foundWindow = SafeGetWindowByName(name, aForceNoOpener, aParent); GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem)); } // 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. nsCOMPtr parentWindow = do_QueryInterface(aParent); nsCOMPtr parentDocShell; if (parentWindow) { parentDocShell = parentWindow->GetDocShell(); if (parentDocShell) { nsCOMPtr foundDocShell = do_QueryInterface(newDocShellItem); if (parentDocShell->IsSandboxedFrom(foundDocShell)) { return NS_ERROR_DOM_INVALID_ACCESS_ERR; } } } // 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. nsIDocument* doc = parentWindow->GetDoc(); hasChromeParent = doc && nsContentUtils::IsChromeDoc(doc) && !openedFromRemoteTab; } // Make sure we call CalculateChromeFlags() *before* we push the // callee context onto the context stack so that // CalculateChromeFlags() sees the actual caller when doing its // security checks. chromeFlags = CalculateChromeFlags(aParent, features.get(), featuresSpecified, aDialog, uriToLoadIsChrome, hasChromeParent, aCalledFromJS, openedFromRemoteTab); // If we are opening a window from a remote browser, the resulting window // should also be remote. MOZ_ASSERT_IF(openedFromRemoteTab, chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW); // If we're not called through our JS version of the API, and we got // our internal modal option, treat the window we're opening as a // modal content window (and set the modal chrome flag). if (!aCalledFromJS && aArgv && WinHasOption(features.get(), "-moz-internal-modal", 0, nullptr)) { windowIsModalContentDialog = true; // CHROME_MODAL gets inherited by dependent windows, which affects various // platform-specific window state (especially on OSX). So we need some way // to determine that this window was actually opened by nsGlobalWindow:: // ShowModalDialog(), and that somebody is actually going to be watching // for return values and all that. chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL_CONTENT_WINDOW; chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL; } SizeSpec sizeSpec; CalcSizeSpec(features.get(), sizeSpec); nsCOMPtr sm( do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID)); bool isCallerChrome = nsContentUtils::LegacyIsCallerChromeOrNativeCode() && !openedFromRemoteTab; dom::AutoJSAPI jsapiChromeGuard; bool windowTypeIsChrome = chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME; 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 parentGlobalObject = do_QueryInterface(aParent); if (!aParent) { jsapiChromeGuard.Init(); } else if (NS_WARN_IF(!jsapiChromeGuard.Init(parentGlobalObject))) { return NS_ERROR_UNEXPECTED; } } uint32_t activeDocsSandboxFlags = 0; if (!newDocShellItem) { // 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 (nsIDocument* doc = parentWindow->GetDoc()) { // Save sandbox flags for copying to new browsing context (docShell). activeDocsSandboxFlags = doc->GetSandboxFlags(); if (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. nsCOMPtr chromeWin = do_QueryInterface(aParent); if (!aDialog && !chromeWin && !(chromeFlags & (nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) { nsCOMPtr provider; if (parentTreeOwner) { provider = do_GetInterface(parentTreeOwner); } else if (XRE_IsContentProcess()) { // we're in a content process but we don't have a tabchild we can // use. provider = nsContentUtils::GetWindowProviderForContentProcess(); } if (provider) { nsCOMPtr newWindow; rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS, sizeSpec.PositionSpecified(), sizeSpec.SizeSpecified(), uriToLoad, name, features, &windowIsNew, getter_AddRefs(newWindow)); if (NS_SUCCEEDED(rv)) { GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem)); if (windowIsNew && newDocShellItem) { // Make sure to stop any loads happening in this window that the // window provider might have started. Otherwise if our caller // manipulates the window it just opened and then the load // completes their stuff will get blown away. nsCOMPtr webNav = do_QueryInterface(newDocShellItem); webNav->Stop(nsIWebNavigation::STOP_NETWORK); } } 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 (!newDocShellItem) { windowIsNew = true; isNewToplevelWindow = true; nsCOMPtr 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 parentWindow(do_GetInterface(parentTreeOwner)); nsCOMPtr 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 newChrome; /* If the window creator is an nsIWindowCreator2, we can give it 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. */ nsCOMPtr windowCreator2( do_QueryInterface(mWindowCreator)); if (windowCreator2) { uint32_t contextFlags = 0; bool popupConditions = false; // is the parent under popup conditions? if (parentWindow) { popupConditions = parentWindow->IsLoadingOrRunningTimeout(); } // chrome is always allowed, so clear the flag if the opener is chrome if (popupConditions) { popupConditions = !isCallerChrome; } if (popupConditions) { contextFlags |= nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT; } // B2G multi-screen support. mozDisplayId is returned from the // "display-changed" event, it is also platform-dependent. #ifdef MOZ_WIDGET_GONK int retval = WinHasOption(features.get(), "mozDisplayId", 0, nullptr); windowCreator2->SetScreenId(retval); #endif bool cancel = false; rv = windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags, contextFlags, uriToLoad, aOpeningTab, &cancel, getter_AddRefs(newChrome)); if (NS_SUCCEEDED(rv) && cancel) { newChrome = 0; // just in case rv = NS_ERROR_ABORT; } } else { rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags, getter_AddRefs(newChrome)); } if (newChrome) { /* It might be a chrome nsXULWindow, 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 newWindow(do_GetInterface(newChrome)); if (newWindow) { GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem)); } if (!newDocShellItem) { newDocShellItem = do_GetInterface(newChrome); } if (!newDocShellItem) { rv = NS_ERROR_FAILURE; } } } } // better have a window to use by this point if (!newDocShellItem) { return rv; } nsCOMPtr newDocShell(do_QueryInterface(newDocShellItem)); NS_ENSURE_TRUE(newDocShell, NS_ERROR_UNEXPECTED); // Set up sandboxing attributes if the window is new. // The flags can only be non-zero for new windows. if (activeDocsSandboxFlags != 0) { newDocShell->SetSandboxFlags(activeDocsSandboxFlags); if (parentWindow) { newDocShell->SetOnePermittedSandboxedNavigator( parentWindow->GetDocShell()); } } rv = ReadyOpenedDocShellItem(newDocShellItem, aParent, windowIsNew, aForceNoOpener, aResult); if (NS_FAILED(rv)) { return rv; } /* 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. */ if (isNewToplevelWindow) { /* at the moment, the strings "height=" or "width=" never happen outside a size specification, so we can do this the Q&D way. */ if (PL_strcasestr(features.get(), "width=") || PL_strcasestr(features.get(), "height=")) { nsCOMPtr newTreeOwner; newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner)); if (newTreeOwner) { newTreeOwner->SetPersistence(false, false, false); } } } if ((aDialog || windowIsModalContentDialog) && aArgv) { // Set the args on the new window. nsCOMPtr piwin(do_QueryInterface(*aResult)); NS_ENSURE_TRUE(piwin, NS_ERROR_UNEXPECTED); rv = piwin->SetArguments(aArgv); NS_ENSURE_SUCCESS(rv, rv); } /* 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")) { newDocShellItem->SetName(name); } else { newDocShellItem->SetName(EmptyString()); } } // 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. nsCOMPtr subjectPrincipal = nsContentUtils::GetCurrentJSContext() ? nsContentUtils::SubjectPrincipal() : nullptr; if (windowIsNew) { // 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. nsCOMPtr newWindow = do_QueryInterface(*aResult); NS_ASSERTION(newWindow == 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 (newWindow) { newWindow->SetInitialPrincipalToSubject(); if (aIsPopupSpam) { nsGlobalWindow* globalWin = static_cast(newWindow.get()); MOZ_ASSERT(!globalWin->IsPopupSpamWindow(), "Who marked it as popup spam already???"); if (!globalWin->IsPopupSpamWindow()) { // Make sure we don't mess up our // counter even if the above // assert fails. globalWin->SetIsPopupSpamWindow(true); } } } } // If all windows should be private, make sure the new window is also // private. Otherwise, see if the caller has explicitly requested a // private or non-private window. bool isPrivateBrowsingWindow = Preferences::GetBool("browser.privatebrowsing.autostart") || (!!(chromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) && !(chromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW)); // Otherwise, propagate the privacy status of the parent window, if // available, to the child. if (!isPrivateBrowsingWindow && !(chromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW)) { nsCOMPtr parentItem; GetWindowTreeItem(aParent, getter_AddRefs(parentItem)); nsCOMPtr parentContext = do_QueryInterface(parentItem); if (parentContext) { isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing(); } } // We rely on CalculateChromeFlags to decide whether remote (out-of-process) // tabs should be used. bool isRemoteWindow = !!(chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW); if (isNewToplevelWindow) { nsCOMPtr childRoot; newDocShellItem->GetRootTreeItem(getter_AddRefs(childRoot)); nsCOMPtr childContext = do_QueryInterface(childRoot); if (childContext) { childContext->SetPrivateBrowsing(isPrivateBrowsingWindow); childContext->SetRemoteTabs(isRemoteWindow); } } else if (windowIsNew) { nsCOMPtr childContext = do_QueryInterface(newDocShellItem); if (childContext) { childContext->SetPrivateBrowsing(isPrivateBrowsingWindow); childContext->SetRemoteTabs(isRemoteWindow); } } nsCOMPtr loadInfo; if (uriToLoad && aNavigate) { newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo)); NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE); if (subjectPrincipal) { loadInfo->SetOwner(subjectPrincipal); } nsCOMPtr referrerWindow = do_QueryInterface(GetEntryGlobal()); if (!referrerWindow) { referrerWindow = do_QueryInterface(aParent); } if (referrerWindow) { /* 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. */ nsCOMPtr doc = referrerWindow->GetExtantDoc(); if (doc) { // Set the referrer loadInfo->SetReferrer(doc->GetDocumentURI()); loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy()); } } } if (isNewToplevelWindow) { // Notify observers that the window is open and ready. // The window has not yet started to load a document. nsCOMPtr obsSvc = mozilla::services::GetObserverService(); if (obsSvc) { obsSvc->NotifyObservers(*aResult, "toplevel-window-ready", nullptr); } } if (uriToLoad && aNavigate) { newDocShell->LoadURI( uriToLoad, loadInfo, windowIsNew ? static_cast(nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD) : static_cast(nsIWebNavigation::LOAD_FLAGS_NONE), true); } // Copy the current session storage for the current domain. if (subjectPrincipal && parentDocShell) { nsCOMPtr parentStorageManager = do_QueryInterface(parentDocShell); nsCOMPtr newStorageManager = do_QueryInterface(newDocShell); if (parentStorageManager && newStorageManager) { nsCOMPtr storage; nsCOMPtr pWin = do_QueryInterface(aParent); nsCOMPtr pInnerWin = pWin->IsInnerWindow() ? pWin.get() : pWin->GetCurrentInnerWindow(); parentStorageManager->GetStorage(pInnerWin, subjectPrincipal, isPrivateBrowsingWindow, getter_AddRefs(storage)); if (storage) { newStorageManager->CloneStorage(storage); } } } if (isNewToplevelWindow) { SizeOpenedDocShellItem(newDocShellItem, aParent, isCallerChrome, sizeSpec); } // XXXbz isn't windowIsModal always true when windowIsModalContentDialog? if (windowIsModal || windowIsModalContentDialog) { nsCOMPtr newTreeOwner; newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner)); nsCOMPtr 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 ? parentWindow->GetOuterWindow() : nullptr); if (!windowStateHelper.DefaultEnabled()) { // Default to cancel not opening the modal window. NS_RELEASE(*aResult); return NS_OK; } bool isAppModal = false; nsCOMPtr parentWindow(do_GetInterface(newTreeOwner)); nsCOMPtr 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. nsAutoPopupStatePusher popupStatePusher(openAbused); newChrome->ShowAsModal(); } } 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 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 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); nsWatcherWindowEnumerator* enumerator = new nsWatcherWindowEnumerator(this); if (enumerator) { return CallQueryInterface(enumerator, aResult); } return NS_ERROR_OUT_OF_MEMORY; } NS_IMETHODIMP nsWindowWatcher::GetNewPrompter(nsIDOMWindow* aParent, nsIPrompt** aResult) { // This is for backwards compat only. Callers should just use the prompt // service directly. nsresult rv; nsCOMPtr factory = do_GetService("@mozilla.org/prompter;1", &rv); NS_ENSURE_SUCCESS(rv, rv); return factory->GetPrompt(aParent, NS_GET_IID(nsIPrompt), reinterpret_cast(aResult)); } NS_IMETHODIMP nsWindowWatcher::GetNewAuthPrompter(nsIDOMWindow* aParent, nsIAuthPrompt** aResult) { // This is for backwards compat only. Callers should just use the prompt // service directly. nsresult rv; nsCOMPtr factory = do_GetService("@mozilla.org/prompter;1", &rv); NS_ENSURE_SUCCESS(rv, rv); return factory->GetPrompt(aParent, NS_GET_IID(nsIAuthPrompt), reinterpret_cast(aResult)); } NS_IMETHODIMP nsWindowWatcher::GetPrompt(nsIDOMWindow* aParent, const nsIID& aIID, void** aResult) { // This is for backwards compat only. Callers should just use the prompt // service directly. nsresult rv; nsCOMPtr 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 oldPrompt; rv = factory->GetPrompt( aParent, NS_GET_IID(nsIAuthPrompt), getter_AddRefs(oldPrompt)); NS_ENSURE_SUCCESS(rv, rv); NS_WrapAuthPrompt(oldPrompt, reinterpret_cast(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(nsIDOMWindow** aActiveWindow) { *aActiveWindow = nullptr; nsCOMPtr fm = do_GetService(FOCUSMANAGER_CONTRACTID); if (fm) { return fm->GetActiveWindow(aActiveWindow); } return NS_OK; } NS_IMETHODIMP nsWindowWatcher::SetActiveWindow(nsIDOMWindow* aActiveWindow) { nsCOMPtr fm = do_GetService(FOCUSMANAGER_CONTRACTID); if (fm) { return fm->SetActiveWindow(aActiveWindow); } return NS_OK; } NS_IMETHODIMP nsWindowWatcher::AddWindow(nsIDOMWindow* aWindow, nsIWebBrowserChrome* aChrome) { if (!aWindow) { return NS_ERROR_INVALID_ARG; } #ifdef DEBUG { nsCOMPtr win(do_QueryInterface(aWindow)); NS_ASSERTION(win->IsOuterWindow(), "Uh, the active window must be an outer window!"); } #endif { 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 supportsweak( do_QueryInterface(aChrome)); if (supportsweak) { supportsweak->GetWeakReference(getter_AddRefs(info->mChromeWeak)); } else { info->mChrome = aChrome; info->mChromeWeak = 0; } 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 os = mozilla::services::GetObserverService(); if (!os) { return NS_ERROR_FAILURE; } nsCOMPtr domwin(do_QueryInterface(aWindow)); return os->NotifyObservers(domwin, "domwindowopened", 0); } NS_IMETHODIMP nsWindowWatcher::RemoveWindow(nsIDOMWindow* 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(nsIDOMWindow* aWindow) { // find the corresponding nsWatcherWindowEntry nsWatcherWindowEntry* info; nsWatcherWindowEntry* listEnd; #ifdef USEWEAKREFS nsresult rv; bool found; #endif info = mOldestWindow; listEnd = 0; #ifdef USEWEAKREFS rv = NS_OK; found = false; while (info != listEnd && NS_SUCCEEDED(rv)) { nsCOMPtr infoWindow(do_QueryReferent(info->mWindow)); if (!infoWindow) { // clean up dangling reference, while we're here rv = RemoveWindow(info); } else if (infoWindow.get() == aWindow) { return info; } info = info->mYounger; listEnd = mOldestWindow; } return 0; #else while (info != listEnd) { if (info->mWindow == aWindow) { return info; } info = info->mYounger; listEnd = mOldestWindow; } return 0; #endif } 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 os = mozilla::services::GetObserverService(); if (os) { #ifdef USEWEAKREFS nsCOMPtr domwin(do_QueryReferent(aInfo->mWindow)); if (domwin) { os->NotifyObservers(domwin, "domwindowclosed", 0); } // else bummer. since the window is gone, there's nothing to notify with. #else nsCOMPtr domwin(do_QueryInterface(aInfo->mWindow)); os->NotifyObservers(domwin, "domwindowclosed", 0); #endif } delete aInfo; return NS_OK; } NS_IMETHODIMP nsWindowWatcher::GetChromeForWindow(nsIDOMWindow* 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(aResult)); } *aResult = info->mChrome; NS_IF_ADDREF(*aResult); } return NS_OK; } NS_IMETHODIMP nsWindowWatcher::GetWindowByName(const char16_t* aTargetName, nsIDOMWindow* aCurrentWindow, nsIDOMWindow** aResult) { if (!aResult) { return NS_ERROR_INVALID_ARG; } *aResult = nullptr; nsCOMPtr treeItem; nsCOMPtr startItem; GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem)); if (startItem) { // Note: original requestor is null here, per idl comments startItem->FindItemWithName(aTargetName, nullptr, nullptr, getter_AddRefs(treeItem)); } else { // Note: original requestor is null here, per idl comments FindItemWithName(aTargetName, nullptr, nullptr, getter_AddRefs(treeItem)); } if (treeItem) { nsCOMPtr domWindow = treeItem->GetWindow(); domWindow.forget(aResult); } return NS_OK; } bool nsWindowWatcher::AddEnumerator(nsWatcherWindowEnumerator* aEnumerator) { // (requires a lock; assumes it's called by someone holding the lock) return mEnumeratorList.AppendElement(aEnumerator) != nullptr; } 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 char* aURL, nsIDOMWindow* aParent, nsIURI** aURI) { // Build the URI relative to the entry global. nsCOMPtr baseWindow = do_QueryInterface(GetEntryGlobal()); // failing that, build it relative to the parent window, if possible if (!baseWindow) { baseWindow = do_QueryInterface(aParent); } // failing that, use the given URL unmodified. It had better not be relative. nsIURI* baseURI = nullptr; // get baseWindow's document URI if (baseWindow) { if (nsIDocument* doc = baseWindow->GetDoc()) { baseURI = doc->GetDocBaseURI(); } } // build and return the absolute URI return NS_NewURI(aURI, aURL, baseURI); } #define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag) \ prefBranch->GetBoolPref(feature, &forceEnable); \ if (forceEnable && !(aDialog && !openedFromContentScript) && \ !(!openedFromContentScript && aHasChromeParent) && !aChromeURL) { \ chromeFlags |= flag; \ } else { \ chromeFlags |= \ WinHasOption(aFeatures, feature, 0, &presenceFlag) ? flag : 0; \ } /** * Calculate the chrome bitmask from a string list of features. * @param aParent the opener window * @param aFeatures a string containing a list of named chrome features * @param aNullFeatures true if aFeatures was a null pointer (which fact * is lost by its conversion to a string in the caller) * @param aDialog affects the assumptions made about unnamed features * @return the chrome bitmask */ // static uint32_t nsWindowWatcher::CalculateChromeFlags(nsIDOMWindow* aParent, const char* aFeatures, bool aFeaturesSpecified, bool aDialog, bool aChromeURL, bool aHasChromeParent, bool aCalledFromJS, bool aOpenedFromRemoteTab) { const bool inContentProcess = XRE_IsContentProcess(); uint32_t chromeFlags = 0; if (!aFeaturesSpecified || !aFeatures) { chromeFlags = nsIWebBrowserChrome::CHROME_ALL; if (aDialog) { chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | nsIWebBrowserChrome::CHROME_OPENAS_CHROME; } if (inContentProcess) { return chromeFlags; } } else { chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS; } bool openedFromContentScript = aOpenedFromRemoteTab ? aCalledFromJS : !nsContentUtils::LegacyIsCallerChromeOrNativeCode(); /* 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 && WinHasOption(aFeatures, "all", 0, &presenceFlag)) { chromeFlags = nsIWebBrowserChrome::CHROME_ALL; } /* Next, allow explicitly named options to override the initial settings */ if (!inContentProcess && !openedFromContentScript) { // Determine whether the window is a private browsing window chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag) ? nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW : 0; chromeFlags |= WinHasOption(aFeatures, "non-private", 0, &presenceFlag) ? nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW : 0; } if (!inContentProcess) { // Determine whether the window should have remote tabs. bool remote = BrowserTabsRemoteAutostart(); if (!openedFromContentScript) { if (remote) { remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag); } else { remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag); } } if (remote) { chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW; } } nsresult rv; nsCOMPtr prefBranch; nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, true); rv = prefs->GetBranch("dom.disable_window_open_feature.", getter_AddRefs(prefBranch)); NS_ENSURE_SUCCESS(rv, true); bool forceEnable = false; NS_CALCULATE_CHROME_FLAG_FOR("titlebar", nsIWebBrowserChrome::CHROME_TITLEBAR); NS_CALCULATE_CHROME_FLAG_FOR("close", nsIWebBrowserChrome::CHROME_WINDOW_CLOSE); NS_CALCULATE_CHROME_FLAG_FOR("toolbar", nsIWebBrowserChrome::CHROME_TOOLBAR); NS_CALCULATE_CHROME_FLAG_FOR("location", nsIWebBrowserChrome::CHROME_LOCATIONBAR); NS_CALCULATE_CHROME_FLAG_FOR("personalbar", nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR); NS_CALCULATE_CHROME_FLAG_FOR("status", nsIWebBrowserChrome::CHROME_STATUSBAR); NS_CALCULATE_CHROME_FLAG_FOR("menubar", nsIWebBrowserChrome::CHROME_MENUBAR); NS_CALCULATE_CHROME_FLAG_FOR("scrollbars", nsIWebBrowserChrome::CHROME_SCROLLBARS); NS_CALCULATE_CHROME_FLAG_FOR("resizable", nsIWebBrowserChrome::CHROME_WINDOW_RESIZE); NS_CALCULATE_CHROME_FLAG_FOR("minimizable", nsIWebBrowserChrome::CHROME_WINDOW_MIN); chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag) ? nsIWebBrowserChrome::CHROME_WINDOW_POPUP : 0; /* 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 (!PL_strcasestr(aFeatures, "titlebar")) { chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR; } if (!PL_strcasestr(aFeatures, "close")) { chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE; } } if (aDialog && aFeaturesSpecified && !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 (WinHasOption(aFeatures, "alwaysLowered", 0, nullptr) || WinHasOption(aFeatures, "z-lock", 0, nullptr)) { chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_LOWERED; } else if (WinHasOption(aFeatures, "alwaysRaised", 0, nullptr)) { chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED; } chromeFlags |= WinHasOption(aFeatures, "macsuppressanimation", 0, nullptr) ? nsIWebBrowserChrome::CHROME_MAC_SUPPRESS_ANIMATION : 0; chromeFlags |= WinHasOption(aFeatures, "chrome", 0, nullptr) ? nsIWebBrowserChrome::CHROME_OPENAS_CHROME : 0; chromeFlags |= WinHasOption(aFeatures, "extrachrome", 0, nullptr) ? nsIWebBrowserChrome::CHROME_EXTRA : 0; chromeFlags |= WinHasOption(aFeatures, "centerscreen", 0, nullptr) ? nsIWebBrowserChrome::CHROME_CENTER_SCREEN : 0; chromeFlags |= WinHasOption(aFeatures, "dependent", 0, nullptr) ? nsIWebBrowserChrome::CHROME_DEPENDENT : 0; chromeFlags |= WinHasOption(aFeatures, "modal", 0, nullptr) ? (nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT) : 0; /* 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 branch = do_QueryInterface(prefs); branch->GetBoolPref("dom.disable_window_open_dialog_feature", &disableDialogFeature); if (openedFromContentScript) { // If the caller context is content, we do not support the // dialog feature. See bug 1095236. disableDialogFeature = true; } if (!disableDialogFeature) { chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nullptr) ? nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0; } /* and dialogs need to have the last word. assume dialogs are dialogs, and opened as chrome, unless explicitly told otherwise. */ if (aDialog) { if (!PL_strcasestr(aFeatures, "dialog")) { chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG; } if (!PL_strcasestr(aFeatures, "chrome")) { chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME; } } /* missing chromeFlags->copy_history */ // Check security state for use in determing window dimensions if (openedFromContentScript || !aHasChromeParent) { // If priv check fails (or if we're called from chrome, but the // parent is not a chrome window), set all elements to minimum // reqs., else leave them alone. chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR; chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE; chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED; chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED; chromeFlags &= ~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) chromeFlags &= ~(nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_OPENAS_CHROME); } if (!(chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)) { // Remove the dependent flag if we're not opening as chrome chromeFlags &= ~nsIWebBrowserChrome::CHROME_DEPENDENT; } // Disable CHROME_OPENAS_DIALOG if the window is inside