diff options
Diffstat (limited to 'xpfe/appshell')
27 files changed, 8928 insertions, 0 deletions
diff --git a/xpfe/appshell/moz.build b/xpfe/appshell/moz.build new file mode 100644 index 000000000..5cfe41924 --- /dev/null +++ b/xpfe/appshell/moz.build @@ -0,0 +1,42 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini'] + +XPIDL_SOURCES += [ + 'nsIAppShellService.idl', + 'nsIPopupWindowManager.idl', + 'nsIWindowlessBrowser.idl', + 'nsIWindowMediator.idl', + 'nsIWindowMediatorListener.idl', + 'nsIXULBrowserWindow.idl', + 'nsIXULWindow.idl', +] + +XPIDL_MODULE = 'appshell' + +EXPORTS += [ + 'nsAppShellCID.h', +] + +UNIFIED_SOURCES += [ + 'nsAppShellFactory.cpp', + 'nsAppShellService.cpp', + 'nsAppShellWindowEnumerator.cpp', + 'nsChromeTreeOwner.cpp', + 'nsContentTreeOwner.cpp', + 'nsWebShellWindow.cpp', + 'nsWindowMediator.cpp', + 'nsXULWindow.cpp', +] + +LOCAL_INCLUDES += [ + '/dom/base', +] + +FINAL_LIBRARY = 'xul' + +include('/ipc/chromium/chromium-config.mozbuild')
\ No newline at end of file diff --git a/xpfe/appshell/nsAppShellCID.h b/xpfe/appshell/nsAppShellCID.h new file mode 100644 index 000000000..2b0edd4ac --- /dev/null +++ b/xpfe/appshell/nsAppShellCID.h @@ -0,0 +1,10 @@ +/* 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/. */ + +#ifndef nsAppShellCID_h__ +#define nsAppShellCID_h__ + +#define NS_APPSHELLSERVICE_CONTRACTID "@mozilla.org/appshell/appShellService;1" + +#endif diff --git a/xpfe/appshell/nsAppShellFactory.cpp b/xpfe/appshell/nsAppShellFactory.cpp new file mode 100644 index 000000000..0b6d3c47a --- /dev/null +++ b/xpfe/appshell/nsAppShellFactory.cpp @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/ModuleUtils.h" +#include "nscore.h" +#include "nsIWindowMediator.h" + +#include "nsIAppShellService.h" +#include "nsAppShellService.h" +#include "nsWindowMediator.h" +#include "nsChromeTreeOwner.h" +#include "nsAppShellCID.h" + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsAppShellService) +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWindowMediator, Init) + +NS_DEFINE_NAMED_CID(NS_APPSHELLSERVICE_CID); +NS_DEFINE_NAMED_CID(NS_WINDOWMEDIATOR_CID); + +static const mozilla::Module::CIDEntry kAppShellCIDs[] = { + { &kNS_APPSHELLSERVICE_CID, false, nullptr, nsAppShellServiceConstructor }, + { &kNS_WINDOWMEDIATOR_CID, false, nullptr, nsWindowMediatorConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kAppShellContracts[] = { + { NS_APPSHELLSERVICE_CONTRACTID, &kNS_APPSHELLSERVICE_CID }, + { NS_WINDOWMEDIATOR_CONTRACTID, &kNS_WINDOWMEDIATOR_CID }, + { nullptr } +}; + +static nsresult +nsAppShellModuleConstructor() +{ + return nsChromeTreeOwner::InitGlobals(); +} + +static void +nsAppShellModuleDestructor() +{ + nsChromeTreeOwner::FreeGlobals(); +} + +static const mozilla::Module kAppShellModule = { + mozilla::Module::kVersion, + kAppShellCIDs, + kAppShellContracts, + nullptr, + nullptr, + nsAppShellModuleConstructor, + nsAppShellModuleDestructor +}; + +NSMODULE_DEFN(appshell) = &kAppShellModule; diff --git a/xpfe/appshell/nsAppShellService.cpp b/xpfe/appshell/nsAppShellService.cpp new file mode 100644 index 000000000..7e1ddf16c --- /dev/null +++ b/xpfe/appshell/nsAppShellService.cpp @@ -0,0 +1,1001 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "nsIAppShellService.h" +#include "nsIComponentManager.h" +#include "nsIURL.h" +#include "nsNetUtil.h" +#include "nsIServiceManager.h" +#include "nsIObserverService.h" +#include "nsIObserver.h" +#include "nsIXPConnect.h" +#include "nsIXULRuntime.h" + +#include "nsIWindowMediator.h" +#include "nsIWindowWatcher.h" +#include "nsPIWindowWatcher.h" +#include "nsIDOMWindow.h" +#include "nsPIDOMWindow.h" +#include "nsWebShellWindow.h" + +#include "prprf.h" + +#include "nsWidgetInitData.h" +#include "nsWidgetsCID.h" +#include "nsIWidget.h" +#include "nsIRequestObserver.h" +#include "nsIEmbeddingSiteWindow.h" + +#include "nsAppShellService.h" +#include "nsContentUtils.h" +#include "nsThreadUtils.h" +#include "nsISupportsPrimitives.h" +#include "nsIChromeRegistry.h" +#include "nsILoadContext.h" +#include "nsIWebNavigation.h" +#include "nsIWindowlessBrowser.h" + +#include "mozilla/Attributes.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "mozilla/StartupTimeline.h" + +#include "nsEmbedCID.h" +#include "nsIWebBrowser.h" +#include "nsIDocShell.h" + +#ifdef MOZ_INSTRUMENT_EVENT_LOOP +#include "EventTracer.h" +#endif + +using namespace mozilla; + +// Default URL for the hidden window, can be overridden by a pref on Mac +#define DEFAULT_HIDDENWINDOW_URL "resource://gre-resources/hiddenWindow.html" + +class nsIAppShell; + +nsAppShellService::nsAppShellService() : + mXPCOMWillShutDown(false), + mXPCOMShuttingDown(false), + mModalWindowCount(0), + mApplicationProvidedHiddenWindow(false), + mScreenId(0) +{ + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + + if (obs) { + obs->AddObserver(this, "xpcom-will-shutdown", false); + obs->AddObserver(this, "xpcom-shutdown", false); + } +} + +nsAppShellService::~nsAppShellService() +{ +} + + +/* + * Implement the nsISupports methods... + */ +NS_IMPL_ISUPPORTS(nsAppShellService, + nsIAppShellService, + nsIObserver) + +NS_IMETHODIMP +nsAppShellService::CreateHiddenWindow() +{ + return CreateHiddenWindowHelper(false); +} + +NS_IMETHODIMP +nsAppShellService::SetScreenId(uint32_t aScreenId) +{ + mScreenId = aScreenId; + return NS_OK; +} + +void +nsAppShellService::EnsurePrivateHiddenWindow() +{ + if (!mHiddenPrivateWindow) { + CreateHiddenWindowHelper(true); + } +} + +nsresult +nsAppShellService::CreateHiddenWindowHelper(bool aIsPrivate) +{ + nsresult rv; + int32_t initialHeight = 100, initialWidth = 100; + +#ifdef XP_MACOSX + uint32_t chromeMask = 0; + nsAdoptingCString prefVal = + Preferences::GetCString("browser.hiddenWindowChromeURL"); + const char* hiddenWindowURL = prefVal.get() ? prefVal.get() : DEFAULT_HIDDENWINDOW_URL; + if (aIsPrivate) { + hiddenWindowURL = DEFAULT_HIDDENWINDOW_URL; + } else { + mApplicationProvidedHiddenWindow = prefVal.get() ? true : false; + } +#else + static const char hiddenWindowURL[] = DEFAULT_HIDDENWINDOW_URL; + uint32_t chromeMask = nsIWebBrowserChrome::CHROME_ALL; +#endif + + nsCOMPtr<nsIURI> url; + rv = NS_NewURI(getter_AddRefs(url), hiddenWindowURL); + NS_ENSURE_SUCCESS(rv, rv); + + RefPtr<nsWebShellWindow> newWindow; + if (!aIsPrivate) { + rv = JustCreateTopWindow(nullptr, url, + chromeMask, initialWidth, initialHeight, + true, nullptr, nullptr, getter_AddRefs(newWindow)); + NS_ENSURE_SUCCESS(rv, rv); + + mHiddenWindow.swap(newWindow); + } else { + // Create the hidden private window + chromeMask |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW; + + rv = JustCreateTopWindow(nullptr, url, + chromeMask, initialWidth, initialHeight, + true, nullptr, nullptr, getter_AddRefs(newWindow)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIDocShell> docShell; + newWindow->GetDocShell(getter_AddRefs(docShell)); + if (docShell) { + docShell->SetAffectPrivateSessionLifetime(false); + } + + mHiddenPrivateWindow.swap(newWindow); + } + + // RegisterTopLevelWindow(newWindow); -- Mac only + + return NS_OK; +} + +NS_IMETHODIMP +nsAppShellService::DestroyHiddenWindow() +{ + if (mHiddenWindow) { + mHiddenWindow->Destroy(); + + mHiddenWindow = nullptr; + } + + if (mHiddenPrivateWindow) { + mHiddenPrivateWindow->Destroy(); + + mHiddenPrivateWindow = nullptr; + } + + return NS_OK; +} + +/* + * Create a new top level window and display the given URL within it... + */ +NS_IMETHODIMP +nsAppShellService::CreateTopLevelWindow(nsIXULWindow *aParent, + nsIURI *aUrl, + uint32_t aChromeMask, + int32_t aInitialWidth, + int32_t aInitialHeight, + nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpenerWindow, + nsIXULWindow **aResult) + +{ + nsresult rv; + + StartupTimeline::RecordOnce(StartupTimeline::CREATE_TOP_LEVEL_WINDOW); + + RefPtr<nsWebShellWindow> newWindow; + rv = JustCreateTopWindow(aParent, aUrl, + aChromeMask, aInitialWidth, aInitialHeight, + false, aOpeningTab, aOpenerWindow, + getter_AddRefs(newWindow)); + newWindow.forget(aResult); + + if (NS_SUCCEEDED(rv)) { + // the addref resulting from this is the owning addref for this window + RegisterTopLevelWindow(*aResult); + nsCOMPtr<nsIXULWindow> parent; + if (aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) + parent = aParent; + (*aResult)->SetZLevel(CalculateWindowZLevel(parent, aChromeMask)); + } + + return rv; +} + +/* + * This class provides a stub implementation of nsIWebBrowserChrome2, as needed + * by nsAppShellService::CreateWindowlessBrowser + */ +class WebBrowserChrome2Stub : public nsIWebBrowserChrome2, + public nsIEmbeddingSiteWindow, + public nsIInterfaceRequestor, + public nsSupportsWeakReference { +protected: + virtual ~WebBrowserChrome2Stub() {} +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIWEBBROWSERCHROME + NS_DECL_NSIWEBBROWSERCHROME2 + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIEMBEDDINGSITEWINDOW +}; + +NS_INTERFACE_MAP_BEGIN(WebBrowserChrome2Stub) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserChrome) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(WebBrowserChrome2Stub) +NS_IMPL_RELEASE(WebBrowserChrome2Stub) + +NS_IMETHODIMP +WebBrowserChrome2Stub::SetStatus(uint32_t aStatusType, const char16_t* aStatus) +{ + return NS_OK; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::GetWebBrowser(nsIWebBrowser** aWebBrowser) +{ + NS_NOTREACHED("WebBrowserChrome2Stub::GetWebBrowser is not supported"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::SetWebBrowser(nsIWebBrowser* aWebBrowser) +{ + NS_NOTREACHED("WebBrowserChrome2Stub::SetWebBrowser is not supported"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::GetChromeFlags(uint32_t* aChromeFlags) +{ + *aChromeFlags = 0; + return NS_OK; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::SetChromeFlags(uint32_t aChromeFlags) +{ + NS_NOTREACHED("WebBrowserChrome2Stub::SetChromeFlags is not supported"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::DestroyBrowserWindow() +{ + NS_NOTREACHED("WebBrowserChrome2Stub::DestroyBrowserWindow is not supported"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::SizeBrowserTo(int32_t aCX, int32_t aCY) +{ + NS_NOTREACHED("WebBrowserChrome2Stub::SizeBrowserTo is not supported"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::ShowAsModal() +{ + NS_NOTREACHED("WebBrowserChrome2Stub::ShowAsModal is not supported"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::IsWindowModal(bool* aResult) +{ + *aResult = false; + return NS_OK; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::ExitModalEventLoop(nsresult aStatus) +{ + NS_NOTREACHED("WebBrowserChrome2Stub::ExitModalEventLoop is not supported"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::SetStatusWithContext(uint32_t aStatusType, + const nsAString& aStatusText, + nsISupports* aStatusContext) +{ + return NS_OK; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::GetInterface(const nsIID& aIID, void** aSink) +{ + return QueryInterface(aIID, aSink); +} + +// nsIEmbeddingSiteWindow impl +NS_IMETHODIMP +WebBrowserChrome2Stub::GetDimensions(uint32_t flags, int32_t* x, int32_t* y, int32_t* cx, int32_t* cy) +{ + if (x) { + *x = 0; + } + + if (y) { + *y = 0; + } + + if (cx) { + *cx = 0; + } + + if (cy) { + *cy = 0; + } + + return NS_OK; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::SetDimensions(uint32_t flags, int32_t x, int32_t y, int32_t cx, int32_t cy) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::SetFocus() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::GetVisibility(bool* aVisibility) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} +NS_IMETHODIMP +WebBrowserChrome2Stub::SetVisibility(bool aVisibility) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::GetTitle(char16_t** aTitle) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} +NS_IMETHODIMP +WebBrowserChrome2Stub::SetTitle(const char16_t* aTitle) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::GetSiteWindow(void** aSiteWindow) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +WebBrowserChrome2Stub::Blur() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +class BrowserDestroyer final : public Runnable +{ +public: + BrowserDestroyer(nsIWebBrowser *aBrowser, nsISupports *aContainer) : + mBrowser(aBrowser), + mContainer(aContainer) + { + } + + NS_IMETHOD + Run() override + { + // Explicitly destroy the browser, in case this isn't the last reference. + nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser); + return window->Destroy(); + } + +protected: + virtual ~BrowserDestroyer() {} + +private: + nsCOMPtr<nsIWebBrowser> mBrowser; + nsCOMPtr<nsISupports> mContainer; +}; + +// This is the "stub" we return from CreateWindowlessBrowser - it exists +// to manage the lifetimes of the nsIWebBrowser and container window. +// In particular, it keeps a strong reference to both, to prevent them from +// being collected while this object remains alive, and ensures that they +// aren't destroyed when it's not safe to run scripts. +class WindowlessBrowser final : public nsIWindowlessBrowser, + public nsIInterfaceRequestor +{ +public: + WindowlessBrowser(nsIWebBrowser *aBrowser, nsISupports *aContainer) : + mBrowser(aBrowser), + mContainer(aContainer), + mClosed(false) + { + mWebNavigation = do_QueryInterface(aBrowser); + mInterfaceRequestor = do_QueryInterface(aBrowser); + } + NS_DECL_ISUPPORTS + NS_FORWARD_SAFE_NSIWEBNAVIGATION(mWebNavigation) + NS_FORWARD_SAFE_NSIINTERFACEREQUESTOR(mInterfaceRequestor) + + NS_IMETHOD + Close() override + { + NS_ENSURE_TRUE(!mClosed, NS_ERROR_UNEXPECTED); + NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), + "WindowlessBrowser::Close called when not safe to run scripts"); + + mClosed = true; + + mWebNavigation = nullptr; + mInterfaceRequestor = nullptr; + + nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(mBrowser); + return window->Destroy(); + } + +protected: + virtual ~WindowlessBrowser() + { + if (mClosed) { + return; + } + + NS_WARNING("Windowless browser was not closed prior to destruction"); + + // The docshell destructor needs to dispatch events, and can only run + // when it's safe to run scripts. If this was triggered by GC, it may + // not always be safe to run scripts, in which cases we need to delay + // destruction until it is. + nsCOMPtr<nsIRunnable> runnable = new BrowserDestroyer(mBrowser, mContainer); + nsContentUtils::AddScriptRunner(runnable); + } + +private: + nsCOMPtr<nsIWebBrowser> mBrowser; + nsCOMPtr<nsIWebNavigation> mWebNavigation; + nsCOMPtr<nsIInterfaceRequestor> mInterfaceRequestor; + // we don't use the container but just hold a reference to it. + nsCOMPtr<nsISupports> mContainer; + + bool mClosed; +}; + +NS_IMPL_ISUPPORTS(WindowlessBrowser, nsIWindowlessBrowser, nsIWebNavigation, nsIInterfaceRequestor) + + +NS_IMETHODIMP +nsAppShellService::CreateWindowlessBrowser(bool aIsChrome, nsIWindowlessBrowser **aResult) +{ + /* First, we create an instance of nsWebBrowser. Instances of this class have + * an associated doc shell, which is what we're interested in. + */ + nsCOMPtr<nsIWebBrowser> browser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID); + if (!browser) { + NS_ERROR("Couldn't create instance of nsWebBrowser!"); + return NS_ERROR_FAILURE; + } + + /* Next, we set the container window for our instance of nsWebBrowser. Since + * we don't actually have a window, we instead set the container window to be + * an instance of WebBrowserChrome2Stub, which provides a stub implementation + * of nsIWebBrowserChrome2. + */ + RefPtr<WebBrowserChrome2Stub> stub = new WebBrowserChrome2Stub(); + browser->SetContainerWindow(stub); + + nsCOMPtr<nsIWebNavigation> navigation = do_QueryInterface(browser); + + nsCOMPtr<nsIDocShellTreeItem> item = do_QueryInterface(navigation); + item->SetItemType(aIsChrome ? nsIDocShellTreeItem::typeChromeWrapper + : nsIDocShellTreeItem::typeContentWrapper); + + /* A windowless web browser doesn't have an associated OS level window. To + * accomplish this, we initialize the window associated with our instance of + * nsWebBrowser with an instance of PuppetWidget, which provides a stub + * implementation of nsIWidget. + */ + nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(nullptr); + if (!widget) { + NS_ERROR("Couldn't create instance of PuppetWidget"); + return NS_ERROR_FAILURE; + } + nsresult rv = + widget->Create(nullptr, 0, LayoutDeviceIntRect(0, 0, 0, 0), nullptr); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIBaseWindow> window = do_QueryInterface(navigation); + window->InitWindow(0, widget, 0, 0, 0, 0); + window->Create(); + + nsISupports *isstub = NS_ISUPPORTS_CAST(nsIWebBrowserChrome2*, stub); + RefPtr<nsIWindowlessBrowser> result = new WindowlessBrowser(browser, isstub); + nsCOMPtr<nsIDocShell> docshell = do_GetInterface(result); + docshell->SetInvisible(true); + + result.forget(aResult); + return NS_OK; +} + +uint32_t +nsAppShellService::CalculateWindowZLevel(nsIXULWindow *aParent, + uint32_t aChromeMask) +{ + uint32_t zLevel; + + zLevel = nsIXULWindow::normalZ; + if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_RAISED) + zLevel = nsIXULWindow::raisedZ; + else if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_LOWERED) + zLevel = nsIXULWindow::loweredZ; + +#ifdef XP_MACOSX + /* Platforms on which modal windows are always application-modal, not + window-modal (that's just the Mac, right?) want modal windows to + be stacked on top of everyone else. + + On Mac OS X, bind modality to parent window instead of app (ala Mac OS 9) + */ + uint32_t modalDepMask = nsIWebBrowserChrome::CHROME_MODAL | + nsIWebBrowserChrome::CHROME_DEPENDENT; + if (aParent && (aChromeMask & modalDepMask)) { + aParent->GetZLevel(&zLevel); + } +#else + /* Platforms with native support for dependent windows (that's everyone + but pre-Mac OS X, right?) know how to stack dependent windows. On these + platforms, give the dependent window the same level as its parent, + so we won't try to override the normal platform behaviour. */ + if ((aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) && aParent) + aParent->GetZLevel(&zLevel); +#endif + + return zLevel; +} + +#ifdef XP_WIN +/* + * Checks to see if any existing window is currently in fullscreen mode. + */ +static bool +CheckForFullscreenWindow() +{ + nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (!wm) + return false; + + nsCOMPtr<nsISimpleEnumerator> windowList; + wm->GetXULWindowEnumerator(nullptr, getter_AddRefs(windowList)); + if (!windowList) + return false; + + for (;;) { + bool more = false; + windowList->HasMoreElements(&more); + if (!more) + return false; + + nsCOMPtr<nsISupports> supportsWindow; + windowList->GetNext(getter_AddRefs(supportsWindow)); + nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supportsWindow)); + if (baseWin) { + nsCOMPtr<nsIWidget> widget; + baseWin->GetMainWidget(getter_AddRefs(widget)); + if (widget && widget->SizeMode() == nsSizeMode_Fullscreen) { + return true; + } + } + } + return false; +} +#endif + +/* + * Just do the window-making part of CreateTopLevelWindow + */ +nsresult +nsAppShellService::JustCreateTopWindow(nsIXULWindow *aParent, + nsIURI *aUrl, + uint32_t aChromeMask, + int32_t aInitialWidth, + int32_t aInitialHeight, + bool aIsHiddenWindow, + nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpenerWindow, + nsWebShellWindow **aResult) +{ + *aResult = nullptr; + NS_ENSURE_STATE(!mXPCOMWillShutDown); + + nsCOMPtr<nsIXULWindow> parent; + if (aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) + parent = aParent; + + RefPtr<nsWebShellWindow> window = new nsWebShellWindow(aChromeMask); + +#ifdef XP_WIN + // If the parent is currently fullscreen, tell the child to ignore persisted + // full screen states. This way new browser windows open on top of fullscreen + // windows normally. + if (window && CheckForFullscreenWindow()) + window->IgnoreXULSizeMode(true); +#endif + + nsWidgetInitData widgetInitData; + + if (aIsHiddenWindow) + widgetInitData.mWindowType = eWindowType_invisible; + else + widgetInitData.mWindowType = aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG ? + eWindowType_dialog : eWindowType_toplevel; + + if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_POPUP) + widgetInitData.mWindowType = eWindowType_popup; + + if (aChromeMask & nsIWebBrowserChrome::CHROME_MAC_SUPPRESS_ANIMATION) + widgetInitData.mIsAnimationSuppressed = true; + +#ifdef XP_MACOSX + // Mac OS X sheet support + // Adding CHROME_OPENAS_CHROME to sheetMask makes modal windows opened from + // nsGlobalWindow::ShowModalDialog() be dialogs (not sheets), while modal + // windows opened from nsPromptService::DoDialog() still are sheets. This + // fixes bmo bug 395465 (see nsCocoaWindow::StandardCreate() and + // nsCocoaWindow::SetModal()). + uint32_t sheetMask = nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | + nsIWebBrowserChrome::CHROME_MODAL | + nsIWebBrowserChrome::CHROME_OPENAS_CHROME; + if (parent && + (parent != mHiddenWindow && parent != mHiddenPrivateWindow) && + ((aChromeMask & sheetMask) == sheetMask)) { + widgetInitData.mWindowType = eWindowType_sheet; + } +#endif + +#if defined(XP_WIN) + if (widgetInitData.mWindowType == eWindowType_toplevel || + widgetInitData.mWindowType == eWindowType_dialog) + widgetInitData.clipChildren = true; +#endif + + // note default chrome overrides other OS chrome settings, but + // not internal chrome + if (aChromeMask & nsIWebBrowserChrome::CHROME_DEFAULT) + widgetInitData.mBorderStyle = eBorderStyle_default; + else if ((aChromeMask & nsIWebBrowserChrome::CHROME_ALL) == nsIWebBrowserChrome::CHROME_ALL) + widgetInitData.mBorderStyle = eBorderStyle_all; + else { + widgetInitData.mBorderStyle = eBorderStyle_none; // assumes none == 0x00 + if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_BORDERS) + widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_border); + if (aChromeMask & nsIWebBrowserChrome::CHROME_TITLEBAR) + widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_title); + if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_CLOSE) + widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_close); + if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) { + widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_resizeh); + // only resizable windows get the maximize button (but not dialogs) + if (!(aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG)) + widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_maximize); + } + // all windows (except dialogs) get minimize buttons and the system menu + if (!(aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG)) + widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_minimize | eBorderStyle_menu); + // but anyone can explicitly ask for a minimize button + if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_MIN) { + widgetInitData.mBorderStyle = static_cast<enum nsBorderStyle>(widgetInitData.mBorderStyle | eBorderStyle_minimize); + } + } + + if (aInitialWidth == nsIAppShellService::SIZE_TO_CONTENT || + aInitialHeight == nsIAppShellService::SIZE_TO_CONTENT) { + aInitialWidth = 1; + aInitialHeight = 1; + window->SetIntrinsicallySized(true); + } + + bool center = aChromeMask & nsIWebBrowserChrome::CHROME_CENTER_SCREEN; + + nsCOMPtr<nsIXULChromeRegistry> reg = + mozilla::services::GetXULChromeRegistryService(); + if (reg) { + nsAutoCString package; + package.AssignLiteral("global"); + bool isRTL = false; + reg->IsLocaleRTL(package, &isRTL); + widgetInitData.mRTL = isRTL; + } + +#ifdef MOZ_WIDGET_GONK + // B2G multi-screen support. Screen ID is for differentiating screens of + // windows, and due to the hardware limitation, it is platform-specific for + // now, which align with the value of display type defined in HWC. + widgetInitData.mScreenId = mScreenId; +#endif + + nsresult rv = window->Initialize(parent, center ? aParent : nullptr, + aUrl, aInitialWidth, aInitialHeight, + aIsHiddenWindow, aOpeningTab, + aOpenerWindow, widgetInitData); + + NS_ENSURE_SUCCESS(rv, rv); + + // Enforce the Private Browsing autoStart pref first. + bool isPrivateBrowsingWindow = + Preferences::GetBool("browser.privatebrowsing.autostart"); + bool isUsingRemoteTabs = mozilla::BrowserTabsRemoteAutostart(); + + if (aChromeMask & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) { + // Caller requested a private window + isPrivateBrowsingWindow = true; + } + if (aChromeMask & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) { + isUsingRemoteTabs = true; + } + + nsCOMPtr<mozIDOMWindowProxy> domWin = do_GetInterface(aParent); + nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(domWin); + nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(webNav); + + if (!isPrivateBrowsingWindow && parentContext) { + // Ensure that we propagate any existing private browsing status + // from the parent, even if it will not actually be used + // as a parent value. + isPrivateBrowsingWindow = parentContext->UsePrivateBrowsing(); + } + + if (parentContext) { + isUsingRemoteTabs = parentContext->UseRemoteTabs(); + } + + nsCOMPtr<mozIDOMWindowProxy> newDomWin = + do_GetInterface(NS_ISUPPORTS_CAST(nsIBaseWindow*, window)); + nsCOMPtr<nsIWebNavigation> newWebNav = do_GetInterface(newDomWin); + nsCOMPtr<nsILoadContext> thisContext = do_GetInterface(newWebNav); + if (thisContext) { + thisContext->SetPrivateBrowsing(isPrivateBrowsingWindow); + thisContext->SetRemoteTabs(isUsingRemoteTabs); + } + + window.forget(aResult); + if (parent) + parent->AddChildWindow(*aResult); + + if (center) + rv = (*aResult)->Center(parent, parent ? false : true, false); + + return rv; +} + +NS_IMETHODIMP +nsAppShellService::GetHiddenWindow(nsIXULWindow **aWindow) +{ + NS_ENSURE_ARG_POINTER(aWindow); + + *aWindow = mHiddenWindow; + NS_IF_ADDREF(*aWindow); + return *aWindow ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsAppShellService::GetHiddenDOMWindow(mozIDOMWindowProxy **aWindow) +{ + nsresult rv; + nsCOMPtr<nsIDocShell> docShell; + NS_ENSURE_TRUE(mHiddenWindow, NS_ERROR_FAILURE); + + rv = mHiddenWindow->GetDocShell(getter_AddRefs(docShell)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); + + nsCOMPtr<nsPIDOMWindowOuter> hiddenDOMWindow(docShell->GetWindow()); + hiddenDOMWindow.forget(aWindow); + return *aWindow ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsAppShellService::GetHiddenPrivateWindow(nsIXULWindow **aWindow) +{ + NS_ENSURE_ARG_POINTER(aWindow); + + EnsurePrivateHiddenWindow(); + + *aWindow = mHiddenPrivateWindow; + NS_IF_ADDREF(*aWindow); + return *aWindow ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsAppShellService::GetHiddenPrivateDOMWindow(mozIDOMWindowProxy **aWindow) +{ + EnsurePrivateHiddenWindow(); + + nsresult rv; + nsCOMPtr<nsIDocShell> docShell; + NS_ENSURE_TRUE(mHiddenPrivateWindow, NS_ERROR_FAILURE); + + rv = mHiddenPrivateWindow->GetDocShell(getter_AddRefs(docShell)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); + + nsCOMPtr<nsPIDOMWindowOuter> hiddenPrivateDOMWindow(docShell->GetWindow()); + hiddenPrivateDOMWindow.forget(aWindow); + return *aWindow ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsAppShellService::GetHasHiddenPrivateWindow(bool* aHasPrivateWindow) +{ + NS_ENSURE_ARG_POINTER(aHasPrivateWindow); + + *aHasPrivateWindow = !!mHiddenPrivateWindow; + return NS_OK; +} + +NS_IMETHODIMP +nsAppShellService::GetApplicationProvidedHiddenWindow(bool* aAPHW) +{ + *aAPHW = mApplicationProvidedHiddenWindow; + return NS_OK; +} + +/* + * Register a new top level window (created elsewhere) + */ +NS_IMETHODIMP +nsAppShellService::RegisterTopLevelWindow(nsIXULWindow* aWindow) +{ + NS_ENSURE_ARG_POINTER(aWindow); + + nsCOMPtr<nsIDocShell> docShell; + aWindow->GetDocShell(getter_AddRefs(docShell)); + NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); + + nsCOMPtr<nsPIDOMWindowOuter> domWindow(docShell->GetWindow()); + NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE); + domWindow->SetInitialPrincipalToSubject(); + + // tell the window mediator about the new window + nsCOMPtr<nsIWindowMediator> mediator + ( do_GetService(NS_WINDOWMEDIATOR_CONTRACTID) ); + NS_ASSERTION(mediator, "Couldn't get window mediator."); + + if (mediator) + mediator->RegisterWindow(aWindow); + + // tell the window watcher about the new window + nsCOMPtr<nsPIWindowWatcher> wwatcher ( do_GetService(NS_WINDOWWATCHER_CONTRACTID) ); + NS_ASSERTION(wwatcher, "No windowwatcher?"); + if (wwatcher && domWindow) { + wwatcher->AddWindow(domWindow, 0); + } + + // an ongoing attempt to quit is stopped by a newly opened window + nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService(); + NS_ASSERTION(obssvc, "Couldn't get observer service."); + + if (obssvc) { + obssvc->NotifyObservers(aWindow, "xul-window-registered", nullptr); + nsXULWindow* xulWindow = static_cast<nsXULWindow*>(aWindow); + xulWindow->WasRegistered(); + } + + return NS_OK; +} + + +NS_IMETHODIMP +nsAppShellService::UnregisterTopLevelWindow(nsIXULWindow* aWindow) +{ + if (mXPCOMShuttingDown) { + /* return an error code in order to: + - avoid doing anything with other member variables while we are in + the destructor + - notify the caller not to release the AppShellService after + unregistering the window + (we don't want to be deleted twice consecutively to + mHiddenWindow->Destroy() in our destructor) + */ + return NS_ERROR_FAILURE; + } + + NS_ENSURE_ARG_POINTER(aWindow); + + if (aWindow == mHiddenWindow) { + // CreateHiddenWindow() does not register the window, so we're done. + return NS_OK; + } + if (aWindow == mHiddenPrivateWindow) { + // CreateHiddenWindow() does not register the window, so we're done. + return NS_OK; + } + + // tell the window mediator + nsCOMPtr<nsIWindowMediator> mediator + ( do_GetService(NS_WINDOWMEDIATOR_CONTRACTID) ); + NS_ASSERTION(mediator, "Couldn't get window mediator. Doing xpcom shutdown?"); + + if (mediator) + mediator->UnregisterWindow(aWindow); + + // tell the window watcher + nsCOMPtr<nsPIWindowWatcher> wwatcher ( do_GetService(NS_WINDOWWATCHER_CONTRACTID) ); + NS_ASSERTION(wwatcher, "Couldn't get windowwatcher, doing xpcom shutdown?"); + if (wwatcher) { + nsCOMPtr<nsIDocShell> docShell; + aWindow->GetDocShell(getter_AddRefs(docShell)); + if (docShell) { + nsCOMPtr<nsPIDOMWindowOuter> domWindow(docShell->GetWindow()); + if (domWindow) + wwatcher->RemoveWindow(domWindow); + } + } + + return NS_OK; +} + + +NS_IMETHODIMP +nsAppShellService::Observe(nsISupports* aSubject, const char *aTopic, + const char16_t *aData) +{ + if (!strcmp(aTopic, "xpcom-will-shutdown")) { + mXPCOMWillShutDown = true; + } else if (!strcmp(aTopic, "xpcom-shutdown")) { + mXPCOMShuttingDown = true; + if (mHiddenWindow) { + mHiddenWindow->Destroy(); + } + if (mHiddenPrivateWindow) { + mHiddenPrivateWindow->Destroy(); + } + } else { + NS_ERROR("Unexpected observer topic!"); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsAppShellService::StartEventLoopLagTracking(bool* aResult) +{ +#ifdef MOZ_INSTRUMENT_EVENT_LOOP + *aResult = mozilla::InitEventTracing(true); +#endif + return NS_OK; +} + +NS_IMETHODIMP +nsAppShellService::StopEventLoopLagTracking() +{ +#ifdef MOZ_INSTRUMENT_EVENT_LOOP + mozilla::ShutdownEventTracing(); +#endif + return NS_OK; +} diff --git a/xpfe/appshell/nsAppShellService.h b/xpfe/appshell/nsAppShellService.h new file mode 100644 index 000000000..feb7b9947 --- /dev/null +++ b/xpfe/appshell/nsAppShellService.h @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef __nsAppShellService_h +#define __nsAppShellService_h + +#include "nsIAppShellService.h" +#include "nsIObserver.h" + +//Interfaces Needed +#include "nsWebShellWindow.h" +#include "nsStringFwd.h" +#include "nsAutoPtr.h" +#include "nsITabParent.h" +#include "mozilla/Attributes.h" + +// {0099907D-123C-4853-A46A-43098B5FB68C} +#define NS_APPSHELLSERVICE_CID \ +{ 0x99907d, 0x123c, 0x4853, { 0xa4, 0x6a, 0x43, 0x9, 0x8b, 0x5f, 0xb6, 0x8c } } + +class nsAppShellService final : public nsIAppShellService, + public nsIObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIAPPSHELLSERVICE + NS_DECL_NSIOBSERVER + + nsAppShellService(); + +protected: + ~nsAppShellService(); + + nsresult CreateHiddenWindowHelper(bool aIsPrivate); + void EnsurePrivateHiddenWindow(); + + nsresult JustCreateTopWindow(nsIXULWindow *aParent, + nsIURI *aUrl, + uint32_t aChromeMask, + int32_t aInitialWidth, int32_t aInitialHeight, + bool aIsHiddenWindow, + nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpenerWindow, + nsWebShellWindow **aResult); + uint32_t CalculateWindowZLevel(nsIXULWindow *aParent, uint32_t aChromeMask); + + RefPtr<nsWebShellWindow> mHiddenWindow; + RefPtr<nsWebShellWindow> mHiddenPrivateWindow; + bool mXPCOMWillShutDown; + bool mXPCOMShuttingDown; + uint16_t mModalWindowCount; + bool mApplicationProvidedHiddenWindow; + uint32_t mScreenId; +}; + +#endif diff --git a/xpfe/appshell/nsAppShellWindowEnumerator.cpp b/xpfe/appshell/nsAppShellWindowEnumerator.cpp new file mode 100644 index 000000000..dc5fc120a --- /dev/null +++ b/xpfe/appshell/nsAppShellWindowEnumerator.cpp @@ -0,0 +1,495 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsAppShellWindowEnumerator.h" + +#include "nsIContentViewer.h" +#include "nsIDocShell.h" +#include "nsIDocument.h" +#include "nsIDOMDocument.h" +#include "nsIDOMElement.h" +#include "nsIDOMWindow.h" +#include "nsIFactory.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIXULWindow.h" + +#include "nsWindowMediator.h" + +// +// static helper functions +// + +static nsCOMPtr<nsIDOMNode> GetDOMNodeFromDocShell(nsIDocShell *aShell); +static void GetAttribute(nsIXULWindow *inWindow, const nsAString &inAttribute, + nsAString &outValue); +static void GetWindowType(nsIXULWindow* inWindow, nsString &outType); + +nsCOMPtr<nsIDOMNode> GetDOMNodeFromDocShell(nsIDocShell *aShell) +{ + nsCOMPtr<nsIDOMNode> node; + + nsCOMPtr<nsIContentViewer> cv; + aShell->GetContentViewer(getter_AddRefs(cv)); + if (cv) { + nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(cv->GetDocument())); + if (domdoc) { + nsCOMPtr<nsIDOMElement> element; + domdoc->GetDocumentElement(getter_AddRefs(element)); + if (element) + node = element; + } + } + + return node; +} + +// generic "retrieve the value of a XUL attribute" function +void GetAttribute(nsIXULWindow *inWindow, const nsAString &inAttribute, + nsAString &outValue) +{ + nsCOMPtr<nsIDocShell> shell; + if (inWindow && NS_SUCCEEDED(inWindow->GetDocShell(getter_AddRefs(shell)))) { + nsCOMPtr<nsIDOMNode> node(GetDOMNodeFromDocShell(shell)); + if (node) { + nsCOMPtr<nsIDOMElement> webshellElement(do_QueryInterface(node)); + if (webshellElement) + webshellElement->GetAttribute(inAttribute, outValue); + } + } +} + +// retrieve the window type, stored as the value of a particular +// attribute in its XUL window tag +void GetWindowType(nsIXULWindow* aWindow, nsString &outType) +{ + GetAttribute(aWindow, NS_LITERAL_STRING("windowtype"), outType); +} + +// +// nsWindowInfo +// + +nsWindowInfo::nsWindowInfo(nsIXULWindow* inWindow, int32_t inTimeStamp) : + mWindow(inWindow),mTimeStamp(inTimeStamp),mZLevel(nsIXULWindow::normalZ) +{ + ReferenceSelf(true, true); +} + +nsWindowInfo::~nsWindowInfo() +{ +} + +// return true if the window described by this WindowInfo has a type +// equal to the given type +bool nsWindowInfo::TypeEquals(const nsAString &aType) +{ + nsAutoString rtnString; + GetWindowType(mWindow, rtnString); + return rtnString == aType; +} + +// insert the struct into their two linked lists, in position after the +// given (independent) method arguments +void nsWindowInfo::InsertAfter(nsWindowInfo *inOlder , nsWindowInfo *inHigher) +{ + if (inOlder) { + mOlder = inOlder; + mYounger = inOlder->mYounger; + mOlder->mYounger = this; + if (mOlder->mOlder == mOlder) + mOlder->mOlder = this; + mYounger->mOlder = this; + if (mYounger->mYounger == mYounger) + mYounger->mYounger = this; + } + if (inHigher) { + mHigher = inHigher; + mLower = inHigher->mLower; + mHigher->mLower = this; + if (mHigher->mHigher == mHigher) + mHigher->mHigher = this; + mLower->mHigher = this; + if (mLower->mLower == mLower) + mLower->mLower = this; + } +} + +// remove the struct from its linked lists +void nsWindowInfo::Unlink(bool inAge, bool inZ) +{ + if (inAge) { + mOlder->mYounger = mYounger; + mYounger->mOlder = mOlder; + } + if (inZ) { + mLower->mHigher = mHigher; + mHigher->mLower = mLower; + } + ReferenceSelf(inAge, inZ); +} + +// initialize the struct to be a valid linked list of one element +void nsWindowInfo::ReferenceSelf(bool inAge, bool inZ) +{ + if (inAge) { + mYounger = this; + mOlder = this; + } + if (inZ) { + mLower = this; + mHigher = this; + } +} + +// +// nsAppShellWindowEnumerator +// + +NS_IMPL_ISUPPORTS(nsAppShellWindowEnumerator, nsISimpleEnumerator) + +nsAppShellWindowEnumerator::nsAppShellWindowEnumerator( + const char16_t* aTypeString, + nsWindowMediator& aMediator) : + mWindowMediator(&aMediator), mType(aTypeString), mCurrentPosition(nullptr) +{ + mWindowMediator->AddEnumerator(this); + NS_ADDREF(mWindowMediator); +} + +nsAppShellWindowEnumerator::~nsAppShellWindowEnumerator() +{ + mWindowMediator->RemoveEnumerator(this); + NS_RELEASE(mWindowMediator); +} + +// after mCurrentPosition has been initialized to point to the beginning +// of the appropriate list, adjust it if necessary +void nsAppShellWindowEnumerator::AdjustInitialPosition() +{ + if (!mType.IsEmpty() && mCurrentPosition && !mCurrentPosition->TypeEquals(mType)) + mCurrentPosition = FindNext(); +} + +NS_IMETHODIMP nsAppShellWindowEnumerator::HasMoreElements(bool *retval) +{ + if (!retval) + return NS_ERROR_INVALID_ARG; + + *retval = mCurrentPosition ? true : false; + return NS_OK; +} + +// if a window is being removed adjust the iterator's current position +void nsAppShellWindowEnumerator::WindowRemoved(nsWindowInfo *inInfo) +{ + if (mCurrentPosition == inInfo) + mCurrentPosition = FindNext(); +} + +// +// nsASDOMWindowEnumerator +// + +nsASDOMWindowEnumerator::nsASDOMWindowEnumerator( + const char16_t* aTypeString, + nsWindowMediator& aMediator) : + nsAppShellWindowEnumerator(aTypeString, aMediator) +{ +} + +nsASDOMWindowEnumerator::~nsASDOMWindowEnumerator() +{ +} + +NS_IMETHODIMP nsASDOMWindowEnumerator::GetNext(nsISupports **retval) +{ + if (!retval) + return NS_ERROR_INVALID_ARG; + + *retval = nullptr; + while (mCurrentPosition) { + nsCOMPtr<nsPIDOMWindowOuter> domWindow; + nsWindowMediator::GetDOMWindow(mCurrentPosition->mWindow, domWindow); + mCurrentPosition = FindNext(); + if (domWindow) + return CallQueryInterface(domWindow, retval); + } + return NS_OK; +} + +// +// nsASXULWindowEnumerator +// + +nsASXULWindowEnumerator::nsASXULWindowEnumerator( + const char16_t* aTypeString, + nsWindowMediator& aMediator) : + nsAppShellWindowEnumerator(aTypeString, aMediator) +{ +} + +nsASXULWindowEnumerator::~nsASXULWindowEnumerator() +{ +} + +NS_IMETHODIMP nsASXULWindowEnumerator::GetNext(nsISupports **retval) +{ + if (!retval) + return NS_ERROR_INVALID_ARG; + + *retval = nullptr; + if (mCurrentPosition) { + CallQueryInterface(mCurrentPosition->mWindow, retval); + mCurrentPosition = FindNext(); + } + return NS_OK; +} + +// +// nsASDOMWindowEarlyToLateEnumerator +// + +nsASDOMWindowEarlyToLateEnumerator::nsASDOMWindowEarlyToLateEnumerator( + const char16_t *aTypeString, + nsWindowMediator &aMediator) : + nsASDOMWindowEnumerator(aTypeString, aMediator) +{ + mCurrentPosition = aMediator.mOldestWindow; + AdjustInitialPosition(); +} + +nsASDOMWindowEarlyToLateEnumerator::~nsASDOMWindowEarlyToLateEnumerator() +{ +} + +nsWindowInfo *nsASDOMWindowEarlyToLateEnumerator::FindNext() +{ + nsWindowInfo *info, + *listEnd; + bool allWindows = mType.IsEmpty(); + + // see nsXULWindowEarlyToLateEnumerator::FindNext + if (!mCurrentPosition) + return nullptr; + + info = mCurrentPosition->mYounger; + listEnd = mWindowMediator->mOldestWindow; + + while (info != listEnd) { + if (allWindows || info->TypeEquals(mType)) + return info; + info = info->mYounger; + } + + return nullptr; +} + +// +// nsASXULWindowEarlyToLateEnumerator +// + +nsASXULWindowEarlyToLateEnumerator::nsASXULWindowEarlyToLateEnumerator( + const char16_t *aTypeString, + nsWindowMediator &aMediator) : + nsASXULWindowEnumerator(aTypeString, aMediator) +{ + mCurrentPosition = aMediator.mOldestWindow; + AdjustInitialPosition(); +} + +nsASXULWindowEarlyToLateEnumerator::~nsASXULWindowEarlyToLateEnumerator() +{ +} + +nsWindowInfo *nsASXULWindowEarlyToLateEnumerator::FindNext() +{ + nsWindowInfo *info, + *listEnd; + bool allWindows = mType.IsEmpty(); + + /* mCurrentPosition null is assumed to mean that the enumerator has run + its course and is now basically useless. It could also be interpreted + to mean that it was created at a time when there were no windows. In + that case it would probably be more appropriate to check to see whether + windows have subsequently been added. But it's not guaranteed that we'll + pick up newly added windows anyway (if they occurred previous to our + current position) so we just don't worry about that. */ + if (!mCurrentPosition) + return nullptr; + + info = mCurrentPosition->mYounger; + listEnd = mWindowMediator->mOldestWindow; + + while (info != listEnd) { + if (allWindows || info->TypeEquals(mType)) + return info; + info = info->mYounger; + } + + return nullptr; +} + +// +// nsASDOMWindowFrontToBackEnumerator +// + +nsASDOMWindowFrontToBackEnumerator::nsASDOMWindowFrontToBackEnumerator( + const char16_t *aTypeString, + nsWindowMediator &aMediator) : + nsASDOMWindowEnumerator(aTypeString, aMediator) +{ + mCurrentPosition = aMediator.mTopmostWindow; + AdjustInitialPosition(); +} + +nsASDOMWindowFrontToBackEnumerator::~nsASDOMWindowFrontToBackEnumerator() +{ +} + +nsWindowInfo *nsASDOMWindowFrontToBackEnumerator::FindNext() +{ + nsWindowInfo *info, + *listEnd; + bool allWindows = mType.IsEmpty(); + + // see nsXULWindowEarlyToLateEnumerator::FindNext + if (!mCurrentPosition) + return nullptr; + + info = mCurrentPosition->mLower; + listEnd = mWindowMediator->mTopmostWindow; + + while (info != listEnd) { + if (allWindows || info->TypeEquals(mType)) + return info; + info = info->mLower; + } + + return nullptr; +} + +// +// nsASXULWindowFrontToBackEnumerator +// + +nsASXULWindowFrontToBackEnumerator::nsASXULWindowFrontToBackEnumerator( + const char16_t *aTypeString, + nsWindowMediator &aMediator) : + nsASXULWindowEnumerator(aTypeString, aMediator) +{ + mCurrentPosition = aMediator.mTopmostWindow; + AdjustInitialPosition(); +} + +nsASXULWindowFrontToBackEnumerator::~nsASXULWindowFrontToBackEnumerator() +{ +} + +nsWindowInfo *nsASXULWindowFrontToBackEnumerator::FindNext() +{ + nsWindowInfo *info, + *listEnd; + bool allWindows = mType.IsEmpty(); + + // see nsXULWindowEarlyToLateEnumerator::FindNext + if (!mCurrentPosition) + return nullptr; + + info = mCurrentPosition->mLower; + listEnd = mWindowMediator->mTopmostWindow; + + while (info != listEnd) { + if (allWindows || info->TypeEquals(mType)) + return info; + info = info->mLower; + } + + return nullptr; +} + +// +// nsASDOMWindowBackToFrontEnumerator +// + +nsASDOMWindowBackToFrontEnumerator::nsASDOMWindowBackToFrontEnumerator( + const char16_t *aTypeString, + nsWindowMediator &aMediator) : + nsASDOMWindowEnumerator(aTypeString, aMediator) +{ + mCurrentPosition = aMediator.mTopmostWindow ? + aMediator.mTopmostWindow->mHigher : nullptr; + AdjustInitialPosition(); +} + +nsASDOMWindowBackToFrontEnumerator::~nsASDOMWindowBackToFrontEnumerator() +{ +} + +nsWindowInfo *nsASDOMWindowBackToFrontEnumerator::FindNext() +{ + nsWindowInfo *info, + *listEnd; + bool allWindows = mType.IsEmpty(); + + // see nsXULWindowEarlyToLateEnumerator::FindNext + if (!mCurrentPosition) + return nullptr; + + info = mCurrentPosition->mHigher; + listEnd = mWindowMediator->mTopmostWindow; + if (listEnd) + listEnd = listEnd->mHigher; + + while (info != listEnd) { + if (allWindows || info->TypeEquals(mType)) + return info; + info = info->mHigher; + } + + return nullptr; +} + +// +// nsASXULWindowBackToFrontEnumerator +// + +nsASXULWindowBackToFrontEnumerator::nsASXULWindowBackToFrontEnumerator( + const char16_t *aTypeString, + nsWindowMediator &aMediator) : + nsASXULWindowEnumerator(aTypeString, aMediator) +{ + mCurrentPosition = aMediator.mTopmostWindow ? + aMediator.mTopmostWindow->mHigher : nullptr; + AdjustInitialPosition(); +} + +nsASXULWindowBackToFrontEnumerator::~nsASXULWindowBackToFrontEnumerator() +{ +} + +nsWindowInfo *nsASXULWindowBackToFrontEnumerator::FindNext() +{ + nsWindowInfo *info, + *listEnd; + bool allWindows = mType.IsEmpty(); + + // see nsXULWindowEarlyToLateEnumerator::FindNext + if (!mCurrentPosition) + return nullptr; + + info = mCurrentPosition->mHigher; + listEnd = mWindowMediator->mTopmostWindow; + if (listEnd) + listEnd = listEnd->mHigher; + + while (info != listEnd) { + if (allWindows || info->TypeEquals(mType)) + return info; + info = info->mHigher; + } + + return nullptr; +} diff --git a/xpfe/appshell/nsAppShellWindowEnumerator.h b/xpfe/appshell/nsAppShellWindowEnumerator.h new file mode 100644 index 000000000..f9a8a2d52 --- /dev/null +++ b/xpfe/appshell/nsAppShellWindowEnumerator.h @@ -0,0 +1,166 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef nsAppShellWindowEnumerator_h +#define nsAppShellWindowEnumerator_h + +#include "nsCOMPtr.h" +#include "nsString.h" + +#include "nsISimpleEnumerator.h" +#include "nsIXULWindow.h" + +class nsWindowMediator; + +// +// nsWindowInfo +// + +struct nsWindowInfo +{ + nsWindowInfo(nsIXULWindow* inWindow, int32_t inTimeStamp); + ~nsWindowInfo(); + + nsCOMPtr<nsIXULWindow> mWindow; + int32_t mTimeStamp; + uint32_t mZLevel; + + // each struct is in two, independent, circular, doubly-linked lists + nsWindowInfo *mYounger, // next younger in sequence + *mOlder; + nsWindowInfo *mLower, // next lower in z-order + *mHigher; + + bool TypeEquals(const nsAString &aType); + void InsertAfter(nsWindowInfo *inOlder, nsWindowInfo *inHigher); + void Unlink(bool inAge, bool inZ); + void ReferenceSelf(bool inAge, bool inZ); +}; + +// +// virtual enumerators +// + +class nsAppShellWindowEnumerator : public nsISimpleEnumerator { + +friend class nsWindowMediator; + +public: + nsAppShellWindowEnumerator(const char16_t* aTypeString, + nsWindowMediator& inMediator); + NS_IMETHOD GetNext(nsISupports **retval) override = 0; + NS_IMETHOD HasMoreElements(bool *retval) override; + + NS_DECL_ISUPPORTS + +protected: + + virtual ~nsAppShellWindowEnumerator(); + + void AdjustInitialPosition(); + virtual nsWindowInfo *FindNext() = 0; + + void WindowRemoved(nsWindowInfo *inInfo); + + nsWindowMediator *mWindowMediator; + nsString mType; + nsWindowInfo *mCurrentPosition; +}; + +class nsASDOMWindowEnumerator : public nsAppShellWindowEnumerator { + +public: + nsASDOMWindowEnumerator(const char16_t* aTypeString, + nsWindowMediator& inMediator); + virtual ~nsASDOMWindowEnumerator(); + NS_IMETHOD GetNext(nsISupports **retval); +}; + +class nsASXULWindowEnumerator : public nsAppShellWindowEnumerator { + +public: + nsASXULWindowEnumerator(const char16_t* aTypeString, + nsWindowMediator& inMediator); + virtual ~nsASXULWindowEnumerator(); + NS_IMETHOD GetNext(nsISupports **retval); +}; + +// +// concrete enumerators +// + +class nsASDOMWindowEarlyToLateEnumerator : public nsASDOMWindowEnumerator { + +public: + nsASDOMWindowEarlyToLateEnumerator(const char16_t* aTypeString, + nsWindowMediator& inMediator); + + virtual ~nsASDOMWindowEarlyToLateEnumerator(); + +protected: + virtual nsWindowInfo *FindNext(); +}; + +class nsASXULWindowEarlyToLateEnumerator : public nsASXULWindowEnumerator { + +public: + nsASXULWindowEarlyToLateEnumerator(const char16_t* aTypeString, + nsWindowMediator& inMediator); + + virtual ~nsASXULWindowEarlyToLateEnumerator(); + +protected: + virtual nsWindowInfo *FindNext(); +}; + +class nsASDOMWindowFrontToBackEnumerator : public nsASDOMWindowEnumerator { + +public: + nsASDOMWindowFrontToBackEnumerator(const char16_t* aTypeString, + nsWindowMediator& inMediator); + + virtual ~nsASDOMWindowFrontToBackEnumerator(); + +protected: + virtual nsWindowInfo *FindNext(); +}; + +class nsASXULWindowFrontToBackEnumerator : public nsASXULWindowEnumerator { + +public: + nsASXULWindowFrontToBackEnumerator(const char16_t* aTypeString, + nsWindowMediator& inMediator); + + virtual ~nsASXULWindowFrontToBackEnumerator(); + +protected: + virtual nsWindowInfo *FindNext(); +}; + +class nsASDOMWindowBackToFrontEnumerator : public nsASDOMWindowEnumerator { + +public: + nsASDOMWindowBackToFrontEnumerator(const char16_t* aTypeString, + nsWindowMediator& inMediator); + + virtual ~nsASDOMWindowBackToFrontEnumerator(); + +protected: + virtual nsWindowInfo *FindNext(); +}; + +class nsASXULWindowBackToFrontEnumerator : public nsASXULWindowEnumerator { + +public: + nsASXULWindowBackToFrontEnumerator(const char16_t* aTypeString, + nsWindowMediator& inMediator); + + virtual ~nsASXULWindowBackToFrontEnumerator(); + +protected: + virtual nsWindowInfo *FindNext(); +}; + +#endif diff --git a/xpfe/appshell/nsChromeTreeOwner.cpp b/xpfe/appshell/nsChromeTreeOwner.cpp new file mode 100644 index 000000000..63fa86373 --- /dev/null +++ b/xpfe/appshell/nsChromeTreeOwner.cpp @@ -0,0 +1,561 @@ +/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * 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/. */ + +// Local Includes +#include "nsChromeTreeOwner.h" +#include "nsXULWindow.h" + +// Helper Classes +#include "nsString.h" +#include "nsIEmbeddingSiteWindow.h" +#include "nsIServiceManager.h" +#include "nsIDocShellTreeItem.h" + +// Interfaces needed to include +#include "nsIPrompt.h" +#include "nsIAuthPrompt.h" +#include "nsIBrowserDOMWindow.h" +#include "nsIWebProgress.h" +#include "nsIWidget.h" +#include "nsIWindowMediator.h" +#include "nsIDOMChromeWindow.h" +#include "nsIDOMNode.h" +#include "nsIDOMElement.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMXULElement.h" +#include "nsIXULBrowserWindow.h" +#include "mozilla/dom/Element.h" + +using namespace mozilla; + +//***************************************************************************** +// nsChromeTreeOwner string literals +//***************************************************************************** + +struct nsChromeTreeOwnerLiterals +{ + const nsLiteralString kPersist; + const nsLiteralString kScreenX; + const nsLiteralString kScreenY; + const nsLiteralString kWidth; + const nsLiteralString kHeight; + const nsLiteralString kSizemode; + const nsLiteralString kSpace; + + nsChromeTreeOwnerLiterals() + : NS_LITERAL_STRING_INIT(kPersist,"persist") + , NS_LITERAL_STRING_INIT(kScreenX,"screenX") + , NS_LITERAL_STRING_INIT(kScreenY,"screenY") + , NS_LITERAL_STRING_INIT(kWidth,"width") + , NS_LITERAL_STRING_INIT(kHeight,"height") + , NS_LITERAL_STRING_INIT(kSizemode,"sizemode") + , NS_LITERAL_STRING_INIT(kSpace," ") + {} +}; + +static nsChromeTreeOwnerLiterals *gLiterals; + +nsresult +nsChromeTreeOwner::InitGlobals() +{ + NS_ASSERTION(gLiterals == nullptr, "already initialized"); + gLiterals = new nsChromeTreeOwnerLiterals(); + return NS_OK; +} + +void +nsChromeTreeOwner::FreeGlobals() +{ + delete gLiterals; + gLiterals = nullptr; +} + +//***************************************************************************** +//*** nsChromeTreeOwner: Object Management +//***************************************************************************** + +nsChromeTreeOwner::nsChromeTreeOwner() : mXULWindow(nullptr) +{ +} + +nsChromeTreeOwner::~nsChromeTreeOwner() +{ +} + +//***************************************************************************** +// nsChromeTreeOwner::nsISupports +//***************************************************************************** + +NS_IMPL_ADDREF(nsChromeTreeOwner) +NS_IMPL_RELEASE(nsChromeTreeOwner) + +NS_INTERFACE_MAP_BEGIN(nsChromeTreeOwner) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellTreeOwner) + NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeOwner) + NS_INTERFACE_MAP_ENTRY(nsIBaseWindow) + NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) +NS_INTERFACE_MAP_END + +//***************************************************************************** +// nsChromeTreeOwner::nsIInterfaceRequestor +//***************************************************************************** + +NS_IMETHODIMP nsChromeTreeOwner::GetInterface(const nsIID& aIID, void** aSink) +{ + NS_ENSURE_ARG_POINTER(aSink); + + if(aIID.Equals(NS_GET_IID(nsIPrompt))) { + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetInterface(aIID, aSink); + } + if(aIID.Equals(NS_GET_IID(nsIAuthPrompt))) { + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetInterface(aIID, aSink); + } + if(aIID.Equals(NS_GET_IID(nsIWebBrowserChrome))) { + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetInterface(aIID, aSink); + } + if (aIID.Equals(NS_GET_IID(nsIEmbeddingSiteWindow))) { + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetInterface(aIID, aSink); + } + if (aIID.Equals(NS_GET_IID(nsIXULWindow))) { + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->QueryInterface(aIID, aSink); + } + + return QueryInterface(aIID, aSink); +} + +//***************************************************************************** +// nsChromeTreeOwner::nsIDocShellTreeOwner +//***************************************************************************** + +NS_IMETHODIMP +nsChromeTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell, + bool aPrimary, bool aTargetable, + const nsAString& aID) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->ContentShellAdded(aContentShell, aPrimary, aTargetable, + aID); +} + +NS_IMETHODIMP +nsChromeTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->ContentShellRemoved(aContentShell); +} + +NS_IMETHODIMP nsChromeTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetPrimaryContentShell(aShell); +} + +NS_IMETHODIMP +nsChromeTreeOwner::TabParentAdded(nsITabParent* aTab, bool aPrimary) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->TabParentAdded(aTab, aPrimary); +} + +NS_IMETHODIMP +nsChromeTreeOwner::TabParentRemoved(nsITabParent* aTab) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->TabParentRemoved(aTab); +} + +NS_IMETHODIMP +nsChromeTreeOwner::GetPrimaryTabParent(nsITabParent** aTab) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetPrimaryTabParent(aTab); +} + +NS_IMETHODIMP +nsChromeTreeOwner::GetPrimaryContentSize(int32_t* aWidth, + int32_t* aHeight) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetPrimaryContentSize(aWidth, aHeight); +} + +NS_IMETHODIMP +nsChromeTreeOwner::SetPrimaryContentSize(int32_t aWidth, + int32_t aHeight) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetPrimaryContentSize(aWidth, aHeight); +} + +NS_IMETHODIMP +nsChromeTreeOwner::GetRootShellSize(int32_t* aWidth, + int32_t* aHeight) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetRootShellSize(aWidth, aHeight); +} + +NS_IMETHODIMP +nsChromeTreeOwner::SetRootShellSize(int32_t aWidth, + int32_t aHeight) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetRootShellSize(aWidth, aHeight); +} + +NS_IMETHODIMP nsChromeTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, + int32_t aCX, int32_t aCY) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SizeShellTo(aShellItem, aCX, aCY); +} + +NS_IMETHODIMP +nsChromeTreeOwner::SetPersistence(bool aPersistPosition, + bool aPersistSize, + bool aPersistSizeMode) +{ + NS_ENSURE_STATE(mXULWindow); + nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement(); + if (!docShellElement) + return NS_ERROR_FAILURE; + + nsAutoString persistString; + docShellElement->GetAttribute(gLiterals->kPersist, persistString); + + bool saveString = false; + int32_t index; + +#define FIND_PERSIST_STRING(aString, aCond) \ + index = persistString.Find(aString); \ + if (!aCond && index > kNotFound) { \ + persistString.Cut(index, aString.Length()); \ + saveString = true; \ + } else if (aCond && index == kNotFound) { \ + persistString.Append(gLiterals->kSpace + aString); \ + saveString = true; \ + } + FIND_PERSIST_STRING(gLiterals->kScreenX, aPersistPosition); + FIND_PERSIST_STRING(gLiterals->kScreenY, aPersistPosition); + FIND_PERSIST_STRING(gLiterals->kWidth, aPersistSize); + FIND_PERSIST_STRING(gLiterals->kHeight, aPersistSize); + FIND_PERSIST_STRING(gLiterals->kSizemode, aPersistSizeMode); + + ErrorResult rv; + if (saveString) { + docShellElement->SetAttribute(gLiterals->kPersist, persistString, rv); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsChromeTreeOwner::GetPersistence(bool* aPersistPosition, + bool* aPersistSize, + bool* aPersistSizeMode) +{ + NS_ENSURE_STATE(mXULWindow); + nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement(); + if (!docShellElement) + return NS_ERROR_FAILURE; + + nsAutoString persistString; + docShellElement->GetAttribute(gLiterals->kPersist, persistString); + + // data structure doesn't quite match the question, but it's close enough + // for what we want (since this method is never actually called...) + if (aPersistPosition) + *aPersistPosition = persistString.Find(gLiterals->kScreenX) > kNotFound || + persistString.Find(gLiterals->kScreenY) > kNotFound; + if (aPersistSize) + *aPersistSize = persistString.Find(gLiterals->kWidth) > kNotFound || + persistString.Find(gLiterals->kHeight) > kNotFound; + if (aPersistSizeMode) + *aPersistSizeMode = persistString.Find(gLiterals->kSizemode) > kNotFound; + + return NS_OK; +} + +NS_IMETHODIMP +nsChromeTreeOwner::GetTargetableShellCount(uint32_t* aResult) +{ + *aResult = 0; + return NS_OK; +} + +NS_IMETHODIMP +nsChromeTreeOwner::GetHasPrimaryContent(bool* aResult) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetHasPrimaryContent(aResult); +} + +//***************************************************************************** +// nsChromeTreeOwner::nsIBaseWindow +//***************************************************************************** + +NS_IMETHODIMP nsChromeTreeOwner::InitWindow(nativeWindow aParentNativeWindow, + nsIWidget* parentWidget, int32_t x, int32_t y, int32_t cx, int32_t cy) +{ + // Ignore widget parents for now. Don't think those are a vaild thing to call. + NS_ENSURE_SUCCESS(SetPositionAndSize(x, y, cx, cy, 0), NS_ERROR_FAILURE); + + return NS_OK; +} + +NS_IMETHODIMP nsChromeTreeOwner::Create() +{ + NS_ASSERTION(false, "You can't call this"); + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP nsChromeTreeOwner::Destroy() +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->Destroy(); +} + +NS_IMETHODIMP nsChromeTreeOwner::GetUnscaledDevicePixelsPerCSSPixel(double *aScale) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale); +} + +NS_IMETHODIMP nsChromeTreeOwner::GetDevicePixelsPerDesktopPixel(double *aScale) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetDevicePixelsPerDesktopPixel(aScale); +} + +NS_IMETHODIMP nsChromeTreeOwner::SetPositionDesktopPix(int32_t x, int32_t y) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetPositionDesktopPix(x, y); +} + +NS_IMETHODIMP nsChromeTreeOwner::SetPosition(int32_t x, int32_t y) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetPosition(x, y); +} + +NS_IMETHODIMP nsChromeTreeOwner::GetPosition(int32_t* x, int32_t* y) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetPosition(x, y); +} + +NS_IMETHODIMP nsChromeTreeOwner::SetSize(int32_t cx, int32_t cy, bool fRepaint) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetSize(cx, cy, fRepaint); +} + +NS_IMETHODIMP nsChromeTreeOwner::GetSize(int32_t* cx, int32_t* cy) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetSize(cx, cy); +} + +NS_IMETHODIMP nsChromeTreeOwner::SetPositionAndSize(int32_t x, int32_t y, int32_t cx, + int32_t cy, uint32_t aFlags) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetPositionAndSize(x, y, cx, cy, aFlags); +} + +NS_IMETHODIMP nsChromeTreeOwner::GetPositionAndSize(int32_t* x, int32_t* y, int32_t* cx, + int32_t* cy) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetPositionAndSize(x, y, cx, cy); +} + +NS_IMETHODIMP nsChromeTreeOwner::Repaint(bool aForce) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->Repaint(aForce); +} + +NS_IMETHODIMP nsChromeTreeOwner::GetParentWidget(nsIWidget** aParentWidget) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetParentWidget(aParentWidget); +} + +NS_IMETHODIMP nsChromeTreeOwner::SetParentWidget(nsIWidget* aParentWidget) +{ + NS_ASSERTION(false, "You can't call this"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsChromeTreeOwner::GetParentNativeWindow(nativeWindow* aParentNativeWindow) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetParentNativeWindow(aParentNativeWindow); +} + +NS_IMETHODIMP nsChromeTreeOwner::SetParentNativeWindow(nativeWindow aParentNativeWindow) +{ + NS_ASSERTION(false, "You can't call this"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsChromeTreeOwner::GetNativeHandle(nsAString& aNativeHandle) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetNativeHandle(aNativeHandle); +} + +NS_IMETHODIMP nsChromeTreeOwner::GetVisibility(bool* aVisibility) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetVisibility(aVisibility); +} + +NS_IMETHODIMP nsChromeTreeOwner::SetVisibility(bool aVisibility) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetVisibility(aVisibility); +} + +NS_IMETHODIMP nsChromeTreeOwner::GetEnabled(bool *aEnabled) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetEnabled(aEnabled); +} + +NS_IMETHODIMP nsChromeTreeOwner::SetEnabled(bool aEnable) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetEnabled(aEnable); +} + +NS_IMETHODIMP nsChromeTreeOwner::GetMainWidget(nsIWidget** aMainWidget) +{ + NS_ENSURE_ARG_POINTER(aMainWidget); + NS_ENSURE_STATE(mXULWindow); + + *aMainWidget = mXULWindow->mWindow; + NS_IF_ADDREF(*aMainWidget); + + return NS_OK; +} + +NS_IMETHODIMP nsChromeTreeOwner::SetFocus() +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetFocus(); +} + +NS_IMETHODIMP nsChromeTreeOwner::GetTitle(char16_t** aTitle) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetTitle(aTitle); +} + +NS_IMETHODIMP nsChromeTreeOwner::SetTitle(const char16_t* aTitle) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetTitle(aTitle); +} + +//***************************************************************************** +// nsChromeTreeOwner::nsIWebProgressListener +//***************************************************************************** + +NS_IMETHODIMP +nsChromeTreeOwner::OnProgressChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + int32_t aCurSelfProgress, + int32_t aMaxSelfProgress, + int32_t aCurTotalProgress, + int32_t aMaxTotalProgress) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsChromeTreeOwner::OnStateChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + uint32_t aProgressStateFlags, + nsresult aStatus) +{ + return NS_OK; +} + +NS_IMETHODIMP nsChromeTreeOwner::OnLocationChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsIURI* aLocation, + uint32_t aFlags) +{ + bool itsForYou = true; + + if (aWebProgress) { + NS_ENSURE_STATE(mXULWindow); + nsCOMPtr<mozIDOMWindowProxy> progressWin; + aWebProgress->GetDOMWindow(getter_AddRefs(progressWin)); + + nsCOMPtr<nsIDocShell> docshell; + mXULWindow->GetDocShell(getter_AddRefs(docshell)); + // XXXkhuey this is totally wrong, bug 1223303. + nsCOMPtr<mozIDOMWindowProxy> ourWin(do_QueryInterface(docshell)); + + if (ourWin != progressWin) + itsForYou = false; + } + + // If loading a new root .xul document, then redo chrome. + if (itsForYou) { + NS_ENSURE_STATE(mXULWindow); + mXULWindow->mChromeLoaded = false; + } + return NS_OK; +} + +NS_IMETHODIMP +nsChromeTreeOwner::OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, + const char16_t* aMessage) +{ + return NS_OK; +} + + + +NS_IMETHODIMP +nsChromeTreeOwner::OnSecurityChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + uint32_t state) +{ + return NS_OK; +} + +//***************************************************************************** +// nsChromeTreeOwner: Helpers +//***************************************************************************** + +//***************************************************************************** +// nsChromeTreeOwner: Accessors +//***************************************************************************** + +void nsChromeTreeOwner::XULWindow(nsXULWindow* aXULWindow) +{ + mXULWindow = aXULWindow; +} + +nsXULWindow* nsChromeTreeOwner::XULWindow() +{ + return mXULWindow; +} diff --git a/xpfe/appshell/nsChromeTreeOwner.h b/xpfe/appshell/nsChromeTreeOwner.h new file mode 100644 index 000000000..77b0dea89 --- /dev/null +++ b/xpfe/appshell/nsChromeTreeOwner.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * 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/. */ + +#ifndef nsChromeTreeOwner_h__ +#define nsChromeTreeOwner_h__ + +// Helper Classes +#include "nsCOMPtr.h" + +// Interfaces Needed +#include "nsIBaseWindow.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIWebProgressListener.h" +#include "nsWeakReference.h" + +class nsXULWindow; + +class nsChromeTreeOwner : public nsIDocShellTreeOwner, + public nsIBaseWindow, + public nsIInterfaceRequestor, + public nsIWebProgressListener, + public nsSupportsWeakReference +{ +friend class nsXULWindow; + +public: + NS_DECL_ISUPPORTS + + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIBASEWINDOW + NS_DECL_NSIDOCSHELLTREEOWNER + NS_DECL_NSIWEBPROGRESSLISTENER + + static nsresult InitGlobals(); + static void FreeGlobals(); + +protected: + nsChromeTreeOwner(); + virtual ~nsChromeTreeOwner(); + + void XULWindow(nsXULWindow* aXULWindow); + nsXULWindow* XULWindow(); + +protected: + nsXULWindow* mXULWindow; +}; + +#endif /* nsChromeTreeOwner_h__ */ diff --git a/xpfe/appshell/nsContentTreeOwner.cpp b/xpfe/appshell/nsContentTreeOwner.cpp new file mode 100644 index 000000000..b39b7610f --- /dev/null +++ b/xpfe/appshell/nsContentTreeOwner.cpp @@ -0,0 +1,1131 @@ +/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=2 sw=2 et tw=79: + * + * 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/. */ + +// Local Includes +#include "nsContentTreeOwner.h" +#include "nsXULWindow.h" + +// Helper Classes +#include "nsIServiceManager.h" +#include "nsAutoPtr.h" + +// Interfaces needed to be included +#include "nsIDOMNode.h" +#include "nsIDOMElement.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMWindow.h" +#include "nsIDOMChromeWindow.h" +#include "nsIBrowserDOMWindow.h" +#include "nsIDOMXULElement.h" +#include "nsIEmbeddingSiteWindow.h" +#include "nsIPrompt.h" +#include "nsIAuthPrompt.h" +#include "nsIWindowMediator.h" +#include "nsIXULBrowserWindow.h" +#include "nsIPrincipal.h" +#include "nsIURIFixup.h" +#include "nsCDefaultURIFixup.h" +#include "nsIWebNavigation.h" +#include "nsDocShellCID.h" +#include "nsIExternalURLHandlerService.h" +#include "nsIMIMEInfo.h" +#include "nsIWidget.h" +#include "nsWindowWatcher.h" +#include "mozilla/BrowserElementParent.h" + +#include "nsIDOMDocument.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsIURI.h" +#include "nsIDocument.h" +#if defined(XP_MACOSX) +#include "nsThreadUtils.h" +#endif + +#include "mozilla/Preferences.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/ScriptSettings.h" + +using namespace mozilla; + +//***************************************************************************** +//*** nsSiteWindow declaration +//***************************************************************************** + +class nsSiteWindow : public nsIEmbeddingSiteWindow +{ + // nsSiteWindow shares a lifetime with nsContentTreeOwner, and proxies it's + // AddRef and Release calls to said object. + // When nsContentTreeOwner is destroyed, nsSiteWindow will be destroyed as well. + // nsContentTreeOwner is a friend class of nsSiteWindow such that it can call + // nsSiteWindow's destructor, which is private, as public destructors + // on reference counted classes are generally unsafe. + friend class nsContentTreeOwner; + +public: + explicit nsSiteWindow(nsContentTreeOwner *aAggregator); + + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIEMBEDDINGSITEWINDOW + +private: + virtual ~nsSiteWindow(); + nsContentTreeOwner *mAggregator; +}; + +//***************************************************************************** +//*** nsContentTreeOwner: Object Management +//***************************************************************************** + +nsContentTreeOwner::nsContentTreeOwner(bool fPrimary) : mXULWindow(nullptr), + mPrimary(fPrimary), mContentTitleSetting(false) +{ + // note if this fails, QI on nsIEmbeddingSiteWindow(2) will simply fail + mSiteWindow = new nsSiteWindow(this); +} + +nsContentTreeOwner::~nsContentTreeOwner() +{ + delete mSiteWindow; +} + +//***************************************************************************** +// nsContentTreeOwner::nsISupports +//***************************************************************************** + +NS_IMPL_ADDREF(nsContentTreeOwner) +NS_IMPL_RELEASE(nsContentTreeOwner) + +NS_INTERFACE_MAP_BEGIN(nsContentTreeOwner) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShellTreeOwner) + NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeOwner) + NS_INTERFACE_MAP_ENTRY(nsIBaseWindow) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2) + NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome3) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) + NS_INTERFACE_MAP_ENTRY(nsIWindowProvider) + // NOTE: This is using aggregation because there are some properties and + // method on nsIBaseWindow (which we implement) and on + // nsIEmbeddingSiteWindow (which we also implement) that have the same name. + // And it just so happens that we want different behavior for these methods + // and properties depending on the interface through which they're called + // (SetFocus() is a good example here). If it were not for that, we could + // ditch the aggregation and just deal with not being able to use NS_DECL_* + // macros for this stuff.... + NS_INTERFACE_MAP_ENTRY_AGGREGATED(nsIEmbeddingSiteWindow, mSiteWindow) +NS_INTERFACE_MAP_END + +//***************************************************************************** +// nsContentTreeOwner::nsIInterfaceRequestor +//***************************************************************************** + +NS_IMETHODIMP nsContentTreeOwner::GetInterface(const nsIID& aIID, void** aSink) +{ + NS_ENSURE_ARG_POINTER(aSink); + *aSink = 0; + + if(aIID.Equals(NS_GET_IID(nsIPrompt))) { + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetInterface(aIID, aSink); + } + if(aIID.Equals(NS_GET_IID(nsIAuthPrompt))) { + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetInterface(aIID, aSink); + } + if (aIID.Equals(NS_GET_IID(nsIDocShellTreeItem))) { + NS_ENSURE_STATE(mXULWindow); + nsCOMPtr<nsIDocShell> shell; + mXULWindow->GetDocShell(getter_AddRefs(shell)); + if (shell) + return shell->QueryInterface(aIID, aSink); + return NS_ERROR_FAILURE; + } + + if (aIID.Equals(NS_GET_IID(nsIDOMWindow)) || + aIID.Equals(NS_GET_IID(nsPIDOMWindowOuter))) { + NS_ENSURE_STATE(mXULWindow); + nsCOMPtr<nsIDocShellTreeItem> shell; + mXULWindow->GetPrimaryContentShell(getter_AddRefs(shell)); + if (shell) { + nsCOMPtr<nsIInterfaceRequestor> thing(do_QueryInterface(shell)); + if (thing) + return thing->GetInterface(aIID, aSink); + } + return NS_ERROR_FAILURE; + } + + if (aIID.Equals(NS_GET_IID(nsIXULWindow))) { + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->QueryInterface(aIID, aSink); + } + + return QueryInterface(aIID, aSink); +} + +//***************************************************************************** +// nsContentTreeOwner::nsIDocShellTreeOwner +//***************************************************************************** + +NS_IMETHODIMP +nsContentTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell, + bool aPrimary, bool aTargetable, + const nsAString& aID) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->ContentShellAdded(aContentShell, aPrimary, aTargetable, + aID); +} + +NS_IMETHODIMP +nsContentTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->ContentShellRemoved(aContentShell); +} + +NS_IMETHODIMP +nsContentTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetPrimaryContentShell(aShell); +} + +NS_IMETHODIMP +nsContentTreeOwner::TabParentAdded(nsITabParent* aTab, bool aPrimary) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->TabParentAdded(aTab, aPrimary); +} + +NS_IMETHODIMP +nsContentTreeOwner::TabParentRemoved(nsITabParent* aTab) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->TabParentRemoved(aTab); +} + +NS_IMETHODIMP +nsContentTreeOwner::GetPrimaryTabParent(nsITabParent** aTab) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetPrimaryTabParent(aTab); +} + +NS_IMETHODIMP +nsContentTreeOwner::GetPrimaryContentSize(int32_t* aWidth, + int32_t* aHeight) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetPrimaryContentSize(aWidth, aHeight); +} + +NS_IMETHODIMP +nsContentTreeOwner::SetPrimaryContentSize(int32_t aWidth, + int32_t aHeight) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetPrimaryContentSize(aWidth, aHeight); +} + +NS_IMETHODIMP +nsContentTreeOwner::GetRootShellSize(int32_t* aWidth, + int32_t* aHeight) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetRootShellSize(aWidth, aHeight); +} + +NS_IMETHODIMP +nsContentTreeOwner::SetRootShellSize(int32_t aWidth, + int32_t aHeight) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetRootShellSize(aWidth, aHeight); +} + +NS_IMETHODIMP nsContentTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem, + int32_t aCX, int32_t aCY) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SizeShellTo(aShellItem, aCX, aCY); +} + +NS_IMETHODIMP +nsContentTreeOwner::SetPersistence(bool aPersistPosition, + bool aPersistSize, + bool aPersistSizeMode) +{ + NS_ENSURE_STATE(mXULWindow); + nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement(); + if (!docShellElement) + return NS_ERROR_FAILURE; + + nsAutoString persistString; + docShellElement->GetAttribute(NS_LITERAL_STRING("persist"), persistString); + + bool saveString = false; + int32_t index; + + // Set X + index = persistString.Find("screenX"); + if (!aPersistPosition && index >= 0) { + persistString.Cut(index, 7); + saveString = true; + } else if (aPersistPosition && index < 0) { + persistString.AppendLiteral(" screenX"); + saveString = true; + } + // Set Y + index = persistString.Find("screenY"); + if (!aPersistPosition && index >= 0) { + persistString.Cut(index, 7); + saveString = true; + } else if (aPersistPosition && index < 0) { + persistString.AppendLiteral(" screenY"); + saveString = true; + } + // Set CX + index = persistString.Find("width"); + if (!aPersistSize && index >= 0) { + persistString.Cut(index, 5); + saveString = true; + } else if (aPersistSize && index < 0) { + persistString.AppendLiteral(" width"); + saveString = true; + } + // Set CY + index = persistString.Find("height"); + if (!aPersistSize && index >= 0) { + persistString.Cut(index, 6); + saveString = true; + } else if (aPersistSize && index < 0) { + persistString.AppendLiteral(" height"); + saveString = true; + } + // Set SizeMode + index = persistString.Find("sizemode"); + if (!aPersistSizeMode && (index >= 0)) { + persistString.Cut(index, 8); + saveString = true; + } else if (aPersistSizeMode && (index < 0)) { + persistString.AppendLiteral(" sizemode"); + saveString = true; + } + + ErrorResult rv; + if(saveString) { + docShellElement->SetAttribute(NS_LITERAL_STRING("persist"), persistString, rv); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsContentTreeOwner::GetPersistence(bool* aPersistPosition, + bool* aPersistSize, + bool* aPersistSizeMode) +{ + NS_ENSURE_STATE(mXULWindow); + nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement(); + if (!docShellElement) + return NS_ERROR_FAILURE; + + nsAutoString persistString; + docShellElement->GetAttribute(NS_LITERAL_STRING("persist"), persistString); + + // data structure doesn't quite match the question, but it's close enough + // for what we want (since this method is never actually called...) + if (aPersistPosition) + *aPersistPosition = persistString.Find("screenX") >= 0 || persistString.Find("screenY") >= 0 ? true : false; + if (aPersistSize) + *aPersistSize = persistString.Find("width") >= 0 || persistString.Find("height") >= 0 ? true : false; + if (aPersistSizeMode) + *aPersistSizeMode = persistString.Find("sizemode") >= 0 ? true : false; + + return NS_OK; +} + +NS_IMETHODIMP +nsContentTreeOwner::GetTargetableShellCount(uint32_t* aResult) +{ + NS_ENSURE_STATE(mXULWindow); + *aResult = mXULWindow->mTargetableShells.Count(); + return NS_OK; +} + +NS_IMETHODIMP +nsContentTreeOwner::GetHasPrimaryContent(bool* aResult) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetHasPrimaryContent(aResult); +} + +//***************************************************************************** +// nsContentTreeOwner::nsIWebBrowserChrome3 +//***************************************************************************** + +NS_IMETHODIMP nsContentTreeOwner::OnBeforeLinkTraversal(const nsAString &originalTarget, + nsIURI *linkURI, + nsIDOMNode *linkNode, + bool isAppTab, + nsAString &_retval) +{ + NS_ENSURE_STATE(mXULWindow); + + nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow; + mXULWindow->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow)); + + if (xulBrowserWindow) + return xulBrowserWindow->OnBeforeLinkTraversal(originalTarget, linkURI, + linkNode, isAppTab, _retval); + + _retval = originalTarget; + return NS_OK; +} + +NS_IMETHODIMP nsContentTreeOwner::ShouldLoadURI(nsIDocShell *aDocShell, + nsIURI *aURI, + nsIURI *aReferrer, + bool *_retval) +{ + NS_ENSURE_STATE(mXULWindow); + + nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow; + mXULWindow->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow)); + + if (xulBrowserWindow) + return xulBrowserWindow->ShouldLoadURI(aDocShell, aURI, aReferrer, _retval); + + *_retval = true; + return NS_OK; +} + +NS_IMETHODIMP nsContentTreeOwner::ReloadInFreshProcess(nsIDocShell* aDocShell, + nsIURI* aURI, + nsIURI* aReferrer, + bool* aRetVal) +{ + NS_WARNING("Cannot reload in fresh process from a nsContentTreeOwner!"); + *aRetVal = false; + return NS_OK; +} + +//***************************************************************************** +// nsContentTreeOwner::nsIWebBrowserChrome2 +//***************************************************************************** + +NS_IMETHODIMP nsContentTreeOwner::SetStatusWithContext(uint32_t aStatusType, + const nsAString &aStatusText, + nsISupports *aStatusContext) +{ + // We only allow the status to be set from the primary content shell + if (!mPrimary && aStatusType != STATUS_LINK) + return NS_OK; + + NS_ENSURE_STATE(mXULWindow); + + nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow; + mXULWindow->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow)); + + if (xulBrowserWindow) + { + switch(aStatusType) + { + case STATUS_SCRIPT: + xulBrowserWindow->SetJSStatus(aStatusText); + break; + case STATUS_LINK: + { + nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aStatusContext); + xulBrowserWindow->SetOverLink(aStatusText, element); + break; + } + } + } + + return NS_OK; +} + +//***************************************************************************** +// nsContentTreeOwner::nsIWebBrowserChrome +//***************************************************************************** + +NS_IMETHODIMP nsContentTreeOwner::SetStatus(uint32_t aStatusType, + const char16_t* aStatus) +{ + return SetStatusWithContext(aStatusType, + aStatus ? static_cast<const nsString &>(nsDependentString(aStatus)) + : EmptyString(), + nullptr); +} + +NS_IMETHODIMP nsContentTreeOwner::SetWebBrowser(nsIWebBrowser* aWebBrowser) +{ + NS_ERROR("Haven't Implemented this yet"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsContentTreeOwner::GetWebBrowser(nsIWebBrowser** aWebBrowser) +{ + // Unimplemented, and probably will remain so; xpfe windows have docshells, + // not webbrowsers. + NS_ENSURE_ARG_POINTER(aWebBrowser); + *aWebBrowser = 0; + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsContentTreeOwner::SetChromeFlags(uint32_t aChromeFlags) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetChromeFlags(aChromeFlags); +} + +NS_IMETHODIMP nsContentTreeOwner::GetChromeFlags(uint32_t* aChromeFlags) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetChromeFlags(aChromeFlags); +} + +NS_IMETHODIMP nsContentTreeOwner::DestroyBrowserWindow() +{ + NS_ERROR("Haven't Implemented this yet"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsContentTreeOwner::SizeBrowserTo(int32_t aCX, int32_t aCY) +{ + NS_ERROR("Haven't Implemented this yet"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsContentTreeOwner::ShowAsModal() +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->ShowModal(); +} + +NS_IMETHODIMP nsContentTreeOwner::IsWindowModal(bool *_retval) +{ + NS_ENSURE_STATE(mXULWindow); + *_retval = mXULWindow->mContinueModalLoop; + return NS_OK; +} + +NS_IMETHODIMP nsContentTreeOwner::ExitModalEventLoop(nsresult aStatus) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->ExitModalLoop(aStatus); +} + +//***************************************************************************** +// nsContentTreeOwner::nsIBaseWindow +//***************************************************************************** + +NS_IMETHODIMP nsContentTreeOwner::InitWindow(nativeWindow aParentNativeWindow, + nsIWidget* parentWidget, int32_t x, int32_t y, int32_t cx, int32_t cy) +{ + // Ignore wigdet parents for now. Don't think those are a vaild thing to call. + NS_ENSURE_SUCCESS(SetPositionAndSize(x, y, cx, cy, 0), NS_ERROR_FAILURE); + + return NS_OK; +} + +NS_IMETHODIMP nsContentTreeOwner::Create() +{ + NS_ASSERTION(false, "You can't call this"); + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP nsContentTreeOwner::Destroy() +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->Destroy(); +} + +NS_IMETHODIMP nsContentTreeOwner::GetUnscaledDevicePixelsPerCSSPixel(double* aScale) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetUnscaledDevicePixelsPerCSSPixel(aScale); +} + +NS_IMETHODIMP nsContentTreeOwner::GetDevicePixelsPerDesktopPixel(double* aScale) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetDevicePixelsPerDesktopPixel(aScale); +} + +NS_IMETHODIMP nsContentTreeOwner::SetPositionDesktopPix(int32_t aX, int32_t aY) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetPositionDesktopPix(aX, aY); +} + +NS_IMETHODIMP nsContentTreeOwner::SetPosition(int32_t aX, int32_t aY) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetPosition(aX, aY); +} + +NS_IMETHODIMP nsContentTreeOwner::GetPosition(int32_t* aX, int32_t* aY) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetPosition(aX, aY); +} + +NS_IMETHODIMP nsContentTreeOwner::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetSize(aCX, aCY, aRepaint); +} + +NS_IMETHODIMP nsContentTreeOwner::GetSize(int32_t* aCX, int32_t* aCY) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetSize(aCX, aCY); +} + +NS_IMETHODIMP nsContentTreeOwner::SetPositionAndSize(int32_t aX, int32_t aY, + int32_t aCX, int32_t aCY, uint32_t aFlags) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetPositionAndSize(aX, aY, aCX, aCY, aFlags); +} + +NS_IMETHODIMP nsContentTreeOwner::GetPositionAndSize(int32_t* aX, int32_t* aY, + int32_t* aCX, int32_t* aCY) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetPositionAndSize(aX, aY, aCX, aCY); +} + +NS_IMETHODIMP nsContentTreeOwner::Repaint(bool aForce) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->Repaint(aForce); +} + +NS_IMETHODIMP nsContentTreeOwner::GetParentWidget(nsIWidget** aParentWidget) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetParentWidget(aParentWidget); +} + +NS_IMETHODIMP nsContentTreeOwner::SetParentWidget(nsIWidget* aParentWidget) +{ + NS_ASSERTION(false, "You can't call this"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsContentTreeOwner::GetParentNativeWindow(nativeWindow* aParentNativeWindow) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetParentNativeWindow(aParentNativeWindow); +} + +NS_IMETHODIMP nsContentTreeOwner::SetParentNativeWindow(nativeWindow aParentNativeWindow) +{ + NS_ASSERTION(false, "You can't call this"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsContentTreeOwner::GetNativeHandle(nsAString& aNativeHandle) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetNativeHandle(aNativeHandle); +} + +NS_IMETHODIMP nsContentTreeOwner::GetVisibility(bool* aVisibility) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetVisibility(aVisibility); +} + +NS_IMETHODIMP nsContentTreeOwner::SetVisibility(bool aVisibility) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetVisibility(aVisibility); +} + +NS_IMETHODIMP nsContentTreeOwner::GetEnabled(bool *aEnabled) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->GetEnabled(aEnabled); +} + +NS_IMETHODIMP nsContentTreeOwner::SetEnabled(bool aEnable) +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetEnabled(aEnable); +} + +NS_IMETHODIMP nsContentTreeOwner::GetMainWidget(nsIWidget** aMainWidget) +{ + NS_ENSURE_ARG_POINTER(aMainWidget); + NS_ENSURE_STATE(mXULWindow); + + *aMainWidget = mXULWindow->mWindow; + NS_IF_ADDREF(*aMainWidget); + + return NS_OK; +} + +NS_IMETHODIMP nsContentTreeOwner::SetFocus() +{ + NS_ENSURE_STATE(mXULWindow); + return mXULWindow->SetFocus(); +} + +NS_IMETHODIMP nsContentTreeOwner::GetTitle(char16_t** aTitle) +{ + NS_ENSURE_ARG_POINTER(aTitle); + NS_ENSURE_STATE(mXULWindow); + + return mXULWindow->GetTitle(aTitle); +} + +NS_IMETHODIMP nsContentTreeOwner::SetTitle(const char16_t* aTitle) +{ + // We only allow the title to be set from the primary content shell + if(!mPrimary || !mContentTitleSetting) + return NS_OK; + + NS_ENSURE_STATE(mXULWindow); + + nsAutoString title; + nsAutoString docTitle(aTitle); + + if (docTitle.IsEmpty()) + docTitle.Assign(mTitleDefault); + + if (!docTitle.IsEmpty()) { + if (!mTitlePreface.IsEmpty()) { + // Title will be: "Preface: Doc Title - Mozilla" + title.Assign(mTitlePreface); + title.Append(docTitle); + } + else { + // Title will be: "Doc Title - Mozilla" + title = docTitle; + } + + if (!mWindowTitleModifier.IsEmpty()) + title += mTitleSeparator + mWindowTitleModifier; + } + else + title.Assign(mWindowTitleModifier); // Title will just be plain "Mozilla" + + // + // if there is no location bar we modify the title to display at least + // the scheme and host (if any) as an anti-spoofing measure. + // + nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement(); + + if (docShellElement) { + nsAutoString chromeString; + docShellElement->GetAttribute(NS_LITERAL_STRING("chromehidden"), chromeString); + if (chromeString.Find(NS_LITERAL_STRING("location")) != kNotFound) { + // + // location bar is turned off, find the browser location + // + // use the document's nsPrincipal to find the true owner + // in case of javascript: or data: documents + // + nsCOMPtr<nsIDocShellTreeItem> dsitem; + GetPrimaryContentShell(getter_AddRefs(dsitem)); + nsCOMPtr<nsIScriptObjectPrincipal> doc = + do_QueryInterface(dsitem ? dsitem->GetDocument() : nullptr); + if (doc) { + nsCOMPtr<nsIURI> uri; + nsIPrincipal* principal = doc->GetPrincipal(); + if (principal) { + principal->GetURI(getter_AddRefs(uri)); + if (uri) { + // + // remove any user:pass information + // + nsCOMPtr<nsIURIFixup> fixup(do_GetService(NS_URIFIXUP_CONTRACTID)); + if (fixup) { + nsCOMPtr<nsIURI> tmpuri; + nsresult rv = fixup->CreateExposableURI(uri,getter_AddRefs(tmpuri)); + if (NS_SUCCEEDED(rv) && tmpuri) { + // (don't bother if there's no host) + nsAutoCString host; + nsAutoCString prepath; + tmpuri->GetHost(host); + tmpuri->GetPrePath(prepath); + if (!host.IsEmpty()) { + // + // We have a scheme/host, update the title + // + title.Insert(NS_ConvertUTF8toUTF16(prepath) + + mTitleSeparator, 0); + } + } + } + } + } + } + } + nsIDocument* document = docShellElement->OwnerDoc(); + ErrorResult rv; + document->SetTitle(title, rv); + return rv.StealNSResult(); + } + + return mXULWindow->SetTitle(title.get()); +} + +//***************************************************************************** +// nsContentTreeOwner: nsIWindowProvider +//***************************************************************************** +NS_IMETHODIMP +nsContentTreeOwner::ProvideWindow(mozIDOMWindowProxy* aParent, + uint32_t aChromeFlags, + bool aCalledFromJS, + bool aPositionSpecified, + bool aSizeSpecified, + nsIURI* aURI, + const nsAString& aName, + const nsACString& aFeatures, + bool aForceNoOpener, + bool* aWindowIsNew, + mozIDOMWindowProxy** aReturn) +{ + NS_ENSURE_ARG_POINTER(aParent); + + auto* parent = nsPIDOMWindowOuter::From(aParent); + + *aReturn = nullptr; + + if (!mXULWindow) { + // Nothing to do here + return NS_OK; + } + +#ifdef DEBUG + nsCOMPtr<nsIWebNavigation> parentNav = do_GetInterface(aParent); + nsCOMPtr<nsIDocShellTreeOwner> parentOwner = do_GetInterface(parentNav); + NS_ASSERTION(SameCOMIdentity(parentOwner, + static_cast<nsIDocShellTreeOwner*>(this)), + "Parent from wrong docshell tree?"); +#endif + + // If aParent is inside an <iframe mozbrowser> and this isn't a request to + // open a modal-type window, we're going to create a new <iframe mozbrowser> + // and return its window here. + nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent); + if (docshell && docshell->GetIsInMozBrowserOrApp() && + !(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL | + nsIWebBrowserChrome::CHROME_OPENAS_DIALOG | + nsIWebBrowserChrome::CHROME_OPENAS_CHROME))) { + + BrowserElementParent::OpenWindowResult opened = + BrowserElementParent::OpenWindowInProcess(parent, aURI, aName, + aFeatures, aForceNoOpener, aReturn); + + // If OpenWindowInProcess handled the open (by opening it or blocking the + // popup), tell our caller not to proceed trying to create a new window + // through other means. + if (opened != BrowserElementParent::OPEN_WINDOW_IGNORED) { + *aWindowIsNew = opened == BrowserElementParent::OPEN_WINDOW_ADDED; + return *aWindowIsNew ? NS_OK : NS_ERROR_ABORT; + } + + // If we're in an app and the target is _blank, send the url to the OS + if (aName.LowerCaseEqualsLiteral("_blank")) { + nsCOMPtr<nsIExternalURLHandlerService> exUrlServ( + do_GetService(NS_EXTERNALURLHANDLERSERVICE_CONTRACTID)); + if (exUrlServ) { + + nsCOMPtr<nsIHandlerInfo> info; + bool found; + exUrlServ->GetURLHandlerInfoFromOS(aURI, &found, getter_AddRefs(info)); + + if (info && found) { + info->LaunchWithURI(aURI, nullptr); + return NS_ERROR_ABORT; + } + + } + } + } + + int32_t openLocation = + nsWindowWatcher::GetWindowOpenLocation(parent, aChromeFlags, aCalledFromJS, + aPositionSpecified, aSizeSpecified); + + if (openLocation != nsIBrowserDOMWindow::OPEN_NEWTAB && + openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) { + // Just open a window normally + return NS_OK; + } + + nsCOMPtr<mozIDOMWindowProxy> domWin; + mXULWindow->GetWindowDOMWindow(getter_AddRefs(domWin)); + nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(domWin); + if (!chromeWin) { + // Really odd... but whatever + NS_WARNING("nsXULWindow's DOMWindow is not a chrome window"); + return NS_OK; + } + + nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin; + chromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin)); + if (!browserDOMWin) { + return NS_OK; + } + + *aWindowIsNew = (openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW); + + { + dom::AutoNoJSAPI nojsapi; + + uint32_t flags = nsIBrowserDOMWindow::OPEN_NEW; + if (aForceNoOpener) { + flags |= nsIBrowserDOMWindow::OPEN_NO_OPENER; + } + + // Get a new rendering area from the browserDOMWin. We don't want + // to be starting any loads here, so get it with a null URI. + // + // This method handles setting the opener for us, so we don't need to set it + // ourselves. + return browserDOMWin->OpenURI(nullptr, aParent, + openLocation, + flags, aReturn); + } +} + +//***************************************************************************** +// nsContentTreeOwner: Accessors +//***************************************************************************** + +#if defined(XP_MACOSX) +class nsContentTitleSettingEvent : public Runnable +{ +public: + nsContentTitleSettingEvent(dom::Element* dse, const nsAString& wtm) + : mElement(dse), + mTitleDefault(wtm) {} + + NS_IMETHOD Run() override + { + ErrorResult rv; + mElement->SetAttribute(NS_LITERAL_STRING("titledefault"), mTitleDefault, rv); + mElement->RemoveAttribute(NS_LITERAL_STRING("titlemodifier"), rv); + return NS_OK; + } + +private: + nsCOMPtr<dom::Element> mElement; + nsString mTitleDefault; +}; +#endif + +void nsContentTreeOwner::XULWindow(nsXULWindow* aXULWindow) +{ + mXULWindow = aXULWindow; + if (mXULWindow && mPrimary) { + // Get the window title modifiers + nsCOMPtr<dom::Element> docShellElement = mXULWindow->GetWindowDOMElement(); + + nsAutoString contentTitleSetting; + + if(docShellElement) + { + docShellElement->GetAttribute(NS_LITERAL_STRING("contenttitlesetting"), contentTitleSetting); + if(contentTitleSetting.EqualsLiteral("true")) + { + mContentTitleSetting = true; + docShellElement->GetAttribute(NS_LITERAL_STRING("titledefault"), mTitleDefault); + docShellElement->GetAttribute(NS_LITERAL_STRING("titlemodifier"), mWindowTitleModifier); + docShellElement->GetAttribute(NS_LITERAL_STRING("titlepreface"), mTitlePreface); + +#if defined(XP_MACOSX) + // On OS X, treat the titlemodifier like it's the titledefault, and don't ever append + // the separator + appname. + if (mTitleDefault.IsEmpty()) { + NS_DispatchToCurrentThread( + new nsContentTitleSettingEvent(docShellElement, + mWindowTitleModifier)); + mTitleDefault = mWindowTitleModifier; + mWindowTitleModifier.Truncate(); + } +#endif + docShellElement->GetAttribute(NS_LITERAL_STRING("titlemenuseparator"), mTitleSeparator); + } + } + else + { + NS_ERROR("This condition should never happen. If it does, " + "we just won't get a modifier, but it still shouldn't happen."); + } + } +} + +nsXULWindow* nsContentTreeOwner::XULWindow() +{ + return mXULWindow; +} + +//***************************************************************************** +//*** nsSiteWindow implementation +//***************************************************************************** + +nsSiteWindow::nsSiteWindow(nsContentTreeOwner *aAggregator) +{ + mAggregator = aAggregator; +} + +nsSiteWindow::~nsSiteWindow() +{ +} + +NS_IMPL_ADDREF_USING_AGGREGATOR(nsSiteWindow, mAggregator) +NS_IMPL_RELEASE_USING_AGGREGATOR(nsSiteWindow, mAggregator) + +NS_INTERFACE_MAP_BEGIN(nsSiteWindow) + NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow) +NS_INTERFACE_MAP_END_AGGREGATED(mAggregator) + +NS_IMETHODIMP +nsSiteWindow::SetDimensions(uint32_t aFlags, + int32_t aX, int32_t aY, int32_t aCX, int32_t aCY) +{ + // XXX we're ignoring aFlags + return mAggregator->SetPositionAndSize(aX, aY, aCX, aCY, + nsIBaseWindow::eRepaint); +} + +NS_IMETHODIMP +nsSiteWindow::GetDimensions(uint32_t aFlags, + int32_t *aX, int32_t *aY, int32_t *aCX, int32_t *aCY) +{ + // XXX we're ignoring aFlags + return mAggregator->GetPositionAndSize(aX, aY, aCX, aCY); +} + +NS_IMETHODIMP +nsSiteWindow::SetFocus(void) +{ +#if 0 + /* This implementation focuses the main document and could make sense. + However this method is actually being used from within + nsGlobalWindow::Focus (providing a hook for MDI embedding apps) + and it's better for our purposes to not pick a document and + focus it, but allow nsGlobalWindow to carry on unhindered. + */ + nsXULWindow *window = mAggregator->XULWindow(); + if (window) { + nsCOMPtr<nsIDocShell> docshell; + window->GetDocShell(getter_AddRefs(docshell)); + if (docShell) { + nsCOMPtr<nsPIDOMWindowOuter> domWindow(docShell->GetWindow()); + if (domWindow) + domWindow->Focus(); + } + } +#endif + return NS_OK; +} + +/* this implementation focuses another window. if there isn't another + window to focus, we do nothing. */ +NS_IMETHODIMP +nsSiteWindow::Blur(void) +{ + NS_DEFINE_CID(kWindowMediatorCID, NS_WINDOWMEDIATOR_CID); + + nsCOMPtr<nsISimpleEnumerator> windowEnumerator; + nsCOMPtr<nsIXULWindow> xulWindow; + bool more, foundUs; + nsXULWindow *ourWindow = mAggregator->XULWindow(); + + { + nsCOMPtr<nsIWindowMediator> windowMediator(do_GetService(kWindowMediatorCID)); + if (windowMediator) + windowMediator->GetZOrderXULWindowEnumerator(0, true, + getter_AddRefs(windowEnumerator)); + } + + if (!windowEnumerator) + return NS_ERROR_FAILURE; + + // step through the top-level windows + foundUs = false; + windowEnumerator->HasMoreElements(&more); + while (more) { + + nsCOMPtr<nsISupports> nextWindow; + nsCOMPtr<nsIXULWindow> nextXULWindow; + + windowEnumerator->GetNext(getter_AddRefs(nextWindow)); + nextXULWindow = do_QueryInterface(nextWindow); + + // got it!(?) + if (foundUs) { + xulWindow = nextXULWindow; + break; + } + + // remember the very first one, in case we have to wrap + if (!xulWindow) + xulWindow = nextXULWindow; + + // look for us + if (nextXULWindow == ourWindow) + foundUs = true; + + windowEnumerator->HasMoreElements(&more); + } + + // change focus to the window we just found + if (xulWindow) { + nsCOMPtr<nsIDocShell> docshell; + xulWindow->GetDocShell(getter_AddRefs(docshell)); + if (!docshell) { + return NS_OK; + } + + nsCOMPtr<nsPIDOMWindowOuter> domWindow = docshell->GetWindow(); + if (domWindow) + domWindow->Focus(); + } + return NS_OK; +} + +NS_IMETHODIMP +nsSiteWindow::GetVisibility(bool *aVisibility) +{ + return mAggregator->GetVisibility(aVisibility); +} + +NS_IMETHODIMP +nsSiteWindow::SetVisibility(bool aVisibility) +{ + return mAggregator->SetVisibility(aVisibility); +} + +NS_IMETHODIMP +nsSiteWindow::GetTitle(char16_t * *aTitle) +{ + return mAggregator->GetTitle(aTitle); +} + +NS_IMETHODIMP +nsSiteWindow::SetTitle(const char16_t * aTitle) +{ + return mAggregator->SetTitle(aTitle); +} + +NS_IMETHODIMP +nsSiteWindow::GetSiteWindow(void **aSiteWindow) +{ + return mAggregator->GetParentNativeWindow(aSiteWindow); +} + diff --git a/xpfe/appshell/nsContentTreeOwner.h b/xpfe/appshell/nsContentTreeOwner.h new file mode 100644 index 000000000..d6a0d42b3 --- /dev/null +++ b/xpfe/appshell/nsContentTreeOwner.h @@ -0,0 +1,63 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * 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/. */ + +#ifndef nsContentTreeOwner_h__ +#define nsContentTreeOwner_h__ + +// Helper Classes +#include "nsCOMPtr.h" +#include "nsString.h" + +// Interfaces Needed +#include "nsIBaseWindow.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIWebBrowserChrome3.h" +#include "nsIWindowProvider.h" + +class nsXULWindow; +class nsSiteWindow; + +class nsContentTreeOwner final : public nsIDocShellTreeOwner, + public nsIBaseWindow, + public nsIInterfaceRequestor, + public nsIWebBrowserChrome3, + public nsIWindowProvider +{ +friend class nsXULWindow; +friend class nsSiteWindow; + +public: + NS_DECL_ISUPPORTS + + NS_DECL_NSIBASEWINDOW + NS_DECL_NSIDOCSHELLTREEOWNER + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIWEBBROWSERCHROME + NS_DECL_NSIWEBBROWSERCHROME2 + NS_DECL_NSIWEBBROWSERCHROME3 + NS_DECL_NSIWINDOWPROVIDER + +protected: + explicit nsContentTreeOwner(bool fPrimary); + virtual ~nsContentTreeOwner(); + + void XULWindow(nsXULWindow* aXULWindow); + nsXULWindow* XULWindow(); + +protected: + nsXULWindow *mXULWindow; + nsSiteWindow *mSiteWindow; + bool mPrimary; + bool mContentTitleSetting; + nsString mWindowTitleModifier; + nsString mTitleSeparator; + nsString mTitlePreface; + nsString mTitleDefault; +}; + +#endif /* nsContentTreeOwner_h__ */ diff --git a/xpfe/appshell/nsIAppShellService.idl b/xpfe/appshell/nsIAppShellService.idl new file mode 100644 index 000000000..e7d96a5d1 --- /dev/null +++ b/xpfe/appshell/nsIAppShellService.idl @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIXULWindow; +interface nsIWindowlessBrowser; +interface nsIURI; +interface mozIDOMWindowProxy; +interface nsIAppShell; +interface nsITabParent; + +[ptr] native JSContext(JSContext); + +%{C++ +#include "js/TypeDecls.h" +%} + +[scriptable, uuid(19266025-354c-4bb9-986b-3483b2b1cdef)] +interface nsIAppShellService : nsISupports +{ + /** + * Create a window, which will be initially invisible. + * @param aParent the parent window. Can be null. + * @param aUrl the contents of the new window. + * @param aChromeMask chrome flags affecting the kind of OS border + * given to the window. see nsIBrowserWindow for + * bit/flag definitions. + * @param aCallbacks interface providing C++ hooks for window initialization + * before the window is made visible. Can be null. + * Deprecated. + * @param aInitialWidth width, in pixels, of the window. Width of window + * at creation. Can be overridden by the "width" + * tag in the XUL. Set to NS_SIZETOCONTENT to force + * the window to wrap to its contents. + * @param aInitialHeight like aInitialWidth, but subtly different. + * @param aOpeningTab The TabParent that requested that this window be opened. + * Can be left null. + * @param aOpenerWindow The Window Proxy which requested that this window be opened. + * Can be left null. + */ + const long SIZE_TO_CONTENT = -1; + nsIXULWindow createTopLevelWindow(in nsIXULWindow aParent, + in nsIURI aUrl, + in uint32_t aChromeMask, + in long aInitialWidth, + in long aInitialHeight, + in nsITabParent aOpeningTab, + in mozIDOMWindowProxy aOpenerWindow); + + /** + * This is the constructor for creating an invisible DocShell. + * It is used to simulate DOM windows without an actual physical + * representation. + * @param aIsChrome Set true if you want to use it for chrome content. + */ + nsIWindowlessBrowser createWindowlessBrowser([optional] in bool aIsChrome); + + [noscript] + void createHiddenWindow(); + + void destroyHiddenWindow(); + + /** + * B2G multi-screen support. When open another top-level window on b2g, + * a screen ID is needed for identifying which screen this window is + * opened to. + * @param aScreenId Differentiate screens of windows. It is platform- + * specific due to the hardware limitation for now. + */ + [noscript] + void setScreenId(in uint32_t aScreenId); + + /** + * Return the (singleton) application hidden window, automatically created + * and maintained by this AppShellService. + * @param aResult the hidden window. Do not unhide hidden window. + * Do not taunt hidden window. + */ + readonly attribute nsIXULWindow hiddenWindow; + + /** + * Return the (singleton) application hidden window, automatically created + * and maintained by this AppShellService. + * @param aResult the hidden window. Do not unhide hidden window. + * Do not taunt hidden window. + */ + readonly attribute mozIDOMWindowProxy hiddenDOMWindow; + + /** + * Return the (singleton) application hidden private window, automatically + * created and maintained by this AppShellService. This window is created + * in private browsing mode. + * @param aResult the hidden private window. Do not unhide hidden window. + * Do not taunt hidden window. + */ + readonly attribute nsIXULWindow hiddenPrivateWindow; + + /** + * Return the (singleton) application hidden private window, automatically + * created and maintained by this AppShellService. This window is created + * in private browsing mode. + * @param aResult the hidden private window. Do not unhide hidden window. + * Do not taunt hidden window. + */ + readonly attribute mozIDOMWindowProxy hiddenPrivateDOMWindow; + + /** + * Return true if the application hidden window was provided by the + * application. If it wasn't, the default hidden window was used. This will + * usually be false on all non-mac platforms. + */ + readonly attribute boolean applicationProvidedHiddenWindow; + + /** + * Add a window to the application's registry of windows. These windows + * are generally shown in the Windows taskbar, and the application + * knows it can't quit until it's out of registered windows. + * @param aWindow the window to register + * @note When this method is successful, it fires the global notification + * "xul-window-registered" + */ + void registerTopLevelWindow(in nsIXULWindow aWindow); + + /** + * Remove a window from the application's window registry. Note that + * this method won't automatically attempt to quit the app when + * the last window is unregistered. For that, see Quit(). + * @param aWindow you see the pattern + */ + void unregisterTopLevelWindow(in nsIXULWindow aWindow); + + /** + * Whether the hidden private window has been lazily created. + */ + [noscript] + readonly attribute boolean hasHiddenPrivateWindow; + + /** + * Start/stop tracking lags in the event loop. + * If the event loop gets unresponsive, a "event-loop-lag" notification + * is sent. Note that calling `startEventLoopLagTracking` when tracking + * is already enabled has no effect. + * @return true if tracking succeeded. + */ + bool startEventLoopLagTracking(); + void stopEventLoopLagTracking(); +}; diff --git a/xpfe/appshell/nsIPopupWindowManager.idl b/xpfe/appshell/nsIPopupWindowManager.idl new file mode 100644 index 000000000..4e6cb99b3 --- /dev/null +++ b/xpfe/appshell/nsIPopupWindowManager.idl @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +/** + * This is the interface to the Popup Window Manager: an object which + * maintains popup window permissions by website. + */ + +#include "nsISupports.idl" + +interface nsIPrincipal; + +[scriptable, uuid(66386aa9-2088-4bae-82c7-9f58bc02be64)] +interface nsIPopupWindowManager : nsISupports { + + /** + * These values are returned by the testPermission method + */ + const uint32_t ALLOW_POPUP = 1; + const uint32_t DENY_POPUP = 2; + const uint32_t ALLOW_POPUP_WITH_PREJUDICE = 3; + + /** + * Test whether a website has permission to show a popup window. + * @param principal is the principal to be tested + * @return one of the enumerated permission actions defined above + */ + uint32_t testPermission(in nsIPrincipal principal); +}; + +%{ C++ +#define NS_POPUPWINDOWMANAGER_CONTRACTID "@mozilla.org/PopupWindowManager;1" +%} diff --git a/xpfe/appshell/nsIWindowMediator.idl b/xpfe/appshell/nsIWindowMediator.idl new file mode 100644 index 000000000..b38297594 --- /dev/null +++ b/xpfe/appshell/nsIWindowMediator.idl @@ -0,0 +1,206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +#include "nsISimpleEnumerator.idl" + +%{C++ +#define NS_WINDOWMEDIATOR_CID \ +{ 0x79a2b7cc, 0xf05b, 0x4605, \ + { 0xbf, 0xa0, 0xfa, 0xc5, 0x4f, 0x27, 0xee, 0xc8 } } + +#define NS_WINDOWMEDIATOR_CONTRACTID \ + "@mozilla.org/appshell/window-mediator;1" +%} + +interface mozIDOMWindow; +interface mozIDOMWindowProxy; +interface nsIXULWindow; +interface nsIWidget; +interface nsIWindowMediatorListener; + +[scriptable, uuid(df0da056-357d-427f-bafd-e6cbf19c9381)] +interface nsIWindowMediator: nsISupports +{ + /** Return an enumerator which iterates over all windows of type aWindowType + * from the oldest window to the youngest. + * @param aWindowType the returned enumerator will enumerate only + * windows of this type. ("type" is the + * |windowtype| attribute of the XML <window> element.) + * If null, all windows will be enumerated. + * @return an enumerator of nsIDOMWindows. Note that windows close + * asynchronously in many cases, so windows returned from this + * enumerator can have .closed set to true. Caveat enumerator! + */ + nsISimpleEnumerator getEnumerator(in wstring aWindowType); + + /** Identical to getEnumerator except: + * @return an enumerator of nsIXULWindows + */ + nsISimpleEnumerator getXULWindowEnumerator(in wstring aWindowType); + + /** Return an enumerator which iterates over all windows of type aWindowType + * in their z (front-to-back) order. Note this interface makes + * no requirement that a window couldn't be revisited if windows + * are re-ordered while z-order enumerators are active. + * @param aWindowType the returned enumerator will enumerate only + * windows of this type. ("type" is the + * |windowtype| attribute of the XML <window> element.) + * If null, all windows will be enumerated. + * @param aFrontToBack if true, the enumerator enumerates windows in order + * from front to back. back to front if false. + * @return an enumerator of nsIDOMWindows + */ + nsISimpleEnumerator getZOrderDOMWindowEnumerator(in wstring aWindowType, + in boolean aFrontToBack); + + /** Identical to getZOrderDOMWindowEnumerator except: + * @return an enumerator of nsIXULWindows + */ + nsISimpleEnumerator getZOrderXULWindowEnumerator(in wstring aWindowType, + in boolean aFrontToBack); + + /** This is a shortcut for simply fetching the first window in + * front to back order. + * @param aWindowType return the topmost window of this type. + * ("type" is the |windowtype| attribute of + * the XML <window> element.) + * If null, return the topmost window of any type. + * @return the topmost window + */ + mozIDOMWindowProxy getMostRecentWindow(in wstring aWindowType); + + /** + * Return the outer window with the given ID, if any. Can return null. + */ + mozIDOMWindowProxy getOuterWindowWithId(in unsigned long long aOuterWindowID); + + /** + * Return the inner window with the given current window ID, if any. + * Can return null if no inner window with the ID exists or if it's not + * a current inner anymore. + */ + mozIDOMWindow getCurrentInnerWindowWithId(in unsigned long long aInnerWindowID); + + /** Add the window to the list of known windows. Listeners (see + * addListener) will be notified through their onOpenWindow method. + * @param aWindow the window to add + */ + [noscript] void registerWindow(in nsIXULWindow aWindow); + + /** Remove the window from the list of known windows. Listeners (see + * addListener) will be be notified through their onCloseWindow method. + * @param aWindow the window to remove + */ + [noscript] void unregisterWindow(in nsIXULWindow aWindow); + + /** Call this method when a window gains focus. It's a primitive means of + * determining the most recent window. It's no longer necessary and it + * really should be removed. + * @param aWindow the window which has gained focus + */ + [noscript] void updateWindowTimeStamp(in nsIXULWindow aWindow); + + /** Call this method when a window's title changes. Listeners (see + * addListener) will be notified through their onWindowTitleChange method. + * @param aWindow the window whose title has changed + * @param inTitle the window's new title + */ + [noscript] void updateWindowTitle(in nsIXULWindow aWindow, + in wstring inTitle ); + + /* z-ordering: */ + + const unsigned long zLevelTop = 1; + const unsigned long zLevelBottom = 2; + const unsigned long zLevelBelow = 3; // below some window + + /** A window wants to be moved in z-order. Calculate whether and how + * it should be constrained. Note this method is advisory only: + * it changes nothing either in WindowMediator's internal state + * or with the window. + * Note it compares the nsIXULWindow to nsIWidgets. A pure interface + * would use all nsIXULWindows. But we expect this to be called from + * callbacks originating in native window code. They are expected to + * hand us comparison values which are pulled from general storage + * in the native widget, and may not correspond to an nsIWidget at all. + * For that reason this interface requires only objects one step + * removed from the native window (nsIWidgets), and its implementation + * must be very understanding of what may be completely invalid + * pointers in those parameters. + * + * @param inWindow the window in question + * @param inPosition requested position + * values: zLevelTop: topmost window. zLevelBottom: bottom. + * zLevelBelow: below ioBelow. (the value of ioBelow will + * be ignored for zLevelTop and Bottom.) + * @param inBelow if inPosition==zLevelBelow, the window + * below which inWindow wants to be placed. Otherwise this + * variable is ignored. + * @param outPosition constrained position, values like inPosition. + * @param outBelow if outPosition==zLevelBelow, the window + * below which inWindow should be placed. Otherwise this + * this value will be null. + * @return PR_TRUE if the position returned is different from + * the position given. + */ + + [noscript] boolean calculateZPosition(in nsIXULWindow inWindow, + in unsigned long inPosition, + in nsIWidget inBelow, + out unsigned long outPosition, + out nsIWidget outBelow); + + /** A window has been positioned behind another. Inform WindowMediator + * @param inWindow the window in question + * @param inPosition new position. values: + * zLevelTop: topmost window. + * zLevelBottom: bottom. + * zLevelBelow: below inBelow. (inBelow is ignored + * for other values of inPosition.) + * @param inBelow the window inWindow is behind, if zLevelBelow + */ + [noscript] void setZPosition(in nsIXULWindow inWindow, + in unsigned long inPosition, + in nsIXULWindow inBelow); + + /** Return the window's Z level (as defined in nsIXULWindow). + * @param aWindow the window in question + * @return aWindow's z level + */ + [noscript] uint32_t getZLevel(in nsIXULWindow aWindow); + + /** Set the window's Z level (as defined in nsIXULWindow). The implementation + * will reposition the window as necessary to match its new Z level. + * The implementation will assume a window's Z level to be + * nsIXULWindow::normalZ until it has been informed of a different level. + * @param aWindow the window in question + * @param aZLevel the window's new Z level + */ + [noscript] void setZLevel(in nsIXULWindow aWindow, in uint32_t aZLevel); + + /** Register a listener for window status changes. + * keeps strong ref? (to be decided) + * @param aListener the listener to register + */ + void addListener(in nsIWindowMediatorListener aListener); + + /** Unregister a listener of window status changes. + * @param aListener the listener to unregister + */ + void removeListener(in nsIWindowMediatorListener aListener); +}; + +// XXXcatalinb: This should be merged to nsIWindowMediator. Using this +// to avoid UUID change in aurora. +[scriptable, uuid(b9ed4063-39a2-4302-8e5c-7287eef021fe)] +interface nsIWindowMediator_44 : nsIWindowMediator +{ + /** + * Same as getMostRecentWindow, but ignores private browsing + * windows. + */ + mozIDOMWindowProxy getMostRecentNonPBWindow(in wstring aWindowType); +}; diff --git a/xpfe/appshell/nsIWindowMediatorListener.idl b/xpfe/appshell/nsIWindowMediatorListener.idl new file mode 100644 index 000000000..459e3b6ab --- /dev/null +++ b/xpfe/appshell/nsIWindowMediatorListener.idl @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +interface nsIXULWindow; + +[scriptable, uuid(2F276982-0D60-4377-A595-D350BA516395)] +interface nsIWindowMediatorListener : nsISupports +{ + void onWindowTitleChange(in nsIXULWindow window, + in wstring newTitle); + + void onOpenWindow(in nsIXULWindow window); + void onCloseWindow(in nsIXULWindow window); +}; + diff --git a/xpfe/appshell/nsIWindowlessBrowser.idl b/xpfe/appshell/nsIWindowlessBrowser.idl new file mode 100644 index 000000000..c959e47a4 --- /dev/null +++ b/xpfe/appshell/nsIWindowlessBrowser.idl @@ -0,0 +1,27 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIWebNavigation.idl" + +/** + * This interface represents a nsIWebBrowser instance with no associated OS + * window. Its main function is to manage the lifetimes of those windows. + * A strong reference to this object must be held until the window is + * ready to be destroyed. + */ +[scriptable, uuid(abb46f48-abfc-41bf-aa9a-7feccefcf977)] +interface nsIWindowlessBrowser : nsIWebNavigation +{ + /** + * "Closes" the windowless browser and destroys its associated nsIWebBrowser + * and docshell. + * + * This method *must* be called for every windowless browser before its last + * reference is released. + */ + void close(); +}; + diff --git a/xpfe/appshell/nsIXULBrowserWindow.idl b/xpfe/appshell/nsIXULBrowserWindow.idl new file mode 100644 index 000000000..40f1898c8 --- /dev/null +++ b/xpfe/appshell/nsIXULBrowserWindow.idl @@ -0,0 +1,78 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +#include "nsIURI.idl" +#include "nsIDOMNode.idl" + +interface nsIRequest; +interface nsIDOMElement; +interface nsIInputStream; +interface nsIDocShell; +interface nsITabParent; +interface mozIDOMWindowProxy; + +/** + * The nsIXULBrowserWindow supplies the methods that may be called from the + * internals of the browser area to tell the containing xul window to update + * its ui. + */ +[scriptable, uuid(a8675fa9-c8b4-4350-9803-c38f344a9e38)] +interface nsIXULBrowserWindow : nsISupports +{ + /** + * Sets the status according to JS' version of status. + */ + void setJSStatus(in AString status); + + /** + * Tells the object implementing this function what link we are currently + * over. + */ + void setOverLink(in AString link, in nsIDOMElement element); + + /** + * Determines the appropriate target for a link. + */ + AString onBeforeLinkTraversal(in AString originalTarget, + in nsIURI linkURI, + in nsIDOMNode linkNode, + in boolean isAppTab); + + /** + * Find the initial browser of the window and set its remote attribute. + * This can be used to ensure that there is a remote browser in a new + * window when it first spawns. + * + */ + nsITabParent forceInitialBrowserRemote(); + void forceInitialBrowserNonRemote(in mozIDOMWindowProxy openerWindow); + + /** + * Determines whether a load should continue. + * + * @param aDocShell + * The docshell performing the load. + * @param aURI + * The URI being loaded. + * @param aReferrer + * The referrer of the load. + */ + bool shouldLoadURI(in nsIDocShell aDocShell, + in nsIURI aURI, + in nsIURI aReferrer); + /** + * Show/hide a tooltip (when the user mouses over a link, say). + */ + void showTooltip(in long x, in long y, in AString tooltip, in AString direction); + void hideTooltip(); + + /** + * Return the number of tabs in this window. + */ + uint32_t getTabCount(); +}; + diff --git a/xpfe/appshell/nsIXULWindow.idl b/xpfe/appshell/nsIXULWindow.idl new file mode 100644 index 000000000..8db12adb6 --- /dev/null +++ b/xpfe/appshell/nsIXULWindow.idl @@ -0,0 +1,168 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +/** + * The nsIXULWindow + * + * When the window is destroyed, it will fire a "xul-window-destroyed" + * notification through the global observer service. + */ + +interface nsIDocShell; +interface nsIDocShellTreeItem; +interface nsIXULBrowserWindow; +interface nsITabParent; +interface mozIDOMWindowProxy; + +[scriptable, uuid(d6d7a014-e28d-4c9d-8727-1cf6d870619b)] +interface nsIXULWindow : nsISupports +{ + /** + * The docshell owning the XUL for this window. + */ + readonly attribute nsIDocShell docShell; + + /** + * Indicates if this window is instrinsically sized. + */ + attribute boolean intrinsicallySized; + + /** + * The primary content shell. + * + * Note that this is a docshell tree item and therefore can not be assured of + * what object it is. It could be an editor, a docshell, or a browser object. + * Or down the road any other object that supports being a DocShellTreeItem + * Query accordingly to determine the capabilities. + */ + readonly attribute nsIDocShellTreeItem primaryContentShell; + + /** + * In multiprocess case we may not have primaryContentShell but + * primaryTabParent. + */ + readonly attribute nsITabParent primaryTabParent; + + void tabParentAdded(in nsITabParent aTab, in boolean aPrimary); + void tabParentRemoved(in nsITabParent aTab); + + /** + * The content shell specified by the supplied id. + * + * Note that this is a docshell tree item and therefore can not be assured of + * what object it is. It could be an editor, a docshell, or a browser object. + * Or down the road any other object that supports being a DocShellTreeItem + * Query accordingly to determine the capabilities. + */ + nsIDocShellTreeItem getContentShellById(in wstring ID); + + /** + * Tell this window that it has picked up a child XUL window + * @param aChild the child window being added + */ + void addChildWindow(in nsIXULWindow aChild); + + /** + * Tell this window that it has lost a child XUL window + * @param aChild the child window being removed + */ + void removeChildWindow(in nsIXULWindow aChild); + + /** + * Move the window to a centered position. + * @param aRelative If not null, the window relative to which the window is + * moved. See aScreen parameter for details. + * @param aScreen PR_TRUE to center the window relative to the screen + * containing aRelative if aRelative is not null. If + * aRelative is null then relative to the screen of the + * opener window if it was initialized by passing it to + * nsWebShellWindow::Initialize. Failing that relative to + * the main screen. + * PR_FALSE to center it relative to aRelative itself. + * @param aAlert PR_TRUE to move the window to an alert position, + * generally centered horizontally and 1/3 down from the top. + */ + void center(in nsIXULWindow aRelative, in boolean aScreen, in boolean aAlert); + + /** + * Shows the window as a modal window. That is, ensures that it is visible + * and runs a local event loop, exiting only once the window has been closed. + */ + void showModal(); + + const unsigned long lowestZ = 0; + const unsigned long loweredZ = 4; /* "alwaysLowered" attribute */ + const unsigned long normalZ = 5; + const unsigned long raisedZ = 6; /* "alwaysRaised" attribute */ + const unsigned long highestZ = 9; + + attribute unsigned long zLevel; + + /** + * contextFlags are from nsIWindowCreator2 + */ + attribute uint32_t contextFlags; + + attribute uint32_t chromeFlags; + + /** + * Begin assuming |chromeFlags| don't change hereafter, and assert + * if they do change. The state change is one-way and idempotent. + */ + void assumeChromeFlagsAreFrozen(); + + /** + * Create a new window. + * @param aChromeFlags see nsIWebBrowserChrome + * @param aOpeningTab the TabParent that requested this new window be opened. + * Can be left null. + * @param aOpener The window which is requesting that this new window be opened. + * @return the newly minted window + */ + nsIXULWindow createNewWindow(in int32_t aChromeFlags, + in nsITabParent aOpeningTab, + in mozIDOMWindowProxy aOpener); + + attribute nsIXULBrowserWindow XULBrowserWindow; + + /** + * Back-door method to force application of chrome flags at a particular + * time. Do NOT call this unless you know what you're doing! In particular, + * calling this when this XUL window doesn't yet have a document in its + * docshell could cause problems. + */ + [noscript] void applyChromeFlags(); + + /** + * Given the dimensions of some content area held within this + * XUL window, and assuming that that content area will change + * its dimensions in linear proportion to the dimensions of this + * XUL window, changes the size of the XUL window so that the + * content area reaches a particular size. + * + * We need to supply the content area dimensions because sometimes + * the child's nsDocShellTreeOwner needs to propagate a SizeShellTo + * call to the parent. But the shellItem argument of the call will + * not be available on the parent side. + * + * Note: this is an internal method, other consumers should never call this. + * + * @param aDesiredWidth + * The desired width of the content area in device pixels. + * @param aDesiredHeight + * The desired height of the content area in device pixels. + * @param shellItemWidth + * The current width of the content area. + * @param shellItemHeight + * The current height of the content area. + */ + [noscript, notxpcom] void sizeShellToWithLimit(in int32_t aDesiredWidth, + in int32_t aDesiredHeight, + in int32_t shellItemWidth, + in int32_t shellItemHeight); +}; diff --git a/xpfe/appshell/nsWebShellWindow.cpp b/xpfe/appshell/nsWebShellWindow.cpp new file mode 100644 index 000000000..288d94759 --- /dev/null +++ b/xpfe/appshell/nsWebShellWindow.cpp @@ -0,0 +1,911 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +#include "nsWebShellWindow.h" + +#include "nsLayoutCID.h" +#include "nsContentCID.h" +#include "nsIWeakReference.h" +#include "nsIContentViewer.h" +#include "nsIComponentManager.h" +#include "nsIServiceManager.h" +#include "nsIURL.h" +#include "nsIIOService.h" +#include "nsIURL.h" +#include "nsNetCID.h" +#include "nsIStringBundle.h" +#include "nsReadableUtils.h" + +#include "nsContentUtils.h" +#include "nsEscape.h" +#include "nsPIDOMWindow.h" +#include "nsIWebNavigation.h" +#include "nsIWindowWatcher.h" + +#include "nsIDOMXULElement.h" + +#include "nsWidgetInitData.h" +#include "nsWidgetsCID.h" +#include "nsIWidget.h" +#include "nsIWidgetListener.h" + +#include "nsIDOMCharacterData.h" +#include "nsIDOMNodeList.h" + +#include "nsITimer.h" +#include "nsXULPopupManager.h" + + +#include "nsIDOMXULDocument.h" + +#include "nsFocusManager.h" + +#include "nsIWebProgress.h" +#include "nsIWebProgressListener.h" + +#include "nsIDocument.h" +#include "nsIDOMDocument.h" +#include "nsIDOMNode.h" +#include "nsIDOMElement.h" +#include "nsIDocumentLoaderFactory.h" +#include "nsIObserverService.h" +#include "prprf.h" + +#include "nsIScreenManager.h" +#include "nsIScreen.h" + +#include "nsIContent.h" // for menus +#include "nsIScriptSecurityManager.h" + +// For calculating size +#include "nsIPresShell.h" +#include "nsPresContext.h" + +#include "nsIBaseWindow.h" +#include "nsIDocShellTreeItem.h" + +#include "mozilla/Attributes.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/MouseEvents.h" + +#include "nsPIWindowRoot.h" + +#ifdef XP_MACOSX +#include "nsINativeMenuService.h" +#define USE_NATIVE_MENUS +#endif + +using namespace mozilla; +using namespace mozilla::dom; + +/* Define Class IDs */ +static NS_DEFINE_CID(kWindowCID, NS_WINDOW_CID); + +#define SIZE_PERSISTENCE_TIMEOUT 500 // msec + +nsWebShellWindow::nsWebShellWindow(uint32_t aChromeFlags) + : nsXULWindow(aChromeFlags) + , mSPTimerLock("nsWebShellWindow.mSPTimerLock") + , mWidgetListenerDelegate(this) +{ +} + +nsWebShellWindow::~nsWebShellWindow() +{ + MutexAutoLock lock(mSPTimerLock); + if (mSPTimer) + mSPTimer->Cancel(); +} + +NS_IMPL_ADDREF_INHERITED(nsWebShellWindow, nsXULWindow) +NS_IMPL_RELEASE_INHERITED(nsWebShellWindow, nsXULWindow) + +NS_INTERFACE_MAP_BEGIN(nsWebShellWindow) + NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) +NS_INTERFACE_MAP_END_INHERITING(nsXULWindow) + +nsresult nsWebShellWindow::Initialize(nsIXULWindow* aParent, + nsIXULWindow* aOpener, + nsIURI* aUrl, + int32_t aInitialWidth, + int32_t aInitialHeight, + bool aIsHiddenWindow, + nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpenerWindow, + nsWidgetInitData& widgetInitData) +{ + nsresult rv; + nsCOMPtr<nsIWidget> parentWidget; + + mIsHiddenWindow = aIsHiddenWindow; + + int32_t initialX = 0, initialY = 0; + nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aOpener)); + if (base) { + rv = base->GetPositionAndSize(&mOpenerScreenRect.x, + &mOpenerScreenRect.y, + &mOpenerScreenRect.width, + &mOpenerScreenRect.height); + if (NS_FAILED(rv)) { + mOpenerScreenRect.SetEmpty(); + } else { + double scale; + if (NS_SUCCEEDED(base->GetUnscaledDevicePixelsPerCSSPixel(&scale))) { + mOpenerScreenRect.x = NSToIntRound(mOpenerScreenRect.x / scale); + mOpenerScreenRect.y = NSToIntRound(mOpenerScreenRect.y / scale); + mOpenerScreenRect.width = NSToIntRound(mOpenerScreenRect.width / scale); + mOpenerScreenRect.height = NSToIntRound(mOpenerScreenRect.height / scale); + } + initialX = mOpenerScreenRect.x; + initialY = mOpenerScreenRect.y; + ConstrainToOpenerScreen(&initialX, &initialY); + } + } + + // XXX: need to get the default window size from prefs... + // Doesn't come from prefs... will come from CSS/XUL/RDF + DesktopIntRect deskRect(initialX, initialY, aInitialWidth, aInitialHeight); + + // Create top level window + mWindow = do_CreateInstance(kWindowCID, &rv); + if (NS_OK != rv) { + return rv; + } + + /* This next bit is troublesome. We carry two different versions of a pointer + to our parent window. One is the parent window's widget, which is passed + to our own widget. The other is a weak reference we keep here to our + parent WebShellWindow. The former is useful to the widget, and we can't + trust its treatment of the parent reference because they're platform- + specific. The latter is useful to this class. + A better implementation would be one in which the parent keeps strong + references to its children and closes them before it allows itself + to be closed. This would mimic the behaviour of OSes that support + top-level child windows in OSes that do not. Later. + */ + nsCOMPtr<nsIBaseWindow> parentAsWin(do_QueryInterface(aParent)); + if (parentAsWin) { + parentAsWin->GetMainWidget(getter_AddRefs(parentWidget)); + mParentWindow = do_GetWeakReference(aParent); + } + + mWindow->SetWidgetListener(&mWidgetListenerDelegate); + rv = mWindow->Create((nsIWidget *)parentWidget, // Parent nsIWidget + nullptr, // Native parent widget + deskRect, // Widget dimensions + &widgetInitData); // Widget initialization data + NS_ENSURE_SUCCESS(rv, rv); + + LayoutDeviceIntRect r = mWindow->GetClientBounds(); + // Match the default background color of content. Important on windows + // since we no longer use content child widgets. + mWindow->SetBackgroundColor(NS_RGB(255,255,255)); + + // Create web shell + mDocShell = do_CreateInstance("@mozilla.org/docshell;1"); + NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); + + mDocShell->SetOpener(aOpeningTab); + + // Make sure to set the item type on the docshell _before_ calling + // Create() so it knows what type it is. + nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(mDocShell)); + NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE); + NS_ENSURE_SUCCESS(EnsureChromeTreeOwner(), NS_ERROR_FAILURE); + + docShellAsItem->SetTreeOwner(mChromeTreeOwner); + docShellAsItem->SetItemType(nsIDocShellTreeItem::typeChrome); + + r.x = r.y = 0; + nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(mDocShell)); + NS_ENSURE_SUCCESS(docShellAsWin->InitWindow(nullptr, mWindow, + r.x, r.y, r.width, r.height), NS_ERROR_FAILURE); + NS_ENSURE_SUCCESS(docShellAsWin->Create(), NS_ERROR_FAILURE); + + // Attach a WebProgress listener.during initialization... + nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(mDocShell, &rv)); + if (webProgress) { + webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK); + } + + if (aOpenerWindow) { + nsPIDOMWindowOuter* window = mDocShell->GetWindow(); + MOZ_ASSERT(window); + window->SetOpenerWindow(nsPIDOMWindowOuter::From(aOpenerWindow), true); + } + + // Eagerly create an about:blank content viewer with the right principal here, + // rather than letting it happening in the upcoming call to + // SetInitialPrincipalToSubject. This avoids creating the about:blank document + // and then blowing it away with a second one, which can cause problems for the + // top-level chrome window case. See bug 789773. + // Note that we don't accept expanded principals here, similar to + // SetInitialPrincipalToSubject. + if (nsContentUtils::IsInitialized()) { // Sometimes this happens really early See bug 793370. + MOZ_ASSERT(mDocShell->ItemType() == nsIDocShellTreeItem::typeChrome); + nsCOMPtr<nsIPrincipal> principal = nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller(); + if (nsContentUtils::IsExpandedPrincipal(principal)) { + principal = nullptr; + } + rv = mDocShell->CreateAboutBlankContentViewer(principal); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIDocument> doc = mDocShell->GetDocument(); + NS_ENSURE_TRUE(!!doc, NS_ERROR_FAILURE); + doc->SetIsInitialDocument(true); + } + + if (nullptr != aUrl) { + nsCString tmpStr; + + rv = aUrl->GetSpec(tmpStr); + if (NS_FAILED(rv)) return rv; + + NS_ConvertUTF8toUTF16 urlString(tmpStr); + nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell)); + NS_ENSURE_TRUE(webNav, NS_ERROR_FAILURE); + rv = webNav->LoadURI(urlString.get(), + nsIWebNavigation::LOAD_FLAGS_NONE, + nullptr, + nullptr, + nullptr); + NS_ENSURE_SUCCESS(rv, rv); + } + + return rv; +} + +nsIPresShell* +nsWebShellWindow::GetPresShell() +{ + if (!mDocShell) + return nullptr; + + return mDocShell->GetPresShell(); +} + +bool +nsWebShellWindow::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) +{ + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) { + nsCOMPtr<nsPIDOMWindowOuter> window = + mDocShell ? mDocShell->GetWindow() : nullptr; + pm->AdjustPopupsOnWindowChange(window); + } + + // Notify all tabs that the widget moved. + if (mDocShell && mDocShell->GetWindow()) { + nsCOMPtr<EventTarget> eventTarget = mDocShell->GetWindow()->GetTopWindowRoot(); + nsContentUtils::DispatchChromeEvent(mDocShell->GetDocument(), + eventTarget, + NS_LITERAL_STRING("MozUpdateWindowPos"), + false, false, nullptr); + } + + // Persist position, but not immediately, in case this OS is firing + // repeated move events as the user drags the window + SetPersistenceTimer(PAD_POSITION); + return false; +} + +bool +nsWebShellWindow::WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) +{ + nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(mDocShell)); + if (shellAsWin) { + shellAsWin->SetPositionAndSize(0, 0, aWidth, aHeight, 0); + } + // Persist size, but not immediately, in case this OS is firing + // repeated size events as the user drags the sizing handle + if (!IsLocked()) + SetPersistenceTimer(PAD_POSITION | PAD_SIZE | PAD_MISC); + return true; +} + +bool +nsWebShellWindow::RequestWindowClose(nsIWidget* aWidget) +{ + // Maintain a reference to this as it is about to get destroyed. + nsCOMPtr<nsIXULWindow> xulWindow(this); + + nsCOMPtr<nsPIDOMWindowOuter> window(mDocShell ? mDocShell->GetWindow() : nullptr); + nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(window); + + nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell(); + if (!presShell) { + mozilla::DebugOnly<bool> dying; + MOZ_ASSERT(NS_SUCCEEDED(mDocShell->IsBeingDestroyed(&dying)) && dying, + "No presShell, but window is not being destroyed"); + } else if (eventTarget) { + RefPtr<nsPresContext> presContext = presShell->GetPresContext(); + + nsEventStatus status = nsEventStatus_eIgnore; + WidgetMouseEvent event(true, eWindowClose, nullptr, + WidgetMouseEvent::eReal); + if (NS_SUCCEEDED(eventTarget->DispatchDOMEvent(&event, nullptr, presContext, &status)) && + status == nsEventStatus_eConsumeNoDefault) + return false; + } + + Destroy(); + return false; +} + +void +nsWebShellWindow::SizeModeChanged(nsSizeMode sizeMode) +{ + // An alwaysRaised (or higher) window will hide any newly opened normal + // browser windows, so here we just drop a raised window to the normal + // zlevel if it's maximized. We make no provision for automatically + // re-raising it when restored. + if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) { + uint32_t zLevel; + GetZLevel(&zLevel); + if (zLevel > nsIXULWindow::normalZ) + SetZLevel(nsIXULWindow::normalZ); + } + mWindow->SetSizeMode(sizeMode); + + // Persist mode, but not immediately, because in many (all?) + // cases this will merge with the similar call in NS_SIZE and + // write the attribute values only once. + SetPersistenceTimer(PAD_MISC); + nsCOMPtr<nsPIDOMWindowOuter> ourWindow = + mDocShell ? mDocShell->GetWindow() : nullptr; + if (ourWindow) { + MOZ_ASSERT(ourWindow->IsOuterWindow()); + + // Ensure that the fullscreen state is synchronized between + // the widget and the outer window object. + if (sizeMode == nsSizeMode_Fullscreen) { + ourWindow->SetFullScreen(true); + } + else if (sizeMode != nsSizeMode_Minimized) { + if (ourWindow->GetFullScreen()) { + // The first SetFullscreenInternal call below ensures that we do + // not trigger any fullscreen transition even if the window was + // put in fullscreen only for the Fullscreen API. The second + // SetFullScreen call ensures that the window really exit from + // fullscreen even if it entered fullscreen for both Fullscreen + // Mode and Fullscreen API. + ourWindow->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen, false); + ourWindow->SetFullScreen(false); + } + } + + // And always fire a user-defined sizemodechange event on the window + ourWindow->DispatchCustomEvent(NS_LITERAL_STRING("sizemodechange")); + } + + nsIPresShell* presShell; + if ((presShell = GetPresShell())) { + presShell->GetPresContext()->SizeModeChanged(sizeMode); + } + + // Note the current implementation of SetSizeMode just stores + // the new state; it doesn't actually resize. So here we store + // the state and pass the event on to the OS. The day is coming + // when we'll handle the event here, and the return result will + // then need to be different. +} + +void +nsWebShellWindow::UIResolutionChanged() +{ + nsCOMPtr<nsPIDOMWindowOuter> ourWindow = + mDocShell ? mDocShell->GetWindow() : nullptr; + if (ourWindow) { + MOZ_ASSERT(ourWindow->IsOuterWindow()); + ourWindow->DispatchCustomEvent(NS_LITERAL_STRING("resolutionchange")); + } +} + +void +nsWebShellWindow::FullscreenChanged(bool aInFullscreen) +{ + if (mDocShell) { + if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) { + ourWindow->FinishFullscreenChange(aInFullscreen); + } + } +} + +void +nsWebShellWindow::OSToolbarButtonPressed() +{ + // Keep a reference as setting the chrome flags can fire events. + nsCOMPtr<nsIXULWindow> xulWindow(this); + + // rjc: don't use "nsIWebBrowserChrome::CHROME_EXTRA" + // due to components with multiple sidebar components + // (such as Mail/News, Addressbook, etc)... and frankly, + // Mac IE, OmniWeb, and other Mac OS X apps all work this way + uint32_t chromeMask = (nsIWebBrowserChrome::CHROME_TOOLBAR | + nsIWebBrowserChrome::CHROME_LOCATIONBAR | + nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR); + + nsCOMPtr<nsIWebBrowserChrome> wbc(do_GetInterface(xulWindow)); + if (!wbc) + return; + + uint32_t chromeFlags, newChromeFlags = 0; + wbc->GetChromeFlags(&chromeFlags); + newChromeFlags = chromeFlags & chromeMask; + if (!newChromeFlags) chromeFlags |= chromeMask; + else chromeFlags &= (~newChromeFlags); + wbc->SetChromeFlags(chromeFlags); +} + +bool +nsWebShellWindow::ZLevelChanged(bool aImmediate, nsWindowZ *aPlacement, + nsIWidget* aRequestBelow, nsIWidget** aActualBelow) +{ + if (aActualBelow) + *aActualBelow = nullptr; + + return ConstrainToZLevel(aImmediate, aPlacement, aRequestBelow, aActualBelow); +} + +void +nsWebShellWindow::WindowActivated() +{ + nsCOMPtr<nsIXULWindow> xulWindow(this); + + // focusing the window could cause it to close, so keep a reference to it + nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell ? mDocShell->GetWindow() : nullptr; + nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID); + if (fm && window) + fm->WindowRaised(window); + + if (mChromeLoaded) { + PersistentAttributesDirty(PAD_POSITION | PAD_SIZE | PAD_MISC); + SavePersistentAttributes(); + } +} + +void +nsWebShellWindow::WindowDeactivated() +{ + nsCOMPtr<nsIXULWindow> xulWindow(this); + + nsCOMPtr<nsPIDOMWindowOuter> window = + mDocShell ? mDocShell->GetWindow() : nullptr; + nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID); + if (fm && window) + fm->WindowLowered(window); +} + +#ifdef USE_NATIVE_MENUS +static void LoadNativeMenus(nsIDOMDocument *aDOMDoc, nsIWidget *aParentWindow) +{ + nsCOMPtr<nsINativeMenuService> nms = do_GetService("@mozilla.org/widget/nativemenuservice;1"); + if (!nms) { + return; + } + + // Find the menubar tag (if there is more than one, we ignore all but + // the first). + nsCOMPtr<nsIDOMNodeList> menubarElements; + aDOMDoc->GetElementsByTagNameNS(NS_LITERAL_STRING("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"), + NS_LITERAL_STRING("menubar"), + getter_AddRefs(menubarElements)); + + nsCOMPtr<nsIDOMNode> menubarNode; + if (menubarElements) + menubarElements->Item(0, getter_AddRefs(menubarNode)); + + if (menubarNode) { + nsCOMPtr<nsIContent> menubarContent(do_QueryInterface(menubarNode)); + nms->CreateNativeMenuBar(aParentWindow, menubarContent); + } else { + nms->CreateNativeMenuBar(aParentWindow, nullptr); + } +} +#endif + +namespace mozilla { + +class WebShellWindowTimerCallback final : public nsITimerCallback +{ +public: + explicit WebShellWindowTimerCallback(nsWebShellWindow* aWindow) + : mWindow(aWindow) + {} + + NS_DECL_THREADSAFE_ISUPPORTS + + NS_IMETHOD Notify(nsITimer* aTimer) override + { + // Although this object participates in a refcount cycle (this -> mWindow + // -> mSPTimer -> this), mSPTimer is a one-shot timer and releases this + // after it fires. So we don't need to release mWindow here. + + mWindow->FirePersistenceTimer(); + return NS_OK; + } + +private: + ~WebShellWindowTimerCallback() {} + + RefPtr<nsWebShellWindow> mWindow; +}; + +NS_IMPL_ISUPPORTS(WebShellWindowTimerCallback, nsITimerCallback) + +} // namespace mozilla + +void +nsWebShellWindow::SetPersistenceTimer(uint32_t aDirtyFlags) +{ + MutexAutoLock lock(mSPTimerLock); + if (!mSPTimer) { + mSPTimer = do_CreateInstance("@mozilla.org/timer;1"); + if (!mSPTimer) { + NS_WARNING("Couldn't create @mozilla.org/timer;1 instance?"); + return; + } + } + + RefPtr<WebShellWindowTimerCallback> callback = + new WebShellWindowTimerCallback(this); + mSPTimer->InitWithCallback(callback, SIZE_PERSISTENCE_TIMEOUT, + nsITimer::TYPE_ONE_SHOT); + + PersistentAttributesDirty(aDirtyFlags); +} + +void +nsWebShellWindow::FirePersistenceTimer() +{ + MutexAutoLock lock(mSPTimerLock); + SavePersistentAttributes(); +} + + +//---------------------------------------- +// nsIWebProgessListener implementation +//---------------------------------------- +NS_IMETHODIMP +nsWebShellWindow::OnProgressChange(nsIWebProgress *aProgress, + nsIRequest *aRequest, + int32_t aCurSelfProgress, + int32_t aMaxSelfProgress, + int32_t aCurTotalProgress, + int32_t aMaxTotalProgress) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + +NS_IMETHODIMP +nsWebShellWindow::OnStateChange(nsIWebProgress *aProgress, + nsIRequest *aRequest, + uint32_t aStateFlags, + nsresult aStatus) +{ + // If the notification is not about a document finishing, then just + // ignore it... + if (!(aStateFlags & nsIWebProgressListener::STATE_STOP) || + !(aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK)) { + return NS_OK; + } + + if (mChromeLoaded) + return NS_OK; + + // If this document notification is for a frame then ignore it... + nsCOMPtr<mozIDOMWindowProxy> eventWin; + aProgress->GetDOMWindow(getter_AddRefs(eventWin)); + auto* eventPWin = nsPIDOMWindowOuter::From(eventWin); + if (eventPWin) { + nsPIDOMWindowOuter *rootPWin = eventPWin->GetPrivateRoot(); + if (eventPWin != rootPWin) + return NS_OK; + } + + mChromeLoaded = true; + mLockedUntilChromeLoad = false; + +#ifdef USE_NATIVE_MENUS + /////////////////////////////// + // Find the Menubar DOM and Load the menus, hooking them up to the loaded commands + /////////////////////////////// + nsCOMPtr<nsIContentViewer> cv; + mDocShell->GetContentViewer(getter_AddRefs(cv)); + if (cv) { + nsCOMPtr<nsIDOMDocument> menubarDOMDoc(do_QueryInterface(cv->GetDocument())); + if (menubarDOMDoc) + LoadNativeMenus(menubarDOMDoc, mWindow); + } +#endif // USE_NATIVE_MENUS + + OnChromeLoaded(); + LoadContentAreas(); + + return NS_OK; +} + +NS_IMETHODIMP +nsWebShellWindow::OnLocationChange(nsIWebProgress *aProgress, + nsIRequest *aRequest, + nsIURI *aURI, + uint32_t aFlags) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + +NS_IMETHODIMP +nsWebShellWindow::OnStatusChange(nsIWebProgress* aWebProgress, + nsIRequest* aRequest, + nsresult aStatus, + const char16_t* aMessage) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + +NS_IMETHODIMP +nsWebShellWindow::OnSecurityChange(nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + uint32_t state) +{ + NS_NOTREACHED("notification excluded in AddProgressListener(...)"); + return NS_OK; +} + + +//---------------------------------------- + +// if the main document URL specified URLs for any content areas, start them loading +void nsWebShellWindow::LoadContentAreas() { + + nsAutoString searchSpec; + + // fetch the chrome document URL + nsCOMPtr<nsIContentViewer> contentViewer; + // yes, it's possible for the docshell to be null even this early + // see bug 57514. + if (mDocShell) + mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); + if (contentViewer) { + nsIDocument* doc = contentViewer->GetDocument(); + if (doc) { + nsIURI* mainURL = doc->GetDocumentURI(); + + nsCOMPtr<nsIURL> url = do_QueryInterface(mainURL); + if (url) { + nsAutoCString search; + url->GetQuery(search); + + AppendUTF8toUTF16(search, searchSpec); + } + } + } + + // content URLs are specified in the search part of the URL + // as <contentareaID>=<escapedURL>[;(repeat)] + if (!searchSpec.IsEmpty()) { + int32_t begPos, + eqPos, + endPos; + nsString contentAreaID, + contentURL; + char *urlChar; + nsresult rv; + for (endPos = 0; endPos < (int32_t)searchSpec.Length(); ) { + // extract contentAreaID and URL substrings + begPos = endPos; + eqPos = searchSpec.FindChar('=', begPos); + if (eqPos < 0) + break; + + endPos = searchSpec.FindChar(';', eqPos); + if (endPos < 0) + endPos = searchSpec.Length(); + searchSpec.Mid(contentAreaID, begPos, eqPos-begPos); + searchSpec.Mid(contentURL, eqPos+1, endPos-eqPos-1); + endPos++; + + // see if we have a docshell with a matching contentAreaID + nsCOMPtr<nsIDocShellTreeItem> content; + rv = GetContentShellById(contentAreaID.get(), getter_AddRefs(content)); + if (NS_SUCCEEDED(rv) && content) { + nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(content)); + if (webNav) { + urlChar = ToNewCString(contentURL); + if (urlChar) { + nsUnescape(urlChar); + contentURL.AssignWithConversion(urlChar); + webNav->LoadURI(contentURL.get(), + nsIWebNavigation::LOAD_FLAGS_NONE, + nullptr, + nullptr, + nullptr); + free(urlChar); + } + } + } + } + } +} + +/** + * ExecuteCloseHandler - Run the close handler, if any. + * @return true iff we found a close handler to run. + */ +bool nsWebShellWindow::ExecuteCloseHandler() +{ + /* If the event handler closes this window -- a likely scenario -- + things get deleted out of order without this death grip. + (The problem may be the death grip in nsWindow::windowProc, + which forces this window's widget to remain alive longer + than it otherwise would.) */ + nsCOMPtr<nsIXULWindow> kungFuDeathGrip(this); + + nsCOMPtr<EventTarget> eventTarget; + if (mDocShell) { + eventTarget = do_QueryInterface(mDocShell->GetWindow()); + } + + if (eventTarget) { + nsCOMPtr<nsIContentViewer> contentViewer; + mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); + if (contentViewer) { + RefPtr<nsPresContext> presContext; + contentViewer->GetPresContext(getter_AddRefs(presContext)); + + nsEventStatus status = nsEventStatus_eIgnore; + WidgetMouseEvent event(true, eWindowClose, nullptr, + WidgetMouseEvent::eReal); + + nsresult rv = + eventTarget->DispatchDOMEvent(&event, nullptr, presContext, &status); + if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault) + return true; + // else fall through and return false + } + } + + return false; +} // ExecuteCloseHandler + +void nsWebShellWindow::ConstrainToOpenerScreen(int32_t* aX, int32_t* aY) +{ + if (mOpenerScreenRect.IsEmpty()) { + *aX = *aY = 0; + return; + } + + int32_t left, top, width, height; + // Constrain initial positions to the same screen as opener + nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1"); + if (screenmgr) { + nsCOMPtr<nsIScreen> screen; + screenmgr->ScreenForRect(mOpenerScreenRect.x, mOpenerScreenRect.y, + mOpenerScreenRect.width, mOpenerScreenRect.height, + getter_AddRefs(screen)); + if (screen) { + screen->GetAvailRectDisplayPix(&left, &top, &width, &height); + if (*aX < left || *aX > left + width) { + *aX = left; + } + if (*aY < top || *aY > top + height) { + *aY = top; + } + } + } +} + +// nsIBaseWindow +NS_IMETHODIMP nsWebShellWindow::Destroy() +{ + nsresult rv; + nsCOMPtr<nsIWebProgress> webProgress(do_GetInterface(mDocShell, &rv)); + if (webProgress) { + webProgress->RemoveProgressListener(this); + } + + nsCOMPtr<nsIXULWindow> kungFuDeathGrip(this); + { + MutexAutoLock lock(mSPTimerLock); + if (mSPTimer) { + mSPTimer->Cancel(); + SavePersistentAttributes(); + mSPTimer = nullptr; + } + } + return nsXULWindow::Destroy(); +} + +nsIXULWindow* +nsWebShellWindow::WidgetListenerDelegate::GetXULWindow() +{ + return mWebShellWindow->GetXULWindow(); +} + +nsIPresShell* +nsWebShellWindow::WidgetListenerDelegate::GetPresShell() +{ + return mWebShellWindow->GetPresShell(); +} + +bool +nsWebShellWindow::WidgetListenerDelegate::WindowMoved( + nsIWidget* aWidget, int32_t aX, int32_t aY) +{ + RefPtr<nsWebShellWindow> holder = mWebShellWindow; + return holder->WindowMoved(aWidget, aX, aY); +} + +bool +nsWebShellWindow::WidgetListenerDelegate::WindowResized( + nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) +{ + RefPtr<nsWebShellWindow> holder = mWebShellWindow; + return holder->WindowResized(aWidget, aWidth, aHeight); +} + +bool +nsWebShellWindow::WidgetListenerDelegate::RequestWindowClose(nsIWidget* aWidget) +{ + RefPtr<nsWebShellWindow> holder = mWebShellWindow; + return holder->RequestWindowClose(aWidget); +} + +void +nsWebShellWindow::WidgetListenerDelegate::SizeModeChanged(nsSizeMode aSizeMode) +{ + RefPtr<nsWebShellWindow> holder = mWebShellWindow; + holder->SizeModeChanged(aSizeMode); +} + +void +nsWebShellWindow::WidgetListenerDelegate::UIResolutionChanged() +{ + RefPtr<nsWebShellWindow> holder = mWebShellWindow; + holder->UIResolutionChanged(); +} + +void +nsWebShellWindow::WidgetListenerDelegate::FullscreenChanged(bool aInFullscreen) +{ + RefPtr<nsWebShellWindow> holder = mWebShellWindow; + holder->FullscreenChanged(aInFullscreen); +} + +void +nsWebShellWindow::WidgetListenerDelegate::OSToolbarButtonPressed() +{ + RefPtr<nsWebShellWindow> holder = mWebShellWindow; + holder->OSToolbarButtonPressed(); +} + +bool +nsWebShellWindow::WidgetListenerDelegate::ZLevelChanged( + bool aImmediate, nsWindowZ *aPlacement, nsIWidget* aRequestBelow, + nsIWidget** aActualBelow) +{ + RefPtr<nsWebShellWindow> holder = mWebShellWindow; + return holder->ZLevelChanged(aImmediate, + aPlacement, + aRequestBelow, + aActualBelow); +} + +void +nsWebShellWindow::WidgetListenerDelegate::WindowActivated() +{ + RefPtr<nsWebShellWindow> holder = mWebShellWindow; + holder->WindowActivated(); +} + +void +nsWebShellWindow::WidgetListenerDelegate::WindowDeactivated() +{ + RefPtr<nsWebShellWindow> holder = mWebShellWindow; + holder->WindowDeactivated(); +} diff --git a/xpfe/appshell/nsWebShellWindow.h b/xpfe/appshell/nsWebShellWindow.h new file mode 100644 index 000000000..e2b796161 --- /dev/null +++ b/xpfe/appshell/nsWebShellWindow.h @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef nsWebShellWindow_h__ +#define nsWebShellWindow_h__ + +#include "mozilla/Mutex.h" +#include "nsIWebProgressListener.h" +#include "nsITimer.h" +#include "nsCOMPtr.h" +#include "nsXULWindow.h" +#include "nsIWidgetListener.h" +#include "nsITabParent.h" + +/* Forward declarations.... */ +class nsIURI; + +struct nsWidgetInitData; + +namespace mozilla { +class WebShellWindowTimerCallback; +} // namespace mozilla + +class nsWebShellWindow final : public nsXULWindow, + public nsIWebProgressListener +{ +public: + + // The implementation of non-refcounted nsIWidgetListener, which would hold a + // strong reference on stack before calling nsWebShellWindow + class WidgetListenerDelegate : public nsIWidgetListener + { + public: + explicit WidgetListenerDelegate(nsWebShellWindow* aWebShellWindow) + : mWebShellWindow(aWebShellWindow) {} + + virtual nsIXULWindow* GetXULWindow() override; + virtual nsIPresShell* GetPresShell() override; + virtual bool WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) override; + virtual bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) override; + virtual bool RequestWindowClose(nsIWidget* aWidget) override; + virtual void SizeModeChanged(nsSizeMode sizeMode) override; + virtual void UIResolutionChanged() override; + virtual void FullscreenChanged(bool aInFullscreen) override; + virtual void OSToolbarButtonPressed() override; + virtual bool ZLevelChanged(bool aImmediate, + nsWindowZ *aPlacement, + nsIWidget* aRequestBelow, + nsIWidget** aActualBelow) override; + virtual void WindowActivated() override; + virtual void WindowDeactivated() override; + + private: + // The lifetime of WidgetListenerDelegate is bound to nsWebShellWindow so + // we just use a raw pointer here. + nsWebShellWindow* mWebShellWindow; + }; + + explicit nsWebShellWindow(uint32_t aChromeFlags); + + // nsISupports interface... + NS_DECL_ISUPPORTS_INHERITED + + // nsWebShellWindow methods... + nsresult Initialize(nsIXULWindow * aParent, nsIXULWindow * aOpener, + nsIURI* aUrl, + int32_t aInitialWidth, int32_t aInitialHeight, + bool aIsHiddenWindow, + nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpenerWIndow, + nsWidgetInitData& widgetInitData); + + nsresult Toolbar(); + + // nsIWebProgressListener + NS_DECL_NSIWEBPROGRESSLISTENER + + // nsIBaseWindow + NS_IMETHOD Destroy() override; + + // nsIWidgetListener + nsIXULWindow* GetXULWindow() { return this; } + nsIPresShell* GetPresShell(); + bool WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y); + bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight); + bool RequestWindowClose(nsIWidget* aWidget); + void SizeModeChanged(nsSizeMode sizeMode); + void UIResolutionChanged(); + void FullscreenChanged(bool aInFullscreen); + void OSToolbarButtonPressed(); + bool ZLevelChanged(bool aImmediate, nsWindowZ *aPlacement, + nsIWidget* aRequestBelow, nsIWidget** aActualBelow); + void WindowActivated(); + void WindowDeactivated(); + +protected: + friend class mozilla::WebShellWindowTimerCallback; + + virtual ~nsWebShellWindow(); + + void LoadContentAreas(); + bool ExecuteCloseHandler(); + void ConstrainToOpenerScreen(int32_t* aX, int32_t* aY); + + nsCOMPtr<nsITimer> mSPTimer; + mozilla::Mutex mSPTimerLock; + WidgetListenerDelegate mWidgetListenerDelegate; + + void SetPersistenceTimer(uint32_t aDirtyFlags); + void FirePersistenceTimer(); +}; + + +#endif /* nsWebShellWindow_h__ */ diff --git a/xpfe/appshell/nsWindowMediator.cpp b/xpfe/appshell/nsWindowMediator.cpp new file mode 100644 index 000000000..6d69bc764 --- /dev/null +++ b/xpfe/appshell/nsWindowMediator.cpp @@ -0,0 +1,846 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsReadableUtils.h" +#include "nsUnicharUtils.h" +#include "nsTArray.h" +#include "nsIBaseWindow.h" +#include "nsIWidget.h" +#include "nsIDOMWindow.h" +#include "nsIObserverService.h" +#include "nsIServiceManager.h" +#include "nsISimpleEnumerator.h" +#include "nsAppShellWindowEnumerator.h" +#include "nsWindowMediator.h" +#include "nsIWindowMediatorListener.h" +#include "nsXPIDLString.h" +#include "nsGlobalWindow.h" + +#include "nsIDocShell.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIXULWindow.h" + +using namespace mozilla; + +static bool notifyOpenWindow(nsIWindowMediatorListener *aElement, void* aData); +static bool notifyCloseWindow(nsIWindowMediatorListener *aElement, void* aData); +static bool notifyWindowTitleChange(nsIWindowMediatorListener *aElement, void* aData); + +// for notifyWindowTitleChange +struct WindowTitleData { + nsIXULWindow* mWindow; + const char16_t *mTitle; +}; + +nsresult +nsWindowMediator::GetDOMWindow(nsIXULWindow* inWindow, + nsCOMPtr<nsPIDOMWindowOuter>& outDOMWindow) +{ + nsCOMPtr<nsIDocShell> docShell; + + outDOMWindow = nullptr; + inWindow->GetDocShell(getter_AddRefs(docShell)); + NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); + + outDOMWindow = docShell->GetWindow(); + return outDOMWindow ? NS_OK : NS_ERROR_FAILURE; +} + +nsWindowMediator::nsWindowMediator() : + mEnumeratorList(), mOldestWindow(nullptr), mTopmostWindow(nullptr), + mTimeStamp(0), mSortingZOrder(false), mReady(false) +{ +} + +nsWindowMediator::~nsWindowMediator() +{ + while (mOldestWindow) + UnregisterWindow(mOldestWindow); +} + +nsresult nsWindowMediator::Init() +{ + nsresult rv; + nsCOMPtr<nsIObserverService> obsSvc = + do_GetService("@mozilla.org/observer-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = obsSvc->AddObserver(this, "xpcom-shutdown", true); + NS_ENSURE_SUCCESS(rv, rv); + + mReady = true; + return NS_OK; +} + +NS_IMETHODIMP nsWindowMediator::RegisterWindow(nsIXULWindow* inWindow) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_STATE(mReady); + + if (GetInfoFor(inWindow)) { + NS_ERROR("multiple window registration"); + return NS_ERROR_FAILURE; + } + + mTimeStamp++; + + // Create window info struct and add to list of windows + nsWindowInfo* windowInfo = new nsWindowInfo(inWindow, mTimeStamp); + + WindowTitleData winData = { inWindow, nullptr }; + mListeners.EnumerateForwards(notifyOpenWindow, &winData); + + if (mOldestWindow) + windowInfo->InsertAfter(mOldestWindow->mOlder, nullptr); + else + mOldestWindow = windowInfo; + + return NS_OK; +} + +NS_IMETHODIMP +nsWindowMediator::UnregisterWindow(nsIXULWindow* inWindow) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_STATE(mReady); + nsWindowInfo *info = GetInfoFor(inWindow); + if (info) + return UnregisterWindow(info); + return NS_ERROR_INVALID_ARG; +} + +nsresult +nsWindowMediator::UnregisterWindow(nsWindowInfo *inInfo) +{ + // Inform the iterators + uint32_t index = 0; + while (index < mEnumeratorList.Length()) { + mEnumeratorList[index]->WindowRemoved(inInfo); + index++; + } + + WindowTitleData winData = { inInfo->mWindow.get(), nullptr }; + mListeners.EnumerateForwards(notifyCloseWindow, &winData); + + // Remove from the lists and free up + if (inInfo == mOldestWindow) + mOldestWindow = inInfo->mYounger; + if (inInfo == mTopmostWindow) + mTopmostWindow = inInfo->mLower; + inInfo->Unlink(true, true); + if (inInfo == mOldestWindow) + mOldestWindow = nullptr; + if (inInfo == mTopmostWindow) + mTopmostWindow = nullptr; + delete inInfo; + + return NS_OK; +} + +nsWindowInfo* +nsWindowMediator::GetInfoFor(nsIXULWindow *aWindow) +{ + nsWindowInfo *info, + *listEnd; + + if (!aWindow) + return nullptr; + + info = mOldestWindow; + listEnd = nullptr; + while (info != listEnd) { + if (info->mWindow.get() == aWindow) + return info; + info = info->mYounger; + listEnd = mOldestWindow; + } + return nullptr; +} + +nsWindowInfo* +nsWindowMediator::GetInfoFor(nsIWidget *aWindow) +{ + nsWindowInfo *info, + *listEnd; + + if (!aWindow) + return nullptr; + + info = mOldestWindow; + listEnd = nullptr; + + nsCOMPtr<nsIWidget> scanWidget; + while (info != listEnd) { + nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(info->mWindow)); + if (base) + base->GetMainWidget(getter_AddRefs(scanWidget)); + if (aWindow == scanWidget.get()) + return info; + info = info->mYounger; + listEnd = mOldestWindow; + } + return nullptr; +} + +NS_IMETHODIMP +nsWindowMediator::GetEnumerator(const char16_t* inType, nsISimpleEnumerator** outEnumerator) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_ARG_POINTER(outEnumerator); + NS_ENSURE_STATE(mReady); + + RefPtr<nsAppShellWindowEnumerator> enumerator = new nsASDOMWindowEarlyToLateEnumerator(inType, *this); + enumerator.forget(outEnumerator); + return NS_OK; +} + +NS_IMETHODIMP +nsWindowMediator::GetXULWindowEnumerator(const char16_t* inType, nsISimpleEnumerator** outEnumerator) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_ARG_POINTER(outEnumerator); + NS_ENSURE_STATE(mReady); + + RefPtr<nsAppShellWindowEnumerator> enumerator = new nsASXULWindowEarlyToLateEnumerator(inType, *this); + enumerator.forget(outEnumerator); + return NS_OK; +} + +NS_IMETHODIMP +nsWindowMediator::GetZOrderDOMWindowEnumerator( + const char16_t *aWindowType, bool aFrontToBack, + nsISimpleEnumerator **_retval) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_ARG_POINTER(_retval); + NS_ENSURE_STATE(mReady); + + RefPtr<nsAppShellWindowEnumerator> enumerator; + if (aFrontToBack) + enumerator = new nsASDOMWindowFrontToBackEnumerator(aWindowType, *this); + else + enumerator = new nsASDOMWindowBackToFrontEnumerator(aWindowType, *this); + + enumerator.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +nsWindowMediator::GetZOrderXULWindowEnumerator( + const char16_t *aWindowType, bool aFrontToBack, + nsISimpleEnumerator **_retval) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_ARG_POINTER(_retval); + NS_ENSURE_STATE(mReady); + + RefPtr<nsAppShellWindowEnumerator> enumerator; + if (aFrontToBack) + enumerator = new nsASXULWindowFrontToBackEnumerator(aWindowType, *this); + else + enumerator = new nsASXULWindowBackToFrontEnumerator(aWindowType, *this); + + enumerator.forget(_retval); + return NS_OK; +} + +int32_t +nsWindowMediator::AddEnumerator(nsAppShellWindowEnumerator * inEnumerator) +{ + return mEnumeratorList.AppendElement(inEnumerator) != nullptr; +} + +int32_t +nsWindowMediator::RemoveEnumerator(nsAppShellWindowEnumerator * inEnumerator) +{ + return mEnumeratorList.RemoveElement(inEnumerator); +} + +// Returns the window of type inType ( if null return any window type ) which has the most recent +// time stamp +NS_IMETHODIMP +nsWindowMediator::GetMostRecentWindow(const char16_t* inType, + mozIDOMWindowProxy** outWindow) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_ARG_POINTER(outWindow); + *outWindow = nullptr; + if (!mReady) + return NS_OK; + + // Find the most window with the highest time stamp that matches + // the requested type + nsWindowInfo* info = MostRecentWindowInfo(inType, false); + if (info && info->mWindow) { + nsCOMPtr<nsPIDOMWindowOuter> DOMWindow; + if (NS_SUCCEEDED(GetDOMWindow(info->mWindow, DOMWindow))) { + DOMWindow.forget(outWindow); + return NS_OK; + } + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsWindowMediator::GetMostRecentNonPBWindow(const char16_t* aType, mozIDOMWindowProxy** aWindow) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_ARG_POINTER(aWindow); + *aWindow = nullptr; + + nsWindowInfo *info = MostRecentWindowInfo(aType, true); + nsCOMPtr<nsPIDOMWindowOuter> domWindow; + if (info && info->mWindow) { + GetDOMWindow(info->mWindow, domWindow); + } + + if (!domWindow) { + return NS_ERROR_FAILURE; + } + + domWindow.forget(aWindow); + return NS_OK; +} + +nsWindowInfo* +nsWindowMediator::MostRecentWindowInfo(const char16_t* inType, + bool aSkipPrivateBrowsingOrClosed) +{ + int32_t lastTimeStamp = -1; + nsAutoString typeString(inType); + bool allWindows = !inType || typeString.IsEmpty(); + + // Find the most recent window with the highest time stamp that matches + // the requested type and has the correct browsing mode. + nsWindowInfo* searchInfo = mOldestWindow; + nsWindowInfo* listEnd = nullptr; + nsWindowInfo* foundInfo = nullptr; + for (; searchInfo != listEnd; searchInfo = searchInfo->mYounger) { + listEnd = mOldestWindow; + + if (!allWindows && !searchInfo->TypeEquals(typeString)) { + continue; + } + if (searchInfo->mTimeStamp < lastTimeStamp) { + continue; + } + if (!searchInfo->mWindow) { + continue; + } + if (aSkipPrivateBrowsingOrClosed) { + nsCOMPtr<nsIDocShell> docShell; + searchInfo->mWindow->GetDocShell(getter_AddRefs(docShell)); + nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell); + if (!loadContext || loadContext->UsePrivateBrowsing()) { + continue; + } + + nsCOMPtr<nsPIDOMWindowOuter> piwindow = docShell->GetWindow(); + if (!piwindow || piwindow->Closed()) { + continue; + } + } + + foundInfo = searchInfo; + lastTimeStamp = searchInfo->mTimeStamp; + } + + return foundInfo; +} + +NS_IMETHODIMP +nsWindowMediator::GetOuterWindowWithId(uint64_t aWindowID, + mozIDOMWindowProxy** aWindow) +{ + RefPtr<nsGlobalWindow> window = nsGlobalWindow::GetOuterWindowWithId(aWindowID); + nsCOMPtr<nsPIDOMWindowOuter> outer = window ? window->AsOuter() : nullptr; + outer.forget(aWindow); + return NS_OK; +} + +NS_IMETHODIMP +nsWindowMediator::GetCurrentInnerWindowWithId(uint64_t aWindowID, + mozIDOMWindow** aWindow) +{ + RefPtr<nsGlobalWindow> window = nsGlobalWindow::GetInnerWindowWithId(aWindowID); + + // not found + if (!window) + return NS_OK; + + nsCOMPtr<nsPIDOMWindowInner> inner = window->AsInner(); + nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow(); + NS_ENSURE_TRUE(outer, NS_ERROR_UNEXPECTED); + + // outer is already using another inner, so it's same as not found + if (outer->GetCurrentInnerWindow() != inner) + return NS_OK; + + inner.forget(aWindow); + return NS_OK; +} + +NS_IMETHODIMP +nsWindowMediator::UpdateWindowTimeStamp(nsIXULWindow* inWindow) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_STATE(mReady); + nsWindowInfo *info = GetInfoFor(inWindow); + if (info) { + // increment the window's time stamp + info->mTimeStamp = ++mTimeStamp; + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowMediator::UpdateWindowTitle(nsIXULWindow* inWindow, + const char16_t* inTitle) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_STATE(mReady); + if (GetInfoFor(inWindow)) { + WindowTitleData winData = { inWindow, inTitle }; + mListeners.EnumerateForwards(notifyWindowTitleChange, &winData); + } + + return NS_OK; +} + +/* This method's plan is to intervene only when absolutely necessary. + We will get requests to place our windows behind unknown windows. + For the most part, we need to leave those alone (turning them into + explicit requests to be on top breaks Windows.) So generally we + calculate a change as seldom as possible. +*/ +NS_IMETHODIMP +nsWindowMediator::CalculateZPosition( + nsIXULWindow *inWindow, + uint32_t inPosition, + nsIWidget *inBelow, + uint32_t *outPosition, + nsIWidget **outBelow, + bool *outAltered) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_ARG_POINTER(outBelow); + NS_ENSURE_STATE(mReady); + + *outBelow = nullptr; + + if (!inWindow || !outPosition || !outAltered) + return NS_ERROR_NULL_POINTER; + + if (inPosition != nsIWindowMediator::zLevelTop && + inPosition != nsIWindowMediator::zLevelBottom && + inPosition != nsIWindowMediator::zLevelBelow) + return NS_ERROR_INVALID_ARG; + + nsWindowInfo *info = mTopmostWindow; + nsIXULWindow *belowWindow = nullptr; + bool found = false; + nsresult result = NS_OK; + + *outPosition = inPosition; + *outAltered = false; + + if (mSortingZOrder) { // don't fight SortZOrder() + *outBelow = inBelow; + NS_IF_ADDREF(*outBelow); + return NS_OK; + } + + uint32_t inZ; + GetZLevel(inWindow, &inZ); + + if (inPosition == nsIWindowMediator::zLevelBelow) { + // locate inBelow. use topmost if it can't be found or isn't in the + // z-order list + info = GetInfoFor(inBelow); + if (!info || (info->mYounger != info && info->mLower == info)) + info = mTopmostWindow; + else + found = true; + + if (!found) { + /* Treat unknown windows as a request to be on top. + Not as it should be, but that's what Windows gives us. + Note we change inPosition, but not *outPosition. This forces + us to go through the "on top" calculation just below, without + necessarily changing the output parameters. */ + inPosition = nsIWindowMediator::zLevelTop; + } + } + + if (inPosition == nsIWindowMediator::zLevelTop) { + if (mTopmostWindow && mTopmostWindow->mZLevel > inZ) { + // asked for topmost, can't have it. locate highest allowed position. + do { + if (info->mZLevel <= inZ) + break; + info = info->mLower; + } while (info != mTopmostWindow); + + *outPosition = nsIWindowMediator::zLevelBelow; + belowWindow = info->mHigher->mWindow; + *outAltered = true; + } + } else if (inPosition == nsIWindowMediator::zLevelBottom) { + if (mTopmostWindow && mTopmostWindow->mHigher->mZLevel < inZ) { + // asked for bottommost, can't have it. locate lowest allowed position. + do { + info = info->mHigher; + if (info->mZLevel >= inZ) + break; + } while (info != mTopmostWindow); + + *outPosition = nsIWindowMediator::zLevelBelow; + belowWindow = info->mWindow; + *outAltered = true; + } + } else { + unsigned long relativeZ; + + // check that we're in the right z-plane + if (found) { + belowWindow = info->mWindow; + relativeZ = info->mZLevel; + if (relativeZ > inZ) { + // might be OK. is lower window, if any, lower? + if (info->mLower != info && info->mLower->mZLevel > inZ) { + do { + if (info->mZLevel <= inZ) + break; + info = info->mLower; + } while (info != mTopmostWindow); + + belowWindow = info->mHigher->mWindow; + *outAltered = true; + } + } else if (relativeZ < inZ) { + // nope. look for a higher window to be behind. + do { + info = info->mHigher; + if (info->mZLevel >= inZ) + break; + } while (info != mTopmostWindow); + + if (info->mZLevel >= inZ) + belowWindow = info->mWindow; + else + *outPosition = nsIWindowMediator::zLevelTop; + *outAltered = true; + } // else they're equal, so it's OK + } + } + + if (NS_SUCCEEDED(result) && belowWindow) { + nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(belowWindow)); + if (base) + base->GetMainWidget(outBelow); + else + result = NS_ERROR_NO_INTERFACE; + } + + return result; +} + +NS_IMETHODIMP +nsWindowMediator::SetZPosition( + nsIXULWindow *inWindow, + uint32_t inPosition, + nsIXULWindow *inBelow) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + nsWindowInfo *inInfo, + *belowInfo; + + if ((inPosition != nsIWindowMediator::zLevelTop && + inPosition != nsIWindowMediator::zLevelBottom && + inPosition != nsIWindowMediator::zLevelBelow) || + !inWindow) { + return NS_ERROR_INVALID_ARG; + } + + if (mSortingZOrder) // don't fight SortZOrder() + return NS_OK; + + NS_ENSURE_STATE(mReady); + + /* Locate inWindow and unlink it from the z-order list. + It's important we look for it in the age list, not the z-order list. + This is because the former is guaranteed complete, while + now may be this window's first exposure to the latter. */ + inInfo = GetInfoFor(inWindow); + if (!inInfo) + return NS_ERROR_INVALID_ARG; + + // locate inBelow, place inWindow behind it + if (inPosition == nsIWindowMediator::zLevelBelow) { + belowInfo = GetInfoFor(inBelow); + // it had better also be in the z-order list + if (belowInfo && + belowInfo->mYounger != belowInfo && belowInfo->mLower == belowInfo) { + belowInfo = nullptr; + } + if (!belowInfo) { + if (inBelow) + return NS_ERROR_INVALID_ARG; + else + inPosition = nsIWindowMediator::zLevelTop; + } + } + if (inPosition == nsIWindowMediator::zLevelTop || + inPosition == nsIWindowMediator::zLevelBottom) + belowInfo = mTopmostWindow ? mTopmostWindow->mHigher : nullptr; + + if (inInfo != belowInfo) { + inInfo->Unlink(false, true); + inInfo->InsertAfter(nullptr, belowInfo); + } + if (inPosition == nsIWindowMediator::zLevelTop) + mTopmostWindow = inInfo; + + return NS_OK; +} + +NS_IMETHODIMP +nsWindowMediator::GetZLevel(nsIXULWindow *aWindow, uint32_t *_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + *_retval = nsIXULWindow::normalZ; + nsWindowInfo *info = GetInfoFor(aWindow); + if (info) { + *_retval = info->mZLevel; + } else { + NS_WARNING("getting z level of unregistered window"); + // this goes off during window destruction + } + return NS_OK; +} + +NS_IMETHODIMP +nsWindowMediator::SetZLevel(nsIXULWindow *aWindow, uint32_t aZLevel) +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + NS_ENSURE_STATE(mReady); + + nsWindowInfo *info = GetInfoFor(aWindow); + NS_ASSERTION(info, "setting z level of unregistered window"); + if (!info) + return NS_ERROR_FAILURE; + + if (info->mZLevel != aZLevel) { + bool lowered = info->mZLevel > aZLevel; + info->mZLevel = aZLevel; + if (lowered) + SortZOrderFrontToBack(); + else + SortZOrderBackToFront(); + } + return NS_OK; +} + +/* Fix potentially out-of-order windows by performing an insertion sort + on the z-order list. The method will work no matter how broken the + list, but its assumed usage is immediately after one window's z level + has been changed, so one window is potentially out of place. Such a sort + is most efficiently done in a particular direction. Use this one + if a window's z level has just been reduced, so the sort is most efficiently + done front to back. + Note it's hardly worth going to all the trouble to write two versions + of this method except that if we choose the inefficient sorting direction, + on slow systems windows could visibly bubble around the window that + was moved. +*/ +void +nsWindowMediator::SortZOrderFrontToBack() +{ + nsWindowInfo *scan, // scans list looking for problems + *search, // searches for correct placement for scan window + *prev, // previous search element + *lowest; // bottom-most window in list + bool finished; + + if (!mTopmostWindow) // early during program execution there's no z list yet + return; // there's also only one window, so this is not dangerous + + mSortingZOrder = true; + + /* Step through the list from top to bottom. If we find a window which + should be moved down in the list, move it to its highest legal position. */ + do { + finished = true; + lowest = mTopmostWindow->mHigher; + scan = mTopmostWindow; + while (scan != lowest) { + uint32_t scanZ = scan->mZLevel; + if (scanZ < scan->mLower->mZLevel) { // out of order + search = scan->mLower; + do { + prev = search; + search = search->mLower; + } while (prev != lowest && scanZ < search->mZLevel); + + // reposition |scan| within the list + if (scan == mTopmostWindow) + mTopmostWindow = scan->mLower; + scan->Unlink(false, true); + scan->InsertAfter(nullptr, prev); + + // fix actual window order + nsCOMPtr<nsIBaseWindow> base; + nsCOMPtr<nsIWidget> scanWidget; + nsCOMPtr<nsIWidget> prevWidget; + base = do_QueryInterface(scan->mWindow); + if (base) + base->GetMainWidget(getter_AddRefs(scanWidget)); + base = do_QueryInterface(prev->mWindow); + if (base) + base->GetMainWidget(getter_AddRefs(prevWidget)); + if (scanWidget) + scanWidget->PlaceBehind(eZPlacementBelow, prevWidget, false); + + finished = false; + break; + } + scan = scan->mLower; + } + } while (!finished); + + mSortingZOrder = false; +} + +// see comment for SortZOrderFrontToBack +void +nsWindowMediator::SortZOrderBackToFront() +{ + nsWindowInfo *scan, // scans list looking for problems + *search, // searches for correct placement for scan window + *lowest; // bottom-most window in list + bool finished; + + if (!mTopmostWindow) // early during program execution there's no z list yet + return; // there's also only one window, so this is not dangerous + + mSortingZOrder = true; + + /* Step through the list from bottom to top. If we find a window which + should be moved up in the list, move it to its lowest legal position. */ + do { + finished = true; + lowest = mTopmostWindow->mHigher; + scan = lowest; + while (scan != mTopmostWindow) { + uint32_t scanZ = scan->mZLevel; + if (scanZ > scan->mHigher->mZLevel) { // out of order + search = scan; + do { + search = search->mHigher; + } while (search != lowest && scanZ > search->mZLevel); + + // reposition |scan| within the list + if (scan != search && scan != search->mLower) { + scan->Unlink(false, true); + scan->InsertAfter(nullptr, search); + } + if (search == lowest) + mTopmostWindow = scan; + + // fix actual window order + nsCOMPtr<nsIBaseWindow> base; + nsCOMPtr<nsIWidget> scanWidget; + nsCOMPtr<nsIWidget> searchWidget; + base = do_QueryInterface(scan->mWindow); + if (base) + base->GetMainWidget(getter_AddRefs(scanWidget)); + if (mTopmostWindow != scan) { + base = do_QueryInterface(search->mWindow); + if (base) + base->GetMainWidget(getter_AddRefs(searchWidget)); + } + if (scanWidget) + scanWidget->PlaceBehind(eZPlacementBelow, searchWidget, false); + finished = false; + break; + } + scan = scan->mHigher; + } + } while (!finished); + + mSortingZOrder = false; +} + +NS_IMPL_ISUPPORTS(nsWindowMediator, + nsIWindowMediator_44, + nsIWindowMediator, + nsIObserver, + nsISupportsWeakReference) + +NS_IMETHODIMP +nsWindowMediator::AddListener(nsIWindowMediatorListener* aListener) +{ + NS_ENSURE_ARG_POINTER(aListener); + + mListeners.AppendObject(aListener); + + return NS_OK; +} + +NS_IMETHODIMP +nsWindowMediator::RemoveListener(nsIWindowMediatorListener* aListener) +{ + NS_ENSURE_ARG_POINTER(aListener); + + mListeners.RemoveObject(aListener); + + return NS_OK; +} + +NS_IMETHODIMP +nsWindowMediator::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) +{ + if (!strcmp(aTopic, "xpcom-shutdown") && mReady) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + while (mOldestWindow) + UnregisterWindow(mOldestWindow); + mReady = false; + } + return NS_OK; +} + +bool +notifyOpenWindow(nsIWindowMediatorListener *aListener, void* aData) +{ + WindowTitleData* winData = static_cast<WindowTitleData*>(aData); + aListener->OnOpenWindow(winData->mWindow); + + return true; +} + +bool +notifyCloseWindow(nsIWindowMediatorListener *aListener, void* aData) +{ + WindowTitleData* winData = static_cast<WindowTitleData*>(aData); + aListener->OnCloseWindow(winData->mWindow); + + return true; +} + +bool +notifyWindowTitleChange(nsIWindowMediatorListener *aListener, void* aData) +{ + WindowTitleData* titleData = reinterpret_cast<WindowTitleData*>(aData); + aListener->OnWindowTitleChange(titleData->mWindow, titleData->mTitle); + + return true; +} diff --git a/xpfe/appshell/nsWindowMediator.h b/xpfe/appshell/nsWindowMediator.h new file mode 100644 index 000000000..a0eaab56d --- /dev/null +++ b/xpfe/appshell/nsWindowMediator.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef nsWindowMediator_h_ +#define nsWindowMediator_h_ + +#include "nsCOMPtr.h" +#include "nsIWindowMediator.h" +#include "nsIObserver.h" +#include "nsTArray.h" +#include "nsXPIDLString.h" +#include "nsWeakReference.h" +#include "nsCOMArray.h" + +class nsAppShellWindowEnumerator; +class nsASXULWindowEarlyToLateEnumerator; +class nsASDOMWindowEarlyToLateEnumerator; +class nsASDOMWindowFrontToBackEnumerator; +class nsASXULWindowFrontToBackEnumerator; +class nsASDOMWindowBackToFrontEnumerator; +class nsASXULWindowBackToFrontEnumerator; +class nsIWindowMediatorListener; +struct nsWindowInfo; + +class nsWindowMediator : + public nsIWindowMediator_44, + public nsIObserver, + public nsSupportsWeakReference +{ +friend class nsAppShellWindowEnumerator; +friend class nsASXULWindowEarlyToLateEnumerator; +friend class nsASDOMWindowEarlyToLateEnumerator; +friend class nsASDOMWindowFrontToBackEnumerator; +friend class nsASXULWindowFrontToBackEnumerator; +friend class nsASDOMWindowBackToFrontEnumerator; +friend class nsASXULWindowBackToFrontEnumerator; + +protected: + virtual ~nsWindowMediator(); + +public: + nsWindowMediator(); + + nsresult Init(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIWINDOWMEDIATOR + NS_DECL_NSIWINDOWMEDIATOR_44 + NS_DECL_NSIOBSERVER + + static nsresult GetDOMWindow(nsIXULWindow* inWindow, + nsCOMPtr<nsPIDOMWindowOuter>& outDOMWindow); + +private: + int32_t AddEnumerator(nsAppShellWindowEnumerator* inEnumerator); + int32_t RemoveEnumerator(nsAppShellWindowEnumerator* inEnumerator); + nsWindowInfo* MostRecentWindowInfo(const char16_t* inType, + bool aSkipPrivateBrowsingOrClosed = false); + + nsresult UnregisterWindow(nsWindowInfo *inInfo); + nsWindowInfo *GetInfoFor(nsIXULWindow *aWindow); + nsWindowInfo *GetInfoFor(nsIWidget *aWindow); + void SortZOrderFrontToBack(); + void SortZOrderBackToFront(); + + nsTArray<nsAppShellWindowEnumerator*> mEnumeratorList; + nsWindowInfo *mOldestWindow; + nsWindowInfo *mTopmostWindow; + int32_t mTimeStamp; + bool mSortingZOrder; + bool mReady; + + nsCOMArray<nsIWindowMediatorListener> mListeners; +}; + +#endif diff --git a/xpfe/appshell/nsXULWindow.cpp b/xpfe/appshell/nsXULWindow.cpp new file mode 100644 index 000000000..8f2d9fde5 --- /dev/null +++ b/xpfe/appshell/nsXULWindow.cpp @@ -0,0 +1,2334 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 ci et: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/MathAlgorithms.h" + +// Local includes +#include "nsXULWindow.h" +#include <algorithm> + +// Helper classes +#include "nsPrintfCString.h" +#include "nsString.h" +#include "nsWidgetsCID.h" +#include "nsThreadUtils.h" +#include "nsNetCID.h" +#include "nsQueryObject.h" +#include "mozilla/Sprintf.h" + +//Interfaces needed to be included +#include "nsIAppShell.h" +#include "nsIAppShellService.h" +#include "nsIServiceManager.h" +#include "nsIContentViewer.h" +#include "nsIDocument.h" +#include "nsIDOMDocument.h" +#include "nsIDOMXULDocument.h" +#include "nsIDOMElement.h" +#include "nsIDOMXULElement.h" +#include "nsPIDOMWindow.h" +#include "nsIDOMScreen.h" +#include "nsIEmbeddingSiteWindow.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIIOService.h" +#include "nsILoadContext.h" +#include "nsIObserverService.h" +#include "nsIWindowMediator.h" +#include "nsIScreenManager.h" +#include "nsIScreen.h" +#include "nsIScrollable.h" +#include "nsIScriptSecurityManager.h" +#include "nsIWindowWatcher.h" +#include "nsIURI.h" +#include "nsIDOMCSSStyleDeclaration.h" +#include "nsAppShellCID.h" +#include "nsReadableUtils.h" +#include "nsStyleConsts.h" +#include "nsPresContext.h" +#include "nsContentUtils.h" +#include "nsWebShellWindow.h" // get rid of this one, too... +#include "nsGlobalWindow.h" + +#include "prenv.h" +#include "mozilla/AutoRestore.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "mozilla/dom/BarProps.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/TabParent.h" + +using namespace mozilla; +using dom::AutoNoJSAPI; + +#define SIZEMODE_NORMAL NS_LITERAL_STRING("normal") +#define SIZEMODE_MAXIMIZED NS_LITERAL_STRING("maximized") +#define SIZEMODE_MINIMIZED NS_LITERAL_STRING("minimized") +#define SIZEMODE_FULLSCREEN NS_LITERAL_STRING("fullscreen") + +#define WINDOWTYPE_ATTRIBUTE NS_LITERAL_STRING("windowtype") + +#define PERSIST_ATTRIBUTE NS_LITERAL_STRING("persist") +#define SCREENX_ATTRIBUTE NS_LITERAL_STRING("screenX") +#define SCREENY_ATTRIBUTE NS_LITERAL_STRING("screenY") +#define WIDTH_ATTRIBUTE NS_LITERAL_STRING("width") +#define HEIGHT_ATTRIBUTE NS_LITERAL_STRING("height") +#define MODE_ATTRIBUTE NS_LITERAL_STRING("sizemode") +#define ZLEVEL_ATTRIBUTE NS_LITERAL_STRING("zlevel") + + +//***************************************************************************** +//*** nsXULWindow: Object Management +//***************************************************************************** + +nsXULWindow::nsXULWindow(uint32_t aChromeFlags) + : mChromeTreeOwner(nullptr), + mContentTreeOwner(nullptr), + mPrimaryContentTreeOwner(nullptr), + mModalStatus(NS_OK), + mContinueModalLoop(false), + mDebuting(false), + mChromeLoaded(false), + mShowAfterLoad(false), + mIntrinsicallySized(false), + mCenterAfterLoad(false), + mIsHiddenWindow(false), + mLockedUntilChromeLoad(false), + mIgnoreXULSize(false), + mIgnoreXULPosition(false), + mChromeFlagsFrozen(false), + mIgnoreXULSizeMode(false), + mDestroying(false), + mRegistered(false), + mContextFlags(0), + mPersistentAttributesDirty(0), + mPersistentAttributesMask(0), + mChromeFlags(aChromeFlags) +{ +} + +nsXULWindow::~nsXULWindow() +{ + Destroy(); +} + +//***************************************************************************** +// nsXULWindow::nsISupports +//***************************************************************************** + +NS_IMPL_ADDREF(nsXULWindow) +NS_IMPL_RELEASE(nsXULWindow) + +NS_INTERFACE_MAP_BEGIN(nsXULWindow) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULWindow) + NS_INTERFACE_MAP_ENTRY(nsIXULWindow) + NS_INTERFACE_MAP_ENTRY(nsIBaseWindow) + NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + if (aIID.Equals(NS_GET_IID(nsXULWindow))) + foundInterface = reinterpret_cast<nsISupports*>(this); + else +NS_INTERFACE_MAP_END + +//***************************************************************************** +// nsXULWindow::nsIIntefaceRequestor +//***************************************************************************** + +NS_IMETHODIMP nsXULWindow::GetInterface(const nsIID& aIID, void** aSink) +{ + nsresult rv; + + NS_ENSURE_ARG_POINTER(aSink); + + if (aIID.Equals(NS_GET_IID(nsIPrompt))) { + rv = EnsurePrompter(); + if (NS_FAILED(rv)) return rv; + return mPrompter->QueryInterface(aIID, aSink); + } + if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) { + rv = EnsureAuthPrompter(); + if (NS_FAILED(rv)) return rv; + return mAuthPrompter->QueryInterface(aIID, aSink); + } + if (aIID.Equals(NS_GET_IID(mozIDOMWindowProxy))) { + return GetWindowDOMWindow(reinterpret_cast<mozIDOMWindowProxy**>(aSink)); + } + if (aIID.Equals(NS_GET_IID(nsIDOMWindow))) { + nsCOMPtr<mozIDOMWindowProxy> window = nullptr; + rv = GetWindowDOMWindow(getter_AddRefs(window)); + nsCOMPtr<nsIDOMWindow> domWindow = do_QueryInterface(window); + domWindow.forget(aSink); + return rv; + } + if (aIID.Equals(NS_GET_IID(nsIDOMWindowInternal))) { + nsCOMPtr<mozIDOMWindowProxy> window = nullptr; + rv = GetWindowDOMWindow(getter_AddRefs(window)); + nsCOMPtr<nsIDOMWindowInternal> domWindowInternal = do_QueryInterface(window); + domWindowInternal.forget(aSink); + return rv; + } + if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome)) && + NS_SUCCEEDED(EnsureContentTreeOwner()) && + NS_SUCCEEDED(mContentTreeOwner->QueryInterface(aIID, aSink))) + return NS_OK; + + if (aIID.Equals(NS_GET_IID(nsIEmbeddingSiteWindow)) && + NS_SUCCEEDED(EnsureContentTreeOwner()) && + NS_SUCCEEDED(mContentTreeOwner->QueryInterface(aIID, aSink))) + return NS_OK; + + return QueryInterface(aIID, aSink); +} + +//***************************************************************************** +// nsXULWindow::nsIXULWindow +//***************************************************************************** + +NS_IMETHODIMP nsXULWindow::GetDocShell(nsIDocShell** aDocShell) +{ + NS_ENSURE_ARG_POINTER(aDocShell); + + *aDocShell = mDocShell; + NS_IF_ADDREF(*aDocShell); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetZLevel(uint32_t *outLevel) +{ + nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (mediator) + mediator->GetZLevel(this, outLevel); + else + *outLevel = normalZ; + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::SetZLevel(uint32_t aLevel) +{ + nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (!mediator) + return NS_ERROR_FAILURE; + + uint32_t zLevel; + mediator->GetZLevel(this, &zLevel); + if (zLevel == aLevel) + return NS_OK; + + /* refuse to raise a maximized window above the normal browser level, + for fear it could hide newly opened browser windows */ + if (aLevel > nsIXULWindow::normalZ && mWindow) { + nsSizeMode sizeMode = mWindow->SizeMode(); + if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) { + return NS_ERROR_FAILURE; + } + } + + // do it + mediator->SetZLevel(this, aLevel); + PersistentAttributesDirty(PAD_MISC); + SavePersistentAttributes(); + + nsCOMPtr<nsIContentViewer> cv; + mDocShell->GetContentViewer(getter_AddRefs(cv)); + if (cv) { + nsCOMPtr<nsIDocument> doc = cv->GetDocument(); + if (doc) { + ErrorResult rv; + RefPtr<dom::Event> event = + doc->CreateEvent(NS_LITERAL_STRING("Events"),rv); + if (event) { + event->InitEvent(NS_LITERAL_STRING("windowZLevel"), true, false); + + event->SetTrusted(true); + + bool defaultActionEnabled; + doc->DispatchEvent(event, &defaultActionEnabled); + } + } + } + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetContextFlags(uint32_t *aContextFlags) +{ + NS_ENSURE_ARG_POINTER(aContextFlags); + *aContextFlags = mContextFlags; + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::SetContextFlags(uint32_t aContextFlags) +{ + mContextFlags = aContextFlags; + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetChromeFlags(uint32_t *aChromeFlags) +{ + NS_ENSURE_ARG_POINTER(aChromeFlags); + *aChromeFlags = mChromeFlags; + /* mChromeFlags is kept up to date, except for scrollbar visibility. + That can be changed directly by the content DOM window, which + doesn't know to update the chrome window. So that we must check + separately. */ + + // however, it's pointless to ask if the window isn't set up yet + if (!mChromeLoaded) + return NS_OK; + + if (GetContentScrollbarVisibility()) + *aChromeFlags |= nsIWebBrowserChrome::CHROME_SCROLLBARS; + else + *aChromeFlags &= ~nsIWebBrowserChrome::CHROME_SCROLLBARS; + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::SetChromeFlags(uint32_t aChromeFlags) +{ + NS_ASSERTION(!mChromeFlagsFrozen, + "SetChromeFlags() after AssumeChromeFlagsAreFrozen()!"); + + mChromeFlags = aChromeFlags; + if (mChromeLoaded) + NS_ENSURE_SUCCESS(ApplyChromeFlags(), NS_ERROR_FAILURE); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::AssumeChromeFlagsAreFrozen() +{ + mChromeFlagsFrozen = true; + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::SetIntrinsicallySized(bool aIntrinsicallySized) +{ + mIntrinsicallySized = aIntrinsicallySized; + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetIntrinsicallySized(bool* aIntrinsicallySized) +{ + NS_ENSURE_ARG_POINTER(aIntrinsicallySized); + + *aIntrinsicallySized = mIntrinsicallySized; + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetPrimaryContentShell(nsIDocShellTreeItem** + aDocShellTreeItem) +{ + NS_ENSURE_ARG_POINTER(aDocShellTreeItem); + NS_IF_ADDREF(*aDocShellTreeItem = mPrimaryContentShell); + return NS_OK; +} + +NS_IMETHODIMP +nsXULWindow::TabParentAdded(nsITabParent* aTab, bool aPrimary) +{ + if (aPrimary) { + mPrimaryTabParent = aTab; + mPrimaryContentShell = nullptr; + } else if (mPrimaryTabParent == aTab) { + mPrimaryTabParent = nullptr; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULWindow::TabParentRemoved(nsITabParent* aTab) +{ + if (aTab == mPrimaryTabParent) { + mPrimaryTabParent = nullptr; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULWindow::GetPrimaryTabParent(nsITabParent** aTab) +{ + nsCOMPtr<nsITabParent> tab = mPrimaryTabParent; + tab.forget(aTab); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetContentShellById(const char16_t* aID, + nsIDocShellTreeItem** aDocShellTreeItem) +{ + NS_ENSURE_ARG_POINTER(aDocShellTreeItem); + *aDocShellTreeItem = nullptr; + + uint32_t count = mContentShells.Length(); + for (uint32_t i = 0; i < count; i++) { + nsContentShellInfo* shellInfo = mContentShells.ElementAt(i); + if (shellInfo->id.Equals(aID)) { + *aDocShellTreeItem = nullptr; + if (shellInfo->child) + CallQueryReferent(shellInfo->child.get(), aDocShellTreeItem); + return NS_OK; + } + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsXULWindow::AddChildWindow(nsIXULWindow *aChild) +{ + // we're not really keeping track of this right now + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::RemoveChildWindow(nsIXULWindow *aChild) +{ + // we're not really keeping track of this right now + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::ShowModal() +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER); + + // Store locally so it doesn't die on us + nsCOMPtr<nsIWidget> window = mWindow; + nsCOMPtr<nsIXULWindow> tempRef = this; + + window->SetModal(true); + mContinueModalLoop = true; + EnableParent(false); + + { + AutoNoJSAPI nojsapi; + nsIThread *thread = NS_GetCurrentThread(); + while (mContinueModalLoop) { + if (!NS_ProcessNextEvent(thread)) + break; + } + } + + mContinueModalLoop = false; + window->SetModal(false); + /* Note there's no EnableParent(true) here to match the false one + above. That's done in ExitModalLoop. It's important that the parent + be re-enabled before this window is made invisible; to do otherwise + causes bizarre z-ordering problems. At this point, the window is + already invisible. + No known current implementation of Enable would have a problem with + re-enabling the parent twice, so we could do it again here without + breaking any current implementation. But that's unnecessary if the + modal loop is always exited using ExitModalLoop (the other way would be + to change the protected member variable directly.) + */ + + return mModalStatus; +} + +//***************************************************************************** +// nsXULWindow::nsIBaseWindow +//***************************************************************************** + +NS_IMETHODIMP nsXULWindow::InitWindow(nativeWindow aParentNativeWindow, + nsIWidget* parentWidget, int32_t x, int32_t y, int32_t cx, int32_t cy) +{ + //XXX First Check In + NS_ASSERTION(false, "Not Yet Implemented"); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::Create() +{ + //XXX First Check In + NS_ASSERTION(false, "Not Yet Implemented"); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::Destroy() +{ + if (!mWindow) + return NS_OK; + + // Ensure we don't reenter this code + if (mDestroying) + return NS_OK; + + mozilla::AutoRestore<bool> guard(mDestroying); + mDestroying = true; + + nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); + NS_ASSERTION(appShell, "Couldn't get appShell... xpcom shutdown?"); + if (appShell) + appShell->UnregisterTopLevelWindow(static_cast<nsIXULWindow*>(this)); + + nsCOMPtr<nsIXULWindow> parentWindow(do_QueryReferent(mParentWindow)); + if (parentWindow) + parentWindow->RemoveChildWindow(this); + + // let's make sure the window doesn't get deleted out from under us + // while we are trying to close....this can happen if the docshell + // we close ends up being the last owning reference to this xulwindow + + // XXXTAB This shouldn't be an issue anymore because the ownership model + // only goes in one direction. When webshell container is fully removed + // try removing this... + + nsCOMPtr<nsIXULWindow> placeHolder = this; + + // Remove modality (if any) and hide while destroying. More than + // a convenience, the hide prevents user interaction with the partially + // destroyed window. This is especially necessary when the eldest window + // in a stack of modal windows is destroyed first. It happens. + ExitModalLoop(NS_OK); + // XXX: Skip unmapping the window on Linux due to GLX hangs on the compositor + // thread with NVIDIA driver 310.32. We don't need to worry about user + // interactions with destroyed windows on X11 either. +#ifndef MOZ_WIDGET_GTK + if (mWindow) + mWindow->Show(false); +#endif + +#if defined(XP_WIN) + // We need to explicitly set the focus on Windows, but + // only if the parent is visible. + nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow)); + if (parent) { + nsCOMPtr<nsIWidget> parentWidget; + parent->GetMainWidget(getter_AddRefs(parentWidget)); + if (!parentWidget || parentWidget->IsVisible()) { + nsCOMPtr<nsIBaseWindow> baseHiddenWindow; + if (appShell) { + nsCOMPtr<nsIXULWindow> hiddenWindow; + appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow)); + if (hiddenWindow) + baseHiddenWindow = do_GetInterface(hiddenWindow); + } + // somebody screwed up somewhere. hiddenwindow shouldn't be anybody's + // parent. still, when it happens, skip activating it. + if (baseHiddenWindow != parent) { + nsCOMPtr<nsIWidget> parentWidget; + parent->GetMainWidget(getter_AddRefs(parentWidget)); + if (parentWidget) + parentWidget->PlaceBehind(eZPlacementTop, 0, true); + } + } + } +#endif + + mDOMWindow = nullptr; + if (mDocShell) { + nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(mDocShell)); + shellAsWin->Destroy(); + mDocShell = nullptr; // this can cause reentrancy of this function + } + + // Remove our ref on the content shells + uint32_t count = mContentShells.Length(); + for (uint32_t i = 0; i < count; i++) { + nsContentShellInfo* shellInfo = mContentShells.ElementAt(i); + delete shellInfo; + } + mContentShells.Clear(); + mPrimaryContentShell = nullptr; + + if (mContentTreeOwner) { + mContentTreeOwner->XULWindow(nullptr); + NS_RELEASE(mContentTreeOwner); + } + if (mPrimaryContentTreeOwner) { + mPrimaryContentTreeOwner->XULWindow(nullptr); + NS_RELEASE(mPrimaryContentTreeOwner); + } + if (mChromeTreeOwner) { + mChromeTreeOwner->XULWindow(nullptr); + NS_RELEASE(mChromeTreeOwner); + } + if (mWindow) { + mWindow->SetWidgetListener(nullptr); // nsWebShellWindow hackery + mWindow->Destroy(); + mWindow = nullptr; + } + + if (!mIsHiddenWindow && mRegistered) { + /* Inform appstartup we've destroyed this window and it could + quit now if it wanted. This must happen at least after mDocShell + is destroyed, because onunload handlers fire then, and those being + script, anything could happen. A new window could open, even. + See bug 130719. */ + nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService(); + NS_ASSERTION(obssvc, "Couldn't get observer service?"); + + if (obssvc) + obssvc->NotifyObservers(nullptr, "xul-window-destroyed", nullptr); + } + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetDevicePixelsPerDesktopPixel(double *aScale) +{ + *aScale = mWindow ? mWindow->GetDesktopToDeviceScale().scale : 1.0; + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetUnscaledDevicePixelsPerCSSPixel(double *aScale) +{ + *aScale = mWindow ? mWindow->GetDefaultScale().scale : 1.0; + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::SetPositionDesktopPix(int32_t aX, int32_t aY) +{ + nsresult rv = mWindow->Move(aX, aY); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + if (!mChromeLoaded) { + // If we're called before the chrome is loaded someone obviously wants this + // window at this position. We don't persist this one-time position. + mIgnoreXULPosition = true; + return NS_OK; + } + PersistentAttributesDirty(PAD_POSITION); + SavePersistentAttributes(); + return NS_OK; +} + +// The parameters here are device pixels; do the best we can to convert to +// desktop px, using the window's current scale factor (if available). +NS_IMETHODIMP nsXULWindow::SetPosition(int32_t aX, int32_t aY) +{ + // Don't reset the window's size mode here - platforms that don't want to move + // maximized windows should reset it in their respective Move implementation. + DesktopToLayoutDeviceScale currScale = mWindow->GetDesktopToDeviceScale(); + DesktopPoint pos = LayoutDeviceIntPoint(aX, aY) / currScale; + return SetPositionDesktopPix(pos.x, pos.y); +} + +NS_IMETHODIMP nsXULWindow::GetPosition(int32_t* aX, int32_t* aY) +{ + return GetPositionAndSize(aX, aY, nullptr, nullptr); +} + +NS_IMETHODIMP nsXULWindow::SetSize(int32_t aCX, int32_t aCY, bool aRepaint) +{ + /* any attempt to set the window's size or position overrides the window's + zoom state. this is important when these two states are competing while + the window is being opened. but it should probably just always be so. */ + mWindow->SetSizeMode(nsSizeMode_Normal); + + mIntrinsicallySized = false; + + DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale(); + DesktopSize size = LayoutDeviceIntSize(aCX, aCY) / scale; + nsresult rv = mWindow->Resize(size.width, size.height, aRepaint); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + if (!mChromeLoaded) { + // If we're called before the chrome is loaded someone obviously wants this + // window at this size & in the normal size mode (since it is the only mode + // in which setting dimensions makes sense). We don't persist this one-time + // size. + mIgnoreXULSize = true; + mIgnoreXULSizeMode = true; + return NS_OK; + } + PersistentAttributesDirty(PAD_SIZE); + SavePersistentAttributes(); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetSize(int32_t* aCX, int32_t* aCY) +{ + return GetPositionAndSize(nullptr, nullptr, aCX, aCY); +} + +NS_IMETHODIMP nsXULWindow::SetPositionAndSize(int32_t aX, int32_t aY, + int32_t aCX, int32_t aCY, uint32_t aFlags) +{ + /* any attempt to set the window's size or position overrides the window's + zoom state. this is important when these two states are competing while + the window is being opened. but it should probably just always be so. */ + mWindow->SetSizeMode(nsSizeMode_Normal); + + mIntrinsicallySized = false; + + DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale(); + DesktopRect rect = LayoutDeviceIntRect(aX, aY, aCX, aCY) / scale; + nsresult rv = mWindow->Resize(rect.x, rect.y, rect.width, rect.height, + !!(aFlags & nsIBaseWindow::eRepaint)); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); + if (!mChromeLoaded) { + // If we're called before the chrome is loaded someone obviously wants this + // window at this size and position. We don't persist this one-time setting. + mIgnoreXULPosition = true; + mIgnoreXULSize = true; + mIgnoreXULSizeMode = true; + return NS_OK; + } + PersistentAttributesDirty(PAD_POSITION | PAD_SIZE); + SavePersistentAttributes(); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetPositionAndSize(int32_t* x, int32_t* y, int32_t* cx, + int32_t* cy) +{ + + if (!mWindow) + return NS_ERROR_FAILURE; + + LayoutDeviceIntRect rect = mWindow->GetScreenBounds(); + + if (x) + *x = rect.x; + if (y) + *y = rect.y; + if (cx) + *cx = rect.width; + if (cy) + *cy = rect.height; + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::Center(nsIXULWindow *aRelative, bool aScreen, bool aAlert) +{ + int32_t left, top, width, height, + ourWidth, ourHeight; + bool screenCoordinates = false, + windowCoordinates = false; + nsresult result; + + if (!mChromeLoaded) { + // note we lose the parameters. at time of writing, this isn't a problem. + mCenterAfterLoad = true; + return NS_OK; + } + + if (!aScreen && !aRelative) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr<nsIScreenManager> screenmgr = do_GetService("@mozilla.org/gfx/screenmanager;1", &result); + if (NS_FAILED(result)) + return result; + + nsCOMPtr<nsIScreen> screen; + + if (aRelative) { + nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aRelative, &result)); + if (base) { + // get window rect + result = base->GetPositionAndSize(&left, &top, &width, &height); + if (NS_SUCCEEDED(result)) { + double scale; + if (NS_SUCCEEDED(base->GetDevicePixelsPerDesktopPixel(&scale))) { + left = NSToIntRound(left / scale); + top = NSToIntRound(top / scale); + width = NSToIntRound(width / scale); + height = NSToIntRound(height / scale); + } + // if centering on screen, convert that to the corresponding screen + if (aScreen) + screenmgr->ScreenForRect(left, top, width, height, getter_AddRefs(screen)); + else + windowCoordinates = true; + } else { + // something's wrong with the reference window. + // fall back to the primary screen + aRelative = 0; + aScreen = true; + } + } + } + if (!aRelative) { + if (!mOpenerScreenRect.IsEmpty()) { + // FIXME - check if these are device or display pixels + screenmgr->ScreenForRect(mOpenerScreenRect.x, mOpenerScreenRect.y, + mOpenerScreenRect.width, mOpenerScreenRect.height, + getter_AddRefs(screen)); + } else { + screenmgr->GetPrimaryScreen(getter_AddRefs(screen)); + } + } + + if (aScreen && screen) { + screen->GetAvailRectDisplayPix(&left, &top, &width, &height); + screenCoordinates = true; + } + + if (screenCoordinates || windowCoordinates) { + NS_ASSERTION(mWindow, "what, no window?"); + double scale = mWindow->GetDesktopToDeviceScale().scale; + GetSize(&ourWidth, &ourHeight); + int32_t scaledWidth, scaledHeight; + scaledWidth = NSToIntRound(ourWidth / scale); + scaledHeight = NSToIntRound(ourHeight / scale); + left += (width - scaledWidth) / 2; + top += (height - scaledHeight) / (aAlert ? 3 : 2); + if (windowCoordinates) { + mWindow->ConstrainPosition(false, &left, &top); + } + SetPosition(left * scale, top * scale); + + // If moving the window caused it to change size, + // re-do the centering. + int32_t newWidth, newHeight; + GetSize(&newWidth, &newHeight); + if (newWidth != ourWidth || newHeight != ourHeight) { + return Center(aRelative, aScreen, aAlert); + } + return NS_OK; + } + + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsXULWindow::Repaint(bool aForce) +{ + //XXX First Check In + NS_ASSERTION(false, "Not Yet Implemented"); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetParentWidget(nsIWidget** aParentWidget) +{ + NS_ENSURE_ARG_POINTER(aParentWidget); + NS_ENSURE_STATE(mWindow); + + NS_IF_ADDREF(*aParentWidget = mWindow->GetParent()); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::SetParentWidget(nsIWidget* aParentWidget) +{ + //XXX First Check In + NS_ASSERTION(false, "Not Yet Implemented"); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetParentNativeWindow(nativeWindow* aParentNativeWindow) +{ + NS_ENSURE_ARG_POINTER(aParentNativeWindow); + + nsCOMPtr<nsIWidget> parentWidget; + NS_ENSURE_SUCCESS(GetParentWidget(getter_AddRefs(parentWidget)), NS_ERROR_FAILURE); + + if (parentWidget) { + *aParentNativeWindow = parentWidget->GetNativeData(NS_NATIVE_WIDGET); + } + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::SetParentNativeWindow(nativeWindow aParentNativeWindow) +{ + //XXX First Check In + NS_ASSERTION(false, "Not Yet Implemented"); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetNativeHandle(nsAString& aNativeHandle) +{ + nsCOMPtr<nsIWidget> mainWidget; + NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(mainWidget)), NS_ERROR_FAILURE); + + if (mainWidget) { + nativeWindow nativeWindowPtr = mainWidget->GetNativeData(NS_NATIVE_WINDOW); + /* the nativeWindow pointer is converted to and exposed as a string. This + is a more reliable way not to lose information (as opposed to JS + |Number| for instance) */ + aNativeHandle = NS_ConvertASCIItoUTF16(nsPrintfCString("0x%p", nativeWindowPtr)); + } + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetVisibility(bool* aVisibility) +{ + NS_ENSURE_ARG_POINTER(aVisibility); + + // Always claim to be visible for now. See bug + // https://bugzilla.mozilla.org/show_bug.cgi?id=306245. + + *aVisibility = true; + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::SetVisibility(bool aVisibility) +{ + if (!mChromeLoaded) { + mShowAfterLoad = aVisibility; + return NS_OK; + } + + if (mDebuting) { + return NS_OK; + } + mDebuting = true; // (Show / Focus is recursive) + + //XXXTAB Do we really need to show docshell and the window? Isn't + // the window good enough? + nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(mDocShell)); + shellAsWin->SetVisibility(aVisibility); + // Store locally so it doesn't die on us. 'Show' can result in the window + // being closed with nsXULWindow::Destroy being called. That would set + // mWindow to null and posibly destroy the nsIWidget while its Show method + // is on the stack. We need to keep it alive until Show finishes. + nsCOMPtr<nsIWidget> window = mWindow; + window->Show(aVisibility); + + nsCOMPtr<nsIWindowMediator> windowMediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (windowMediator) + windowMediator->UpdateWindowTimeStamp(static_cast<nsIXULWindow*>(this)); + + // notify observers so that we can hide the splash screen if possible + nsCOMPtr<nsIObserverService> obssvc = services::GetObserverService(); + NS_ASSERTION(obssvc, "Couldn't get observer service."); + if (obssvc) { + obssvc->NotifyObservers(nullptr, "xul-window-visible", nullptr); + } + + mDebuting = false; + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetEnabled(bool *aEnabled) +{ + NS_ENSURE_ARG_POINTER(aEnabled); + + if (mWindow) { + *aEnabled = mWindow->IsEnabled(); + return NS_OK; + } + + *aEnabled = true; // better guess than most + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsXULWindow::SetEnabled(bool aEnable) +{ + if (mWindow) { + mWindow->Enable(aEnable); + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsXULWindow::GetMainWidget(nsIWidget** aMainWidget) +{ + NS_ENSURE_ARG_POINTER(aMainWidget); + + *aMainWidget = mWindow; + NS_IF_ADDREF(*aMainWidget); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::SetFocus() +{ + //XXX First Check In + NS_ASSERTION(false, "Not Yet Implemented"); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetTitle(char16_t** aTitle) +{ + NS_ENSURE_ARG_POINTER(aTitle); + + *aTitle = ToNewUnicode(mTitle); + if (!*aTitle) + return NS_ERROR_OUT_OF_MEMORY; + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::SetTitle(const char16_t* aTitle) +{ + NS_ENSURE_STATE(mWindow); + mTitle.Assign(aTitle); + mTitle.StripChars("\n\r"); + NS_ENSURE_SUCCESS(mWindow->SetTitle(mTitle), NS_ERROR_FAILURE); + + // Tell the window mediator that a title has changed + nsCOMPtr<nsIWindowMediator> windowMediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (!windowMediator) + return NS_OK; + + windowMediator->UpdateWindowTitle(static_cast<nsIXULWindow*>(this), aTitle); + + return NS_OK; +} + + +//***************************************************************************** +// nsXULWindow: Helpers +//***************************************************************************** + +NS_IMETHODIMP nsXULWindow::EnsureChromeTreeOwner() +{ + if (mChromeTreeOwner) + return NS_OK; + + mChromeTreeOwner = new nsChromeTreeOwner(); + NS_ADDREF(mChromeTreeOwner); + mChromeTreeOwner->XULWindow(this); + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::EnsureContentTreeOwner() +{ + if (mContentTreeOwner) + return NS_OK; + + mContentTreeOwner = new nsContentTreeOwner(false); + NS_ADDREF(mContentTreeOwner); + mContentTreeOwner->XULWindow(this); + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::EnsurePrimaryContentTreeOwner() +{ + if (mPrimaryContentTreeOwner) + return NS_OK; + + mPrimaryContentTreeOwner = new nsContentTreeOwner(true); + NS_ADDREF(mPrimaryContentTreeOwner); + mPrimaryContentTreeOwner->XULWindow(this); + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::EnsurePrompter() +{ + if (mPrompter) + return NS_OK; + + nsCOMPtr<mozIDOMWindowProxy> ourWindow; + nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIWindowWatcher> wwatch = + do_GetService(NS_WINDOWWATCHER_CONTRACTID); + if (wwatch) + wwatch->GetNewPrompter(ourWindow, getter_AddRefs(mPrompter)); + } + return mPrompter ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsXULWindow::EnsureAuthPrompter() +{ + if (mAuthPrompter) + return NS_OK; + + nsCOMPtr<mozIDOMWindowProxy> ourWindow; + nsresult rv = GetWindowDOMWindow(getter_AddRefs(ourWindow)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID)); + if (wwatch) + wwatch->GetNewAuthPrompter(ourWindow, getter_AddRefs(mAuthPrompter)); + } + return mAuthPrompter ? NS_OK : NS_ERROR_FAILURE; +} + +void nsXULWindow::OnChromeLoaded() +{ + nsresult rv = EnsureContentTreeOwner(); + + if (NS_SUCCEEDED(rv)) { + mChromeLoaded = true; + ApplyChromeFlags(); + SyncAttributesToWidget(); + + int32_t specWidth = -1, specHeight = -1; + bool gotSize = false; + + if (!mIgnoreXULSize) { + gotSize = LoadSizeFromXUL(specWidth, specHeight); + } + + bool positionSet = !mIgnoreXULPosition; + nsCOMPtr<nsIXULWindow> parentWindow(do_QueryReferent(mParentWindow)); +#if defined(XP_UNIX) && !defined(XP_MACOSX) + // don't override WM placement on unix for independent, top-level windows + // (however, we think the benefits of intelligent dependent window placement + // trump that override.) + if (!parentWindow) + positionSet = false; +#endif + if (positionSet) { + // We have to do this before sizing the window, because sizing depends + // on the resolution of the screen we're on. But positioning needs to + // know the size so that it can constrain to screen bounds.... as an + // initial guess here, we'll use the specified size (if any). + positionSet = LoadPositionFromXUL(specWidth, specHeight); + } + + if (gotSize) { + SetSpecifiedSize(specWidth, specHeight); + } + + if (mIntrinsicallySized) { + // (if LoadSizeFromXUL set the size, mIntrinsicallySized will be false) + nsCOMPtr<nsIContentViewer> cv; + mDocShell->GetContentViewer(getter_AddRefs(cv)); + if (cv) { + nsCOMPtr<nsIDocShellTreeItem> docShellAsItem = do_QueryInterface(mDocShell); + nsCOMPtr<nsIDocShellTreeOwner> treeOwner; + docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner)); + if (treeOwner) { + // GetContentSize can fail, so initialise |width| and |height| to be + // on the safe side. + int32_t width = 0, height = 0; + if (NS_SUCCEEDED(cv->GetContentSize(&width, &height))) { + treeOwner->SizeShellTo(docShellAsItem, width, height); + // Update specified size for the final LoadPositionFromXUL call. + specWidth = width; + specHeight = height; + } + } + } + } + + // Now that we have set the window's final size, we can re-do its + // positioning so that it is properly constrained to the screen. + if (positionSet) { + LoadPositionFromXUL(specWidth, specHeight); + } + + LoadMiscPersistentAttributesFromXUL(); + + if (mCenterAfterLoad && !positionSet) { + Center(parentWindow, parentWindow ? false : true, false); + } + + if (mShowAfterLoad) { + SetVisibility(true); + // At this point the window may have been closed during Show(), so + // nsXULWindow::Destroy may already have been called. Take care! + } + } + mPersistentAttributesMask |= PAD_POSITION | PAD_SIZE | PAD_MISC; +} + +// If aSpecWidth and/or aSpecHeight are > 0, we will use these CSS px sizes +// to fit to the screen when staggering windows; if they're negative, +// we use the window's current size instead. +bool nsXULWindow::LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight) +{ + bool gotPosition = false; + + // if we're the hidden window, don't try to validate our size/position. We're + // special. + if (mIsHiddenWindow) + return false; + + nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement(); + NS_ENSURE_TRUE(windowElement, false); + + int32_t currX = 0; + int32_t currY = 0; + int32_t currWidth = 0; + int32_t currHeight = 0; + nsresult errorCode; + int32_t temp; + + GetPositionAndSize(&currX, &currY, &currWidth, &currHeight); + + // Convert to global display pixels for consistent window management across + // screens with diverse resolutions + double devToDesktopScale = 1.0 / mWindow->GetDesktopToDeviceScale().scale; + currX = NSToIntRound(currX * devToDesktopScale); + currY = NSToIntRound(currY * devToDesktopScale); + + // For size, use specified value if > 0, else current value + double devToCSSScale = 1.0 / mWindow->GetDefaultScale().scale; + int32_t cssWidth = + aSpecWidth > 0 ? aSpecWidth : NSToIntRound(currWidth * devToCSSScale); + int32_t cssHeight = + aSpecHeight > 0 ? aSpecHeight : NSToIntRound(currHeight * devToCSSScale); + + // Obtain the position information from the <xul:window> element. + int32_t specX = currX; + int32_t specY = currY; + nsAutoString posString; + + windowElement->GetAttribute(SCREENX_ATTRIBUTE, posString); + temp = posString.ToInteger(&errorCode); + if (NS_SUCCEEDED(errorCode)) { + specX = temp; + gotPosition = true; + } + windowElement->GetAttribute(SCREENY_ATTRIBUTE, posString); + temp = posString.ToInteger(&errorCode); + if (NS_SUCCEEDED(errorCode)) { + specY = temp; + gotPosition = true; + } + + if (gotPosition) { + // our position will be relative to our parent, if any + nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow)); + if (parent) { + int32_t parentX, parentY; + if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) { + double scale; + if (NS_SUCCEEDED(parent->GetDevicePixelsPerDesktopPixel(&scale))) { + parentX = NSToIntRound(parentX / scale); + parentY = NSToIntRound(parentY / scale); + } + specX += parentX; + specY += parentY; + } + } + else { + StaggerPosition(specX, specY, cssWidth, cssHeight); + } + } + mWindow->ConstrainPosition(false, &specX, &specY); + if (specX != currX || specY != currY) { + SetPositionDesktopPix(specX, specY); + } + + return gotPosition; +} + +bool +nsXULWindow::LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight) +{ + bool gotSize = false; + + // if we're the hidden window, don't try to validate our size/position. We're + // special. + if (mIsHiddenWindow) { + return false; + } + + nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement(); + NS_ENSURE_TRUE(windowElement, false); + + nsresult errorCode; + int32_t temp; + + // Obtain the sizing information from the <xul:window> element. + aSpecWidth = 100; + aSpecHeight = 100; + nsAutoString sizeString; + + windowElement->GetAttribute(WIDTH_ATTRIBUTE, sizeString); + temp = sizeString.ToInteger(&errorCode); + if (NS_SUCCEEDED(errorCode) && temp > 0) { + aSpecWidth = std::max(temp, 100); + gotSize = true; + } + windowElement->GetAttribute(HEIGHT_ATTRIBUTE, sizeString); + temp = sizeString.ToInteger(&errorCode); + if (NS_SUCCEEDED(errorCode) && temp > 0) { + aSpecHeight = std::max(temp, 100); + gotSize = true; + } + + return gotSize; +} + +void +nsXULWindow::SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight) +{ + // constrain to screen size + nsCOMPtr<mozIDOMWindowProxy> domWindow; + GetWindowDOMWindow(getter_AddRefs(domWindow)); + if (domWindow) { + auto* window = nsPIDOMWindowOuter::From(domWindow); + nsCOMPtr<nsIDOMScreen> screen = window->GetScreen(); + if (screen) { + int32_t screenWidth; + int32_t screenHeight; + screen->GetAvailWidth(&screenWidth); // CSS pixels + screen->GetAvailHeight(&screenHeight); + if (aSpecWidth > screenWidth) { + aSpecWidth = screenWidth; + } + if (aSpecHeight > screenHeight) { + aSpecHeight = screenHeight; + } + } + } + + NS_ASSERTION(mWindow, "we expected to have a window already"); + + int32_t currWidth = 0; + int32_t currHeight = 0; + GetSize(&currWidth, &currHeight); // returns device pixels + + // convert specified values to device pixels, and resize if needed + double cssToDevPx = mWindow ? mWindow->GetDefaultScale().scale : 1.0; + aSpecWidth = NSToIntRound(aSpecWidth * cssToDevPx); + aSpecHeight = NSToIntRound(aSpecHeight * cssToDevPx); + mIntrinsicallySized = false; + if (aSpecWidth != currWidth || aSpecHeight != currHeight) { + SetSize(aSpecWidth, aSpecHeight, false); + } +} + +/* Miscellaneous persistent attributes are attributes named in the + |persist| attribute, other than size and position. Those are special + because it's important to load those before one of the misc + attributes (sizemode) and they require extra processing. */ +bool nsXULWindow::LoadMiscPersistentAttributesFromXUL() +{ + bool gotState = false; + + /* There are no misc attributes of interest to the hidden window. + It's especially important not to try to validate that window's + size or position, because some platforms (Mac OS X) need to + make it visible and offscreen. */ + if (mIsHiddenWindow) + return false; + + nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement(); + NS_ENSURE_TRUE(windowElement, false); + + nsAutoString stateString; + + // sizemode + windowElement->GetAttribute(MODE_ATTRIBUTE, stateString); + nsSizeMode sizeMode = nsSizeMode_Normal; + /* ignore request to minimize, to not confuse novices + if (stateString.Equals(SIZEMODE_MINIMIZED)) + sizeMode = nsSizeMode_Minimized; + */ + if (!mIgnoreXULSizeMode && + (stateString.Equals(SIZEMODE_MAXIMIZED) || stateString.Equals(SIZEMODE_FULLSCREEN))) { + /* Honor request to maximize only if the window is sizable. + An unsizable, unmaximizable, yet maximized window confuses + Windows OS and is something of a travesty, anyway. */ + if (mChromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) { + mIntrinsicallySized = false; + + if (stateString.Equals(SIZEMODE_MAXIMIZED)) + sizeMode = nsSizeMode_Maximized; + else + sizeMode = nsSizeMode_Fullscreen; + } + } + + // If we are told to ignore the size mode attribute update the + // document so the attribute and window are in sync. + if (mIgnoreXULSizeMode) { + nsAutoString sizeString; + if (sizeMode == nsSizeMode_Maximized) + sizeString.Assign(SIZEMODE_MAXIMIZED); + else if (sizeMode == nsSizeMode_Fullscreen) + sizeString.Assign(SIZEMODE_FULLSCREEN); + else if (sizeMode == nsSizeMode_Normal) + sizeString.Assign(SIZEMODE_NORMAL); + if (!sizeString.IsEmpty()) { + ErrorResult rv; + windowElement->SetAttribute(MODE_ATTRIBUTE, sizeString, rv); + } + } + + if (sizeMode == nsSizeMode_Fullscreen) { + nsCOMPtr<mozIDOMWindowProxy> ourWindow; + GetWindowDOMWindow(getter_AddRefs(ourWindow)); + auto* piWindow = nsPIDOMWindowOuter::From(ourWindow); + piWindow->SetFullScreen(true); + } else { + mWindow->SetSizeMode(sizeMode); + } + gotState = true; + + // zlevel + windowElement->GetAttribute(ZLEVEL_ATTRIBUTE, stateString); + if (!stateString.IsEmpty()) { + nsresult errorCode; + int32_t zLevel = stateString.ToInteger(&errorCode); + if (NS_SUCCEEDED(errorCode) && zLevel >= lowestZ && zLevel <= highestZ) + SetZLevel(zLevel); + } + + return gotState; +} + +/* Stagger windows of the same type so they don't appear on top of each other. + This code does have a scary double loop -- it'll keep passing through + the entire list of open windows until it finds a non-collision. Doesn't + seem to be a problem, but it deserves watching. + The aRequested{X,Y} parameters here are in desktop pixels; + the aSpec{Width,Height} parameters are CSS pixel dimensions. +*/ +void nsXULWindow::StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY, + int32_t aSpecWidth, int32_t aSpecHeight) +{ + // These "constants" will be converted from CSS to desktop pixels + // for the appropriate screen, assuming we find a screen to use... + // hence they're not actually declared const here. + int32_t kOffset = 22; + uint32_t kSlop = 4; + + bool keepTrying; + int bouncedX = 0, // bounced off vertical edge of screen + bouncedY = 0; // bounced off horizontal edge + + // look for any other windows of this type + nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (!wm) + return; + + nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement(); + if (!windowElement) + return; + + nsCOMPtr<nsIXULWindow> ourXULWindow(this); + + nsAutoString windowType; + windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, windowType); + + int32_t screenTop = 0, // it's pointless to initialize these ... + screenRight = 0, // ... but to prevent oversalubrious and ... + screenBottom = 0, // ... underbright compilers from ... + screenLeft = 0; // ... issuing warnings. + bool gotScreen = false; + + { // fetch screen coordinates + nsCOMPtr<nsIScreenManager> screenMgr(do_GetService( + "@mozilla.org/gfx/screenmanager;1")); + if (screenMgr) { + nsCOMPtr<nsIScreen> ourScreen; + // the coordinates here are already display pixels + screenMgr->ScreenForRect(aRequestedX, aRequestedY, + aSpecWidth, aSpecHeight, + getter_AddRefs(ourScreen)); + if (ourScreen) { + int32_t screenWidth, screenHeight; + ourScreen->GetAvailRectDisplayPix(&screenLeft, &screenTop, + &screenWidth, &screenHeight); + screenBottom = screenTop + screenHeight; + screenRight = screenLeft + screenWidth; + // Get the screen's scaling factors and convert staggering constants + // from CSS px to desktop pixel units + double desktopToDeviceScale = 1.0, cssToDeviceScale = 1.0; + ourScreen->GetContentsScaleFactor(&desktopToDeviceScale); + ourScreen->GetDefaultCSSScaleFactor(&cssToDeviceScale); + double cssToDesktopFactor = cssToDeviceScale / desktopToDeviceScale; + kOffset = NSToIntRound(kOffset * cssToDesktopFactor); + kSlop = NSToIntRound(kSlop * cssToDesktopFactor); + // Convert dimensions from CSS to desktop pixels + aSpecWidth = NSToIntRound(aSpecWidth * cssToDesktopFactor); + aSpecHeight = NSToIntRound(aSpecHeight * cssToDesktopFactor); + gotScreen = true; + } + } + } + + // One full pass through all windows of this type, repeat until no collisions. + do { + keepTrying = false; + nsCOMPtr<nsISimpleEnumerator> windowList; + wm->GetXULWindowEnumerator(windowType.get(), getter_AddRefs(windowList)); + + if (!windowList) + break; + + // One full pass through all windows of this type, offset and stop on collision. + do { + bool more; + windowList->HasMoreElements(&more); + if (!more) + break; + + nsCOMPtr<nsISupports> supportsWindow; + windowList->GetNext(getter_AddRefs(supportsWindow)); + + nsCOMPtr<nsIXULWindow> listXULWindow(do_QueryInterface(supportsWindow)); + if (listXULWindow != ourXULWindow) { + int32_t listX, listY; + nsCOMPtr<nsIBaseWindow> listBaseWindow(do_QueryInterface(supportsWindow)); + listBaseWindow->GetPosition(&listX, &listY); + double scale; + if (NS_SUCCEEDED(listBaseWindow->GetDevicePixelsPerDesktopPixel(&scale))) { + listX = NSToIntRound(listX / scale); + listY = NSToIntRound(listY / scale); + } + + if (Abs(listX - aRequestedX) <= kSlop && Abs(listY - aRequestedY) <= kSlop) { + // collision! offset and start over + if (bouncedX & 0x1) + aRequestedX -= kOffset; + else + aRequestedX += kOffset; + aRequestedY += kOffset; + + if (gotScreen) { + // if we're moving to the right and we need to bounce... + if (!(bouncedX & 0x1) && ((aRequestedX + aSpecWidth) > screenRight)) { + aRequestedX = screenRight - aSpecWidth; + ++bouncedX; + } + + // if we're moving to the left and we need to bounce... + if ((bouncedX & 0x1) && aRequestedX < screenLeft) { + aRequestedX = screenLeft; + ++bouncedX; + } + + // if we hit the bottom then bounce to the top + if (aRequestedY + aSpecHeight > screenBottom) { + aRequestedY = screenTop; + ++bouncedY; + } + } + + /* loop around again, + but it's time to give up once we've covered the screen. + there's a potential infinite loop with lots of windows. */ + keepTrying = bouncedX < 2 || bouncedY == 0; + break; + } + } + } while(1); + } while (keepTrying); +} + +void nsXULWindow::SyncAttributesToWidget() +{ + nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement(); + if (!windowElement) + return; + + nsAutoString attr; + + // "hidechrome" attribute + if (windowElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidechrome, + nsGkAtoms::_true, eCaseMatters)) { + mWindow->HideWindowChrome(true); + } + + // "chromemargin" attribute + nsIntMargin margins; + windowElement->GetAttribute(NS_LITERAL_STRING("chromemargin"), attr); + if (nsContentUtils::ParseIntMarginValue(attr, margins)) { + LayoutDeviceIntMargin tmp = LayoutDeviceIntMargin::FromUnknownMargin(margins); + mWindow->SetNonClientMargins(tmp); + } + + // "windowtype" attribute + windowElement->GetAttribute(WINDOWTYPE_ATTRIBUTE, attr); + if (!attr.IsEmpty()) { + mWindow->SetWindowClass(attr); + } + + // "id" attribute for icon + windowElement->GetAttribute(NS_LITERAL_STRING("id"), attr); + if (attr.IsEmpty()) { + attr.AssignLiteral("default"); + } + mWindow->SetIcon(attr); + + // "drawtitle" attribute + windowElement->GetAttribute(NS_LITERAL_STRING("drawtitle"), attr); + mWindow->SetDrawsTitle(attr.LowerCaseEqualsLiteral("true")); + + // "toggletoolbar" attribute + windowElement->GetAttribute(NS_LITERAL_STRING("toggletoolbar"), attr); + mWindow->SetShowsToolbarButton(attr.LowerCaseEqualsLiteral("true")); + + // "fullscreenbutton" attribute + windowElement->GetAttribute(NS_LITERAL_STRING("fullscreenbutton"), attr); + mWindow->SetShowsFullScreenButton(attr.LowerCaseEqualsLiteral("true")); + + // "macanimationtype" attribute + windowElement->GetAttribute(NS_LITERAL_STRING("macanimationtype"), attr); + if (attr.EqualsLiteral("document")) { + mWindow->SetWindowAnimationType(nsIWidget::eDocumentWindowAnimation); + } +} + +NS_IMETHODIMP nsXULWindow::SavePersistentAttributes() +{ + // can happen when the persistence timer fires at an inopportune time + // during window shutdown + if (!mDocShell) + return NS_ERROR_FAILURE; + + nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement(); + if (!docShellElement) + return NS_ERROR_FAILURE; + + nsAutoString persistString; + docShellElement->GetAttribute(PERSIST_ATTRIBUTE, persistString); + if (persistString.IsEmpty()) { // quick check which sometimes helps + mPersistentAttributesDirty = 0; + return NS_OK; + } + + bool isFullscreen = false; + if (nsPIDOMWindowOuter* domWindow = mDocShell->GetWindow()) { + isFullscreen = domWindow->GetFullScreen(); + } + + // get our size, position and mode to persist + LayoutDeviceIntRect rect; + bool gotRestoredBounds = NS_SUCCEEDED(mWindow->GetRestoredBounds(rect)); + + // we use CSS pixels for size, but desktop pixels for position + CSSToLayoutDeviceScale sizeScale = mWindow->GetDefaultScale(); + DesktopToLayoutDeviceScale posScale = mWindow->GetDesktopToDeviceScale(); + + // make our position relative to our parent, if any + nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow)); + if (parent && gotRestoredBounds) { + int32_t parentX, parentY; + if (NS_SUCCEEDED(parent->GetPosition(&parentX, &parentY))) { + rect.x -= parentX; + rect.y -= parentY; + } + } + + char sizeBuf[10]; + nsAutoString sizeString; + nsAutoString windowElementId; + nsCOMPtr<nsIDOMXULDocument> ownerXULDoc; + + // fetch docShellElement's ID and XUL owner document + ownerXULDoc = do_QueryInterface(docShellElement->OwnerDoc()); + if (docShellElement->IsXULElement()) { + docShellElement->GetId(windowElementId); + } + + bool shouldPersist = !isFullscreen && ownerXULDoc; + ErrorResult rv; + // (only for size elements which are persisted) + if ((mPersistentAttributesDirty & PAD_POSITION) && gotRestoredBounds) { + if (persistString.Find("screenX") >= 0) { + SprintfLiteral(sizeBuf, "%d", NSToIntRound(rect.x / posScale.scale)); + sizeString.AssignWithConversion(sizeBuf); + docShellElement->SetAttribute(SCREENX_ATTRIBUTE, sizeString, rv); + if (shouldPersist) { + ownerXULDoc->Persist(windowElementId, SCREENX_ATTRIBUTE); + } + } + if (persistString.Find("screenY") >= 0) { + SprintfLiteral(sizeBuf, "%d", NSToIntRound(rect.y / posScale.scale)); + sizeString.AssignWithConversion(sizeBuf); + docShellElement->SetAttribute(SCREENY_ATTRIBUTE, sizeString, rv); + if (shouldPersist) { + ownerXULDoc->Persist(windowElementId, SCREENY_ATTRIBUTE); + } + } + } + + if ((mPersistentAttributesDirty & PAD_SIZE) && gotRestoredBounds) { + if (persistString.Find("width") >= 0) { + SprintfLiteral(sizeBuf, "%d", NSToIntRound(rect.width / sizeScale.scale)); + sizeString.AssignWithConversion(sizeBuf); + docShellElement->SetAttribute(WIDTH_ATTRIBUTE, sizeString, rv); + if (shouldPersist) { + ownerXULDoc->Persist(windowElementId, WIDTH_ATTRIBUTE); + } + } + if (persistString.Find("height") >= 0) { + SprintfLiteral(sizeBuf, "%d", NSToIntRound(rect.height / sizeScale.scale)); + sizeString.AssignWithConversion(sizeBuf); + docShellElement->SetAttribute(HEIGHT_ATTRIBUTE, sizeString, rv); + if (shouldPersist) { + ownerXULDoc->Persist(windowElementId, HEIGHT_ATTRIBUTE); + } + } + } + + if (mPersistentAttributesDirty & PAD_MISC) { + nsSizeMode sizeMode = mWindow->SizeMode(); + + if (sizeMode != nsSizeMode_Minimized) { + if (sizeMode == nsSizeMode_Maximized) + sizeString.Assign(SIZEMODE_MAXIMIZED); + else if (sizeMode == nsSizeMode_Fullscreen) + sizeString.Assign(SIZEMODE_FULLSCREEN); + else + sizeString.Assign(SIZEMODE_NORMAL); + docShellElement->SetAttribute(MODE_ATTRIBUTE, sizeString, rv); + if (shouldPersist && persistString.Find("sizemode") >= 0) { + ownerXULDoc->Persist(windowElementId, MODE_ATTRIBUTE); + } + } + if (persistString.Find("zlevel") >= 0) { + uint32_t zLevel; + nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (mediator) { + mediator->GetZLevel(this, &zLevel); + SprintfLiteral(sizeBuf, "%" PRIu32, zLevel); + sizeString.AssignWithConversion(sizeBuf); + docShellElement->SetAttribute(ZLEVEL_ATTRIBUTE, sizeString, rv); + if (shouldPersist) { + ownerXULDoc->Persist(windowElementId, ZLEVEL_ATTRIBUTE); + } + } + } + } + + mPersistentAttributesDirty = 0; + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow) +{ + NS_ENSURE_STATE(mDocShell); + + if (!mDOMWindow) + mDOMWindow = mDocShell->GetWindow(); + NS_ENSURE_TRUE(mDOMWindow, NS_ERROR_FAILURE); + + *aDOMWindow = mDOMWindow; + NS_ADDREF(*aDOMWindow); + return NS_OK; +} + +dom::Element* +nsXULWindow::GetWindowDOMElement() const +{ + NS_ENSURE_TRUE(mDocShell, nullptr); + + nsCOMPtr<nsIContentViewer> cv; + mDocShell->GetContentViewer(getter_AddRefs(cv)); + NS_ENSURE_TRUE(cv, nullptr); + + const nsIDocument* document = cv->GetDocument(); + NS_ENSURE_TRUE(document, nullptr); + + return document->GetRootElement(); +} + +nsresult nsXULWindow::ContentShellAdded(nsIDocShellTreeItem* aContentShell, + bool aPrimary, bool aTargetable, const nsAString& aID) +{ + nsContentShellInfo* shellInfo = nullptr; + + uint32_t i, count = mContentShells.Length(); + nsWeakPtr contentShellWeak = do_GetWeakReference(aContentShell); + for (i = 0; i < count; i++) { + nsContentShellInfo* info = mContentShells.ElementAt(i); + if (info->id.Equals(aID)) { + // We already exist. Do a replace. + info->child = contentShellWeak; + shellInfo = info; + } + else if (info->child == contentShellWeak) + info->child = nullptr; + } + + if (!shellInfo) { + shellInfo = new nsContentShellInfo(aID, contentShellWeak); + mContentShells.AppendElement(shellInfo); + } + + // Set the default content tree owner + if (aPrimary) { + NS_ENSURE_SUCCESS(EnsurePrimaryContentTreeOwner(), NS_ERROR_FAILURE); + aContentShell->SetTreeOwner(mPrimaryContentTreeOwner); + mPrimaryContentShell = aContentShell; + mPrimaryTabParent = nullptr; + } + else { + NS_ENSURE_SUCCESS(EnsureContentTreeOwner(), NS_ERROR_FAILURE); + aContentShell->SetTreeOwner(mContentTreeOwner); + if (mPrimaryContentShell == aContentShell) + mPrimaryContentShell = nullptr; + } + + if (aTargetable) { +#ifdef DEBUG + int32_t debugCount = mTargetableShells.Count(); + int32_t debugCounter; + for (debugCounter = debugCount - 1; debugCounter >= 0; --debugCounter) { + nsCOMPtr<nsIDocShellTreeItem> curItem = + do_QueryReferent(mTargetableShells[debugCounter]); + NS_ASSERTION(!SameCOMIdentity(curItem, aContentShell), + "Adding already existing item to mTargetableShells"); + } +#endif + + // put the new shell at the start of the targetable shells list if either + // it's the new primary shell or there is no existing primary shell (which + // means that chances are this one just stopped being primary). If we + // really cared, we could keep track of the "last no longer primary shell" + // explicitly, but it probably doesn't matter enough: the difference would + // only be felt in a situation where all shells were non-primary, which + // doesn't happen much. In a situation where there is one and only one + // primary shell, and in which shells get unmarked as primary before some + // other shell gets marked as primary, this effectively stores the list of + // targetable shells in "most recently primary first" order. + bool inserted; + if (aPrimary || !mPrimaryContentShell) { + inserted = mTargetableShells.InsertObjectAt(contentShellWeak, 0); + } else { + inserted = mTargetableShells.AppendObject(contentShellWeak); + } + NS_ENSURE_TRUE(inserted, NS_ERROR_OUT_OF_MEMORY); + } + + return NS_OK; +} + +nsresult nsXULWindow::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) +{ + if (mPrimaryContentShell == aContentShell) { + mPrimaryContentShell = nullptr; + } + + int32_t i, count = mContentShells.Length(); + for (i = count - 1; i >= 0; --i) { + nsContentShellInfo* info = mContentShells.ElementAt(i); + nsCOMPtr<nsIDocShellTreeItem> curItem = do_QueryReferent(info->child); + if (!curItem || SameCOMIdentity(curItem, aContentShell)) { + mContentShells.RemoveElementAt(i); + delete info; + } + } + + count = mTargetableShells.Count(); + for (i = count - 1; i >= 0; --i) { + nsCOMPtr<nsIDocShellTreeItem> curItem = + do_QueryReferent(mTargetableShells[i]); + if (!curItem || SameCOMIdentity(curItem, aContentShell)) { + mTargetableShells.RemoveObjectAt(i); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULWindow::GetPrimaryContentSize(int32_t* aWidth, + int32_t* aHeight) +{ + if (mPrimaryTabParent) { + return GetPrimaryTabParentSize(aWidth, aHeight); + } else if (mPrimaryContentShell) { + return GetPrimaryContentShellSize(aWidth, aHeight); + } + return NS_ERROR_UNEXPECTED; +} + +nsresult +nsXULWindow::GetPrimaryTabParentSize(int32_t* aWidth, + int32_t* aHeight) +{ + TabParent* tabParent = TabParent::GetFrom(mPrimaryTabParent); + // Need strong ref, since Client* can run script. + nsCOMPtr<Element> element = tabParent->GetOwnerElement(); + NS_ENSURE_STATE(element); + + *aWidth = element->ClientWidth(); + *aHeight = element->ClientHeight(); + return NS_OK; +} + +nsresult +nsXULWindow::GetPrimaryContentShellSize(int32_t* aWidth, + int32_t* aHeight) +{ + NS_ENSURE_STATE(mPrimaryContentShell); + + nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(mPrimaryContentShell)); + NS_ENSURE_STATE(shellWindow); + + int32_t devicePixelWidth, devicePixelHeight; + double shellScale = 1.0; + // We want to return CSS pixels. First, we get device pixels + // from the content area... + shellWindow->GetSize(&devicePixelWidth, &devicePixelHeight); + // And then get the device pixel scaling factor. Dividing device + // pixels by this scaling factor gives us CSS pixels. + shellWindow->GetUnscaledDevicePixelsPerCSSPixel(&shellScale); + *aWidth = NSToIntRound(devicePixelWidth / shellScale); + *aHeight = NSToIntRound(devicePixelHeight / shellScale); + return NS_OK; +} + +NS_IMETHODIMP +nsXULWindow::SetPrimaryContentSize(int32_t aWidth, + int32_t aHeight) +{ + if (mPrimaryTabParent) { + return SetPrimaryTabParentSize(aWidth, aHeight); + } else if (mPrimaryContentShell) { + return SizeShellTo(mPrimaryContentShell, aWidth, aHeight); + } + return NS_ERROR_UNEXPECTED; +} + +nsresult +nsXULWindow::SetPrimaryTabParentSize(int32_t aWidth, + int32_t aHeight) +{ + int32_t shellWidth, shellHeight; + GetPrimaryTabParentSize(&shellWidth, &shellHeight); + + double scale = 1.0; + GetUnscaledDevicePixelsPerCSSPixel(&scale); + + SizeShellToWithLimit(aWidth, aHeight, + shellWidth * scale, shellHeight * scale); + return NS_OK; +} + +nsresult +nsXULWindow::GetRootShellSize(int32_t* aWidth, + int32_t* aHeight) +{ + nsCOMPtr<nsIBaseWindow> shellAsWin = do_QueryInterface(mDocShell); + NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE); + return shellAsWin->GetSize(aWidth, aHeight); +} + +nsresult +nsXULWindow::SetRootShellSize(int32_t aWidth, + int32_t aHeight) +{ + nsCOMPtr<nsIDocShellTreeItem> docShellAsItem = do_QueryInterface(mDocShell); + return SizeShellTo(docShellAsItem, aWidth, aHeight); +} + +NS_IMETHODIMP nsXULWindow::SizeShellTo(nsIDocShellTreeItem* aShellItem, + int32_t aCX, int32_t aCY) +{ + // XXXTAB This is wrong, we should actually reflow based on the passed in + // shell. For now we are hacking and doing delta sizing. This is bad + // because it assumes all size we add will go to the shell which probably + // won't happen. + + nsCOMPtr<nsIBaseWindow> shellAsWin(do_QueryInterface(aShellItem)); + NS_ENSURE_TRUE(shellAsWin, NS_ERROR_FAILURE); + + int32_t width = 0; + int32_t height = 0; + shellAsWin->GetSize(&width, &height); + + SizeShellToWithLimit(aCX, aCY, width, height); + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::ExitModalLoop(nsresult aStatus) +{ + if (mContinueModalLoop) + EnableParent(true); + mContinueModalLoop = false; + mModalStatus = aStatus; + return NS_OK; +} + +// top-level function to create a new window +NS_IMETHODIMP nsXULWindow::CreateNewWindow(int32_t aChromeFlags, + nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpener, + nsIXULWindow **_retval) +{ + NS_ENSURE_ARG_POINTER(_retval); + + if (aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME) + return CreateNewChromeWindow(aChromeFlags, aOpeningTab, aOpener, _retval); + return CreateNewContentWindow(aChromeFlags, aOpeningTab, aOpener, _retval); +} + +NS_IMETHODIMP nsXULWindow::CreateNewChromeWindow(int32_t aChromeFlags, + nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpener, + nsIXULWindow **_retval) +{ + nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); + NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); + + // Just do a normal create of a window and return. + nsCOMPtr<nsIXULWindow> newWindow; + appShell->CreateTopLevelWindow(this, nullptr, aChromeFlags, + nsIAppShellService::SIZE_TO_CONTENT, + nsIAppShellService::SIZE_TO_CONTENT, + aOpeningTab, aOpener, + getter_AddRefs(newWindow)); + + NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE); + + *_retval = newWindow; + NS_ADDREF(*_retval); + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::CreateNewContentWindow(int32_t aChromeFlags, + nsITabParent *aOpeningTab, + mozIDOMWindowProxy *aOpener, + nsIXULWindow **_retval) +{ + nsCOMPtr<nsIAppShellService> appShell(do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); + NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); + + // We need to create a new top level window and then enter a nested + // loop. Eventually the new window will be told that it has loaded, + // at which time we know it is safe to spin out of the nested loop + // and allow the opening code to proceed. + + nsCOMPtr<nsIURI> uri; + + nsAdoptingCString urlStr = Preferences::GetCString("browser.chromeURL"); + if (urlStr.IsEmpty()) { + urlStr.AssignLiteral("chrome://navigator/content/navigator.xul"); + } + + nsCOMPtr<nsIIOService> service(do_GetService(NS_IOSERVICE_CONTRACTID)); + if (service) { + service->NewURI(urlStr, nullptr, nullptr, getter_AddRefs(uri)); + } + NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE); + + // We need to create a chrome window to contain the content window we're about + // to pass back. The subject principal needs to be system while we're creating + // it to make things work right, so force a system caller. See bug 799348 + // comment 13 for a description of what happens when we don't. + nsCOMPtr<nsIXULWindow> newWindow; + { + AutoNoJSAPI nojsapi; + // We actually want this toplevel window which we are creating to have a + // null opener, as we will be creating the content xul:browser window inside + // of it, so we pass nullptr as our aOpener. + appShell->CreateTopLevelWindow(this, uri, + aChromeFlags, 615, 480, + aOpeningTab, nullptr, + getter_AddRefs(newWindow)); + NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE); + } + + // Specify that we want the window to remain locked until the chrome has loaded. + nsXULWindow *xulWin = static_cast<nsXULWindow*> + (static_cast<nsIXULWindow*> + (newWindow)); + + if (aOpener) { + nsCOMPtr<nsIDocShell> docShell; + xulWin->GetDocShell(getter_AddRefs(docShell)); + MOZ_ASSERT(docShell); + nsCOMPtr<nsIDOMChromeWindow> chromeWindow = + do_QueryInterface(docShell->GetWindow()); + MOZ_ASSERT(chromeWindow); + + chromeWindow->SetOpenerForInitialContentBrowser(aOpener); + } + + xulWin->LockUntilChromeLoad(); + + { + AutoNoJSAPI nojsapi; + nsIThread *thread = NS_GetCurrentThread(); + while (xulWin->IsLocked()) { + if (!NS_ProcessNextEvent(thread)) + break; + } + } + + NS_ENSURE_STATE(xulWin->mPrimaryContentShell || xulWin->mPrimaryTabParent); + + *_retval = newWindow; + NS_ADDREF(*_retval); + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetHasPrimaryContent(bool* aResult) +{ + *aResult = mPrimaryTabParent || mPrimaryContentShell; + return NS_OK; +} + +void nsXULWindow::EnableParent(bool aEnable) +{ + nsCOMPtr<nsIBaseWindow> parentWindow; + nsCOMPtr<nsIWidget> parentWidget; + + parentWindow = do_QueryReferent(mParentWindow); + if (parentWindow) + parentWindow->GetMainWidget(getter_AddRefs(parentWidget)); + if (parentWidget) + parentWidget->Enable(aEnable); +} + +// Constrain the window to its proper z-level +bool nsXULWindow::ConstrainToZLevel(bool aImmediate, + nsWindowZ *aPlacement, + nsIWidget *aReqBelow, + nsIWidget **aActualBelow) +{ +#if 0 + /* Do we have a parent window? This means our z-order is already constrained, + since we're a dependent window. Our window list isn't hierarchical, + so we can't properly calculate placement for such a window. + Should we just abort? */ + nsCOMPtr<nsIBaseWindow> parentWindow = do_QueryReferent(mParentWindow); + if (parentWindow) + return false; +#endif + + nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (!mediator) + return false; + + bool altered; + uint32_t position, + newPosition, + zLevel; + nsIXULWindow *us = this; + + altered = false; + mediator->GetZLevel(this, &zLevel); + + // translate from WidgetGUIEvent to nsIWindowMediator constants + position = nsIWindowMediator::zLevelTop; + if (*aPlacement == nsWindowZBottom || zLevel == nsIXULWindow::lowestZ) + position = nsIWindowMediator::zLevelBottom; + else if (*aPlacement == nsWindowZRelative) + position = nsIWindowMediator::zLevelBelow; + + if (NS_SUCCEEDED(mediator->CalculateZPosition(us, position, aReqBelow, + &newPosition, aActualBelow, &altered))) { + /* If we were asked to move to the top but constrained to remain + below one of our other windows, first move all windows in that + window's layer and above to the top. This allows the user to + click a window which can't be topmost and still bring mozilla + to the foreground. */ + if (altered && + (position == nsIWindowMediator::zLevelTop || + (position == nsIWindowMediator::zLevelBelow && aReqBelow == 0))) + PlaceWindowLayersBehind(zLevel + 1, nsIXULWindow::highestZ, 0); + + if (*aPlacement != nsWindowZBottom && + position == nsIWindowMediator::zLevelBottom) + altered = true; + if (altered || aImmediate) { + if (newPosition == nsIWindowMediator::zLevelTop) + *aPlacement = nsWindowZTop; + else if (newPosition == nsIWindowMediator::zLevelBottom) + *aPlacement = nsWindowZBottom; + else + *aPlacement = nsWindowZRelative; + + if (aImmediate) { + nsCOMPtr<nsIBaseWindow> ourBase = do_QueryObject(this); + if (ourBase) { + nsCOMPtr<nsIWidget> ourWidget; + ourBase->GetMainWidget(getter_AddRefs(ourWidget)); + ourWidget->PlaceBehind(*aPlacement == nsWindowZBottom ? + eZPlacementBottom : eZPlacementBelow, + *aActualBelow, false); + } + } + } + + /* CalculateZPosition can tell us to be below nothing, because it tries + not to change something it doesn't recognize. A request to verify + being below an unrecognized window, then, is treated as a request + to come to the top (below null) */ + nsCOMPtr<nsIXULWindow> windowAbove; + if (newPosition == nsIWindowMediator::zLevelBelow && *aActualBelow) { + windowAbove = (*aActualBelow)->GetWidgetListener()->GetXULWindow(); + } + + mediator->SetZPosition(us, newPosition, windowAbove); + } + + return altered; +} + +/* Re-z-position all windows in the layers from aLowLevel to aHighLevel, + inclusive, to be behind aBehind. aBehind of null means on top. + Note this method actually does nothing to our relative window positions. + (And therefore there's no need to inform WindowMediator we're moving + things, because we aren't.) This method is useful for, say, moving + a range of layers of our own windows relative to windows belonging to + external applications. +*/ +void nsXULWindow::PlaceWindowLayersBehind(uint32_t aLowLevel, + uint32_t aHighLevel, + nsIXULWindow *aBehind) +{ + // step through windows in z-order from top to bottommost window + + nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID)); + if (!mediator) + return; + + nsCOMPtr<nsISimpleEnumerator> windowEnumerator; + mediator->GetZOrderXULWindowEnumerator(0, true, + getter_AddRefs(windowEnumerator)); + if (!windowEnumerator) + return; + + // each window will be moved behind previousHighWidget, itself + // a moving target. initialize it. + nsCOMPtr<nsIWidget> previousHighWidget; + if (aBehind) { + nsCOMPtr<nsIBaseWindow> highBase(do_QueryInterface(aBehind)); + if (highBase) + highBase->GetMainWidget(getter_AddRefs(previousHighWidget)); + } + + // get next lower window + bool more; + while (windowEnumerator->HasMoreElements(&more), more) { + uint32_t nextZ; // z-level of nextWindow + nsCOMPtr<nsISupports> nextWindow; + windowEnumerator->GetNext(getter_AddRefs(nextWindow)); + nsCOMPtr<nsIXULWindow> nextXULWindow(do_QueryInterface(nextWindow)); + nextXULWindow->GetZLevel(&nextZ); + if (nextZ < aLowLevel) + break; // we've processed all windows through aLowLevel + + // move it just below its next higher window + nsCOMPtr<nsIBaseWindow> nextBase(do_QueryInterface(nextXULWindow)); + if (nextBase) { + nsCOMPtr<nsIWidget> nextWidget; + nextBase->GetMainWidget(getter_AddRefs(nextWidget)); + if (nextZ <= aHighLevel) + nextWidget->PlaceBehind(eZPlacementBelow, previousHighWidget, false); + previousHighWidget = nextWidget; + } + } +} + +void nsXULWindow::SetContentScrollbarVisibility(bool aVisible) +{ + nsCOMPtr<nsPIDOMWindowOuter> contentWin(do_GetInterface(mPrimaryContentShell)); + if (!contentWin) { + return; + } + + nsContentUtils::SetScrollbarsVisibility(contentWin->GetDocShell(), aVisible); +} + +bool nsXULWindow::GetContentScrollbarVisibility() +{ + // This code already exists in dom/src/base/nsBarProp.cpp, but we + // can't safely get to that from here as this function is called + // while the DOM window is being set up, and we need the DOM window + // to get to that code. + nsCOMPtr<nsIScrollable> scroller(do_QueryInterface(mPrimaryContentShell)); + + if (scroller) { + int32_t prefValue; + scroller->GetDefaultScrollbarPreferences( + nsIScrollable::ScrollOrientation_Y, &prefValue); + if (prefValue == nsIScrollable::Scrollbar_Never) // try the other way + scroller->GetDefaultScrollbarPreferences( + nsIScrollable::ScrollOrientation_X, &prefValue); + + if (prefValue == nsIScrollable::Scrollbar_Never) + return false; + } + + return true; +} + +// during spinup, attributes that haven't been loaded yet can't be dirty +void nsXULWindow::PersistentAttributesDirty(uint32_t aDirtyFlags) +{ + mPersistentAttributesDirty |= aDirtyFlags & mPersistentAttributesMask; +} + +NS_IMETHODIMP nsXULWindow::ApplyChromeFlags() +{ + nsCOMPtr<dom::Element> window = GetWindowDOMElement(); + NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); + + if (mChromeLoaded) { + // The two calls in this block don't need to happen early because they + // don't cause a global restyle on the document. Not only that, but the + // scrollbar stuff needs a content area to toggle the scrollbars on anyway. + // So just don't do these until mChromeLoaded is true. + + // Scrollbars have their own special treatment. + SetContentScrollbarVisibility(mChromeFlags & + nsIWebBrowserChrome::CHROME_SCROLLBARS ? + true : false); + } + + /* the other flags are handled together. we have style rules + in navigator.css that trigger visibility based on + the 'chromehidden' attribute of the <window> tag. */ + nsAutoString newvalue; + + if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_MENUBAR)) + newvalue.AppendLiteral("menubar "); + + if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_TOOLBAR)) + newvalue.AppendLiteral("toolbar "); + + if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_LOCATIONBAR)) + newvalue.AppendLiteral("location "); + + if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR)) + newvalue.AppendLiteral("directories "); + + if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_STATUSBAR)) + newvalue.AppendLiteral("status "); + + if (! (mChromeFlags & nsIWebBrowserChrome::CHROME_EXTRA)) + newvalue.AppendLiteral("extrachrome "); + + // Note that if we're not actually changing the value this will be a no-op, + // so no need to compare to the old value. + ErrorResult rv; + window->SetAttribute(NS_LITERAL_STRING("chromehidden"), newvalue, rv); + + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::GetXULBrowserWindow(nsIXULBrowserWindow * *aXULBrowserWindow) +{ + NS_IF_ADDREF(*aXULBrowserWindow = mXULBrowserWindow); + return NS_OK; +} + +NS_IMETHODIMP nsXULWindow::SetXULBrowserWindow(nsIXULBrowserWindow * aXULBrowserWindow) +{ + mXULBrowserWindow = aXULBrowserWindow; + return NS_OK; +} + +void nsXULWindow::SizeShellToWithLimit(int32_t aDesiredWidth, + int32_t aDesiredHeight, + int32_t shellItemWidth, + int32_t shellItemHeight) +{ + int32_t widthDelta = aDesiredWidth - shellItemWidth; + int32_t heightDelta = aDesiredHeight - shellItemHeight; + + if (widthDelta || heightDelta) { + int32_t winWidth = 0; + int32_t winHeight = 0; + + GetSize(&winWidth, &winHeight); + // There's no point in trying to make the window smaller than the + // desired content area size --- that's not likely to work. This whole + // function assumes that the outer docshell is adding some constant + // "border" chrome to the content area. + winWidth = std::max(winWidth + widthDelta, aDesiredWidth); + winHeight = std::max(winHeight + heightDelta, aDesiredHeight); + SetSize(winWidth, winHeight, true); + } +} + +//***************************************************************************** +//*** nsContentShellInfo: Object Management +//***************************************************************************** + +nsContentShellInfo::nsContentShellInfo(const nsAString& aID, + nsIWeakReference* aContentShell) + : id(aID), + child(aContentShell) +{ + MOZ_COUNT_CTOR(nsContentShellInfo); +} + +nsContentShellInfo::~nsContentShellInfo() +{ + MOZ_COUNT_DTOR(nsContentShellInfo); + //XXX Set Tree Owner to null if the tree owner is nsXULWindow->mContentTreeOwner +} diff --git a/xpfe/appshell/nsXULWindow.h b/xpfe/appshell/nsXULWindow.h new file mode 100644 index 000000000..eb059c939 --- /dev/null +++ b/xpfe/appshell/nsXULWindow.h @@ -0,0 +1,199 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * 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/. */ + +#ifndef nsXULWindow_h__ +#define nsXULWindow_h__ + +// Local Includes +#include "nsChromeTreeOwner.h" +#include "nsContentTreeOwner.h" + +// Helper classes +#include "nsCOMPtr.h" +#include "nsTArray.h" +#include "nsString.h" +#include "nsWeakReference.h" +#include "nsCOMArray.h" +#include "nsRect.h" +#include "Units.h" + +// Interfaces needed +#include "nsIBaseWindow.h" +#include "nsIDocShell.h" +#include "nsIDocShellTreeItem.h" +#include "nsIDOMWindow.h" +#include "nsIInterfaceRequestor.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsIXULWindow.h" +#include "nsIPrompt.h" +#include "nsIAuthPrompt.h" +#include "nsIXULBrowserWindow.h" +#include "nsIWeakReference.h" +#include "nsIWidgetListener.h" +#include "nsITabParent.h" + +namespace mozilla { +namespace dom { +class Element; +} // namespace dom +} // namespace mozilla + +// nsXULWindow + +#define NS_XULWINDOW_IMPL_CID \ +{ /* 8eaec2f3-ed02-4be2-8e0f-342798477298 */ \ + 0x8eaec2f3, \ + 0xed02, \ + 0x4be2, \ + { 0x8e, 0x0f, 0x34, 0x27, 0x98, 0x47, 0x72, 0x98 } \ +} + +class nsContentShellInfo; + +class nsXULWindow : public nsIBaseWindow, + public nsIInterfaceRequestor, + public nsIXULWindow, + public nsSupportsWeakReference +{ +friend class nsChromeTreeOwner; +friend class nsContentTreeOwner; + +public: + NS_DECL_THREADSAFE_ISUPPORTS + + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSIXULWINDOW + NS_DECL_NSIBASEWINDOW + + NS_DECLARE_STATIC_IID_ACCESSOR(NS_XULWINDOW_IMPL_CID) + + void LockUntilChromeLoad() { mLockedUntilChromeLoad = true; } + bool IsLocked() const { return mLockedUntilChromeLoad; } + void IgnoreXULSizeMode(bool aEnable) { mIgnoreXULSizeMode = aEnable; } + void WasRegistered() { mRegistered = true; } + +protected: + enum persistentAttributes { + PAD_MISC = 0x1, + PAD_POSITION = 0x2, + PAD_SIZE = 0x4 + }; + + explicit nsXULWindow(uint32_t aChromeFlags); + virtual ~nsXULWindow(); + + NS_IMETHOD EnsureChromeTreeOwner(); + NS_IMETHOD EnsureContentTreeOwner(); + NS_IMETHOD EnsurePrimaryContentTreeOwner(); + NS_IMETHOD EnsurePrompter(); + NS_IMETHOD EnsureAuthPrompter(); + + void OnChromeLoaded(); + void StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY, + int32_t aSpecWidth, int32_t aSpecHeight); + bool LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight); + bool LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight); + void SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight); + bool LoadMiscPersistentAttributesFromXUL(); + void SyncAttributesToWidget(); + NS_IMETHOD SavePersistentAttributes(); + + NS_IMETHOD GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow); + mozilla::dom::Element* GetWindowDOMElement() const; + + // See nsIDocShellTreeOwner for docs on next two methods + nsresult ContentShellAdded(nsIDocShellTreeItem* aContentShell, + bool aPrimary, bool aTargetable, + const nsAString& aID); + nsresult ContentShellRemoved(nsIDocShellTreeItem* aContentShell); + NS_IMETHOD GetPrimaryContentSize(int32_t* aWidth, + int32_t* aHeight); + NS_IMETHOD SetPrimaryContentSize(int32_t aWidth, + int32_t aHeight); + nsresult GetRootShellSize(int32_t* aWidth, + int32_t* aHeight); + nsresult SetRootShellSize(int32_t aWidth, + int32_t aHeight); + + NS_IMETHOD SizeShellTo(nsIDocShellTreeItem* aShellItem, int32_t aCX, + int32_t aCY); + NS_IMETHOD ExitModalLoop(nsresult aStatus); + NS_IMETHOD CreateNewChromeWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, mozIDOMWindowProxy* aOpenerWindow, nsIXULWindow **_retval); + NS_IMETHOD CreateNewContentWindow(int32_t aChromeFlags, nsITabParent* aOpeningTab, mozIDOMWindowProxy* aOpenerWindow, nsIXULWindow **_retval); + NS_IMETHOD GetHasPrimaryContent(bool* aResult); + + void EnableParent(bool aEnable); + bool ConstrainToZLevel(bool aImmediate, nsWindowZ *aPlacement, + nsIWidget *aReqBelow, nsIWidget **aActualBelow); + void PlaceWindowLayersBehind(uint32_t aLowLevel, uint32_t aHighLevel, + nsIXULWindow *aBehind); + void SetContentScrollbarVisibility(bool aVisible); + bool GetContentScrollbarVisibility(); + void PersistentAttributesDirty(uint32_t aDirtyFlags); + + nsChromeTreeOwner* mChromeTreeOwner; + nsContentTreeOwner* mContentTreeOwner; + nsContentTreeOwner* mPrimaryContentTreeOwner; + nsCOMPtr<nsIWidget> mWindow; + nsCOMPtr<nsIDocShell> mDocShell; + nsCOMPtr<nsPIDOMWindowOuter> mDOMWindow; + nsCOMPtr<nsIWeakReference> mParentWindow; + nsCOMPtr<nsIPrompt> mPrompter; + nsCOMPtr<nsIAuthPrompt> mAuthPrompter; + nsCOMPtr<nsIXULBrowserWindow> mXULBrowserWindow; + nsCOMPtr<nsIDocShellTreeItem> mPrimaryContentShell; + nsTArray<nsContentShellInfo*> mContentShells; // array of doc shells by id + nsresult mModalStatus; + bool mContinueModalLoop; + bool mDebuting; // being made visible right now + bool mChromeLoaded; // True when chrome has loaded + bool mShowAfterLoad; + bool mIntrinsicallySized; + bool mCenterAfterLoad; + bool mIsHiddenWindow; + bool mLockedUntilChromeLoad; + bool mIgnoreXULSize; + bool mIgnoreXULPosition; + bool mChromeFlagsFrozen; + bool mIgnoreXULSizeMode; + // mDestroying is used to prevent reentry into into Destroy(), which can + // otherwise happen due to script running as we tear down various things. + bool mDestroying; + bool mRegistered; + uint32_t mContextFlags; + uint32_t mPersistentAttributesDirty; // persistentAttributes + uint32_t mPersistentAttributesMask; + uint32_t mChromeFlags; + nsString mTitle; + nsIntRect mOpenerScreenRect; // the screen rect of the opener + + nsCOMArray<nsIWeakReference> mTargetableShells; // targetable shells only + + nsCOMPtr<nsITabParent> mPrimaryTabParent; +private: + nsresult GetPrimaryTabParentSize(int32_t* aWidth, int32_t* aHeight); + nsresult GetPrimaryContentShellSize(int32_t* aWidth, int32_t* aHeight); + nsresult SetPrimaryTabParentSize(int32_t aWidth, int32_t aHeight); +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsXULWindow, NS_XULWINDOW_IMPL_CID) + +// nsContentShellInfo +// Used to map shell IDs to nsIDocShellTreeItems. + +class nsContentShellInfo +{ +public: + nsContentShellInfo(const nsAString& aID, + nsIWeakReference* aContentShell); + ~nsContentShellInfo(); + +public: + nsString id; // The identifier of the content shell + nsWeakPtr child; // content shell (weak reference to nsIDocShellTreeItem) +}; + +#endif /* nsXULWindow_h__ */ diff --git a/xpfe/appshell/test/chrome.ini b/xpfe/appshell/test/chrome.ini new file mode 100644 index 000000000..6f4b72829 --- /dev/null +++ b/xpfe/appshell/test/chrome.ini @@ -0,0 +1,5 @@ +[DEFAULT] +skip-if = os == 'android' + +[test_hiddenPrivateWindow.xul] +[test_windowlessBrowser.xul] diff --git a/xpfe/appshell/test/test_hiddenPrivateWindow.xul b/xpfe/appshell/test/test_hiddenPrivateWindow.xul new file mode 100644 index 000000000..a1dfb486d --- /dev/null +++ b/xpfe/appshell/test/test_hiddenPrivateWindow.xul @@ -0,0 +1,45 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=815847 +--> +<window title="Mozilla Bug 815847" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=815847">Mozilla Bug 815847</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +var Cu = Components.utils; + +Cu.import('resource://gre/modules/Services.jsm'); +Cu.import('resource://gre/modules/PrivateBrowsingUtils.jsm'); + +ok(Services.appShell.hiddenWindow, "hiddenWindow should exist"); +ok(Services.appShell.hiddenDOMWindow, "hiddenDOMWindow should exist"); +ok(Services.appShell.hiddenPrivateWindow, "hiddenPrivateWindow should exist"); +ok(Services.appShell.hiddenPrivateDOMWindow, "hiddenPrivateDOMWindow should exist"); + +ok(!PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenWindow.docShell), "hiddenWindow should not be private"); +ok(!PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenDOMWindow), "hiddenDOMWindow should not be private"); +ok(PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenPrivateWindow.docShell), "hiddenPrivateWindow should be private"); +ok(PrivateBrowsingUtils.isWindowPrivate(Services.appShell.hiddenPrivateDOMWindow), "hiddenPrivateDOMWindow should be private"); + +]]> +</script> + +</window> diff --git a/xpfe/appshell/test/test_windowlessBrowser.xul b/xpfe/appshell/test/test_windowlessBrowser.xul new file mode 100644 index 000000000..ef91f7655 --- /dev/null +++ b/xpfe/appshell/test/test_windowlessBrowser.xul @@ -0,0 +1,75 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" + type="text/css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=815847 +--> +<window title="Mozilla Bug 815847" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> + +<body xmlns="http://www.w3.org/1999/xhtml"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1214174">Mozilla Bug 1214174</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +</body> + +<script class="testbody" type="application/javascript"> +<![CDATA[ + +var Cu = Components.utils; +var Ci = Components.interfaces; + +Cu.import('resource://gre/modules/Services.jsm'); + +function testWindowlessBrowser(chromePrivileged) { + var webNav = Services.appShell.createWindowlessBrowser(chromePrivileged); + + ok(webNav, "createWindowlessBrowser should return a wevNav"); + + let interfaceRequestor = webNav.QueryInterface(Ci.nsIInterfaceRequestor); + let docShell = interfaceRequestor.getInterface(Ci.nsIDocShell); + + ok(docShell, "docShell should be defined"); + ok(docShell.contentViewer.DOMDocument.defaultView, "docShell defaultView should be defined"); + + var win = docShell.contentViewer.DOMDocument.defaultView; + + ok(win.screenX == 0, "window.screenX should be 0 in a windowless browser"); + ok(win.screenY == 0, "window.screenY should be 0 in a windowless browser"); + ok(win.outerWidth == 0, "window.outerWidth should be 0 in a windowless browser"); + ok(win.outerHeight == 0, "window.outerHeight should be 0 in a windowless browser"); + + ok(win.sidebar, "window.sidebar should be defined"); + ok(win.external, "window.external should be defined"); + + var exception; + + try { + win.external.AddSearchProvider("http://test-fake.url"); + } catch(e) { + exception = e; + } + + ok(!exception, "window.external.AddSearchProvider should be ignore withour raising an exception"); + + webNav.close(); +} + +info("Test Bug 1214174 on a content privileged windowless browser"); +testWindowlessBrowser(false); + +info("Test Bug 1214174 on a chrome privileged windowless browser"); +testWindowlessBrowser(true); + +]]> +</script> + +</window> |