forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			3492 lines
		
	
	
	
		
			114 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			3492 lines
		
	
	
	
		
			114 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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"
 | |
| #include "mozilla/Try.h"
 | |
| 
 | |
| // Interfaces needed to be included
 | |
| #include "nsGlobalWindowOuter.h"
 | |
| #include "nsIAppShell.h"
 | |
| #include "nsIAppShellService.h"
 | |
| #include "nsIDocumentViewer.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 "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"
 | |
| 
 | |
| #if defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK)
 | |
| #  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<nsIDocumentViewer> viewer;
 | |
|   mDocShell->GetDocViewer(getter_AddRefs(viewer));
 | |
|   if (viewer) {
 | |
|     RefPtr<dom::Document> doc = viewer->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::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;
 | |
| 
 | |
| #ifdef USE_NATIVE_MENUS
 | |
|   if (!gfxPlatform::IsHeadless()) {
 | |
|     // On macOS, for modals created early in startup. (e.g.
 | |
|     // ProfileManager/ProfileDowngrade) this creates a fallback menu for the
 | |
|     // menu bar which only contains a "Quit" menu item. This allows the user to
 | |
|     // quit the application in a regular way with cmd+Q.
 | |
|     widget::NativeMenuSupport::CreateNativeMenuBar(mWindow, nullptr);
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP AppWindow::RollupAllPopups() {
 | |
|   if (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
 | |
|     pm->Rollup({});
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| //*****************************************************************************
 | |
| // 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));
 | |
|   }
 | |
| 
 | |
|   // 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);
 | |
| 
 | |
|   *aAvailWidth = screen->AvailWidth();
 | |
|   *aAvailHeight = screen->AvailHeight();
 | |
|   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);
 | |
| 
 | |
|   // Only change blank window status once we're loaded, so that a
 | |
|   // partially-loaded browser window doesn't start painting early.
 | |
|   if (mChromeLoaded) {
 | |
|     mWindow->SetIsEarlyBlankWindow(attr.EqualsLiteral("navigator:blank"));
 | |
|     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) {
 | |
|   if (!XRE_IsParentProcess()) {
 | |
|     // The XULStore is only available in the parent process.
 | |
|     return NS_ERROR_UNEXPECTED;
 | |
|   }
 | |
| 
 | |
|   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) {
 | |
|   if (!XRE_IsParentProcess()) {
 | |
|     // The XULStore is only available in the parent process.
 | |
|     return NS_ERROR_UNEXPECTED;
 | |
|   }
 | |
| 
 | |
|   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<nsIDocumentViewer> viewer;
 | |
|   mDocShell->GetDocViewer(getter_AddRefs(viewer));
 | |
|   NS_ENSURE_TRUE(viewer, nullptr);
 | |
| 
 | |
|   const dom::Document* document = viewer->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<nsIDocumentViewer> viewer;
 | |
|   mDocShell->GetDocViewer(getter_AddRefs(viewer));
 | |
|   if (!viewer) {
 | |
|     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 =
 | |
|       viewer->GetContentSize(maxWidth, maxHeight, prefWidth);
 | |
|   if (!size) {
 | |
|     return;
 | |
|   }
 | |
|   nsPresContext* pc = viewer->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",
 | |
|           RFPTarget::RoundWindowSize) &&
 | |
|       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::FullscreenWillChange(bool aInFullscreen) {
 | |
|   if (mDocShell) {
 | |
|     if (nsCOMPtr<nsPIDOMWindowOuter> ourWindow = mDocShell->GetWindow()) {
 | |
|       ourWindow->FullscreenWillChange(aInFullscreen);
 | |
|     }
 | |
|   }
 | |
|   MOZ_ASSERT(mFullscreenChangeState == FullscreenChangeState::NotChanging);
 | |
| 
 | |
|   CSSToLayoutDeviceScale scale = UnscaledDevicePixelsPerCSSPixel();
 | |
|   CSSIntSize windowSizeCSS = RoundedToInt(GetSize() / scale);
 | |
| 
 | |
|   CSSIntSize screenSizeCSS;
 | |
|   GetAvailScreenSize(&screenSizeCSS.width, &screenSizeCSS.height);
 | |
| 
 | |
|   // 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 == (windowSizeCSS.width == screenSizeCSS.width &&
 | |
|                          windowSizeCSS.height >= screenSizeCSS.height))
 | |
|           ? 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 (nsXULPopupManager* pm = nsXULPopupManager::GetInstance()) {
 | |
|     pm->Rollup({});
 | |
|   }
 | |
|   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;
 | |
| };
 | |
| 
 | |
| // On macOS the hidden window is created eagerly, and we want to wait for it to
 | |
| // load the native menus.
 | |
| static bool sWaitingForHiddenWindowToLoadNativeMenus =
 | |
| #  ifdef XP_MACOSX
 | |
|     true
 | |
| #  else
 | |
|     false
 | |
| #  endif
 | |
|     ;
 | |
| 
 | |
| 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(
 | |
|       u"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"_ns,
 | |
|       u"menubar"_ns);
 | |
| 
 | |
|   RefPtr<Element> menubar;
 | |
|   if (menubarElements) {
 | |
|     menubar = Element::FromNodeOrNull(menubarElements->Item(0));
 | |
|   }
 | |
| 
 | |
|   widget::NativeMenuSupport::CreateNativeMenuBar(aParentWindow, menubar);
 | |
| 
 | |
|   if (sWaitingForHiddenWindowToLoadNativeMenus) {
 | |
|     sWaitingForHiddenWindowToLoadNativeMenus = false;
 | |
|     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) {
 | |
|   if (RefPtr<DocumentL10n> l10n = aDoc->GetL10n()) {
 | |
|     // Wait for l10n to be ready so the menus are localized.
 | |
|     RefPtr<Promise> promise = l10n->Ready();
 | |
|     MOZ_ASSERT(promise);
 | |
|     RefPtr 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()) {
 | |
|     if (RefPtr<Document> menubarDoc = mDocShell->GetExtantDocument()) {
 | |
|       if (mIsHiddenWindow || !sWaitingForHiddenWindowToLoadNativeMenus) {
 | |
|         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<nsIDocumentViewer> viewer;
 | |
|     mDocShell->GetDocViewer(getter_AddRefs(viewer));
 | |
|     if (viewer) {
 | |
|       RefPtr<nsPresContext> presContext = viewer->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::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
 | 
