michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=78: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: //#define USEWEAKREFS // (haven't quite figured that out yet) michael@0: michael@0: #include "nsWindowWatcher.h" michael@0: #include "nsAutoWindowStateHelper.h" michael@0: michael@0: #include "nsCRT.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsJSUtils.h" michael@0: #include "plstr.h" michael@0: michael@0: #include "nsIBaseWindow.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIDocShellLoadInfo.h" michael@0: #include "nsIDocShellTreeItem.h" michael@0: #include "nsIDocShellTreeOwner.h" michael@0: #include "nsIDocumentLoader.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMWindow.h" michael@0: #include "nsIDOMChromeWindow.h" michael@0: #include "nsIDOMModalContentWindow.h" michael@0: #include "nsIPrompt.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "nsIScreen.h" michael@0: #include "nsIScreenManager.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIWebBrowser.h" michael@0: #include "nsIWebBrowserChrome.h" michael@0: #include "nsIWebNavigation.h" michael@0: #include "nsIWindowCreator.h" michael@0: #include "nsIWindowCreator2.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsIXULRuntime.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIMarkupDocumentViewer.h" michael@0: #include "nsIContentViewer.h" michael@0: #include "nsIWindowProvider.h" michael@0: #include "nsIMutableArray.h" michael@0: #include "nsISupportsArray.h" michael@0: #include "nsIDOMStorage.h" michael@0: #include "nsIDOMStorageManager.h" michael@0: #include "nsIWidget.h" michael@0: #include "nsFocusManager.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsIPrefBranch.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsSandboxFlags.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: #ifdef USEWEAKREFS michael@0: #include "nsIWeakReference.h" michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: michael@0: /**************************************************************** michael@0: ******************** nsWatcherWindowEntry ********************** michael@0: ****************************************************************/ michael@0: michael@0: class nsWindowWatcher; michael@0: michael@0: struct nsWatcherWindowEntry { michael@0: michael@0: nsWatcherWindowEntry(nsIDOMWindow *inWindow, nsIWebBrowserChrome *inChrome) { michael@0: #ifdef USEWEAKREFS michael@0: mWindow = do_GetWeakReference(inWindow); michael@0: #else michael@0: mWindow = inWindow; michael@0: #endif michael@0: nsCOMPtr supportsweak(do_QueryInterface(inChrome)); michael@0: if (supportsweak) { michael@0: supportsweak->GetWeakReference(getter_AddRefs(mChromeWeak)); michael@0: } else { michael@0: mChrome = inChrome; michael@0: mChromeWeak = 0; michael@0: } michael@0: ReferenceSelf(); michael@0: } michael@0: ~nsWatcherWindowEntry() {} michael@0: michael@0: void InsertAfter(nsWatcherWindowEntry *inOlder); michael@0: void Unlink(); michael@0: void ReferenceSelf(); michael@0: michael@0: #ifdef USEWEAKREFS michael@0: nsCOMPtr mWindow; michael@0: #else // still not an owning ref michael@0: nsIDOMWindow *mWindow; michael@0: #endif michael@0: nsIWebBrowserChrome *mChrome; michael@0: nsWeakPtr mChromeWeak; michael@0: // each struct is in a circular, doubly-linked list michael@0: nsWatcherWindowEntry *mYounger, // next younger in sequence michael@0: *mOlder; michael@0: }; michael@0: michael@0: void nsWatcherWindowEntry::InsertAfter(nsWatcherWindowEntry *inOlder) michael@0: { michael@0: if (inOlder) { michael@0: mOlder = inOlder; michael@0: mYounger = inOlder->mYounger; michael@0: mOlder->mYounger = this; michael@0: if (mOlder->mOlder == mOlder) michael@0: mOlder->mOlder = this; michael@0: mYounger->mOlder = this; michael@0: if (mYounger->mYounger == mYounger) michael@0: mYounger->mYounger = this; michael@0: } michael@0: } michael@0: michael@0: void nsWatcherWindowEntry::Unlink() { michael@0: michael@0: mOlder->mYounger = mYounger; michael@0: mYounger->mOlder = mOlder; michael@0: ReferenceSelf(); michael@0: } michael@0: michael@0: void nsWatcherWindowEntry::ReferenceSelf() { michael@0: michael@0: mYounger = this; michael@0: mOlder = this; michael@0: } michael@0: michael@0: /**************************************************************** michael@0: ****************** nsWatcherWindowEnumerator ******************* michael@0: ****************************************************************/ michael@0: michael@0: class nsWatcherWindowEnumerator : public nsISimpleEnumerator { michael@0: michael@0: public: michael@0: nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher); michael@0: virtual ~nsWatcherWindowEnumerator(); michael@0: NS_IMETHOD HasMoreElements(bool *retval); michael@0: NS_IMETHOD GetNext(nsISupports **retval); michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: private: michael@0: friend class nsWindowWatcher; michael@0: michael@0: nsWatcherWindowEntry *FindNext(); michael@0: void WindowRemoved(nsWatcherWindowEntry *inInfo); michael@0: michael@0: nsWindowWatcher *mWindowWatcher; michael@0: nsWatcherWindowEntry *mCurrentPosition; michael@0: }; michael@0: michael@0: NS_IMPL_ADDREF(nsWatcherWindowEnumerator) michael@0: NS_IMPL_RELEASE(nsWatcherWindowEnumerator) michael@0: NS_IMPL_QUERY_INTERFACE(nsWatcherWindowEnumerator, nsISimpleEnumerator) michael@0: michael@0: nsWatcherWindowEnumerator::nsWatcherWindowEnumerator(nsWindowWatcher *inWatcher) michael@0: : mWindowWatcher(inWatcher), michael@0: mCurrentPosition(inWatcher->mOldestWindow) michael@0: { michael@0: mWindowWatcher->AddEnumerator(this); michael@0: mWindowWatcher->AddRef(); michael@0: } michael@0: michael@0: nsWatcherWindowEnumerator::~nsWatcherWindowEnumerator() michael@0: { michael@0: mWindowWatcher->RemoveEnumerator(this); michael@0: mWindowWatcher->Release(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWatcherWindowEnumerator::HasMoreElements(bool *retval) michael@0: { michael@0: if (!retval) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: *retval = mCurrentPosition? true : false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWatcherWindowEnumerator::GetNext(nsISupports **retval) michael@0: { michael@0: if (!retval) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: *retval = nullptr; michael@0: michael@0: #ifdef USEWEAKREFS michael@0: while (mCurrentPosition) { michael@0: CallQueryReferent(mCurrentPosition->mWindow, retval); michael@0: if (*retval) { michael@0: mCurrentPosition = FindNext(); michael@0: break; michael@0: } else // window is gone! michael@0: mWindowWatcher->RemoveWindow(mCurrentPosition); michael@0: } michael@0: NS_IF_ADDREF(*retval); michael@0: #else michael@0: if (mCurrentPosition) { michael@0: CallQueryInterface(mCurrentPosition->mWindow, retval); michael@0: mCurrentPosition = FindNext(); michael@0: } michael@0: #endif michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsWatcherWindowEntry * michael@0: nsWatcherWindowEnumerator::FindNext() michael@0: { michael@0: nsWatcherWindowEntry *info; michael@0: michael@0: if (!mCurrentPosition) michael@0: return 0; michael@0: michael@0: info = mCurrentPosition->mYounger; michael@0: return info == mWindowWatcher->mOldestWindow ? 0 : info; michael@0: } michael@0: michael@0: // if a window is being removed adjust the iterator's current position michael@0: void nsWatcherWindowEnumerator::WindowRemoved(nsWatcherWindowEntry *inInfo) { michael@0: michael@0: if (mCurrentPosition == inInfo) michael@0: mCurrentPosition = mCurrentPosition != inInfo->mYounger ? michael@0: inInfo->mYounger : 0; michael@0: } michael@0: michael@0: /**************************************************************** michael@0: *********************** nsWindowWatcher ************************ michael@0: ****************************************************************/ michael@0: michael@0: NS_IMPL_ADDREF(nsWindowWatcher) michael@0: NS_IMPL_RELEASE(nsWindowWatcher) michael@0: NS_IMPL_QUERY_INTERFACE(nsWindowWatcher, michael@0: nsIWindowWatcher, michael@0: nsIPromptFactory, michael@0: nsPIWindowWatcher) michael@0: michael@0: nsWindowWatcher::nsWindowWatcher() : michael@0: mEnumeratorList(), michael@0: mOldestWindow(0), michael@0: mListLock("nsWindowWatcher.mListLock") michael@0: { michael@0: } michael@0: michael@0: nsWindowWatcher::~nsWindowWatcher() michael@0: { michael@0: // delete data michael@0: while (mOldestWindow) michael@0: RemoveWindow(mOldestWindow); michael@0: } michael@0: michael@0: nsresult michael@0: nsWindowWatcher::Init() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Convert aArguments into either an nsIArray or nullptr. michael@0: * michael@0: * - If aArguments is nullptr, return nullptr. michael@0: * - If aArguments is an nsArray, return nullptr if it's empty, or otherwise michael@0: * return the array. michael@0: * - If aArguments is an nsISupportsArray, return nullptr if it's empty, or michael@0: * otherwise add its elements to an nsArray and return the new array. michael@0: * - Otherwise, return an nsIArray with one element: aArguments. michael@0: */ michael@0: static already_AddRefed michael@0: ConvertArgsToArray(nsISupports* aArguments) michael@0: { michael@0: if (!aArguments) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr array = do_QueryInterface(aArguments); michael@0: if (array) { michael@0: uint32_t argc = 0; michael@0: array->GetLength(&argc); michael@0: if (argc == 0) michael@0: return nullptr; michael@0: michael@0: return array.forget(); michael@0: } michael@0: michael@0: nsCOMPtr supArray = do_QueryInterface(aArguments); michael@0: if (supArray) { michael@0: uint32_t argc = 0; michael@0: supArray->Count(&argc); michael@0: if (argc == 0) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr mutableArray = michael@0: do_CreateInstance(NS_ARRAY_CONTRACTID); michael@0: NS_ENSURE_TRUE(mutableArray, nullptr); michael@0: michael@0: for (uint32_t i = 0; i < argc; i++) { michael@0: nsCOMPtr elt; michael@0: supArray->GetElementAt(i, getter_AddRefs(elt)); michael@0: nsresult rv = mutableArray->AppendElement(elt, /* aWeak = */ false); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: } michael@0: michael@0: return mutableArray.forget(); michael@0: } michael@0: michael@0: nsCOMPtr singletonArray = michael@0: do_CreateInstance(NS_ARRAY_CONTRACTID); michael@0: NS_ENSURE_TRUE(singletonArray, nullptr); michael@0: michael@0: nsresult rv = singletonArray->AppendElement(aArguments, /* aWeak = */ false); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: return singletonArray.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::OpenWindow(nsIDOMWindow *aParent, michael@0: const char *aUrl, michael@0: const char *aName, michael@0: const char *aFeatures, michael@0: nsISupports *aArguments, michael@0: nsIDOMWindow **_retval) michael@0: { michael@0: nsCOMPtr argv = ConvertArgsToArray(aArguments); michael@0: michael@0: uint32_t argc = 0; michael@0: if (argv) { michael@0: argv->GetLength(&argc); michael@0: } michael@0: bool dialog = (argc != 0); michael@0: michael@0: return OpenWindowInternal(aParent, aUrl, aName, aFeatures, michael@0: /* calledFromJS = */ false, dialog, michael@0: /* navigate = */ true, argv, _retval); michael@0: } michael@0: michael@0: struct SizeSpec { michael@0: SizeSpec() : michael@0: mLeftSpecified(false), michael@0: mTopSpecified(false), michael@0: mOuterWidthSpecified(false), michael@0: mOuterHeightSpecified(false), michael@0: mInnerWidthSpecified(false), michael@0: mInnerHeightSpecified(false), michael@0: mUseDefaultWidth(false), michael@0: mUseDefaultHeight(false) michael@0: {} michael@0: michael@0: int32_t mLeft; michael@0: int32_t mTop; michael@0: int32_t mOuterWidth; // Total window width michael@0: int32_t mOuterHeight; // Total window height michael@0: int32_t mInnerWidth; // Content area width michael@0: int32_t mInnerHeight; // Content area height michael@0: michael@0: bool mLeftSpecified; michael@0: bool mTopSpecified; michael@0: bool mOuterWidthSpecified; michael@0: bool mOuterHeightSpecified; michael@0: bool mInnerWidthSpecified; michael@0: bool mInnerHeightSpecified; michael@0: michael@0: // If these booleans are true, don't look at the corresponding width values michael@0: // even if they're specified -- they'll be bogus michael@0: bool mUseDefaultWidth; michael@0: bool mUseDefaultHeight; michael@0: michael@0: bool PositionSpecified() const { michael@0: return mLeftSpecified || mTopSpecified; michael@0: } michael@0: michael@0: bool SizeSpecified() const { michael@0: return mOuterWidthSpecified || mOuterHeightSpecified || michael@0: mInnerWidthSpecified || mInnerHeightSpecified; michael@0: } michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::OpenWindow2(nsIDOMWindow *aParent, michael@0: const char *aUrl, michael@0: const char *aName, michael@0: const char *aFeatures, michael@0: bool aCalledFromScript, michael@0: bool aDialog, michael@0: bool aNavigate, michael@0: nsISupports *aArguments, michael@0: nsIDOMWindow **_retval) michael@0: { michael@0: nsCOMPtr argv = ConvertArgsToArray(aArguments); michael@0: michael@0: uint32_t argc = 0; michael@0: if (argv) { michael@0: argv->GetLength(&argc); michael@0: } michael@0: michael@0: // This is extremely messed up, but this behavior is necessary because michael@0: // callers lie about whether they're a dialog window and whether they're michael@0: // called from script. Fixing this is bug 779939. michael@0: bool dialog = aDialog; michael@0: if (!aCalledFromScript) { michael@0: dialog = argc > 0; michael@0: } michael@0: michael@0: return OpenWindowInternal(aParent, aUrl, aName, aFeatures, michael@0: aCalledFromScript, dialog, michael@0: aNavigate, argv, _retval); michael@0: } michael@0: michael@0: nsresult michael@0: nsWindowWatcher::OpenWindowInternal(nsIDOMWindow *aParent, michael@0: const char *aUrl, michael@0: const char *aName, michael@0: const char *aFeatures, michael@0: bool aCalledFromJS, michael@0: bool aDialog, michael@0: bool aNavigate, michael@0: nsIArray *argv, michael@0: nsIDOMWindow **_retval) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: bool nameSpecified, michael@0: featuresSpecified, michael@0: isNewToplevelWindow = false, michael@0: windowIsNew = false, michael@0: windowNeedsName = false, michael@0: windowIsModal = false, michael@0: uriToLoadIsChrome = false, michael@0: windowIsModalContentDialog = false; michael@0: uint32_t chromeFlags; michael@0: nsAutoString name; // string version of aName michael@0: nsAutoCString features; // string version of aFeatures michael@0: nsCOMPtr uriToLoad; // from aUrl, if any michael@0: nsCOMPtr parentTreeOwner; // from the parent window, if any michael@0: nsCOMPtr newDocShellItem; // from the new window michael@0: nsCxPusher callerContextGuard; michael@0: michael@0: NS_ENSURE_ARG_POINTER(_retval); michael@0: *_retval = 0; michael@0: michael@0: if (!nsContentUtils::IsSafeToRunScript()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: GetWindowTreeOwner(aParent, getter_AddRefs(parentTreeOwner)); michael@0: michael@0: if (aUrl) { michael@0: rv = URIfromURL(aUrl, aParent, getter_AddRefs(uriToLoad)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: uriToLoad->SchemeIs("chrome", &uriToLoadIsChrome); michael@0: } michael@0: michael@0: nameSpecified = false; michael@0: if (aName) { michael@0: CopyUTF8toUTF16(aName, name); michael@0: nameSpecified = true; michael@0: } michael@0: michael@0: featuresSpecified = false; michael@0: if (aFeatures) { michael@0: features.Assign(aFeatures); michael@0: featuresSpecified = true; michael@0: features.StripWhitespace(); michael@0: } michael@0: michael@0: // try to find an extant window with the given name michael@0: nsCOMPtr foundWindow = SafeGetWindowByName(name, aParent); michael@0: GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem)); michael@0: michael@0: // Do sandbox checks here, instead of waiting until nsIDocShell::LoadURI. michael@0: // The state of the window can change before this call and if we are blocked michael@0: // because of sandboxing, we wouldn't want that to happen. michael@0: nsCOMPtr parentWindow = do_QueryInterface(aParent); michael@0: nsCOMPtr parentDocShell; michael@0: if (parentWindow) { michael@0: parentDocShell = parentWindow->GetDocShell(); michael@0: if (parentDocShell) { michael@0: nsCOMPtr foundDocShell = do_QueryInterface(newDocShellItem); michael@0: if (parentDocShell->IsSandboxedFrom(foundDocShell)) { michael@0: return NS_ERROR_DOM_INVALID_ACCESS_ERR; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // no extant window? make a new one. michael@0: michael@0: // If no parent, consider it chrome. michael@0: bool hasChromeParent = true; michael@0: if (aParent) { michael@0: // Check if the parent document has chrome privileges. michael@0: nsCOMPtr domdoc; michael@0: aParent->GetDocument(getter_AddRefs(domdoc)); michael@0: nsCOMPtr doc = do_QueryInterface(domdoc); michael@0: hasChromeParent = doc && nsContentUtils::IsChromeDoc(doc); michael@0: } michael@0: michael@0: // Make sure we call CalculateChromeFlags() *before* we push the michael@0: // callee context onto the context stack so that michael@0: // CalculateChromeFlags() sees the actual caller when doing its michael@0: // security checks. michael@0: chromeFlags = CalculateChromeFlags(aParent, features.get(), featuresSpecified, michael@0: aDialog, uriToLoadIsChrome, michael@0: hasChromeParent); michael@0: michael@0: // If we're not called through our JS version of the API, and we got michael@0: // our internal modal option, treat the window we're opening as a michael@0: // modal content window (and set the modal chrome flag). michael@0: if (!aCalledFromJS && argv && michael@0: WinHasOption(features.get(), "-moz-internal-modal", 0, nullptr)) { michael@0: windowIsModalContentDialog = true; michael@0: michael@0: // CHROME_MODAL gets inherited by dependent windows, which affects various michael@0: // platform-specific window state (especially on OSX). So we need some way michael@0: // to determine that this window was actually opened by nsGlobalWindow:: michael@0: // ShowModalDialog(), and that somebody is actually going to be watching michael@0: // for return values and all that. michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL_CONTENT_WINDOW; michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL; michael@0: } michael@0: michael@0: SizeSpec sizeSpec; michael@0: CalcSizeSpec(features.get(), sizeSpec); michael@0: michael@0: nsCOMPtr michael@0: sm(do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID)); michael@0: michael@0: bool isCallerChrome = nsContentUtils::IsCallerChrome(); michael@0: michael@0: JSContext *cx = GetJSContextFromWindow(aParent); michael@0: michael@0: bool windowTypeIsChrome = chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME; michael@0: if (isCallerChrome && !hasChromeParent && !windowTypeIsChrome && cx) { michael@0: // open() is called from chrome on a non-chrome window, push the context of the michael@0: // callee onto the context stack to prevent the caller's priveleges from leaking michael@0: // into code that runs while opening the new window. michael@0: // michael@0: // The reasoning for this is in bug 289204. Basically, chrome sometimes does michael@0: // someContentWindow.open(untrustedURL), and wants to be insulated from nasty michael@0: // javascript: URLs and such. But there are also cases where we create a michael@0: // window parented to a content window (such as a download dialog), usually michael@0: // directly with nsIWindowWatcher. In those cases, we want the principal of michael@0: // the initial about:blank document to be system, so that the subsequent XUL michael@0: // load can reuse the inner window and avoid blowing away expandos. As such, michael@0: // we decide whether to load with the principal of the caller or of the parent michael@0: // based on whether the docshell type is chrome or content. michael@0: michael@0: callerContextGuard.Push(cx); michael@0: } michael@0: michael@0: uint32_t activeDocsSandboxFlags = 0; michael@0: if (!newDocShellItem) { michael@0: // We're going to either open up a new window ourselves or ask a michael@0: // nsIWindowProvider for one. In either case, we'll want to set the right michael@0: // name on it. michael@0: windowNeedsName = true; michael@0: michael@0: // If the parent trying to open a new window is sandboxed michael@0: // without 'allow-popups', this is not allowed and we fail here. michael@0: if (aParent) { michael@0: nsCOMPtr domdoc; michael@0: aParent->GetDocument(getter_AddRefs(domdoc)); michael@0: nsCOMPtr doc = do_QueryInterface(domdoc); michael@0: michael@0: if (doc) { michael@0: // Save sandbox flags for copying to new browsing context (docShell). michael@0: activeDocsSandboxFlags = doc->GetSandboxFlags(); michael@0: if (activeDocsSandboxFlags & SANDBOXED_AUXILIARY_NAVIGATION) { michael@0: return NS_ERROR_DOM_INVALID_ACCESS_ERR; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Now check whether it's ok to ask a window provider for a window. Don't michael@0: // do it if we're opening a dialog or if our parent is a chrome window or michael@0: // if we're opening something that has modal, dialog, or chrome flags set. michael@0: nsCOMPtr chromeWin = do_QueryInterface(aParent); michael@0: if (!aDialog && !chromeWin && michael@0: !(chromeFlags & (nsIWebBrowserChrome::CHROME_MODAL | michael@0: nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | michael@0: nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) { michael@0: nsCOMPtr provider = do_GetInterface(parentTreeOwner); michael@0: if (provider) { michael@0: NS_ASSERTION(aParent, "We've _got_ to have a parent here!"); michael@0: michael@0: nsCOMPtr newWindow; michael@0: rv = provider->ProvideWindow(aParent, chromeFlags, aCalledFromJS, michael@0: sizeSpec.PositionSpecified(), michael@0: sizeSpec.SizeSpecified(), michael@0: uriToLoad, name, features, &windowIsNew, michael@0: getter_AddRefs(newWindow)); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem)); michael@0: if (windowIsNew && newDocShellItem) { michael@0: // Make sure to stop any loads happening in this window that the michael@0: // window provider might have started. Otherwise if our caller michael@0: // manipulates the window it just opened and then the load michael@0: // completes their stuff will get blown away. michael@0: nsCOMPtr webNav = michael@0: do_QueryInterface(newDocShellItem); michael@0: webNav->Stop(nsIWebNavigation::STOP_NETWORK); michael@0: } michael@0: } michael@0: else if (rv == NS_ERROR_ABORT) { michael@0: // NS_ERROR_ABORT means the window provider has flat-out rejected michael@0: // the open-window call and we should bail. Don't return an error michael@0: // here, because our caller may propagate that error, which might michael@0: // cause e.g. window.open to throw! Just return null for our out michael@0: // param. michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool newWindowShouldBeModal = false; michael@0: bool parentIsModal = false; michael@0: if (!newDocShellItem) { michael@0: windowIsNew = true; michael@0: isNewToplevelWindow = true; michael@0: michael@0: nsCOMPtr parentChrome(do_GetInterface(parentTreeOwner)); michael@0: michael@0: // is the parent (if any) modal? if so, we must be, too. michael@0: bool weAreModal = (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL) != 0; michael@0: newWindowShouldBeModal = weAreModal; michael@0: if (!weAreModal && parentChrome) { michael@0: parentChrome->IsWindowModal(&weAreModal); michael@0: parentIsModal = weAreModal; michael@0: } michael@0: michael@0: if (weAreModal) { michael@0: windowIsModal = true; michael@0: // in case we added this because weAreModal michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_MODAL | michael@0: nsIWebBrowserChrome::CHROME_DEPENDENT; michael@0: } michael@0: michael@0: // Make sure to not create modal windows if our parent is invisible and michael@0: // isn't a chrome window. Otherwise we can end up in a bizarre situation michael@0: // where we can't shut down because an invisible window is open. If michael@0: // someone tries to do this, throw. michael@0: if (!hasChromeParent && (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)) { michael@0: nsCOMPtr parentWindow(do_GetInterface(parentTreeOwner)); michael@0: nsCOMPtr parentWidget; michael@0: if (parentWindow) michael@0: parentWindow->GetMainWidget(getter_AddRefs(parentWidget)); michael@0: // NOTE: the logic for this visibility check is duplicated in michael@0: // nsIDOMWindowUtils::isParentWindowMainWidgetVisible - if we change michael@0: // how a window is determined "visible" in this context then we should michael@0: // also adjust that attribute and/or any consumers of it... michael@0: if (parentWidget && !parentWidget->IsVisible()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_ASSERTION(mWindowCreator, michael@0: "attempted to open a new window with no WindowCreator"); michael@0: rv = NS_ERROR_FAILURE; michael@0: if (mWindowCreator) { michael@0: nsCOMPtr newChrome; michael@0: michael@0: /* If the window creator is an nsIWindowCreator2, we can give it michael@0: some hints. The only hint at this time is whether the opening window michael@0: is in a situation that's likely to mean this is an unrequested michael@0: popup window we're creating. However we're not completely honest: michael@0: we clear that indicator if the opener is chrome, so that the michael@0: downstream consumer can treat the indicator to mean simply michael@0: that the new window is subject to popup control. */ michael@0: nsCOMPtr windowCreator2(do_QueryInterface(mWindowCreator)); michael@0: if (windowCreator2) { michael@0: uint32_t contextFlags = 0; michael@0: bool popupConditions = false; michael@0: michael@0: // is the parent under popup conditions? michael@0: if (parentWindow) { michael@0: popupConditions = parentWindow->IsLoadingOrRunningTimeout(); michael@0: } michael@0: michael@0: // chrome is always allowed, so clear the flag if the opener is chrome michael@0: if (popupConditions) { michael@0: popupConditions = !isCallerChrome; michael@0: } michael@0: michael@0: if (popupConditions) michael@0: contextFlags |= nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT; michael@0: michael@0: bool cancel = false; michael@0: rv = windowCreator2->CreateChromeWindow2(parentChrome, chromeFlags, michael@0: contextFlags, uriToLoad, michael@0: &cancel, michael@0: getter_AddRefs(newChrome)); michael@0: if (NS_SUCCEEDED(rv) && cancel) { michael@0: newChrome = 0; // just in case michael@0: rv = NS_ERROR_ABORT; michael@0: } michael@0: } michael@0: else michael@0: rv = mWindowCreator->CreateChromeWindow(parentChrome, chromeFlags, michael@0: getter_AddRefs(newChrome)); michael@0: if (newChrome) { michael@0: /* It might be a chrome nsXULWindow, in which case it won't have michael@0: an nsIDOMWindow (primary content shell). But in that case, it'll michael@0: be able to hand over an nsIDocShellTreeItem directly. */ michael@0: nsCOMPtr newWindow(do_GetInterface(newChrome)); michael@0: if (newWindow) michael@0: GetWindowTreeItem(newWindow, getter_AddRefs(newDocShellItem)); michael@0: if (!newDocShellItem) michael@0: newDocShellItem = do_GetInterface(newChrome); michael@0: if (!newDocShellItem) michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // better have a window to use by this point michael@0: if (!newDocShellItem) michael@0: return rv; michael@0: michael@0: nsCOMPtr newDocShell(do_QueryInterface(newDocShellItem)); michael@0: NS_ENSURE_TRUE(newDocShell, NS_ERROR_UNEXPECTED); michael@0: michael@0: // Set up sandboxing attributes if the window is new. michael@0: // The flags can only be non-zero for new windows. michael@0: if (activeDocsSandboxFlags != 0) { michael@0: newDocShell->SetSandboxFlags(activeDocsSandboxFlags); michael@0: if (parentWindow) { michael@0: newDocShell-> michael@0: SetOnePermittedSandboxedNavigator(parentWindow->GetDocShell()); michael@0: } michael@0: } michael@0: michael@0: rv = ReadyOpenedDocShellItem(newDocShellItem, aParent, windowIsNew, _retval); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: /* disable persistence of size/position in popups (determined by michael@0: determining whether the features parameter specifies width or height michael@0: in any way). We consider any overriding of the window's size or position michael@0: in the open call as disabling persistence of those attributes. michael@0: Popup windows (which should not persist size or position) generally set michael@0: the size. */ michael@0: if (isNewToplevelWindow) { michael@0: /* at the moment, the strings "height=" or "width=" never happen michael@0: outside a size specification, so we can do this the Q&D way. */ michael@0: michael@0: if (PL_strcasestr(features.get(), "width=") || PL_strcasestr(features.get(), "height=")) { michael@0: michael@0: nsCOMPtr newTreeOwner; michael@0: newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner)); michael@0: if (newTreeOwner) michael@0: newTreeOwner->SetPersistence(false, false, false); michael@0: } michael@0: } michael@0: michael@0: if ((aDialog || windowIsModalContentDialog) && argv) { michael@0: // Set the args on the new window. michael@0: nsCOMPtr piwin(do_QueryInterface(*_retval)); michael@0: NS_ENSURE_TRUE(piwin, NS_ERROR_UNEXPECTED); michael@0: michael@0: rv = piwin->SetArguments(argv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: /* allow a window that we found by name to keep its name (important for cases michael@0: like _self where the given name is different (and invalid)). Also, _blank michael@0: is not a window name. */ michael@0: if (windowNeedsName) { michael@0: if (nameSpecified && !name.LowerCaseEqualsLiteral("_blank")) { michael@0: newDocShellItem->SetName(name); michael@0: } else { michael@0: newDocShellItem->SetName(EmptyString()); michael@0: } michael@0: } michael@0: michael@0: // Now we have to set the right opener principal on the new window. Note michael@0: // that we have to do this _before_ starting any URI loads, thanks to the michael@0: // sync nature of javascript: loads. Since this is the only place where we michael@0: // set said opener principal, we need to do it for all URIs, including michael@0: // chrome ones. So to deal with the mess that is bug 79775, just press on in michael@0: // a reasonable way even if GetSubjectPrincipal fails. In that case, just michael@0: // use a null subjectPrincipal. michael@0: nsCOMPtr subjectPrincipal; michael@0: if (NS_FAILED(sm->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal)))) { michael@0: subjectPrincipal = nullptr; michael@0: } michael@0: michael@0: if (windowIsNew) { michael@0: // Now set the opener principal on the new window. Note that we need to do michael@0: // this no matter whether we were opened from JS; if there is nothing on michael@0: // the JS stack, just use the principal of our parent window. In those michael@0: // cases we do _not_ set the parent window principal as the owner of the michael@0: // load--since we really don't know who the owner is, just leave it null. michael@0: nsCOMPtr newWindow = do_QueryInterface(*_retval); michael@0: #ifdef DEBUG michael@0: nsCOMPtr newDebugWindow = do_GetInterface(newDocShell); michael@0: NS_ASSERTION(newWindow == newDebugWindow, "Different windows??"); michael@0: #endif michael@0: // The principal of the initial about:blank document gets set up in michael@0: // nsWindowWatcher::AddWindow. Make sure to call it. In the common case michael@0: // this call already happened when the window was created, but michael@0: // SetInitialPrincipalToSubject is safe to call multiple times. michael@0: if (newWindow) { michael@0: newWindow->SetInitialPrincipalToSubject(); michael@0: } michael@0: } michael@0: michael@0: // If all windows should be private, make sure the new window is also michael@0: // private. Otherwise, see if the caller has explicitly requested a michael@0: // private or non-private window. michael@0: bool isPrivateBrowsingWindow = michael@0: Preferences::GetBool("browser.privatebrowsing.autostart") || michael@0: (!!(chromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) && michael@0: !(chromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW)); michael@0: michael@0: // Otherwise, propagate the privacy status of the parent window, if michael@0: // available, to the child. michael@0: if (!isPrivateBrowsingWindow && michael@0: !(chromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW)) { michael@0: nsCOMPtr parentItem; michael@0: GetWindowTreeItem(aParent, getter_AddRefs(parentItem)); michael@0: nsCOMPtr parentContext = do_QueryInterface(parentItem); michael@0: if (parentContext) { michael@0: isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing(); michael@0: } michael@0: } michael@0: michael@0: // We rely on CalculateChromeFlags to decide whether remote (out-of-process) michael@0: // tabs should be used. michael@0: bool isRemoteWindow = michael@0: !!(chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW); michael@0: michael@0: if (isNewToplevelWindow) { michael@0: nsCOMPtr childRoot; michael@0: newDocShellItem->GetRootTreeItem(getter_AddRefs(childRoot)); michael@0: nsCOMPtr childContext = do_QueryInterface(childRoot); michael@0: if (childContext) { michael@0: childContext->SetPrivateBrowsing(isPrivateBrowsingWindow); michael@0: childContext->SetRemoteTabs(isRemoteWindow); michael@0: } michael@0: } else if (windowIsNew) { michael@0: nsCOMPtr childContext = do_QueryInterface(newDocShellItem); michael@0: if (childContext) { michael@0: childContext->SetPrivateBrowsing(isPrivateBrowsingWindow); michael@0: childContext->SetRemoteTabs(isRemoteWindow); michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr loadInfo; michael@0: if (uriToLoad && aNavigate) { // get the script principal and pass it to docshell michael@0: michael@0: // The historical ordering of attempts here is: michael@0: // (1) Stack-top cx. michael@0: // (2) cx associated with aParent. michael@0: // (3) Safe JSContext. michael@0: // michael@0: // We could just use an AutoJSContext here if it weren't for (2), which michael@0: // is probably nonsensical anyway. But we preserve the old semantics for michael@0: // now, because this is part of a large patch where we don't want to risk michael@0: // subtle behavioral modifications. michael@0: cx = nsContentUtils::GetCurrentJSContext(); michael@0: nsCxPusher pusher; michael@0: if (!cx) { michael@0: cx = GetJSContextFromWindow(aParent); michael@0: if (!cx) michael@0: cx = nsContentUtils::GetSafeJSContext(); michael@0: pusher.Push(cx); michael@0: } michael@0: michael@0: newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo)); michael@0: NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE); michael@0: michael@0: if (subjectPrincipal) { michael@0: loadInfo->SetOwner(subjectPrincipal); michael@0: } michael@0: michael@0: // Set the new window's referrer from the calling context's document: michael@0: michael@0: // get its document, if any michael@0: JSContext* ccx = nsContentUtils::GetCurrentJSContext(); michael@0: if (ccx) { michael@0: nsIScriptGlobalObject *sgo = nsJSUtils::GetDynamicScriptGlobal(ccx); michael@0: michael@0: nsCOMPtr w(do_QueryInterface(sgo)); michael@0: if (w) { michael@0: /* use the URL from the *extant* document, if any. The usual accessor michael@0: GetDocument will synchronously create an about:blank document if michael@0: it has no better answer, and we only care about a real document. michael@0: Also using GetDocument to force document creation seems to michael@0: screw up focus in the hidden window; see bug 36016. michael@0: */ michael@0: nsCOMPtr doc = w->GetExtantDoc(); michael@0: if (doc) { michael@0: // Set the referrer michael@0: loadInfo->SetReferrer(doc->GetDocumentURI()); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (isNewToplevelWindow) { michael@0: // Notify observers that the window is open and ready. michael@0: // The window has not yet started to load a document. michael@0: nsCOMPtr obsSvc = michael@0: mozilla::services::GetObserverService(); michael@0: if (obsSvc) michael@0: obsSvc->NotifyObservers(*_retval, "toplevel-window-ready", nullptr); michael@0: } michael@0: michael@0: if (uriToLoad && aNavigate) { michael@0: newDocShell->LoadURI(uriToLoad, michael@0: loadInfo, michael@0: windowIsNew michael@0: ? static_cast(nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD) michael@0: : static_cast(nsIWebNavigation::LOAD_FLAGS_NONE), michael@0: true); michael@0: } michael@0: michael@0: // Copy the current session storage for the current domain. michael@0: if (subjectPrincipal && parentDocShell) { michael@0: nsCOMPtr parentStorageManager = do_QueryInterface(parentDocShell); michael@0: nsCOMPtr newStorageManager = do_QueryInterface(newDocShell); michael@0: michael@0: if (parentStorageManager && newStorageManager) { michael@0: nsCOMPtr storage; michael@0: parentStorageManager->GetStorageForFirstParty(uriToLoad, subjectPrincipal, michael@0: isPrivateBrowsingWindow, getter_AddRefs(storage)); michael@0: if (storage) michael@0: newStorageManager->CloneStorage(storage); michael@0: } michael@0: } michael@0: michael@0: if (isNewToplevelWindow) michael@0: SizeOpenedDocShellItem(newDocShellItem, aParent, sizeSpec); michael@0: michael@0: // XXXbz isn't windowIsModal always true when windowIsModalContentDialog? michael@0: if (windowIsModal || windowIsModalContentDialog) { michael@0: nsCOMPtr newTreeOwner; michael@0: newDocShellItem->GetTreeOwner(getter_AddRefs(newTreeOwner)); michael@0: nsCOMPtr newChrome(do_GetInterface(newTreeOwner)); michael@0: michael@0: // Throw an exception here if no web browser chrome is available, michael@0: // we need that to show a modal window. michael@0: NS_ENSURE_TRUE(newChrome, NS_ERROR_NOT_AVAILABLE); michael@0: michael@0: // Dispatch dialog events etc, but we only want to do that if michael@0: // we're opening a modal content window (the helper classes are michael@0: // no-ops if given no window), for chrome dialogs we don't want to michael@0: // do any of that (it's done elsewhere for us). michael@0: // Make sure we maintain the state on an outer window, because michael@0: // that's where it lives; inner windows assert if you try to michael@0: // maintain the state on them. michael@0: nsAutoWindowStateHelper windowStateHelper( michael@0: parentWindow ? parentWindow->GetOuterWindow() : nullptr); michael@0: michael@0: if (!windowStateHelper.DefaultEnabled()) { michael@0: // Default to cancel not opening the modal window. michael@0: NS_RELEASE(*_retval); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: if (!newWindowShouldBeModal && parentIsModal) { michael@0: nsCOMPtr parentWindow(do_GetInterface(newTreeOwner)); michael@0: if (parentWindow) { michael@0: nsCOMPtr parentWidget; michael@0: parentWindow->GetMainWidget(getter_AddRefs(parentWidget)); michael@0: if (parentWidget) { michael@0: parentWidget->SetModal(true); michael@0: } michael@0: } michael@0: } else { michael@0: // Reset popup state while opening a modal dialog, and firing michael@0: // events about the dialog, to prevent the current state from michael@0: // being active the whole time a modal dialog is open. michael@0: nsAutoPopupStatePusher popupStatePusher(openAbused); michael@0: michael@0: newChrome->ShowAsModal(); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::RegisterNotification(nsIObserver *aObserver) michael@0: { michael@0: // just a convenience method; it delegates to nsIObserverService michael@0: michael@0: if (!aObserver) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (!os) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsresult rv = os->AddObserver(aObserver, "domwindowopened", false); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = os->AddObserver(aObserver, "domwindowclosed", false); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::UnregisterNotification(nsIObserver *aObserver) michael@0: { michael@0: // just a convenience method; it delegates to nsIObserverService michael@0: michael@0: if (!aObserver) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (!os) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: os->RemoveObserver(aObserver, "domwindowopened"); michael@0: os->RemoveObserver(aObserver, "domwindowclosed"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::GetWindowEnumerator(nsISimpleEnumerator** _retval) michael@0: { michael@0: if (!_retval) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: MutexAutoLock lock(mListLock); michael@0: nsWatcherWindowEnumerator *enumerator = new nsWatcherWindowEnumerator(this); michael@0: if (enumerator) michael@0: return CallQueryInterface(enumerator, _retval); michael@0: michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::GetNewPrompter(nsIDOMWindow *aParent, nsIPrompt **_retval) michael@0: { michael@0: // This is for backwards compat only. Callers should just use the prompt service directly. michael@0: nsresult rv; michael@0: nsCOMPtr factory = do_GetService("@mozilla.org/prompter;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return factory->GetPrompt(aParent, NS_GET_IID(nsIPrompt), reinterpret_cast(_retval)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::GetNewAuthPrompter(nsIDOMWindow *aParent, nsIAuthPrompt **_retval) michael@0: { michael@0: // This is for backwards compat only. Callers should just use the prompt service directly. michael@0: nsresult rv; michael@0: nsCOMPtr factory = do_GetService("@mozilla.org/prompter;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return factory->GetPrompt(aParent, NS_GET_IID(nsIAuthPrompt), reinterpret_cast(_retval)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::GetPrompt(nsIDOMWindow *aParent, const nsIID& aIID, michael@0: void **_retval) michael@0: { michael@0: // This is for backwards compat only. Callers should just use the prompt service directly. michael@0: nsresult rv; michael@0: nsCOMPtr factory = do_GetService("@mozilla.org/prompter;1", &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: rv = factory->GetPrompt(aParent, aIID, _retval); michael@0: michael@0: // Allow for an embedding implementation to not support nsIAuthPrompt2. michael@0: if (rv == NS_NOINTERFACE && aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) { michael@0: nsCOMPtr oldPrompt; michael@0: rv = factory->GetPrompt(aParent, michael@0: NS_GET_IID(nsIAuthPrompt), michael@0: getter_AddRefs(oldPrompt)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_WrapAuthPrompt(oldPrompt, reinterpret_cast(_retval)); michael@0: if (!*_retval) michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::SetWindowCreator(nsIWindowCreator *creator) michael@0: { michael@0: mWindowCreator = creator; // it's an nsCOMPtr, so this is an ownership ref michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::HasWindowCreator(bool *result) michael@0: { michael@0: *result = mWindowCreator; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::GetActiveWindow(nsIDOMWindow **aActiveWindow) michael@0: { michael@0: *aActiveWindow = nullptr; michael@0: nsCOMPtr fm = do_GetService(FOCUSMANAGER_CONTRACTID); michael@0: if (fm) michael@0: return fm->GetActiveWindow(aActiveWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::SetActiveWindow(nsIDOMWindow *aActiveWindow) michael@0: { michael@0: nsCOMPtr fm = do_GetService(FOCUSMANAGER_CONTRACTID); michael@0: if (fm) michael@0: return fm->SetActiveWindow(aActiveWindow); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::AddWindow(nsIDOMWindow *aWindow, nsIWebBrowserChrome *aChrome) michael@0: { michael@0: if (!aWindow) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: #ifdef DEBUG michael@0: { michael@0: nsCOMPtr win(do_QueryInterface(aWindow)); michael@0: michael@0: NS_ASSERTION(win->IsOuterWindow(), michael@0: "Uh, the active window must be an outer window!"); michael@0: } michael@0: #endif michael@0: michael@0: { michael@0: nsWatcherWindowEntry *info; michael@0: MutexAutoLock lock(mListLock); michael@0: michael@0: // if we already have an entry for this window, adjust michael@0: // its chrome mapping and return michael@0: info = FindWindowEntry(aWindow); michael@0: if (info) { michael@0: nsCOMPtr supportsweak(do_QueryInterface(aChrome)); michael@0: if (supportsweak) { michael@0: supportsweak->GetWeakReference(getter_AddRefs(info->mChromeWeak)); michael@0: } else { michael@0: info->mChrome = aChrome; michael@0: info->mChromeWeak = 0; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // create a window info struct and add it to the list of windows michael@0: info = new nsWatcherWindowEntry(aWindow, aChrome); michael@0: if (!info) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: if (mOldestWindow) michael@0: info->InsertAfter(mOldestWindow->mOlder); michael@0: else michael@0: mOldestWindow = info; michael@0: } // leave the mListLock michael@0: michael@0: // a window being added to us signifies a newly opened window. michael@0: // send notifications. michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (!os) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr domwin(do_QueryInterface(aWindow)); michael@0: return os->NotifyObservers(domwin, "domwindowopened", 0); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::RemoveWindow(nsIDOMWindow *aWindow) michael@0: { michael@0: // find the corresponding nsWatcherWindowEntry, remove it michael@0: michael@0: if (!aWindow) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsWatcherWindowEntry *info = FindWindowEntry(aWindow); michael@0: if (info) { michael@0: RemoveWindow(info); michael@0: return NS_OK; michael@0: } michael@0: NS_WARNING("requested removal of nonexistent window"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: nsWatcherWindowEntry * michael@0: nsWindowWatcher::FindWindowEntry(nsIDOMWindow *aWindow) michael@0: { michael@0: // find the corresponding nsWatcherWindowEntry michael@0: nsWatcherWindowEntry *info, michael@0: *listEnd; michael@0: #ifdef USEWEAKREFS michael@0: nsresult rv; michael@0: bool found; michael@0: #endif michael@0: michael@0: info = mOldestWindow; michael@0: listEnd = 0; michael@0: #ifdef USEWEAKREFS michael@0: rv = NS_OK; michael@0: found = false; michael@0: while (info != listEnd && NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr infoWindow(do_QueryReferent(info->mWindow)); michael@0: if (!infoWindow) { // clean up dangling reference, while we're here michael@0: rv = RemoveWindow(info); michael@0: } michael@0: else if (infoWindow.get() == aWindow) michael@0: return info; michael@0: michael@0: info = info->mYounger; michael@0: listEnd = mOldestWindow; michael@0: } michael@0: return 0; michael@0: #else michael@0: while (info != listEnd) { michael@0: if (info->mWindow == aWindow) michael@0: return info; michael@0: info = info->mYounger; michael@0: listEnd = mOldestWindow; michael@0: } michael@0: return 0; michael@0: #endif michael@0: } michael@0: michael@0: nsresult nsWindowWatcher::RemoveWindow(nsWatcherWindowEntry *inInfo) michael@0: { michael@0: uint32_t ctr, michael@0: count = mEnumeratorList.Length(); michael@0: michael@0: { michael@0: // notify the enumerators michael@0: MutexAutoLock lock(mListLock); michael@0: for (ctr = 0; ctr < count; ++ctr) michael@0: mEnumeratorList[ctr]->WindowRemoved(inInfo); michael@0: michael@0: // remove the element from the list michael@0: if (inInfo == mOldestWindow) michael@0: mOldestWindow = inInfo->mYounger == mOldestWindow ? 0 : inInfo->mYounger; michael@0: inInfo->Unlink(); michael@0: } michael@0: michael@0: // a window being removed from us signifies a newly closed window. michael@0: // send notifications. michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) { michael@0: #ifdef USEWEAKREFS michael@0: nsCOMPtr domwin(do_QueryReferent(inInfo->mWindow)); michael@0: if (domwin) michael@0: os->NotifyObservers(domwin, "domwindowclosed", 0); michael@0: // else bummer. since the window is gone, there's nothing to notify with. michael@0: #else michael@0: nsCOMPtr domwin(do_QueryInterface(inInfo->mWindow)); michael@0: os->NotifyObservers(domwin, "domwindowclosed", 0); michael@0: #endif michael@0: } michael@0: michael@0: delete inInfo; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::GetChromeForWindow(nsIDOMWindow *aWindow, nsIWebBrowserChrome **_retval) michael@0: { michael@0: if (!aWindow || !_retval) michael@0: return NS_ERROR_INVALID_ARG; michael@0: *_retval = 0; michael@0: michael@0: MutexAutoLock lock(mListLock); michael@0: nsWatcherWindowEntry *info = FindWindowEntry(aWindow); michael@0: if (info) { michael@0: if (info->mChromeWeak != nullptr) { michael@0: return info->mChromeWeak-> michael@0: QueryReferent(NS_GET_IID(nsIWebBrowserChrome), michael@0: reinterpret_cast(_retval)); michael@0: } michael@0: *_retval = info->mChrome; michael@0: NS_IF_ADDREF(*_retval); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsWindowWatcher::GetWindowByName(const char16_t *aTargetName, michael@0: nsIDOMWindow *aCurrentWindow, michael@0: nsIDOMWindow **aResult) michael@0: { michael@0: if (!aResult) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: *aResult = nullptr; michael@0: michael@0: nsCOMPtr treeItem; michael@0: michael@0: nsCOMPtr startItem; michael@0: GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem)); michael@0: if (startItem) { michael@0: // Note: original requestor is null here, per idl comments michael@0: startItem->FindItemWithName(aTargetName, nullptr, nullptr, michael@0: getter_AddRefs(treeItem)); michael@0: } michael@0: else { michael@0: // Note: original requestor is null here, per idl comments michael@0: FindItemWithName(aTargetName, nullptr, nullptr, getter_AddRefs(treeItem)); michael@0: } michael@0: michael@0: nsCOMPtr domWindow = do_GetInterface(treeItem); michael@0: domWindow.swap(*aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsWindowWatcher::AddEnumerator(nsWatcherWindowEnumerator* inEnumerator) michael@0: { michael@0: // (requires a lock; assumes it's called by someone holding the lock) michael@0: return mEnumeratorList.AppendElement(inEnumerator) != nullptr; michael@0: } michael@0: michael@0: bool michael@0: nsWindowWatcher::RemoveEnumerator(nsWatcherWindowEnumerator* inEnumerator) michael@0: { michael@0: // (requires a lock; assumes it's called by someone holding the lock) michael@0: return mEnumeratorList.RemoveElement(inEnumerator); michael@0: } michael@0: michael@0: nsresult michael@0: nsWindowWatcher::URIfromURL(const char *aURL, michael@0: nsIDOMWindow *aParent, michael@0: nsIURI **aURI) michael@0: { michael@0: nsCOMPtr baseWindow; michael@0: michael@0: /* build the URI relative to the calling JS Context, if any. michael@0: (note this is the same context used to make the security check michael@0: in nsGlobalWindow.cpp.) */ michael@0: JSContext *cx = nsContentUtils::GetCurrentJSContext(); michael@0: if (cx) { michael@0: nsIScriptContext *scriptcx = nsJSUtils::GetDynamicScriptContext(cx); michael@0: if (scriptcx) { michael@0: baseWindow = do_QueryInterface(scriptcx->GetGlobalObject()); michael@0: } michael@0: } michael@0: michael@0: // failing that, build it relative to the parent window, if possible michael@0: if (!baseWindow) michael@0: baseWindow = aParent; michael@0: michael@0: // failing that, use the given URL unmodified. It had better not be relative. michael@0: michael@0: nsIURI *baseURI = nullptr; michael@0: michael@0: // get baseWindow's document URI michael@0: if (baseWindow) { michael@0: nsCOMPtr domDoc; michael@0: baseWindow->GetDocument(getter_AddRefs(domDoc)); michael@0: if (domDoc) { michael@0: nsCOMPtr doc; michael@0: doc = do_QueryInterface(domDoc); michael@0: if (doc) { michael@0: baseURI = doc->GetDocBaseURI(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // build and return the absolute URI michael@0: return NS_NewURI(aURI, aURL, baseURI); michael@0: } michael@0: michael@0: #define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag) \ michael@0: prefBranch->GetBoolPref(feature, &forceEnable); \ michael@0: if (forceEnable && !(aDialog && isCallerChrome) && \ michael@0: !(isCallerChrome && aHasChromeParent) && !aChromeURL) { \ michael@0: chromeFlags |= flag; \ michael@0: } else { \ michael@0: chromeFlags |= WinHasOption(aFeatures, feature, \ michael@0: 0, &presenceFlag) \ michael@0: ? flag : 0; \ michael@0: } michael@0: michael@0: /** michael@0: * Calculate the chrome bitmask from a string list of features. michael@0: * @param aParent the opener window michael@0: * @param aFeatures a string containing a list of named chrome features michael@0: * @param aNullFeatures true if aFeatures was a null pointer (which fact michael@0: * is lost by its conversion to a string in the caller) michael@0: * @param aDialog affects the assumptions made about unnamed features michael@0: * @return the chrome bitmask michael@0: */ michael@0: // static michael@0: uint32_t nsWindowWatcher::CalculateChromeFlags(nsIDOMWindow *aParent, michael@0: const char *aFeatures, michael@0: bool aFeaturesSpecified, michael@0: bool aDialog, michael@0: bool aChromeURL, michael@0: bool aHasChromeParent) michael@0: { michael@0: if(!aFeaturesSpecified || !aFeatures) { michael@0: if(aDialog) michael@0: return nsIWebBrowserChrome::CHROME_ALL | michael@0: nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | michael@0: nsIWebBrowserChrome::CHROME_OPENAS_CHROME; michael@0: else michael@0: return nsIWebBrowserChrome::CHROME_ALL; michael@0: } michael@0: michael@0: /* This function has become complicated since browser windows and michael@0: dialogs diverged. The difference is, browser windows assume all michael@0: chrome not explicitly mentioned is off, if the features string michael@0: is not null. Exceptions are some OS border chrome new with Mozilla. michael@0: Dialogs interpret a (mostly) empty features string to mean michael@0: "OS's choice," and also support an "all" flag explicitly disallowed michael@0: in the standards-compliant window.(normal)open. */ michael@0: michael@0: uint32_t chromeFlags = 0; michael@0: bool presenceFlag = false; michael@0: michael@0: chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS; michael@0: if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag)) michael@0: chromeFlags = nsIWebBrowserChrome::CHROME_ALL; michael@0: michael@0: /* Next, allow explicitly named options to override the initial settings */ michael@0: michael@0: bool isCallerChrome = nsContentUtils::IsCallerChrome(); michael@0: michael@0: // Determine whether the window is a private browsing window michael@0: if (isCallerChrome) { michael@0: chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag) ? michael@0: nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW : 0; michael@0: chromeFlags |= WinHasOption(aFeatures, "non-private", 0, &presenceFlag) ? michael@0: nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW : 0; michael@0: } michael@0: michael@0: // Determine whether the window should have remote tabs. michael@0: if (isCallerChrome) { michael@0: bool remote; michael@0: if (Preferences::GetBool("browser.tabs.remote.autostart")) { michael@0: remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag); michael@0: } else { michael@0: remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag); michael@0: } michael@0: if (remote) { michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW; michael@0: } michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr prefBranch; michael@0: nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: michael@0: rv = prefs->GetBranch("dom.disable_window_open_feature.", getter_AddRefs(prefBranch)); michael@0: NS_ENSURE_SUCCESS(rv, true); michael@0: michael@0: bool forceEnable = false; michael@0: michael@0: NS_CALCULATE_CHROME_FLAG_FOR("titlebar", michael@0: nsIWebBrowserChrome::CHROME_TITLEBAR); michael@0: NS_CALCULATE_CHROME_FLAG_FOR("close", michael@0: nsIWebBrowserChrome::CHROME_WINDOW_CLOSE); michael@0: NS_CALCULATE_CHROME_FLAG_FOR("toolbar", michael@0: nsIWebBrowserChrome::CHROME_TOOLBAR); michael@0: NS_CALCULATE_CHROME_FLAG_FOR("location", michael@0: nsIWebBrowserChrome::CHROME_LOCATIONBAR); michael@0: NS_CALCULATE_CHROME_FLAG_FOR("personalbar", michael@0: nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR); michael@0: NS_CALCULATE_CHROME_FLAG_FOR("status", michael@0: nsIWebBrowserChrome::CHROME_STATUSBAR); michael@0: NS_CALCULATE_CHROME_FLAG_FOR("menubar", michael@0: nsIWebBrowserChrome::CHROME_MENUBAR); michael@0: NS_CALCULATE_CHROME_FLAG_FOR("scrollbars", michael@0: nsIWebBrowserChrome::CHROME_SCROLLBARS); michael@0: NS_CALCULATE_CHROME_FLAG_FOR("resizable", michael@0: nsIWebBrowserChrome::CHROME_WINDOW_RESIZE); michael@0: NS_CALCULATE_CHROME_FLAG_FOR("minimizable", michael@0: nsIWebBrowserChrome::CHROME_WINDOW_MIN); michael@0: michael@0: chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag) michael@0: ? nsIWebBrowserChrome::CHROME_WINDOW_POPUP : 0; michael@0: michael@0: /* OK. michael@0: Normal browser windows, in spite of a stated pattern of turning off michael@0: all chrome not mentioned explicitly, will want the new OS chrome (window michael@0: borders, titlebars, closebox) on, unless explicitly turned off. michael@0: Dialogs, on the other hand, take the absence of any explicit settings michael@0: to mean "OS' choice." */ michael@0: michael@0: // default titlebar and closebox to "on," if not mentioned at all michael@0: if (!(chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)) { michael@0: if (!PL_strcasestr(aFeatures, "titlebar")) michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR; michael@0: if (!PL_strcasestr(aFeatures, "close")) michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE; michael@0: } michael@0: michael@0: if (aDialog && !presenceFlag) michael@0: chromeFlags = nsIWebBrowserChrome::CHROME_DEFAULT; michael@0: michael@0: /* Finally, once all the above normal chrome has been divined, deal michael@0: with the features that are more operating hints than appearance michael@0: instructions. (Note modality implies dependence.) */ michael@0: michael@0: if (WinHasOption(aFeatures, "alwaysLowered", 0, nullptr) || michael@0: WinHasOption(aFeatures, "z-lock", 0, nullptr)) michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_LOWERED; michael@0: else if (WinHasOption(aFeatures, "alwaysRaised", 0, nullptr)) michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_RAISED; michael@0: michael@0: chromeFlags |= WinHasOption(aFeatures, "macsuppressanimation", 0, nullptr) ? michael@0: nsIWebBrowserChrome::CHROME_MAC_SUPPRESS_ANIMATION : 0; michael@0: michael@0: chromeFlags |= WinHasOption(aFeatures, "chrome", 0, nullptr) ? michael@0: nsIWebBrowserChrome::CHROME_OPENAS_CHROME : 0; michael@0: chromeFlags |= WinHasOption(aFeatures, "extrachrome", 0, nullptr) ? michael@0: nsIWebBrowserChrome::CHROME_EXTRA : 0; michael@0: chromeFlags |= WinHasOption(aFeatures, "centerscreen", 0, nullptr) ? michael@0: nsIWebBrowserChrome::CHROME_CENTER_SCREEN : 0; michael@0: chromeFlags |= WinHasOption(aFeatures, "dependent", 0, nullptr) ? michael@0: nsIWebBrowserChrome::CHROME_DEPENDENT : 0; michael@0: chromeFlags |= WinHasOption(aFeatures, "modal", 0, nullptr) ? michael@0: (nsIWebBrowserChrome::CHROME_MODAL | nsIWebBrowserChrome::CHROME_DEPENDENT) : 0; michael@0: michael@0: /* On mobile we want to ignore the dialog window feature, since the mobile UI michael@0: does not provide any affordance for dialog windows. This does not interfere michael@0: with dialog windows created through openDialog. */ michael@0: bool disableDialogFeature = false; michael@0: nsCOMPtr branch = do_QueryInterface(prefs); michael@0: branch->GetBoolPref("dom.disable_window_open_dialog_feature", &disableDialogFeature); michael@0: michael@0: bool isFullScreen = false; michael@0: if (aParent) { michael@0: aParent->GetFullScreen(&isFullScreen); michael@0: } michael@0: if (isFullScreen && !isCallerChrome) { michael@0: // If the parent window is in fullscreen & the caller context is content, michael@0: // dialog feature is disabled. (see bug 803675) michael@0: disableDialogFeature = true; michael@0: } michael@0: michael@0: if (!disableDialogFeature) { michael@0: chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nullptr) ? michael@0: nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0; michael@0: } michael@0: michael@0: /* and dialogs need to have the last word. assume dialogs are dialogs, michael@0: and opened as chrome, unless explicitly told otherwise. */ michael@0: if (aDialog) { michael@0: if (!PL_strcasestr(aFeatures, "dialog")) michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG; michael@0: if (!PL_strcasestr(aFeatures, "chrome")) michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_CHROME; michael@0: } michael@0: michael@0: /* missing michael@0: chromeFlags->copy_history michael@0: */ michael@0: michael@0: // Check security state for use in determing window dimensions michael@0: if (!isCallerChrome || !aHasChromeParent) { michael@0: // If priv check fails (or if we're called from chrome, but the michael@0: // parent is not a chrome window), set all elements to minimum michael@0: // reqs., else leave them alone. michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR; michael@0: chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE; michael@0: chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED; michael@0: chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED; michael@0: chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_POPUP; michael@0: /* Untrusted script is allowed to pose modal windows with a chrome michael@0: scheme. This check could stand to be better. But it effectively michael@0: prevents untrusted script from opening modal windows in general michael@0: while still allowing alerts and the like. */ michael@0: if (!aChromeURL) michael@0: chromeFlags &= ~(nsIWebBrowserChrome::CHROME_MODAL | michael@0: nsIWebBrowserChrome::CHROME_OPENAS_CHROME); michael@0: } michael@0: michael@0: if (!(chromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME)) { michael@0: // Remove the dependent flag if we're not opening as chrome michael@0: chromeFlags &= ~nsIWebBrowserChrome::CHROME_DEPENDENT; michael@0: } michael@0: michael@0: // Disable CHROME_OPENAS_DIALOG if the window is inside