summaryrefslogtreecommitdiffstats
path: root/xpfe/appshell/nsXULWindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpfe/appshell/nsXULWindow.cpp')
-rw-r--r--xpfe/appshell/nsXULWindow.cpp2334
1 files changed, 2334 insertions, 0 deletions
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
+}