summaryrefslogtreecommitdiffstats
path: root/xpfe/appshell
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /xpfe/appshell
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xpfe/appshell')
-rw-r--r--xpfe/appshell/AppWindow.cpp3498
-rw-r--r--xpfe/appshell/AppWindow.h398
-rw-r--r--xpfe/appshell/LiveResizeListener.h27
-rw-r--r--xpfe/appshell/components.conf25
-rw-r--r--xpfe/appshell/moz.build48
-rw-r--r--xpfe/appshell/nsAppShellCID.h10
-rw-r--r--xpfe/appshell/nsAppShellService.cpp918
-rw-r--r--xpfe/appshell/nsAppShellService.h53
-rw-r--r--xpfe/appshell/nsAppShellWindowEnumerator.cpp340
-rw-r--r--xpfe/appshell/nsAppShellWindowEnumerator.h133
-rw-r--r--xpfe/appshell/nsChromeTreeOwner.cpp480
-rw-r--r--xpfe/appshell/nsChromeTreeOwner.h51
-rw-r--r--xpfe/appshell/nsContentTreeOwner.cpp660
-rw-r--r--xpfe/appshell/nsContentTreeOwner.h61
-rw-r--r--xpfe/appshell/nsIAppShellService.idl128
-rw-r--r--xpfe/appshell/nsIAppWindow.idl167
-rw-r--r--xpfe/appshell/nsIWindowMediator.idl193
-rw-r--r--xpfe/appshell/nsIWindowMediatorListener.idl15
-rw-r--r--xpfe/appshell/nsIWindowlessBrowser.idl41
-rw-r--r--xpfe/appshell/nsIXULBrowserWindow.idl58
-rw-r--r--xpfe/appshell/nsWindowMediator.cpp749
-rw-r--r--xpfe/appshell/nsWindowMediator.h73
-rw-r--r--xpfe/appshell/test/chrome.ini4
-rw-r--r--xpfe/appshell/test/test_windowlessBrowser.xhtml67
24 files changed, 8197 insertions, 0 deletions
diff --git a/xpfe/appshell/AppWindow.cpp b/xpfe/appshell/AppWindow.cpp
new file mode 100644
index 0000000000..a5dab3b157
--- /dev/null
+++ b/xpfe/appshell/AppWindow.cpp
@@ -0,0 +1,3498 @@
+/* -*- 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 "ErrorList.h"
+#include "mozilla/MathAlgorithms.h"
+
+// Local includes
+#include "AppWindow.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/ProfilerLabels.h"
+#include "mozilla/Sprintf.h"
+
+// Interfaces needed to be included
+#include "nsGlobalWindowOuter.h"
+#include "nsIAppShell.h"
+#include "nsIAppShellService.h"
+#include "nsIContentViewer.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "nsPIDOMWindow.h"
+#include "nsScreen.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIIOService.h"
+#include "nsIObserverService.h"
+#include "nsIOpenWindowInfo.h"
+#include "nsIWindowMediator.h"
+#include "nsIScreenManager.h"
+#include "nsIScreen.h"
+#include "nsIWindowWatcher.h"
+#include "nsIURI.h"
+#include "nsAppShellCID.h"
+#include "nsReadableUtils.h"
+#include "nsStyleConsts.h"
+#include "nsPresContext.h"
+#include "nsContentUtils.h"
+#include "nsGlobalWindow.h"
+#include "nsXULTooltipListener.h"
+#include "nsXULPopupManager.h"
+#include "nsFocusManager.h"
+#include "nsContentList.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsServiceManagerUtils.h"
+
+#include "prenv.h"
+#include "mozilla/AppShutdown.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/Services.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/dom/BarProps.h"
+#include "mozilla/dom/DOMRect.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/BrowserHost.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/LoadURIOptionsBinding.h"
+#include "mozilla/intl/LocaleService.h"
+#include "mozilla/EventDispatcher.h"
+
+#ifdef XP_WIN
+# include "mozilla/PreXULSkeletonUI.h"
+# include "nsIWindowsUIUtils.h"
+#endif
+
+#include "mozilla/dom/DocumentL10n.h"
+
+#ifdef XP_MACOSX
+# include "mozilla/widget/NativeMenuSupport.h"
+# define USE_NATIVE_MENUS
+#endif
+
+#define SIZEMODE_NORMAL u"normal"_ns
+#define SIZEMODE_MAXIMIZED u"maximized"_ns
+#define SIZEMODE_MINIMIZED u"minimized"_ns
+#define SIZEMODE_FULLSCREEN u"fullscreen"_ns
+
+#define SIZE_PERSISTENCE_TIMEOUT 500 // msec
+
+//*****************************************************************************
+//*** AppWindow: Object Management
+//*****************************************************************************
+
+namespace mozilla {
+
+using dom::AutoNoJSAPI;
+using dom::BrowserHost;
+using dom::BrowsingContext;
+using dom::Document;
+using dom::DocumentL10n;
+using dom::Element;
+using dom::EventTarget;
+using dom::LoadURIOptions;
+using dom::Promise;
+
+AppWindow::AppWindow(uint32_t aChromeFlags)
+ : mChromeTreeOwner(nullptr),
+ mContentTreeOwner(nullptr),
+ mPrimaryContentTreeOwner(nullptr),
+ mModalStatus(NS_OK),
+ mFullscreenChangeState(FullscreenChangeState::NotChanging),
+ mContinueModalLoop(false),
+ mDebuting(false),
+ mChromeLoaded(false),
+ mSizingShellFromXUL(false),
+ mShowAfterLoad(false),
+ mIntrinsicallySized(false),
+ mCenterAfterLoad(false),
+ mIsHiddenWindow(false),
+ mLockedUntilChromeLoad(false),
+ mIgnoreXULSize(false),
+ mIgnoreXULPosition(false),
+ mChromeFlagsFrozen(false),
+ mIgnoreXULSizeMode(false),
+ mDestroying(false),
+ mRegistered(false),
+ mDominantClientSize(false),
+ mChromeFlags(aChromeFlags),
+ mWidgetListenerDelegate(this) {}
+
+AppWindow::~AppWindow() {
+ if (mSPTimer) {
+ mSPTimer->Cancel();
+ mSPTimer = nullptr;
+ }
+ Destroy();
+}
+
+//*****************************************************************************
+// AppWindow::nsISupports
+//*****************************************************************************
+
+NS_IMPL_ADDREF(AppWindow)
+NS_IMPL_RELEASE(AppWindow)
+
+NS_INTERFACE_MAP_BEGIN(AppWindow)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAppWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIAppWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+ NS_INTERFACE_MAP_ENTRY_CONCRETE(AppWindow)
+NS_INTERFACE_MAP_END
+
+nsresult AppWindow::Initialize(nsIAppWindow* aParent, nsIAppWindow* aOpener,
+ int32_t aInitialWidth, int32_t aInitialHeight,
+ bool aIsHiddenWindow,
+ widget::InitData& widgetInitData) {
+ nsresult rv;
+ nsCOMPtr<nsIWidget> parentWidget;
+
+ mIsHiddenWindow = aIsHiddenWindow;
+
+ DesktopIntPoint initialPos;
+ nsCOMPtr<nsIBaseWindow> base(do_QueryInterface(aOpener));
+ if (base) {
+ LayoutDeviceIntRect rect = base->GetPositionAndSize();
+ mOpenerScreenRect =
+ DesktopIntRect::Round(rect / base->DevicePixelsPerDesktopPixel());
+ if (!mOpenerScreenRect.IsEmpty()) {
+ initialPos = mOpenerScreenRect.TopLeft();
+ ConstrainToOpenerScreen(&initialPos.x.value, &initialPos.y.value);
+ }
+ }
+
+ // XXX: need to get the default window size from prefs...
+ // Doesn't come from prefs... will come from CSS/XUL/RDF
+ DesktopIntRect deskRect(initialPos,
+ DesktopIntSize(aInitialWidth, aInitialHeight));
+
+ // Create top level window
+ if (gfxPlatform::IsHeadless()) {
+ mWindow = nsIWidget::CreateHeadlessWidget();
+ } else {
+ mWindow = nsIWidget::CreateTopLevelWindow();
+ }
+ if (!mWindow) {
+ return NS_ERROR_FAILURE;
+ }
+
+ /* 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 AppWindow. 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));
+
+ // All Chrome BCs exist within the same BrowsingContextGroup, so we don't need
+ // to pass in the opener window here. The opener is set later, if needed, by
+ // nsWindowWatcher.
+ RefPtr<BrowsingContext> browsingContext =
+ BrowsingContext::CreateIndependent(BrowsingContext::Type::Chrome);
+
+ // Create web shell
+ mDocShell = nsDocShell::Create(browsingContext);
+ NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
+
+ // Make sure to set the item type on the docshell _before_ calling
+ // InitWindow() so it knows what type it is.
+ NS_ENSURE_SUCCESS(EnsureChromeTreeOwner(), NS_ERROR_FAILURE);
+
+ mDocShell->SetTreeOwner(mChromeTreeOwner);
+
+ r.MoveTo(0, 0);
+ NS_ENSURE_SUCCESS(mDocShell->InitWindow(nullptr, mWindow, r.X(), r.Y(),
+ r.Width(), r.Height()),
+ NS_ERROR_FAILURE);
+
+ // Attach a WebProgress listener.during initialization...
+ mDocShell->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_NETWORK);
+
+ mWindow->MaybeDispatchInitialFocusEvent();
+
+ return rv;
+}
+
+//*****************************************************************************
+// AppWindow::nsIIntefaceRequestor
+//*****************************************************************************
+
+NS_IMETHODIMP AppWindow::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(nsIWebBrowserChrome)) &&
+ NS_SUCCEEDED(EnsureContentTreeOwner()) &&
+ NS_SUCCEEDED(mContentTreeOwner->QueryInterface(aIID, aSink))) {
+ return NS_OK;
+ }
+
+ return QueryInterface(aIID, aSink);
+}
+
+//*****************************************************************************
+// AppWindow::nsIAppWindow
+//*****************************************************************************
+
+NS_IMETHODIMP AppWindow::GetDocShell(nsIDocShell** aDocShell) {
+ NS_ENSURE_ARG_POINTER(aDocShell);
+
+ *aDocShell = mDocShell;
+ NS_IF_ADDREF(*aDocShell);
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::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 AppWindow::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 > nsIAppWindow::normalZ && mWindow) {
+ nsSizeMode sizeMode = mWindow->SizeMode();
+ if (sizeMode == nsSizeMode_Maximized || sizeMode == nsSizeMode_Fullscreen) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ // do it
+ mediator->SetZLevel(this, aLevel);
+ PersistentAttributesDirty(PersistentAttribute::Misc, Sync);
+
+ nsCOMPtr<nsIContentViewer> cv;
+ mDocShell->GetContentViewer(getter_AddRefs(cv));
+ if (cv) {
+ RefPtr<dom::Document> doc = cv->GetDocument();
+ if (doc) {
+ ErrorResult rv;
+ RefPtr<dom::Event> event =
+ doc->CreateEvent(u"Events"_ns, dom::CallerType::System, rv);
+ if (event) {
+ event->InitEvent(u"windowZLevel"_ns, true, false);
+
+ event->SetTrusted(true);
+
+ doc->DispatchEvent(*event);
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::GetChromeFlags(uint32_t* aChromeFlags) {
+ NS_ENSURE_ARG_POINTER(aChromeFlags);
+ *aChromeFlags = mChromeFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::SetChromeFlags(uint32_t aChromeFlags) {
+ NS_ASSERTION(!mChromeFlagsFrozen,
+ "SetChromeFlags() after AssumeChromeFlagsAreFrozen()!");
+
+ mChromeFlags = aChromeFlags;
+ if (mChromeLoaded) {
+ ApplyChromeFlags();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::AssumeChromeFlagsAreFrozen() {
+ mChromeFlagsFrozen = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::SetIntrinsicallySized(bool aIntrinsicallySized) {
+ mIntrinsicallySized = aIntrinsicallySized;
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::GetIntrinsicallySized(bool* aIntrinsicallySized) {
+ NS_ENSURE_ARG_POINTER(aIntrinsicallySized);
+
+ *aIntrinsicallySized = mIntrinsicallySized;
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::GetPrimaryContentShell(
+ nsIDocShellTreeItem** aDocShellTreeItem) {
+ NS_ENSURE_ARG_POINTER(aDocShellTreeItem);
+ NS_IF_ADDREF(*aDocShellTreeItem = mPrimaryContentShell);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
+ if (aPrimary) {
+ mPrimaryBrowserParent = aTab;
+ mPrimaryContentShell = nullptr;
+ } else if (mPrimaryBrowserParent == aTab) {
+ mPrimaryBrowserParent = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::RemoteTabRemoved(nsIRemoteTab* aTab) {
+ if (aTab == mPrimaryBrowserParent) {
+ mPrimaryBrowserParent = nullptr;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
+ nsCOMPtr<nsIRemoteTab> tab = mPrimaryBrowserParent;
+ tab.forget(aTab);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::GetPrimaryContentBrowsingContext(
+ mozilla::dom::BrowsingContext** aBc) {
+ if (mPrimaryBrowserParent) {
+ return mPrimaryBrowserParent->GetBrowsingContext(aBc);
+ }
+ if (mPrimaryContentShell) {
+ return mPrimaryContentShell->GetBrowsingContextXPCOM(aBc);
+ }
+ *aBc = nullptr;
+ return NS_OK;
+}
+
+static LayoutDeviceIntSize GetOuterToInnerSizeDifference(nsIWidget* aWindow) {
+ if (!aWindow) {
+ return LayoutDeviceIntSize();
+ }
+ return aWindow->ClientToWindowSizeDifference();
+}
+
+static CSSIntSize GetOuterToInnerSizeDifferenceInCSSPixels(
+ nsIWidget* aWindow, CSSToLayoutDeviceScale aScale) {
+ LayoutDeviceIntSize devPixelSize = GetOuterToInnerSizeDifference(aWindow);
+ return RoundedToInt(devPixelSize / aScale);
+}
+
+NS_IMETHODIMP
+AppWindow::GetOuterToInnerHeightDifferenceInCSSPixels(uint32_t* aResult) {
+ *aResult = GetOuterToInnerSizeDifferenceInCSSPixels(
+ mWindow, UnscaledDevicePixelsPerCSSPixel())
+ .height;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::GetOuterToInnerWidthDifferenceInCSSPixels(uint32_t* aResult) {
+ *aResult = GetOuterToInnerSizeDifferenceInCSSPixels(
+ mWindow, UnscaledDevicePixelsPerCSSPixel())
+ .width;
+ return NS_OK;
+}
+
+nsTArray<RefPtr<mozilla::LiveResizeListener>>
+AppWindow::GetLiveResizeListeners() {
+ nsTArray<RefPtr<mozilla::LiveResizeListener>> listeners;
+ if (mPrimaryBrowserParent) {
+ BrowserHost* host = BrowserHost::GetFrom(mPrimaryBrowserParent.get());
+ RefPtr<mozilla::LiveResizeListener> actor = host->GetActor();
+ if (actor) {
+ listeners.AppendElement(actor);
+ }
+ }
+ return listeners;
+}
+
+NS_IMETHODIMP AppWindow::AddChildWindow(nsIAppWindow* aChild) {
+ // we're not really keeping track of this right now
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::RemoveChildWindow(nsIAppWindow* aChild) {
+ // we're not really keeping track of this right now
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::ShowModal() {
+ AUTO_PROFILER_LABEL("AppWindow::ShowModal", OTHER);
+
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ MOZ_ASSERT_UNREACHABLE(
+ "Trying to show modal window after shutdown started.");
+ return NS_ERROR_ILLEGAL_DURING_SHUTDOWN;
+ }
+
+ // Store locally so it doesn't die on us
+ nsCOMPtr<nsIWidget> window = mWindow;
+ nsCOMPtr<nsIAppWindow> tempRef = this;
+
+ window->SetModal(true);
+ mContinueModalLoop = true;
+ EnableParent(false);
+
+ {
+ AutoNoJSAPI nojsapi;
+ SpinEventLoopUntil("AppWindow::ShowModal"_ns, [&]() {
+ if (MOZ_UNLIKELY(
+ AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed))) {
+ // TODO: Bug 1699041 would apply also here: Should we return an error
+ // if we are bailing out from a pre-existing modal dialog for shutdown?
+ ExitModalLoop(NS_OK);
+ }
+ return !mContinueModalLoop;
+ });
+ }
+
+ 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;
+}
+
+//*****************************************************************************
+// AppWindow::nsIBaseWindow
+//*****************************************************************************
+
+NS_IMETHODIMP AppWindow::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 AppWindow::Destroy() {
+ nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
+
+ if (mDocShell) {
+ mDocShell->RemoveProgressListener(this);
+ }
+
+ if (mSPTimer) {
+ mSPTimer->Cancel();
+ SavePersistentAttributes();
+ mSPTimer = nullptr;
+ }
+
+ 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<nsIAppWindow*>(this));
+
+ nsCOMPtr<nsIAppWindow> parentWindow(do_QueryReferent(mParentWindow));
+ if (parentWindow) parentWindow->RemoveChildWindow(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()) {
+ bool isParentHiddenWindow = false;
+
+ if (appShell) {
+ bool hasHiddenWindow = false;
+ appShell->GetHasHiddenWindow(&hasHiddenWindow);
+ if (hasHiddenWindow) {
+ nsCOMPtr<nsIBaseWindow> baseHiddenWindow;
+ nsCOMPtr<nsIAppWindow> hiddenWindow;
+ appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow));
+ if (hiddenWindow) {
+ baseHiddenWindow = do_GetInterface(hiddenWindow);
+ isParentHiddenWindow = (baseHiddenWindow == parent);
+ }
+ }
+ }
+
+ // somebody screwed up somewhere. hiddenwindow shouldn't be anybody's
+ // parent. still, when it happens, skip activating it.
+ if (!isParentHiddenWindow) {
+ parentWidget->PlaceBehind(eZPlacementTop, 0, true);
+ }
+ }
+ }
+#endif
+
+ RemoveTooltipSupport();
+
+ mDOMWindow = nullptr;
+ if (mDocShell) {
+ RefPtr<BrowsingContext> bc(mDocShell->GetBrowsingContext());
+ mDocShell->Destroy();
+ bc->Detach();
+ mDocShell = nullptr; // this can cause reentrancy of this function
+ }
+
+ mPrimaryContentShell = nullptr;
+
+ if (mContentTreeOwner) {
+ mContentTreeOwner->AppWindow(nullptr);
+ NS_RELEASE(mContentTreeOwner);
+ }
+ if (mPrimaryContentTreeOwner) {
+ mPrimaryContentTreeOwner->AppWindow(nullptr);
+ NS_RELEASE(mPrimaryContentTreeOwner);
+ }
+ if (mChromeTreeOwner) {
+ mChromeTreeOwner->AppWindow(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 AppWindow::GetDevicePixelsPerDesktopPixel(double* aScale) {
+ *aScale = mWindow ? mWindow->GetDesktopToDeviceScale().scale : 1.0;
+ return NS_OK;
+}
+
+double AppWindow::GetWidgetCSSToDeviceScale() {
+ return mWindow ? mWindow->GetDefaultScale().scale : 1.0;
+}
+
+NS_IMETHODIMP AppWindow::SetPositionDesktopPix(int32_t aX, int32_t aY) {
+ return MoveResize(Some(DesktopIntPoint(aX, aY)), Nothing(), false);
+}
+
+// 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 AppWindow::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.
+ return MoveResize(Some(LayoutDeviceIntPoint(aX, aY)), Nothing(), false);
+}
+
+NS_IMETHODIMP AppWindow::GetPosition(int32_t* aX, int32_t* aY) {
+ return GetPositionAndSize(aX, aY, nullptr, nullptr);
+}
+
+NS_IMETHODIMP AppWindow::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. */
+ return MoveResize(Nothing(), Some(LayoutDeviceIntSize(aCX, aCY)), aRepaint);
+}
+
+NS_IMETHODIMP AppWindow::GetSize(int32_t* aCX, int32_t* aCY) {
+ return GetPositionAndSize(nullptr, nullptr, aCX, aCY);
+}
+
+NS_IMETHODIMP AppWindow::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. */
+ return MoveResize(Some(LayoutDeviceIntPoint(aX, aY)),
+ Some(LayoutDeviceIntSize(aCX, aCY)),
+ !!(aFlags & nsIBaseWindow::eRepaint));
+}
+
+NS_IMETHODIMP AppWindow::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
+AppWindow::SetDimensions(DimensionRequest&& aRequest) {
+ if (aRequest.mDimensionKind == DimensionKind::Inner) {
+ // For the chrome the inner size is the root shell size, and for the
+ // content it's the primary content size. We lack an indicator here that
+ // would allow us to distinguish between the two.
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ MOZ_TRY(aRequest.SupplementFrom(this));
+ return aRequest.ApplyOuterTo(this);
+}
+
+NS_IMETHODIMP
+AppWindow::GetDimensions(DimensionKind aDimensionKind, int32_t* aX, int32_t* aY,
+ int32_t* aCX, int32_t* aCY) {
+ if (aDimensionKind == DimensionKind::Inner) {
+ // For the chrome the inner size is the root shell size, and for the
+ // content it's the primary content size. We lack an indicator here that
+ // would allow us to distinguish between the two.
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ return GetPositionAndSize(aX, aY, aCX, aCY);
+}
+
+nsresult AppWindow::MoveResize(const Maybe<LayoutDeviceIntPoint>& aPosition,
+ const Maybe<LayoutDeviceIntSize>& aSize,
+ bool aRepaint) {
+ DesktopToLayoutDeviceScale scale = mWindow->GetDesktopToDeviceScale();
+
+ return MoveResize(aPosition ? Some(*aPosition / scale) : Nothing(),
+ aSize ? Some(*aSize / scale) : Nothing(), aRepaint);
+}
+
+nsresult AppWindow::MoveResize(const Maybe<DesktopPoint>& aPosition,
+ const Maybe<DesktopSize>& aSize, bool aRepaint) {
+ NS_ENSURE_STATE(mWindow);
+ PersistentAttributes dirtyAttributes;
+
+ if (!aPosition && !aSize) {
+ MOZ_ASSERT_UNREACHABLE("Doing nothing?");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (aSize) {
+ mWindow->SetSizeMode(nsSizeMode_Normal);
+ mIntrinsicallySized = false;
+ mDominantClientSize = false;
+ }
+
+ if (aPosition && aSize) {
+ mWindow->Resize(aPosition->x, aPosition->y, aSize->width, aSize->height,
+ aRepaint);
+ dirtyAttributes = {PersistentAttribute::Size,
+ PersistentAttribute::Position};
+ } else if (aSize) {
+ mWindow->Resize(aSize->width, aSize->height, aRepaint);
+ dirtyAttributes = {PersistentAttribute::Size};
+ } else if (aPosition) {
+ mWindow->Move(aPosition->x, aPosition->y);
+ dirtyAttributes = {PersistentAttribute::Position};
+ }
+
+ if (mSizingShellFromXUL) {
+ // If we're invoked for sizing from XUL, we want to neither ignore anything
+ // nor persist anything, since it's already the value in XUL.
+ return NS_OK;
+ }
+ 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
+ // position/size.
+ if (aPosition) {
+ mIgnoreXULPosition = true;
+ }
+ if (aSize) {
+ mIgnoreXULSize = true;
+ mIgnoreXULSizeMode = true;
+ }
+ return NS_OK;
+ }
+
+ PersistentAttributesDirty(dirtyAttributes, Sync);
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::Center(nsIAppWindow* aRelative, bool aScreen,
+ bool aAlert) {
+ DesktopIntRect rect;
+ 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));
+ if (base) {
+ rect = RoundedToInt(base->GetPositionAndSize() /
+ base->DevicePixelsPerDesktopPixel());
+ // if centering on screen, convert that to the corresponding screen
+ if (aScreen) {
+ screen = screenmgr->ScreenForRect(rect);
+ } else {
+ windowCoordinates = true;
+ }
+ }
+ }
+ if (!aRelative) {
+ if (!mOpenerScreenRect.IsEmpty()) {
+ screen = screenmgr->ScreenForRect(mOpenerScreenRect);
+ } else {
+ screenmgr->GetPrimaryScreen(getter_AddRefs(screen));
+ }
+ }
+
+ if (aScreen && screen) {
+ rect = screen->GetAvailRectDisplayPix();
+ screenCoordinates = true;
+ }
+
+ if (!screenCoordinates && !windowCoordinates) {
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ASSERTION(mWindow, "what, no window?");
+ const LayoutDeviceIntSize ourDevSize = GetSize();
+ const DesktopIntSize ourSize =
+ RoundedToInt(ourDevSize / DevicePixelsPerDesktopPixel());
+ auto newPos =
+ rect.TopLeft() +
+ DesktopIntPoint((rect.width - ourSize.width) / 2,
+ (rect.height - ourSize.height) / (aAlert ? 3 : 2));
+ if (windowCoordinates) {
+ mWindow->ConstrainPosition(newPos);
+ }
+
+ SetPositionDesktopPix(newPos.x, newPos.y);
+
+ // If moving the window caused it to change size, re-do the centering.
+ if (GetSize() != ourDevSize) {
+ return Center(aRelative, aScreen, aAlert);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::Repaint(bool aForce) {
+ // XXX First Check In
+ NS_ASSERTION(false, "Not Yet Implemented");
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::GetParentWidget(nsIWidget** aParentWidget) {
+ NS_ENSURE_ARG_POINTER(aParentWidget);
+ NS_ENSURE_STATE(mWindow);
+
+ NS_IF_ADDREF(*aParentWidget = mWindow->GetParent());
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::SetParentWidget(nsIWidget* aParentWidget) {
+ // XXX First Check In
+ NS_ASSERTION(false, "Not Yet Implemented");
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::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 AppWindow::SetParentNativeWindow(
+ nativeWindow aParentNativeWindow) {
+ // XXX First Check In
+ NS_ASSERTION(false, "Not Yet Implemented");
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::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 AppWindow::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 AppWindow::SetVisibility(bool aVisibility) {
+ if (!mChromeLoaded) {
+ mShowAfterLoad = aVisibility;
+ return NS_OK;
+ }
+
+ if (mDebuting) {
+ return NS_OK;
+ }
+
+ NS_ENSURE_STATE(mDocShell);
+
+ mDebuting = true; // (Show / Focus is recursive)
+
+ // XXXTAB Do we really need to show docshell and the window? Isn't
+ // the window good enough?
+ mDocShell->SetVisibility(aVisibility);
+ // Store locally so it doesn't die on us. 'Show' can result in the window
+ // being closed with AppWindow::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<nsIAppWindow*>(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(static_cast<nsIAppWindow*>(this),
+ "xul-window-visible", nullptr);
+ }
+
+ mDebuting = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::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 AppWindow::SetEnabled(bool aEnable) {
+ if (mWindow) {
+ mWindow->Enable(aEnable);
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP AppWindow::GetMainWidget(nsIWidget** aMainWidget) {
+ NS_ENSURE_ARG_POINTER(aMainWidget);
+ NS_IF_ADDREF(*aMainWidget = mWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::GetTitle(nsAString& aTitle) {
+ aTitle = mTitle;
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::SetTitle(const nsAString& aTitle) {
+ NS_ENSURE_STATE(mWindow);
+ mTitle.Assign(aTitle);
+ mTitle.StripCRLF();
+ NS_ENSURE_SUCCESS(mWindow->SetTitle(mTitle), NS_ERROR_FAILURE);
+ return NS_OK;
+}
+
+//*****************************************************************************
+// AppWindow: Helpers
+//*****************************************************************************
+
+NS_IMETHODIMP AppWindow::EnsureChromeTreeOwner() {
+ if (mChromeTreeOwner) return NS_OK;
+
+ mChromeTreeOwner = new nsChromeTreeOwner();
+ NS_ADDREF(mChromeTreeOwner);
+ mChromeTreeOwner->AppWindow(this);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::EnsureContentTreeOwner() {
+ if (mContentTreeOwner) return NS_OK;
+
+ mContentTreeOwner = new nsContentTreeOwner(false);
+ NS_ADDREF(mContentTreeOwner);
+ mContentTreeOwner->AppWindow(this);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::EnsurePrimaryContentTreeOwner() {
+ if (mPrimaryContentTreeOwner) return NS_OK;
+
+ mPrimaryContentTreeOwner = new nsContentTreeOwner(true);
+ NS_ADDREF(mPrimaryContentTreeOwner);
+ mPrimaryContentTreeOwner->AppWindow(this);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::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 AppWindow::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;
+}
+
+NS_IMETHODIMP AppWindow::GetAvailScreenSize(int32_t* aAvailWidth,
+ int32_t* aAvailHeight) {
+ nsCOMPtr<mozIDOMWindowProxy> domWindow;
+ GetWindowDOMWindow(getter_AddRefs(domWindow));
+ NS_ENSURE_STATE(domWindow);
+
+ auto* window = nsGlobalWindowOuter::Cast(domWindow);
+
+ RefPtr<nsScreen> screen = window->GetScreen();
+ NS_ENSURE_STATE(screen);
+
+ ErrorResult rv;
+ *aAvailWidth = screen->GetAvailWidth(rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return rv.StealNSResult();
+ }
+
+ *aAvailHeight = screen->GetAvailHeight(rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return rv.StealNSResult();
+ }
+
+ return NS_OK;
+}
+
+// Rounds window size to 1000x1000, or, if there isn't enough available
+// screen space, to a multiple of 200x100.
+NS_IMETHODIMP AppWindow::ForceRoundedDimensions() {
+ if (mIsHiddenWindow) {
+ return NS_OK;
+ }
+
+ CSSToLayoutDeviceScale scale = UnscaledDevicePixelsPerCSSPixel();
+
+ CSSIntSize availSizeCSS;
+ GetAvailScreenSize(&availSizeCSS.width, &availSizeCSS.height);
+
+ // To get correct chrome size, we have to resize the window to a proper
+ // size first. So, here, we size it to its available size.
+ SetSpecifiedSize(availSizeCSS.width, availSizeCSS.height);
+
+ // Get the current window size for calculating chrome UI size.
+ CSSIntSize windowSizeCSS = RoundedToInt(GetSize() / scale);
+
+ // Get the content size for calculating chrome UI size.
+ LayoutDeviceIntSize contentSizeDev;
+ GetPrimaryContentSize(&contentSizeDev.width, &contentSizeDev.height);
+ CSSIntSize contentSizeCSS = RoundedToInt(contentSizeDev / scale);
+
+ // Calculate the chrome UI size.
+ CSSIntSize chromeSizeCSS = windowSizeCSS - contentSizeCSS;
+
+ CSSIntSize targetSizeCSS;
+ // Here, we use the available screen dimensions as the input dimensions to
+ // force the window to be rounded as the maximum available content size.
+ nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
+ chromeSizeCSS.width, chromeSizeCSS.height, availSizeCSS.width,
+ availSizeCSS.height, availSizeCSS.width, availSizeCSS.height,
+ false, // aSetOuterWidth
+ false, // aSetOuterHeight
+ &targetSizeCSS.width, &targetSizeCSS.height);
+
+ LayoutDeviceIntSize targetSizeDev = RoundedToInt(targetSizeCSS * scale);
+
+ SetPrimaryContentSize(targetSizeDev.width, targetSizeDev.height);
+
+ return NS_OK;
+}
+
+void AppWindow::OnChromeLoaded() {
+ nsresult rv = EnsureContentTreeOwner();
+
+ if (NS_SUCCEEDED(rv)) {
+ mChromeLoaded = true;
+ ApplyChromeFlags();
+ SyncAttributesToWidget();
+ if (mWindow) {
+ SizeShell();
+ if (mShowAfterLoad) {
+ SetVisibility(true);
+ }
+ AddTooltipSupport();
+ }
+ // At this point the window may have been closed already during Show() or
+ // SyncAttributesToWidget(), so AppWindow::Destroy may already have been
+ // called. Take care!
+ }
+ mPersistentAttributesMask += AllPersistentAttributes();
+}
+
+bool AppWindow::NeedsTooltipListener() {
+ nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
+ if (!docShellElement || docShellElement->IsXULElement()) {
+ // Tooltips in XUL are handled by each element.
+ return false;
+ }
+ // All other non-XUL document types need a tooltip listener.
+ return true;
+}
+
+void AppWindow::AddTooltipSupport() {
+ if (!NeedsTooltipListener()) {
+ return;
+ }
+ nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
+ if (!listener) {
+ return;
+ }
+
+ nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
+ MOZ_ASSERT(docShellElement);
+ listener->AddTooltipSupport(docShellElement);
+}
+
+void AppWindow::RemoveTooltipSupport() {
+ if (!NeedsTooltipListener()) {
+ return;
+ }
+ nsXULTooltipListener* listener = nsXULTooltipListener::GetInstance();
+ if (!listener) {
+ return;
+ }
+
+ nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
+ MOZ_ASSERT(docShellElement);
+ listener->RemoveTooltipSupport(docShellElement);
+}
+
+static Maybe<int32_t> ReadIntAttribute(const Element& aElement,
+ nsAtom* aPrimary,
+ nsAtom* aSecondary = nullptr) {
+ nsAutoString attrString;
+ if (!aElement.GetAttr(aPrimary, attrString)) {
+ if (aSecondary) {
+ return ReadIntAttribute(aElement, aSecondary);
+ }
+ return Nothing();
+ }
+
+ nsresult res = NS_OK;
+ int32_t ret = attrString.ToInteger(&res);
+ return NS_SUCCEEDED(res) ? Some(ret) : Nothing();
+}
+
+// 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 AppWindow::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;
+ }
+
+ RefPtr<dom::Element> root = GetWindowDOMElement();
+ NS_ENSURE_TRUE(root, false);
+
+ const LayoutDeviceIntRect devRect = GetPositionAndSize();
+
+ // Convert to global display pixels for consistent window management across
+ // screens with diverse resolutions
+ const DesktopIntPoint curPoint =
+ RoundedToInt(devRect.TopLeft() / DevicePixelsPerDesktopPixel());
+
+ // For size, use specified value if > 0, else current value
+ CSSIntSize cssSize(aSpecWidth, aSpecHeight);
+ {
+ CSSIntSize currentSize =
+ RoundedToInt(devRect.Size() / UnscaledDevicePixelsPerCSSPixel());
+ if (aSpecHeight <= 0) {
+ cssSize.height = currentSize.height;
+ }
+ if (aSpecWidth <= 0) {
+ cssSize.width = currentSize.width;
+ }
+ }
+
+ // Obtain the position information from the <xul:window> element.
+ DesktopIntPoint specPoint = curPoint;
+
+ // Also read lowercase screenx/y because the front-end sometimes sets these
+ // via setAttribute on HTML documents like about:blank, and stuff gets
+ // lowercased.
+ //
+ // TODO(emilio): We should probably rename screenX/Y to screen-x/y to
+ // prevent this impedance mismatch.
+ if (auto attr =
+ ReadIntAttribute(*root, nsGkAtoms::screenX, nsGkAtoms::screenx)) {
+ specPoint.x = *attr;
+ gotPosition = true;
+ }
+
+ if (auto attr =
+ ReadIntAttribute(*root, nsGkAtoms::screenY, nsGkAtoms::screeny)) {
+ specPoint.y = *attr;
+ gotPosition = true;
+ }
+
+ if (gotPosition) {
+ // Our position will be relative to our parent, if any
+ nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
+ if (parent) {
+ const DesktopIntPoint parentPos = RoundedToInt(
+ parent->GetPosition() / parent->DevicePixelsPerDesktopPixel());
+ specPoint += parentPos;
+ } else {
+ StaggerPosition(specPoint.x.value, specPoint.y.value, cssSize.width,
+ cssSize.height);
+ }
+ }
+ mWindow->ConstrainPosition(specPoint);
+ if (specPoint != curPoint) {
+ SetPositionDesktopPix(specPoint.x, specPoint.y);
+ }
+
+ return gotPosition;
+}
+
+static Maybe<int32_t> ReadSize(const Element& aElement, nsAtom* aAttr,
+ nsAtom* aMinAttr, nsAtom* aMaxAttr) {
+ Maybe<int32_t> attr = ReadIntAttribute(aElement, aAttr);
+ if (!attr) {
+ return Nothing();
+ }
+
+ int32_t min =
+ std::max(100, ReadIntAttribute(aElement, aMinAttr).valueOr(100));
+ int32_t max = ReadIntAttribute(aElement, aMaxAttr)
+ .valueOr(std::numeric_limits<int32_t>::max());
+
+ return Some(std::min(max, std::max(*attr, min)));
+}
+
+bool AppWindow::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);
+
+ // Obtain the sizing information from the <xul:window> element.
+ aSpecWidth = 100;
+ aSpecHeight = 100;
+
+ if (auto width = ReadSize(*windowElement, nsGkAtoms::width,
+ nsGkAtoms::minwidth, nsGkAtoms::maxwidth)) {
+ aSpecWidth = *width;
+ gotSize = true;
+ }
+
+ if (auto height = ReadSize(*windowElement, nsGkAtoms::height,
+ nsGkAtoms::minheight, nsGkAtoms::maxheight)) {
+ aSpecHeight = *height;
+ gotSize = true;
+ }
+
+ return gotSize;
+}
+
+void AppWindow::SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight) {
+ // These are in CSS pixels of the main window.
+ // TODO(emilio): In my testing we usually have a pres context around, can we
+ // just use it? That'd simplify the coordinate calculations.
+ {
+ int32_t screenWidth;
+ int32_t screenHeight;
+
+ if (NS_SUCCEEDED(GetAvailScreenSize(&screenWidth, &screenHeight))) {
+ if (aSpecWidth > screenWidth) {
+ aSpecWidth = screenWidth;
+ }
+ if (aSpecHeight > screenHeight) {
+ aSpecHeight = screenHeight;
+ }
+ }
+ }
+
+ NS_ASSERTION(mWindow, "we expected to have a window already");
+
+ mIntrinsicallySized = false;
+
+ // Convert specified values to device pixels, and resize
+ auto newSize = RoundedToInt(CSSIntSize(aSpecWidth, aSpecHeight) *
+ UnscaledDevicePixelsPerCSSPixel());
+
+ // Note: Because of the asynchronous resizing on Linux we have to call
+ // SetSize even when the size doesn't appear to change. A previous call that
+ // has yet to complete can still change the size. We want the latest call to
+ // define the final size.
+ SetSize(newSize.width, newSize.height, 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 AppWindow::UpdateWindowStateFromMiscXULAttributes() {
+ 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;
+ nsSizeMode sizeMode = nsSizeMode_Normal;
+
+ // If we are told to ignore the size mode attribute, force
+ // normal sizemode.
+ if (mIgnoreXULSizeMode) {
+ windowElement->SetAttr(nsGkAtoms::sizemode, SIZEMODE_NORMAL,
+ IgnoreErrors());
+ } else {
+ // Otherwise, read sizemode from DOM and, if the window is resizable,
+ // set it later.
+ windowElement->GetAttr(nsGkAtoms::sizemode, stateString);
+ if ((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 (sizeMode == nsSizeMode_Fullscreen) {
+ nsCOMPtr<mozIDOMWindowProxy> ourWindow;
+ GetWindowDOMWindow(getter_AddRefs(ourWindow));
+ auto* piWindow = nsPIDOMWindowOuter::From(ourWindow);
+ piWindow->SetFullScreen(true);
+ } else {
+ // For maximized windows, ignore the XUL size and position attributes,
+ // as setting them would set the window back to normal sizemode.
+ if (sizeMode == nsSizeMode_Maximized) {
+ mIgnoreXULSize = true;
+ mIgnoreXULPosition = true;
+ }
+ mWindow->SetSizeMode(sizeMode);
+ }
+ gotState = true;
+
+ // zlevel
+ windowElement->GetAttr(nsGkAtoms::zlevel, 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 AppWindow::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<nsIAppWindow> ourAppWindow(this);
+
+ nsAutoString windowType;
+ windowElement->GetAttr(nsGkAtoms::windowtype, windowType);
+
+ DesktopIntRect screenRect;
+ 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
+ // XXX aSpecWidth and aSpecHeight are CSS pixels!
+ screenMgr->ScreenForRect(aRequestedX, aRequestedY, aSpecWidth,
+ aSpecHeight, getter_AddRefs(ourScreen));
+ if (ourScreen) {
+ screenRect = ourScreen->GetAvailRectDisplayPix();
+
+ // Get the screen's scaling factors and convert staggering constants
+ // from CSS px to desktop pixel units
+ auto scale = ourScreen->GetCSSToDesktopScale();
+ kOffset = (CSSCoord(kOffset) * scale).Rounded();
+ kSlop = (CSSCoord(kSlop) * scale).Rounded();
+ // Convert dimensions from CSS to desktop pixels
+ aSpecWidth = (CSSCoord(aSpecWidth) * scale).Rounded();
+ aSpecHeight = (CSSCoord(aSpecHeight) * scale).Rounded();
+ gotScreen = true;
+ }
+ }
+ }
+
+ // One full pass through all windows of this type, repeat until no collisions.
+ do {
+ keepTrying = false;
+ nsCOMPtr<nsISimpleEnumerator> windowList;
+ wm->GetAppWindowEnumerator(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<nsIAppWindow> listAppWindow(do_QueryInterface(supportsWindow));
+ if (listAppWindow != ourAppWindow) {
+ 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) > screenRect.XMost())) {
+ aRequestedX = screenRect.XMost() - aSpecWidth;
+ ++bouncedX;
+ }
+
+ // if we're moving to the left and we need to bounce...
+ if ((bouncedX & 0x1) && aRequestedX < screenRect.X()) {
+ aRequestedX = screenRect.X();
+ ++bouncedX;
+ }
+
+ // if we hit the bottom then bounce to the top
+ if (aRequestedY + aSpecHeight > screenRect.YMost()) {
+ aRequestedY = screenRect.Y();
+ ++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 (true);
+ } while (keepTrying);
+}
+
+void AppWindow::SyncAttributesToWidget() {
+ nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
+ if (!windowElement) return;
+
+ MOZ_DIAGNOSTIC_ASSERT(mWindow, "No widget on SyncAttributesToWidget?");
+
+ nsAutoString attr;
+
+ // Some attributes can change the client size (e.g. chromemargin on Windows
+ // and MacOS). But we might want to keep it.
+ const LayoutDeviceIntSize oldClientSize = mWindow->GetClientSize();
+ // We have to check now whether we want to restore the client size, as any
+ // change in size will reset its state.
+ bool maintainClientSize = mDominantClientSize;
+
+ // "hidechrome" attribute
+ if (windowElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidechrome,
+ nsGkAtoms::_true, eCaseMatters)) {
+ mWindow->HideWindowChrome(true);
+ }
+
+ NS_ENSURE_TRUE_VOID(mWindow);
+
+ // "chromemargin" attribute
+ nsIntMargin margins;
+ windowElement->GetAttribute(u"chromemargin"_ns, attr);
+ if (nsContentUtils::ParseIntMarginValue(attr, margins)) {
+ mWindow->SetNonClientMargins(
+ LayoutDeviceIntMargin::FromUnknownMargin(margins));
+ }
+
+ NS_ENSURE_TRUE_VOID(mWindow);
+
+ // "windowtype", "windowclass", "windowname" attributes
+ nsAutoString windowClassAttr, windowNameAttr;
+ windowElement->GetAttr(nsGkAtoms::windowtype, attr);
+ windowElement->GetAttribute(u"windowclass"_ns, windowClassAttr);
+ windowElement->GetAttribute(u"windowname"_ns, windowNameAttr);
+ mWindow->SetWindowClass(attr, windowClassAttr, windowNameAttr);
+
+ NS_ENSURE_TRUE_VOID(mWindow);
+
+ // "icon" attribute
+ windowElement->GetAttribute(u"icon"_ns, attr);
+ if (!attr.IsEmpty()) {
+ mWindow->SetIcon(attr);
+
+ NS_ENSURE_TRUE_VOID(mWindow);
+ }
+
+ // "drawtitle" attribute
+ windowElement->GetAttribute(u"drawtitle"_ns, attr);
+ mWindow->SetDrawsTitle(attr.LowerCaseEqualsLiteral("true"));
+
+ NS_ENSURE_TRUE_VOID(mWindow);
+
+ // "toggletoolbar" attribute
+ windowElement->GetAttribute(u"toggletoolbar"_ns, attr);
+ mWindow->SetShowsToolbarButton(attr.LowerCaseEqualsLiteral("true"));
+
+ NS_ENSURE_TRUE_VOID(mWindow);
+
+ // "macnativefullscreen" attribute
+ windowElement->GetAttribute(u"macnativefullscreen"_ns, attr);
+ mWindow->SetSupportsNativeFullscreen(attr.LowerCaseEqualsLiteral("true"));
+
+ NS_ENSURE_TRUE_VOID(mWindow);
+
+ // "macanimationtype" attribute
+ windowElement->GetAttribute(u"macanimationtype"_ns, attr);
+ if (attr.EqualsLiteral("document")) {
+ mWindow->SetWindowAnimationType(nsIWidget::eDocumentWindowAnimation);
+ }
+
+ // Check if the client size did change and if we want to restore it.
+ if (maintainClientSize && mWindow->SizeMode() == nsSizeMode_Normal &&
+ oldClientSize != mWindow->GetClientSize()) {
+ mWindow->ResizeClient(oldClientSize / mWindow->GetDesktopToDeviceScale(),
+ true);
+ mDominantClientSize = true;
+ }
+}
+
+enum class ConversionDirection {
+ InnerToOuter,
+ OuterToInner,
+};
+
+static void ConvertWindowSize(nsIAppWindow* aWin, const nsAtom* aAttr,
+ ConversionDirection aDirection,
+ nsAString& aInOutString) {
+ MOZ_ASSERT(aWin);
+ MOZ_ASSERT(aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height);
+
+ nsresult rv;
+ int32_t size = aInOutString.ToInteger(&rv);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ int32_t sizeDiff = aAttr == nsGkAtoms::width
+ ? aWin->GetOuterToInnerWidthDifferenceInCSSPixels()
+ : aWin->GetOuterToInnerHeightDifferenceInCSSPixels();
+
+ if (!sizeDiff) {
+ return;
+ }
+
+ int32_t multiplier = aDirection == ConversionDirection::InnerToOuter ? 1 : -1;
+
+ CopyASCIItoUTF16(nsPrintfCString("%d", size + multiplier * sizeDiff),
+ aInOutString);
+}
+
+nsresult AppWindow::GetPersistentValue(const nsAtom* aAttr, nsAString& aValue) {
+ nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
+ if (!docShellElement) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoString windowElementId;
+ docShellElement->GetId(windowElementId);
+ // Elements must have an ID to be persisted.
+ if (windowElementId.IsEmpty()) {
+ return NS_OK;
+ }
+
+ RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
+ nsIURI* docURI = ownerDoc->GetDocumentURI();
+ if (!docURI) {
+ return NS_ERROR_FAILURE;
+ }
+ nsAutoCString utf8uri;
+ nsresult rv = docURI->GetSpec(utf8uri);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ConvertUTF8toUTF16 uri(utf8uri);
+
+ if (!mLocalStore) {
+ mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
+ if (NS_WARN_IF(!mLocalStore)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ }
+
+ rv = mLocalStore->GetValue(uri, windowElementId, nsDependentAtomString(aAttr),
+ aValue);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
+ // Convert attributes from outer size to inner size for top-level
+ // windows, see bug 1444525 & co.
+ ConvertWindowSize(this, aAttr, ConversionDirection::OuterToInner, aValue);
+ }
+
+ return NS_OK;
+}
+
+nsresult AppWindow::GetDocXulStoreKeys(nsString& aUriSpec,
+ nsString& aWindowElementId) {
+ nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
+ if (!docShellElement) {
+ return NS_ERROR_FAILURE;
+ }
+
+ docShellElement->GetId(aWindowElementId);
+ // Match the behavior of XULPersist and only persist values if the element
+ // has an ID.
+ if (aWindowElementId.IsEmpty()) {
+ return NS_OK;
+ }
+
+ RefPtr<dom::Document> ownerDoc = docShellElement->OwnerDoc();
+ nsIURI* docURI = ownerDoc->GetDocumentURI();
+ if (!docURI) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString utf8uri;
+ nsresult rv = docURI->GetSpec(utf8uri);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ aUriSpec = NS_ConvertUTF8toUTF16(utf8uri);
+
+ return NS_OK;
+}
+
+nsresult AppWindow::MaybeSaveEarlyWindowPersistentValues(
+ const LayoutDeviceIntRect& aRect) {
+#ifdef XP_WIN
+ nsAutoString uri;
+ nsAutoString windowElementId;
+ nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!windowElementId.EqualsLiteral("main-window") ||
+ !uri.EqualsLiteral("chrome://browser/content/browser.xhtml")) {
+ return NS_OK;
+ }
+
+ SkeletonUISettings settings;
+
+ settings.screenX = aRect.X();
+ settings.screenY = aRect.Y();
+ settings.width = aRect.Width();
+ settings.height = aRect.Height();
+
+ settings.maximized = mWindow->SizeMode() == nsSizeMode_Maximized;
+ settings.cssToDevPixelScaling = UnscaledDevicePixelsPerCSSPixel().scale;
+
+ nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
+ Document* doc = windowElement->GetComposedDoc();
+ Element* urlbarEl = doc->GetElementById(u"urlbar"_ns);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow();
+ nsCOMPtr<nsIDOMWindowUtils> utils =
+ nsGlobalWindowOuter::Cast(window)->WindowUtils();
+ RefPtr<dom::DOMRect> urlbarRect;
+ rv = utils->GetBoundsWithoutFlushing(urlbarEl, getter_AddRefs(urlbarRect));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ double urlbarX = urlbarRect->X();
+ double urlbarWidth = urlbarRect->Width();
+
+ // Hard-coding the following values and this behavior in general is rather
+ // fragile, and can easily get out of sync with the actual front-end values.
+ // This is not intended as a long-term solution, but only as the relatively
+ // straightforward implementation of an experimental feature. If we want to
+ // ship the skeleton UI to all users, we should strongly consider a more
+ // robust solution than this. The vertical position of the urlbar will be
+ // fixed.
+ nsAutoString attributeValue;
+ urlbarEl->GetAttribute(u"breakout-extend"_ns, attributeValue);
+ // Scale down the urlbar if it is focused
+ if (attributeValue.EqualsLiteral("true")) {
+ // defined in browser.inc.css as 2px
+ int urlbarBreakoutExtend = 2;
+ // defined in urlbar-searchbar.inc.css as 5px
+ int urlbarMarginInline = 5;
+
+ // breakout-extend measurements are defined in urlbar-searchbar.inc.css
+ urlbarX += (double)(urlbarBreakoutExtend + urlbarMarginInline);
+ urlbarWidth -= (double)(2 * (urlbarBreakoutExtend + urlbarMarginInline));
+ }
+ CSSPixelSpan urlbar;
+ urlbar.start = urlbarX;
+ urlbar.end = urlbar.start + urlbarWidth;
+ settings.urlbarSpan = urlbar;
+
+ Element* navbar = doc->GetElementById(u"nav-bar"_ns);
+
+ Element* searchbarEl = doc->GetElementById(u"searchbar"_ns);
+ CSSPixelSpan searchbar;
+ if (navbar->Contains(searchbarEl)) {
+ RefPtr<dom::DOMRect> searchbarRect;
+ rv = utils->GetBoundsWithoutFlushing(searchbarEl,
+ getter_AddRefs(searchbarRect));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ searchbar.start = searchbarRect->X();
+ searchbar.end = searchbar.start + searchbarRect->Width();
+ } else {
+ // There is no searchbar in the UI
+ searchbar.start = 0;
+ searchbar.end = 0;
+ }
+ settings.searchbarSpan = searchbar;
+
+ nsAutoString bookmarksVisibility;
+ Preferences::GetString("browser.toolbars.bookmarks.visibility",
+ bookmarksVisibility);
+ settings.bookmarksToolbarShown =
+ bookmarksVisibility.EqualsLiteral("always") ||
+ bookmarksVisibility.EqualsLiteral("newtab");
+
+ Element* menubar = doc->GetElementById(u"toolbar-menubar"_ns);
+ menubar->GetAttribute(u"autohide"_ns, attributeValue);
+ settings.menubarShown = attributeValue.EqualsLiteral("false");
+
+ ErrorResult err;
+ nsCOMPtr<nsIHTMLCollection> toolbarSprings = navbar->GetElementsByTagNameNS(
+ u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns,
+ u"toolbarspring"_ns, err);
+ if (err.Failed()) {
+ return NS_ERROR_FAILURE;
+ }
+ mozilla::Vector<CSSPixelSpan> springs;
+ for (size_t i = 0; i < toolbarSprings->Length(); i++) {
+ RefPtr<Element> springEl = toolbarSprings->Item(i);
+ RefPtr<dom::DOMRect> springRect;
+ rv = utils->GetBoundsWithoutFlushing(springEl, getter_AddRefs(springRect));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ CSSPixelSpan spring;
+ spring.start = springRect->X();
+ spring.end = spring.start + springRect->Width();
+ if (!settings.springs.append(spring)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ settings.rtlEnabled = intl::LocaleService::GetInstance()->IsAppLocaleRTL();
+
+ bool isInTabletMode = false;
+ bool autoTouchModePref =
+ Preferences::GetBool("browser.touchmode.auto", false);
+ if (autoTouchModePref) {
+ nsCOMPtr<nsIWindowsUIUtils> uiUtils(
+ do_GetService("@mozilla.org/windows-ui-utils;1"));
+ if (!NS_WARN_IF(!uiUtils)) {
+ uiUtils->GetInTabletMode(&isInTabletMode);
+ }
+ }
+
+ if (isInTabletMode) {
+ settings.uiDensity = SkeletonUIDensity::Touch;
+ } else {
+ int uiDensityPref = Preferences::GetInt("browser.uidensity", 0);
+ switch (uiDensityPref) {
+ case 0: {
+ settings.uiDensity = SkeletonUIDensity::Default;
+ break;
+ }
+ case 1: {
+ settings.uiDensity = SkeletonUIDensity::Compact;
+ break;
+ }
+ case 2: {
+ settings.uiDensity = SkeletonUIDensity::Touch;
+ break;
+ }
+ }
+ }
+
+ Unused << PersistPreXULSkeletonUIValues(settings);
+#endif
+
+ return NS_OK;
+}
+
+nsresult AppWindow::SetPersistentValue(const nsAtom* aAttr,
+ const nsAString& aValue) {
+ nsAutoString uri;
+ nsAutoString windowElementId;
+ nsresult rv = GetDocXulStoreKeys(uri, windowElementId);
+
+ if (NS_FAILED(rv) || windowElementId.IsEmpty()) {
+ return rv;
+ }
+
+ nsAutoString maybeConvertedValue(aValue);
+ if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
+ // Make sure we store the <window> attributes as outer window size, see
+ // bug 1444525 & co.
+ ConvertWindowSize(this, aAttr, ConversionDirection::InnerToOuter,
+ maybeConvertedValue);
+ }
+
+ if (!mLocalStore) {
+ mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
+ if (NS_WARN_IF(!mLocalStore)) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ }
+
+ return mLocalStore->SetValue(
+ uri, windowElementId, nsDependentAtomString(aAttr), maybeConvertedValue);
+}
+
+void AppWindow::MaybeSavePersistentPositionAndSize(
+ PersistentAttributes aAttributes, Element& aRootElement,
+ const nsAString& aPersistString, bool aShouldPersist) {
+ if ((aAttributes & PersistentAttributes{PersistentAttribute::Position,
+ PersistentAttribute::Size})
+ .isEmpty()) {
+ return;
+ }
+
+ // get our size, position and mode to persist
+ LayoutDeviceIntRect rect;
+ if (NS_FAILED(mWindow->GetRestoredBounds(rect))) {
+ return;
+ }
+
+ // we use CSS pixels for size, but desktop pixels for position
+ CSSToLayoutDeviceScale sizeScale = UnscaledDevicePixelsPerCSSPixel();
+ DesktopToLayoutDeviceScale posScale = DevicePixelsPerDesktopPixel();
+
+ // make our position 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))) {
+ rect.MoveBy(-parentX, -parentY);
+ }
+ }
+
+ nsAutoString sizeString;
+ // (only for size elements which are persisted)
+ if (aAttributes.contains(PersistentAttribute::Position)) {
+ if (aPersistString.Find(u"screenX") >= 0) {
+ sizeString.Truncate();
+ sizeString.AppendInt(NSToIntRound(rect.X() / posScale.scale));
+ aRootElement.SetAttr(nsGkAtoms::screenX, sizeString, IgnoreErrors());
+ if (aShouldPersist) {
+ Unused << SetPersistentValue(nsGkAtoms::screenX, sizeString);
+ }
+ }
+ if (aPersistString.Find(u"screenY") >= 0) {
+ sizeString.Truncate();
+ sizeString.AppendInt(NSToIntRound(rect.Y() / posScale.scale));
+ aRootElement.SetAttr(nsGkAtoms::screenY, sizeString, IgnoreErrors());
+ if (aShouldPersist) {
+ Unused << SetPersistentValue(nsGkAtoms::screenY, sizeString);
+ }
+ }
+ }
+
+ if (aAttributes.contains(PersistentAttribute::Size)) {
+ LayoutDeviceIntRect innerRect =
+ rect - GetOuterToInnerSizeDifference(mWindow);
+ if (aPersistString.Find(u"width") >= 0) {
+ sizeString.Truncate();
+ sizeString.AppendInt(NSToIntRound(innerRect.Width() / sizeScale.scale));
+ aRootElement.SetAttr(nsGkAtoms::width, sizeString, IgnoreErrors());
+ if (aShouldPersist) {
+ Unused << SetPersistentValue(nsGkAtoms::width, sizeString);
+ }
+ }
+ if (aPersistString.Find(u"height") >= 0) {
+ sizeString.Truncate();
+ sizeString.AppendInt(NSToIntRound(innerRect.Height() / sizeScale.scale));
+ aRootElement.SetAttr(nsGkAtoms::height, sizeString, IgnoreErrors());
+ if (aShouldPersist) {
+ Unused << SetPersistentValue(nsGkAtoms::height, sizeString);
+ }
+ }
+ }
+
+ Unused << MaybeSaveEarlyWindowPersistentValues(rect);
+}
+
+void AppWindow::MaybeSavePersistentMiscAttributes(
+ PersistentAttributes aAttributes, Element& aRootElement,
+ const nsAString& aPersistString, bool aShouldPersist) {
+ if (!aAttributes.contains(PersistentAttribute::Misc)) {
+ return;
+ }
+
+ nsSizeMode sizeMode = mWindow->SizeMode();
+ nsAutoString sizeString;
+ 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);
+ }
+ aRootElement.SetAttr(nsGkAtoms::sizemode, sizeString, IgnoreErrors());
+ if (aShouldPersist && aPersistString.Find(u"sizemode") >= 0) {
+ Unused << SetPersistentValue(nsGkAtoms::sizemode, sizeString);
+ }
+ }
+ aRootElement.SetAttribute(u"gtktiledwindow"_ns,
+ mWindow->IsTiled() ? u"true"_ns : u"false"_ns,
+ IgnoreErrors());
+ if (aPersistString.Find(u"zlevel") >= 0) {
+ uint32_t zLevel;
+ nsCOMPtr<nsIWindowMediator> mediator(
+ do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (mediator) {
+ mediator->GetZLevel(this, &zLevel);
+ sizeString.Truncate();
+ sizeString.AppendInt(zLevel);
+ aRootElement.SetAttr(nsGkAtoms::zlevel, sizeString, IgnoreErrors());
+ if (aShouldPersist) {
+ Unused << SetPersistentValue(nsGkAtoms::zlevel, sizeString);
+ }
+ }
+ }
+}
+
+void AppWindow::SavePersistentAttributes(
+ const PersistentAttributes aAttributes) {
+ // can happen when the persistence timer fires at an inopportune time
+ // during window shutdown
+ if (!mDocShell) {
+ return;
+ }
+
+ nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
+ if (!docShellElement) {
+ return;
+ }
+
+ nsAutoString persistString;
+ docShellElement->GetAttr(nsGkAtoms::persist, persistString);
+ if (persistString.IsEmpty()) { // quick check which sometimes helps
+ mPersistentAttributesDirty.clear();
+ return;
+ }
+
+ bool shouldPersist = mWindow->SizeMode() != nsSizeMode_Fullscreen;
+ MaybeSavePersistentPositionAndSize(aAttributes, *docShellElement,
+ persistString, shouldPersist);
+ MaybeSavePersistentMiscAttributes(aAttributes, *docShellElement,
+ persistString, shouldPersist);
+ mPersistentAttributesDirty -= aAttributes;
+}
+
+NS_IMETHODIMP AppWindow::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* AppWindow::GetWindowDOMElement() const {
+ NS_ENSURE_TRUE(mDocShell, nullptr);
+
+ nsCOMPtr<nsIContentViewer> cv;
+ mDocShell->GetContentViewer(getter_AddRefs(cv));
+ NS_ENSURE_TRUE(cv, nullptr);
+
+ const dom::Document* document = cv->GetDocument();
+ NS_ENSURE_TRUE(document, nullptr);
+
+ return document->GetRootElement();
+}
+
+nsresult AppWindow::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
+ bool aPrimary) {
+ // Set the default content tree owner
+ if (aPrimary) {
+ NS_ENSURE_SUCCESS(EnsurePrimaryContentTreeOwner(), NS_ERROR_FAILURE);
+ aContentShell->SetTreeOwner(mPrimaryContentTreeOwner);
+ mPrimaryContentShell = aContentShell;
+ mPrimaryBrowserParent = nullptr;
+ } else {
+ NS_ENSURE_SUCCESS(EnsureContentTreeOwner(), NS_ERROR_FAILURE);
+ aContentShell->SetTreeOwner(mContentTreeOwner);
+ if (mPrimaryContentShell == aContentShell) mPrimaryContentShell = nullptr;
+ }
+
+ return NS_OK;
+}
+
+nsresult AppWindow::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
+ if (mPrimaryContentShell == aContentShell) {
+ mPrimaryContentShell = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
+ if (mPrimaryBrowserParent) {
+ return GetPrimaryRemoteTabSize(aWidth, aHeight);
+ }
+ if (mPrimaryContentShell) {
+ return GetPrimaryContentShellSize(aWidth, aHeight);
+ }
+ return NS_ERROR_UNEXPECTED;
+}
+
+nsresult AppWindow::GetPrimaryRemoteTabSize(int32_t* aWidth, int32_t* aHeight) {
+ BrowserHost* host = BrowserHost::GetFrom(mPrimaryBrowserParent.get());
+ // Need strong ref, since Client* can run script.
+ RefPtr<dom::Element> element = host->GetOwnerElement();
+ NS_ENSURE_STATE(element);
+
+ CSSIntSize size(element->ClientWidth(), element->ClientHeight());
+ LayoutDeviceIntSize sizeDev =
+ RoundedToInt(size * UnscaledDevicePixelsPerCSSPixel());
+ if (aWidth) {
+ *aWidth = sizeDev.width;
+ }
+ if (aHeight) {
+ *aHeight = sizeDev.height;
+ }
+ return NS_OK;
+}
+
+nsresult AppWindow::GetPrimaryContentShellSize(int32_t* aWidth,
+ int32_t* aHeight) {
+ NS_ENSURE_STATE(mPrimaryContentShell);
+
+ nsCOMPtr<nsIBaseWindow> shellWindow(do_QueryInterface(mPrimaryContentShell));
+ NS_ENSURE_STATE(shellWindow);
+
+ LayoutDeviceIntSize sizeDev = shellWindow->GetSize();
+ if (aWidth) {
+ *aWidth = sizeDev.width;
+ }
+ if (aHeight) {
+ *aHeight = sizeDev.height;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
+ if (mPrimaryBrowserParent) {
+ return SetPrimaryRemoteTabSize(aWidth, aHeight);
+ }
+ if (mPrimaryContentShell) {
+ return SizeShellTo(mPrimaryContentShell, aWidth, aHeight);
+ }
+ return NS_ERROR_UNEXPECTED;
+}
+
+nsresult AppWindow::SetPrimaryRemoteTabSize(int32_t aWidth, int32_t aHeight) {
+ int32_t shellWidth, shellHeight;
+ GetPrimaryRemoteTabSize(&shellWidth, &shellHeight);
+ SizeShellToWithLimit(aWidth, aHeight, shellWidth, shellHeight);
+ return NS_OK;
+}
+
+nsresult AppWindow::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
+ NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
+ return mDocShell->GetSize(aWidth, aHeight);
+}
+
+nsresult AppWindow::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
+ return SizeShellTo(mDocShell, aWidth, aHeight);
+}
+
+NS_IMETHODIMP AppWindow::SizeShellTo(nsIDocShellTreeItem* aShellItem,
+ int32_t aCX, int32_t aCY) {
+ MOZ_ASSERT(aShellItem == mDocShell || aShellItem == mPrimaryContentShell);
+ if (aShellItem == mDocShell) {
+ auto newSize =
+ LayoutDeviceIntSize(aCX, aCY) + GetOuterToInnerSizeDifference(mWindow);
+ SetSize(newSize.width, newSize.height, /* aRepaint = */ true);
+ mDominantClientSize = true;
+ return NS_OK;
+ }
+
+ // 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 AppWindow::ExitModalLoop(nsresult aStatus) {
+ if (mContinueModalLoop) EnableParent(true);
+ mContinueModalLoop = false;
+ mModalStatus = aStatus;
+ return NS_OK;
+}
+
+// top-level function to create a new window
+NS_IMETHODIMP AppWindow::CreateNewWindow(int32_t aChromeFlags,
+ nsIOpenWindowInfo* aOpenWindowInfo,
+ nsIAppWindow** _retval) {
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ if (aChromeFlags & nsIWebBrowserChrome::CHROME_OPENAS_CHROME) {
+ MOZ_RELEASE_ASSERT(
+ !aOpenWindowInfo,
+ "Unexpected nsOpenWindowInfo when creating a new chrome window");
+ return CreateNewChromeWindow(aChromeFlags, _retval);
+ }
+
+ return CreateNewContentWindow(aChromeFlags, aOpenWindowInfo, _retval);
+}
+
+NS_IMETHODIMP AppWindow::CreateNewChromeWindow(int32_t aChromeFlags,
+ nsIAppWindow** _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<nsIAppWindow> newWindow;
+ appShell->CreateTopLevelWindow(
+ this, nullptr, aChromeFlags, nsIAppShellService::SIZE_TO_CONTENT,
+ nsIAppShellService::SIZE_TO_CONTENT, getter_AddRefs(newWindow));
+
+ NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
+
+ newWindow.forget(_retval);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::CreateNewContentWindow(
+ int32_t aChromeFlags, nsIOpenWindowInfo* aOpenWindowInfo,
+ nsIAppWindow** _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;
+ nsAutoCString urlStr;
+ urlStr.AssignLiteral(BROWSER_CHROME_URL_QUOTED);
+
+ 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<nsIAppWindow> newWindow;
+ {
+ AutoNoJSAPI nojsapi;
+ appShell->CreateTopLevelWindow(this, uri, aChromeFlags, 615, 480,
+ getter_AddRefs(newWindow));
+ NS_ENSURE_TRUE(newWindow, NS_ERROR_FAILURE);
+ }
+
+ AppWindow* appWin =
+ static_cast<AppWindow*>(static_cast<nsIAppWindow*>(newWindow));
+
+ // Specify which flags should be used by browser.xhtml to create the initial
+ // content browser window.
+ appWin->mInitialOpenWindowInfo = aOpenWindowInfo;
+
+ // Specify that we want the window to remain locked until the chrome has
+ // loaded.
+ appWin->LockUntilChromeLoad();
+
+ {
+ AutoNoJSAPI nojsapi;
+ SpinEventLoopUntil("AppWindow::CreateNewContentWindow"_ns,
+ [&]() { return !appWin->IsLocked(); });
+ }
+
+ NS_ENSURE_STATE(appWin->mPrimaryContentShell ||
+ appWin->mPrimaryBrowserParent);
+ MOZ_ASSERT_IF(appWin->mPrimaryContentShell,
+ !aOpenWindowInfo->GetNextRemoteBrowser());
+
+ newWindow.forget(_retval);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::GetHasPrimaryContent(bool* aResult) {
+ *aResult = mPrimaryBrowserParent || mPrimaryContentShell;
+ return NS_OK;
+}
+
+void AppWindow::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 AppWindow::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;
+ nsIAppWindow* us = this;
+
+ altered = false;
+ mediator->GetZLevel(this, &zLevel);
+
+ // translate from WidgetGUIEvent to nsIWindowMediator constants
+ position = nsIWindowMediator::zLevelTop;
+ if (*aPlacement == nsWindowZBottom || zLevel == nsIAppWindow::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, nsIAppWindow::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<nsIAppWindow> windowAbove;
+ if (newPosition == nsIWindowMediator::zLevelBelow && *aActualBelow) {
+ windowAbove = (*aActualBelow)->GetWidgetListener()->GetAppWindow();
+ }
+
+ 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 AppWindow::PlaceWindowLayersBehind(uint32_t aLowLevel, uint32_t aHighLevel,
+ nsIAppWindow* 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->GetZOrderAppWindowEnumerator(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 (NS_SUCCEEDED(windowEnumerator->HasMoreElements(&more)) && more) {
+ uint32_t nextZ; // z-level of nextWindow
+ nsCOMPtr<nsISupports> nextWindow;
+ windowEnumerator->GetNext(getter_AddRefs(nextWindow));
+ nsCOMPtr<nsIAppWindow> nextAppWindow(do_QueryInterface(nextWindow));
+ nextAppWindow->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(nextAppWindow));
+ if (nextBase) {
+ nsCOMPtr<nsIWidget> nextWidget;
+ nextBase->GetMainWidget(getter_AddRefs(nextWidget));
+ if (nextZ <= aHighLevel)
+ nextWidget->PlaceBehind(eZPlacementBelow, previousHighWidget, false);
+ previousHighWidget = nextWidget;
+ }
+ }
+}
+
+void AppWindow::SetContentScrollbarVisibility(bool aVisible) {
+ nsCOMPtr<nsPIDOMWindowOuter> contentWin(
+ do_GetInterface(mPrimaryContentShell));
+ if (!contentWin) {
+ return;
+ }
+
+ nsContentUtils::SetScrollbarsVisibility(contentWin->GetDocShell(), aVisible);
+}
+
+void AppWindow::ApplyChromeFlags() {
+ nsCOMPtr<dom::Element> window = GetWindowDOMElement();
+ if (!window) {
+ return;
+ }
+
+ 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);
+ }
+
+ /* 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.
+ IgnoredErrorResult rv;
+ window->SetAttribute(u"chromehidden"_ns, newvalue, rv);
+}
+
+NS_IMETHODIMP
+AppWindow::BeforeStartLayout() {
+ ApplyChromeFlags();
+ // Ordering here is important, loading width/height values in
+ // LoadPersistentWindowState() depends on the chromemargin attribute (since
+ // we need to translate outer to inner sizes).
+ SyncAttributesToWidget();
+ LoadPersistentWindowState();
+ if (mWindow) {
+ SizeShell();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::LockAspectRatio(bool aShouldLock) {
+ mWindow->LockAspectRatio(aShouldLock);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::NeedFastSnaphot() {
+ MOZ_ASSERT(mWindow);
+ if (!mWindow) {
+ return NS_ERROR_FAILURE;
+ }
+ mWindow->SetNeedFastSnaphot();
+ return NS_OK;
+}
+
+void AppWindow::LoadPersistentWindowState() {
+ nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
+ if (!docShellElement) {
+ return;
+ }
+
+ // Check if the window wants to persist anything.
+ nsAutoString persist;
+ docShellElement->GetAttr(nsGkAtoms::persist, persist);
+ if (persist.IsEmpty()) {
+ return;
+ }
+
+ auto loadValue = [&](nsAtom* aAttr) {
+ nsDependentAtomString attrString(aAttr);
+ if (persist.Find(attrString) >= 0) {
+ nsAutoString value;
+ nsresult rv = GetPersistentValue(aAttr, value);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to get persistent state.");
+ if (NS_SUCCEEDED(rv) && !value.IsEmpty()) {
+ docShellElement->SetAttr(aAttr, value, IgnoreErrors());
+ }
+ }
+ };
+
+ loadValue(nsGkAtoms::screenX);
+ loadValue(nsGkAtoms::screenY);
+ loadValue(nsGkAtoms::width);
+ loadValue(nsGkAtoms::height);
+ loadValue(nsGkAtoms::sizemode);
+}
+
+void AppWindow::IntrinsicallySizeShell(const CSSIntSize& aWindowDiff,
+ int32_t& aSpecWidth,
+ int32_t& aSpecHeight) {
+ nsCOMPtr<nsIContentViewer> cv;
+ mDocShell->GetContentViewer(getter_AddRefs(cv));
+ if (!cv) {
+ return;
+ }
+ RefPtr<nsDocShell> docShell = mDocShell;
+
+ CSSIntCoord maxWidth = 0;
+ CSSIntCoord maxHeight = 0;
+ CSSIntCoord prefWidth = 0;
+ if (RefPtr element = GetWindowDOMElement()) {
+ nsAutoString prefWidthAttr;
+ if (element->GetAttr(nsGkAtoms::prefwidth, prefWidthAttr)) {
+ // TODO: Make this more generic perhaps?
+ if (prefWidthAttr.EqualsLiteral("min-width")) {
+ if (auto* f = element->GetPrimaryFrame(FlushType::Frames)) {
+ const auto& coord = f->StylePosition()->mMinWidth;
+ if (coord.ConvertsToLength()) {
+ prefWidth = CSSPixel::FromAppUnitsRounded(coord.ToLength());
+ }
+ }
+ }
+ }
+ }
+
+ Maybe<CSSIntSize> size = cv->GetContentSize(maxWidth, maxHeight, prefWidth);
+ if (!size) {
+ return;
+ }
+ nsPresContext* pc = cv->GetPresContext();
+ MOZ_ASSERT(pc, "Should have pres context");
+
+ int32_t width = pc->CSSPixelsToDevPixels(size->width);
+ int32_t height = pc->CSSPixelsToDevPixels(size->height);
+ SizeShellTo(docShell, width, height);
+
+ // Update specified size for the final LoadPositionFromXUL call.
+ aSpecWidth = size->width + aWindowDiff.width;
+ aSpecHeight = size->height + aWindowDiff.height;
+}
+
+void AppWindow::SizeShell() {
+ AutoRestore<bool> sizingShellFromXUL(mSizingShellFromXUL);
+ mSizingShellFromXUL = true;
+
+ int32_t specWidth = -1, specHeight = -1;
+ bool gotSize = false;
+
+ nsAutoString windowType;
+ if (nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement()) {
+ windowElement->GetAttr(nsGkAtoms::windowtype, windowType);
+ }
+
+ const CSSIntSize windowDiff = GetOuterToInnerSizeDifferenceInCSSPixels(
+ mWindow, UnscaledDevicePixelsPerCSSPixel());
+
+ // If we're using fingerprint resistance, we're going to resize the window
+ // once we have primary content.
+ if (nsContentUtils::ShouldResistFingerprinting(
+ "if RFP is enabled we want to round the dimensions of the new"
+ "new pop up window regardless of their origin") &&
+ windowType.EqualsLiteral("navigator:browser")) {
+ // Once we've got primary content, force dimensions.
+ if (mPrimaryContentShell || mPrimaryBrowserParent) {
+ ForceRoundedDimensions();
+ }
+ // Always avoid setting size/sizemode on this window.
+ mIgnoreXULSize = true;
+ mIgnoreXULSizeMode = true;
+ } else if (!mIgnoreXULSize) {
+ gotSize = LoadSizeFromXUL(specWidth, specHeight);
+ specWidth += windowDiff.width;
+ specHeight += windowDiff.height;
+ }
+
+ bool positionSet = !mIgnoreXULPosition;
+ nsCOMPtr<nsIAppWindow> 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 LoadSizeFromXUL set the size, mIntrinsicallySized will be false.
+ if (mIntrinsicallySized) {
+ IntrinsicallySizeShell(windowDiff, specWidth, specHeight);
+ }
+
+ // 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);
+ }
+
+ UpdateWindowStateFromMiscXULAttributes();
+
+ if (mChromeLoaded && mCenterAfterLoad && !positionSet &&
+ mWindow->SizeMode() == nsSizeMode_Normal) {
+ Center(parentWindow, parentWindow ? false : true, false);
+ }
+}
+
+NS_IMETHODIMP AppWindow::GetXULBrowserWindow(
+ nsIXULBrowserWindow** aXULBrowserWindow) {
+ NS_IF_ADDREF(*aXULBrowserWindow = mXULBrowserWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP AppWindow::SetXULBrowserWindow(
+ nsIXULBrowserWindow* aXULBrowserWindow) {
+ mXULBrowserWindow = aXULBrowserWindow;
+ return NS_OK;
+}
+
+// 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.
+void AppWindow::SizeShellToWithLimit(int32_t aDesiredWidth,
+ int32_t aDesiredHeight,
+ int32_t shellItemWidth,
+ int32_t shellItemHeight) {
+ int32_t widthDelta = aDesiredWidth - shellItemWidth;
+ int32_t heightDelta = aDesiredHeight - shellItemHeight;
+
+ 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);
+
+ // Note: Because of the asynchronous resizing on Linux we have to call
+ // SetSize even when the size doesn't appear to change. A previous call that
+ // has yet to complete can still change the size. We want the latest call to
+ // define the final size.
+ SetSize(winWidth, winHeight, true);
+ mDominantClientSize = true;
+}
+
+nsresult AppWindow::GetTabCount(uint32_t* aResult) {
+ if (mXULBrowserWindow) {
+ return mXULBrowserWindow->GetTabCount(aResult);
+ }
+
+ *aResult = 0;
+ return NS_OK;
+}
+
+nsresult AppWindow::GetInitialOpenWindowInfo(
+ nsIOpenWindowInfo** aOpenWindowInfo) {
+ NS_ENSURE_ARG_POINTER(aOpenWindowInfo);
+ *aOpenWindowInfo = do_AddRef(mInitialOpenWindowInfo).take();
+ return NS_OK;
+}
+
+PresShell* AppWindow::GetPresShell() {
+ if (!mDocShell) {
+ return nullptr;
+ }
+ return mDocShell->GetPresShell();
+}
+
+bool AppWindow::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, u"MozUpdateWindowPos"_ns,
+ CanBubble::eNo, Cancelable::eNo, nullptr);
+ }
+
+ // Persist position, but not immediately, in case this OS is firing
+ // repeated move events as the user drags the window
+ PersistentAttributesDirty(PersistentAttribute::Position, Async);
+ return false;
+}
+
+bool AppWindow::WindowResized(nsIWidget* aWidget, int32_t aWidth,
+ int32_t aHeight) {
+ mDominantClientSize = false;
+ if (mDocShell) {
+ mDocShell->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()) {
+ PersistentAttributesDirty(AllPersistentAttributes(), Async);
+ }
+ // Check if we need to continue a fullscreen change.
+ switch (mFullscreenChangeState) {
+ case FullscreenChangeState::WillChange:
+ mFullscreenChangeState = FullscreenChangeState::WidgetResized;
+ break;
+ case FullscreenChangeState::WidgetEnteredFullscreen:
+ FinishFullscreenChange(true);
+ break;
+ case FullscreenChangeState::WidgetExitedFullscreen:
+ FinishFullscreenChange(false);
+ break;
+ case FullscreenChangeState::WidgetResized:
+ case FullscreenChangeState::NotChanging:
+ break;
+ }
+ return true;
+}
+
+bool AppWindow::RequestWindowClose(nsIWidget* aWidget) {
+ // Maintain a reference to this as it is about to get destroyed.
+ nsCOMPtr<nsIAppWindow> appWindow(this);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window(mDocShell ? mDocShell->GetWindow()
+ : nullptr);
+ nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(window);
+
+ RefPtr<PresShell> 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, eClose, nullptr, WidgetMouseEvent::eReal);
+ if (NS_SUCCEEDED(EventDispatcher::Dispatch(eventTarget, presContext, &event,
+ nullptr, &status)) &&
+ status == nsEventStatus_eConsumeNoDefault)
+ return false;
+ }
+
+ Destroy();
+ return false;
+}
+
+void AppWindow::SizeModeChanged(nsSizeMode aSizeMode) {
+ const bool wasWidgetInFullscreen = mIsWidgetInFullscreen;
+ // Fullscreen and minimized states are usually compatible, and the widget
+ // typically returns to fullscreen after restoration. By not updating the
+ // widget's fullscreen state while it is minimized, we can avoid unnecessary
+ // fullscreen exits, such as those encountered in bug 1823284.
+ if (aSizeMode != nsSizeMode_Minimized) {
+ mIsWidgetInFullscreen = aSizeMode == nsSizeMode_Fullscreen;
+ }
+
+ const bool fullscreenChanged = wasWidgetInFullscreen != mIsWidgetInFullscreen;
+ if (fullscreenChanged) {
+ FullscreenWillChange(mIsWidgetInFullscreen);
+ }
+
+ // 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 (aSizeMode == nsSizeMode_Maximized || aSizeMode == nsSizeMode_Fullscreen) {
+ uint32_t zLevel;
+ GetZLevel(&zLevel);
+ if (zLevel > nsIAppWindow::normalZ) {
+ SetZLevel(nsIAppWindow::normalZ);
+ }
+ }
+
+ RecomputeBrowsingContextVisibility();
+
+ PersistentAttributesDirty(PersistentAttribute::Misc, Sync);
+ nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
+ mDocShell ? mDocShell->GetWindow() : nullptr;
+ if (ourWindow) {
+ // Always fire a user-defined sizemodechange event on the window
+ ourWindow->DispatchCustomEvent(u"sizemodechange"_ns);
+ }
+
+ if (PresShell* presShell = GetPresShell()) {
+ presShell->GetPresContext()->SizeModeChanged(aSizeMode);
+ }
+
+ if (fullscreenChanged) {
+ FullscreenChanged(mIsWidgetInFullscreen);
+ }
+
+ // 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 AppWindow::UIResolutionChanged() {
+ nsCOMPtr<nsPIDOMWindowOuter> ourWindow =
+ mDocShell ? mDocShell->GetWindow() : nullptr;
+ if (ourWindow) {
+ ourWindow->DispatchCustomEvent(u"resolutionchange"_ns,
+ ChromeOnlyDispatch::eYes);
+ }
+}
+
+void AppWindow::FullscreenWillChange(bool aInFullscreen) {
+ if (mDocShell) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
+ ourWindow->FullscreenWillChange(aInFullscreen);
+ }
+ }
+ MOZ_ASSERT(mFullscreenChangeState == FullscreenChangeState::NotChanging);
+
+ int32_t winWidth = 0;
+ int32_t winHeight = 0;
+ GetSize(&winWidth, &winHeight);
+
+ int32_t screenWidth = 0;
+ int32_t screenHeight = 0;
+ GetAvailScreenSize(&screenWidth, &screenHeight);
+
+ // Check if the window is already at the expected dimensions. If it is, set
+ // the fullscreen change state to WidgetResized to avoid waiting for a resize
+ // event. On macOS, a fullscreen window could be slightly higher than
+ // available screen size because of the OS menu bar isn't yet hidden.
+ mFullscreenChangeState =
+ (aInFullscreen == (winWidth == screenWidth && winHeight >= screenHeight))
+ ? FullscreenChangeState::WidgetResized
+ : FullscreenChangeState::WillChange;
+}
+
+void AppWindow::FullscreenChanged(bool aInFullscreen) {
+ if (mFullscreenChangeState == FullscreenChangeState::WidgetResized) {
+ FinishFullscreenChange(aInFullscreen);
+ } else {
+ NS_WARNING_ASSERTION(
+ mFullscreenChangeState == FullscreenChangeState::WillChange,
+ "Unexpected fullscreen change state");
+ FullscreenChangeState newState =
+ aInFullscreen ? FullscreenChangeState::WidgetEnteredFullscreen
+ : FullscreenChangeState::WidgetExitedFullscreen;
+ mFullscreenChangeState = newState;
+ nsCOMPtr<nsIAppWindow> kungFuDeathGrip(this);
+ // Wait for resize for a small amount of time.
+ // 80ms is actually picked arbitrarily. But it shouldn't be too large
+ // in case the widget resize is not going to happen at all, which can
+ // be the case for some Linux window managers and possibly Android.
+ NS_DelayedDispatchToCurrentThread(
+ NS_NewRunnableFunction(
+ "AppWindow::FullscreenChanged",
+ [this, kungFuDeathGrip, newState, aInFullscreen]() {
+ if (mFullscreenChangeState == newState) {
+ FinishFullscreenChange(aInFullscreen);
+ }
+ }),
+ 80);
+ }
+}
+
+void AppWindow::FinishFullscreenChange(bool aInFullscreen) {
+ mFullscreenChangeState = FullscreenChangeState::NotChanging;
+ if (mDocShell) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
+ ourWindow->FinishFullscreenChange(aInFullscreen);
+ }
+ }
+}
+
+void AppWindow::MacFullscreenMenubarOverlapChanged(
+ mozilla::DesktopCoord aOverlapAmount) {
+ if (mDocShell) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
+ ourWindow->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
+ }
+ }
+}
+
+void AppWindow::RecomputeBrowsingContextVisibility() {
+ if (!mDocShell) {
+ return;
+ }
+ RefPtr bc = mDocShell->GetBrowsingContext();
+ if (!bc) {
+ return;
+ }
+ bc->Canonical()->RecomputeAppWindowVisibility();
+}
+
+void AppWindow::OcclusionStateChanged(bool aIsFullyOccluded) {
+ if (!mDocShell) {
+ return;
+ }
+ RecomputeBrowsingContextVisibility();
+ if (RefPtr win = mDocShell->GetWindow()) {
+ // And always fire a user-defined occlusionstatechange event on the window
+ win->DispatchCustomEvent(u"occlusionstatechange"_ns,
+ ChromeOnlyDispatch::eYes);
+ }
+}
+
+void AppWindow::OSToolbarButtonPressed() {
+ // Keep a reference as setting the chrome flags can fire events.
+ nsCOMPtr<nsIAppWindow> appWindow(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(appWindow));
+ 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 AppWindow::ZLevelChanged(bool aImmediate, nsWindowZ* aPlacement,
+ nsIWidget* aRequestBelow,
+ nsIWidget** aActualBelow) {
+ if (aActualBelow) *aActualBelow = nullptr;
+
+ return ConstrainToZLevel(aImmediate, aPlacement, aRequestBelow, aActualBelow);
+}
+
+void AppWindow::WindowActivated() {
+ nsCOMPtr<nsIAppWindow> appWindow(this);
+
+ // focusing the window could cause it to close, so keep a reference to it
+ if (mDocShell) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow()) {
+ if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
+ fm->WindowRaised(window, nsFocusManager::GenerateFocusActionId());
+ }
+ }
+ }
+
+ if (mChromeLoaded) {
+ PersistentAttributesDirty(AllPersistentAttributes(), Sync);
+ }
+}
+
+void AppWindow::WindowDeactivated() {
+ if (mDocShell) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> window = mDocShell->GetWindow()) {
+ if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
+ if (!fm->IsTestMode()) {
+ fm->WindowLowered(window, nsFocusManager::GenerateFocusActionId());
+ }
+ }
+ }
+ }
+}
+
+#ifdef USE_NATIVE_MENUS
+
+struct LoadNativeMenusListener {
+ LoadNativeMenusListener(Document* aDoc, nsIWidget* aParentWindow)
+ : mDocument(aDoc), mParentWindow(aParentWindow) {}
+
+ RefPtr<Document> mDocument;
+ nsCOMPtr<nsIWidget> mParentWindow;
+};
+
+static bool sHiddenWindowLoadedNativeMenus = false;
+static nsTArray<LoadNativeMenusListener> sLoadNativeMenusListeners;
+
+static void BeginLoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow);
+
+static void LoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
+ MOZ_ASSERT(!gfxPlatform::IsHeadless());
+
+ // Find the menubar tag (if there is more than one, we ignore all but
+ // the first).
+ nsCOMPtr<nsINodeList> menubarElements = aDoc->GetElementsByTagNameNS(
+ nsLiteralString(
+ u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"),
+ u"menubar"_ns);
+
+ nsCOMPtr<nsINode> menubarNode;
+ if (menubarElements) {
+ menubarNode = menubarElements->Item(0);
+ }
+
+ using widget::NativeMenuSupport;
+ if (menubarNode) {
+ nsCOMPtr<Element> menubarContent(do_QueryInterface(menubarNode));
+ NativeMenuSupport::CreateNativeMenuBar(aParentWindow, menubarContent);
+ } else {
+ NativeMenuSupport::CreateNativeMenuBar(aParentWindow, nullptr);
+ }
+
+ if (!sHiddenWindowLoadedNativeMenus) {
+ sHiddenWindowLoadedNativeMenus = true;
+ for (auto& listener : sLoadNativeMenusListeners) {
+ BeginLoadNativeMenus(listener.mDocument, listener.mParentWindow);
+ }
+ sLoadNativeMenusListeners.Clear();
+ }
+}
+
+class L10nReadyPromiseHandler final : public dom::PromiseNativeHandler {
+ public:
+ NS_DECL_ISUPPORTS
+
+ L10nReadyPromiseHandler(Document* aDoc, nsIWidget* aParentWindow)
+ : mDocument(aDoc), mWindow(aParentWindow) {}
+
+ void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) override {
+ LoadNativeMenus(mDocument, mWindow);
+ }
+
+ void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) override {
+ // Again, this shouldn't happen, but fallback to loading the menus as is.
+ NS_WARNING(
+ "L10nReadyPromiseHandler rejected - loading fallback native "
+ "menu.");
+ LoadNativeMenus(mDocument, mWindow);
+ }
+
+ private:
+ ~L10nReadyPromiseHandler() = default;
+
+ RefPtr<Document> mDocument;
+ nsCOMPtr<nsIWidget> mWindow;
+};
+
+NS_IMPL_ISUPPORTS0(L10nReadyPromiseHandler)
+
+static void BeginLoadNativeMenus(Document* aDoc, nsIWidget* aParentWindow) {
+ RefPtr<DocumentL10n> l10n = aDoc->GetL10n();
+ if (l10n) {
+ // Wait for l10n to be ready so the menus are localized.
+ RefPtr<Promise> promise = l10n->Ready();
+ MOZ_ASSERT(promise);
+ RefPtr<L10nReadyPromiseHandler> handler =
+ new L10nReadyPromiseHandler(aDoc, aParentWindow);
+ promise->AppendNativeHandler(handler);
+ } else {
+ // Something went wrong loading the doc and l10n wasn't created. This
+ // shouldn't really happen, but if it does fallback to trying to load
+ // the menus as is.
+ LoadNativeMenus(aDoc, aParentWindow);
+ }
+}
+
+#endif
+
+class AppWindowTimerCallback final : public nsITimerCallback, public nsINamed {
+ public:
+ explicit AppWindowTimerCallback(AppWindow* 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;
+ }
+
+ NS_IMETHOD GetName(nsACString& aName) override {
+ aName.AssignLiteral("AppWindowTimerCallback");
+ return NS_OK;
+ }
+
+ private:
+ ~AppWindowTimerCallback() {}
+
+ RefPtr<AppWindow> mWindow;
+};
+
+NS_IMPL_ISUPPORTS(AppWindowTimerCallback, nsITimerCallback, nsINamed)
+
+void AppWindow::PersistentAttributesDirty(PersistentAttributes aAttributes,
+ PersistentAttributeUpdate aUpdate) {
+ aAttributes = aAttributes & mPersistentAttributesMask;
+ if (aAttributes.isEmpty()) {
+ return;
+ }
+
+ mPersistentAttributesDirty += aAttributes;
+ if (aUpdate == Sync) {
+ // Only apply the attributes we've been requested to apply sync, not other
+ // potentially dirty attributes that have been requested asynchronously.
+ SavePersistentAttributes(aAttributes);
+ return;
+ }
+ if (!mSPTimer) {
+ mSPTimer = NS_NewTimer();
+ if (!mSPTimer) {
+ NS_WARNING("Couldn't create timer instance?");
+ return;
+ }
+ }
+
+ RefPtr<AppWindowTimerCallback> callback = new AppWindowTimerCallback(this);
+ mSPTimer->InitWithCallback(callback, SIZE_PERSISTENCE_TIMEOUT,
+ nsITimer::TYPE_ONE_SHOT);
+}
+
+void AppWindow::FirePersistenceTimer() { SavePersistentAttributes(); }
+
+//----------------------------------------
+// nsIWebProgessListener implementation
+//----------------------------------------
+NS_IMETHODIMP
+AppWindow::OnProgressChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
+ int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress,
+ int32_t aMaxTotalProgress) {
+ MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::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
+ ///////////////////////////////
+ if (!gfxPlatform::IsHeadless()) {
+ nsCOMPtr<nsIContentViewer> cv;
+ mDocShell->GetContentViewer(getter_AddRefs(cv));
+ if (cv) {
+ RefPtr<Document> menubarDoc = cv->GetDocument();
+ if (menubarDoc) {
+ if (mIsHiddenWindow || sHiddenWindowLoadedNativeMenus) {
+ BeginLoadNativeMenus(menubarDoc, mWindow);
+ } else {
+ sLoadNativeMenusListeners.EmplaceBack(menubarDoc, mWindow);
+ }
+ }
+ }
+ }
+#endif // USE_NATIVE_MENUS
+
+ OnChromeLoaded();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::OnLocationChange(nsIWebProgress* aProgress, nsIRequest* aRequest,
+ nsIURI* aURI, uint32_t aFlags) {
+ MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+ nsresult aStatus, const char16_t* aMessage) {
+ MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+ uint32_t aState) {
+ MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+AppWindow::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest, uint32_t aEvent) {
+ MOZ_ASSERT_UNREACHABLE("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+/**
+ * ExecuteCloseHandler - Run the close handler, if any.
+ * @return true iff we found a close handler to run.
+ */
+bool AppWindow::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<nsIAppWindow> 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();
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+ WidgetMouseEvent event(true, eClose, nullptr, WidgetMouseEvent::eReal);
+
+ nsresult rv = EventDispatcher::Dispatch(eventTarget, presContext, &event,
+ nullptr, &status);
+ if (NS_SUCCEEDED(rv) && status == nsEventStatus_eConsumeNoDefault)
+ return true;
+ // else fall through and return false
+ }
+ }
+
+ return false;
+} // ExecuteCloseHandler
+
+void AppWindow::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);
+ if (screen) {
+ screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
+ if (*aX < left || *aX > left + width) {
+ *aX = left;
+ }
+ if (*aY < top || *aY > top + height) {
+ *aY = top;
+ }
+ }
+ }
+}
+
+nsIAppWindow* AppWindow::WidgetListenerDelegate::GetAppWindow() {
+ return mAppWindow->GetAppWindow();
+}
+
+PresShell* AppWindow::WidgetListenerDelegate::GetPresShell() {
+ return mAppWindow->GetPresShell();
+}
+
+bool AppWindow::WidgetListenerDelegate::WindowMoved(nsIWidget* aWidget,
+ int32_t aX, int32_t aY,
+ ByMoveToRect) {
+ RefPtr<AppWindow> holder = mAppWindow;
+ return holder->WindowMoved(aWidget, aX, aY);
+}
+
+bool AppWindow::WidgetListenerDelegate::WindowResized(nsIWidget* aWidget,
+ int32_t aWidth,
+ int32_t aHeight) {
+ RefPtr<AppWindow> holder = mAppWindow;
+ return holder->WindowResized(aWidget, aWidth, aHeight);
+}
+
+bool AppWindow::WidgetListenerDelegate::RequestWindowClose(nsIWidget* aWidget) {
+ RefPtr<AppWindow> holder = mAppWindow;
+ return holder->RequestWindowClose(aWidget);
+}
+
+void AppWindow::WidgetListenerDelegate::SizeModeChanged(nsSizeMode aSizeMode) {
+ RefPtr<AppWindow> holder = mAppWindow;
+ holder->SizeModeChanged(aSizeMode);
+}
+
+void AppWindow::WidgetListenerDelegate::UIResolutionChanged() {
+ RefPtr<AppWindow> holder = mAppWindow;
+ holder->UIResolutionChanged();
+}
+
+void AppWindow::WidgetListenerDelegate::MacFullscreenMenubarOverlapChanged(
+ DesktopCoord aOverlapAmount) {
+ RefPtr<AppWindow> holder = mAppWindow;
+ return holder->MacFullscreenMenubarOverlapChanged(aOverlapAmount);
+}
+
+void AppWindow::WidgetListenerDelegate::OcclusionStateChanged(
+ bool aIsFullyOccluded) {
+ RefPtr<AppWindow> holder = mAppWindow;
+ holder->OcclusionStateChanged(aIsFullyOccluded);
+}
+
+void AppWindow::WidgetListenerDelegate::OSToolbarButtonPressed() {
+ RefPtr<AppWindow> holder = mAppWindow;
+ holder->OSToolbarButtonPressed();
+}
+
+bool AppWindow::WidgetListenerDelegate::ZLevelChanged(
+ bool aImmediate, nsWindowZ* aPlacement, nsIWidget* aRequestBelow,
+ nsIWidget** aActualBelow) {
+ RefPtr<AppWindow> holder = mAppWindow;
+ return holder->ZLevelChanged(aImmediate, aPlacement, aRequestBelow,
+ aActualBelow);
+}
+
+void AppWindow::WidgetListenerDelegate::WindowActivated() {
+ RefPtr<AppWindow> holder = mAppWindow;
+ holder->WindowActivated();
+}
+
+void AppWindow::WidgetListenerDelegate::WindowDeactivated() {
+ RefPtr<AppWindow> holder = mAppWindow;
+ holder->WindowDeactivated();
+}
+
+} // namespace mozilla
diff --git a/xpfe/appshell/AppWindow.h b/xpfe/appshell/AppWindow.h
new file mode 100644
index 0000000000..4acee5dbfe
--- /dev/null
+++ b/xpfe/appshell/AppWindow.h
@@ -0,0 +1,398 @@
+/* -*- 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 mozilla_AppWindow_h__
+#define mozilla_AppWindow_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 "nsDocShell.h"
+#include "nsRect.h"
+#include "Units.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Mutex.h"
+
+// Interfaces needed
+#include "nsIBaseWindow.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIAppWindow.h"
+#include "nsIPrompt.h"
+#include "nsIAuthPrompt.h"
+#include "nsIXULBrowserWindow.h"
+#include "nsIWidgetListener.h"
+#include "nsIRemoteTab.h"
+#include "nsIWebProgressListener.h"
+#include "nsITimer.h"
+#include "nsIXULStore.h"
+
+class nsAtom;
+class nsXULTooltipListener;
+
+namespace mozilla {
+class PresShell;
+class AppWindowTimerCallback;
+class L10nReadyPromiseHandler;
+namespace dom {
+class Element;
+} // namespace dom
+namespace widget {
+struct InitData;
+} // namespace widget
+} // namespace mozilla
+
+// AppWindow
+
+#define NS_APPWINDOW_IMPL_CID \
+ { /* 8eaec2f3-ed02-4be2-8e0f-342798477298 */ \
+ 0x8eaec2f3, 0xed02, 0x4be2, { \
+ 0x8e, 0x0f, 0x34, 0x27, 0x98, 0x47, 0x72, 0x98 \
+ } \
+ }
+
+class nsContentShellInfo;
+
+namespace mozilla {
+
+class AppWindow final : public nsIBaseWindow,
+ public nsIInterfaceRequestor,
+ public nsIAppWindow,
+ public nsSupportsWeakReference,
+ public nsIWebProgressListener {
+ friend class ::nsChromeTreeOwner;
+ friend class ::nsContentTreeOwner;
+
+ public:
+ // The implementation of non-refcounted nsIWidgetListener, which would hold a
+ // strong reference on stack before calling AppWindow's
+ // MOZ_CAN_RUN_SCRIPT methods.
+ class WidgetListenerDelegate : public nsIWidgetListener {
+ public:
+ explicit WidgetListenerDelegate(AppWindow* aAppWindow)
+ : mAppWindow(aAppWindow) {}
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual nsIAppWindow* GetAppWindow() override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual mozilla::PresShell* GetPresShell() override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual bool WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y,
+ ByMoveToRect) override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual bool WindowResized(nsIWidget* aWidget, int32_t aWidth,
+ int32_t aHeight) override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual bool RequestWindowClose(nsIWidget* aWidget) override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual void SizeModeChanged(nsSizeMode sizeMode) override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual void UIResolutionChanged() override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual void MacFullscreenMenubarOverlapChanged(
+ mozilla::DesktopCoord aOverlapAmount) override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual void OcclusionStateChanged(bool aIsFullyOccluded) override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual void OSToolbarButtonPressed() override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual bool ZLevelChanged(bool aImmediate, nsWindowZ* aPlacement,
+ nsIWidget* aRequestBelow,
+ nsIWidget** aActualBelow) override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual void WindowActivated() override;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ virtual void WindowDeactivated() override;
+
+ private:
+ // The lifetime of WidgetListenerDelegate is bound to AppWindow so
+ // we just use a raw pointer here.
+ AppWindow* mAppWindow;
+ };
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIAPPWINDOW
+ NS_DECL_NSIBASEWINDOW
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_APPWINDOW_IMPL_CID)
+
+ void LockUntilChromeLoad() { mLockedUntilChromeLoad = true; }
+ bool IsLocked() const { return mLockedUntilChromeLoad; }
+ void IgnoreXULSizeMode(bool aEnable) { mIgnoreXULSizeMode = aEnable; }
+ void WasRegistered() { mRegistered = true; }
+
+ using nsIBaseWindow::GetPositionAndSize;
+ using nsIBaseWindow::GetSize;
+
+ // AppWindow methods...
+ nsresult Initialize(nsIAppWindow* aParent, nsIAppWindow* aOpener,
+ int32_t aInitialWidth, int32_t aInitialHeight,
+ bool aIsHiddenWindow, widget::InitData& widgetInitData);
+
+ nsDocShell* GetDocShell() { return mDocShell; }
+
+ nsresult Toolbar();
+
+ // nsIWebProgressListener
+ NS_DECL_NSIWEBPROGRESSLISTENER
+
+ // nsIWidgetListener methods for WidgetListenerDelegate.
+ nsIAppWindow* GetAppWindow() { return this; }
+ mozilla::PresShell* GetPresShell();
+ MOZ_CAN_RUN_SCRIPT
+ bool WindowMoved(nsIWidget* aWidget, int32_t aX, int32_t aY);
+ MOZ_CAN_RUN_SCRIPT
+ bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight);
+ MOZ_CAN_RUN_SCRIPT bool RequestWindowClose(nsIWidget* aWidget);
+ MOZ_CAN_RUN_SCRIPT void SizeModeChanged(nsSizeMode aSizeMode);
+ MOZ_CAN_RUN_SCRIPT void UIResolutionChanged();
+ MOZ_CAN_RUN_SCRIPT void FullscreenWillChange(bool aInFullscreen);
+ MOZ_CAN_RUN_SCRIPT void FullscreenChanged(bool aInFullscreen);
+ MOZ_CAN_RUN_SCRIPT void MacFullscreenMenubarOverlapChanged(
+ mozilla::DesktopCoord aOverlapAmount);
+ MOZ_CAN_RUN_SCRIPT void OcclusionStateChanged(bool aIsFullyOccluded);
+ void RecomputeBrowsingContextVisibility();
+ MOZ_CAN_RUN_SCRIPT void OSToolbarButtonPressed();
+ MOZ_CAN_RUN_SCRIPT
+ bool ZLevelChanged(bool aImmediate, nsWindowZ* aPlacement,
+ nsIWidget* aRequestBelow, nsIWidget** aActualBelow);
+ MOZ_CAN_RUN_SCRIPT void WindowActivated();
+ MOZ_CAN_RUN_SCRIPT void WindowDeactivated();
+
+ explicit AppWindow(uint32_t aChromeFlags);
+
+ protected:
+ enum class PersistentAttribute : uint8_t {
+ Position,
+ Size,
+ Misc,
+ };
+ using PersistentAttributes = EnumSet<PersistentAttribute>;
+
+ static PersistentAttributes AllPersistentAttributes() {
+ return {PersistentAttribute::Position, PersistentAttribute::Size,
+ PersistentAttribute::Misc};
+ }
+
+ virtual ~AppWindow();
+
+ friend class mozilla::AppWindowTimerCallback;
+
+ MOZ_CAN_RUN_SCRIPT bool ExecuteCloseHandler();
+ void ConstrainToOpenerScreen(int32_t* aX, int32_t* aY);
+
+ void SetPersistenceTimer(uint32_t aDirtyFlags);
+ void FirePersistenceTimer();
+
+ NS_IMETHOD EnsureChromeTreeOwner();
+ NS_IMETHOD EnsureContentTreeOwner();
+ NS_IMETHOD EnsurePrimaryContentTreeOwner();
+ NS_IMETHOD EnsurePrompter();
+ NS_IMETHOD EnsureAuthPrompter();
+ NS_IMETHOD ForceRoundedDimensions();
+ NS_IMETHOD GetAvailScreenSize(int32_t* aAvailWidth, int32_t* aAvailHeight);
+
+ void FinishFullscreenChange(bool aInFullscreen);
+
+ void ApplyChromeFlags();
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY void SizeShell();
+ 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 UpdateWindowStateFromMiscXULAttributes();
+ void SyncAttributesToWidget();
+ void SavePersistentAttributes(PersistentAttributes);
+ void MaybeSavePersistentPositionAndSize(PersistentAttributes,
+ dom::Element& aRootElement,
+ const nsAString& aPersistString,
+ bool aShouldPersist);
+ void MaybeSavePersistentMiscAttributes(PersistentAttributes,
+ dom::Element& aRootElement,
+ const nsAString& aPersistString,
+ bool aShouldPersist);
+ void SavePersistentAttributes() {
+ SavePersistentAttributes(mPersistentAttributesDirty);
+ }
+
+ bool NeedsTooltipListener();
+ void AddTooltipSupport();
+ void RemoveTooltipSupport();
+
+ NS_IMETHOD GetWindowDOMWindow(mozIDOMWindowProxy** aDOMWindow);
+ dom::Element* GetWindowDOMElement() const;
+
+ // See nsIDocShellTreeOwner for docs on next two methods
+ nsresult ContentShellAdded(nsIDocShellTreeItem* aContentShell, bool aPrimary);
+ 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,
+ nsIAppWindow** _retval);
+ NS_IMETHOD CreateNewContentWindow(int32_t aChromeFlags,
+ nsIOpenWindowInfo* aOpenWindowInfo,
+ nsIAppWindow** _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,
+ nsIAppWindow* aBehind);
+ void SetContentScrollbarVisibility(bool aVisible);
+
+ enum PersistentAttributeUpdate { Sync, Async };
+ void PersistentAttributesDirty(PersistentAttributes,
+ PersistentAttributeUpdate);
+ nsresult GetTabCount(uint32_t* aResult);
+
+ void LoadPersistentWindowState();
+ nsresult GetPersistentValue(const nsAtom* aAttr, nsAString& aValue);
+ nsresult SetPersistentValue(const nsAtom* aAttr, const nsAString& aValue);
+
+ // Saves window size and positioning values in order to display a very early
+ // skeleton UI. This has to happen before we can reasonably initialize the
+ // xulstore (i.e., before even loading libxul), so they have to use a special
+ // purpose store to do so.
+ nsresult MaybeSaveEarlyWindowPersistentValues(
+ const LayoutDeviceIntRect& aRect);
+
+ // Gets the uri spec and the window element ID for this window.
+ nsresult GetDocXulStoreKeys(nsString& aUriSpec, nsString& aWindowElementId);
+
+ // Enum for the current state of a fullscreen change.
+ //
+ // It is used to ensure that fullscreen change is issued after both
+ // the window state change and the window size change at best effort.
+ // This is needed because some platforms can't guarantee the order
+ // between such two events.
+ //
+ // It's changed in the following way:
+ // +---------------------------+--------------------------------------+
+ // | | |
+ // | v |
+ // | NotChanging |
+ // | + |
+ // | | FullscreenWillChange |
+ // | v |
+ // | +-----------+ WillChange +------------------+ |
+ // | | WindowResized FullscreenChanged | |
+ // | v v |
+ // | WidgetResized WidgetEnteredFullscreen |
+ // | + or WidgetExitedFullscreen |
+ // | | FullscreenChanged + |
+ // | v WindowResized or | |
+ // +--------+ delayed dispatch | |
+ // v |
+ // +-------------+
+ //
+ // The delayed dispatch serves as timeout, which is necessary because it's
+ // not even guaranteed that the widget will be resized at all.
+ enum class FullscreenChangeState : uint8_t {
+ // No current fullscreen change. Any previous change has finished.
+ NotChanging,
+ // Indicate there is going to be a fullscreen change.
+ WillChange,
+ // The widget has been resized since WillChange.
+ WidgetResized,
+ // The widget has entered fullscreen state since WillChange.
+ WidgetEnteredFullscreen,
+ // The widget has exited fullscreen state since WillChange.
+ WidgetExitedFullscreen,
+ };
+
+ nsChromeTreeOwner* mChromeTreeOwner;
+ nsContentTreeOwner* mContentTreeOwner;
+ nsContentTreeOwner* mPrimaryContentTreeOwner;
+ nsCOMPtr<nsIWidget> mWindow;
+ RefPtr<nsDocShell> mDocShell;
+ nsCOMPtr<nsPIDOMWindowOuter> mDOMWindow;
+ nsWeakPtr mParentWindow;
+ nsCOMPtr<nsIPrompt> mPrompter;
+ nsCOMPtr<nsIAuthPrompt> mAuthPrompter;
+ nsCOMPtr<nsIXULBrowserWindow> mXULBrowserWindow;
+ nsCOMPtr<nsIDocShellTreeItem> mPrimaryContentShell;
+ nsresult mModalStatus;
+ FullscreenChangeState mFullscreenChangeState;
+ bool mContinueModalLoop;
+ bool mDebuting; // being made visible right now
+ bool mChromeLoaded; // True when chrome has loaded
+ bool mSizingShellFromXUL; // true when in SizeShell()
+ 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;
+ // Indicator for whether the client size, instead of the window size, should
+ // be maintained in case of a change in their relation.
+ bool mDominantClientSize;
+ PersistentAttributes mPersistentAttributesDirty;
+ PersistentAttributes mPersistentAttributesMask;
+ uint32_t mChromeFlags;
+ nsCOMPtr<nsIOpenWindowInfo> mInitialOpenWindowInfo;
+ nsString mTitle;
+
+ // The screen rect of the opener.
+ mozilla::DesktopIntRect mOpenerScreenRect;
+
+ nsCOMPtr<nsIRemoteTab> mPrimaryBrowserParent;
+
+ nsCOMPtr<nsITimer> mSPTimer;
+ WidgetListenerDelegate mWidgetListenerDelegate;
+
+ private:
+ MOZ_CAN_RUN_SCRIPT void IntrinsicallySizeShell(const CSSIntSize& aWindowDiff,
+ int32_t& aSpecWidth,
+ int32_t& aSpecHeight);
+
+ // GetPrimaryBrowserParentSize is called from xpidl methods and we don't have
+ // a good way to annotate those with MOZ_CAN_RUN_SCRIPT yet. It takes no
+ // refcounted args other than "this", and the "this" uses seem ok.
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
+ GetPrimaryRemoteTabSize(int32_t* aWidth, int32_t* aHeight);
+ nsresult GetPrimaryContentShellSize(int32_t* aWidth, int32_t* aHeight);
+ nsresult SetPrimaryRemoteTabSize(int32_t aWidth, int32_t aHeight);
+ void SizeShellToWithLimit(int32_t aDesiredWidth, int32_t aDesiredHeight,
+ int32_t shellItemWidth, int32_t shellItemHeight);
+ nsresult MoveResize(const Maybe<LayoutDeviceIntPoint>& aPosition,
+ const Maybe<LayoutDeviceIntSize>& aSize, bool aRepaint);
+ nsresult MoveResize(const Maybe<DesktopPoint>& aPosition,
+ const Maybe<DesktopSize>& aSize, bool aRepaint);
+ nsCOMPtr<nsIXULStore> mLocalStore;
+ bool mIsWidgetInFullscreen = false;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(AppWindow, NS_APPWINDOW_IMPL_CID)
+
+} // namespace mozilla
+
+#endif /* mozilla_AppWindow_h__ */
diff --git a/xpfe/appshell/LiveResizeListener.h b/xpfe/appshell/LiveResizeListener.h
new file mode 100644
index 0000000000..e878ad7d35
--- /dev/null
+++ b/xpfe/appshell/LiveResizeListener.h
@@ -0,0 +1,27 @@
+/* -*- 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 mozilla_LiveResizeListener_h
+#define mozilla_LiveResizeListener_h
+
+#include "nsISupportsImpl.h"
+
+namespace mozilla {
+
+class LiveResizeListener {
+ public:
+ virtual void LiveResizeStarted() = 0;
+ virtual void LiveResizeStopped() = 0;
+
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ protected:
+ virtual ~LiveResizeListener() = default;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_LiveResizeListener_h
diff --git a/xpfe/appshell/components.conf b/xpfe/appshell/components.conf
new file mode 100644
index 0000000000..a1d848ae1f
--- /dev/null
+++ b/xpfe/appshell/components.conf
@@ -0,0 +1,25 @@
+# -*- 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/.
+
+Classes = [
+ {
+ 'js_name': 'appShell',
+ 'cid': '{0099907d-123c-4853-a46a-43098b5fb68c}',
+ 'contract_ids': ['@mozilla.org/appshell/appShellService;1'],
+ 'interfaces': ['nsIAppShellService'],
+ 'type': 'nsAppShellService',
+ 'headers': ['/xpfe/appshell/nsAppShellService.h'],
+ },
+ {
+ 'js_name': 'wm',
+ 'cid': '{79a2b7cc-f05b-4605-bfa0-fac54f27eec8}',
+ 'contract_ids': ['@mozilla.org/appshell/window-mediator;1'],
+ 'interfaces': ['nsIWindowMediator'],
+ 'type': 'nsWindowMediator',
+ 'headers': ['/xpfe/appshell/nsWindowMediator.h'],
+ 'init_method': 'Init',
+ },
+]
diff --git a/xpfe/appshell/moz.build b/xpfe/appshell/moz.build
new file mode 100644
index 0000000000..f32c647fb1
--- /dev/null
+++ b/xpfe/appshell/moz.build
@@ -0,0 +1,48 @@
+# -*- 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "Window Management")
+
+MOCHITEST_CHROME_MANIFESTS += ["test/chrome.ini"]
+
+XPIDL_SOURCES += [
+ "nsIAppShellService.idl",
+ "nsIAppWindow.idl",
+ "nsIWindowlessBrowser.idl",
+ "nsIWindowMediator.idl",
+ "nsIWindowMediatorListener.idl",
+ "nsIXULBrowserWindow.idl",
+]
+
+XPIDL_MODULE = "appshell"
+
+EXPORTS += [
+ "LiveResizeListener.h",
+ "nsAppShellCID.h",
+]
+
+UNIFIED_SOURCES += [
+ "AppWindow.cpp",
+ "nsAppShellService.cpp",
+ "nsAppShellWindowEnumerator.cpp",
+ "nsChromeTreeOwner.cpp",
+ "nsContentTreeOwner.cpp",
+ "nsWindowMediator.cpp",
+]
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+LOCAL_INCLUDES += [
+ "/dom/base",
+ "/dom/xul",
+]
+
+FINAL_LIBRARY = "xul"
+
+include("/ipc/chromium/chromium-config.mozbuild")
diff --git a/xpfe/appshell/nsAppShellCID.h b/xpfe/appshell/nsAppShellCID.h
new file mode 100644
index 0000000000..2b0edd4aca
--- /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/nsAppShellService.cpp b/xpfe/appshell/nsAppShellService.cpp
new file mode 100644
index 0000000000..d174144e52
--- /dev/null
+++ b/xpfe/appshell/nsAppShellService.cpp
@@ -0,0 +1,918 @@
+/* -*- 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 "nsNetUtil.h"
+#include "nsIObserverService.h"
+#include "nsIObserver.h"
+#include "nsIXULRuntime.h"
+
+#include "nsIWindowMediator.h"
+#include "nsPIWindowWatcher.h"
+#include "nsPIDOMWindow.h"
+#include "AppWindow.h"
+
+#include "mozilla/widget/InitData.h"
+#include "nsWidgetsCID.h"
+#include "nsIWidget.h"
+
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsAppShellService.h"
+#include "nsContentUtils.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsThreadUtils.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 "mozilla/StaticPrefs_browser.h"
+#include "mozilla/StaticPrefs_fission.h"
+#include "mozilla/intl/LocaleService.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/Document.h"
+
+#include "nsEmbedCID.h"
+#include "nsIWebBrowser.h"
+#include "nsIDocShell.h"
+#include "gfxPlatform.h"
+
+#include "nsWebBrowser.h"
+#include "nsDocShell.h"
+#include "nsDocShellLoadState.h"
+
+#ifdef MOZ_INSTRUMENT_EVENT_LOOP
+# include "EventTracer.h"
+#endif
+
+using namespace mozilla;
+using mozilla::dom::BrowsingContext;
+using mozilla::intl::LocaleService;
+
+// 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::SetScreenId(uint32_t aScreenId) {
+ mScreenId = aScreenId;
+ return NS_OK;
+}
+
+void nsAppShellService::EnsureHiddenWindow() {
+ if (!mHiddenWindow) {
+ (void)CreateHiddenWindow();
+ }
+}
+
+NS_IMETHODIMP
+nsAppShellService::CreateHiddenWindow() {
+ if (!XRE_IsParentProcess()) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ if (mXPCOMShuttingDown) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mHiddenWindow) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> profileDir;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+ getter_AddRefs(profileDir));
+ if (!profileDir) {
+ // This is too early on startup to create the hidden window
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+ int32_t initialHeight = 100, initialWidth = 100;
+
+#ifdef XP_MACOSX
+ uint32_t chromeMask = 0;
+ nsAutoCString prefVal;
+ rv = Preferences::GetCString("browser.hiddenWindowChromeURL", prefVal);
+ const char* hiddenWindowURL =
+ NS_SUCCEEDED(rv) ? prefVal.get() : DEFAULT_HIDDENWINDOW_URL;
+ 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<AppWindow> newWindow;
+ rv = JustCreateTopWindow(nullptr, url, chromeMask, initialWidth,
+ initialHeight, true, getter_AddRefs(newWindow));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIDocShell> docShell;
+ newWindow->GetDocShell(getter_AddRefs(docShell));
+ if (docShell) {
+ Unused << docShell->GetBrowsingContext()->SetExplicitActive(
+ dom::ExplicitActiveStatus::Inactive);
+ }
+
+ mHiddenWindow.swap(newWindow);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAppShellService::DestroyHiddenWindow() {
+ if (mHiddenWindow) {
+ mHiddenWindow->Destroy();
+
+ mHiddenWindow = nullptr;
+ }
+
+ return NS_OK;
+}
+
+/*
+ * Create a new top level window and display the given URL within it...
+ */
+NS_IMETHODIMP
+nsAppShellService::CreateTopLevelWindow(nsIAppWindow* aParent, nsIURI* aUrl,
+ uint32_t aChromeMask,
+ int32_t aInitialWidth,
+ int32_t aInitialHeight,
+ nsIAppWindow** aResult) {
+ nsresult rv;
+
+ StartupTimeline::RecordOnce(StartupTimeline::CREATE_TOP_LEVEL_WINDOW);
+
+ RefPtr<AppWindow> newWindow;
+ rv = JustCreateTopWindow(aParent, aUrl, aChromeMask, aInitialWidth,
+ aInitialHeight, false, 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<nsIAppWindow> parent;
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) parent = aParent;
+ (*aResult)->SetZLevel(CalculateWindowZLevel(parent, aChromeMask));
+ }
+
+ return rv;
+}
+
+/*
+ * This class provides a stub implementation of nsIWebBrowserChrome, as needed
+ * by nsAppShellService::CreateWindowlessBrowser
+ */
+class WebBrowserChrome2Stub final : public nsIWebBrowserChrome,
+ public nsIInterfaceRequestor,
+ public nsSupportsWeakReference {
+ protected:
+ nsCOMPtr<nsIWebBrowser> mBrowser;
+ virtual ~WebBrowserChrome2Stub() = default;
+
+ public:
+ void SetBrowser(nsIWebBrowser* aBrowser) { mBrowser = aBrowser; }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWEBBROWSERCHROME
+ NS_DECL_NSIINTERFACEREQUESTOR
+};
+
+NS_INTERFACE_MAP_BEGIN(WebBrowserChrome2Stub)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserChrome)
+ NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(WebBrowserChrome2Stub)
+NS_IMPL_RELEASE(WebBrowserChrome2Stub)
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetChromeFlags(uint32_t* aChromeFlags) {
+ *aChromeFlags = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetChromeFlags(uint32_t aChromeFlags) {
+ MOZ_ASSERT_UNREACHABLE(
+ "WebBrowserChrome2Stub::SetChromeFlags is "
+ "not supported");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::ShowAsModal() {
+ MOZ_ASSERT_UNREACHABLE("WebBrowserChrome2Stub::ShowAsModal is not supported");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::IsWindowModal(bool* aResult) {
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetLinkStatus(const nsAString& aStatusText) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetInterface(const nsIID& aIID, void** aSink) {
+ return QueryInterface(aIID, aSink);
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
+ int32_t* aY, int32_t* aCX, int32_t* aCY) {
+ if (aX) {
+ *aX = 0;
+ }
+ if (aY) {
+ *aY = 0;
+ }
+ if (aCX) {
+ *aCX = 0;
+ }
+ if (aCY) {
+ *aCY = 0;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::SetDimensions(DimensionRequest&& aRequest) {
+ nsCOMPtr<nsIBaseWindow> window(do_QueryInterface(mBrowser));
+ NS_ENSURE_STATE(window);
+ // Inner and outer dimensions are equal.
+ aRequest.mDimensionKind = DimensionKind::Outer;
+ MOZ_TRY(aRequest.SupplementFrom(window));
+ return aRequest.ApplyOuterTo(window);
+}
+
+NS_IMETHODIMP
+WebBrowserChrome2Stub::Blur() { return NS_ERROR_NOT_IMPLEMENTED; }
+
+class BrowserDestroyer final : public Runnable {
+ public:
+ BrowserDestroyer(nsIWebBrowser* aBrowser, nsISupports* aContainer)
+ : mozilla::Runnable("BrowserDestroyer"),
+ mBrowser(aBrowser),
+ mContainer(aContainer) {}
+
+ static nsresult Destroy(nsIWebBrowser* aBrowser) {
+ nsCOMPtr<nsIBaseWindow> window(do_QueryInterface(aBrowser));
+ return window->Destroy();
+ }
+
+ NS_IMETHOD
+ Run() override {
+ // Explicitly destroy the browser, in case this isn't the last reference.
+ return Destroy(mBrowser);
+ }
+
+ 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_DECL_NSIWINDOWLESSBROWSER
+ NS_FORWARD_SAFE_NSIWEBNAVIGATION(mWebNavigation)
+ NS_FORWARD_SAFE_NSIINTERFACEREQUESTOR(mInterfaceRequestor)
+
+ private:
+ ~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.
+ auto runnable = MakeRefPtr<BrowserDestroyer>(mBrowser, mContainer);
+ nsContentUtils::AddScriptRunner(runnable.forget());
+ }
+
+ 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
+WindowlessBrowser::Close() {
+ 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;
+ return BrowserDestroyer::Destroy(mBrowser);
+}
+
+NS_IMETHODIMP
+WindowlessBrowser::GetBrowsingContext(BrowsingContext** aBrowsingContext) {
+ nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = do_QueryInterface(mBrowser);
+ if (!docShellTreeItem) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ return docShellTreeItem->GetBrowsingContextXPCOM(aBrowsingContext);
+}
+
+NS_IMETHODIMP
+WindowlessBrowser::GetDocShell(nsIDocShell** aDocShell) {
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mInterfaceRequestor);
+ if (!docShell) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ docShell.forget(aDocShell);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAppShellService::CreateWindowlessBrowser(bool aIsChrome, uint32_t aChromeMask,
+ nsIWindowlessBrowser** aResult) {
+ if (aChromeMask) {
+ MOZ_DIAGNOSTIC_ASSERT(aIsChrome, "Got chrome flags for non-chrome browser");
+ if (aChromeMask & ~(nsIWebBrowserChrome::CHROME_REMOTE_WINDOW |
+ nsIWebBrowserChrome::CHROME_FISSION_WINDOW |
+ nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW)) {
+ NS_ERROR("Received unexpected chrome flags");
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ /* First, 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 nsIWebBrowserChrome.
+ */
+ RefPtr<WebBrowserChrome2Stub> stub = new WebBrowserChrome2Stub();
+
+ /* 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 HeadlessWidget/PuppetWidget, which provide
+ * a stub implementation of nsIWidget.
+ */
+ nsCOMPtr<nsIWidget> widget;
+ if (gfxPlatform::IsHeadless()) {
+ widget = nsIWidget::CreateHeadlessWidget();
+ } else {
+ widget = nsIWidget::CreatePuppetWidget(nullptr);
+ }
+ if (!widget) {
+ NS_ERROR("Couldn't create instance of stub widget");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv =
+ widget->Create(nullptr, 0, LayoutDeviceIntRect(0, 0, 0, 0), nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Create a BrowsingContext for our windowless browser.
+ RefPtr<BrowsingContext> browsingContext = BrowsingContext::CreateIndependent(
+ aIsChrome ? BrowsingContext::Type::Chrome
+ : BrowsingContext::Type::Content);
+
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) {
+ browsingContext->SetRemoteTabs(true);
+ }
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_FISSION_WINDOW) {
+ browsingContext->SetRemoteSubframes(true);
+ }
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) {
+ browsingContext->SetPrivateBrowsing(true);
+ }
+
+ /* Next, 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 = nsWebBrowser::Create(
+ stub, widget, browsingContext, nullptr /* initialWindowChild */);
+
+ if (NS_WARN_IF(!browser)) {
+ NS_ERROR("Couldn't create instance of nsWebBrowser!");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Make sure the container window owns the the nsWebBrowser instance.
+ stub->SetBrowser(browser);
+
+ nsISupports* isstub = NS_ISUPPORTS_CAST(nsIWebBrowserChrome*, 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(nsIAppWindow* aParent,
+ uint32_t aChromeMask) {
+ uint32_t zLevel;
+
+ zLevel = nsIAppWindow::normalZ;
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_RAISED)
+ zLevel = nsIAppWindow::raisedZ;
+ else if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_LOWERED)
+ zLevel = nsIAppWindow::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->GetAppWindowEnumerator(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;
+ }
+ }
+ }
+}
+#endif
+
+/*
+ * Just do the window-making part of CreateTopLevelWindow
+ */
+nsresult nsAppShellService::JustCreateTopWindow(
+ nsIAppWindow* aParent, nsIURI* aUrl, uint32_t aChromeMask,
+ int32_t aInitialWidth, int32_t aInitialHeight, bool aIsHiddenWindow,
+ AppWindow** aResult) {
+ using BorderStyle = widget::BorderStyle;
+ *aResult = nullptr;
+ NS_ENSURE_STATE(!mXPCOMWillShutDown);
+
+ nsCOMPtr<nsIAppWindow> parent;
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_DEPENDENT) parent = aParent;
+
+ RefPtr<AppWindow> window = new AppWindow(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
+
+ widget::InitData widgetInitData;
+
+ if (aIsHiddenWindow)
+ widgetInitData.mWindowType = widget::WindowType::Invisible;
+ else
+ widgetInitData.mWindowType =
+ aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG
+ ? widget::WindowType::Dialog
+ : widget::WindowType::TopLevel;
+
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_POPUP)
+ widgetInitData.mWindowType = widget::WindowType::Popup;
+
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_SUPPRESS_ANIMATION)
+ widgetInitData.mIsAnimationSuppressed = true;
+
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_ALWAYS_ON_TOP)
+ widgetInitData.mAlwaysOnTop = true;
+
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW)
+ widgetInitData.mHasRemoteContent = true;
+
+#if defined(MOZ_WIDGET_GTK) || defined(XP_WIN)
+ // Windows/Gtk PIP window support. It's Chrome dialog window, always on top
+ // and without any bar.
+ uint32_t pipMask = nsIWebBrowserChrome::CHROME_ALWAYS_ON_TOP |
+ nsIWebBrowserChrome::CHROME_OPENAS_CHROME |
+ nsIWebBrowserChrome::CHROME_WINDOW_RESIZE;
+ uint32_t barMask = nsIWebBrowserChrome::CHROME_MENUBAR |
+ nsIWebBrowserChrome::CHROME_TOOLBAR |
+ nsIWebBrowserChrome::CHROME_LOCATIONBAR |
+ nsIWebBrowserChrome::CHROME_TITLEBAR |
+ nsIWebBrowserChrome::CHROME_STATUSBAR;
+ if (widgetInitData.mWindowType == widget::WindowType::Dialog &&
+ ((aChromeMask & pipMask) == pipMask) && !(aChromeMask & barMask)) {
+ widgetInitData.mPIPWindow = true;
+ }
+#endif
+
+#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) &&
+ ((aChromeMask & sheetMask) == sheetMask)) {
+ widgetInitData.mWindowType = widget::WindowType::Sheet;
+ }
+#endif
+
+#if defined(XP_WIN)
+ if (widgetInitData.mWindowType == widget::WindowType::TopLevel ||
+ widgetInitData.mWindowType == widget::WindowType::Dialog)
+ widgetInitData.mClipChildren = true;
+#endif
+
+ // note default chrome overrides other OS chrome settings, but
+ // not internal chrome
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_DEFAULT)
+ widgetInitData.mBorderStyle = BorderStyle::Default;
+ else if ((aChromeMask & nsIWebBrowserChrome::CHROME_ALL) ==
+ nsIWebBrowserChrome::CHROME_ALL)
+ widgetInitData.mBorderStyle = BorderStyle::All;
+ else {
+ widgetInitData.mBorderStyle = BorderStyle::None; // assumes none == 0x00
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_BORDERS)
+ widgetInitData.mBorderStyle |= BorderStyle::Border;
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_TITLEBAR)
+ widgetInitData.mBorderStyle |= BorderStyle::Title;
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_CLOSE)
+ widgetInitData.mBorderStyle |= BorderStyle::Close;
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE) {
+ widgetInitData.mResizable = true;
+ widgetInitData.mBorderStyle |= BorderStyle::ResizeH;
+ // only resizable windows get the maximize button (but not dialogs)
+ if (!(aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG))
+ widgetInitData.mBorderStyle |= BorderStyle::Maximize;
+ }
+ // all windows (except dialogs) get minimize buttons and the system menu
+ if (!(aChromeMask & nsIWebBrowserChrome::CHROME_OPENAS_DIALOG))
+ widgetInitData.mBorderStyle |= BorderStyle::Minimize | BorderStyle::Menu;
+ // but anyone can explicitly ask for a minimize button
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_WINDOW_MIN) {
+ widgetInitData.mBorderStyle |= BorderStyle::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;
+
+ widgetInitData.mRTL = LocaleService::GetInstance()->IsAppLocaleRTL();
+
+ // Enforce the Private Browsing autoStart pref first.
+ bool isPrivateBrowsingWindow =
+ StaticPrefs::browser_privatebrowsing_autostart();
+ if (aChromeMask & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW) {
+ // Caller requested a private window
+ isPrivateBrowsingWindow = true;
+ }
+ widgetInitData.mIsPrivate = isPrivateBrowsingWindow;
+
+ nsresult rv =
+ window->Initialize(parent, center ? aParent : nullptr, aInitialWidth,
+ aInitialHeight, aIsHiddenWindow, widgetInitData);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ 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 (RefPtr<nsDocShell> docShell = window->GetDocShell()) {
+ MOZ_ASSERT(docShell->GetBrowsingContext()->IsChrome());
+
+ docShell->SetPrivateBrowsing(isPrivateBrowsingWindow);
+ docShell->SetRemoteTabs(aChromeMask &
+ nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
+ docShell->SetRemoteSubframes(aChromeMask &
+ nsIWebBrowserChrome::CHROME_FISSION_WINDOW);
+
+ // Eagerly create an about:blank content viewer with the right principal
+ // here, rather than letting it happen in the upcoming call to
+ // SetInitialPrincipal. 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.
+ // Toplevel chrome windows always have a system principal, so ensure the
+ // initial window is created with that principal.
+ // We need to do this even when creating a chrome window to load a content
+ // window, see bug 799348 comment 13 for details about what previously
+ // happened here due to it using the subject principal.
+ if (nsContentUtils::IsInitialized()) { // Sometimes this happens really
+ // early. See bug 793370.
+ MOZ_DIAGNOSTIC_ASSERT(
+ nsContentUtils::LegacyIsCallerChromeOrNativeCode(),
+ "Previously, this method would use the subject principal rather than "
+ "hardcoding the system principal");
+ // Use the system principal as the storage principal too until the new
+ // window finishes navigating and gets a real storage principal.
+ rv = docShell->CreateAboutBlankContentViewer(
+ nsContentUtils::GetSystemPrincipal(),
+ nsContentUtils::GetSystemPrincipal(),
+ /* aCsp = */ nullptr, /* aBaseURI = */ nullptr,
+ /* aIsInitialDocument = */ true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ RefPtr<dom::Document> doc = docShell->GetDocument();
+ NS_ENSURE_TRUE(!!doc, NS_ERROR_FAILURE);
+ MOZ_ASSERT(doc->IsInitialDocument(),
+ "Document should be an initial document");
+ }
+
+ // Begin loading the URL provided.
+ if (aUrl) {
+ RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aUrl);
+ loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
+ loadState->SetFirstParty(true);
+ rv = docShell->LoadURI(loadState, /* aSetNavigating */ true);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+
+ window.forget(aResult);
+ if (parent) parent->AddChildWindow(*aResult);
+
+ if (center) rv = (*aResult)->Center(parent, parent ? false : true, false);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsAppShellService::GetHiddenWindow(nsIAppWindow** aWindow) {
+ NS_ENSURE_ARG_POINTER(aWindow);
+
+ EnsureHiddenWindow();
+
+ *aWindow = mHiddenWindow;
+ NS_IF_ADDREF(*aWindow);
+ return *aWindow ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsAppShellService::GetHiddenDOMWindow(mozIDOMWindowProxy** aWindow) {
+ NS_ENSURE_ARG_POINTER(aWindow);
+
+ EnsureHiddenWindow();
+
+ 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::GetHasHiddenWindow(bool* aHasHiddenWindow) {
+ NS_ENSURE_ARG_POINTER(aHasHiddenWindow);
+
+ *aHasHiddenWindow = !!mHiddenWindow;
+ 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(nsIAppWindow* 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);
+
+ // Toplevel chrome windows always have a system principal, so ensure the
+ // initial window is created with that principal.
+ // We need to do this even when creating a chrome window to load a content
+ // window, see bug 799348 comment 13 for details about what previously
+ // happened here due to it using the subject principal.
+ MOZ_DIAGNOSTIC_ASSERT(
+ nsContentUtils::LegacyIsCallerChromeOrNativeCode(),
+ "Previously, this method would use the subject principal rather than "
+ "hardcoding the system principal");
+ domWindow->SetInitialPrincipal(nsContentUtils::GetSystemPrincipal(), nullptr,
+ Nothing());
+
+ // 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);
+ AppWindow* appWindow = static_cast<AppWindow*>(aWindow);
+ appWindow->WasRegistered();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsAppShellService::UnregisterTopLevelWindow(nsIAppWindow* 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;
+ }
+
+ // 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();
+ }
+ } 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 0000000000..8e85958897
--- /dev/null
+++ b/xpfe/appshell/nsAppShellService.h
@@ -0,0 +1,53 @@
+/* -*- 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 "AppWindow.h"
+#include "nsStringFwd.h"
+#include "nsIRemoteTab.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();
+
+ void EnsureHiddenWindow();
+
+ nsresult JustCreateTopWindow(nsIAppWindow* aParent, nsIURI* aUrl,
+ uint32_t aChromeMask, int32_t aInitialWidth,
+ int32_t aInitialHeight, bool aIsHiddenWindow,
+ mozilla::AppWindow** aResult);
+ uint32_t CalculateWindowZLevel(nsIAppWindow* aParent, uint32_t aChromeMask);
+
+ RefPtr<mozilla::AppWindow> mHiddenWindow;
+ 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 0000000000..3e7a4fa4b8
--- /dev/null
+++ b/xpfe/appshell/nsAppShellWindowEnumerator.cpp
@@ -0,0 +1,340 @@
+/* -*- 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 "mozilla/dom/Document.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIAppWindow.h"
+#include "mozilla/dom/Element.h"
+
+#include "nsWindowMediator.h"
+
+using mozilla::dom::Document;
+using mozilla::dom::Element;
+
+//
+// static helper functions
+//
+
+static void GetAttribute(nsIAppWindow* inWindow, const nsAString& inAttribute,
+ nsAString& outValue);
+static void GetWindowType(nsIAppWindow* inWindow, nsString& outType);
+
+static Element* GetElementFromDocShell(nsIDocShell* aShell) {
+ nsCOMPtr<nsIContentViewer> cv;
+ aShell->GetContentViewer(getter_AddRefs(cv));
+ if (cv) {
+ RefPtr<Document> doc = cv->GetDocument();
+ if (doc) {
+ return doc->GetDocumentElement();
+ }
+ }
+
+ return nullptr;
+}
+
+// generic "retrieve the value of a XUL attribute" function
+void GetAttribute(nsIAppWindow* inWindow, const nsAString& inAttribute,
+ nsAString& outValue) {
+ nsCOMPtr<nsIDocShell> shell;
+ if (inWindow && NS_SUCCEEDED(inWindow->GetDocShell(getter_AddRefs(shell)))) {
+ RefPtr<Element> webshellElement = GetElementFromDocShell(shell);
+ 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(nsIAppWindow* aWindow, nsString& outType) {
+ GetAttribute(aWindow, u"windowtype"_ns, outType);
+}
+
+//
+// nsWindowInfo
+//
+
+nsWindowInfo::nsWindowInfo(nsIAppWindow* inWindow, int32_t inTimeStamp)
+ : mWindow(inWindow),
+ mTimeStamp(inTimeStamp),
+ mZLevel(nsIAppWindow::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
+//
+
+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_ERROR_FAILURE;
+}
+
+//
+// nsASAppWindowEnumerator
+//
+
+nsASAppWindowEnumerator::nsASAppWindowEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& aMediator)
+ : nsAppShellWindowEnumerator(aTypeString, aMediator) {}
+
+nsASAppWindowEnumerator::~nsASAppWindowEnumerator() {}
+
+NS_IMETHODIMP nsASAppWindowEnumerator::GetNext(nsISupports** retval) {
+ if (!retval) return NS_ERROR_INVALID_ARG;
+
+ *retval = nullptr;
+ if (mCurrentPosition) {
+ CallQueryInterface(mCurrentPosition->mWindow, retval);
+ mCurrentPosition = FindNext();
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+//
+// 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 AppWindowEarlyToLateEnumerator::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;
+}
+
+//
+// nsASAppWindowEarlyToLateEnumerator
+//
+
+nsASAppWindowEarlyToLateEnumerator::nsASAppWindowEarlyToLateEnumerator(
+ const char16_t* aTypeString, nsWindowMediator& aMediator)
+ : nsASAppWindowEnumerator(aTypeString, aMediator) {
+ mCurrentPosition = aMediator.mOldestWindow;
+ AdjustInitialPosition();
+}
+
+nsASAppWindowEarlyToLateEnumerator::~nsASAppWindowEarlyToLateEnumerator() {}
+
+nsWindowInfo* nsASAppWindowEarlyToLateEnumerator::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;
+}
+
+//
+// nsASAppWindowFrontToBackEnumerator
+//
+
+nsASAppWindowFrontToBackEnumerator::nsASAppWindowFrontToBackEnumerator(
+ const char16_t* aTypeString, nsWindowMediator& aMediator)
+ : nsASAppWindowEnumerator(aTypeString, aMediator) {
+ mCurrentPosition = aMediator.mTopmostWindow;
+ AdjustInitialPosition();
+}
+
+nsASAppWindowFrontToBackEnumerator::~nsASAppWindowFrontToBackEnumerator() {}
+
+nsWindowInfo* nsASAppWindowFrontToBackEnumerator::FindNext() {
+ nsWindowInfo *info, *listEnd;
+ bool allWindows = mType.IsEmpty();
+
+ // see AppWindowEarlyToLateEnumerator::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;
+}
+
+//
+// nsASAppWindowBackToFrontEnumerator
+//
+
+nsASAppWindowBackToFrontEnumerator::nsASAppWindowBackToFrontEnumerator(
+ const char16_t* aTypeString, nsWindowMediator& aMediator)
+ : nsASAppWindowEnumerator(aTypeString, aMediator) {
+ mCurrentPosition =
+ aMediator.mTopmostWindow ? aMediator.mTopmostWindow->mHigher : nullptr;
+ AdjustInitialPosition();
+}
+
+nsASAppWindowBackToFrontEnumerator::~nsASAppWindowBackToFrontEnumerator() {}
+
+nsWindowInfo* nsASAppWindowBackToFrontEnumerator::FindNext() {
+ nsWindowInfo *info, *listEnd;
+ bool allWindows = mType.IsEmpty();
+
+ // see AppWindowEarlyToLateEnumerator::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 0000000000..b5dc828dcf
--- /dev/null
+++ b/xpfe/appshell/nsAppShellWindowEnumerator.h
@@ -0,0 +1,133 @@
+/* -*- 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 "nsSimpleEnumerator.h"
+#include "nsIAppWindow.h"
+
+class nsWindowMediator;
+
+//
+// nsWindowInfo
+//
+
+struct nsWindowInfo {
+ nsWindowInfo(nsIAppWindow* inWindow, int32_t inTimeStamp);
+ ~nsWindowInfo();
+
+ nsCOMPtr<nsIAppWindow> 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 nsSimpleEnumerator {
+ friend class nsWindowMediator;
+
+ public:
+ nsAppShellWindowEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+ NS_IMETHOD GetNext(nsISupports** retval) override = 0;
+ NS_IMETHOD HasMoreElements(bool* retval) override;
+
+ protected:
+ ~nsAppShellWindowEnumerator() override;
+
+ 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) override;
+};
+
+class nsASAppWindowEnumerator : public nsAppShellWindowEnumerator {
+ public:
+ nsASAppWindowEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+ virtual ~nsASAppWindowEnumerator();
+ NS_IMETHOD GetNext(nsISupports** retval) override;
+
+ const nsID& DefaultInterface() override { return NS_GET_IID(nsIAppWindow); }
+};
+
+//
+// concrete enumerators
+//
+
+class nsASDOMWindowEarlyToLateEnumerator : public nsASDOMWindowEnumerator {
+ public:
+ nsASDOMWindowEarlyToLateEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+
+ virtual ~nsASDOMWindowEarlyToLateEnumerator();
+
+ protected:
+ virtual nsWindowInfo* FindNext() override;
+};
+
+class nsASAppWindowEarlyToLateEnumerator : public nsASAppWindowEnumerator {
+ public:
+ nsASAppWindowEarlyToLateEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+
+ virtual ~nsASAppWindowEarlyToLateEnumerator();
+
+ protected:
+ virtual nsWindowInfo* FindNext() override;
+};
+
+class nsASAppWindowFrontToBackEnumerator : public nsASAppWindowEnumerator {
+ public:
+ nsASAppWindowFrontToBackEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+
+ virtual ~nsASAppWindowFrontToBackEnumerator();
+
+ protected:
+ virtual nsWindowInfo* FindNext() override;
+};
+
+class nsASAppWindowBackToFrontEnumerator : public nsASAppWindowEnumerator {
+ public:
+ nsASAppWindowBackToFrontEnumerator(const char16_t* aTypeString,
+ nsWindowMediator& inMediator);
+
+ virtual ~nsASAppWindowBackToFrontEnumerator();
+
+ protected:
+ virtual nsWindowInfo* FindNext() override;
+};
+
+#endif
diff --git a/xpfe/appshell/nsChromeTreeOwner.cpp b/xpfe/appshell/nsChromeTreeOwner.cpp
new file mode 100644
index 0000000000..a84c3faa90
--- /dev/null
+++ b/xpfe/appshell/nsChromeTreeOwner.cpp
@@ -0,0 +1,480 @@
+/* -*- 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 "AppWindow.h"
+
+// Helper Classes
+#include "nsString.h"
+#include "nsIDocShellTreeItem.h"
+
+// Interfaces needed to include
+#include "nsIPrompt.h"
+#include "nsIAuthPrompt.h"
+#include "nsIWebProgress.h"
+#include "nsIWidget.h"
+#include "mozilla/dom/Element.h"
+
+using namespace mozilla;
+
+//*****************************************************************************
+// nsChromeTreeOwner string literals
+//*****************************************************************************
+
+const nsLiteralString kPersist(u"persist");
+const nsLiteralString kScreenX(u"screenX");
+const nsLiteralString kScreenY(u"screenY");
+const nsLiteralString kWidth(u"width");
+const nsLiteralString kHeight(u"height");
+const nsLiteralString kSizemode(u"sizemode");
+const nsLiteralString kSpace(u" ");
+
+//*****************************************************************************
+//*** nsChromeTreeOwner: Object Management
+//*****************************************************************************
+
+nsChromeTreeOwner::nsChromeTreeOwner() : mAppWindow(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(mAppWindow);
+ return mAppWindow->GetInterface(aIID, aSink);
+ }
+ if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetInterface(aIID, aSink);
+ }
+ if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome))) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetInterface(aIID, aSink);
+ }
+ if (aIID.Equals(NS_GET_IID(nsIAppWindow))) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->QueryInterface(aIID, aSink);
+ }
+
+ return QueryInterface(aIID, aSink);
+}
+
+//*****************************************************************************
+// nsChromeTreeOwner::nsIDocShellTreeOwner
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsChromeTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
+ bool aPrimary) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->ContentShellAdded(aContentShell, aPrimary);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->ContentShellRemoved(aContentShell);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetPrimaryContentShell(
+ nsIDocShellTreeItem** aShell) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetPrimaryContentShell(aShell);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->RemoteTabAdded(aTab, aPrimary);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::RemoteTabRemoved(nsIRemoteTab* aTab) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->RemoteTabRemoved(aTab);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetPrimaryRemoteTab(aTab);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetPrimaryContentBrowsingContext(
+ mozilla::dom::BrowsingContext** aBc) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetPrimaryContentBrowsingContext(aBc);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetRootShellSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetRootShellSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
+ int32_t aCX, int32_t aCY) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SizeShellTo(aShellItem, aCX, aCY);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::SetPersistence(bool aPersistPosition, bool aPersistSize,
+ bool aPersistSizeMode) {
+ NS_ENSURE_STATE(mAppWindow);
+ nsCOMPtr<dom::Element> docShellElement = mAppWindow->GetWindowDOMElement();
+ if (!docShellElement) return NS_ERROR_FAILURE;
+
+ nsAutoString persistString;
+ docShellElement->GetAttribute(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(kSpace + aString); \
+ saveString = true; \
+ }
+ FIND_PERSIST_STRING(kScreenX, aPersistPosition);
+ FIND_PERSIST_STRING(kScreenY, aPersistPosition);
+ FIND_PERSIST_STRING(kWidth, aPersistSize);
+ FIND_PERSIST_STRING(kHeight, aPersistSize);
+ FIND_PERSIST_STRING(kSizemode, aPersistSizeMode);
+
+ ErrorResult rv;
+ if (saveString) {
+ docShellElement->SetAttribute(kPersist, persistString, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetPersistence(bool* aPersistPosition, bool* aPersistSize,
+ bool* aPersistSizeMode) {
+ NS_ENSURE_STATE(mAppWindow);
+ nsCOMPtr<dom::Element> docShellElement = mAppWindow->GetWindowDOMElement();
+ if (!docShellElement) return NS_ERROR_FAILURE;
+
+ nsAutoString persistString;
+ docShellElement->GetAttribute(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(kScreenX) > kNotFound ||
+ persistString.Find(kScreenY) > kNotFound;
+ if (aPersistSize)
+ *aPersistSize = persistString.Find(kWidth) > kNotFound ||
+ persistString.Find(kHeight) > kNotFound;
+ if (aPersistSizeMode)
+ *aPersistSizeMode = persistString.Find(kSizemode) > kNotFound;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetTabCount(uint32_t* aResult) {
+ if (mAppWindow) {
+ return mAppWindow->GetTabCount(aResult);
+ }
+
+ *aResult = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetHasPrimaryContent(bool* aResult) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->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::Destroy() {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->Destroy();
+}
+
+double nsChromeTreeOwner::GetWidgetCSSToDeviceScale() {
+ return mAppWindow ? mAppWindow->GetWidgetCSSToDeviceScale() : 1.0;
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetDevicePixelsPerDesktopPixel(
+ double* aScale) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetDevicePixelsPerDesktopPixel(aScale);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetPositionDesktopPix(int32_t x, int32_t y) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetPositionDesktopPix(x, y);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetPosition(int32_t x, int32_t y) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetPosition(x, y);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetPosition(int32_t* x, int32_t* y) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetPosition(x, y);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetSize(int32_t cx, int32_t cy,
+ bool fRepaint) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetSize(cx, cy, fRepaint);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetSize(int32_t* cx, int32_t* cy) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->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(mAppWindow);
+ return mAppWindow->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(mAppWindow);
+ return mAppWindow->GetPositionAndSize(x, y, cx, cy);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::SetDimensions(DimensionRequest&& aRequest) {
+ NS_ENSURE_STATE(mAppWindow);
+ if (aRequest.mDimensionKind == DimensionKind::Outer) {
+ return mAppWindow->SetDimensions(std::move(aRequest));
+ }
+
+ MOZ_TRY(aRequest.SupplementFrom(this));
+ return aRequest.ApplyInnerTo(this, /* aAsRootShell */ true);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
+ int32_t* aY, int32_t* aCX, int32_t* aCY) {
+ NS_ENSURE_STATE(mAppWindow);
+ if (aDimensionKind == DimensionKind::Outer) {
+ return mAppWindow->GetDimensions(aDimensionKind, aX, aY, aCX, aCY);
+ }
+ if (aY || aX) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ return GetRootShellSize(aCX, aCY);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::Repaint(bool aForce) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->Repaint(aForce);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetParentWidget(nsIWidget** aParentWidget) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->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(mAppWindow);
+ return mAppWindow->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(mAppWindow);
+ return mAppWindow->GetNativeHandle(aNativeHandle);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetVisibility(bool* aVisibility) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetVisibility(bool aVisibility) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetEnabled(bool* aEnabled) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetEnabled(aEnabled);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetEnabled(bool aEnable) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetEnabled(aEnable);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetMainWidget(nsIWidget** aMainWidget) {
+ NS_ENSURE_ARG_POINTER(aMainWidget);
+ NS_ENSURE_STATE(mAppWindow);
+
+ *aMainWidget = mAppWindow->mWindow;
+ NS_IF_ADDREF(*aMainWidget);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::GetTitle(nsAString& aTitle) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetTitle(aTitle);
+}
+
+NS_IMETHODIMP nsChromeTreeOwner::SetTitle(const nsAString& aTitle) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->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) {
+ NS_ENSURE_STATE(mAppWindow);
+
+ // If loading a new root .xul document, then redo chrome.
+ if (aWebProgress) {
+ nsCOMPtr<nsIDocShell> docshell;
+ mAppWindow->GetDocShell(getter_AddRefs(docshell));
+
+ nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(docshell));
+ if (webProgress != aWebProgress) {
+ return NS_OK;
+ }
+ }
+
+ mAppWindow->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 aState) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ uint32_t aEvent) {
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsChromeTreeOwner: Helpers
+//*****************************************************************************
+
+//*****************************************************************************
+// nsChromeTreeOwner: Accessors
+//*****************************************************************************
+
+void nsChromeTreeOwner::AppWindow(mozilla::AppWindow* aAppWindow) {
+ mAppWindow = aAppWindow;
+}
+
+mozilla::AppWindow* nsChromeTreeOwner::AppWindow() { return mAppWindow; }
diff --git a/xpfe/appshell/nsChromeTreeOwner.h b/xpfe/appshell/nsChromeTreeOwner.h
new file mode 100644
index 0000000000..768fc44628
--- /dev/null
+++ b/xpfe/appshell/nsChromeTreeOwner.h
@@ -0,0 +1,51 @@
+/* -*- 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"
+
+namespace mozilla {
+class AppWindow;
+}
+
+class nsChromeTreeOwner : public nsIDocShellTreeOwner,
+ public nsIBaseWindow,
+ public nsIInterfaceRequestor,
+ public nsIWebProgressListener,
+ public nsSupportsWeakReference {
+ friend class mozilla::AppWindow;
+
+ public:
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIBASEWINDOW
+ NS_DECL_NSIDOCSHELLTREEOWNER
+ NS_DECL_NSIWEBPROGRESSLISTENER
+
+ protected:
+ nsChromeTreeOwner();
+ virtual ~nsChromeTreeOwner();
+
+ void AppWindow(mozilla::AppWindow* aAppWindow);
+ mozilla::AppWindow* AppWindow();
+
+ protected:
+ mozilla::AppWindow* mAppWindow;
+};
+
+#endif /* nsChromeTreeOwner_h__ */
diff --git a/xpfe/appshell/nsContentTreeOwner.cpp b/xpfe/appshell/nsContentTreeOwner.cpp
new file mode 100644
index 0000000000..f02fc3dcda
--- /dev/null
+++ b/xpfe/appshell/nsContentTreeOwner.cpp
@@ -0,0 +1,660 @@
+/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=2 sw=2 et tw=80:
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Local Includes
+#include "nsContentTreeOwner.h"
+#include "AppWindow.h"
+
+// Interfaces needed to be included
+#include "nsIDOMWindow.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsIBrowserDOMWindow.h"
+#include "nsIOpenWindowInfo.h"
+#include "nsIPrompt.h"
+#include "nsIAuthPrompt.h"
+#include "nsIXULBrowserWindow.h"
+#include "nsIPrincipal.h"
+#include "nsIURIFixup.h"
+#include "nsIWebNavigation.h"
+#include "nsDocShellCID.h"
+#include "nsIMIMEInfo.h"
+#include "nsIWidget.h"
+#include "nsWindowWatcher.h"
+#include "nsIWindowMediator.h"
+#include "mozilla/Components.h"
+#include "mozilla/NullPrincipal.h"
+#include "nsDocShell.h"
+#include "nsDocShellLoadState.h"
+#include "nsQueryActor.h"
+
+#include "nsIScriptObjectPrincipal.h"
+#include "nsIURI.h"
+#include "mozilla/dom/Document.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;
+
+//*****************************************************************************
+//*** nsContentTreeOwner: Object Management
+//*****************************************************************************
+
+nsContentTreeOwner::nsContentTreeOwner(bool fPrimary)
+ : mAppWindow(nullptr), mPrimary(fPrimary) {}
+
+//*****************************************************************************
+// 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(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
+NS_INTERFACE_MAP_END
+
+//*****************************************************************************
+// nsContentTreeOwner::nsIInterfaceRequestor
+//*****************************************************************************
+
+NS_IMETHODIMP nsContentTreeOwner::GetInterface(const nsIID& aIID,
+ void** aSink) {
+ NS_ENSURE_ARG_POINTER(aSink);
+ *aSink = nullptr;
+
+ if (aIID.Equals(NS_GET_IID(nsIPrompt))) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetInterface(aIID, aSink);
+ }
+ if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetInterface(aIID, aSink);
+ }
+ if (aIID.Equals(NS_GET_IID(nsIDocShellTreeItem))) {
+ NS_ENSURE_STATE(mAppWindow);
+ nsCOMPtr<nsIDocShell> shell;
+ mAppWindow->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(mAppWindow);
+ nsCOMPtr<nsIDocShellTreeItem> shell;
+ mAppWindow->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(nsIAppWindow))) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->QueryInterface(aIID, aSink);
+ }
+
+ return QueryInterface(aIID, aSink);
+}
+
+//*****************************************************************************
+// nsContentTreeOwner::nsIDocShellTreeOwner
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsContentTreeOwner::ContentShellAdded(nsIDocShellTreeItem* aContentShell,
+ bool aPrimary) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->ContentShellAdded(aContentShell, aPrimary);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->ContentShellRemoved(aContentShell);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetPrimaryContentShell(aShell);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::RemoteTabAdded(nsIRemoteTab* aTab, bool aPrimary) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->RemoteTabAdded(aTab, aPrimary);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::RemoteTabRemoved(nsIRemoteTab* aTab) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->RemoteTabRemoved(aTab);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetPrimaryRemoteTab(nsIRemoteTab** aTab) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetPrimaryRemoteTab(aTab);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetPrimaryContentBrowsingContext(
+ mozilla::dom::BrowsingContext** aBc) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetPrimaryContentBrowsingContext(aBc);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetPrimaryContentSize(int32_t* aWidth, int32_t* aHeight) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::SetPrimaryContentSize(int32_t aWidth, int32_t aHeight) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetPrimaryContentSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetRootShellSize(int32_t* aWidth, int32_t* aHeight) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetRootShellSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::SetRootShellSize(int32_t aWidth, int32_t aHeight) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetRootShellSize(aWidth, aHeight);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
+ int32_t aCX, int32_t aCY) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SizeShellTo(aShellItem, aCX, aCY);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::SetPersistence(bool aPersistPosition, bool aPersistSize,
+ bool aPersistSizeMode) {
+ NS_ENSURE_STATE(mAppWindow);
+ nsCOMPtr<dom::Element> docShellElement = mAppWindow->GetWindowDOMElement();
+ if (!docShellElement) return NS_ERROR_FAILURE;
+
+ nsAutoString persistString;
+ docShellElement->GetAttr(nsGkAtoms::persist, persistString);
+
+ bool saveString = false;
+ int32_t index;
+
+ // Set X
+ index = persistString.Find(u"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(u"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(u"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(u"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(u"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(u"persist"_ns, persistString, rv);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetPersistence(bool* aPersistPosition, bool* aPersistSize,
+ bool* aPersistSizeMode) {
+ NS_ENSURE_STATE(mAppWindow);
+ nsCOMPtr<dom::Element> docShellElement = mAppWindow->GetWindowDOMElement();
+ if (!docShellElement) return NS_ERROR_FAILURE;
+
+ nsAutoString persistString;
+ docShellElement->GetAttr(nsGkAtoms::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(u"screenX") >= 0 ||
+ persistString.Find(u"screenY") >= 0;
+ }
+ if (aPersistSize) {
+ *aPersistSize =
+ persistString.Find(u"width") >= 0 || persistString.Find(u"height") >= 0;
+ }
+ if (aPersistSizeMode) {
+ *aPersistSizeMode = persistString.Find(u"sizemode") >= 0;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetTabCount(uint32_t* aResult) {
+ if (mAppWindow) {
+ return mAppWindow->GetTabCount(aResult);
+ }
+
+ *aResult = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetHasPrimaryContent(bool* aResult) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetHasPrimaryContent(aResult);
+}
+
+//*****************************************************************************
+// nsContentTreeOwner::nsIWebBrowserChrome
+//*****************************************************************************
+
+NS_IMETHODIMP nsContentTreeOwner::SetLinkStatus(const nsAString& aStatusText) {
+ NS_ENSURE_STATE(mAppWindow);
+
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow;
+ mAppWindow->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow));
+
+ if (xulBrowserWindow) {
+ xulBrowserWindow->SetOverLink(aStatusText);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetChromeFlags(uint32_t aChromeFlags) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetChromeFlags(aChromeFlags);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetChromeFlags(uint32_t* aChromeFlags) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetChromeFlags(aChromeFlags);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::ShowAsModal() {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->ShowModal();
+}
+
+NS_IMETHODIMP nsContentTreeOwner::IsWindowModal(bool* _retval) {
+ NS_ENSURE_STATE(mAppWindow);
+ *_retval = mAppWindow->mContinueModalLoop;
+ return NS_OK;
+}
+
+//*****************************************************************************
+// 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::Destroy() {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->Destroy();
+}
+
+double nsContentTreeOwner::GetWidgetCSSToDeviceScale() {
+ return mAppWindow ? mAppWindow->GetWidgetCSSToDeviceScale() : 1.0;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetDevicePixelsPerDesktopPixel(
+ double* aScale) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetDevicePixelsPerDesktopPixel(aScale);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetPositionDesktopPix(int32_t aX,
+ int32_t aY) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetPositionDesktopPix(aX, aY);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetPosition(int32_t aX, int32_t aY) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetPosition(aX, aY);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetPosition(int32_t* aX, int32_t* aY) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetPosition(aX, aY);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetSize(int32_t aCX, int32_t aCY,
+ bool aRepaint) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetSize(aCX, aCY, aRepaint);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetSize(int32_t* aCX, int32_t* aCY) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->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(mAppWindow);
+ return mAppWindow->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(mAppWindow);
+ return mAppWindow->GetPositionAndSize(aX, aY, aCX, aCY);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::SetDimensions(DimensionRequest&& aRequest) {
+ NS_ENSURE_STATE(mAppWindow);
+ if (aRequest.mDimensionKind == DimensionKind::Outer) {
+ return mAppWindow->SetDimensions(std::move(aRequest));
+ }
+
+ MOZ_TRY(aRequest.SupplementFrom(this));
+ return aRequest.ApplyInnerTo(this, /* aAsRootShell */ false);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
+ int32_t* aY, int32_t* aCX, int32_t* aCY) {
+ NS_ENSURE_STATE(mAppWindow);
+ if (aDimensionKind == DimensionKind::Outer) {
+ return mAppWindow->GetDimensions(aDimensionKind, aX, aY, aCX, aCY);
+ }
+ if (aY || aX) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ return GetPrimaryContentSize(aCX, aCY);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::Repaint(bool aForce) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->Repaint(aForce);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetParentWidget(nsIWidget** aParentWidget) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->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(mAppWindow);
+ return mAppWindow->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(mAppWindow);
+ return mAppWindow->GetNativeHandle(aNativeHandle);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetVisibility(bool* aVisibility) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetVisibility(bool aVisibility) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetVisibility(aVisibility);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetEnabled(bool* aEnabled) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->GetEnabled(aEnabled);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetEnabled(bool aEnable) {
+ NS_ENSURE_STATE(mAppWindow);
+ return mAppWindow->SetEnabled(aEnable);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetMainWidget(nsIWidget** aMainWidget) {
+ NS_ENSURE_ARG_POINTER(aMainWidget);
+ NS_ENSURE_STATE(mAppWindow);
+
+ *aMainWidget = mAppWindow->mWindow;
+ NS_IF_ADDREF(*aMainWidget);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsContentTreeOwner::GetTitle(nsAString& aTitle) {
+ NS_ENSURE_STATE(mAppWindow);
+
+ return mAppWindow->GetTitle(aTitle);
+}
+
+NS_IMETHODIMP nsContentTreeOwner::SetTitle(const nsAString& aTitle) {
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsContentTreeOwner: nsIWindowProvider
+//*****************************************************************************
+NS_IMETHODIMP
+nsContentTreeOwner::ProvideWindow(
+ nsIOpenWindowInfo* aOpenWindowInfo, uint32_t aChromeFlags,
+ bool aCalledFromJS, nsIURI* aURI, const nsAString& aName,
+ const nsACString& aFeatures, bool aForceNoOpener, bool aForceNoReferrer,
+ bool aIsPopupRequested, nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
+ dom::BrowsingContext** aReturn) {
+ NS_ENSURE_ARG_POINTER(aOpenWindowInfo);
+
+ RefPtr<dom::BrowsingContext> parent = aOpenWindowInfo->GetParent();
+
+ *aReturn = nullptr;
+
+ if (!mAppWindow) {
+ // Nothing to do here
+ return NS_OK;
+ }
+
+#ifdef DEBUG
+ nsCOMPtr<nsIDocShell> docshell = parent->GetDocShell();
+ nsCOMPtr<nsIDocShellTreeOwner> parentOwner = do_GetInterface(docshell);
+ NS_ASSERTION(
+ SameCOMIdentity(parentOwner, static_cast<nsIDocShellTreeOwner*>(this)),
+ "Parent from wrong docshell tree?");
+#endif
+
+ int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation(
+ parent->GetDOMWindow(), aChromeFlags, aCalledFromJS,
+ aOpenWindowInfo->GetIsForPrinting());
+
+ if (openLocation != nsIBrowserDOMWindow::OPEN_NEWTAB &&
+ openLocation != nsIBrowserDOMWindow::OPEN_CURRENTWINDOW &&
+ openLocation != nsIBrowserDOMWindow::OPEN_PRINT_BROWSER) {
+ // Just open a window normally
+ return NS_OK;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> domWin;
+ mAppWindow->GetWindowDOMWindow(getter_AddRefs(domWin));
+ nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(domWin);
+ if (!chromeWin) {
+ // Really odd... but whatever
+ NS_WARNING("AppWindow'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;
+ }
+ if (aForceNoReferrer) {
+ flags |= nsIBrowserDOMWindow::OPEN_NO_REFERRER;
+ }
+
+ // Get a new rendering area from the browserDOMWin.
+ // Since we are not loading any URI, we follow the principle of least
+ // privilege and use a nullPrincipal as the triggeringPrincipal.
+ //
+ // This method handles setting the opener for us, so we don't need to set it
+ // ourselves.
+ RefPtr<NullPrincipal> nullPrincipal =
+ NullPrincipal::CreateWithoutOriginAttributes();
+ return browserDOMWin->CreateContentWindow(aURI, aOpenWindowInfo,
+ openLocation, flags,
+ nullPrincipal, nullptr, aReturn);
+ }
+}
+
+//*****************************************************************************
+// nsContentTreeOwner: Accessors
+//*****************************************************************************
+
+void nsContentTreeOwner::AppWindow(mozilla::AppWindow* aAppWindow) {
+ mAppWindow = aAppWindow;
+}
+
+mozilla::AppWindow* nsContentTreeOwner::AppWindow() { return mAppWindow; }
+
+/* this implementation focuses another window. if there isn't another
+ window to focus, we do nothing. */
+NS_IMETHODIMP
+nsContentTreeOwner::Blur() {
+ NS_DEFINE_CID(kWindowMediatorCID, NS_WINDOWMEDIATOR_CID);
+
+ nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
+ nsCOMPtr<nsIAppWindow> appWindow;
+ bool more, foundUs;
+
+ {
+ nsCOMPtr<nsIWindowMediator> windowMediator(
+ do_GetService(kWindowMediatorCID));
+ if (windowMediator) {
+ windowMediator->GetZOrderAppWindowEnumerator(
+ nullptr, 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<nsIAppWindow> nextAppWindow;
+
+ windowEnumerator->GetNext(getter_AddRefs(nextWindow));
+ nextAppWindow = do_QueryInterface(nextWindow);
+
+ // got it!(?)
+ if (foundUs) {
+ appWindow = nextAppWindow;
+ break;
+ }
+
+ // remember the very first one, in case we have to wrap
+ if (!appWindow) appWindow = nextAppWindow;
+
+ // look for us
+ if (nextAppWindow == mAppWindow) {
+ foundUs = true;
+ }
+
+ windowEnumerator->HasMoreElements(&more);
+ }
+
+ // change focus to the window we just found
+ if (appWindow) {
+ nsCOMPtr<nsIDocShell> docshell;
+ appWindow->GetDocShell(getter_AddRefs(docshell));
+ if (!docshell) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> domWindow = docshell->GetWindow();
+ if (domWindow) domWindow->Focus(mozilla::dom::CallerType::System);
+ }
+ return NS_OK;
+}
diff --git a/xpfe/appshell/nsContentTreeOwner.h b/xpfe/appshell/nsContentTreeOwner.h
new file mode 100644
index 0000000000..a2544e0bfb
--- /dev/null
+++ b/xpfe/appshell/nsContentTreeOwner.h
@@ -0,0 +1,61 @@
+/* -*- 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 "nsIWebBrowserChrome.h"
+#include "nsIWindowProvider.h"
+
+namespace mozilla {
+class AppWindow;
+}
+
+class nsContentTreeOwner final : public nsIDocShellTreeOwner,
+ public nsIBaseWindow,
+ public nsIInterfaceRequestor,
+ public nsIWebBrowserChrome,
+ public nsIWindowProvider {
+ friend class mozilla::AppWindow;
+
+ public:
+ NS_DECL_ISUPPORTS
+
+ NS_DECL_NSIBASEWINDOW
+ NS_DECL_NSIDOCSHELLTREEOWNER
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIWINDOWPROVIDER
+
+ /* nsIWebBrowserChrome (Get/SetDimensions overlap with nsIBaseWindow) */
+ NS_IMETHOD SetLinkStatus(const nsAString& status) override;
+ NS_IMETHOD GetChromeFlags(uint32_t* aChromeFlags) override;
+ NS_IMETHOD SetChromeFlags(uint32_t aChromeFlags) override;
+ NS_IMETHOD ShowAsModal() override;
+ NS_IMETHOD IsWindowModal(bool* _retval) override;
+ NS_IMETHOD Blur() override;
+
+ protected:
+ explicit nsContentTreeOwner(bool fPrimary);
+ virtual ~nsContentTreeOwner() = default;
+
+ void AppWindow(mozilla::AppWindow* aAppWindow);
+ mozilla::AppWindow* AppWindow();
+
+ protected:
+ mozilla::AppWindow* mAppWindow;
+ bool mPrimary;
+};
+
+#endif /* nsContentTreeOwner_h__ */
diff --git a/xpfe/appshell/nsIAppShellService.idl b/xpfe/appshell/nsIAppShellService.idl
new file mode 100644
index 0000000000..d8de8ae149
--- /dev/null
+++ b/xpfe/appshell/nsIAppShellService.idl
@@ -0,0 +1,128 @@
+/* -*- 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 nsIAppWindow;
+interface nsIWindowlessBrowser;
+interface nsIURI;
+interface mozIDOMWindowProxy;
+interface nsIAppShell;
+interface nsIRemoteTab;
+
+[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 nsIWebBrowserChrome 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.
+ */
+ const long SIZE_TO_CONTENT = -1;
+ nsIAppWindow createTopLevelWindow(in nsIAppWindow aParent,
+ in nsIURI aUrl,
+ in uint32_t aChromeMask,
+ in long aInitialWidth,
+ in long aInitialHeight);
+
+ /**
+ * 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.
+ * @param aChromeMask Used to specify chrome flags that should be set on the
+ * window. See nsIWebBrowserChrome for flag definitions.
+ */
+ nsIWindowlessBrowser createWindowlessBrowser([optional] in bool aIsChrome,
+ [optional] in uint32_t aChromeMask);
+
+ [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 nsIAppWindow 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 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 nsIAppWindow 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 nsIAppWindow aWindow);
+
+ /**
+ * Whether the hidden window has been lazily created.
+ */
+ readonly attribute boolean hasHiddenWindow;
+
+ /**
+ * 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/nsIAppWindow.idl b/xpfe/appshell/nsIAppWindow.idl
new file mode 100644
index 0000000000..c72f2da4e2
--- /dev/null
+++ b/xpfe/appshell/nsIAppWindow.idl
@@ -0,0 +1,167 @@
+/* -*- 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 nsIAppWindow
+ *
+ * When the window is destroyed, it will fire a "xul-window-destroyed"
+ * notification through the global observer service.
+ */
+
+%{C++
+#include "LiveResizeListener.h"
+#include "nsTArray.h"
+%}
+
+interface nsIDocShell;
+interface nsIDocShellTreeItem;
+interface nsIXULBrowserWindow;
+interface nsIRemoteTab;
+interface mozIDOMWindowProxy;
+interface nsIOpenWindowInfo;
+webidl BrowsingContext;
+
+native LiveResizeListenerArray(nsTArray<RefPtr<mozilla::LiveResizeListener>>);
+
+[builtinclass, scriptable, uuid(d6d7a014-e28d-4c9d-8727-1cf6d870619b)]
+interface nsIAppWindow : 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
+ * primaryRemoteTab.
+ */
+ readonly attribute nsIRemoteTab primaryRemoteTab;
+
+ /**
+ * Helper for getting the BrowsingContext from either `primaryContentShell` or
+ * `primaryRemoteTab` depending on which is available.
+ */
+ readonly attribute BrowsingContext primaryContentBrowsingContext;
+
+ void remoteTabAdded(in nsIRemoteTab aTab, in boolean aPrimary);
+ void remoteTabRemoved(in nsIRemoteTab aTab);
+
+ [noscript,notxpcom] LiveResizeListenerArray getLiveResizeListeners();
+
+ /**
+ * Tell this window that it has picked up a child XUL window
+ * @param aChild the child window being added
+ */
+ void addChildWindow(in nsIAppWindow aChild);
+
+ /**
+ * Returns the difference between the inner window size (client size) and the
+ * outer window size, in CSS pixels.
+ */
+ [infallible] readonly attribute unsigned long outerToInnerHeightDifferenceInCSSPixels;
+ [infallible] readonly attribute unsigned long outerToInnerWidthDifferenceInCSSPixels;
+
+ /**
+ * Tell this window that it has lost a child XUL window
+ * @param aChild the child window being removed
+ */
+ void removeChildWindow(in nsIAppWindow 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 nsIAppWindow 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();
+
+ /**
+ * Locks the aspect ratio for a window.
+ * @param aShouldLock boolean
+ */
+ void lockAspectRatio(in bool aShouldLock);
+
+ 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;
+
+ 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 aOpenWindowInfo information about the request for a content window
+ * to be opened. Will be null for non-content loads.
+ * @return the newly minted window
+ */
+ nsIAppWindow createNewWindow(in int32_t aChromeFlags,
+ in nsIOpenWindowInfo aOpenWindowInfo);
+
+ attribute nsIXULBrowserWindow XULBrowserWindow;
+
+ /**
+ * Back-door method to make sure some stuff is done when the document is
+ * ready for layout, that would cause expensive computation otherwise later.
+ *
+ * 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 beforeStartLayout();
+
+ /**
+ * If the window was opened as a content window, this will return the initial
+ * nsIOpenWindowInfo to use.
+ */
+ readonly attribute nsIOpenWindowInfo initialOpenWindowInfo;
+
+ /**
+ * Request fast snapshot at RenderCompositor of WebRender.
+ * Since readback of Windows DirectComposition is very slow.
+ */
+ void needFastSnaphot();
+};
diff --git a/xpfe/appshell/nsIWindowMediator.idl b/xpfe/appshell/nsIWindowMediator.idl
new file mode 100644
index 0000000000..ef891c0537
--- /dev/null
+++ b/xpfe/appshell/nsIWindowMediator.idl
@@ -0,0 +1,193 @@
+/* -*- 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 nsIAppWindow;
+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 nsIAppWindows
+ */
+ nsISimpleEnumerator getAppWindowEnumerator(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 nsIAppWindows
+ */
+ nsISimpleEnumerator getZOrderAppWindowEnumerator(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);
+
+ /** This is a shortcut for getMostRecentWindow('navigator:browser'), but
+ * if that fails it also tries 'navigator:geckoview' and 'mail:3pane'.
+ *
+ * @return the topmost browser window
+ */
+ mozIDOMWindowProxy getMostRecentBrowserWindow();
+
+ /**
+ * Same as getMostRecentWindow, but ignores private browsing
+ * windows.
+ */
+ mozIDOMWindowProxy getMostRecentNonPBWindow(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 nsIAppWindow 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 nsIAppWindow 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 nsIAppWindow aWindow);
+
+ /* 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 nsIAppWindow to nsIWidgets. A pure interface
+ * would use all nsIAppWindows. 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 nsIAppWindow 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 nsIAppWindow inWindow,
+ in unsigned long inPosition,
+ in nsIAppWindow inBelow);
+
+ /** Return the window's Z level (as defined in nsIAppWindow).
+ * @param aWindow the window in question
+ * @return aWindow's z level
+ */
+ [noscript] uint32_t getZLevel(in nsIAppWindow aWindow);
+
+ /** Set the window's Z level (as defined in nsIAppWindow). 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
+ * nsIAppWindow::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 nsIAppWindow 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);
+};
diff --git a/xpfe/appshell/nsIWindowMediatorListener.idl b/xpfe/appshell/nsIWindowMediatorListener.idl
new file mode 100644
index 0000000000..39c2c64ba7
--- /dev/null
+++ b/xpfe/appshell/nsIWindowMediatorListener.idl
@@ -0,0 +1,15 @@
+/* -*- 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 nsIAppWindow;
+
+[scriptable, uuid(2F276982-0D60-4377-A595-D350BA516395)]
+interface nsIWindowMediatorListener : nsISupports
+{
+ void onOpenWindow(in nsIAppWindow window);
+ void onCloseWindow(in nsIAppWindow window);
+};
diff --git a/xpfe/appshell/nsIWindowlessBrowser.idl b/xpfe/appshell/nsIWindowlessBrowser.idl
new file mode 100644
index 0000000000..c8c08a3499
--- /dev/null
+++ b/xpfe/appshell/nsIWindowlessBrowser.idl
@@ -0,0 +1,41 @@
+/* -*- 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"
+
+interface nsIDocShell;
+webidl BrowsingContext;
+
+/**
+ * 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, builtinclass, 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();
+
+ /**
+ * Get the docshell for this browser. This is the docshell that gets
+ * navigated when the browser's nsIWebNavigation interface is used.
+ */
+ readonly attribute nsIDocShell docShell;
+
+ /**
+ * Get the Browsing Context for this browser. This is the Browsing Context
+ * that owns the docshell used for navigation.
+ */
+ readonly attribute BrowsingContext browsingContext;
+};
diff --git a/xpfe/appshell/nsIXULBrowserWindow.idl b/xpfe/appshell/nsIXULBrowserWindow.idl
new file mode 100644
index 0000000000..9adb4dd5a1
--- /dev/null
+++ b/xpfe/appshell/nsIXULBrowserWindow.idl
@@ -0,0 +1,58 @@
+/* -*- 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"
+
+interface nsIBrowser;
+interface nsIRequest;
+interface nsIInputStream;
+interface nsIDocShell;
+interface nsIRemoteTab;
+interface nsIPrincipal;
+interface mozIDOMWindowProxy;
+interface nsIContentSecurityPolicy;
+interface nsIReferrerInfo;
+
+webidl Element;
+webidl Node;
+
+/**
+ * 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
+{
+ /**
+ * Tells the object implementing this function what link we are currently
+ * over.
+ */
+ void setOverLink(in AString link);
+
+ /**
+ * Determines the appropriate target for a link.
+ */
+ AString onBeforeLinkTraversal(in AString originalTarget,
+ in nsIURI linkURI,
+ in Node linkNode,
+ in boolean isAppTab);
+
+ /**
+ * Show/hide a tooltip (when the user mouses over a link, say).
+ *
+ * x and y coordinates are in device pixels.
+ */
+ void showTooltip(in long x, in long y, in AString tooltip, in AString direction,
+ in Element browser);
+ void hideTooltip();
+
+ /**
+ * Return the number of tabs in this window.
+ */
+ uint32_t getTabCount();
+};
diff --git a/xpfe/appshell/nsWindowMediator.cpp b/xpfe/appshell/nsWindowMediator.cpp
new file mode 100644
index 0000000000..82ebe6f00d
--- /dev/null
+++ b/xpfe/appshell/nsWindowMediator.cpp
@@ -0,0 +1,749 @@
+/* -*- 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 "nsEnumeratorUtils.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsTArray.h"
+#include "nsIBaseWindow.h"
+#include "nsIWidget.h"
+#include "nsIObserverService.h"
+#include "nsISimpleEnumerator.h"
+#include "nsAppShellWindowEnumerator.h"
+#include "nsWindowMediator.h"
+#include "nsIWindowMediatorListener.h"
+#include "nsGlobalWindow.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nsIDocShell.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIAppWindow.h"
+
+using namespace mozilla;
+
+nsresult nsWindowMediator::GetDOMWindow(
+ nsIAppWindow* 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(nsIAppWindow* inWindow) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ if (!mReady) {
+ NS_ERROR("Mediator is not initialized or about to die.");
+ return NS_ERROR_FAILURE;
+ }
+
+ 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);
+
+ for (const auto& listener : mListeners.ForwardRange()) {
+ listener->OnOpenWindow(inWindow);
+ }
+
+ if (mOldestWindow)
+ windowInfo->InsertAfter(mOldestWindow->mOlder, nullptr);
+ else
+ mOldestWindow = windowInfo;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::UnregisterWindow(nsIAppWindow* inWindow) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mReady);
+ 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++;
+ }
+
+ nsIAppWindow* window = inInfo->mWindow.get();
+ for (const auto& listener : mListeners.ForwardRange()) {
+ listener->OnCloseWindow(window);
+ }
+
+ // 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(nsIAppWindow* 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);
+ if (!mReady) {
+ // If we get here with mReady false, we most likely did observe
+ // xpcom-shutdown. We will return an empty enumerator such that
+ // we make happy Javascripts calling late without throwing.
+ return NS_NewEmptyEnumerator(outEnumerator);
+ }
+ RefPtr<nsAppShellWindowEnumerator> enumerator =
+ new nsASDOMWindowEarlyToLateEnumerator(inType, *this);
+ enumerator.forget(outEnumerator);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetAppWindowEnumerator(const char16_t* inType,
+ nsISimpleEnumerator** outEnumerator) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(outEnumerator);
+ if (!mReady) {
+ // If we get here with mReady false, we most likely did observe
+ // xpcom-shutdown. We will return an empty enumerator such that
+ // we make happy Javascripts calling late without throwing.
+ return NS_NewEmptyEnumerator(outEnumerator);
+ }
+ RefPtr<nsAppShellWindowEnumerator> enumerator =
+ new nsASAppWindowEarlyToLateEnumerator(inType, *this);
+ enumerator.forget(outEnumerator);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetZOrderAppWindowEnumerator(const char16_t* aWindowType,
+ bool aFrontToBack,
+ nsISimpleEnumerator** _retval) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(_retval);
+ if (!mReady) {
+ // If we get here with mReady false, we most likely did observe
+ // xpcom-shutdown. We will return an empty enumerator such that
+ // we make happy Javascripts calling late without throwing.
+ return NS_NewEmptyEnumerator(_retval);
+ }
+ RefPtr<nsAppShellWindowEnumerator> enumerator;
+ if (aFrontToBack)
+ enumerator = new nsASAppWindowFrontToBackEnumerator(aWindowType, *this);
+ else
+ enumerator = new nsASAppWindowBackToFrontEnumerator(aWindowType, *this);
+
+ enumerator.forget(_retval);
+ return NS_OK;
+}
+
+void nsWindowMediator::AddEnumerator(nsAppShellWindowEnumerator* inEnumerator) {
+ mEnumeratorList.AppendElement(inEnumerator);
+}
+
+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::GetMostRecentBrowserWindow(mozIDOMWindowProxy** outWindow) {
+ nsresult rv = GetMostRecentWindow(u"navigator:browser", outWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef MOZ_WIDGET_ANDROID
+ if (!*outWindow) {
+ rv = GetMostRecentWindow(u"navigator:geckoview", outWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+#endif
+
+#ifdef MOZ_THUNDERBIRD
+ if (!*outWindow) {
+ rv = GetMostRecentWindow(u"mail:3pane", outWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+#endif
+
+ 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<nsGlobalWindowOuter> window =
+ nsGlobalWindowOuter::GetOuterWindowWithId(aWindowID);
+ window.forget(aWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::GetCurrentInnerWindowWithId(uint64_t aWindowID,
+ mozIDOMWindow** aWindow) {
+ RefPtr<nsGlobalWindowInner> window =
+ nsGlobalWindowInner::GetInnerWindowWithId(aWindowID);
+
+ // not found
+ if (!window) return NS_OK;
+
+ nsCOMPtr<nsPIDOMWindowOuter> outer = window->GetOuterWindow();
+ NS_ENSURE_TRUE(outer, NS_ERROR_UNEXPECTED);
+
+ // outer is already using another inner, so it's same as not found
+ if (outer->GetCurrentInnerWindow() != window) return NS_OK;
+
+ window.forget(aWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::UpdateWindowTimeStamp(nsIAppWindow* inWindow) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mReady);
+ 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;
+}
+
+/* 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(nsIAppWindow* inWindow,
+ uint32_t inPosition, nsIWidget* inBelow,
+ uint32_t* outPosition,
+ nsIWidget** outBelow, bool* outAltered) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_ENSURE_ARG_POINTER(outBelow);
+ MOZ_ASSERT(mReady);
+ 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;
+ nsIAppWindow* 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(nsIAppWindow* inWindow, uint32_t inPosition,
+ nsIAppWindow* 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;
+
+ MOZ_ASSERT(mReady);
+ 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(nsIAppWindow* aWindow, uint32_t* _retval) {
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nsIAppWindow::normalZ;
+ // This can fail during window destruction.
+ nsWindowInfo* info = GetInfoFor(aWindow);
+ if (info) {
+ *_retval = info->mZLevel;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::SetZLevel(nsIAppWindow* aWindow, uint32_t aZLevel) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mReady);
+ 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, nsIObserver,
+ nsISupportsWeakReference)
+
+NS_IMETHODIMP
+nsWindowMediator::AddListener(nsIWindowMediatorListener* aListener) {
+ NS_ENSURE_ARG_POINTER(aListener);
+
+ mListeners.AppendElement(aListener);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowMediator::RemoveListener(nsIWindowMediatorListener* aListener) {
+ NS_ENSURE_ARG_POINTER(aListener);
+
+ mListeners.RemoveElement(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;
+}
diff --git a/xpfe/appshell/nsWindowMediator.h b/xpfe/appshell/nsWindowMediator.h
new file mode 100644
index 0000000000..578f901ea7
--- /dev/null
+++ b/xpfe/appshell/nsWindowMediator.h
@@ -0,0 +1,73 @@
+/* -*- 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 "nsPIDOMWindow.h"
+#include "nsString.h"
+#include "nsWeakReference.h"
+#include "nsTObserverArray.h"
+
+class nsAppShellWindowEnumerator;
+class nsASAppWindowEarlyToLateEnumerator;
+class nsASDOMWindowEarlyToLateEnumerator;
+class nsASAppWindowFrontToBackEnumerator;
+class nsASAppWindowBackToFrontEnumerator;
+class nsIWindowMediatorListener;
+struct nsWindowInfo;
+
+class nsWindowMediator : public nsIWindowMediator,
+ public nsIObserver,
+ public nsSupportsWeakReference {
+ friend class nsAppShellWindowEnumerator;
+ friend class nsASAppWindowEarlyToLateEnumerator;
+ friend class nsASDOMWindowEarlyToLateEnumerator;
+ friend class nsASAppWindowFrontToBackEnumerator;
+ friend class nsASAppWindowBackToFrontEnumerator;
+
+ protected:
+ virtual ~nsWindowMediator();
+
+ public:
+ nsWindowMediator();
+
+ nsresult Init();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWINDOWMEDIATOR
+ NS_DECL_NSIOBSERVER
+
+ static nsresult GetDOMWindow(nsIAppWindow* inWindow,
+ nsCOMPtr<nsPIDOMWindowOuter>& outDOMWindow);
+
+ private:
+ void AddEnumerator(nsAppShellWindowEnumerator* inEnumerator);
+ int32_t RemoveEnumerator(nsAppShellWindowEnumerator* inEnumerator);
+ nsWindowInfo* MostRecentWindowInfo(const char16_t* inType,
+ bool aSkipPrivateBrowsingOrClosed = false);
+
+ nsresult UnregisterWindow(nsWindowInfo* inInfo);
+ nsWindowInfo* GetInfoFor(nsIAppWindow* aWindow);
+ nsWindowInfo* GetInfoFor(nsIWidget* aWindow);
+ void SortZOrderFrontToBack();
+ void SortZOrderBackToFront();
+
+ nsTArray<nsAppShellWindowEnumerator*> mEnumeratorList;
+ nsWindowInfo* mOldestWindow;
+ nsWindowInfo* mTopmostWindow;
+ int32_t mTimeStamp;
+ bool mSortingZOrder;
+ bool mReady;
+
+ typedef nsTObserverArray<nsCOMPtr<nsIWindowMediatorListener>> ListenerArray;
+ ListenerArray mListeners;
+};
+
+#endif
diff --git a/xpfe/appshell/test/chrome.ini b/xpfe/appshell/test/chrome.ini
new file mode 100644
index 0000000000..ac756b6fae
--- /dev/null
+++ b/xpfe/appshell/test/chrome.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+skip-if = os == 'android'
+
+[test_windowlessBrowser.xhtml]
diff --git a/xpfe/appshell/test/test_windowlessBrowser.xhtml b/xpfe/appshell/test/test_windowlessBrowser.xhtml
new file mode 100644
index 0000000000..2e4fe6cab0
--- /dev/null
+++ b/xpfe/appshell/test/test_windowlessBrowser.xhtml
@@ -0,0 +1,67 @@
+<?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 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[
+
+function testWindowlessBrowser(chromePrivileged) {
+ var webNav = Services.appShell.createWindowlessBrowser(chromePrivileged);
+
+ ok(webNav, "createWindowlessBrowser should return a wevNav");
+
+ let docShell = webNav.docShell;
+
+ 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.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>